diff --git c/core/modules/book/book.module w/core/modules/book/book.module
index 1039b59..282aa92 100644
--- c/core/modules/book/book.module
+++ w/core/modules/book/book.module
@@ -195,11 +195,7 @@ function book_menu() {
   );
   $items['node/%node/outline/remove'] = array(
     'title' => 'Remove from outline',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('book_remove_form', 1),
-    'access callback' => '_book_outline_remove_access',
-    'access arguments' => array(1),
-    'file' => 'book.pages.inc',
+    'route_name' => 'book.remove',
   );
 
   return $items;
diff --git c/core/modules/book/book.pages.inc w/core/modules/book/book.pages.inc
index fe4c6a0..3462906 100644
--- c/core/modules/book/book.pages.inc
+++ w/core/modules/book/book.pages.inc
@@ -30,6 +30,8 @@ function book_remove_button_submit($form, &$form_state) {
  * @see book_remove_form_submit()
  * @see book_menu()
  * @ingroup forms
+ *
+ * @depracated Use \Drupal\book\Form\BookForm::remove()
  */
 function book_remove_form($form, &$form_state, EntityInterface $node) {
   $form['#node'] = $node;
diff --git c/core/modules/book/book.routing.yml w/core/modules/book/book.routing.yml
index 39aff5c..c97aa87 100644
--- c/core/modules/book/book.routing.yml
+++ w/core/modules/book/book.routing.yml
@@ -50,3 +50,15 @@ book.admin_edit:
     _permission: 'administer book outlines'
     _entity_access: 'node.view'
     node: \d+
+
+book.remove:
+  path: '/node/{node}/outline/remove'
+  defaults:
+    _content: '\Drupal\book\Form\BookForm::remove'
+    _title: 'Remove from outline'
+  options:
+    _access_mode: 'ALL'
+  requirements:
+    _permission: 'administer book outlines'
+    _entity_access: 'node.view'
+    _access_book_removable: 'TRUE'
diff --git c/core/modules/book/book.services.yml w/core/modules/book/book.services.yml
index 510573a..2f1097a 100644
--- c/core/modules/book/book.services.yml
+++ w/core/modules/book/book.services.yml
@@ -5,3 +5,9 @@ services:
   book.export:
     class: Drupal\book\BookExport
     arguments: ['@entity.manager']
+
+  access_check.book.removable:
+    class: Drupal\book\Access\BookNodeIsRemovableAccessCheck
+    arguments: ['@book.manager']
+    tags:
+      - { name: access_check }
diff --git c/core/modules/book/lib/Drupal/book/Access/BookNodeIsRemovableAccessCheck.php w/core/modules/book/lib/Drupal/book/Access/BookNodeIsRemovableAccessCheck.php
new file mode 100644
index 0000000..ccf343c
--- /dev/null
+++ w/core/modules/book/lib/Drupal/book/Access/BookNodeIsRemovableAccessCheck.php
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\book\Access\BookNodeIsRemovableAccessCheck.
+ */
+
+namespace Drupal\book\Access;
+
+use Drupal\book\BookManager;
+use Drupal\Core\Access\StaticAccessCheckInterface;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Determines whether the requested node can be removed from its book.
+ */
+class BookNodeIsRemovableAccessCheck implements StaticAccessCheckInterface {
+
+  /**
+   * Book Manager Service.
+   *
+   * @var \Drupal\book\BookManager
+   */
+  protected $bookManager;
+
+  /**
+   * Constructs a BookNodeIsRemovableAccessCheck object.
+   *
+   * @param \Drupal\book\BookManager $book_manager
+   *   Book Manager Service.
+   */
+  public function __construct(BookManager $book_manager) {
+    $this->bookManager = $book_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function appliesTo() {
+    return array('_access_book_removable');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access(Route $route, Request $request) {
+    $node = $request->attributes->get('node');
+    if (!empty($node)) {
+      return $this->bookManager->checkNodeIsRemovable($node) ? static::ALLOW : static::DENY;
+    }
+    return static::DENY;
+  }
+
+}
diff --git c/core/modules/book/lib/Drupal/book/Form/BookForm.php w/core/modules/book/lib/Drupal/book/Form/BookForm.php
new file mode 100644
index 0000000..c69ecd6
--- /dev/null
+++ w/core/modules/book/lib/Drupal/book/Form/BookForm.php
@@ -0,0 +1,26 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\book\Form\BookForm.
+ */
+
+namespace Drupal\book\Form;
+
+use Drupal\Core\Entity\EntityInterface;
+
+/**
+ * Temporary form controller for book module.
+ */
+class BookForm {
+
+  /**
+   * Wraps book_remove_form().
+   *
+   * @todo Remove book_remove_form().
+   */
+  public function remove(EntityInterface $node) {
+    module_load_include('pages.inc', 'book');
+    return drupal_get_form('book_remove_form', $node);
+  }
+
+}
diff --git c/core/modules/content_translation/content_translation.module w/core/modules/content_translation/content_translation.module
index f26ff20..f2ae71d 100644
--- c/core/modules/content_translation/content_translation.module
+++ w/core/modules/content_translation/content_translation.module
@@ -192,10 +192,7 @@ function content_translation_menu() {
       // Delete translation callback.
       $items["$path/translations/delete/%language"] = array(
         'title' => 'Delete',
-        'page callback' => 'drupal_get_form',
-        'page arguments' => array('content_translation_delete_confirm', $entity_position, $language_position),
-        'access callback' => 'content_translation_delete_access',
-        'access arguments' => $args,
+        'route_name' => "content_translation.delete_$entity_type",
       ) + $item;
     }
   }
diff --git c/core/modules/content_translation/content_translation.pages.inc w/core/modules/content_translation/content_translation.pages.inc
index a7ce4af..3ead3e1 100644
--- c/core/modules/content_translation/content_translation.pages.inc
+++ w/core/modules/content_translation/content_translation.pages.inc
@@ -242,6 +242,8 @@ function content_translation_prepare_translation(EntityInterface $entity, Langua
 
 /**
  * Form constructor for the translation deletion confirmation.
+ *
+ * @depracated Use \Drupal\content_translation\Form\ContentTranslationForm::deleteTranslation()
  */
 function content_translation_delete_confirm(array $form, array $form_state, EntityInterface $entity, Language $language) {
   $langcode = $language->id;
diff --git c/core/modules/content_translation/content_translation.services.yml w/core/modules/content_translation/content_translation.services.yml
index 678724d..c6514bc 100644
--- c/core/modules/content_translation/content_translation.services.yml
+++ w/core/modules/content_translation/content_translation.services.yml
@@ -2,3 +2,15 @@ services:
   content_translation.synchronizer:
     class: Drupal\content_translation\FieldTranslationSynchronizer
     arguments: ['@entity.manager']
+
+  content_translation.subscriber:
+    class: Drupal\content_translation\Routing\ContentTranslationRouteSubscriber
+    arguments: ['@plugin.manager.entity']
+    tags:
+      - { name: event_subscriber }
+
+  content_translation.manage_access:
+    class: Drupal\content_translation\Access\ContentTranslationManageAccessCheck
+    arguments: ['@plugin.manager.entity']
+    tags:
+      - { name: access_check }
diff --git c/core/modules/content_translation/lib/Drupal/content_translation/Access/ContentTranslationManageAccessCheck.php w/core/modules/content_translation/lib/Drupal/content_translation/Access/ContentTranslationManageAccessCheck.php
new file mode 100644
index 0000000..6a0f360
--- /dev/null
+++ w/core/modules/content_translation/lib/Drupal/content_translation/Access/ContentTranslationManageAccessCheck.php
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\content_translation\Access\ContentTranslationManageAccessCheck.
+ */
+
+namespace Drupal\content_translation\Access;
+
+use Drupal\Core\Entity\EntityManager;
+use Drupal\Core\Access\StaticAccessCheckInterface;
+use Drupal\Core\Language\Language;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Access check for entity translation CRUD operation.
+ */
+class ContentTranslationManageAccessCheck implements StaticAccessCheckInterface {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManager
+   */
+  protected $entityManager;
+
+  /**
+   * Constructs a ContentTranslationManageAccessCheck object.
+   *
+   * @param \Drupal\Core\Entity\EntityManager $manager
+   *   The entity type manager.
+   */
+  public function __construct(EntityManager $manager) {
+    $this->entityManager = $manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function appliesTo() {
+    return array('_access_content_translation_manage');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access(Route $route, Request $request) {
+    if ($entity = $request->attributes->get('entity')) {
+      $operation = $route->getRequirement('_access_content_translation_manage');
+      $entity_type = $entity->entityType();
+      $controller_class = $this->entityManager->getControllerClass($entity_type, 'translation');
+      $controller = new $controller_class($entity_type, $entity->entityInfo());
+
+      // Load translation.
+      $translations = $entity->getTranslationLanguages();
+      $languages = language_list();
+
+      if ($operation == 'delete') {
+        $language = language_load($request->attributes->get('language'));
+        $language = !empty($language) ? $language : language(Language::TYPE_CONTENT);
+        return isset($languages[$language->id])
+          && $language->id != $entity->getUntranslated()->language()->id
+          && isset($translations[$language->id])
+          && $controller->getTranslationAccess($entity, $operation)
+          ? static::ALLOW : static::DENY;
+      }
+    }
+    return static::DENY;
+  }
+}
diff --git c/core/modules/content_translation/lib/Drupal/content_translation/Form/ContentTranslationForm.php w/core/modules/content_translation/lib/Drupal/content_translation/Form/ContentTranslationForm.php
new file mode 100644
index 0000000..7142a49
--- /dev/null
+++ w/core/modules/content_translation/lib/Drupal/content_translation/Form/ContentTranslationForm.php
@@ -0,0 +1,28 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\content_translation\Form\ContentTranslationForm.
+ */
+
+namespace Drupal\content_translation\Form;
+
+use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Language\Language;
+
+/**
+ * Temporary form controller for content_translation module.
+ */
+class ContentTranslationForm {
+
+  /**
+   * Wraps content_translation_delete_confirm().
+   *
+   * @todo Remove content_translation_delete_confirm().
+   */
+  public function deleteTranslation(EntityInterface $entity, $language) {
+    module_load_include('pages.inc', 'content_translation');
+    $language = language_load($language);
+    return drupal_get_form('content_translation_delete_confirm', $entity, $language);
+  }
+
+}
diff --git c/core/modules/content_translation/lib/Drupal/content_translation/Routing/ContentTranslationRouteSubscriber.php w/core/modules/content_translation/lib/Drupal/content_translation/Routing/ContentTranslationRouteSubscriber.php
new file mode 100644
index 0000000..9826b62
--- /dev/null
+++ w/core/modules/content_translation/lib/Drupal/content_translation/Routing/ContentTranslationRouteSubscriber.php
@@ -0,0 +1,80 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\content_translation\Routing\ContentTranslationRouteSubscriber.
+ */
+
+namespace Drupal\content_translation\Routing;
+
+use Drupal\Core\Routing\RouteBuildEvent;
+use Drupal\Core\Routing\RoutingEvents;
+use Drupal\Core\Entity\EntityManager;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+use Symfony\Component\Routing\Route;
+
+/**
+ * Subscriber for entity translation routes.
+ */
+class ContentTranslationRouteSubscriber implements EventSubscriberInterface {
+
+  /**
+   * The entity type manager.
+   *
+   * @var \Drupal\Core\Entity\EntityManager
+   */
+  protected $entityManager;
+
+  /**
+   * Constructs a ContentTranslationRouteSubscriber object.
+   *
+   * @param \Drupal\Core\Entity\EntityManager $entityManager
+   *   The entity type manager.
+   */
+  public function __construct(EntityManager $entityManager) {
+    $this->entityManager = $entityManager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function getSubscribedEvents() {
+    $events[RoutingEvents::DYNAMIC] = 'routes';
+    return $events;
+  }
+
+  /**
+   * Adds routes for entity translations.
+   */
+  public function routes(RouteBuildEvent $event) {
+    $collection = $event->getRouteCollection();
+    foreach ($this->entityManager->getDefinitions() as $entity_type => $entity_info) {
+      if ($entity_info['translatable'] && isset($entity_info['translation'])) {
+        $route = new Route(
+          '/' . str_replace($entity_info['menu_path_wildcard'], '{entity}', $entity_info['menu_base_path']) . "/translations/delete/{language}",
+          array(
+            '_content' => '\Drupal\content_translation\Form\ContentTranslationForm::deleteTranslation',
+            'language' => NULL,
+            '_title' => 'Delete',
+          ),
+          array(
+            '_permission' => 'translate any entity',
+            '_access_content_translation_manage' => 'delete',
+          ),
+          array(
+            'parameters' => array(
+              'entity' => array(
+                'type' => 'entity:' . $entity_type,
+              ),
+              'language' => array(
+                'type' => 'language',
+              ),
+            ),
+          )
+        );
+        $collection->add("content_translation.delete_$entity_type", $route);
+      }
+    }
+  }
+
+}
diff --git c/core/modules/language/language.admin.inc w/core/modules/language/language.admin.inc
index 562580b..ef9a93f 100644
--- c/core/modules/language/language.admin.inc
+++ w/core/modules/language/language.admin.inc
@@ -26,6 +26,8 @@ function language_admin_predefined_list() {
 
 /**
  * Builds the configuration form for language negotiation.
+ *
+ * @depracated Use \Drupal\language\Form\LanguageForm::negotiation()
  */
 function language_negotiation_configure_form() {
   language_negotiation_include();
diff --git c/core/modules/language/language.module w/core/modules/language/language.module
index 6031e33..52c14c9 100644
--- c/core/modules/language/language.module
+++ w/core/modules/language/language.module
@@ -92,11 +92,8 @@ function language_menu() {
   // Language negotiation.
   $items['admin/config/regional/language/detection'] = array(
     'title' => 'Detection and selection',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('language_negotiation_configure_form'),
-    'access arguments' => array('administer languages'),
+    'route_name' => 'language.negotiation',
     'weight' => 10,
-    'file' => 'language.admin.inc',
     'type' => MENU_LOCAL_TASK,
   );
   $items['admin/config/regional/language/detection/url'] = array(
diff --git c/core/modules/language/language.routing.yml w/core/modules/language/language.routing.yml
index ae980ec..a2628e6 100644
--- c/core/modules/language/language.routing.yml
+++ w/core/modules/language/language.routing.yml
@@ -61,3 +61,11 @@ language.negotiation_browser_delete:
     _form: '\Drupal\language\Form\NegotiationBrowserDeleteForm'
   requirements:
     _permission: 'administer languages'
+
+language.negotiation:
+  path: '/admin/config/regional/language/detection'
+  defaults:
+    _content: '\Drupal\language\Form\LanguageForm::negotiation'
+    _title: 'Detection and selection'
+  requirements:
+    _permission: 'administer languages'
diff --git c/core/modules/language/lib/Drupal/language/Form/LanguageForm.php w/core/modules/language/lib/Drupal/language/Form/LanguageForm.php
new file mode 100644
index 0000000..9194c6b
--- /dev/null
+++ w/core/modules/language/lib/Drupal/language/Form/LanguageForm.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\language\Form\LanguageForm.
+ */
+
+namespace Drupal\language\Form;
+
+/**
+ * Temporary form controller for language module.
+ */
+class LanguageForm {
+
+  /**
+   * Wraps language_negotiation_configure_form().
+   *
+   * @todo Remove language_negotiation_configure_form().
+   */
+  public function negotiation() {
+    module_load_include('admin.inc', 'language');
+    return drupal_get_form('language_negotiation_configure_form');
+  }
+
+}
diff --git c/core/modules/locale/lib/Drupal/locale/Form/LocaleForm.php w/core/modules/locale/lib/Drupal/locale/Form/LocaleForm.php
new file mode 100644
index 0000000..c06a4aa
--- /dev/null
+++ w/core/modules/locale/lib/Drupal/locale/Form/LocaleForm.php
@@ -0,0 +1,44 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\locale\Form\LocaleForm.
+ */
+
+namespace Drupal\locale\Form;
+
+/**
+ * Temporary form controller for locale module.
+ */
+class LocaleForm {
+
+  /**
+   * Wraps locale_translate_import_form().
+   *
+   * @todo Remove locale_translate_import_form().
+   */
+  public function import() {
+    module_load_include('bulk.inc', 'locale');
+    return drupal_get_form('locale_translate_import_form');
+  }
+
+  /**
+   * Wraps locale_translate_export_form().
+   *
+   * @todo Remove locale_translate_export_form().
+   */
+  public function export() {
+    module_load_include('bulk.inc', 'locale');
+    return drupal_get_form('locale_translate_export_form');
+  }
+
+  /**
+   * Wraps locale_translation_status_form().
+   *
+   * @todo Remove locale_translation_status_form().
+   */
+  public function status() {
+    module_load_include('pages.inc', 'locale');
+    return drupal_get_form('locale_translation_status_form');
+  }
+
+}
diff --git c/core/modules/locale/locale.bulk.inc w/core/modules/locale/locale.bulk.inc
index 355ccb1..082656f 100644
--- c/core/modules/locale/locale.bulk.inc
+++ w/core/modules/locale/locale.bulk.inc
@@ -17,6 +17,8 @@
  *
  * @see locale_translate_import_form_submit()
  * @ingroup forms
+ *
+ * @depracated Use \Drupal\locale\Form\LocaleForm::import()
  */
 function locale_translate_import_form($form, &$form_state) {
   drupal_static_reset('language_list');
@@ -144,6 +146,8 @@ function locale_translate_import_form_submit($form, &$form_state) {
  *
  * @see locale_translate_export_form_submit()
  * @ingroup forms
+ *
+ * @depracated Use \Drupal\locale\Form\LocaleForm::export()
  */
 function locale_translate_export_form($form, &$form_state) {
   $languages = language_list();
diff --git c/core/modules/locale/locale.module w/core/modules/locale/locale.module
index f426670..6ba156d 100644
--- c/core/modules/locale/locale.module
+++ w/core/modules/locale/locale.module
@@ -182,21 +182,15 @@ function locale_menu() {
   );
   $items['admin/config/regional/translate/import'] = array(
     'title' => 'Import',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('locale_translate_import_form'),
-    'access arguments' => array('translate interface'),
+    'route_name' => 'locale.translate_import',
     'weight' => 20,
     'type' => MENU_LOCAL_TASK,
-    'file' => 'locale.bulk.inc',
   );
   $items['admin/config/regional/translate/export'] = array(
     'title' => 'Export',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('locale_translate_export_form'),
-    'access arguments' => array('translate interface'),
+    'route_name' => 'locale.translate_export',
     'weight' => 30,
     'type' => MENU_LOCAL_TASK,
-    'file' => 'locale.bulk.inc',
   );
   $items['admin/config/regional/translate/settings'] = array(
     'title' => 'Settings',
@@ -206,11 +200,8 @@ function locale_menu() {
   );
   $items['admin/reports/translations'] = array(
     'title' => 'Available translation updates',
+    'route_name' => 'locale.translate_status',
     'description' => 'Get a status report about available interface translations for your installed modules and themes.',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('locale_translation_status_form'),
-    'access arguments' => array('translate interface'),
-    'file' => 'locale.pages.inc',
   );
 
   return $items;
diff --git c/core/modules/locale/locale.pages.inc w/core/modules/locale/locale.pages.inc
index 1e045fe..f3cea89 100644
--- c/core/modules/locale/locale.pages.inc
+++ w/core/modules/locale/locale.pages.inc
@@ -40,6 +40,8 @@ function locale_translation_manual_status() {
  * Page callback: Display the current translation status.
  *
  * @see locale_menu()
+ *
+ * @depracated Use \Drupal\locale\Form\LocaleForm::status()
  */
 function locale_translation_status_form($form, &$form_state) {
   module_load_include('translation.inc', 'locale');
diff --git c/core/modules/locale/locale.routing.yml w/core/modules/locale/locale.routing.yml
index 7b08569..cc60aec 100644
--- c/core/modules/locale/locale.routing.yml
+++ w/core/modules/locale/locale.routing.yml
@@ -18,3 +18,27 @@ locale.translate_page:
     _content: 'Drupal\locale\Controller\LocaleController::translatePage'
   requirements:
     _permission: 'translate interface'
+
+locale.translate_import:
+  path: '/admin/config/regional/translate/import'
+  defaults:
+    _content: '\Drupal\locale\Form\LocaleForm::import'
+    _title: 'Import'
+  requirements:
+    _permission: 'translate interface'
+
+locale.translate_export:
+  path: '/admin/config/regional/translate/export'
+  defaults:
+    _content: '\Drupal\locale\Form\LocaleForm::export'
+    _title: 'Export'
+  requirements:
+    _permission: 'translate interface'
+
+locale.translate_status:
+  path: '/admin/reports/translations'
+  defaults:
+    _content: '\Drupal\locale\Form\LocaleForm::status'
+    _title: 'Available translation updates'
+  requirements:
+    _permission: 'translate interface'
diff --git c/core/modules/shortcut/lib/Drupal/shortcut/Access/LinkAccessCheck.php w/core/modules/shortcut/lib/Drupal/shortcut/Access/LinkAccessCheck.php
new file mode 100644
index 0000000..85e48b2
--- /dev/null
+++ w/core/modules/shortcut/lib/Drupal/shortcut/Access/LinkAccessCheck.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\shortcut\Access\LinkAccessCheck.
+ */
+
+namespace Drupal\shortcut\Access;
+
+use Drupal\Core\Access\StaticAccessCheckInterface;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Provides an access check for shortcut link delete routes.
+ */
+class LinkAccessCheck implements StaticAccessCheckInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function appliesTo() {
+    return array('_access_shortcut_link');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access(Route $route, Request $request) {
+    $menu_link = $request->attributes->get('menu_link');
+    $set_name = str_replace('shortcut-', '', $menu_link['menu_name']);
+    if ($shortcut_set = shortcut_set_load($set_name)) {
+      return shortcut_set_edit_access($shortcut_set) ? static::ALLOW : static::DENY;
+    }
+    return static::DENY;
+  }
+
+}
diff --git c/core/modules/shortcut/lib/Drupal/shortcut/Access/LinkDeleteAccessCheck.php w/core/modules/shortcut/lib/Drupal/shortcut/Access/LinkDeleteAccessCheck.php
deleted file mode 100644
index 30153df..0000000
--- c/core/modules/shortcut/lib/Drupal/shortcut/Access/LinkDeleteAccessCheck.php
+++ /dev/null
@@ -1,37 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains Drupal\shortcut\Access\LinkDeleteAccessCheck.
- */
-
-namespace Drupal\shortcut\Access;
-
-use Drupal\Core\Access\StaticAccessCheckInterface;
-use Symfony\Component\Routing\Route;
-use Symfony\Component\HttpFoundation\Request;
-
-/**
- * Provides an access check for shortcut link delete routes.
- */
-class LinkDeleteAccessCheck implements StaticAccessCheckInterface {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function appliesTo() {
-    return array('_access_shortcut_link_delete');
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function access(Route $route, Request $request) {
-    $menu_link = $request->attributes->get('menu_link');
-    $set_name = str_replace('shortcut-', '', $menu_link['menu_name']);
-    if ($shortcut_set = shortcut_set_load($set_name)) {
-      return shortcut_set_edit_access($shortcut_set) ? static::ALLOW : static::DENY;
-    }
-  }
-
-}
diff --git c/core/modules/shortcut/lib/Drupal/shortcut/Access/ShortcutSetEditAccessCheck.php w/core/modules/shortcut/lib/Drupal/shortcut/Access/ShortcutSetEditAccessCheck.php
new file mode 100644
index 0000000..7ca4b01
--- /dev/null
+++ w/core/modules/shortcut/lib/Drupal/shortcut/Access/ShortcutSetEditAccessCheck.php
@@ -0,0 +1,43 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\shortcut\Access\ShortcutSetEditAccessCheck.
+ */
+
+namespace Drupal\shortcut\Access;
+
+use Drupal\Core\Access\StaticAccessCheckInterface;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Provides an access check for shortcut link delete routes.
+ */
+class ShortcutSetEditAccessCheck implements StaticAccessCheckInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function appliesTo() {
+    return array('_access_shortcut_set_edit');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access(Route $route, Request $request) {
+    $account = \Drupal::currentUser();
+    $shortcut_set = $request->attributes->get('shortcut_set');
+    // Sufficiently-privileged users can edit their currently displayed shortcut
+    // set, but not other sets. Shortcut administrators can edit any set.
+    if ($account->hasPermission('administer shortcuts')) {
+      return static::ALLOW;
+    }
+    if ($account->hasPermission('customize shortcut links')) {
+      return !isset($shortcut_set) || $shortcut_set == shortcut_current_displayed_set() ? static::ALLOW : static::DENY;
+    }
+    return static::DENY;
+  }
+
+}
diff --git c/core/modules/shortcut/lib/Drupal/shortcut/Access/ShortcutSetSwitchAccessCheck.php w/core/modules/shortcut/lib/Drupal/shortcut/Access/ShortcutSetSwitchAccessCheck.php
new file mode 100644
index 0000000..aacecc4
--- /dev/null
+++ w/core/modules/shortcut/lib/Drupal/shortcut/Access/ShortcutSetSwitchAccessCheck.php
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\shortcut\Access\ShortcutSetSwitchAccessCheck.
+ */
+
+namespace Drupal\shortcut\Access;
+
+use Drupal\Core\Access\StaticAccessCheckInterface;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Provides an access check for shortcut link delete routes.
+ */
+class ShortcutSetSwitchAccessCheck implements StaticAccessCheckInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function appliesTo() {
+    return array('_access_shortcut_set_switch');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access(Route $route, Request $request) {
+    $user = \Drupal::currentUser();
+    $account = $request->attributes->get('account');
+
+    if ($user->hasPermission('administer shortcuts')) {
+      // Administrators can switch anyone's shortcut set.
+      return static::ALLOW;
+    }
+
+    if (!$user->hasPermission('switch shortcut sets')) {
+      // The user has no permission to switch anyone's shortcut set.
+      return static::DENY;
+    }
+
+    if (!isset($account) || $user->id() == $account->id()) {
+      // Users with the 'switch shortcut sets' permission can switch their own
+      // shortcuts sets.
+      return static::ALLOW;
+    }
+    return static::DENY;
+  }
+
+}
diff --git c/core/modules/shortcut/lib/Drupal/shortcut/Form/ShortcutForm.php w/core/modules/shortcut/lib/Drupal/shortcut/Form/ShortcutForm.php
new file mode 100644
index 0000000..fc5fec2
--- /dev/null
+++ w/core/modules/shortcut/lib/Drupal/shortcut/Form/ShortcutForm.php
@@ -0,0 +1,48 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\shortcut\Form\ShortcutForm.
+ */
+
+namespace Drupal\shortcut\Form;
+
+use Drupal\menu_link\MenuLinkInterface;
+use Drupal\shortcut\ShortcutSetInterface;
+use Drupal\user\UserInterface;
+
+/**
+ * Temporary form controller for shortcut module.
+ */
+class ShortcutForm {
+
+  /**
+   * Wraps shortcut_link_edit().
+   *
+   * @todo Remove shortcut_link_edit().
+   */
+  public function edit(MenuLinkInterface $menu_link) {
+    module_load_include('admin.inc', 'shortcut');
+    return drupal_get_form('shortcut_link_edit', $menu_link);
+  }
+
+  /**
+   * Wraps shortcut_link_add().
+   *
+   * @todo Remove shortcut_link_add().
+   */
+  public function add(ShortcutSetInterface $shortcut_set) {
+    module_load_include('admin.inc', 'shortcut');
+    return drupal_get_form('shortcut_link_add', $shortcut_set);
+  }
+
+  /**
+   * Wraps shortcut_set_switch().
+   *
+   * @todo Remove shortcut_set_switch().
+   */
+  public function overview(UserInterface $user) {
+    module_load_include('admin.inc', 'shortcut');
+    return drupal_get_form('shortcut_set_switch', $user);
+  }
+
+}
diff --git c/core/modules/shortcut/shortcut.admin.inc w/core/modules/shortcut/shortcut.admin.inc
index 523fead..f24f5db 100644
--- c/core/modules/shortcut/shortcut.admin.inc
+++ w/core/modules/shortcut/shortcut.admin.inc
@@ -25,6 +25,8 @@
  * @ingroup forms
  * @see shortcut_set_switch_validate()
  * @see shortcut_set_switch_submit()
+ *
+ * @depracated Use \Drupal\shortcut\Form\ShortcutForm::overview()
  */
 function shortcut_set_switch($form, &$form_state, $account = NULL) {
   $user = \Drupal::currentUser();
@@ -188,6 +190,8 @@ function shortcut_set_switch_submit($form, &$form_state) {
  * @ingroup forms
  * @see shortcut_link_edit_validate()
  * @see shortcut_link_add_submit()
+ *
+ * @depracated Use \Drupal\shortcut\Form\ShortcutForm::add()
  */
 function shortcut_link_add($form, &$form_state, $shortcut_set) {
   drupal_set_title(t('Add new shortcut'));
@@ -215,6 +219,8 @@ function shortcut_link_add($form, &$form_state, $shortcut_set) {
  * @ingroup forms
  * @see shortcut_link_edit_validate()
  * @see shortcut_link_edit_submit()
+ *
+ * @depracated Use \Drupal\shortcut\Form\ShortcutForm::edit()
  */
 function shortcut_link_edit($form, &$form_state, $shortcut_link) {
   drupal_set_title(t('Editing @shortcut', array('@shortcut' => $shortcut_link['link_title'])));
diff --git c/core/modules/shortcut/shortcut.module w/core/modules/shortcut/shortcut.module
index e47b5ed..1368558 100644
--- c/core/modules/shortcut/shortcut.module
+++ w/core/modules/shortcut/shortcut.module
@@ -117,20 +117,12 @@ function shortcut_menu() {
   );
   $items['admin/config/user-interface/shortcut/manage/%shortcut_set/add-link'] = array(
     'title' => 'Add shortcut',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('shortcut_link_add', 5),
-    'access callback' => 'shortcut_set_edit_access',
-    'access arguments' => array(5),
+    'route_name' => 'shortcut.link_edit',
     'type' => MENU_LOCAL_ACTION,
-    'file' => 'shortcut.admin.inc',
   );
   $items['admin/config/user-interface/shortcut/link/%menu_link'] = array(
     'title' => 'Edit shortcut',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('shortcut_link_edit', 5),
-    'access callback' => 'shortcut_link_access',
-    'access arguments' => array(5),
-    'file' => 'shortcut.admin.inc',
+    'route_name' => 'shortcut.link_edit',
   );
   $items['admin/config/user-interface/shortcut/link/%menu_link/delete'] = array(
     'title' => 'Delete shortcut',
@@ -138,12 +130,8 @@ function shortcut_menu() {
   );
   $items['user/%user/shortcuts'] = array(
     'title' => 'Shortcuts',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('shortcut_set_switch', 1),
-    'access callback' => 'shortcut_set_switch_access',
-    'access arguments' => array(1),
+    'route_name' => 'shortcut.overview',
     'type' => MENU_LOCAL_TASK,
-    'file' => 'shortcut.admin.inc',
   );
 
   return $items;
diff --git c/core/modules/shortcut/shortcut.routing.yml w/core/modules/shortcut/shortcut.routing.yml
index 204ce81..95d59d9 100644
--- c/core/modules/shortcut/shortcut.routing.yml
+++ w/core/modules/shortcut/shortcut.routing.yml
@@ -3,7 +3,7 @@ shortcut.link_delete:
   defaults:
     _form: 'Drupal\shortcut\Form\LinkDelete'
   requirements:
-    _access_shortcut_link_delete: 'TRUE'
+    _access_shortcut_link: 'TRUE'
 
 shortcut.set_delete:
   path: '/admin/config/user-interface/shortcut/manage/{shortcut_set}/delete'
@@ -46,3 +46,28 @@ shortcut.set_customize:
     _entity_form: 'shortcut_set.customize'
   requirements:
     _entity_access: 'shortcut_set.update'
+
+shortcut.link_add:
+  path: '/admin/config/user-interface/shortcut/manage/{shortcut_set}/add-link'
+  defaults:
+    _content: '\Drupal\shortcut\Form\ShortcutForm::add'
+    _title: 'Add Shortcut'
+  requirements:
+    _access_shortcut_set_edit: 'TRUE'
+
+shortcut.link_edit:
+  path: '/admin/config/user-interface/shortcut/link/{menu_link}'
+  defaults:
+    _content: '\Drupal\shortcut\Form\ShortcutForm::edit'
+    _title: 'Add Shortcut'
+  requirements:
+    _access_shortcut_link: 'TRUE'
+
+shortcut.overview:
+  path: 'user/{user}/shortcuts'
+  defaults:
+    _content: '\Drupal\shortcut\Form\ShortcutForm::overview'
+    _title: 'Shortcuts'
+  requirements:
+    _access_shortcut_set_switch: 'TRUE'
+
diff --git c/core/modules/shortcut/shortcut.services.yml w/core/modules/shortcut/shortcut.services.yml
index bb95f49..439a0e0 100644
--- c/core/modules/shortcut/shortcut.services.yml
+++ w/core/modules/shortcut/shortcut.services.yml
@@ -1,5 +1,15 @@
 services:
   access_check.shortcut.link:
-    class: Drupal\shortcut\Access\LinkDeleteAccessCheck
+    class: Drupal\shortcut\Access\LinkAccessCheck
+    tags:
+      - { name: access_check }
+
+  access_check.shortcut.shortcut_set_edit:
+    class: Drupal\shortcut\Access\ShortcutSetEditAccessCheck
+    tags:
+      - { name: access_check }
+
+  access_check.shortcut.shortcut_set_switch:
+    class: Drupal\shortcut\Access\ShortcutSetSwitchAccessCheck
     tags:
       - { name: access_check }
diff --git c/core/modules/system/lib/Drupal/system/Form/SystemForm.php w/core/modules/system/lib/Drupal/system/Form/SystemForm.php
new file mode 100644
index 0000000..20b64db
--- /dev/null
+++ w/core/modules/system/lib/Drupal/system/Form/SystemForm.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\system\Form\SystemForm.
+ */
+
+namespace Drupal\system\Form;
+
+/**
+ * Temporary form controller for system module.
+ */
+class SystemForm {
+
+  /**
+   * Wraps system_date_format_localize_form().
+   *
+   * @todo Remove system_date_format_localize_form().
+   */
+  public function localizeDateFormat($langcode) {
+    module_load_include('admin.inc', 'system');
+    return drupal_get_form('system_date_format_localize_form', $langcode);
+  }
+
+}
diff --git c/core/modules/system/system.admin.inc w/core/modules/system/system.admin.inc
index 866b553..6309812 100644
--- c/core/modules/system/system.admin.inc
+++ w/core/modules/system/system.admin.inc
@@ -732,6 +732,8 @@ function theme_system_themes_page($variables) {
  * @see locale_menu()
  * @see system_date_format_localize_form_submit()
  * @ingroup forms
+ *
+ * @depracated Use \Drupal\system\Form\SystemForm::localizeDateFormat()
  */
 function system_date_format_localize_form($form, &$form_state, $langcode) {
   // Display the current language name.
diff --git c/core/modules/system/system.module w/core/modules/system/system.module
index f62f640..a7db847 100644
--- c/core/modules/system/system.module
+++ w/core/modules/system/system.module
@@ -909,11 +909,8 @@ function system_menu() {
     );
     $items['admin/config/regional/date-time/locale/%/edit'] = array(
       'title' => 'Localize date formats',
+      'route_name' => 'system.localize_date_format',
       'description' => 'Configure date formats for each locale',
-      'page callback' => 'drupal_get_form',
-      'page arguments' => array('system_date_format_localize_form', 5),
-      'access arguments' => array('administer site configuration'),
-      'file' => 'system.admin.inc',
     );
     $items['admin/config/regional/date-time/locale/%/reset'] = array(
       'title' => 'Reset date formats',
diff --git c/core/modules/system/system.routing.yml w/core/modules/system/system.routing.yml
index 4ad6509..f63dfa5 100644
--- c/core/modules/system/system.routing.yml
+++ w/core/modules/system/system.routing.yml
@@ -326,3 +326,11 @@ system.batch_page:
     _controller: '\Drupal\system\Controller\BatchController::batchPage'
   requirements:
     _access: 'TRUE'
+
+system.localize_date_format:
+  path: '/admin/config/regional/date-time/locale/{langcode}/edit'
+  defaults:
+    _content: '\Drupal\system\Form\SystemForm::localizeDateFormat'
+    _title: 'Localize date formats'
+  requirements:
+    _permission: 'administer site configuration'
diff --git c/core/modules/update/lib/Drupal/update/Access/UpdateManagerAccessCheck.php w/core/modules/update/lib/Drupal/update/Access/UpdateManagerAccessCheck.php
new file mode 100644
index 0000000..adc04c8
--- /dev/null
+++ w/core/modules/update/lib/Drupal/update/Access/UpdateManagerAccessCheck.php
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * @file
+ * Contains Drupal\update\Access\UpdateManagerAccessCheck.
+ */
+
+namespace Drupal\update\Access;
+
+use Drupal\Component\Utility\Settings;
+use Drupal\Core\Access\StaticAccessCheckInterface;
+use Symfony\Component\Routing\Route;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Determines whether allow authorized operations is set.
+ */
+class UpdateManagerAccessCheck implements StaticAccessCheckInterface {
+
+  /**
+   * Settings Service.
+   *
+   * @var \Drupal\Component\Utility\Settings
+   */
+  protected $settings;
+
+  /**
+   * Constructs a UpdateManagerAccessCheck object.
+   *
+   * @param \Drupal\update\updateManager $update_manager
+   *   update Manager Service.
+   */
+  public function __construct(Settings $settings) {
+    $this->settings = $settings;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function appliesTo() {
+    return array('_access_update_manager');
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function access(Route $route, Request $request) {
+    return $this->settings->get('allow_authorize_operations', TRUE) ? static::ALLOW : static::DENY;
+  }
+
+}
diff --git c/core/modules/update/lib/Drupal/update/Form/UpdateForm.php w/core/modules/update/lib/Drupal/update/Form/UpdateForm.php
new file mode 100644
index 0000000..b904633
--- /dev/null
+++ w/core/modules/update/lib/Drupal/update/Form/UpdateForm.php
@@ -0,0 +1,84 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\update\Form\UpdateForm.
+ */
+
+namespace Drupal\update\Form;
+
+/**
+ * Temporary form controller for update module.
+ */
+class UpdateForm {
+
+  /**
+   * Wraps update_manager_install_form().
+   *
+   * @todo Remove update_manager_install_form().
+   */
+  public function reportInstall() {
+    module_load_include('manager.inc', 'update');
+    return drupal_get_form('update_manager_install_form', 'report');
+  }
+
+  /**
+   * Wraps update_manager_update_form().
+   *
+   * @todo Remove update_manager_update_form().
+   */
+  public function reportUpdate() {
+    module_load_include('manager.inc', 'update');
+    return drupal_get_form('update_manager_update_form', 'report');
+  }
+
+  /**
+   * Wraps update_manager_install_form().
+   *
+   * @todo Remove update_manager_install_form().
+   */
+  public function moduleInstall() {
+    module_load_include('manager.inc', 'update');
+    return drupal_get_form('update_manager_install_form', 'module');
+  }
+
+  /**
+   * Wraps update_manager_update_form().
+   *
+   * @todo Remove update_manager_update_form().
+   */
+  public function moduleUpdate() {
+    module_load_include('manager.inc', 'update');
+    return drupal_get_form('update_manager_update_form', 'module');
+  }
+
+  /**
+   * Wraps update_manager_install_form().
+   *
+   * @todo Remove update_manager_install_form().
+   */
+  public function themeInstall() {
+    module_load_include('manager.inc', 'update');
+    return drupal_get_form('update_manager_install_form', 'theme');
+  }
+
+  /**
+   * Wraps update_manager_update_form().
+   *
+   * @todo Remove update_manager_update_form().
+   */
+  public function themeUpdate() {
+    module_load_include('manager.inc', 'update');
+    return drupal_get_form('update_manager_update_form', 'theme');
+  }
+
+  /**
+   * Wraps update_manager_update_ready_form().
+   *
+   * @todo Remove update_manager_update_ready_form().
+   */
+  public function confirmUpdates() {
+    module_load_include('manager.inc', 'update');
+    return drupal_get_form('update_manager_update_ready_form');
+  }
+
+}
diff --git c/core/modules/update/update.manager.inc w/core/modules/update/update.manager.inc
index a1eccb9..21bc568 100644
--- c/core/modules/update/update.manager.inc
+++ w/core/modules/update/update.manager.inc
@@ -62,6 +62,10 @@
  * @see update_manager_update_form_submit()
  * @see update_menu()
  * @ingroup forms
+ *
+ * @depracated Use \Drupal\update\Form\UpdateForm::reportUpdate(),
+ *   \Drupal\update\Form\UpdateForm::moduleUpdate(), or
+ *   \Drupal\update\Form\UpdateForm::moduleUpdate()
  */
 function update_manager_update_form($form, $form_state = array(), $context) {
   if (!_update_manager_check_backends($form, 'update')) {
@@ -507,6 +511,10 @@ function update_manager_update_ready_form_submit($form, &$form_state) {
  * @see update_manager_install_form_submit()
  * @see update_menu()
  * @ingroup forms
+ *
+ * @depracated Use \Drupal\update\Form\UpdateForm::reportInstall(),
+ *   \Drupal\update\Form\UpdateForm::moduleInstall(), or
+ *   \Drupal\update\Form\UpdateForm::moduleInstall()
  */
 function update_manager_install_form($form, &$form_state, $context) {
   if (!_update_manager_check_backends($form, 'install')) {
diff --git c/core/modules/update/update.module w/core/modules/update/update.module
index 0210c01..3960f05 100644
--- c/core/modules/update/update.module
+++ w/core/modules/update/update.module
@@ -194,23 +194,15 @@ function update_menu() {
   );
   foreach ($paths as $context => $path) {
     $items[$path . '/install'] = array(
-      'page callback' => 'drupal_get_form',
-      'page arguments' => array('update_manager_install_form', $context),
-      'access callback' => 'update_manager_access',
-      'access arguments' => array(),
+      'route_name' => "update.{$context}_install",
       'weight' => 25,
       'type' => MENU_LOCAL_ACTION,
-      'file' => 'update.manager.inc',
     );
     $items[$path . '/update'] = array(
-      'page callback' => 'drupal_get_form',
-      'page arguments' => array('update_manager_update_form', $context),
-      'access callback' => 'update_manager_access',
-      'access arguments' => array(),
+      'route_name' => "update.{$context}_update",
       'weight' => 10,
       'title' => 'Update',
       'type' => MENU_LOCAL_TASK,
-      'file' => 'update.manager.inc',
     );
   }
   // Customize the titles of the action links depending on where they appear.
@@ -219,18 +211,6 @@ function update_menu() {
   $items['admin/modules/install'] += array('title' => 'Install new module');
   $items['admin/appearance/install'] += array('title' => 'Install new theme');
 
-  // Menu callback used for the confirmation page after all the releases
-  // have been downloaded, asking you to backup before installing updates.
-  $items['admin/update/ready'] = array(
-    'title' => 'Ready to update',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('update_manager_update_ready_form'),
-    'access callback' => 'update_manager_access',
-    'access arguments' => array(),
-    'type' => MENU_CALLBACK,
-    'file' => 'update.manager.inc',
-  );
-
   return $items;
 }
 
diff --git c/core/modules/update/update.routing.yml w/core/modules/update/update.routing.yml
index f6bd120..1a0e104 100644
--- c/core/modules/update/update.routing.yml
+++ w/core/modules/update/update.routing.yml
@@ -11,3 +11,77 @@ update.status:
     _content: '\Drupal\update\Controller\UpdateController::updateStatus'
   requirements:
     _permission: 'administer site configuration'
+
+update.report_install:
+  path: '/admin/reports/updates/install'
+  defaults:
+    _content: '\Drupal\update\Form\UpdateForm::reportInstall'
+  options:
+    _access_mode: 'ALL'
+  requirements:
+    _permission: 'administer software updates'
+    _access_update_manager: 'TRUE'
+
+update.report_update:
+  path: '/admin/reports/updates/update'
+  defaults:
+    _content: '\Drupal\update\Form\UpdateForm::reportUpdate'
+    _title: 'Update'
+  options:
+    _access_mode: 'ALL'
+  requirements:
+    _permission: 'administer software updates'
+    _access_update_manager: 'TRUE'
+
+update.module_install:
+  path: '/admin/modules/install'
+  defaults:
+    _content: '\Drupal\update\Form\UpdateForm::moduleInstall'
+  options:
+    _access_mode: 'ALL'
+  requirements:
+    _permission: 'administer software updates'
+    _access_update_manager: 'TRUE'
+
+update.module_update:
+  path: '/admin/modules/update'
+  defaults:
+    _content: '\Drupal\update\Form\UpdateForm::moduleUpdate'
+    _title: 'Update'
+  options:
+    _access_mode: 'ALL'
+  requirements:
+    _permission: 'administer software updates'
+    _access_update_manager: 'TRUE'
+
+update.theme_install:
+  path: '/admin/theme/install'
+  defaults:
+    _content: '\Drupal\update\Form\UpdateForm::themeInstall'
+  options:
+    _access_mode: 'ALL'
+  requirements:
+    _permission: 'administer software updates'
+    _access_update_manager: 'TRUE'
+
+update.theme_update:
+  path: '/admin/theme/update'
+  defaults:
+    _content: '\Drupal\update\Form\UpdateForm::themeUpdate'
+    _title: 'Update'
+  options:
+    _access_mode: 'ALL'
+  requirements:
+    _permission: 'administer software updates'
+    _access_update_manager: 'TRUE'
+
+update.confirmation_page:
+  path: '/admin/update/ready'
+  defaults:
+    _content: '\Drupal\update\Form\UpdateForm::confirmUpdates'
+    _title: 'Ready to update'
+  options:
+    _access_mode: 'ALL'
+  requirements:
+    _permission: 'administer software updates'
+    _access_update_manager: 'TRUE'
diff --git c/core/modules/update/update.services.yml w/core/modules/update/update.services.yml
new file mode 100644
index 0000000..0ffafc2
--- /dev/null
+++ w/core/modules/update/update.services.yml
@@ -0,0 +1,6 @@
+services:
+  access_check.update.manager_access:
+    class: Drupal\update\Access\UpdateManagerAccessCheck
+    arguments: ['@settings']
+    tags:
+      - { name: access_check }
diff --git c/core/modules/user/lib/Drupal/user/Form/UserForm.php w/core/modules/user/lib/Drupal/user/Form/UserForm.php
new file mode 100644
index 0000000..49c8c76
--- /dev/null
+++ w/core/modules/user/lib/Drupal/user/Form/UserForm.php
@@ -0,0 +1,24 @@
+<?php
+/**
+ * @file
+ * Contains \Drupal\user\Form\UserForm.
+ */
+
+namespace Drupal\user\Form;
+
+/**
+ * Temporary form controller for user module.
+ */
+class UserForm {
+
+  /**
+   * Wraps user_pass_reset().
+   *
+   * @todo Remove user_pass_reset().
+   */
+  public function resetPass($uid, $timestamp, $hash, $operation) {
+    module_load_include('pages.inc', 'user');
+    return drupal_get_form('user_pass_reset', $uid, $timestamp, $hash, $operation);
+  }
+
+}
diff --git c/core/modules/user/user.module w/core/modules/user/user.module
index 3bc4534..9ca5d09 100644
--- c/core/modules/user/user.module
+++ w/core/modules/user/user.module
@@ -759,14 +759,6 @@ function user_menu() {
     'route_name' => 'user.pass',
     'type' => MENU_LOCAL_TASK,
   );
-  $items['user/reset/%/%/%'] = array(
-    'title' => 'Reset password',
-    'page callback' => 'drupal_get_form',
-    'page arguments' => array('user_pass_reset', 2, 3, 4),
-    'access callback' => TRUE,
-    'type' => MENU_CALLBACK,
-    'file' => 'user.pages.inc',
-  );
 
   $items['user/logout'] = array(
     'title' => 'Log out',
diff --git c/core/modules/user/user.pages.inc w/core/modules/user/user.pages.inc
index 6e4f999..df70051 100644
--- c/core/modules/user/user.pages.inc
+++ w/core/modules/user/user.pages.inc
@@ -13,6 +13,8 @@
 
 /**
  * Menu callback; process one time login link and redirects to the user page on success.
+ *
+ * @depracated Use \Drupal\user\Form\UserForm::resetPass()
  */
 function user_pass_reset($form, &$form_state, $uid, $timestamp, $hashed_pass, $action = NULL) {
   global $user;
diff --git c/core/modules/user/user.routing.yml w/core/modules/user/user.routing.yml
index 22b6c5e..9b60814 100644
--- c/core/modules/user/user.routing.yml
+++ w/core/modules/user/user.routing.yml
@@ -132,3 +132,12 @@ user.cancel_confirm:
     _entity_form: 'user.cancel'
   requirements:
     _entity_access: 'user.delete'
+
+user.reset:
+  path: '/user/reset/{uid}/{timestamp}/{hash}/{operation}'
+  defaults:
+    _content: '\Drupal\user\Form\UserForm::resetPass'
+    _title: 'Reset password'
+    operation: NULL
+  requirements:
+    _access: 'TRUE'
