diff --git a/core/lib/Drupal/Core/Entity/Annotation/EntityType.php b/core/lib/Drupal/Core/Entity/Annotation/EntityType.php
index b8e4061..720c3ad 100644
--- a/core/lib/Drupal/Core/Entity/Annotation/EntityType.php
+++ b/core/lib/Drupal/Core/Entity/Annotation/EntityType.php
@@ -222,7 +222,7 @@ class EntityType extends Plugin {
   public $bundle_prefix;
 
   /**
-   * The base menu router path to which the entity admin user interface responds.
+   * The base menu router path to which the entity 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.
@@ -232,27 +232,6 @@ class EntityType extends Plugin {
   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.
    *
    * Links are an array of standard link relations to the URI template that
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlock.php b/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlock.php
index 74dfc25..3ac7147 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlock.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/Plugin/Core/Entity/CustomBlock.php
@@ -34,6 +34,10 @@
  *   revision_table = "custom_block_revision",
  *   route_base_path = "admin/structure/custom-blocks/manage/{bundle}",
  *   menu_base_path = "block/%custom_block",
+ *   links = {
+ *     "canonical" = "block/{id}",
+ *     "edit-form" = "block/{id}/edit"
+ *   },
  *   fieldable = TRUE,
  *   translatable = TRUE,
  *   entity_keys = {
@@ -172,19 +176,6 @@ protected function init() {
   /**
    * {@inheritdoc}
    */
-  public function uri() {
-    return array(
-      'path' => 'block/' . $this->id(),
-      'options' => array(
-        'entity_type' => $this->entityType,
-        'entity' => $this,
-      )
-    );
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function postSave(EntityStorageControllerInterface $storage_controller, $update = TRUE) {
     // Invalidate the block cache to update custom block-based derivatives.
     \Drupal::service('plugin.manager.block')->clearCachedDefinitions();
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentTranslationUITest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentTranslationUITest.php
index 49d6814..fd91f2c 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentTranslationUITest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentTranslationUITest.php
@@ -103,11 +103,11 @@ protected function assertPublishedStatus() {
     $languages = language_list();
 
     // Check that simple users cannot see unpublished field translations.
-    $path = $this->controller->getViewPath($entity);
+    $uri = $entity->uri('canonical');
     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 2de5947..7bdd358 100644
--- a/core/modules/content_translation/content_translation.module
+++ b/core/modules/content_translation/content_translation.module
@@ -79,16 +79,18 @@ function content_translation_entity_info_alter(array &$entity_info) {
       continue;
     }
 
-    if (!isset($info['translation']['content_translation'])) {
-      $info['translation']['content_translation'] = array();
-    }
-
     // 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');
 
+    // Provide default links for the translation paths.
+    $canonical = $info['links']['canonical'];
+    $info['links'] += array(
+      'translation-overview' => $canonical . '/translations',
+    );
+
     // If no menu base path is provided we default to the usual
     // "entity_type/%entity_type" pattern.
     if (!isset($info['menu_base_path'])) {
@@ -96,15 +98,11 @@ function content_translation_entity_info_alter(array &$entity_info) {
       $info['menu_base_path'] = $path;
     }
 
-    $path = $info['menu_base_path'];
-
-    $info += array(
-      'menu_view_path' => $path,
-      'menu_edit_path' => "$path/edit",
-      'menu_path_wildcard' => "%$entity_type",
-    );
+    if (!isset($info['translation']['content_translation'])) {
+      $info['translation']['content_translation'] = array();
+    }
 
-    $entity_position = count(explode('/', $path)) - 1;
+    $entity_position = count(explode('/', $info['menu_base_path'])) - 1;
     $info['translation']['content_translation'] += array(
       'access_callback' => 'content_translation_translate_access',
       'access_arguments' => array($entity_position),
@@ -236,7 +234,7 @@ function content_translation_menu_alter(array &$items) {
       }
       else {
         $entity_position = count(explode('/', $path)) - 1;
-        $edit_path = $info['menu_edit_path'];
+        $edit_path = $info['menu_base_path'] . '/edit';
 
         if (isset($items[$edit_path])) {
           // If the edit path is a default local task we need to find the parent
diff --git a/core/modules/content_translation/content_translation.pages.inc b/core/modules/content_translation/content_translation.pages.inc
index 5bc9501..b5a2a91 100644
--- a/core/modules/content_translation/content_translation.pages.inc
+++ b/core/modules/content_translation/content_translation.pages.inc
@@ -22,10 +22,13 @@ function content_translation_overview(EntityInterface $entity) {
   $original = $entity->language()->langcode;
   $translations = $entity->getTranslationLanguages();
   $field_ui = module_exists('field_ui') && user_access('administer ' . $entity->entityType() . ' fields');
+  $generator = Drupal::urlGenerator();
 
-  $path = $controller->getViewPath($entity);
-  $base_path = $controller->getBasePath($entity);
-  $edit_path = $controller->getEditPath($entity);
+  $rel = array();
+  foreach (array('canonical', 'edit-form', 'translation-overview') as $name) {
+    $rel[$name] = $entity->uri($name);
+  }
+  $base_path = $rel['translation-overview']['path'];
 
   $header = array(t('Language'), t('Translation'), t('Source language'), t('Status'), t('Operations'));
   $rows = array();
@@ -33,8 +36,8 @@ function content_translation_overview(EntityInterface $entity) {
   if (language_multilingual()) {
     // If we have a view path defined for the current entity get the switch
     // links based on it.
-    if ($path) {
-      $links = _content_translation_get_switch_links($path);
+    if (!empty($rel['canonical'])) {
+      $links = _content_translation_get_switch_links($rel['canonical']['path']);
     }
 
     // Determine whether the current entity is translatable.
@@ -51,13 +54,13 @@ function content_translation_overview(EntityInterface $entity) {
     foreach ($languages as $language) {
       $language_name = $language->name;
       $langcode = $language->langcode;
-      $add_path = $base_path . '/translations/add/' . $original . '/' . $langcode;
-      $translate_path = $base_path . '/translations/edit/' . $langcode;
-      $delete_path = $base_path . '/translations/delete/' . $langcode;
+      $add_path = $base_path . '/add/' . $original . '/' . $langcode;
+      $translate_path = $base_path . '/edit/' . $langcode;
+      $delete_path = $base_path . '/delete/' . $langcode;
 
       if ($base_path) {
         $add_links = _content_translation_get_switch_links($add_path);
-        $edit_links = _content_translation_get_switch_links($edit_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($delete_path);
       }
@@ -76,7 +79,7 @@ function content_translation_overview(EntityInterface $entity) {
         $is_original = $langcode == $original;
         $translation = $translations[$langcode];
         $label = $entity->label($langcode);
-        $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'])) {
@@ -86,8 +89,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);
@@ -268,11 +271,12 @@ function content_translation_prepare_translation(EntityInterface $entity, Langua
 function content_translation_delete_confirm(array $form, array $form_state, EntityInterface $entity, Language $language) {
   $langcode = $language->langcode;
   $controller = content_translation_controller($entity->entityType());
+  $uri = $entity->uri('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')
@@ -293,9 +297,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 (module_exists('path')) {
-    $conditions = array('source' => $controller->getViewPath($entity), 'langcode' => $language->langcode);
+    $uri = $entity->uri();
+    $conditions = array('source' => $uri['path'], 'langcode' => $language->langcode);
     Drupal::service('path.crud')->delete($conditions);
   }
 
-  $form_state['redirect'] = $controller->getBasePath($entity) . '/translations';
+  $uri = $entity->uri('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 5e4ad79..2b819a4 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationController.php
@@ -70,27 +70,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().
    */
   public function getTranslationAccess(EntityInterface $entity, $op) {
@@ -461,7 +440,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('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)));
@@ -488,9 +469,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('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;
   }
 
   /**
@@ -503,17 +484,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 b28dd8d..f840e87 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationControllerInterface.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/ContentTranslationControllerInterface.php
@@ -22,14 +22,8 @@
  * 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
+ * case at least the 'menu_base_path' key must be defined.
+ *
  * 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
@@ -62,7 +56,6 @@
  *   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',
@@ -79,39 +72,6 @@
 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/Tests/ContentTranslationUITest.php b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationUITest.php
index 6ffad4f..a191dd3 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 assertBasicTranslation() {
     $this->entityId = $this->createEntity($values[$default_langcode], $default_langcode);
     $entity = entity_load($this->entityType, $this->entityId, TRUE);
     $this->assertTrue($entity, t('Entity found in the database.'));
+    $uri = $entity->uri();
+    $this->drupalGet($uri['path']);
+    $this->assertResponse(200, t('Entity URL is valid.'));
 
     $translation = $this->getTranslation($entity, $default_langcode);
     foreach ($values[$default_langcode] as $property => $value) {
@@ -65,8 +68,8 @@ protected function assertBasicTranslation() {
     $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('translation-overview');
+    $path = $langcode . '/' . $uri['path'] . '/add/' . $default_langcode . '/' . $langcode;
     $this->drupalPost($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 assertBasicTranslation() {
     $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->drupalPost($path, $edit, t('Change'));
     $this->assertFieldByXPath("//input[@name=\"{$this->fieldName}[fr][0][value]\"]", $values[$source_langcode][$this->fieldName][0]['value'], 'Source language correctly switched.');
 
@@ -109,13 +112,14 @@ protected function assertOutdatedStatus() {
 
     // Mark translations as outdated.
     $edit = array('content_translation[retranslate]' => TRUE);
-    $this->drupalPost($langcode . '/' . $this->controller->getEditPath($entity), $edit, $this->getFormSubmitAction($entity));
+    $uri = $entity->uri('edit-form');
+    $this->drupalPost($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.');
@@ -137,7 +141,8 @@ protected function assertOutdatedStatus() {
    */
   protected function assertPublishedStatus() {
     $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) {
@@ -159,7 +164,8 @@ protected function assertPublishedStatus() {
    */
   protected function assertAuthoringInfo() {
     $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.
@@ -203,7 +209,8 @@ protected function assertTranslationDeletion() {
     // Confirm and delete a translation.
     $langcode = 'fr';
     $entity = entity_load($this->entityType, $this->entityId, TRUE);
-    $this->drupalPost($langcode . '/' . $this->controller->getEditPath($entity), array(), t('Delete translation'));
+    $uri = $entity->uri('edit-form');
+    $this->drupalPost($langcode . '/' . $uri['path'], array(), t('Delete translation'));
     $this->drupalPost(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 4930283..ad3c775 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
@@ -66,7 +66,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('translation-overview');
+    $add_translation_path = $uri['path'] . "/add/$default_langcode/{$this->langcodes[2]}";
     $this->drupalPost($add_translation_path, array(), t('Save'));
     $this->rebuildContainer();
   }
@@ -89,7 +90,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('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);
@@ -122,14 +124,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('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/node/lib/Drupal/node/Tests/NodeTranslationUITest.php b/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php
index bea3245..0f68f57 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeTranslationUITest.php
@@ -80,7 +80,7 @@ protected function getFormSubmitAction(EntityInterface $entity) {
    */
   protected function assertPublishedStatus() {
     $entity = entity_load($this->entityType, $this->entityId, TRUE);
-    $path = $this->controller->getEditPath($entity);
+    $uri = $entity->uri('edit-form');
     $languages = language_list();
 
     $actions = array(
@@ -95,7 +95,7 @@ protected function assertPublishedStatus() {
         if (!empty($status_actions)) {
           $action = array_shift($status_actions);
         }
-        $this->drupalPost($path, array(), $action, array('language' => $languages[$langcode]));
+        $this->drupalPost($uri['path'], array(), $action, array('language' => $languages[$langcode]));
       }
       $entity = entity_load($this->entityType, $this->entityId, TRUE);
       foreach ($this->langcodes as $langcode) {
@@ -112,7 +112,7 @@ protected function assertPublishedStatus() {
    */
   protected function assertAuthoringInfo() {
     $entity = entity_load($this->entityType, $this->entityId, TRUE);
-    $path = $this->controller->getEditPath($entity);
+    $uri = $entity->uri('edit-form');
     $languages = language_list();
     $values = array();
 
@@ -128,7 +128,7 @@ protected function assertAuthoringInfo() {
         'date[date]' => format_date($values[$langcode]['created'], 'custom', 'Y-m-d'),
         'date[time]' => format_date($values[$langcode]['created'], 'custom', 'H:i:s'),
       );
-      $this->drupalPost($path, $edit, $this->getFormSubmitAction($entity), array('language' => $languages[$langcode]));
+      $this->drupalPost($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.info.yml b/core/modules/system/tests/modules/entity_test/entity_test.info.yml
index 5bea26a..8fe33f7 100644
--- a/core/modules/system/tests/modules/entity_test/entity_test.info.yml
+++ b/core/modules/system/tests/modules/entity_test/entity_test.info.yml
@@ -7,4 +7,4 @@ core: 8.x
 dependencies:
   - field
   - text
-hidden: true
+hidden: false
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTest.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTest.php
index 332b322..ea9a9c0 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTest.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTest.php
@@ -35,7 +35,11 @@
  *     "uuid" = "uuid",
  *     "bundle" = "type",
  *   },
- *   menu_base_path = "entity-test/manage/%entity_test"
+ *   menu_base_path = "entity-test/manage/%entity_test",
+ *   links = {
+ *     "canonical" = "entity-test/manage/{id}",
+ *     "edit-form" = "entity-test/manage/{id}"
+ *   }
  * )
  */
 class EntityTest extends EntityNG {
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestCache.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestCache.php
index 615bffc..e87b76c 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestCache.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestCache.php
@@ -35,7 +35,11 @@
  *     "uuid" = "uuid",
  *     "bundle" = "type"
  *   },
- *   menu_base_path = "entity-test/manage/%entity_test"
+ *   menu_base_path = "entity-test/manage/%entity_test",
+ *   links = {
+ *     "canonical" = "entity-test/manage/{id}",
+ *     "edit-form" = "entity-test/manage/{id}"
+ *   }
  * )
  */
 class EntityTestCache extends EntityNG {
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestMul.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestMul.php
index 99455a5..9afd595 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestMul.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestMul.php
@@ -35,7 +35,11 @@
  *     "uuid" = "uuid",
  *     "bundle" = "type"
  *   },
- *   menu_base_path = "entity_test_mul/manage/%entity_test_mul"
+ *   menu_base_path = "entity_test_mul/manage/%entity_test_mul",
+ *   links = {
+ *     "canonical" = "entity_test_mul/manage/{id}",
+ *     "edit-form" = "entity_test_mul/manage/{id}"
+ *   }
  * )
  */
 class EntityTestMul extends EntityTest {
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestMulRev.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestMulRev.php
index b0bd877..5eb21b0 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestMulRev.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestMulRev.php
@@ -37,7 +37,11 @@
  *     "revision" = "revision_id",
  *     "bundle" = "type"
  *   },
- *   menu_base_path = "entity_test_mulrev/manage/%entity_test_mulrev"
+ *   menu_base_path = "entity_test_mulrev/manage/%entity_test_mulrev",
+ *   links = {
+ *     "canonical" = "entity_test_mulrev/manage/{id}",
+ *     "edit-form" = "entity_test_mulrev/manage/{id}"
+ *   }
  * )
  */
 class EntityTestMulRev extends EntityTestRev {
diff --git a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestRev.php b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestRev.php
index 6209cf5..dc6e587 100644
--- a/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestRev.php
+++ b/core/modules/system/tests/modules/entity_test/lib/Drupal/entity_test/Plugin/Core/Entity/EntityTestRev.php
@@ -35,7 +35,11 @@
  *     "revision" = "revision_id",
  *     "bundle" = "type"
  *   },
- *   menu_base_path = "entity_test_rev/manage/%entity_test_rev"
+ *   menu_base_path = "entity_test_rev/manage/%entity_test_rev",
+ *   links = {
+ *     "canonical" = "entity_test_rev/manage/{id}",
+ *     "edit-form" = "entity_test_rev/manage/{id}"
+ *   }
  * )
  */
 class EntityTestRev extends EntityTest {
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/TermTranslationController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/TermTranslationController.php
index 3b50d17..03771a2 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 5cb9cc8..ef0032a 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'];
     }
   }
 }
