diff --git a/core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php b/core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php
index 590b80f0c5bf12955089f5866a80e4fad9a05bf0..48c3801eb4767e16fb6890297b3948ebf248dcc6 100644
--- a/core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php
+++ b/core/lib/Drupal/Core/Entity/ContentEntityDeleteForm.php
@@ -7,11 +7,141 @@
 
 namespace Drupal\Core\Entity;
 
+use Drupal\Core\Form\FormStateInterface;
+
 /**
  * Provides a generic base class for a content entity deletion form.
+ *
+ * @todo Re-evaluate and streamline the entity deletion form class hierarchy in
+ *   https://www.drupal.org/node/2491057.
  */
 class ContentEntityDeleteForm extends ContentEntityConfirmFormBase {
 
-  use EntityDeleteFormTrait;
+  use EntityDeleteFormTrait {
+    getQuestion as traitGetQuestion;
+    logDeletionMessage as traitLogDeletionMessage;
+    getDeletionMessage as traitGetDeletionMessage;
+    getCancelUrl as traitGetCancelUrl;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function buildForm(array $form, FormStateInterface $form_state) {
+    $form = parent::buildForm($form, $form_state);
+
+    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
+    $entity = $this->getEntity();
+    if ($entity->isDefaultTranslation()) {
+      if (count($entity->getTranslationLanguages()) > 1) {
+        $languages = [];
+        foreach ($entity->getTranslationLanguages() as $language) {
+          $languages[] = $language->getName();
+        }
+
+        $form['deleted_translations'] = array(
+          '#theme' => 'item_list',
+          '#title' => $this->t('The following @entity-type translations will be deleted:', [
+            '@entity-type' => $entity->getEntityType()->getLowercaseLabel()
+          ]),
+          '#items' => $languages,
+        );
+
+        $form['actions']['submit']['#value'] = $this->t('Delete all translations');
+      }
+    }
+    else {
+      $form['actions']['submit']['#value'] = $this->t('Delete @language translation', array('@language' => $entity->language()->getName()));
+    }
+
+    return $form;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function submitForm(array &$form, FormStateInterface $form_state) {
+    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
+    $entity = $this->getEntity();
+
+    // Make sure that deleting a translation does not delete the whole entity.
+    if (!$entity->isDefaultTranslation()) {
+      $untranslated_entity = $entity->getUntranslated();
+      $untranslated_entity->removeTranslation($entity->language()->getId());
+      $untranslated_entity->save();
+      $form_state->setRedirectUrl($untranslated_entity->urlInfo('canonical'));
+    }
+    else {
+      $entity->delete();
+      $form_state->setRedirectUrl($this->getRedirectUrl());
+    }
+
+    drupal_set_message($this->getDeletionMessage());
+    $this->logDeletionMessage();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCancelUrl() {
+    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
+    $entity = $this->getEntity();
+    return $entity->isDefaultTranslation() ? $this->traitGetCancelUrl() : $entity->urlInfo('canonical');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getDeletionMessage() {
+    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
+    $entity = $this->getEntity();
+
+    if (!$entity->isDefaultTranslation()) {
+      return $this->t('The @entity-type %label @language translation has been deleted.', [
+        '@entity-type' => $entity->getEntityType()->getLowercaseLabel(),
+        '%label'       => $entity->label(),
+        '@language'    => $entity->language()->getName(),
+      ]);
+    }
+
+    return $this->traitGetDeletionMessage();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function logDeletionMessage() {
+    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
+    $entity = $this->getEntity();
+
+    if (!$entity->isDefaultTranslation()) {
+      $this->logger($entity->getEntityType()->getProvider())->notice('The @entity-type %label @language translation has been deleted.', [
+        '@entity-type' => $entity->getEntityType()->getLowercaseLabel(),
+        '%label'       => $entity->label(),
+        '@language'    => $entity->language()->getName(),
+      ]);
+    }
+    else {
+      $this->traitLogDeletionMessage();
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getQuestion() {
+    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
+    $entity = $this->getEntity();
+
+    if (!$entity->isDefaultTranslation()) {
+      return $this->t('Are you sure you want to delete the @language translation of the @entity-type %label?', array(
+        '@language' => $entity->language()->getName(),
+        '@entity-type' => $this->getEntity()->getEntityType()->getLowercaseLabel(),
+        '%label' => $this->getEntity()->label(),
+      ));
+    }
+
+    return $this->traitGetQuestion();
+  }
 
 }
diff --git a/core/lib/Drupal/Core/Entity/EntityDeleteFormTrait.php b/core/lib/Drupal/Core/Entity/EntityDeleteFormTrait.php
index d35bcea71aee8fe6e3856f0e38d899b7bb0c5aab..8b9fc0eb31ef7059fe8885fb95bf307f06aeceb8 100644
--- a/core/lib/Drupal/Core/Entity/EntityDeleteFormTrait.php
+++ b/core/lib/Drupal/Core/Entity/EntityDeleteFormTrait.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Config\Entity\ConfigDependencyDeleteFormTrait;
 use Drupal\Core\Form\FormStateInterface;
+use Drupal\Core\Url;
 
 /**
  * Provides a trait for an entity deletion form.
@@ -90,6 +91,24 @@ public function getCancelUrl() {
     }
   }
 
+  /**
+   * Returns the URL where the user should be redirected after deletion.
+   *
+   * @return \Drupal\Core\Url
+   *   The redirect URL.
+   */
+  protected function getRedirectUrl() {
+    $entity = $this->getEntity();
+    if ($entity->hasLinkTemplate('collection')) {
+      // If available, return the collection URL.
+      return $entity->urlInfo('collection');
+    }
+    else {
+      // Otherwise fall back to the front page.
+      return Url::fromRoute('<front>');
+    }
+  }
+
   /**
    * Logs a message about the deleted entity.
    */
diff --git a/core/lib/Drupal/Core/Form/ConfirmFormBase.php b/core/lib/Drupal/Core/Form/ConfirmFormBase.php
index 28966900e53674e928381e524c96ae2804cd4468..03c7acd242860472947846a3234d9158097e7286 100644
--- a/core/lib/Drupal/Core/Form/ConfirmFormBase.php
+++ b/core/lib/Drupal/Core/Form/ConfirmFormBase.php
@@ -54,6 +54,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
     $form['actions']['submit'] = array(
       '#type' => 'submit',
       '#value' => $this->getConfirmText(),
+      '#button_type' => 'primary',
     );
 
     $form['actions']['cancel'] = ConfirmFormHelper::buildCancelLink($this, $this->getRequest());
diff --git a/core/modules/aggregator/src/Form/FeedDeleteForm.php b/core/modules/aggregator/src/Form/FeedDeleteForm.php
index 2acab06d1d1b15a5679324545fba39123f04af6c..f353a8c22069f2032f3575cf0e876bea37981bfc 100644
--- a/core/modules/aggregator/src/Form/FeedDeleteForm.php
+++ b/core/modules/aggregator/src/Form/FeedDeleteForm.php
@@ -22,6 +22,13 @@ public function getCancelUrl() {
     return new Url('aggregator.admin_overview');
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  protected function getRedirectUrl() {
+    return $this->getCancelUrl();
+  }
+
   /**
    * {@inheritdoc}
    */
diff --git a/core/modules/comment/src/Form/DeleteForm.php b/core/modules/comment/src/Form/DeleteForm.php
index c754bfefe0c3f6626b466933b82e658f7cd9d16f..cfeaa53cd2702115ea850ea4fd510438092f9d68 100644
--- a/core/modules/comment/src/Form/DeleteForm.php
+++ b/core/modules/comment/src/Form/DeleteForm.php
@@ -8,7 +8,6 @@
 namespace Drupal\comment\Form;
 
 use Drupal\Core\Entity\ContentEntityDeleteForm;
-use Drupal\Core\Form\FormStateInterface;
 
 /**
  * Provides the comment delete confirmation form.
@@ -23,6 +22,13 @@ public function getCancelUrl() {
     return $this->entity->get('entity_id')->entity->urlInfo();
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  protected function getRedirectUrl() {
+    return $this->getCancelUrl();
+  }
+
   /**
    * {@inheritdoc}
    */
diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module
index 280d4d626dfbb7d3a0b3a5d8346223015d5d2702..24f8f18ffd1f95334f4269a56f3a8786eedc8ea9 100644
--- a/core/modules/content_translation/content_translation.module
+++ b/core/modules/content_translation/content_translation.module
@@ -127,6 +127,9 @@ function content_translation_entity_type_alter(array &$entity_types) {
       if (!$entity_type->get('content_translation_metadata')) {
         $entity_type->set('content_translation_metadata', 'Drupal\content_translation\ContentTranslationMetadataWrapper');
       }
+      if (!$entity_type->getFormClass('content_translation_deletion')) {
+        $entity_type->setFormClass('content_translation_deletion', '\Drupal\content_translation\Form\ContentTranslationDeleteForm');
+      }
 
       $translation = $entity_type->get('translation');
       if (!$translation || !isset($translation['content_translation'])) {
@@ -277,8 +280,10 @@ function content_translation_form_alter(array &$form, FormStateInterface $form_s
     return;
   }
   $entity = $form_object->getEntity();
+  $op = $form_object->getOperation();
 
-  if ($entity instanceof ContentEntityInterface && $entity->isTranslatable() && count($entity->getTranslationLanguages()) > 1) {
+  // Let the content translation handler alter the content entity edit form.
+  if ($entity instanceof ContentEntityInterface && $entity->isTranslatable() && count($entity->getTranslationLanguages()) > 1 && ($op == 'edit' || $op == 'default')) {
     $controller = \Drupal::entityManager()->getHandler($entity->getEntityTypeId(), 'translation');
     $controller->entityFormAlter($form, $form_state, $entity);
 
diff --git a/core/modules/content_translation/src/Access/ContentTranslationManageAccessCheck.php b/core/modules/content_translation/src/Access/ContentTranslationManageAccessCheck.php
index aa01d674e044f21eebdfdc0bd6d3e76206580777..9d2e66472f889e5f876304d6e5f8841b073ac712 100644
--- a/core/modules/content_translation/src/Access/ContentTranslationManageAccessCheck.php
+++ b/core/modules/content_translation/src/Access/ContentTranslationManageAccessCheck.php
@@ -73,13 +73,28 @@ public function __construct(EntityManagerInterface $manager, LanguageManagerInte
   public function access(Route $route, RouteMatchInterface $route_match, AccountInterface $account, $source = NULL, $target = NULL, $language = NULL, $entity_type_id = NULL) {
     /* @var \Drupal\Core\Entity\ContentEntityInterface $entity */
     if ($entity = $route_match->getParameter($entity_type_id)) {
+      $operation = $route->getRequirement('_access_content_translation_manage');
+      $language = $this->languageManager->getLanguage($language) ?: $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT);
+      $entity_type = $this->entityManager->getDefinition($entity_type_id);
+
+      if (in_array($operation, ['update', 'delete'])) {
+        // Translation operations cannot be performed on the default
+        // translation.
+        if ($language->getId() == $entity->getUntranslated()->language()->getId()) {
+          return AccessResult::forbidden()->cacheUntilEntityChanges($entity);
+        }
+        // Editors have no access to the translation operations, as entity
+        // access already grants them an equal or greater access level.
+        $templates = ['update' => 'edit-form', 'delete' => 'delete-form'];
+        if ($entity->access($operation) && $entity_type->hasLinkTemplate($templates[$operation])) {
+          return AccessResult::forbidden()->cachePerPermissions();
+        }
+      }
 
       if ($account->hasPermission('translate any entity')) {
         return AccessResult::allowed()->cachePerPermissions();
       }
 
-      $operation = $route->getRequirement('_access_content_translation_manage');
-
       /* @var \Drupal\content_translation\ContentTranslationHandlerInterface $handler */
       $handler = $this->entityManager->getHandler($entity->getEntityTypeId(), 'translation');
 
@@ -98,9 +113,8 @@ public function access(Route $route, RouteMatchInterface $route_match, AccountIn
           return AccessResult::allowedIf($is_new_translation)->cachePerPermissions()->cacheUntilEntityChanges($entity)
             ->andIf($handler->getTranslationAccess($entity, $operation));
 
-        case 'update':
         case 'delete':
-          $language = $this->languageManager->getLanguage($language) ?: $this->languageManager->getCurrentLanguage(LanguageInterface::TYPE_CONTENT);
+        case 'update':
           $has_translation = isset($languages[$language->getId()])
             && $language->getId() != $entity->getUntranslated()->language()->getId()
             && isset($translations[$language->getId()]);
diff --git a/core/modules/content_translation/src/ContentTranslationHandler.php b/core/modules/content_translation/src/ContentTranslationHandler.php
index 0bd26f4d7a3f16ce9b442eda064c56823d20adf4..76e05bd46e1c2df6c7522e89a8f9de37ba959195 100644
--- a/core/modules/content_translation/src/ContentTranslationHandler.php
+++ b/core/modules/content_translation/src/ContentTranslationHandler.php
@@ -319,12 +319,13 @@ public function entityFormAlter(array &$form, FormStateInterface $form_state, En
             break;
           }
         }
+        $access = $this->getTranslationAccess($entity, 'delete')->isAllowed() || ($entity->access('delete') && $this->entityType->hasLinkTemplate('delete-form'));
         $form['actions']['delete_translation'] = array(
           '#type' => 'submit',
           '#value' => t('Delete translation'),
           '#weight' => $weight,
           '#submit' => array(array($this, 'entityFormDeleteTranslation')),
-          '#access' => $this->getTranslationAccess($entity, 'delete')->isAllowed(),
+          '#access' => $access,
         );
       }
 
@@ -607,13 +608,20 @@ function entityFormDelete($form, FormStateInterface $form_state) {
    * Takes care of content translation deletion.
    */
   function entityFormDeleteTranslation($form, FormStateInterface $form_state) {
+    /** @var \Drupal\Core\Entity\ContentEntityFormInterface $form_object */
     $form_object = $form_state->getFormObject();
+    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
     $entity = $form_object->getEntity();
     $entity_type_id = $entity->getEntityTypeId();
-    $form_state->setRedirect('content_translation.translation_delete_' . $entity_type_id, array(
-      $entity_type_id => $entity->id(),
-      'language' => $form_object->getFormLangcode($form_state),
-    ));
+    if ($entity->access('delete') && $this->entityType->hasLinkTemplate('delete-form')) {
+      $form_state->setRedirectUrl($entity->urlInfo('delete-form'));
+    }
+    else {
+      $form_state->setRedirect('content_translation.translation_delete_' . $entity_type_id, [
+        $entity_type_id => $entity->id(),
+        'language' => $form_object->getFormLangcode($form_state),
+      ]);
+    }
   }
 
   /**
diff --git a/core/modules/content_translation/src/Controller/ContentTranslationController.php b/core/modules/content_translation/src/Controller/ContentTranslationController.php
index d00dee24a41b251e8b87ffd815fcc957e617a8e3..bae6a940e19b835225d7dd392b750ac25e58f29c 100644
--- a/core/modules/content_translation/src/Controller/ContentTranslationController.php
+++ b/core/modules/content_translation/src/Controller/ContentTranslationController.php
@@ -71,10 +71,12 @@ public function prepareTranslation(ContentEntityInterface $entity, LanguageInter
    * Array of page elements to render.
    */
   public function overview(RouteMatchInterface $route_match, $entity_type_id = NULL) {
+    /** @var \Drupal\Core\Entity\ContentEntityInterface $entity */
     $entity = $route_match->getParameter($entity_type_id);
     $account = $this->currentUser();
     $handler = $this->entityManager()->getHandler($entity_type_id, 'translation');
     $manager = $this->manager;
+    $entity_type = $entity->getEntityType();
 
     $languages = $this->languageManager()->getLanguages();
     $original = $entity->getUntranslated()->language()->getId();
@@ -164,7 +166,7 @@ public function overview(RouteMatchInterface $route_match, $entity_type_id = NUL
           // If the user is allowed to edit the entity we point the edit link to
           // the entity form, otherwise if we are not dealing with the original
           // language we point the link to the translation form.
-          if ($entity->access('update')) {
+          if ($entity->access('update') && $entity_type->hasLinkTemplate('edit-form')) {
             $links['edit']['url'] = $entity->urlInfo('edit-form');
             $links['edit']['language'] = $language;
           }
@@ -190,7 +192,14 @@ public function overview(RouteMatchInterface $route_match, $entity_type_id = NUL
           }
           else {
             $source_name = isset($languages[$source]) ? $languages[$source]->getName() : $this->t('n/a');
-            if ($handler->getTranslationAccess($entity, 'delete')->isAllowed()) {
+            if ($entity->access('delete') && $entity_type->hasLinkTemplate('delete-form')) {
+              $links['delete'] = array(
+                'title' => $this->t('Delete'),
+                'url' => $entity->urlInfo('delete-form'),
+                'language' => $language,
+              );
+            }
+            elseif ($handler->getTranslationAccess($entity, 'delete')->isAllowed()) {
               $links['delete'] = array(
                 'title' => $this->t('Delete'),
                 'url' => $delete_url,
diff --git a/core/modules/content_translation/src/Form/ContentTranslationDeleteForm.php b/core/modules/content_translation/src/Form/ContentTranslationDeleteForm.php
index a17479a8993cac0a541c76ba2e75d444b4910523..0a6c589292b6c5d038581ff7f702bce96f4d1570 100644
--- a/core/modules/content_translation/src/Form/ContentTranslationDeleteForm.php
+++ b/core/modules/content_translation/src/Form/ContentTranslationDeleteForm.php
@@ -7,29 +7,14 @@
 
 namespace Drupal\content_translation\Form;
 
-use Drupal\Core\Form\ConfirmFormBase;
+use Drupal\Core\Entity\ContentEntityDeleteForm;
 use Drupal\Core\Form\FormStateInterface;
 use Drupal\Core\Language\LanguageInterface;
-use Drupal\Core\Url;
 
 /**
  * Delete translation form for content_translation module.
  */
-class ContentTranslationDeleteForm extends ConfirmFormBase {
-
-  /**
-   * The entity whose translation is being deleted.
-   *
-   * @var \Drupal\Core\Entity\EntityInterface
-   */
-  protected $entity;
-
-  /**
-   * The language of the translation being deleted.
-   *
-   * @var \Drupal\Core\Language\LanguageInterface
-   */
-  protected $language;
+class ContentTranslationDeleteForm extends ContentEntityDeleteForm {
 
   /**
    * {@inheritdoc}
@@ -41,43 +26,11 @@ public function getFormId() {
   /**
    * {@inheritdoc}
    */
-  public function buildForm(array $form, FormStateInterface $form_state, $entity_type_id = NULL, LanguageInterface $language = NULL) {
-    $this->entity = $this->getRouteMatch()->getParameter($entity_type_id);
-    $this->language = $language;
+  public function buildForm(array $form, FormStateInterface $form_state, LanguageInterface $language = NULL) {
+    if ($language) {
+      $form_state->set('langcode', $language->getId());
+    }
     return parent::buildForm($form, $form_state);
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function getConfirmText() {
-    return $this->t('Delete');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getQuestion() {
-    return $this->t('Are you sure you want to delete the @language translation of %label?', array('@language' => $this->language->getName(), '%label' => $this->entity->label()));
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getCancelUrl() {
-    return $this->entity->urlInfo('drupal:content-translation-overview');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function submitForm(array &$form, FormStateInterface $form_state) {
-    // Remove the translated values.
-    $this->entity = $this->entity->getUntranslated();
-    $this->entity->removeTranslation($this->language->getId());
-    $this->entity->save();
-
-    $form_state->setRedirectUrl($this->getCancelUrl());
-  }
-
 }
diff --git a/core/modules/content_translation/src/Routing/ContentTranslationRouteSubscriber.php b/core/modules/content_translation/src/Routing/ContentTranslationRouteSubscriber.php
index d9d672c9707cd6317e9de50769b318f28c6e71df..1aa35b93ef13214dfbb35979cb1e64d31365ef00 100644
--- a/core/modules/content_translation/src/Routing/ContentTranslationRouteSubscriber.php
+++ b/core/modules/content_translation/src/Routing/ContentTranslationRouteSubscriber.php
@@ -141,7 +141,7 @@ protected function alterRoutes(RouteCollection $collection) {
       $route = new Route(
         $path . '/delete/{language}',
         array(
-          '_form' => '\Drupal\content_translation\Form\ContentTranslationDeleteForm',
+          '_entity_form' => $entity_type_id . '.content_translation_deletion',
           'language' => NULL,
           '_title' => 'Delete',
           'entity_type_id' => $entity_type_id,
diff --git a/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php b/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php
index 3c1bd787fb3901c74daa7424eccec885a023d229..bf781fb6dde9198482b2787746d98ad52b105b1c 100644
--- a/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php
+++ b/core/modules/content_translation/src/Tests/ContentTranslationUITestBase.php
@@ -253,16 +253,23 @@ protected function doTestAuthoringInfo() {
    */
   protected function doTestTranslationDeletion() {
     // Confirm and delete a translation.
+    $this->drupalLogin($this->translator);
     $langcode = 'fr';
     $entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
-    $url = $entity->urlInfo('edit-form', array('language' => ConfigurableLanguage::load($langcode)));
+    $language = ConfigurableLanguage::load($langcode);
+    $url = $entity->urlInfo('edit-form', array('language' => $language));
     $this->drupalPostForm($url, array(), t('Delete translation'));
-    $this->drupalPostForm(NULL, array(), t('Delete'));
+    $this->drupalPostForm(NULL, array(), t('Delete @language translation', array('@language' => $language->getName())));
     $entity = entity_load($this->entityTypeId, $this->entityId, TRUE);
     if ($this->assertTrue(is_object($entity), 'Entity found')) {
       $translations = $entity->getTranslationLanguages();
       $this->assertTrue(count($translations) == 2 && empty($translations[$langcode]), 'Translation successfully deleted.');
     }
+
+    // Check that the translator cannot delete the original translation.
+    $args = [$this->entityTypeId => $entity->id(), 'language' => 'en'];
+    $this->drupalGet(Url::fromRoute('content_translation.translation_delete_' . $this->entityTypeId, $args));
+    $this->assertResponse(403);
   }
 
   /**
diff --git a/core/modules/content_translation/src/Tests/ContentTranslationWorkflowsTest.php b/core/modules/content_translation/src/Tests/ContentTranslationWorkflowsTest.php
index 62daf7557e015fa20a9397a031a54ebc33d386e6..3de797ec5fdfb778ca4f9a6af15ad749fcf69db5 100644
--- a/core/modules/content_translation/src/Tests/ContentTranslationWorkflowsTest.php
+++ b/core/modules/content_translation/src/Tests/ContentTranslationWorkflowsTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\content_translation\Tests;
 
+use Drupal\Component\Utility\SafeMarkup;
 use Drupal\Core\Language\LanguageInterface;
 use Drupal\Core\Url;
 use Drupal\user\UserInterface;
@@ -72,16 +73,37 @@ protected function setupEntity() {
    */
   function testWorkflows() {
     // Test workflows for the editor.
-    $expected_status = array('edit' => 200, 'overview' => 403, 'add_translation' => 403, 'edit_translation' => 403);
-    $this->assertWorkflows($this->editor, $expected_status);
+    $expected_status = [
+      'edit' => 200,
+      'delete' => 200,
+      'overview' => 403,
+      'add_translation' => 403,
+      'edit_translation' => 403,
+      'delete_translation' => 403,
+    ];
+    $this->doTestWorkflows($this->editor, $expected_status);
 
     // Test workflows for the translator.
-    $expected_status = array('edit' => 403, 'overview' => 200, 'add_translation' => 200, 'edit_translation' => 200);
-    $this->assertWorkflows($this->translator, $expected_status);
+    $expected_status = [
+      'edit' => 403,
+      'delete' => 403,
+      'overview' => 200,
+      'add_translation' => 200,
+      'edit_translation' => 200,
+      'delete_translation' => 200,
+    ];
+    $this->doTestWorkflows($this->translator, $expected_status);
 
     // Test workflows for the admin.
-    $expected_status = array('edit' => 200, 'overview' => 200, 'add_translation' => 200, 'edit_translation' => 200);
-    $this->assertWorkflows($this->administrator, $expected_status);
+    $expected_status = [
+      'edit' => 200,
+      'delete' => 200,
+      'overview' => 200,
+      'add_translation' => 200,
+      'edit_translation' => 403,
+      'delete_translation' => 403,
+    ];
+    $this->doTestWorkflows($this->administrator, $expected_status);
 
     // Check that translation permissions allow the associated operations.
     $ops = array('create' => t('Add'), 'update' => t('Edit'), 'delete' => t('Delete'));
@@ -111,30 +133,35 @@ function testWorkflows() {
    *   The an associative array with the operation name as key and the expected
    *   status as value.
    */
-  protected function assertWorkflows(UserInterface $user, $expected_status) {
+  protected function doTestWorkflows(UserInterface $user, $expected_status) {
     $default_langcode = $this->langcodes[0];
     $languages = $this->container->get('language_manager')->getLanguages();
-    $args = array('@user_label' => $user->getUsername());
+    $args = ['@user_label' => $user->getUsername()];
+    $options = ['language' => $languages[$default_langcode], 'absolute' => TRUE];
     $this->drupalLogin($user);
 
     // Check whether the user is allowed to access the entity form in edit mode.
-    $options = array('language' => $languages[$default_langcode], 'absolute' => TRUE);
     $edit_url = $this->entity->urlInfo('edit-form', $options);
     $this->drupalGet($edit_url, $options);
-    $this->assertResponse($expected_status['edit'], format_string('The @user_label has the expected edit access.', $args));
+    $this->assertResponse($expected_status['edit'], SafeMarkup::format('The @user_label has the expected edit access.', $args));
+
+    // Check whether the user is allowed to access the entity delete form.
+    $delete_url = $this->entity->urlInfo('delete-form', $options);
+    $this->drupalGet($delete_url, $options);
+    $this->assertResponse($expected_status['delete'], SafeMarkup::format('The @user_label has the expected delete access.', $args));
 
     // Check whether the user is allowed to access the translation overview.
     $langcode = $this->langcodes[1];
-    $options = array('language' => $languages[$langcode], 'absolute' => TRUE);
+    $options['language'] = $languages[$langcode];
     $translations_url = $this->entity->url('drupal:content-translation-overview', $options);
     $this->drupalGet($translations_url);
-    $this->assertResponse($expected_status['overview'], format_string('The @user_label has the expected translation overview access.', $args));
+    $this->assertResponse($expected_status['overview'], SafeMarkup::format('The @user_label has the expected translation overview access.', $args));
 
     // Check whether the user is allowed to create a translation.
     $add_translation_url = Url::fromRoute('content_translation.translation_add_' . $this->entityTypeId, [$this->entityTypeId => $this->entity->id(), 'source' => $default_langcode, 'target' => $langcode], $options);
     if ($expected_status['add_translation'] == 200) {
       $this->clickLink('Add');
-      $this->assertUrl($add_translation_url->toString(), array(), 'The translation overview points to the translation form when creating translations.');
+      $this->assertUrl($add_translation_url->toString(), [], 'The translation overview points to the translation form when creating translations.');
       // Check that the translation form does not contain shared elements for
       // translators.
       if ($expected_status['edit'] == 403) {
@@ -144,13 +171,12 @@ protected function assertWorkflows(UserInterface $user, $expected_status) {
     else {
       $this->drupalGet($add_translation_url);
     }
-    $this->assertResponse($expected_status['add_translation'], format_string('The @user_label has the expected translation creation access.', $args));
+    $this->assertResponse($expected_status['add_translation'], SafeMarkup::format('The @user_label has the expected translation creation access.', $args));
 
     // Check whether the user is allowed to edit a translation.
     $langcode = $this->langcodes[2];
     $options['language'] = $languages[$langcode];
     $edit_translation_url = Url::fromRoute('content_translation.translation_edit_' . $this->entityTypeId, [$this->entityTypeId => $this->entity->id(), 'language' => $langcode], $options);
-    $options = ['language' => $languages[$langcode], 'absolute' => TRUE];
     if ($expected_status['edit_translation'] == 200) {
       $this->drupalGet($translations_url);
       $editor = $expected_status['edit'] == 200;
@@ -158,7 +184,6 @@ protected function assertWorkflows(UserInterface $user, $expected_status) {
       if ($editor) {
         $this->clickLink('Edit', 2);
         // An editor should be pointed to the entity form in multilingual mode.
-
         // We need a new expected edit path with a new language.
         $expected_edit_path = $this->entity->url('edit-form', $options);
         $this->assertUrl($expected_edit_path, [], 'The translation overview points to the edit form for editors when editing translations.');
@@ -166,7 +191,7 @@ protected function assertWorkflows(UserInterface $user, $expected_status) {
       else {
         $this->clickLink('Edit');
         // While a translator should be pointed to the translation form.
-        $this->assertUrl($edit_translation_url->toString(), array(), 'The translation overview points to the translation form for translators when editing translations.');
+        $this->assertUrl($edit_translation_url->toString(), [], 'The translation overview points to the translation form for translators when editing translations.');
         // Check that the translation form does not contain shared elements.
         $this->assertNoSharedElements();
       }
@@ -174,7 +199,35 @@ protected function assertWorkflows(UserInterface $user, $expected_status) {
     else {
       $this->drupalGet($edit_translation_url);
     }
-    $this->assertResponse($expected_status['edit_translation'], format_string('The @user_label has the expected translation creation access.', $args));
+    $this->assertResponse($expected_status['edit_translation'], SafeMarkup::format('The @user_label has the expected translation edit access.', $args));
+
+    // Check whether the user is allowed to delete a translation.
+    $langcode = $this->langcodes[2];
+    $options['language'] = $languages[$langcode];
+    $delete_translation_url = Url::fromRoute('content_translation.translation_delete_' . $this->entityTypeId, [$this->entityTypeId => $this->entity->id(), 'language' => $langcode], $options);
+    if ($expected_status['delete_translation'] == 200) {
+      $this->drupalGet($translations_url);
+      $editor = $expected_status['delete'] == 200;
+
+      if ($editor) {
+        $this->clickLink('Delete', 2);
+        // An editor should be pointed to the entity deletion form in
+        // multilingual mode. We need a new expected delete path with a new
+        // language.
+        $expected_delete_path = $this->entity->url('delete-form', $options);
+        $this->assertUrl($expected_delete_path, [], 'The translation overview points to the delete form for editors when deleting translations.');
+      }
+      else {
+        $this->clickLink('Delete');
+        // While a translator should be pointed to the translation deletion
+        // form.
+        $this->assertUrl($delete_translation_url->toString(), [], 'The translation overview points to the translation deletion form for translators when deleting translations.');
+      }
+    }
+    else {
+      $this->drupalGet($delete_translation_url);
+    }
+    $this->assertResponse($expected_status['delete_translation'], SafeMarkup::format('The @user_label has the expected translation deletion access.', $args));
   }
 
   /**
diff --git a/core/modules/content_translation/tests/src/Unit/Access/ContentTranslationManageAccessCheckTest.php b/core/modules/content_translation/tests/src/Unit/Access/ContentTranslationManageAccessCheckTest.php
index 0487d08352b083db04856a99087845d9c7990c46..512e73544e0d9de00dfd2f879cb51a880248ab05 100644
--- a/core/modules/content_translation/tests/src/Unit/Access/ContentTranslationManageAccessCheckTest.php
+++ b/core/modules/content_translation/tests/src/Unit/Access/ContentTranslationManageAccessCheckTest.php
@@ -47,13 +47,17 @@ public function testCreateAccess() {
     // Set the mock language manager.
     $language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
     $language_manager->expects($this->at(0))
+      ->method('getLanguage')
+      ->with($this->equalTo($source))
+      ->will($this->returnValue(new Language(array('id' => 'en'))));
+    $language_manager->expects($this->at(1))
       ->method('getLanguages')
       ->will($this->returnValue(array('en' => array(), 'it' => array())));
-    $language_manager->expects($this->at(1))
+    $language_manager->expects($this->at(2))
       ->method('getLanguage')
       ->with($this->equalTo($source))
       ->will($this->returnValue(new Language(array('id' => 'en'))));
-    $language_manager->expects($this->at(2))
+    $language_manager->expects($this->at(3))
       ->method('getLanguage')
       ->with($this->equalTo($target))
       ->will($this->returnValue(new Language(array('id' => 'it'))));
diff --git a/core/modules/menu_link_content/src/Form/MenuLinkContentDeleteForm.php b/core/modules/menu_link_content/src/Form/MenuLinkContentDeleteForm.php
index dcc416dd6ae36e58fbc8b6226bfee4ab25be9a79..e75d075f2fb9ec98f5f8908629d82f1f055e984f 100644
--- a/core/modules/menu_link_content/src/Form/MenuLinkContentDeleteForm.php
+++ b/core/modules/menu_link_content/src/Form/MenuLinkContentDeleteForm.php
@@ -22,6 +22,13 @@ public function getCancelUrl() {
     return new Url('entity.menu.edit_form', array('menu' => $this->entity->getMenuName()));
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  protected function getRedirectUrl() {
+    return $this->getCancelUrl();
+  }
+
   /**
    * {@inheritdoc}
    */
diff --git a/core/modules/node/src/Form/NodeDeleteForm.php b/core/modules/node/src/Form/NodeDeleteForm.php
index 16c86e6b367e17bb4041f5e82565916d024d6863..0e22c82fea132006988f54c2b020e9e702cb52c0 100644
--- a/core/modules/node/src/Form/NodeDeleteForm.php
+++ b/core/modules/node/src/Form/NodeDeleteForm.php
@@ -8,7 +8,6 @@
 namespace Drupal\node\Form;
 
 use Drupal\Core\Entity\ContentEntityDeleteForm;
-use Drupal\Core\Form\FormStateInterface;
 
 /**
  * Provides a form for deleting a node.
@@ -18,13 +17,34 @@ class NodeDeleteForm extends ContentEntityDeleteForm {
   /**
    * {@inheritdoc}
    */
-  public function submitForm(array &$form, FormStateInterface $form_state) {
-    $this->entity->delete();
-    $this->logger('content')->notice('@type: deleted %title.', array('@type' => $this->entity->bundle(), '%title' => $this->entity->label()));
+  protected function getDeletionMessage() {
+    /** @var \Drupal\node\NodeInterface $entity */
+    $entity = $this->getEntity();
+
     $node_type_storage = $this->entityManager->getStorage('node_type');
-    $node_type = $node_type_storage->load($this->entity->bundle())->label();
-    drupal_set_message(t('@type %title has been deleted.', array('@type' => $node_type, '%title' => $this->entity->label())));
-    $form_state->setRedirect('<front>');
+    $node_type = $node_type_storage->load($entity->bundle())->label();
+
+    if (!$entity->isDefaultTranslation()) {
+      return $this->t('@language translation of the @type %label has been deleted.', [
+        '@language' => $entity->language()->getName(),
+        '@type' => $node_type,
+        '%label' => $entity->label(),
+      ]);
+    }
+
+    return $this->t('The @type %title has been deleted.', array(
+      '@type' => $node_type,
+      '%title' => $this->getEntity()->label(),
+    ));
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function logDeletionMessage() {
+    /** @var \Drupal\node\NodeInterface $entity */
+    $entity = $this->getEntity();
+    $this->logger('content')->notice('@type: deleted %title.', ['@type' => $entity->getType(), '%title' => $entity->label()]);
   }
 
 }
diff --git a/core/modules/shortcut/src/Form/ShortcutDeleteForm.php b/core/modules/shortcut/src/Form/ShortcutDeleteForm.php
index 2b0ebb2222e26c49e437419841272c2945cda4d8..b3864cd5dff6c9c4f4f592b09252860ed4c6f626 100644
--- a/core/modules/shortcut/src/Form/ShortcutDeleteForm.php
+++ b/core/modules/shortcut/src/Form/ShortcutDeleteForm.php
@@ -31,4 +31,11 @@ public function getCancelUrl() {
     ));
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  protected function getRedirectUrl() {
+    return $this->getCancelUrl();
+  }
+
 }
diff --git a/core/modules/system/src/Tests/Entity/EntityFormTest.php b/core/modules/system/src/Tests/Entity/EntityFormTest.php
index 307b69a2dbce4249d95add7bd67fbaeb121b983d..e4365ebe977b11b8555bf742826c329b9b4904e0 100644
--- a/core/modules/system/src/Tests/Entity/EntityFormTest.php
+++ b/core/modules/system/src/Tests/Entity/EntityFormTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\system\Tests\Entity;
 
+use Drupal\language\Entity\ConfigurableLanguage;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -27,6 +28,9 @@ protected function setUp() {
     parent::setUp();
     $web_user = $this->drupalCreateUser(array('administer entity_test content'));
     $this->drupalLogin($web_user);
+
+    // Add a language.
+    ConfigurableLanguage::createFromLangcode('ro')->save();
   }
 
   /**
@@ -39,6 +43,16 @@ function testFormCRUD() {
     }
   }
 
+  /**
+   * Tests basic multilingual form CRUD functionality.
+   */
+  public function testMultilingualFormCRUD() {
+    // All entity variations have to have the same results.
+    foreach (entity_test_entity_types(ENTITY_TEST_TYPES_MULTILINGUAL) as $entity_type) {
+      $this->doTestMultilingualFormCRUD($entity_type);
+    }
+  }
+
   /**
    * Tests hook_entity_form_display_alter().
    *
@@ -84,13 +98,55 @@ protected function doTestFormCRUD($entity_type) {
     $this->assertFalse($entity, format_string('%entity_type: Entity not found in the database.', array('%entity_type' => $entity_type)));
   }
 
+  /**
+   * Executes the multilingual form CRUD tests for the given entity type ID.
+   *
+   * @param string $entity_type_id
+   *   The ID of entity type to run the tests with.
+   */
+  protected function doTestMultilingualFormCRUD($entity_type_id) {
+    $name1 = $this->randomMachineName(8);
+    $name1_ro = $this->randomMachineName(9);
+    $name2_ro = $this->randomMachineName(11);
+
+    $edit = array(
+      'name[0][value]' => $name1,
+      'field_test_text[0][value]' => $this->randomMachineName(16),
+    );
+
+    $this->drupalPostForm($entity_type_id . '/add', $edit, t('Save'));
+    $entity = $this->loadEntityByName($entity_type_id, $name1);
+    $this->assertTrue($entity, format_string('%entity_type: Entity found in the database.', array('%entity_type' => $entity_type_id)));
+
+    // Add a translation to the newly created entity without using the Content
+    // translation module.
+    $entity->addTranslation('ro', ['name' => $name1_ro])->save();
+    $translated_entity = $this->loadEntityByName($entity_type_id, $name1)->getTranslation('ro');
+    $this->assertEqual($translated_entity->name->value, $name1_ro, format_string('%entity_type: The translation has been added.', array('%entity_type' => $entity_type_id)));
+
+    $edit['name[0][value]'] = $name2_ro;
+    $this->drupalPostForm('ro/' . $entity_type_id . '/manage/' . $entity->id(), $edit, t('Save'));
+    $translated_entity = $this->loadEntityByName($entity_type_id, $name1)->getTranslation('ro');
+    $this->assertTrue($translated_entity, format_string('%entity_type: Modified translation found in the database.', array('%entity_type' => $entity_type_id)));
+    $this->assertEqual($translated_entity->name->value, $name2_ro, format_string('%entity_type: The name of the translation has been modified.', array('%entity_type' => $entity_type_id)));
+
+    $this->drupalGet('ro/' . $entity_type_id . '/manage/' . $entity->id());
+    $this->clickLink(t('Delete'));
+    $this->drupalPostForm(NULL, array(), t('Delete Romanian translation'));
+    $entity = $this->loadEntityByName($entity_type_id, $name1);
+    $this->assertNotNull($entity, format_string('%entity_type: The original entity still exists.', array('%entity_type' => $entity_type_id)));
+    $this->assertFalse($entity->hasTranslation('ro'), format_string('%entity_type: Entity translation does not exist anymore.', array('%entity_type' => $entity_type_id)));
+  }
+
   /**
    * Loads a test entity by name always resetting the storage cache.
    */
   protected function loadEntityByName($entity_type, $name) {
     // Always load the entity from the database to ensure that changes are
     // correctly picked up.
-    $this->container->get('entity.manager')->getStorage($entity_type)->resetCache();
-    return current(entity_load_multiple_by_properties($entity_type, array('name' => $name)));
+    $entity_storage = $this->container->get('entity.manager')->getStorage($entity_type);
+    $entity_storage->resetCache();
+    $entities = $entity_storage->loadByProperties(array('name' => $name));
+    return $entities ? current($entities) : NULL;
   }
 }
diff --git a/core/modules/taxonomy/src/Form/TermDeleteForm.php b/core/modules/taxonomy/src/Form/TermDeleteForm.php
index 1552fd3622db99f50de67035195336b1e0718be5..17019465429838b9bd1f5898ac61e3acf4ddad12 100644
--- a/core/modules/taxonomy/src/Form/TermDeleteForm.php
+++ b/core/modules/taxonomy/src/Form/TermDeleteForm.php
@@ -19,24 +19,17 @@ class TermDeleteForm extends ContentEntityDeleteForm {
   /**
    * {@inheritdoc}
    */
-  public function getFormId() {
-    return 'taxonomy_term_confirm_delete';
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getQuestion() {
-    return $this->t('Are you sure you want to delete the term %title?', array('%title' => $this->entity->getName()));
+  public function getCancelUrl() {
+    // The cancel URL is the vocabulary collection, terms have no global
+    // list page.
+    return new Url('entity.taxonomy_vocabulary.collection');
   }
 
   /**
    * {@inheritdoc}
    */
-  public function getCancelUrl() {
-    // The cancel URL is the vocabulary collection, terms have no global
-    // list page.
-    return new Url('entity.taxonomy_vocabulary.collection');
+  protected function getRedirectUrl() {
+    return $this->getCancelUrl();
   }
 
   /**
@@ -59,12 +52,15 @@ protected function getDeletionMessage() {
   public function submitForm(array &$form, FormStateInterface $form_state) {
     parent::submitForm($form, $form_state);
 
-    $storage = $this->entityManager->getStorage('taxonomy_vocabulary');
-    $vocabulary = $storage->load($this->entity->bundle());
-
-    // @todo Move to storage https://www.drupal.org/node/1988712.
-    taxonomy_check_vocabulary_hierarchy($vocabulary, array('tid' => $this->entity->id()));
+    /** @var \Drupal\Core\Entity\ContentEntityInterface $term */
+    $term = $this->getEntity();
+    if ($term->isDefaultTranslation()) {
+      $storage = $this->entityManager->getStorage('taxonomy_vocabulary');
+      $vocabulary = $storage->load($this->entity->bundle());
 
+      // @todo Move to storage http://drupal.org/node/1988712
+      taxonomy_check_vocabulary_hierarchy($vocabulary, array('tid' => $term->id()));
+    }
   }
 
 }
diff --git a/core/modules/taxonomy/src/Tests/VocabularyPermissionsTest.php b/core/modules/taxonomy/src/Tests/VocabularyPermissionsTest.php
index f6c7480b78e327bb6bab9b20db8307104e9063bf..00df7896ad7f3b9badd59a8aa620a1b00b7ad028 100644
--- a/core/modules/taxonomy/src/Tests/VocabularyPermissionsTest.php
+++ b/core/modules/taxonomy/src/Tests/VocabularyPermissionsTest.php
@@ -51,7 +51,7 @@ function testVocabularyPermissionsTaxonomyTerm() {
 
     // Delete the vocabulary.
     $this->drupalGet('taxonomy/term/' . $term->id() . '/delete');
-    $this->assertRaw(t('Are you sure you want to delete the term %name?', array('%name' => $edit['name[0][value]'])), 'Delete taxonomy term form opened successfully.');
+    $this->assertRaw(t('Are you sure you want to delete the @entity-type %label?', array('@entity-type' => 'taxonomy term', '%label' => $edit['name[0][value]'])), 'Delete taxonomy term form opened successfully.');
 
     // Confirm deletion.
     $this->drupalPostForm(NULL, NULL, t('Delete'));
@@ -98,7 +98,7 @@ function testVocabularyPermissionsTaxonomyTerm() {
 
     // Delete the vocabulary.
     $this->drupalGet('taxonomy/term/' . $term->id() . '/delete');
-    $this->assertRaw(t('Are you sure you want to delete the term %name?', array('%name' => $term->getName())), 'Delete taxonomy term form opened successfully.');
+    $this->assertRaw(t('Are you sure you want to delete the @entity-type %label?', array('@entity-type' => 'taxonomy term', '%label' => $term->getName())), 'Delete taxonomy term form opened successfully.');
 
     // Confirm deletion.
     $this->drupalPostForm(NULL, NULL, t('Delete'));