diff --git a/core/modules/block_content/block_content.routing.yml b/core/modules/block_content/block_content.routing.yml
index 65fc9e1df6..407257745a 100644
--- a/core/modules/block_content/block_content.routing.yml
+++ b/core/modules/block_content/block_content.routing.yml
@@ -1,54 +1,3 @@
-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.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 95ba40da6d..7c2e86e6e9 100644
--- a/core/modules/block_content/src/Entity/BlockContent.php
+++ b/core/modules/block_content/src/Entity/BlockContent.php
@@ -36,7 +36,10 @@
  *       "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",
@@ -45,6 +48,8 @@
  *   revision_data_table = "block_content_field_revision",
  *   show_revision_ui = TRUE,
  *   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 0000000000..12534b8461
--- /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 e92e8daf79..d594bad665 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:
diff --git a/core/modules/comment/src/Controller/CommentController.php b/core/modules/comment/src/Controller/CommentController.php
index 2cba4ac918..1bc417d229 100644
--- a/core/modules/comment/src/Controller/CommentController.php
+++ b/core/modules/comment/src/Controller/CommentController.php
@@ -171,6 +171,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->entityRepository->getTranslationFromContext($comment)->label();
diff --git a/core/modules/comment/src/Entity/Comment.php b/core/modules/comment/src/Entity/Comment.php
index f9afe73124..b1cb6bbddf 100644
--- a/core/modules/comment/src/Entity/Comment.php
+++ b/core/modules/comment/src/Entity/Comment.php
@@ -39,7 +39,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 0000000000..65524bdc69
--- /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 8c00ee0704..0000000000
--- 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 35ea1b660d..5c3630962f 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->entityTypeManager()
diff --git a/core/modules/menu_link_content/src/Entity/MenuLinkContent.php b/core/modules/menu_link_content/src/Entity/MenuLinkContent.php
index 1132883cd6..c1bc2e5caa 100644
--- a/core/modules/menu_link_content/src/Entity/MenuLinkContent.php
+++ b/core/modules/menu_link_content/src/Entity/MenuLinkContent.php
@@ -6,6 +6,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;
 
@@ -30,9 +31,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",
@@ -55,6 +61,7 @@
  *     "revision_log_message" = "revision_log_message",
  *   },
  *   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",
@@ -424,4 +431,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 0000000000..0ed5edfd5a
--- /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 a7525c0ddc..16605685ac 100644
--- a/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
+++ b/core/modules/menu_link_content/src/Form/MenuLinkContentForm.php
@@ -10,6 +10,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;
 
 /**
@@ -76,6 +78,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}
    */
diff --git a/core/modules/shortcut/shortcut.routing.yml b/core/modules/shortcut/shortcut.routing.yml
index 0bf0a3bd28..9d0a0d1c81 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 d70cd7e07f..7b45dee149 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->entityTypeManager()->getStorage('shortcut')->create(['shortcut_set' => $shortcut_set->id()]);
diff --git a/core/modules/shortcut/src/Entity/Shortcut.php b/core/modules/shortcut/src/Entity/Shortcut.php
index 0cca92e2d9..3f2c2330c5 100644
--- a/core/modules/shortcut/src/Entity/Shortcut.php
+++ b/core/modules/shortcut/src/Entity/Shortcut.php
@@ -34,6 +34,9 @@
  *       "edit" = "Drupal\shortcut\ShortcutForm",
  *       "delete" = "Drupal\shortcut\Form\ShortcutDeleteForm"
  *     },
+ *     "route_provider" = {
+ *       "html" = "Drupal\shortcut\Entity\ShortcutHtmlRouteProvider",
+ *     },
  *   },
  *   base_table = "shortcut",
  *   data_table = "shortcut_field_data",
@@ -46,6 +49,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 0000000000..bff6d72184
--- /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 3c04339009..15fc017023 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->entityTypeManager()->getStorage('taxonomy_term')->create(['vid' => $taxonomy_vocabulary->id()]);
diff --git a/core/modules/taxonomy/src/Entity/Term.php b/core/modules/taxonomy/src/Entity/Term.php
index 8d3aaaee7c..46a83a0508 100644
--- a/core/modules/taxonomy/src/Entity/Term.php
+++ b/core/modules/taxonomy/src/Entity/Term.php
@@ -31,10 +31,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",
@@ -60,6 +65,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 0000000000..afb4fb8bf2
--- /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 a9a30a91a3..c8634cb8dc 100644
--- a/core/modules/taxonomy/taxonomy.routing.yml
+++ b/core/modules/taxonomy/taxonomy.routing.yml
@@ -1,33 +1,3 @@
-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_term.canonical:
   path: '/taxonomy/term/{taxonomy_term}'
   defaults:
