diff --git a/core/core.services.yml b/core/core.services.yml
index 2ab84ee9c5958a30081b4744bd655089bad8e74f..37a1c482d334a6a2817406576e7058031c9e263b 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -387,7 +387,7 @@ services:
     arguments: ['@http_kernel', '@controller_resolver', '@string_translation', '@title_resolver']
   controller.dialog:
     class: Drupal\Core\Controller\DialogController
-    arguments: ['@http_kernel']
+    arguments: ['@http_kernel', '@title_resolver']
   router_listener:
     class: Symfony\Component\HttpKernel\EventListener\RouterListener
     tags:
diff --git a/core/includes/menu.inc b/core/includes/menu.inc
index 417d7efbf72403613a9ff0773105068380dda386..a8a30b6c9a3fbc53453473289b35faaa1057c0e1 100644
--- a/core/includes/menu.inc
+++ b/core/includes/menu.inc
@@ -149,11 +149,6 @@
  */
 const MENU_IS_LOCAL_TASK = 0x0080;
 
-/**
- * Internal menu flag -- menu item is a local action.
- */
-const MENU_IS_LOCAL_ACTION = 0x0100;
-
 /**
  * @} End of "defgroup menu_flags".
  */
@@ -211,21 +206,13 @@
  */
 define('MENU_DEFAULT_LOCAL_TASK', MENU_IS_LOCAL_TASK | MENU_LINKS_TO_PARENT | MENU_VISIBLE_IN_BREADCRUMB);
 
-/**
- * Menu type -- An action specific to the parent, usually rendered as a link.
- *
- * Local actions are menu items that describe actions on the parent item such
- * as adding a new user, taxonomy term, etc.
- */
-define('MENU_LOCAL_ACTION', MENU_IS_LOCAL_TASK | MENU_IS_LOCAL_ACTION | MENU_VISIBLE_IN_BREADCRUMB);
-
 /**
  * Menu type -- A task specific to the parent, which is never rendered.
  *
  * Sibling local tasks are not rendered themselves, but affect the active
  * trail and need their sibling tasks rendered as tabs.
  */
-define('MENU_SIBLING_LOCAL_TASK', MENU_IS_LOCAL_TASK | MENU_IS_LOCAL_ACTION | MENU_VISIBLE_IN_BREADCRUMB);
+define('MENU_SIBLING_LOCAL_TASK', MENU_IS_LOCAL_TASK | MENU_VISIBLE_IN_BREADCRUMB);
 
 /**
  * @} End of "defgroup menu_item_types".
@@ -2117,27 +2104,12 @@ function _menu_get_legacy_tasks($router_item, &$data, &$root_path) {
           $tab_count++;
         }
         else {
-          // Actions can be normal items too, so bitmask with
-          // MENU_IS_LOCAL_ACTION before checking.
-          if (($item['type'] & MENU_IS_LOCAL_ACTION) == MENU_IS_LOCAL_ACTION) {
-            // The item is an action, display it as such.
-            $key = isset($link['route_name']) ? $link['route_name'] : $link['href'];
-            $actions_current[$key] = array(
-              '#theme' => 'menu_local_action',
-              '#link' => $link,
-              '#weight' => isset($link['weight']) ? $link['weight'] : NULL,
-            );
-            $action_count++;
-          }
-          else {
-            // Otherwise, it's a normal tab.
-            $tabs_current[$link['href']] = array(
-              '#theme' => 'menu_local_task',
-              '#link' => $link,
-              '#weight' => isset($link['weight']) ? $link['weight'] : NULL,
-            );
-            $tab_count++;
-          }
+          $tabs_current[$link['href']] = array(
+            '#theme' => 'menu_local_task',
+            '#link' => $link,
+            '#weight' => isset($link['weight']) ? $link['weight'] : NULL,
+          );
+          $tab_count++;
         }
       }
     }
@@ -2158,10 +2130,6 @@ function _menu_get_legacy_tasks($router_item, &$data, &$root_path) {
     $next_parent = '';
     $count = 0;
     foreach ($children[$parent] as $item) {
-      // Skip local actions.
-      if ($item['type'] & MENU_IS_LOCAL_ACTION) {
-        continue;
-      }
       if ($item['access']) {
         $count++;
         $link = $item;
diff --git a/core/lib/Drupal/Core/Controller/DialogController.php b/core/lib/Drupal/Core/Controller/DialogController.php
index cde28767e0560daf2615ea6abc745e00ec2e0dd0..7bb34ffad332900e927322f5fad475e57de65d34 100644
--- a/core/lib/Drupal/Core/Controller/DialogController.php
+++ b/core/lib/Drupal/Core/Controller/DialogController.php
@@ -25,14 +25,24 @@ class DialogController {
    */
   protected $httpKernel;
 
