diff --git a/core/modules/block_content/block_content.routing.yml b/core/modules/block_content/block_content.routing.yml
index 75ea9b2..e3e4a84 100644
--- a/core/modules/block_content/block_content.routing.yml
+++ b/core/modules/block_content/block_content.routing.yml
@@ -6,26 +6,6 @@ entity.block_content_type.collection:
   requirements:
     _permission: 'administer blocks'
 
-block_content.add_page:
-  path: '/block/add'
-  defaults:
-    _controller: '\Drupal\block_content\Controller\BlockContentController::add'
-    _title: 'Add custom block'
-  options:
-    _admin_route: TRUE
-  requirements:
-    _permission: 'administer blocks'
-
-block_content.add_form:
-  path: '/block/add/{block_content_type}'
-  defaults:
-    _controller: '\Drupal\block_content\Controller\BlockContentController::addForm'
-    _title_callback: 'Drupal\block_content\Controller\BlockContentController::getAddFormTitle'
-  options:
-    _admin_route: TRUE
-  requirements:
-    _permission: 'administer blocks'
-
 entity.block_content_type.delete_form:
   path: '/admin/structure/block/block-content/manage/{block_content_type}/delete'
   defaults:
@@ -36,37 +16,6 @@ entity.block_content_type.delete_form:
   options:
     _admin_route: TRUE
 
-entity.block_content.canonical:
-  path: '/block/{block_content}'
-  defaults:
-    _entity_form: 'block_content.edit'
-  options:
-    _admin_route: TRUE
-  requirements:
-    _entity_access: 'block_content.update'
-    block_content: \d+
-
-entity.block_content.edit_form:
-  path: '/block/{block_content}'
-  defaults:
-    _entity_form: 'block_content.edit'
-  options:
-    _admin_route: TRUE
-  requirements:
-    _entity_access: 'block_content.update'
-    block_content: \d+
-
-entity.block_content.delete_form:
-  path: '/block/{block_content}/delete'
-  defaults:
-    _entity_form: 'block_content.delete'
-    _title: 'Delete'
-  options:
-    _admin_route: TRUE
-  requirements:
-    _entity_access: 'block_content.delete'
-    block_content: \d+
-
 block_content.type_add:
   path: '/admin/structure/block/block-content/types/add'
   defaults:
diff --git a/core/modules/block_content/src/Entity/BlockContent.php b/core/modules/block_content/src/Entity/BlockContent.php
index 9e0531e..20d0f41 100644
--- a/core/modules/block_content/src/Entity/BlockContent.php
+++ b/core/modules/block_content/src/Entity/BlockContent.php
@@ -29,13 +29,18 @@
  *       "delete" = "Drupal\block_content\Form\BlockContentDeleteForm",
  *       "default" = "Drupal\block_content\BlockContentForm"
  *     },
