diff --git a/core/lib/Drupal/Core/Entity/Annotation/EntityType.php b/core/lib/Drupal/Core/Entity/Annotation/EntityType.php
index b46d9f71cb7783d9b88c8a84cd13255cbd321ebd..0171dd58826a17eb02fe30953e8d62cc67492f7a 100644
--- a/core/lib/Drupal/Core/Entity/Annotation/EntityType.php
+++ b/core/lib/Drupal/Core/Entity/Annotation/EntityType.php
@@ -236,37 +236,6 @@ class EntityType extends Plugin {
    */
   public $route_base_path;
 
-  /**
-   * The base menu router path to which the entity admin user interface responds.
-   *
-   * It can be used to generate UI links and to attach additional router items
-   * to the entity UI in a generic fashion.
-   *
-   * @var string (optional)
-   */
-  public $menu_base_path;
-
-  /**
-   * The menu router path to be used to view the entity.
-   *
-   * @var string (optional)
-   */
-  public $menu_view_path;
-
-  /**
-   * The menu router path to be used to edit the entity.
-   *
-   * @var string (optional)
-   */
-  public $menu_edit_path;
-
-  /**
-   * A string identifying the menu loader in the router path.
-   *
-   * @var string (optional)
-   */
-  public $menu_path_wildcard;
-
   /**
    * Link templates using the URI template syntax.
    *
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php
index 2baf1fe61efb4117b67e26876e3bba58c0107d5b..426b479cbb540dc51adf265e09137e51e8843374 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Entity/CustomBlock.php
@@ -37,8 +37,10 @@
  *   base_table = "custom_block",
  *   revision_table = "custom_block_revision",
  *   route_base_path = "admin/structure/block/custom-blocks/manage/{bundle}",
- *   menu_base_path = "block/%custom_block",
- *   menu_edit_path = "block/%custom_block",
+ *   links = {
+ *     "canonical" = "/block/{custom_block}",
+ *     "edit-form" = "/block/{custom_block}"
+ *   },
  *   fieldable = TRUE,
  *   translatable = TRUE,
  *   entity_keys = {
@@ -174,19 +176,6 @@ protected function init() {
     unset($this->new);
   }
 
-  /**
-   * {@inheritdoc}
-   */
-  public function uri() {
-    return array(
-      'path' => 'block/' . $this->id(),
-      'options' => array(
-        'entity_type' => $this->entityType,
-        'entity' => $this,
-      )
-    );
-  }
-
   /**
    * {@inheritdoc}
    */
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentTranslationUITest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentTranslationUITest.php
index 7944fddda2e203ae98284d8a264d1362ab942585..a90be4a7f9b56960a4c6165c87ae9fa00336835c 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentTranslationUITest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentTranslationUITest.php
@@ -68,7 +68,7 @@ function setupBundle() {
    * Overrides \Drupal\content_translation\Tests\ContentTranslationUITest::getTranslatorPermission().
    */
   protected function getTranslatorPermissions() {
-    return array_merge(parent::getTranslatorPermissions(), array('post comments', 'administer comments'));
+    return array_merge(parent::getTranslatorPermissions(), array('post comments', 'administer comments', 'access comments'));
   }
 
   /**
@@ -134,11 +134,11 @@ protected function assertPublishedStatus() {
     $languages = language_list();
 
     // Check that simple users cannot see unpublished field translations.
-    $path = $this->controller->getViewPath($entity);
+    $uri = $entity->uri();
     foreach ($this->langcodes as $index => $langcode) {
       $translation = $this->getTranslation($entity, $langcode);
       $value = $this->getValue($translation, 'comment_body', $langcode);
-      $this->drupalGet($path, array('language' => $languages[$langcode]));
+      $this->drupalGet($uri['path'], array('language' => $languages[$langcode]));
       if ($index > 0) {
         $this->assertNoRaw($value, 'Unpublished field translation is not shown.');
       }
diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module
index 66bb5006c183b0361bf340f08184eedc8909726b..ae4a86e6b9660e2f2c2b57c215cd9ca7d115535c 100644
--- a/core/modules/content_translation/content_translation.module
+++ b/core/modules/content_translation/content_translation.module
@@ -78,40 +78,33 @@ function content_translation_language_types_info_alter(array &$language_types) {
 function content_translation_entity_info_alter(array &$entity_info) {
   // Provide defaults for translation info.
   foreach ($entity_info as $entity_type => &$info) {
-    if (empty($info['translatable'])) {
-      continue;
-    }
+    if (!empty($info['translatable'])) {
+      // Every fieldable entity type must have a translation controller class,
+      // no matter if it is enabled for translation or not. As a matter of fact
+      // we might need it to correctly switch field translatability when a field
+      // is shared accross different entities.
+      $info['controllers'] += array('translation' => 'Drupal\content_translation\ContentTranslationController');
+
+      if (!isset($info['translation']['content_translation'])) {
+        $info['translation']['content_translation'] = array();
+      }
 
-    if (!isset($info['translation']['content_translation'])) {
-      $info['translation']['content_translation'] = array();
-    }
+      if (!empty($info['links']['canonical'])) {
+        // Provide default links for the translation paths.
+        $info['links'] += array(
+          'drupal:content-translation-overview' => $info['links']['canonical'] . '/translations',
+        );
 
-    // Every fieldable entity type must have a translation controller class, no
-    // matter if it is enabled for translation or not. As a matter of fact we
-    // might need it to correctly switch field translatability when a field is
-    // shared accross different entities.
-    $info['controllers'] += array('translation' => 'Drupal\content_translation\ContentTranslationController');
-
-    // If no menu base path is provided we default to the usual
-    // "entity_type/%entity_type" pattern.
-    if (!isset($info['menu_base_path'])) {
-      $path = "$entity_type/%$entity_type";
-      $info['menu_base_path'] = $path;
+        $parts = explode('/', trim($info['links']['canonical'], '/'));
+        $entity_position = array_search("{{$entity_type}}", $parts);
+        if ($entity_position !== FALSE) {
+          $info['translation']['content_translation'] += array(
+            'access_callback' => 'content_translation_translate_access',
+            'access_arguments' => array($entity_position),
+          );
+        }
+      }
     }
-
-    $path = $info['menu_base_path'];
-
-    $info += array(
-      'menu_view_path' => $path,
-      'menu_edit_path' => "$path/edit",
-      'menu_path_wildcard' => "%$entity_type",
-    );
-
-    $entity_position = count(explode('/', $path)) - 1;
-    $info['translation']['content_translation'] += array(
-      'access_callback' => 'content_translation_translate_access',
-      'access_arguments' => array($entity_position),
-    );
   }
 }
 
@@ -156,6 +149,9 @@ function content_translation_entity_field_info_alter(&$info, $entity_type) {
 
 /**
  * Implements hook_menu().
+ *
+ * @todo Split this into route definition and menu link definition. See
+ *   https://drupal.org/node/1987882 and https://drupal.org/node/2047633.
  */
 function content_translation_menu() {
   $items = array();
@@ -164,7 +160,7 @@ function content_translation_menu() {
   foreach (entity_get_info() as $entity_type => $info) {
     // Provide the translation UI only for enabled types.
     if (content_translation_enabled($entity_type)) {
-      $path = $info['menu_base_path'];
+      $path = _content_translation_link_to_router_path($entity_type, $info['links']['canonical']);
       $entity_position = count(explode('/', $path)) - 1;
       $keys = array_flip(array('theme_callback', 'theme_arguments', 'access_callback', 'access_arguments', 'load_arguments'));
       $menu_info = array_intersect_key($info['translation']['content_translation'], $keys) + array('file' => 'content_translation.pages.inc');
@@ -228,6 +224,9 @@ function content_translation_menu() {
 
 /**
  * Implements hook_menu_alter().
+ *
+ * @todo Split this into route definition and menu link definition. See
+ *   https://drupal.org/node/1987882 and https://drupal.org/node/2047633.
  */
 function content_translation_menu_alter(array &$items) {
   // Clarify where translation settings are located.
@@ -237,7 +236,7 @@ function content_translation_menu_alter(array &$items) {
   // Check that the declared menu base paths are actually valid.
   foreach (entity_get_info() as $entity_type => $info) {
     if (content_translation_enabled($entity_type)) {
-      $path = $info['menu_base_path'];
+      $path = _content_translation_link_to_router_path($entity_type, $info['links']['canonical']);
 
       // If the base path is not defined we cannot provide the translation UI
       // for this entity type. In some cases the actual base path might not have
@@ -255,7 +254,7 @@ function content_translation_menu_alter(array &$items) {
       }
       else {
         $entity_position = count(explode('/', $path)) - 1;
-        $edit_path = $info['menu_edit_path'];
+        $edit_path = $path . '/edit';
 
         if (isset($items[$edit_path])) {
           // If the edit path is a default local task we need to find the parent
@@ -286,6 +285,24 @@ function content_translation_local_tasks_alter(&$local_tasks) {
   $derivative->alterLocalTasks($local_tasks);
 }
 
+/**
+ * Convert an entity canonical link to a router path.
+ *
+ * @param string $link
+ *   The entity link to be converted.
+ *
+ * @return string
+ *   The resulting router path. For instance "/node/{node}" is turned into
+ *   "node/%node".
+ *
+ * @todo Remove this and use the actual link values when all the Content
+ *   Translation code is adapted to the new routing system.
+ */
+function _content_translation_link_to_router_path($entity_type, $link) {
+  $path = preg_replace('|{([^}]+)}|', '%$1', trim($link, '/'));
+  return str_replace('%id', '%' . $entity_type, $path);
+}
+
 /**
  * Strips out menu loaders from the given path.
  *
@@ -482,9 +499,8 @@ function content_translation_set_config($entity_type, $bundle, $setting, $value)
  */
 function content_translation_enabled($entity_type, $bundle = NULL) {
   $enabled = FALSE;
-  $info = entity_get_info($entity_type);
 
-  if (!empty($info['translatable'])) {
+  if (content_translation_supported($entity_type)) {
     $bundles = !empty($bundle) ? array($bundle) : array_keys(entity_get_bundles($entity_type));
     foreach ($bundles as $bundle) {
       if (content_translation_get_config($entity_type, $bundle, 'enabled')) {
@@ -498,31 +514,17 @@ function content_translation_enabled($entity_type, $bundle = NULL) {
 }
 
 /**
- * Returns all the translatable entity types.
+ * Checks whether an entity type supports translation.
+ *
+ * @param string $entity_type
+ *   The entity type.
  *
- * @return array
- *   An array of entity types keyed by entity type.
+ * @return bool
+ *   TRUE if an entity type is supported, FALSE otherwise.
  */
-function content_translation_types_translatable() {
-  $entity_types = &drupal_static(__FUNCTION__, array());
-
-  foreach (entity_get_info() as $entity_type => $info) {
-    if (content_translation_enabled($entity_type)) {
-      // Lazy load router items.
-      if (!isset($items)) {
-        $items = menu_get_router();
-      }
-      // Check whether the required paths are defined. We need to strip out the
-      // menu loader and replace it with a plain "%" as router items have no
-      // menu loader in them.
-      $path = _content_translation_menu_strip_loaders($info['menu_base_path']);
-      if (!empty($items[$path]) && !empty($items[$path . '/translations'])) {
-        $entity_types[$entity_type] = $entity_type;
-      }
-    }
-  }
-
-  return $entity_types;
+function content_translation_supported($entity_type) {
+  $info = entity_get_info($entity_type);
+  return !empty($info['translatable']) && !empty($info['links']['drupal:content-translation-overview']);
 }
 
 /**
diff --git a/core/modules/content_translation/content_translation.pages.inc b/core/modules/content_translation/content_translation.pages.inc
index f64599f86413ffb3835b18a59dc6c44f3079a7f3..b6473ae8c6548b5c51fa150ba0e98562e4b229e6 100644
--- a/core/modules/content_translation/content_translation.pages.inc
+++ b/core/modules/content_translation/content_translation.pages.inc
@@ -25,14 +25,21 @@ function content_translation_overview(EntityInterface $entity) {
   $translations = $entity->getTranslationLanguages();
   $field_ui = \Drupal::moduleHandler()->moduleExists('field_ui') && user_access('administer ' . $entity->entityType() . ' fields');
 
-  $path = $controller->getViewPath($entity);
-  $base_path = $controller->getBasePath($entity);
-  $edit_path = $controller->getEditPath($entity);
+  $rel = array();
+  foreach (array('canonical', 'edit-form', 'drupal:content-translation-overview') as $name) {
+    $rel[$name] = $entity->uri($name);
+  }
 
   $header = array(t('Language'), t('Translation'), t('Source language'), t('Status'), t('Operations'));
   $rows = array();
 
   if (language_multilingual()) {
+    // If we have a view path defined for the current entity get the switch
+    // links based on it.
+    if (!empty($rel['canonical'])) {
+      $links = _content_translation_get_switch_links($rel['canonical']['path']);
+    }
+
     // Determine whether the current entity is translatable.
     $translatable = FALSE;
     foreach (field_info_instances($entity->entityType(), $entity->bundle()) as $instance) {
@@ -45,16 +52,14 @@ function content_translation_overview(EntityInterface $entity) {
     foreach ($languages as $language) {
       $language_name = $language->name;
       $langcode = $language->id;
-      $add_path = $base_path . '/translations/add/' . $original . '/' . $langcode;
-      $translate_path = $base_path . '/translations/edit/' . $langcode;
-      $delete_path = $base_path . '/translations/delete/' . $langcode;
-
-      if ($base_path) {
-        $add_links = _content_translation_get_switch_links($add_path);
-        $edit_links = _content_translation_get_switch_links($edit_path);
-        $translate_links = _content_translation_get_switch_links($translate_path);
-        $delete_links = _content_translation_get_switch_links($delete_path);
-      }
+
+      $add_path = $rel['drupal:content-translation-overview']['path'] . '/add/' . $original . '/' . $langcode;
+      $translate_path = $rel['drupal:content-translation-overview']['path'] . '/edit/' . $langcode;
+
+      $add_links = _content_translation_get_switch_links($add_path);
+      $edit_links = _content_translation_get_switch_links($rel['edit-form']['path']);
+      $translate_links = _content_translation_get_switch_links($translate_path);
+      $delete_links = _content_translation_get_switch_links($rel['drupal:content-translation-overview']['path'] . '/delete/' . $langcode);
 
       $operations = array(
         'data' => array(
@@ -69,7 +74,7 @@ function content_translation_overview(EntityInterface $entity) {
         $source = isset($entity->translation[$langcode]['source']) ? $entity->translation[$langcode]['source'] : '';
         $is_original = $langcode == $original;
         $label = $entity->getTranslation($langcode)->label();
-        $link = isset($links->links[$langcode]['href']) ? $links->links[$langcode] : array('href' => $path, 'language' => $language);
+        $link = isset($links->links[$langcode]['href']) ? $links->links[$langcode] : array('href' => $rel['canonical']['path'], 'language' => $language);
         $row_title = l($label, $link['href'], $link);
 
         if (empty($link['href'])) {
@@ -79,8 +84,8 @@ function content_translation_overview(EntityInterface $entity) {
         // 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 ($edit_path && $entity->access('update')) {
-          $links['edit'] = isset($edit_links->links[$langcode]['href']) ? $edit_links->links[$langcode] : array('href' => $edit_path, 'language' => $language);
+        if ($entity->access('update')) {
+          $links['edit'] = isset($edit_links->links[$langcode]['href']) ? $edit_links->links[$langcode] : array('href' => $rel['edit-form']['path'], 'language' => $language);
         }
         elseif (!$is_original && $controller->getTranslationAccess($entity, 'update')) {
           $links['edit'] = isset($translate_links->links[$langcode]['href']) ? $translate_links->links[$langcode] : array('href' => $translate_path, 'language' => $language);
@@ -246,11 +251,12 @@ function content_translation_prepare_translation(EntityInterface $entity, Langua
  */
 function content_translation_delete_confirm(array $form, array $form_state, EntityInterface $entity, Language $language) {
   $controller = content_translation_controller($entity->entityType());
+  $uri = $entity->uri('drupal:content-translation-overview');
 
   return confirm_form(
     $form,
     t('Are you sure you want to delete the @language translation of %label?', array('@language' => $language->name, '%label' => $entity->label())),
-    $controller->getEditPath($entity),
+    $uri['path'],
     t('This action cannot be undone.'),
     t('Delete'),
     t('Cancel')
@@ -271,9 +277,11 @@ function content_translation_delete_confirm_submit(array $form, array &$form_sta
   // Remove any existing path alias for the removed translation.
   // @todo This should be taken care of by the Path module.
   if (\Drupal::moduleHandler()->moduleExists('path')) {
-    $conditions = array('source' => $controller->getViewPath($entity), 'langcode' => $language->id);
+    $uri = $entity->uri();
+    $conditions = array('source' => $uri['path'], 'langcode' => $language->id);
     \Drupal::service('path.crud')->delete($conditions);
   }
 
-  $form_state['redirect'] = $controller->getBasePath($entity) . '/translations';
+  $uri = $entity->uri('drupal:content-translation-overview');
+  $form_state['redirect'] = $uri['path'];
 }
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php b/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php
index c4998068fe6f3ceaa2cdbf32e83fc12e9cb01b4d..a639f30f7188a519b614d91a8aeced6bc64c30c7 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php
@@ -53,27 +53,6 @@ public function retranslate(EntityInterface $entity, $langcode = NULL) {
     }
   }
 
-  /**
-   * Implements ContentTranslationControllerInterface::getBasePath().
-   */
-  public function getBasePath(EntityInterface $entity) {
-    return $this->getPathInstance($this->entityInfo['menu_base_path'], $entity->id());
-  }
-
-  /**
-   * Implements ContentTranslationControllerInterface::getEditPath().
-   */
-  public function getEditPath(EntityInterface $entity) {
-    return isset($this->entityInfo['menu_edit_path']) ? $this->getPathInstance($this->entityInfo['menu_edit_path'], $entity->id()) : FALSE;
-  }
-
-  /**
-   * Implements ContentTranslationControllerInterface::getViewPath().
-   */
-  public function getViewPath(EntityInterface $entity) {
-    return isset($this->entityInfo['menu_view_path']) ? $this->getPathInstance($this->entityInfo['menu_view_path'], $entity->id()) : FALSE;
-  }
-
   /**
    * Implements ContentTranslationControllerInterface::getTranslationAccess().
    */
@@ -446,7 +425,9 @@ public function entityFormSourceChange($form, &$form_state) {
     $form_controller = content_translation_form_controller($form_state);
     $entity = $form_controller->getEntity();
     $source = $form_state['values']['source_langcode']['source'];
-    $path = $this->getBasePath($entity) . '/translations/add/' . $source . '/' . $form_controller->getFormLangcode($form_state);
+
+    $uri = $entity->uri('drupal:content-translation-overview');
+    $path = $uri['path'] . '/add/' . $source . '/' . $form_controller->getFormLangcode($form_state);
     $form_state['redirect'] = $path;
     $languages = language_list();
     drupal_set_message(t('Source language set to: %language', array('%language' => $languages[$source]->name)));
@@ -473,9 +454,9 @@ function entityFormDelete($form, &$form_state) {
   function entityFormDeleteTranslation($form, &$form_state) {
     $form_controller = content_translation_form_controller($form_state);
     $entity = $form_controller->getEntity();
-    $base_path = $this->getBasePath($entity);
+    $uri = $entity->uri('drupal:content-translation-overview');
     $form_langcode = $form_controller->getFormLangcode($form_state);
-    $form_state['redirect'] = $base_path . '/translations/delete/' . $form_langcode;
+    $form_state['redirect'] = $uri['path'] . '/delete/' . $form_langcode;
   }
 
   /**
@@ -488,17 +469,4 @@ protected function entityFormTitle(EntityInterface $entity) {
     return $entity->label();
   }
 
-  /**
-   * Returns an instance of the given path.
-   *
-   * @param $path
-   *   An internal path containing the entity id wildcard.
-   *
-   * @return string
-   *   The instantiated path.
-   */
-  protected function getPathInstance($path, $entity_id) {
-    $wildcard = $this->entityInfo['menu_path_wildcard'];
-    return str_replace($wildcard, $entity_id, $path);
-  }
 }
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationControllerInterface.php b/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationControllerInterface.php
index d2d3c2c18edad4433fa0bb7f2d63f0c478f19b7b..ed68e8f34135835dee0c72c4ca06197105a340cb 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationControllerInterface.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationControllerInterface.php
@@ -22,95 +22,26 @@
  * To make Content Translation automatically support an entity type some keys
  * may need to be defined, but none of them is required unless the entity path
  * is different from ENTITY_TYPE/%ENTITY_TYPE (e.g. taxonomy/term/1), in which
- * case at least the 'menu_base_path' key must be defined. This is used to
- * determine the view and edit paths if they follow the standard path patterns.
- * Otherwise the 'menu_view_path' and 'menu_edit_path' keys must be defined. If
- * an entity type is enabled for translation and no menu path key is defined,
- * the following defaults will be assumed:
- * - menu_base_path: ENTITY_TYPE/%ENTITY_TYPE
- * - menu_view_path: ENTITY_TYPE/%ENTITY_TYPE
- * - menu_edit_path: ENTITY_TYPE/%ENTITY_TYPE/edit
- * The menu base path is also used to reliably alter menu router information to
- * provide the translation overview page for any entity.
- * If the entity uses a menu loader different from %ENTITY_TYPE also the 'menu
- * path wildcard' info key needs to be defined.
+ * case at least the 'canonical' key in the 'links' entity info property must be
+ * defined.
  *
  * Every entity type needs a translation controller to be translated. This can
  * be specified through the "controllers['translation']" key in the entity
  * info. If an entity type is enabled for translation and no translation
- * controller is defined, Drupal\content_translation\ContentTranslationController
- * will be assumed. Every translation controller class must implement
- * Drupal\content_translation\ContentTranslationControllerInterface.
+ * controller is defined,
+ * \Drupal\content_translation\ContentTranslationController will be assumed.
+ * Every translation controller class must implement
+ * \Drupal\content_translation\ContentTranslationControllerInterface.
  *
  * If the entity paths match the default patterns above and there is no need for
  * an entity-specific translation controller class, Content Translation will
  * provide built-in support for the entity. It will still be required to enable
  * translation for each translatable bundle.
  *
- * Additionally some more entity info keys can be defined to further customize
- * the translation UI. The content translation info is an associative array that
- * has to match the following structure. Two nested arrays keyed respectively
- * by the 'translation' key and the 'content_translation' key. Elements:
- * - access callback: The access callback for the translation pages. Defaults to
- *   'entity_translation_translate_access'.
- * - access arguments: The access arguments for the translation pages. By
- *   default only the entity object is passed to the access callback.
- *
- * This is how entity info would look for a module defining a new translatable
- * entity type:
- * @code
- *   function mymodule_entity_info_alter(array &$info) {
- *     $info['myentity'] += array(
- *       'menu_base_path' => 'mymodule/myentity/%my_entity_loader',
- *       'menu_path_wildcard' => '%my_entity_loader',
- *       'translation' => array(
- *         'content_translation' => array(
- *           'access_callback' => 'mymodule_myentity_translate_access',
- *           'access_arguments' => array(2),
- *         ),
- *       ),
- *     );
- *     $info['myentity']['controllers'] += array('translation' => 'Drupal\mymodule\MyContentTranslationController');
- *   }
- * @endcode
- *
  * @see \Drupal\Core\Entity\EntityManagerInterface
  */
 interface ContentTranslationControllerInterface {
 
-  /**
-   * Returns the base path for the current entity.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity to the path should refer to.
-   *
-   * @return string
-   *   The entity base path.
-   */
-  public function getBasePath(EntityInterface $entity);
-
-  /**
-   * Returns the path of the entity edit form.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity to the path should refer to.
-   *
-   * @return string
-   *   The entity edit path.
-   */
-  public function getEditPath(EntityInterface $entity);
-
-  /**
-   * Returns the path of the entity view page.
-   *
-   * @param \Drupal\Core\Entity\EntityInterface $entity
-   *   The entity to the path should refer to.
-   *
-   * @return string
-   *   The entity view path.
-   */
-  public function getViewPath(EntityInterface $entity);
-
   /**
    * Checks if the user can perform the given operation on translations of the
    * wrapped entity.
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Plugin/Derivative/ContentTranslationLocalTasks.php b/core/modules/content_translation/lib/Drupal/content_translation/Plugin/Derivative/ContentTranslationLocalTasks.php
index 21c352ae2e7b02ad0934b66635dd671c87d595d1..f7da9a71f6cbe07fa5f2e2c26f6dd54ddc2881f3 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/Plugin/Derivative/ContentTranslationLocalTasks.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/Plugin/Derivative/ContentTranslationLocalTasks.php
@@ -72,7 +72,7 @@ public static function create(ContainerInterface $container, $base_plugin_id) {
   public function getDerivativeDefinitions(array $base_plugin_definition) {
     // Create tabs for all possible entity types.
     foreach ($this->entityManager->getDefinitions() as $entity_type => $entity_info) {
-      if ($entity_info['translatable'] && isset($entity_info['translation'])) {
+      if (!empty($entity_info['translatable'])) {
         // Find the route name for the translation overview.
         $translation_route_name = "content_translation.translation_overview_$entity_type";
         $translation_tab = $translation_route_name;
@@ -92,8 +92,8 @@ public function getDerivativeDefinitions(array $base_plugin_definition) {
    */
   public function alterLocalTasks(array &$local_tasks) {
     foreach ($this->entityManager->getDefinitions() as $entity_type => $entity_info) {
-      if ($entity_info['translatable'] && isset($entity_info['translation'])) {
-        $path = '/' . preg_replace('/%(.*)/', '{$1}', $entity_info['menu_base_path']);
+      if (!empty($entity_info['translatable']) && !empty($entity_info['links']['canonical'])) {
+        $path = $entity_info['links']['canonical'];
         if ($routes = $this->routeProvider->getRoutesByPattern($path)->all()) {
           // Find the route name for the entity page.
           $entity_route_name = key($routes);
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Routing/ContentTranslationRouteSubscriber.php b/core/modules/content_translation/lib/Drupal/content_translation/Routing/ContentTranslationRouteSubscriber.php
index 63033d4eecf3437c0a2a73dff2c14afb412b3e05..368dc273db4d1148bc8e001459ef529990788938 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/Routing/ContentTranslationRouteSubscriber.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/Routing/ContentTranslationRouteSubscriber.php
@@ -39,8 +39,9 @@ public function __construct(EntityManagerInterface $entityManager) {
    */
   protected function routes(RouteCollection $collection) {
     foreach ($this->entityManager->getDefinitions() as $entity_type => $entity_info) {
-      if ($entity_info['translatable'] && isset($entity_info['translation'])) {
-        $path = '/' . str_replace($entity_info['menu_path_wildcard'], '{' . $entity_type . '}', $entity_info['menu_base_path']) . '/translations';
+      if (!empty($entity_info['translatable']) && !empty($entity_info['links']['drupal:content-translation-overview'])) {
+        $path = $entity_info['links']['drupal:content-translation-overview'];
+
         $route = new Route(
          $path,
           array(
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationUITest.php b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationUITest.php
index c24e064b6c031e461b6334028582c8b3e245bb30..8a1b1983874cd142c69905cfa58d48eb63870047 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationUITest.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationUITest.php
@@ -52,6 +52,9 @@ protected function doTestBasicTranslation() {
     $this->entityId = $this->createEntity($values[$default_langcode], $default_langcode);
     $entity = entity_load($this->entityType, $this->entityId, TRUE);
     $this->assertTrue($entity, 'Entity found in the database.');
+    $uri = $entity->uri();
+    $this->drupalGet($uri['path']);
+    $this->assertResponse(200, 'Entity URL is valid.');
 
     $translation = $this->getTranslation($entity, $default_langcode);
     foreach ($values[$default_langcode] as $property => $value) {
@@ -65,8 +68,8 @@ protected function doTestBasicTranslation() {
     $langcode = 'it';
     $values[$langcode] = $this->getNewEntityValues($langcode);
 
-    $base_path = $this->controller->getBasePath($entity);
-    $path = $langcode . '/' . $base_path . '/translations/add/' . $default_langcode . '/' . $langcode;
+    $uri = $entity->uri('drupal:content-translation-overview');
+    $path = $langcode . '/' . $uri['path'] . '/add/' . $default_langcode . '/' . $langcode;
     $this->drupalPostForm($path, $this->getEditValues($values, $langcode), $this->getFormSubmitAction($entity));
     if ($this->testLanguageSelector) {
       $this->assertNoFieldByXPath('//select[@id="edit-langcode"]', NULL, 'Language selector correclty disabled on translations.');
@@ -77,7 +80,7 @@ protected function doTestBasicTranslation() {
     $langcode = 'fr';
     $source_langcode = 'it';
     $edit = array('source_langcode[source]' => $source_langcode);
-    $path = $langcode . '/' . $base_path . '/translations/add/' . $default_langcode . '/' . $langcode;
+    $path = $langcode . '/' . $uri['path'] . '/add/' . $default_langcode . '/' . $langcode;
     $this->drupalPostForm($path, $edit, t('Change'));
     $this->assertFieldByXPath("//input[@name=\"{$this->fieldName}[0][value]\"]", $values[$source_langcode][$this->fieldName][0]['value'], 'Source language correctly switched.');
 
@@ -104,8 +107,8 @@ protected function doTestBasicTranslation() {
    */
   protected function doTestTranslationOverview() {
     $entity = entity_load($this->entityType, $this->entityId, TRUE);
-    $path = $this->controller->getBasePath($entity) . '/translations';
-    $this->drupalGet($path);
+    $uri = $entity->uri('drupal:content-translation-overview');
+    $this->drupalGet($uri['path']);
 
     foreach ($this->langcodes as $langcode) {
       if ($entity->hasTranslation($langcode)) {
@@ -124,13 +127,14 @@ protected function doTestOutdatedStatus() {
 
     // Mark translations as outdated.
     $edit = array('content_translation[retranslate]' => TRUE);
-    $this->drupalPostForm($langcode . '/' . $this->controller->getEditPath($entity), $edit, $this->getFormSubmitAction($entity));
+    $uri = $entity->uri('edit-form');
+    $this->drupalPostForm($langcode . '/' . $uri['path'], $edit, $this->getFormSubmitAction($entity));
     $entity = entity_load($this->entityType, $this->entityId, TRUE);
 
     // Check that every translation has the correct "outdated" status.
     foreach ($this->langcodes as $enabled_langcode) {
       $prefix = $enabled_langcode != $default_langcode ? $enabled_langcode . '/' : '';
-      $path = $prefix . $this->controller->getEditPath($entity);
+      $path = $prefix . $uri['path'];
       $this->drupalGet($path);
       if ($enabled_langcode == $langcode) {
         $this->assertFieldByXPath('//input[@name="content_translation[retranslate]"]', FALSE, 'The retranslate flag is not checked by default.');
@@ -152,7 +156,8 @@ protected function doTestOutdatedStatus() {
    */
   protected function doTestPublishedStatus() {
     $entity = entity_load($this->entityType, $this->entityId, TRUE);
-    $path = $this->controller->getEditPath($entity);
+    $uri = $entity->uri('edit-form');
+    $path = $uri['path'];
 
     // Unpublish translations.
     foreach ($this->langcodes as $index => $langcode) {
@@ -174,7 +179,8 @@ protected function doTestPublishedStatus() {
    */
   protected function doTestAuthoringInfo() {
     $entity = entity_load($this->entityType, $this->entityId, TRUE);
-    $path = $this->controller->getEditPath($entity);
+    $uri = $entity->uri('edit-form');
+    $path = $uri['path'];
     $values = array();
 
     // Post different authoring information for each translation.
@@ -218,7 +224,8 @@ protected function doTestTranslationDeletion() {
     // Confirm and delete a translation.
     $langcode = 'fr';
     $entity = entity_load($this->entityType, $this->entityId, TRUE);
-    $this->drupalPostForm($langcode . '/' . $this->controller->getEditPath($entity), array(), t('Delete translation'));
+    $uri = $entity->uri('edit-form');
+    $this->drupalPostForm($langcode . '/' . $uri['path'], array(), t('Delete translation'));
     $this->drupalPostForm(NULL, array(), t('Delete'));
     $entity = entity_load($this->entityType, $this->entityId, TRUE);
     if ($this->assertTrue(is_object($entity), 'Entity found')) {
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationWorkflowsTest.php b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationWorkflowsTest.php
index c52390025832d9ba2737a7c771702c53e5d7711f..0b3da770420eb5a1da41b0228b804941d76c6a65 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationWorkflowsTest.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationWorkflowsTest.php
@@ -67,7 +67,8 @@ protected function setupEntity() {
 
     // Create a translation.
     $this->drupalLogin($this->translator);
-    $add_translation_path = $this->controller->getBasePath($this->entity) . "/translations/add/$default_langcode/{$this->langcodes[2]}";
+    $uri = $this->entity->uri('drupal:content-translation-overview');
+    $add_translation_path = $uri['path'] . "/add/$default_langcode/{$this->langcodes[2]}";
     $this->drupalPostForm($add_translation_path, array(), t('Save'));
     $this->rebuildContainer();
   }
@@ -90,7 +91,8 @@ function testWorkflows() {
 
     // Check that translation permissions governate the associated operations.
     $ops = array('create' => t('Add'), 'update' => t('Edit'), 'delete' => t('Delete'));
-    $translations_path = $this->controller->getBasePath($this->entity) . "/translations";
+    $uri = $this->entity->uri('drupal:content-translation-overview');
+    $translations_path = $uri['path'];
     foreach ($ops as $current_op => $label) {
       $user = $this->drupalCreateUser(array($this->getTranslatePermission(), "$current_op content translations"));
       $this->drupalLogin($user);
@@ -123,14 +125,16 @@ protected function assertWorkflows(UserInterface $user, $expected_status) {
     $this->drupalLogin($user);
 
     // Check whether the user is allowed to access the entity form in edit mode.
-    $edit_path = $this->controller->getEditPath($this->entity);
+    $uri = $this->entity->uri('edit-form');
+    $edit_path = $uri['path'];
     $options = array('language' => $languages[$default_langcode]);
     $this->drupalGet($edit_path, $options);
     $this->assertResponse($expected_status['edit'], format_string('The @user_label has the expected edit access.', $args));
 
     // Check whether the user is allowed to access the translation overview.
     $langcode = $this->langcodes[1];
-    $translations_path = $this->controller->getBasePath($this->entity) . "/translations";
+    $uri = $this->entity->uri('drupal:content-translation-overview');
+    $translations_path = $uri['path'];
     $options = array('language' => $languages[$langcode]);
     $this->drupalGet($translations_path, $options);
     $this->assertResponse($expected_status['overview'], format_string('The @user_label has the expected translation overview access.', $args));
diff --git a/core/modules/content_translation/tests/Drupal/content_translation/Tests/Menu/ContentTranslationLocalTasksTest.php b/core/modules/content_translation/tests/Drupal/content_translation/Tests/Menu/ContentTranslationLocalTasksTest.php
index cef060cd3a3753f4a4fb0b8cda3c653a0a1dd5b6..a83ee6fe6027a8aab9e2bf162974ef26bcf9b521 100644
--- a/core/modules/content_translation/tests/Drupal/content_translation/Tests/Menu/ContentTranslationLocalTasksTest.php
+++ b/core/modules/content_translation/tests/Drupal/content_translation/Tests/Menu/ContentTranslationLocalTasksTest.php
@@ -38,13 +38,10 @@ public function setUp() {
       ->method('getDefinitions')
       ->will($this->returnValue(array(
         'node' => array(
-          'translatable' => true,
-          'translation' => array(
-            'content_translation' => array(
-              // things.
-            ),
+          'translatable' => TRUE,
+          'links' => array(
+            'canonical' => '/node/{node}',
           ),
-          'menu_base_path' => 'node/%node',
         ),
       )));
     \Drupal::getContainer()->set('entity.manager', $entity_manager);
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php b/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php
index baa8d233dce622144b904b850d7a09540ca4dc0f..bab99961f4be98dca04167937cb91a7bbafcbf61 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php
@@ -74,7 +74,7 @@ protected function getFormSubmitAction(EntityInterface $entity) {
    */
   protected function doTestPublishedStatus() {
     $entity = entity_load($this->entityType, $this->entityId, TRUE);
-    $path = $this->controller->getEditPath($entity);
+    $uri = $entity->uri('edit-form');
     $languages = language_list();
 
     $actions = array(
@@ -89,7 +89,7 @@ protected function doTestPublishedStatus() {
         if (!empty($status_actions)) {
           $action = array_shift($status_actions);
         }
-        $this->drupalPostForm($path, array(), $action, array('language' => $languages[$langcode]));
+        $this->drupalPostForm($uri['path'], array(), $action, array('language' => $languages[$langcode]));
       }
       $entity = entity_load($this->entityType, $this->entityId, TRUE);
       foreach ($this->langcodes as $langcode) {
@@ -106,7 +106,7 @@ protected function doTestPublishedStatus() {
    */
   protected function doTestAuthoringInfo() {
     $entity = entity_load($this->entityType, $this->entityId, TRUE);
-    $path = $this->controller->getEditPath($entity);
+    $uri = $entity->uri('edit-form');
     $languages = language_list();
     $values = array();
 
@@ -122,7 +122,7 @@ protected function doTestAuthoringInfo() {
         'date[date]' => format_date($values[$langcode]['created'], 'custom', 'Y-m-d'),
         'date[time]' => format_date($values[$langcode]['created'], 'custom', 'H:i:s'),
       );
-      $this->drupalPostForm($path, $edit, $this->getFormSubmitAction($entity), array('language' => $languages[$langcode]));
+      $this->drupalPostForm($uri['path'], $edit, $this->getFormSubmitAction($entity), array('language' => $languages[$langcode]));
     }
 
     $entity = entity_load($this->entityType, $this->entityId, TRUE);
diff --git a/core/modules/system/tests/modules/entity_test/entity_test.module b/core/modules/system/tests/modules/entity_test/entity_test.module
index c8f868e29c3e6415484240b639add716cc3499fd..2348856e07dadb16cb8b43aa606dd2e6ff4a80ba 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.module
+++ b/core/modules/system/tests/modules/entity_test/entity_test.module
@@ -62,9 +62,6 @@ function entity_test_entity_types($filter = NULL) {
  */
 function entity_test_entity_info_alter(&$info) {
   foreach (entity_test_entity_types() as $entity_type) {
-    // Ensure these test entity types use their base path as their edit path.
-    $info[$entity_type]['menu_edit_path'] = $info[$entity_type]['menu_base_path'];
-
     // Optionally specify a translation handler for testing translations.
     if (\Drupal::state()->get('entity_test.translation')) {
       $info[$entity_type]['translation'][$entity_type] = TRUE;
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php
index 69f1aabbe5364d80dc41f9f452486ab1528a3e80..d776c85bc0ebdd7ba4c6037ce657d32464933b01 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTest.php
@@ -37,7 +37,6 @@
  *     "bundle" = "type",
  *     "label" = "name"
  *   },
- *   menu_base_path = "entity_test/manage/%entity_test",
  *   route_base_path = "admin/structure/entity-test/manage/{bundle}",
  *   links = {
  *     "canonical" = "/entity_test/{entity_test}",
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestCache.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestCache.php
index 55a7b5e8a7e9be6636b182b6eab23e12b48b3226..48d345e9b3887208106666bf6964886d33b9bf66 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestCache.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestCache.php
@@ -32,8 +32,7 @@
  *     "id" = "id",
  *     "uuid" = "uuid",
  *     "bundle" = "type"
- *   },
- *   menu_base_path = "entity-test/manage/%entity_test"
+ *   }
  * )
  */
 class EntityTestCache extends EntityTest {
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMul.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMul.php
index 790732283c417b262dc48e6aa37b9f3cd9b6d94e..e4e0f9c6892794b85377ce51287b0daf9d753e8f 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMul.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMul.php
@@ -36,8 +36,11 @@
  *     "bundle" = "type",
  *     "label" = "name"
  *   },
- *   menu_base_path = "entity_test_mul/manage/%entity_test_mul",
- *   route_base_path = "entity_test_mul/structure/{bundle}"
+ *   route_base_path = "entity_test_mul/structure/{bundle}",
+ *   links = {
+ *     "canonical" = "/entity_test_mul/manage/{entity_test_mul}",
+ *     "edit-form" = "/entity_test_mul/manage/{entity_test_mul}"
+ *   }
  * )
  */
 class EntityTestMul extends EntityTest {
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMulRev.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMulRev.php
index 3d8df50f03f6778361084b55fa8bf962116ef1c8..25887a50b2bc053a1c327d71337b271fcffb2ac1 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMulRev.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestMulRev.php
@@ -37,7 +37,10 @@
  *     "revision" = "revision_id",
  *     "bundle" = "type"
  *   },
- *   menu_base_path = "entity_test_mulrev/manage/%entity_test_mulrev"
+ *   links = {
+ *     "canonical" = "/entity_test_mulrev/manage/{entity_test_mulrev}",
+ *     "edit-form" = "/entity_test_mulrev/manage/{entity_test_mulrev}"
+ *   }
  * )
  */
 class EntityTestMulRev extends EntityTestRev {
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestRev.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestRev.php
index 19760e8471b7a14774dffde497833b7a320102ef..fb6d42cf50ce0fcb088f261a189ac666fdf54961 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestRev.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Entity/EntityTestRev.php
@@ -34,7 +34,10 @@
  *     "revision" = "revision_id",
  *     "bundle" = "type"
  *   },
- *   menu_base_path = "entity_test_rev/manage/%entity_test_rev"
+ *   links = {
+ *     "canonical" = "/entity_test_rev/manage/{entity_test_rev}",
+ *     "edit-form" = "/entity_test_rev/manage/{entity_test_rev}"
+ *   }
  * )
  */
 class EntityTestRev extends EntityTest {
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php
index 033c5f14ffd893256963bd977795fce16cf90955..7c9e9c311f01c0bf35c8b957881154e319a28503 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Entity/Term.php
@@ -48,7 +48,6 @@
  *     "canonical" = "/taxonomy/term/{taxonomy_term}",
  *     "edit-form" = "/taxonomy/term/{taxonomy_term}/edit"
  *   },
- *   menu_base_path = "taxonomy/term/%taxonomy_term",
  *   route_base_path = "admin/structure/taxonomy/manage/{bundle}",
  *   permission_granularity = "bundle"
  * )
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/TermTranslationController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/TermTranslationController.php
index f7c704af19ce79ce9681f109d5383691c6c414d1..ba951a1d7d8e3e11696d7d0ae7c006fdc17e8af2 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/TermTranslationController.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/TermTranslationController.php
@@ -36,7 +36,8 @@ function entityFormSave(array $form, array &$form_state) {
       // We need a redirect here, otherwise we would get an access denied page,
       // since the current URL would be preserved and we would try to add a
       // translation for a language that already has a translation.
-      $form_state['redirect'] = $this->getEditPath($entity);
+      $uri = $entity->uri('edit-form');
+      $form_state['redirect'] = $uri['path'];
     }
   }
 
diff --git a/core/modules/user/lib/Drupal/user/ProfileTranslationController.php b/core/modules/user/lib/Drupal/user/ProfileTranslationController.php
index 499f28c91ad8e1a32e553035a05a173adb813bc9..966717088b87c2adfee3b18e2247363698f7d06f 100644
--- a/core/modules/user/lib/Drupal/user/ProfileTranslationController.php
+++ b/core/modules/user/lib/Drupal/user/ProfileTranslationController.php
@@ -36,7 +36,8 @@ function entityFormSave(array $form, array &$form_state) {
       // We need a redirect here, otherwise we would get an access denied page
       // since the current URL would be preserved and we would try to add a
       // translation for a language that already has a translation.
-      $form_state['redirect'] = $this->getViewPath($entity);
+      $uri = $entity->uri();
+      $form_state['redirect'] = $uri['path'];
     }
   }
 }