+  /**
+   * The title resolver.
+   *
+   * @var \Drupal\Core\Controller\TitleResolver
+   */
+  protected $titleResolver;
+
   /**
    * Constructs a new DialogController.
    *
    * @param \Symfony\Component\HttpKernel\HttpKernelInterface $kernel
    *   The kernel.
+   * @param \Drupal\Core\Controller\TitleResolverInterface $title_resolver
+   *   The title resolver.
    */
-  public function __construct(HttpKernelInterface $kernel) {
+  public function __construct(HttpKernelInterface $kernel, TitleResolverInterface $title_resolver) {
     $this->httpKernel = $kernel;
+    $this->titleResolver = $title_resolver;
   }
 
   /**
@@ -94,9 +104,11 @@ public function dialog(Request $request, $modal = FALSE) {
     $subrequest = $this->forward($request);
     if ($subrequest->isOk()) {
       $content = $subrequest->getContent();
-      // @todo Remove use of drupal_get_title() when
-      //  http://drupal.org/node/1871596 is in.
-      $title = drupal_get_title();
+      if (!$title = $this->titleResolver->getTitle($request, $request->attributes->get(RouteObjectInterface::ROUTE_OBJECT))) {
+        // @todo Remove use of drupal_get_title() when
+        //  http://drupal.org/node/1871596 is in.
+        $title = drupal_get_title();
+      }
       $response = new AjaxResponse();
       // Fetch any modal options passed in from data-dialog-options.
       if (!($options = $request->request->get('dialogOptions'))) {
diff --git a/core/modules/aggregator/aggregator.local_actions.yml b/core/modules/aggregator/aggregator.local_actions.yml
new file mode 100644
index 0000000000000000000000000000000000000000..ca3d850e21f12d27dde8cbc5544310eb14127a4f
--- /dev/null
+++ b/core/modules/aggregator/aggregator.local_actions.yml
@@ -0,0 +1,17 @@
+aggregator.feed_add:
+  route_name: aggregator.feed_add
+  title: 'Add feed'
+  appears_on:
+    - 'aggregator.admin_overview'
+
+aggregator.category_add:
+  route_name: aggregator.category_add
+  title: 'Add category'
+  appears_on:
+    - 'aggregator.admin_overview'
+
+aggregator.opml_add:
+  route_name: aggregator.opml_add
+  title: 'Import OPML'
+  appears_on:
+    - 'aggregator.admin_overview'
diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module
index 045c156aeb0d4eb219a87c1b4bdc89c2356ada1e..726f2282a405b21643f2d0dcdaa291bab30e0e16 100644
--- a/core/modules/aggregator/aggregator.module
+++ b/core/modules/aggregator/aggregator.module
@@ -97,21 +97,6 @@ function aggregator_menu() {
     'route_name' => 'aggregator.admin_overview',
     'weight' => 10,
   );
-  $items['admin/config/services/aggregator/add/feed'] = array(
-    'title' => 'Add feed',
-    'route_name' => 'aggregator.feed_add',
-    'type' => MENU_LOCAL_ACTION,
-  );
-  $items['admin/config/services/aggregator/add/category'] = array(
-    'title' => 'Add category',
-    'type' => MENU_LOCAL_ACTION,
-    'route_name' => 'aggregator.category_add',
-  );
-  $items['admin/config/services/aggregator/add/opml'] = array(
-    'title' => 'Import OPML',
-    'type' => MENU_LOCAL_ACTION,
-    'route_name' => 'aggregator.opml_add',
-  );
   $items['admin/config/services/aggregator/remove/%aggregator_feed'] = array(
     'title' => 'Remove items',
     'route_name' => 'aggregator.feed_items_delete',
diff --git a/core/modules/contact/contact.local_actions.yml b/core/modules/contact/contact.local_actions.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c917e7ff882b89156879dc649eb06848d9a9bde0
--- /dev/null
+++ b/core/modules/contact/contact.local_actions.yml
@@ -0,0 +1,6 @@
+contact.category_add:
+  route_name: contact.category_add
+  title: 'Add category'
+  weight: 1
+  appears_on:
+    - contact.category_list
diff --git a/core/modules/contact/contact.module b/core/modules/contact/contact.module
index 17900fa8fac01e6b38f3ed8a5660bd6816d405f8..66a80ece3a274adb341fa63a36b666ed235351e6 100644
--- a/core/modules/contact/contact.module
+++ b/core/modules/contact/contact.module
@@ -63,12 +63,6 @@ function contact_menu() {
     'description' => 'Create a system contact form and set up categories for the form to use.',
     'route_name' => 'contact.category_list',
   );
-  $items['admin/structure/contact/add'] = array(
-    'title' => 'Add category',
-    'route_name' => 'contact.category_add',
-    'type' => MENU_LOCAL_ACTION,
-    'weight' => 1,
-  );
   $items['admin/structure/contact/manage/%contact_category'] = array(
     'title' => 'Edit contact category',
     'route_name' => 'contact.category_edit',
diff --git a/core/modules/contact/contact.routing.yml b/core/modules/contact/contact.routing.yml
index 74481922d8f963e6904b3f2dfc770136e26301ac..f12d67e67eb66c10b9c0fab9a2a6034fa65343e7 100644
--- a/core/modules/contact/contact.routing.yml
+++ b/core/modules/contact/contact.routing.yml
@@ -17,6 +17,7 @@ contact.category_add:
   path: '/admin/structure/contact/add'
   defaults:
     _entity_form: contact_category.add
+    _title: 'Add category'
   requirements:
     _permission: 'administer contact forms'
 
diff --git a/core/modules/menu/menu.local_actions.yml b/core/modules/menu/menu.local_actions.yml
index a0016bc35bf1429785e89fe7a388c49cb9d5492e..cca55d8a762e04925c9e0884cd1898bd9ef7b175 100644
--- a/core/modules/menu/menu.local_actions.yml
+++ b/core/modules/menu/menu.local_actions.yml
@@ -3,3 +3,9 @@ menu_link_add:
   title: 'Add link'
   appears_on:
     - menu.menu_edit
+
+menu.menu_add:
+  route_name: menu.menu_add
+  title: 'Add menu'
+  appears_on:
+    - menu.overview_page
diff --git a/core/modules/menu/menu.module b/core/modules/menu/menu.module
index c48bb465a73501a53078890a37532fdb1cfd52e0..5fe0aa709c6a079cbe904de224d1ed7d98138fa4 100644
--- a/core/modules/menu/menu.module
+++ b/core/modules/menu/menu.module
@@ -74,11 +74,6 @@ function menu_menu() {
     'title' => 'List menus',
     'type' => MENU_DEFAULT_LOCAL_TASK,
   );
-  $items['admin/structure/menu/add'] = array(
-    'title' => 'Add menu',
-    'route_name' => 'menu.menu_add',
-    'type' => MENU_LOCAL_ACTION,
-  );
   $items['admin/structure/menu/settings'] = array(
     'title' => 'Settings',
     'route_name' => 'menu.settings',
diff --git a/core/modules/node/node.local_actions.yml b/core/modules/node/node.local_actions.yml
new file mode 100644
index 0000000000000000000000000000000000000000..feb202883af1dae273da0dcca71c21222a0f9b67
--- /dev/null
+++ b/core/modules/node/node.local_actions.yml
@@ -0,0 +1,5 @@
+node.type_add:
+  route_name: node.type_add
+  title: 'Add content type'
+  appears_on:
+    - node.overview_types
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 4bed03fba9ced1b4b6003e7dfd2b9dc0778a7d0c..438104d4822a17054f87ad73d343a61f07429fcf 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -949,11 +949,6 @@ function node_menu() {
     'title' => 'List',
     'type' => MENU_DEFAULT_LOCAL_TASK,
   );
-  $items['admin/structure/types/add'] = array(
-    'title' => 'Add content type',
-    'route_name' => 'node.type_add',
-    'type' => MENU_LOCAL_ACTION,
-  );
   $items['admin/structure/types/manage/%node_type'] = array(
     'title' => 'Edit content type',
     'title callback' => 'entity_page_label',
diff --git a/core/modules/path/path.local_actions.yml b/core/modules/path/path.local_actions.yml
new file mode 100644
index 0000000000000000000000000000000000000000..9c589848079779f7765c5723b921669153ea84b7
--- /dev/null
+++ b/core/modules/path/path.local_actions.yml
@@ -0,0 +1,5 @@
+path.admin_add:
+  route_name: path.admin_add
+  title: 'Add alias'
+  appears_on:
+    - path.admin_overview
diff --git a/core/modules/path/path.module b/core/modules/path/path.module
index 08d82d53c59ce31ca3c8e4c1a40536588b91240d..a965e9d6315566ad5fd98de807500932260b7f7d 100644
--- a/core/modules/path/path.module
+++ b/core/modules/path/path.module
@@ -74,11 +74,6 @@ function path_menu() {
     'title' => 'Delete alias',
     'route_name' => 'path.delete',
   );
-  $items['admin/config/search/path/add'] = array(
-    'title' => 'Add alias',
-    'route_name' => 'path.admin_add',
-    'type' => MENU_LOCAL_ACTION,
-  );
 
   return $items;
 }
diff --git a/core/modules/picture/picture.local_actions.yml b/core/modules/picture/picture.local_actions.yml
new file mode 100644
index 0000000000000000000000000000000000000000..0fccbb22ea0b447f46428fb245620633e04aa1d1
--- /dev/null
+++ b/core/modules/picture/picture.local_actions.yml
@@ -0,0 +1,5 @@
+picture.mapping_page_add:
+  route_name: picture.mapping_page_add
+  title: 'Add picture mapping'
+  appears_on:
+    - picture.mapping_page
diff --git a/core/modules/picture/picture.module b/core/modules/picture/picture.module
index 2a6e6d2d1ce823743e2207b46924eda3dceea6e9..b9b3b016ab7555e4e65cb9285b987cc4460bdf56 100644
--- a/core/modules/picture/picture.module
+++ b/core/modules/picture/picture.module
@@ -59,11 +59,6 @@ function picture_menu() {
     'weight' => 10,
     'route_name' => 'picture.mapping_page',
   );
-  $items['admin/config/media/picturemapping/add'] = array(
-    'title' => 'Add picture mapping',
-    'route_name' => 'picture.mapping_page_add',
-    'type' => MENU_LOCAL_ACTION,
-  );
   $items['admin/config/media/picturemapping/%picture_mapping'] = array(
     'title' => 'Edit picture mapping',
     'route_name' => 'picture.mapping_page_edit',
diff --git a/core/modules/shortcut/shortcut.local_actions.yml b/core/modules/shortcut/shortcut.local_actions.yml
index 2067f7de4550380459ae2c49d09da75bd95ecf14..3ed4a7589c1ada6eeec0a4724cc85de091fe5745 100644
--- a/core/modules/shortcut/shortcut.local_actions.yml
+++ b/core/modules/shortcut/shortcut.local_actions.yml
@@ -3,3 +3,8 @@ shortcut_set_add_local_action:
   title: 'Add shortcut set'
   appears_on:
     - shortcut.set_admin
+shortcut.link_add:
+  route_name: shortcut.link_add
+  title: 'Add shortcut'
+  appears_on:
+    - shortcut.set_customize
diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module
index dc9bd86b3f5991a6246d7d92b97733eba7bd6ccf..724309c15d0e1a90be8d0ddacc5e7a2787d6c056 100644
--- a/core/modules/shortcut/shortcut.module
+++ b/core/modules/shortcut/shortcut.module
@@ -106,11 +106,6 @@ function shortcut_menu() {
     'type' => MENU_LOCAL_TASK,
     'weight' => 10,
   );
-  $items['admin/config/user-interface/shortcut/manage/%shortcut_set/add-link'] = array(
-    'title' => 'Add shortcut',
-    'route_name' => 'shortcut.link_edit',
-    'type' => MENU_LOCAL_ACTION,
-  );
   $items['admin/config/user-interface/shortcut/link/%menu_link'] = array(
     'title' => 'Edit shortcut',
     'route_name' => 'shortcut.link_edit',
diff --git a/core/modules/system/lib/Drupal/system/Plugin/Block/SystemHelpBlock.php b/core/modules/system/lib/Drupal/system/Plugin/Block/SystemHelpBlock.php
index a8c05fe3755f1b6718181cf0723ae50709e57c3b..f7b389d91f09273bd51f32f47ca17005a2e57d28 100644
--- a/core/modules/system/lib/Drupal/system/Plugin/Block/SystemHelpBlock.php
+++ b/core/modules/system/lib/Drupal/system/Plugin/Block/SystemHelpBlock.php
@@ -92,13 +92,13 @@ public function access(AccountInterface $account) {
    */
   protected function getActiveHelp(Request $request) {
     $output = '';
-    $router_path = menu_tab_root_path();
+    $router_path = $request->attributes->get('_system_path');
     // We will always have a path unless we are on a 403 or 404.
     if (!$router_path) {
       return '';
     }
 
-    $arg = drupal_help_arg(explode('/', $request->attributes->get('_system_path')));
+    $arg = drupal_help_arg(explode('/', $router_path));
 
     foreach ($this->moduleHandler->getImplementations('help') as $module) {
       $function = $module . '_help';
diff --git a/core/modules/system/lib/Drupal/system/Tests/Ajax/DialogTest.php b/core/modules/system/lib/Drupal/system/Tests/Ajax/DialogTest.php
index 9849cb3cfc568e2cb573e20bcc785cd15fa38ee7..3d97b36b8e88ec3ed9178a0f4c3ea177ca4f6512 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Ajax/DialogTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Ajax/DialogTest.php
@@ -68,7 +68,7 @@ public function testDialog() {
       'settings' => NULL,
       'dialogOptions' => array(
         'modal' => TRUE,
-        'title' => 'Home',
+        'title' => 'Add category',
       ),
     );
     $normal_expected_response = array(
diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/LocalActionTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/LocalActionTest.php
index de61cb87fce4a7b02ed3d957681abee32bb8c07b..d8804dd4887f2ee7bf12d6fcfc5c0a449546ce9e 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Menu/LocalActionTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Menu/LocalActionTest.php
@@ -37,8 +37,8 @@ public function testLocalAction() {
     $this->drupalGet('menu-test-local-action');
     // Ensure that both menu and route based actions are shown.
     $this->assertLocalAction(array(
-      'menu-test-local-action/hook_menu' => 'My hook_menu action',
       'menu-test-local-action/dynamic-title' => 'My dynamic-title action',
+      'menu-test-local-action/hook_menu' => 'My hook_menu action',
       'menu-test-local-action/routing' => 'My YAML discovery action',
       'menu-test-local-action/routing2' => 'Title override',
     ));
diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php
index 4b69e980b3169f797dd39fb261588884b2edd2bf..71cf0c4d771354404724cdee79ec788b45d08c78 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php
@@ -232,10 +232,6 @@ protected function doTestMenuHidden() {
     $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
     $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
 
-    $link = $links['menu-test/hidden/menu/add'];
-    $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
-    $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
-
     $link = $links['menu-test/hidden/menu/settings'];
     $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
     $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
@@ -252,10 +248,6 @@ protected function doTestMenuHidden() {
     $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
     $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
 
-    $link = $links['menu-test/hidden/menu/manage/%/add'];
-    $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
-    $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
-
     $link = $links['menu-test/hidden/menu/manage/%/edit'];
     $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
     $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
@@ -284,10 +276,6 @@ protected function doTestMenuHidden() {
     $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
     $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
 
-    $link = $links['menu-test/hidden/block/add'];
-    $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
-    $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
-
     $link = $links['menu-test/hidden/block/manage/%/%'];
     $this->assertEqual($link['depth'], $depth, format_string('%path depth @link_depth is equal to @depth.', array('%path' => $link['router_path'], '@link_depth' => $link['depth'], '@depth' => $depth)));
     $this->assertEqual($link['plid'], $plid, format_string('%path plid @link_plid is equal to @plid.', array('%path' => $link['router_path'], '@link_plid' => $link['plid'], '@plid' => $plid)));
diff --git a/core/modules/system/system.local_actions.yml b/core/modules/system/system.local_actions.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c08a2cfc781bbb5cfa0695f0d35780295e162894
--- /dev/null
+++ b/core/modules/system/system.local_actions.yml
@@ -0,0 +1,6 @@
+system.date_format_add:
+  route_name: system.date_format_add
+  title: 'Add format'
+  weight: -10
+  appears_on:
+    - system.date_format_list
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 66e5a6ff18be1c85d66f586e096b9e9554f166ce..9e2231b3dd455ff91d3abe93191acb71641373cc 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -736,13 +736,6 @@ function system_menu() {
     'route_name' => 'system.date_format_list',
     'weight' => -9,
   );
-  $items['admin/config/regional/date-time/formats/add'] = array(
-    'title' => 'Add format',
-    'description' => 'Allow users to add additional date formats.',
-    'type' => MENU_LOCAL_ACTION,
-    'route_name' => 'system.date_format_add',
-    'weight' => -10,
-  );
   $items['admin/config/regional/date-time/formats/manage/%'] = array(
     'title' => 'Edit date format',
     'description' => 'Allow users to edit a configured date format.',
diff --git a/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/Plugin/Menu/LocalAction/TestLocalAction4.php b/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/Plugin/Menu/LocalAction/TestLocalAction4.php
new file mode 100644
index 0000000000000000000000000000000000000000..3f9a44e1cf868e966af35144007521590917029f
--- /dev/null
+++ b/core/modules/system/tests/modules/menu_test/lib/Drupal/menu_test/Plugin/Menu/LocalAction/TestLocalAction4.php
@@ -0,0 +1,24 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\menu_test\Plugin\Menu\LocalAction\TestLocalAction4.
+ */
+
+namespace Drupal\menu_test\Plugin\Menu\LocalAction;
+
+use Drupal\Core\Menu\LocalActionDefault;
+
+/**
+ * Defines a local action plugin with a dynamic title.
+ */
+class TestLocalAction4 extends LocalActionDefault {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getTitle() {
+    return $this->t('My @arg action', array('@arg' => 'dynamic-title'));
+  }
+
+}
diff --git a/core/modules/system/tests/modules/menu_test/menu_test.local_actions.yml b/core/modules/system/tests/modules/menu_test/menu_test.local_actions.yml
index 76bde782962773576e80c8c86bed04fe7e1885fe..24c92a2c0675090b981f0cb90bc76714ff242672 100644
--- a/core/modules/system/tests/modules/menu_test/menu_test.local_actions.yml
+++ b/core/modules/system/tests/modules/menu_test/menu_test.local_actions.yml
@@ -11,3 +11,35 @@ menu_test.local_action5:
   appears_on:
     - menu_test.local_action1
 
+menu_test.local_action2:
+  route_name: menu_test.local_action2
+  title: 'My hook_menu action'
+  weight: -10
+  appears_on:
+    - menu_test.local_action1
+
+menu_test.local_action4:
+  route_name: menu_test.local_action4
+  title: 'My dynamic title action'
+  weight: -20
+  class: '\Drupal\menu_test\Plugin\Menu\LocalAction\TestLocalAction4'
+  appears_on:
+    - menu_test.local_action1
+
+menu_test.hidden_menu_add:
+  route_name: menu_test.hidden_menu_add
+  title: 'Add menu'
+  appears_on:
+    - menu_test.hidden_menu
+
+menu_test.hidden_manage_add:
+  route_name: menu_test.hidden_manage_add
+  title: 'Add link'
+  appears_on:
+    - menu_test.hidden_manage
+
+menu_test.hidden_block_add:
+  route_name: menu_test.hidden_block_add
+  title: 'Add block'
+  appears_on:
+    - menu_test.hidden_block
diff --git a/core/modules/system/tests/modules/menu_test/menu_test.module b/core/modules/system/tests/modules/menu_test/menu_test.module
index c42e7057bf237cf1562550585134bbbc87aeea64..ab2ef302894bf941f5c1ee699acd89784dc26adb 100644
--- a/core/modules/system/tests/modules/menu_test/menu_test.module
+++ b/core/modules/system/tests/modules/menu_test/menu_test.module
@@ -108,11 +108,6 @@ function menu_test_menu() {
     'title' => 'List menus',
     'type' => MENU_DEFAULT_LOCAL_TASK,
   );
-  $items['menu-test/hidden/menu/add'] = array(
-    'title' => 'Add menu',
-    'route_name' => 'menu_test.hidden_menu_add',
-    'type' => MENU_LOCAL_ACTION,
-  );
   $items['menu-test/hidden/menu/settings'] = array(
     'title' => 'Settings',
     'route_name' => 'menu_test.hidden_menu_settings',
@@ -128,11 +123,6 @@ function menu_test_menu() {
     'type' => MENU_DEFAULT_LOCAL_TASK,
     'context' => MENU_CONTEXT_PAGE,
   );
-  $items['menu-test/hidden/menu/manage/%menu/add'] = array(
-    'title' => 'Add link',
-    'route_name' => 'menu_test.hidden_manage_add',
-    'type' => MENU_LOCAL_ACTION,
-  );
   $items['menu-test/hidden/menu/manage/%menu/edit'] = array(
     'title' => 'Edit menu',
     'route_name' => 'menu_test.hidden_manage_edit',
@@ -153,11 +143,6 @@ function menu_test_menu() {
     'title' => 'List',
     'type' => MENU_DEFAULT_LOCAL_TASK,
   );
-  $items['menu-test/hidden/block/add'] = array(
-    'title' => 'Add block',
-    'route_name' => 'menu_test.hidden_block_add',
-    'type' => MENU_LOCAL_ACTION,
-  );
   $items['menu-test/hidden/block/manage/%/%'] = array(
     'title' => 'Configure block',
     'route_name' => 'menu_test.hidden_block_configure',
@@ -298,22 +283,6 @@ function menu_test_menu() {
     'route_name' => 'menu_test.local_action1',
   );
 
-  $items['menu-test-local-action/hook_menu'] = array(
-    'title' => 'My hook_menu action',
-    'route_name' => 'menu_test.local_action2',
-    'weight' => -10,
-    'type' => MENU_LOCAL_ACTION,
-  );
-
-  $items['menu-test-local-action/dynamic-title'] = array(
-    'title' => 'My dynamic title action',
-    'title callback' => 'menu_test_local_action_dynamic_title',
-    'title arguments' => array(1),
-    'route_name' => 'menu_test.local_action4',
-    'weight' => -10,
-    'type' => MENU_LOCAL_ACTION,
-  );
-
   $items['menu-local-task-test/tasks'] = array(
     'title' => 'Local tasks',
     'route_name' => 'menu_test.local_task_test_tasks',
@@ -327,13 +296,6 @@ function menu_test_menu() {
   return $items;
 }
 
-/**
- * Title callback: Set a dynamic title for a local action.
- */
-function menu_test_local_action_dynamic_title($arg) {
-  return t('My @arg action', array('@arg' => $arg));
-}
-
 /**
  * Implements hook_menu_local_tasks().
  *
diff --git a/core/modules/taxonomy/taxonomy.local_actions.yml b/core/modules/taxonomy/taxonomy.local_actions.yml
index 4e865396b2f5bb0c1e41d58cd8fa9164f8a3020b..689754a3aba41304a485f2de8b294e6f69092684 100644
--- a/core/modules/taxonomy/taxonomy.local_actions.yml
+++ b/core/modules/taxonomy/taxonomy.local_actions.yml
@@ -3,3 +3,9 @@ taxonomy_add_vocabulary_local_action:
   title: 'Add vocabulary'
   appears_on:
     - taxonomy.vocabulary_list
+
+taxonomy.term_add:
+  route_name: taxonomy.term_add
+  title: 'Add term'
+  appears_on:
+    - taxonomy.overview_terms
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index acab6995137fe0a5703f673ac67bfb0d6a25c2ff..ad014a0f9384db93bc032c3852cc3d55fff1a1f8 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -278,12 +278,6 @@ function taxonomy_menu() {
     'type' => MENU_LOCAL_TASK,
   );
 
-  $items['admin/structure/taxonomy/manage/%taxonomy_vocabulary/add'] = array(
-    'title' => 'Add term',
-    'route_name' => 'taxonomy.term_add',
-    'type' => MENU_LOCAL_ACTION,
-  );
-
   return $items;
 }
 
diff --git a/core/modules/update/lib/Drupal/update/Tests/UpdateCoreTest.php b/core/modules/update/lib/Drupal/update/Tests/UpdateCoreTest.php
index f0c55be564beb350e2efe34f11a93940c31f8a8b..792845581a515d07fc96acefb77aafe6c66ada51 100644
--- a/core/modules/update/lib/Drupal/update/Tests/UpdateCoreTest.php
+++ b/core/modules/update/lib/Drupal/update/Tests/UpdateCoreTest.php
@@ -242,4 +242,25 @@ protected function setSystemInfo7_0() {
     );
     \Drupal::config('update_test.settings')->set('system_info', $setting)->save();
   }
+
+  /**
+   * Ensures that the local actions appear.
+   */
+  public function testLocalActions() {
+    $admin_user = $this->drupalCreateUser(array('administer site configuration', 'administer modules', 'administer software updates', 'administer themes'));
+    $this->drupalLogin($admin_user);
+
+    $this->drupalGet('admin/modules');
+    $this->clickLink(t('Install new module'));
+    $this->assertUrl('admin/modules/install');
+
+    $this->drupalGet('admin/appearance');
+    $this->clickLink(t('Install new theme'));
+    $this->assertUrl('admin/theme/install');
+
+    $this->drupalGet('admin/reports/updates');
+    $this->clickLink(t('Install new module or theme'));
+    $this->assertUrl('admin/reports/updates/install');
+  }
+
 }
diff --git a/core/modules/update/update.local_actions.yml b/core/modules/update/update.local_actions.yml
new file mode 100644
index 0000000000000000000000000000000000000000..d6f6b8dfc4ea3081817199fa93f21c28d7fbdbd4
--- /dev/null
+++ b/core/modules/update/update.local_actions.yml
@@ -0,0 +1,20 @@
+update.report_install:
+  route_name: update.report_install
+  title: 'Install new module or theme'
+  weight: 25
+  appears_on:
+    - update.status
+
+update.module_install:
+  route_name: update.module_install
+  title: 'Install new module'
+  weight: 25
+  appears_on:
+    - system.modules_list
+
+update.theme_install:
+  route_name: update.theme_install
+  title: 'Install new theme'
+  weight: 25
+  appears_on:
+    - system.themes_page
diff --git a/core/modules/update/update.module b/core/modules/update/update.module
index 4b9b67d8504a2669215268ef20b7cceaaaa68a1f..4cf9c0dfa8e5f96caf8f392e9b2957d00d368ffb 100644
--- a/core/modules/update/update.module
+++ b/core/modules/update/update.module
@@ -157,30 +157,6 @@ function update_menu() {
     'weight' => -50,
   );
 
-  // We want action links for updating projects at a few different locations:
-  // both the module and theme administration pages, and on the available
-  // updates report itself. The menu items will be mostly identical, except the
-  // paths and titles, so we just define them in a loop. We pass in a string
-  // indicating what context we're entering the action from, so that can
-  // customize the appearance as needed.
-  $paths = array(
-    'report' => 'admin/reports/updates',
-    'module' => 'admin/modules',
-    'theme' => 'admin/appearance',
-  );
-  foreach ($paths as $context => $path) {
-    $items[$path . '/install'] = array(
-      'route_name' => "update.{$context}_install",
-      'weight' => 25,
-      'type' => MENU_LOCAL_ACTION,
-    );
-  }
-  // Customize the titles of the action links depending on where they appear.
-  // We use += array() to let the translation extractor find these menu titles.
-  $items['admin/reports/updates/install'] += array('title' => 'Install new module or theme');
-  $items['admin/modules/install'] += array('title' => 'Install new module');
-  $items['admin/appearance/install'] += array('title' => 'Install new theme');
-
   return $items;
 }
 
diff --git a/core/modules/user/user.local_actions.yml b/core/modules/user/user.local_actions.yml
index 9a5777254adee57f4f35f198e2c476e06e04bf23..2a1f8929be264879c383ec037fdecbb5cdd0c7ec 100644
--- a/core/modules/user/user.local_actions.yml
+++ b/core/modules/user/user.local_actions.yml
@@ -3,3 +3,8 @@ user_admin_create:
   title: 'Add user'
   appears_on:
     - user.admin_account
+user.role_add:
+  route_name: user.role_add
+  title: 'Add role'
+  appears_on:
+    - user.role_list
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 83baa54659197b73b4de220a5e60fd7756f7d672..8dfae6ad037a4afcd2e91f28fca7f99447757863 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -781,11 +781,6 @@ function user_menu() {
     'route_name' => 'user.admin_permissions',
     'type' => MENU_LOCAL_TASK,
   );
-  $items['admin/people/roles/add'] = array(
-    'title' => 'Add role',
-    'route_name' => 'user.role_add',
-    'type' => MENU_LOCAL_ACTION,
-  );
 
   $items['admin/people/roles/manage/%user_role'] = array(
     'title' => 'Edit role',