- *     "translation" = "Drupal\block_content\BlockContentTranslationHandler"
+ *     "translation" = "Drupal\block_content\BlockContentTranslationHandler",
+ *     "route_provider" = {
+ *       "html" = "Drupal\block_content\Entity\BlockContentHtmlRouteProvider",
+ *     },
  *   },
  *   admin_permission = "administer blocks",
  *   base_table = "block_content",
  *   revision_table = "block_content_revision",
  *   data_table = "block_content_field_data",
  *   links = {
+ *     "add-page" = "/block/add",
+ *     "add-form" = "/block/add/{block_content_type}",
  *     "canonical" = "/block/{block_content}",
  *     "delete-form" = "/block/{block_content}/delete",
  *     "edit-form" = "/block/{block_content}",
diff --git a/core/modules/block_content/src/Entity/BlockContentHtmlRouteProvider.php b/core/modules/block_content/src/Entity/BlockContentHtmlRouteProvider.php
new file mode 100644
index 0000000..12534b8
--- /dev/null
+++ b/core/modules/block_content/src/Entity/BlockContentHtmlRouteProvider.php
@@ -0,0 +1,70 @@
+<?php
+
+namespace Drupal\block_content\Entity;
+
+use Drupal\block_content\Controller\BlockContentController;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\Routing\AdminHtmlRouteProvider;
+
+/**
+ * Provides HTML routes for block contents.
+ */
+class BlockContentHtmlRouteProvider extends AdminHtmlRouteProvider {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRoutes(EntityTypeInterface $entity_type) {
+    $collection = parent::getRoutes($entity_type);
+
+    // Rename the add page and add form routes for backwards compatibility.
+    $entity_type_id = $entity_type->id();
+    if ($add_page_route = $collection->get("entity.{$entity_type_id}.add_page")) {
+      $collection->add('block_content.add_page', $add_page_route);
+      $collection->remove("entity.{$entity_type_id}.add_page");
+    }
+    if ($add_form_route = $collection->get("entity.{$entity_type_id}.add_form")) {
+      $collection->add('block_content.add_form', $add_form_route);
+      $collection->remove("entity.{$entity_type_id}.add_form");
+    }
+
+    return $collection;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getAddPageRoute(EntityTypeInterface $entity_type) {
+    if ($route = parent::getAddPageRoute($entity_type)) {
+      // Set a custom controller which sets a dedicated theme function for
+      // backwards compatibility.
+      $route->setDefault('_controller', BlockContentController::class . '::add');
+      return $route;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getAddFormRoute(EntityTypeInterface $entity_type) {
+    if ($route = parent::getAddFormRoute($entity_type)) {
+      $route
+        // Depending on a request parameter the theme of the block is being
+        // updated which requires a custom controller.
+        ->setDefault('_controller', BlockContentController::class . '::addForm')
+        // Provide a more explicit title for backwards compatibility.
+        ->setDefault('_title_callback', BlockContentController::class . '::getAddFormTitle');
+      return $route;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getCanonicalRoute(EntityTypeInterface $entity_type) {
+    // Modules like Content Translation require a canonical route, but there is
+    // no view page for custom blocks, so we just reuse the edit form.
+    return $this->getEditFormRoute($entity_type);
+  }
+
+}
diff --git a/core/modules/comment/comment.routing.yml b/core/modules/comment/comment.routing.yml
index 3d698b8..df3c3d2 100644
--- a/core/modules/comment/comment.routing.yml
+++ b/core/modules/comment/comment.routing.yml
@@ -16,15 +16,6 @@ comment.admin_approval:
   requirements:
     _permission: 'administer comments'
 
-entity.comment.edit_form:
-  path: '/comment/{comment}/edit'
-  defaults:
-    _title: 'Edit'
-    _entity_form: 'comment.default'
-  requirements:
-    _entity_access: 'comment.update'
-    comment: \d+
-
 comment.approve:
   path: '/comment/{comment}/approve'
   defaults:
@@ -36,24 +27,6 @@ comment.approve:
     _csrf_token: 'TRUE'
     comment: \d+
 
-entity.comment.canonical:
-  path: '/comment/{comment}'
-  defaults:
-    _title_callback: '\Drupal\comment\Controller\CommentController::commentPermalinkTitle'
-    _controller: '\Drupal\comment\Controller\CommentController::commentPermalink'
-  requirements:
-    _entity_access: 'comment.view'
-    comment: \d+
-
-entity.comment.delete_form:
-  path: '/comment/{comment}/delete'
-  defaults:
-    _title: 'Delete'
-    _entity_form: 'comment.delete'
-  requirements:
-    _entity_access: 'comment.delete'
-    comment: \d+
-
 comment.reply:
   path: '/comment/reply/{entity_type}/{entity}/{field_name}/{pid}'
   defaults:
diff --git a/core/modules/comment/src/Controller/CommentController.php b/core/modules/comment/src/Controller/CommentController.php
index 0b682e8..4085169 100644
--- a/core/modules/comment/src/Controller/CommentController.php
+++ b/core/modules/comment/src/Controller/CommentController.php
@@ -155,6 +155,9 @@ public function commentPermalink(Request $request, CommentInterface $comment) {
    *
    * @return string
    *   The translated comment subject.
+   *
+   * @deprecated in 8.2.x, will be removed in Drupal 9. Use
+   *   \Drupal\Core\Entity\Controller\EntityController::title() instead.
    */
   public function commentPermalinkTitle(CommentInterface $comment) {
     return $this->entityManager()->getTranslationFromContext($comment)->label();
diff --git a/core/modules/comment/src/Entity/Comment.php b/core/modules/comment/src/Entity/Comment.php
index d626be3..28009e1 100644
--- a/core/modules/comment/src/Entity/Comment.php
+++ b/core/modules/comment/src/Entity/Comment.php
@@ -32,7 +32,10 @@
  *       "default" = "Drupal\comment\CommentForm",
  *       "delete" = "Drupal\comment\Form\DeleteForm"
  *     },
- *     "translation" = "Drupal\comment\CommentTranslationHandler"
+ *     "translation" = "Drupal\comment\CommentTranslationHandler",
+ *     "route_provider" = {
+ *       "html" = "Drupal\comment\Entity\CommentHtmlRouteProvider",
+ *     },
  *   },
  *   base_table = "comment",
  *   data_table = "comment_field_data",
diff --git a/core/modules/comment/src/Entity/CommentHtmlRouteProvider.php b/core/modules/comment/src/Entity/CommentHtmlRouteProvider.php
new file mode 100644
index 0000000..65524bd
--- /dev/null
+++ b/core/modules/comment/src/Entity/CommentHtmlRouteProvider.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Drupal\comment\Entity;
+
+use Drupal\comment\Controller\CommentController;
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider;
+
+/**
+ * Provides HTML routes for comments.
+ */
+class CommentHtmlRouteProvider extends DefaultHtmlRouteProvider {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getCanonicalRoute(EntityTypeInterface $entity_type) {
+    if ($route = parent::getCanonicalRoute($entity_type)) {
+      $route->setDefault('_controller', CommentController::class . '::commentPermalink');
+      return $route;
+    }
+  }
+
+}
diff --git a/core/modules/menu_link_content/menu_link_content.routing.yml b/core/modules/menu_link_content/menu_link_content.routing.yml
deleted file mode 100644
index 8c00ee0..0000000
--- a/core/modules/menu_link_content/menu_link_content.routing.yml
+++ /dev/null
@@ -1,31 +0,0 @@
-entity.menu.add_link_form:
-  path: '/admin/structure/menu/manage/{menu}/add'
-  defaults:
-    _controller: '\Drupal\menu_link_content\Controller\MenuController::addLink'
-    _title: 'Add menu link'
-  requirements:
-    _entity_create_access: 'menu_link_content'
-
-entity.menu_link_content.canonical:
-  path: '/admin/structure/menu/item/{menu_link_content}/edit'
-  defaults:
-    _entity_form: 'menu_link_content.default'
-    _title: 'Edit menu link'
-  requirements:
-    _entity_access: 'menu_link_content.update'
-
-entity.menu_link_content.edit_form:
-  path: '/admin/structure/menu/item/{menu_link_content}/edit'
-  defaults:
-    _entity_form: 'menu_link_content.default'
-    _title: 'Edit menu link'
-  requirements:
-    _entity_access: 'menu_link_content.update'
-
-entity.menu_link_content.delete_form:
-  path: '/admin/structure/menu/item/{menu_link_content}/delete'
-  defaults:
-    _entity_form: 'menu_link_content.delete'
-    _title: 'Delete menu link'
-  requirements:
-    _entity_access: 'menu_link_content.delete'
diff --git a/core/modules/menu_link_content/src/Controller/MenuController.php b/core/modules/menu_link_content/src/Controller/MenuController.php
index 6aaae64..2adb50d 100644
--- a/core/modules/menu_link_content/src/Controller/MenuController.php
+++ b/core/modules/menu_link_content/src/Controller/MenuController.php
@@ -18,6 +18,10 @@ class MenuController extends ControllerBase {
    *
    * @return array
    *   Returns the menu link creation form.
+   *
+   * @deprecated in 8.2.x, will be removed in Drupal 9. Use
+   *   '_entity_form: menu_link_content.add' as a route default instead and
+   *   specify the bundle as a route default additionally.
    */
   public function addLink(MenuInterface $menu) {
     $menu_link = $this->entityManager()->getStorage('menu_link_content')->create(array(
diff --git a/core/modules/menu_link_content/src/Entity/MenuLinkContent.php b/core/modules/menu_link_content/src/Entity/MenuLinkContent.php
index 1dcd1cd..57f6c63 100644
--- a/core/modules/menu_link_content/src/Entity/MenuLinkContent.php
+++ b/core/modules/menu_link_content/src/Entity/MenuLinkContent.php
@@ -7,6 +7,7 @@
 use Drupal\Core\Entity\EntityStorageInterface;
 use Drupal\Core\Entity\EntityTypeInterface;
 use Drupal\Core\Field\BaseFieldDefinition;
+use Drupal\Core\Url;
 use Drupal\link\LinkItemInterface;
 use Drupal\menu_link_content\MenuLinkContentInterface;
 
@@ -24,9 +25,14 @@
  *     "storage_schema" = "Drupal\menu_link_content\MenuLinkContentStorageSchema",
  *     "access" = "Drupal\menu_link_content\MenuLinkContentAccessControlHandler",
  *     "form" = {
+ *       "add" = "Drupal\menu_link_content\Form\MenuLinkContentForm",
+ *       "edit" = "Drupal\menu_link_content\Form\MenuLinkContentForm",
  *       "default" = "Drupal\menu_link_content\Form\MenuLinkContentForm",
  *       "delete" = "Drupal\menu_link_content\Form\MenuLinkContentDeleteForm"
- *     }
+ *     },
+ *     "route_provider" = {
+ *       "html" = "Drupal\menu_link_content\Entity\MenuLinkContentHtmlRouteProvider",
+ *     },
  *   },
  *   admin_permission = "administer menu",
  *   base_table = "menu_link_content",
@@ -40,6 +46,7 @@
  *     "bundle" = "bundle"
  *   },
  *   links = {
+ *     "add-form" = "/admin/structure/menu/manage/{menu}/add",
  *     "canonical" = "/admin/structure/menu/item/{menu_link_content}/edit",
  *     "edit-form" = "/admin/structure/menu/item/{menu_link_content}/edit",
  *     "delete-form" = "/admin/structure/menu/item/{menu_link_content}/delete",
@@ -379,4 +386,17 @@ public function setRequiresRediscovery($rediscovery) {
     return $this;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function toUrl($rel = 'canonical', array $options = array()) {
+    // Account for legacy route names.
+    /** @var \Drupal\Core\Url $url */
+    $url = parent::toUrl($rel, $options);
+    if ($rel === 'add-form') {
+      $url = Url::fromRoute('entity.menu.add_link_form', $url->getRouteParameters(), $url->getOptions());
+    }
+    return $url;
+  }
+
 }
diff --git a/core/modules/menu_link_content/src/Entity/MenuLinkContentHtmlRouteProvider.php b/core/modules/menu_link_content/src/Entity/MenuLinkContentHtmlRouteProvider.php
new file mode 100644
index 0000000..0ed5edf
--- /dev/null
+++ b/core/modules/menu_link_content/src/Entity/MenuLinkContentHtmlRouteProvider.php
@@ -0,0 +1,66 @@
+<?php
+
+namespace Drupal\menu_link_content\Entity;
+
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider;
+
+/**
+ * Provides HTML routes for custom menu links.
+ */
+class MenuLinkContentHtmlRouteProvider extends DefaultHtmlRouteProvider {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRoutes(EntityTypeInterface $entity_type) {
+    $collection = parent::getRoutes($entity_type);
+    // Rename the add form route for backwards compatibility.
+    $entity_type_id = $entity_type->id();
+    if ($add_form_route = $collection->get("entity.{$entity_type_id}.add_form")) {
+      $collection->add('entity.menu.add_link_form', $add_form_route);
+      $collection->remove("entity.{$entity_type_id}.add_form");
+    }
+    return $collection;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getAddFormRoute(EntityTypeInterface $entity_type) {
+    if ($route = parent::getAddFormRoute($entity_type)) {
+      $route->setDefault('_title', 'Add menu link');
+      return $route;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getCanonicalRoute(EntityTypeInterface $entity_type) {
+    // Modules like Content Translation require a canonical route, but there is
+    // no view page for menu links, so we just reuse the edit form.
+    return $this->getEditFormRoute($entity_type);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getEditFormRoute(EntityTypeInterface $entity_type) {
+    if ($route = parent::getEditFormRoute($entity_type)) {
+      $route->setDefault('_title', 'Edit menu link');
+      return $route;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getDeleteFormRoute(EntityTypeInterface $entity_type) {
+    if ($route = parent::getDeleteFormRoute($entity_type)) {
+      $route->setDefault('_title', 'Delete menu link');
+      return $route;
+    }
+  }
+
+}
diff --git a/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php b/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
index ed372a9..d24db77 100644
--- a/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
+++ b/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
@@ -8,6 +8,8 @@
 use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Menu\MenuParentFormSelectorInterface;
 use Drupal\Core\Path\PathValidatorInterface;
+use Drupal\Core\Routing\RouteMatchInterface;
+use Drupal\Core\Url;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -69,6 +71,18 @@ public static function create(ContainerInterface $container) {
   /**
    * {@inheritdoc}
    */
+  public function getEntityFromRouteMatch(RouteMatchInterface $route_match, $entity_type_id) {
+    /** @var \Drupal\menu_link_content\MenuLinkContentInterface $entity */
+    $entity = parent::getEntityFromRouteMatch($route_match, $entity_type_id);
+    if ($menu_name = $route_match->getParameter('menu')) {
+      $entity->set('menu_name', $menu_name);
+    }
+    return $entity;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function form(array $form, FormStateInterface $form_state) {
     $form = parent::form($form, $form_state);
 
diff --git a/core/modules/shortcut/shortcut.routing.yml b/core/modules/shortcut/shortcut.routing.yml
index 1133357..a052ce0 100644
--- a/core/modules/shortcut/shortcut.routing.yml
+++ b/core/modules/shortcut/shortcut.routing.yml
@@ -46,32 +46,6 @@ entity.shortcut_set.customize_form:
   requirements:
     _entity_access: 'shortcut_set.update'
 
-shortcut.link_add:
-  path: '/admin/config/user-interface/shortcut/manage/{shortcut_set}/add-link'
-  defaults:
-    _controller: '\Drupal\shortcut\Controller\ShortcutController::addForm'
-    _title: 'Add link'
-  requirements:
-    _entity_create_access: 'shortcut:{shortcut_set}'
-
-entity.shortcut.canonical:
-  path: '/admin/config/user-interface/shortcut/link/{shortcut}'
-  defaults:
-    _entity_form: 'shortcut.default'
-    _title: 'Edit'
-  requirements:
-    _entity_access: 'shortcut.update'
-    shortcut: \d+
-
-entity.shortcut.edit_form:
-  path: '/admin/config/user-interface/shortcut/link/{shortcut}'
-  defaults:
-    _entity_form: 'shortcut.default'
-    _title: 'Edit'
-  requirements:
-    _entity_access: 'shortcut.update'
-    shortcut: \d+
-
 entity.shortcut.link_delete_inline:
   path: '/admin/config/user-interface/shortcut/link/{shortcut}/delete-inline'
   defaults:
@@ -81,15 +55,6 @@ entity.shortcut.link_delete_inline:
     _csrf_token: 'TRUE'
     shortcut: \d+
 
-entity.shortcut.delete_form:
-  path: '/admin/config/user-interface/shortcut/link/{shortcut}/delete'
-  defaults:
-    _entity_form: 'shortcut.delete'
-    _title: 'Delete'
-  requirements:
-    _entity_access: 'shortcut.delete'
-    shortcut: \d+
-
 shortcut.set_switch:
   path: '/user/{user}/shortcuts'
   defaults:
diff --git a/core/modules/shortcut/src/Controller/ShortcutController.php b/core/modules/shortcut/src/Controller/ShortcutController.php
index ff0e184..0dae46f 100644
--- a/core/modules/shortcut/src/Controller/ShortcutController.php
+++ b/core/modules/shortcut/src/Controller/ShortcutController.php
@@ -19,6 +19,9 @@ class ShortcutController extends ControllerBase {
    *
    * @return array
    *   The shortcut add form.
+   *
+   * @deprecated in 8.2.x, will be removed in 9.x. Use
+   *   '_entity_form: shortcut.add' in route declarations instead.
    */
   public function addForm(ShortcutSetInterface $shortcut_set) {
     $shortcut = $this->entityManager()->getStorage('shortcut')->create(array('shortcut_set' => $shortcut_set->id()));
diff --git a/core/modules/shortcut/src/Entity/Shortcut.php b/core/modules/shortcut/src/Entity/Shortcut.php
index 6ddaf05..25d7197 100644
--- a/core/modules/shortcut/src/Entity/Shortcut.php
+++ b/core/modules/shortcut/src/Entity/Shortcut.php
@@ -26,7 +26,10 @@
  *       "edit" = "Drupal\shortcut\ShortcutForm",
  *       "delete" = "Drupal\shortcut\Form\ShortcutDeleteForm"
  *     },
- *     "translation" = "Drupal\content_translation\ContentTranslationHandler"
+ *     "translation" = "Drupal\content_translation\ContentTranslationHandler",
+ *     "route_provider" = {
+ *       "html" = "Drupal\shortcut\Entity\ShortcutHtmlRouteProvider",
+ *     },
  *   },
  *   base_table = "shortcut",
  *   data_table = "shortcut_field_data",
@@ -39,6 +42,7 @@
  *     "langcode" = "langcode",
  *   },
  *   links = {
+ *     "add-form" = "/admin/config/user-interface/shortcut/manage/{shortcut_set}/add-link",
  *     "canonical" = "/admin/config/user-interface/shortcut/link/{shortcut}",
  *     "delete-form" = "/admin/config/user-interface/shortcut/link/{shortcut}/delete",
  *     "edit-form" = "/admin/config/user-interface/shortcut/link/{shortcut}",
diff --git a/core/modules/shortcut/src/Entity/ShortcutHtmlRouteProvider.php b/core/modules/shortcut/src/Entity/ShortcutHtmlRouteProvider.php
new file mode 100644
index 0000000..bff6d72
--- /dev/null
+++ b/core/modules/shortcut/src/Entity/ShortcutHtmlRouteProvider.php
@@ -0,0 +1,62 @@
+<?php
+
+namespace Drupal\shortcut\Entity;
+
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\Routing\DefaultHtmlRouteProvider;
+
+/**
+ * Provides HTML routes for block contents.
+ */
+class ShortcutHtmlRouteProvider extends DefaultHtmlRouteProvider {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getRoutes(EntityTypeInterface $entity_type) {
+    $collection = parent::getRoutes($entity_type);
+
+    // Rename the add form route for backwards compatibility.
+    $entity_type_id = $entity_type->id();
+    if ($add_form_route = $collection->get("entity.{$entity_type_id}.add_form")) {
+      $collection->add('shortcut.link_add', $add_form_route);
+      $collection->remove("entity.{$entity_type_id}.add_form");
+    }
+
+    return $collection;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getAddFormRoute(EntityTypeInterface $entity_type) {
+    if ($route = parent::getAddFormRoute($entity_type)) {
+      $defaults = $route->getDefaults();
+      unset($defaults['_title_callback']);
+      $defaults['_title'] = 'Add link';
+      return $route->setDefaults($defaults);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getCanonicalRoute(EntityTypeInterface $entity_type) {
+    // Modules like Content Translation require a canonical route, but there is
+    // no view page for shortcuts, so we just reuse the edit form.
+    return $this->getEditFormRoute($entity_type);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getEditFormRoute(EntityTypeInterface $entity_type) {
+    if ($route = parent::getEditFormRoute($entity_type)) {
+      $defaults = $route->getDefaults();
+      unset($defaults['_title_callback']);
+      $defaults['_title'] = 'Edit';
+      return $route->setDefaults($defaults);
+    }
+  }
+
+}
diff --git a/core/modules/taxonomy/src/Controller/TaxonomyController.php b/core/modules/taxonomy/src/Controller/TaxonomyController.php
index 843cdd7..d55dc50 100644
--- a/core/modules/taxonomy/src/Controller/TaxonomyController.php
+++ b/core/modules/taxonomy/src/Controller/TaxonomyController.php
@@ -20,6 +20,9 @@ class TaxonomyController extends ControllerBase {
    *
    * @return array
    *   The taxonomy term add form.
+   *
+   * @deprecated in 8.2.x, will be removed in 9.x. Use
+   *   '_entity_form: taxonomy_term.add' in route declarations instead.
    */
   public function addForm(VocabularyInterface $taxonomy_vocabulary) {
     $term = $this->entityManager()->getStorage('taxonomy_term')->create(array('vid' => $taxonomy_vocabulary->id()));
diff --git a/core/modules/taxonomy/src/Entity/Term.php b/core/modules/taxonomy/src/Entity/Term.php
index 2e41e2d..1e405bc 100644
--- a/core/modules/taxonomy/src/Entity/Term.php
+++ b/core/modules/taxonomy/src/Entity/Term.php
@@ -23,10 +23,15 @@
  *     "access" = "Drupal\taxonomy\TermAccessControlHandler",
  *     "views_data" = "Drupal\taxonomy\TermViewsData",
  *     "form" = {
+ *       "add" = "Drupal\taxonomy\TermForm",
+ *       "edit" = "Drupal\taxonomy\TermForm",
  *       "default" = "Drupal\taxonomy\TermForm",
  *       "delete" = "Drupal\taxonomy\Form\TermDeleteForm"
  *     },
- *     "translation" = "Drupal\taxonomy\TermTranslationHandler"
+ *     "translation" = "Drupal\taxonomy\TermTranslationHandler",
+ *     "route_provider" = {
+ *       "html" = "Drupal\taxonomy\Entity\TermHtmlRouteProvider",
+ *     },
  *   },
  *   base_table = "taxonomy_term_data",
  *   data_table = "taxonomy_term_field_data",
@@ -43,6 +48,7 @@
  *   field_ui_base_route = "entity.taxonomy_vocabulary.overview_form",
  *   common_reference_target = TRUE,
  *   links = {
+ *     "add-form" = "/admin/structure/taxonomy/manage/{taxonomy_vocabulary}/add",
  *     "canonical" = "/taxonomy/term/{taxonomy_term}",
  *     "delete-form" = "/taxonomy/term/{taxonomy_term}/delete",
  *     "edit-form" = "/taxonomy/term/{taxonomy_term}/edit",
diff --git a/core/modules/taxonomy/src/Entity/TermHtmlRouteProvider.php b/core/modules/taxonomy/src/Entity/TermHtmlRouteProvider.php
new file mode 100644
index 0000000..afb4fb8
--- /dev/null
+++ b/core/modules/taxonomy/src/Entity/TermHtmlRouteProvider.php
@@ -0,0 +1,37 @@
+<?php
+
+namespace Drupal\taxonomy\Entity;
+
+use Drupal\Core\Entity\EntityTypeInterface;
+use Drupal\Core\Entity\Routing\AdminHtmlRouteProvider;
+
+/**
+ * Provides HTML routes for block contents.
+ */
+class TermHtmlRouteProvider extends AdminHtmlRouteProvider {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getAddFormRoute(EntityTypeInterface $entity_type) {
+    if ($route = parent::getAddFormRoute($entity_type)) {
+      $defaults = $route->getDefaults();
+      unset($defaults['_title_callback']);
+      $defaults['_title'] = 'Add term';
+      return $route->setDefaults($defaults);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getEditFormRoute(EntityTypeInterface $entity_type) {
+    if ($route = parent::getEditFormRoute($entity_type)) {
+      $defaults = $route->getDefaults();
+      unset($defaults['_title_callback']);
+      $defaults['_title'] = 'Edit term';
+      return $route->setDefaults($defaults);
+    }
+  }
+
+}
diff --git a/core/modules/taxonomy/taxonomy.routing.yml b/core/modules/taxonomy/taxonomy.routing.yml
index 8a3bd1a..9377d92 100644
--- a/core/modules/taxonomy/taxonomy.routing.yml
+++ b/core/modules/taxonomy/taxonomy.routing.yml
@@ -6,36 +6,6 @@ entity.taxonomy_vocabulary.collection:
   requirements:
     _permission: 'administer taxonomy'
 
-entity.taxonomy_term.add_form:
-  path: '/admin/structure/taxonomy/manage/{taxonomy_vocabulary}/add'
-  defaults:
-    _controller: '\Drupal\taxonomy\Controller\TaxonomyController::addForm'
-    _title: 'Add term'
-  requirements:
-    _entity_create_access: 'taxonomy_term:{taxonomy_vocabulary}'
-
-entity.taxonomy_term.edit_form:
-  path: '/taxonomy/term/{taxonomy_term}/edit'
-  defaults:
-    _entity_form: 'taxonomy_term.default'
-    _title: 'Edit term'
-  options:
-    _admin_route: TRUE
-  requirements:
-    _entity_access: 'taxonomy_term.update'
-    taxonomy_term: \d+
-
-entity.taxonomy_term.delete_form:
-  path: '/taxonomy/term/{taxonomy_term}/delete'
-  defaults:
-    _entity_form: 'taxonomy_term.delete'
-    _title: 'Delete term'
-  options:
-    _admin_route: TRUE
-  requirements:
-    _entity_access: 'taxonomy_term.delete'
-    taxonomy_term: \d+
-
 entity.taxonomy_vocabulary.add_form:
   path: '/admin/structure/taxonomy/add'
   defaults:
