diff --git a/core/includes/menu.inc b/core/includes/menu.inc
index a0ca6d74a40b6a034523ac187889fb5c55394eb7..15708f8e590c92553e4a1d12a21e79744ca21243 100644
--- a/core/includes/menu.inc
+++ b/core/includes/menu.inc
@@ -9,7 +9,6 @@
 use Drupal\Core\Cache\Cache;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Template\Attribute;
-use Drupal\menu_link\MenuLinkInterface;
 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
 
 /**
@@ -37,9 +36,9 @@
  *   the stuff in the routing.yml file means.
  *
  * @section Defining menu links
- * Once you have a route defined, you can use hook_menu_link_defaults() to
+ * Once you have a route defined, you can use module.menu_links.yml to
  * define links for your module's paths in the main Navigation menu or other
- * menus. See the hook_menu_link_defaults() documentation for more details.
+ * menus.
  *
  * @todo The rest of this topic has not been reviewed or updated for Drupal 8.x
  *   and is not correct!
@@ -102,86 +101,6 @@
  * add more with menu_link_save().
  */
 
-/**
- * @defgroup menu_flags Menu flags
- * @{
- * Flags for use in the "type" attribute of menu items.
- */
-
-/**
- * Internal menu flag -- menu item is the root of the menu tree.
- */
-const MENU_IS_ROOT = 0x0001;
-
-/**
- * Internal menu flag -- menu item is visible in the menu tree.
- */
-const MENU_VISIBLE_IN_TREE = 0x0002;
-
-/**
- * Internal menu flag -- menu item links back to its parent.
- */
-const MENU_LINKS_TO_PARENT = 0x0008;
-
-/**
- * Internal menu flag -- menu item can be modified by administrator.
- */
-const MENU_MODIFIED_BY_ADMIN = 0x0020;
-
-/**
- * Internal menu flag -- menu item was created by administrator.
- */
-const MENU_CREATED_BY_ADMIN = 0x0040;
-
-/**
- * Internal menu flag -- menu item is a local task.
- */
-const MENU_IS_LOCAL_TASK = 0x0080;
-
-/**
- * @} End of "defgroup menu_flags".
- */
-
-/**
- * @defgroup menu_item_types Menu item types
- * @{
- * Definitions for various menu item types.
- *
- * Menu item definitions provide one of these constants, which are shortcuts for
- * combinations of @link menu_flags Menu flags @endlink.
- */
-
-/**
- * Menu type -- A "normal" menu item that's shown in menus.
- *
- * Normal menu items show up in the menu tree and can be moved/hidden by
- * the administrator. Use this for most menu items. It is the default value if
- * no menu item type is specified.
- */
-define('MENU_NORMAL_ITEM', MENU_VISIBLE_IN_TREE);
-
-/**
- * Menu type -- A hidden, internal callback, typically used for API calls.
- *
- * Callbacks simply register a path so that the correct function is fired
- * when the URL is accessed. They do not appear in menus.
- */
-const MENU_CALLBACK = 0x0000;
-
-/**
- * Menu type -- A normal menu item, hidden until enabled by an administrator.
- *
- * Modules may "suggest" menu items that the administrator may enable. They act
- * just as callbacks do until enabled, at which time they act like normal items.
- * Note for the value: 0x0010 was a flag which is no longer used, but this way
- * the values of MENU_CALLBACK and MENU_SUGGESTED_ITEM are separate.
- */
-define('MENU_SUGGESTED_ITEM', 0x0010);
-
-/**
- * @} End of "defgroup menu_item_types".
- */
-
 /**
  * @defgroup menu_status_codes Menu status codes
  * @{
@@ -998,24 +917,7 @@ function _menu_link_save_recursive($controller, $machine_name, &$children, &$lin
 }
 
 /**
- * Gets all default menu link definitions.
- *
- * @return array
- *   An array of default menu links.
- */
-function menu_link_get_defaults() {
-  $module_handler = \Drupal::moduleHandler();
-  $all_links = $module_handler->invokeAll('menu_link_defaults');
-  // Fill in the machine name from the array key.
-  foreach ($all_links as $machine_name => &$link) {
-    $link['machine_name'] = $machine_name;
-  }
-  $module_handler->alter('menu_link_defaults', $all_links);
-  return $all_links;
-}
-
-/**
- * Builds menu links for the items returned from hook_menu_link_defaults().
+ * Builds menu links for the items returned from the menu_link.static service.
  */
 function menu_link_rebuild_defaults() {
   // Ensure that all configuration used to build the menu items are loaded
@@ -1034,7 +936,7 @@ function menu_link_rebuild_defaults() {
   $links = array();
   $children = array();
   $top_links = array();
-  $all_links = menu_link_get_defaults();
+  $all_links = \Drupal::service('menu_link.static')->getLinks();
   if ($all_links) {
     foreach ($all_links as $machine_name => $link) {
       // For performance reasons, do a straight query now and convert to a menu
@@ -1043,7 +945,6 @@ function menu_link_rebuild_defaults() {
       $existing_item = db_select('menu_links')
         ->fields('menu_links')
         ->condition('machine_name', $machine_name)
-        ->condition('module', 'system')
         ->execute()->fetchObject();
       if ($existing_item) {
         $existing_item->options = unserialize($existing_item->options);
@@ -1060,7 +961,7 @@ function menu_link_rebuild_defaults() {
         $existing_item = $menu_link_storage->create(get_object_vars($existing_item));
 
         if (!$existing_item->customized) {
-          // A change in hook_menu_link_defaults() may move the link to a
+          // A change in the default menu links may move the link to a
           // different menu or parent.
           if (!empty($link['menu_name']) && ($link['menu_name'] != $existing_item->menu_name)) {
             $menu_link->plid = NULL;
@@ -1108,7 +1009,7 @@ function menu_link_rebuild_defaults() {
     }
   }
 
-  // Find any item whose entry in hook_menu_link_defaults() no longer exists.
+  // Find any item whose default menu link no longer exists.
   if ($all_links) {
     $query = \Drupal::entityQuery('menu_link')
       ->condition('machine_name', array_keys($all_links), 'NOT IN')
diff --git a/core/modules/action/action.menu_links.yml b/core/modules/action/action.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..fa5695125daa0ccffe302eeeb14e34f61e65dce5
--- /dev/null
+++ b/core/modules/action/action.menu_links.yml
@@ -0,0 +1,5 @@
+action.admin:
+  title: Actions
+  description: 'Manage the actions defined for your site.'
+  route_name: action.admin
+  parent: system.admin_config_system
diff --git a/core/modules/action/action.module b/core/modules/action/action.module
index a88e0ed0c9109073c4130c7f871245ba1b96c413..3712b14175ba1ea5e5f4d4db602045ef1721ae38 100644
--- a/core/modules/action/action.module
+++ b/core/modules/action/action.module
@@ -44,20 +44,6 @@ function action_permission() {
   );
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function action_menu_link_defaults() {
-  $links['action.admin'] = array(
-    'link_title' => 'Actions',
-    'description' => 'Manage the actions defined for your site.',
-    'route_name' => 'action.admin',
-    'parent' => 'system.admin_config_system',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_entity_type_build().
  */
diff --git a/core/modules/aggregator/aggregator.menu_links.yml b/core/modules/aggregator/aggregator.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..43773c659119ffca41a9c3655aea82bd5d9d3ca5
--- /dev/null
+++ b/core/modules/aggregator/aggregator.menu_links.yml
@@ -0,0 +1,16 @@
+aggregator.admin_overview:
+  title: 'Feed aggregator'
+  description: 'Configure which content your site aggregates from other sites, how often it polls them, and how they''re categorized.'
+  route_name: aggregator.admin_overview
+  parent: system.admin_config_services
+  weight: 10
+aggregator.page_last:
+  title: 'Feed aggregator'
+  weight: 5
+  route_name: aggregator.page_last
+aggregator.sources:
+  title: Sources
+  route_name: aggregator.sources
+aggregator.feed_add:
+  title: 'Add feed'
+  route_name: aggregator.feed_add
diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module
index 86011d61680e51f9de72552835029c7aaf31bc8c..90b7f29e7743e7d0bfdc36bb72a821e14818899b 100644
--- a/core/modules/aggregator/aggregator.module
+++ b/core/modules/aggregator/aggregator.module
@@ -86,35 +86,6 @@ function aggregator_theme() {
   );
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function aggregator_menu_link_defaults() {
-  $links = array();
-  $links['aggregator.admin_overview'] = array(
-    'link_title' => 'Feed aggregator',
-    'description' => "Configure which content your site aggregates from other sites, how often it polls them, and how they're categorized.",
-    'route_name' => 'aggregator.admin_overview',
-    'parent' => 'system.admin_config_services',
-    'weight' => 10,
-  );
-  $links['aggregator.page_last'] = array(
-    'link_title' => 'Feed aggregator',
-    'weight' => 5,
-    'route_name' => 'aggregator.page_last',
-  );
-  $links['aggregator.sources'] = array(
-    'link_title' => 'Sources',
-    'route_name' => 'aggregator.sources',
-  );
-  $links['aggregator.feed_add'] = array(
-    'link_title' => 'Add feed',
-    'route_name' => 'aggregator.feed_add',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_permission().
  */
diff --git a/core/modules/ban/ban.menu_links.yml b/core/modules/ban/ban.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..dc30e3a3cdf40fb4390edba4527efa340923c484
--- /dev/null
+++ b/core/modules/ban/ban.menu_links.yml
@@ -0,0 +1,6 @@
+ban.admin_page:
+  title: 'IP address bans'
+  description: 'Manage banned IP addresses.'
+  route_name: ban.admin_page
+  weight: 10
+  parent: user.admin_index
diff --git a/core/modules/ban/ban.module b/core/modules/ban/ban.module
index 03201c9e27605cf1bcbe2e1a655c3f85532167e7..5e6ba658d29a0498ede7b610a18d98ca5da0a507 100644
--- a/core/modules/ban/ban.module
+++ b/core/modules/ban/ban.module
@@ -36,18 +36,3 @@ function ban_permission() {
     ),
   );
 }
-
-/**
- * Implements hook_menu_link_defaults().
- */
-function ban_menu_link_defaults() {
-  $links['ban.admin_page'] = array(
-    'link_title' => 'IP address bans',
-    'description' => 'Manage banned IP addresses.',
-    'route_name' => 'ban.admin_page',
-    'weight' => 10,
-    'parent' => 'user.admin_index',
-  );
-
-  return $links;
-}
diff --git a/core/modules/block/block.menu_links.yml b/core/modules/block/block.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b2782aa05c8079b128de52479689071e7fb2756c
--- /dev/null
+++ b/core/modules/block/block.menu_links.yml
@@ -0,0 +1,5 @@
+block.admin_display:
+  title: 'Block layout'
+  parent: system.admin_structure
+  description: 'Configure what block content appears in your site''s sidebars and other regions.'
+  route_name: block.admin_display
diff --git a/core/modules/block/block.module b/core/modules/block/block.module
index c217c9ff89fdceb4c50a8798022c4099f158271b..73514c22bfa3b9a66b867e9502b387014e6005fe 100644
--- a/core/modules/block/block.module
+++ b/core/modules/block/block.module
@@ -93,20 +93,6 @@ function block_permission() {
   );
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function block_menu_link_defaults() {
-  $links['block.admin_display'] = array(
-    'link_title' => 'Block layout',
-    'parent' => 'system.admin_structure',
-    'description' => 'Configure what block content appears in your site\'s sidebars and other regions.',
-    'route_name' => 'block.admin_display',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_page_build().
  *
diff --git a/core/modules/book/book.menu_links.yml b/core/modules/book/book.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..f7dbc18220e6625cb17217b1a32547b3824e79e2
--- /dev/null
+++ b/core/modules/book/book.menu_links.yml
@@ -0,0 +1,9 @@
+book.admin:
+  title: Books
+  description: 'Manage your site''s book outlines.'
+  parent: system.admin_structure
+  route_name: book.admin
+book.render:
+  title: Books
+  route_name: book.render
+  hidden: 1
diff --git a/core/modules/book/book.module b/core/modules/book/book.module
index fca7d44ebf2badd71b1ca67e5a11a5b1c4a62dbd..fca17ab8ab5b301467f3861553fa2eeaa559f0cb 100644
--- a/core/modules/book/book.module
+++ b/core/modules/book/book.module
@@ -147,26 +147,6 @@ function book_node_links_alter(array &$node_links, NodeInterface $node, array &$
   }
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function book_menu_link_defaults() {
-  $links['book.admin'] = array(
-    'link_title' => 'Books',
-    'description' => "Manage your site's book outlines.",
-    'parent' => 'system.admin_structure',
-    'route_name' => 'book.admin',
-  );
-  $links['book.render'] = array(
-    'link_title' => 'Books',
-    'route_name' => 'book.render',
-    // @todo what to do about MENU_SUGGESTED_ITEM, maybe specify no menu_name?
-    'type' => MENU_SUGGESTED_ITEM,
-  );
-
-  return $links;
-}
-
 /**
  * Returns an array of all books.
  *
diff --git a/core/modules/comment/comment.menu_links.yml b/core/modules/comment/comment.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..7d715fe18561e9832f9c42a567be59549605cb8e
--- /dev/null
+++ b/core/modules/comment/comment.menu_links.yml
@@ -0,0 +1,10 @@
+comment.admin:
+  title: Comments
+  route_name: comment.admin
+  parent: system.admin
+  description: 'List and edit site comments and the comment approval queue.'
+comment.bundle_list:
+  title: 'Comment forms'
+  route_name: comment.bundle_list
+  parent: system.admin_structure
+  description: 'Manage fields and displays settings for comment forms.'
diff --git a/core/modules/comment/comment.module b/core/modules/comment/comment.module
index a5e9ec0d0dba5f9124edeb18b9bb3a288332a982..9ee58fc350e36922a249469140914cb209808e86 100644
--- a/core/modules/comment/comment.module
+++ b/core/modules/comment/comment.module
@@ -164,26 +164,6 @@ function comment_theme() {
   );
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function comment_menu_link_defaults() {
-  $links['comment.admin'] = array(
-    'link_title' => 'Comments',
-    'route_name' => 'comment.admin',
-    'parent' => \Drupal::moduleHandler()->moduleExists('node') ? 'node.content_overview' : 'system.admin',
-    'description' => 'List and edit site comments and the comment approval queue.',
-  );
-  $links['comment.bundle_list'] = array(
-    'link_title' => 'Comment forms',
-    'route_name' => 'comment.bundle_list',
-    'parent' => 'system.admin_structure',
-    'description' => 'Manage fields and displays settings for comment forms.',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_menu_link_defaults_alter()
  */
@@ -192,6 +172,9 @@ function comment_menu_link_defaults_alter(&$links) {
     // Add comments to the description for admin/content if any.
     $links['node.content_overview']['description'] = 'Administer content and comments.';
   }
+  if (\Drupal::moduleHandler()->moduleExists('node')) {
+    $links['comment.admin']['parent'] = 'node.content_overview';
+  }
 }
 
 /**
diff --git a/core/modules/config/config.menu_links.yml b/core/modules/config/config.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..87a9b92400e0525a8be0b4c3ccd4727fcd7bd74d
--- /dev/null
+++ b/core/modules/config/config.menu_links.yml
@@ -0,0 +1,5 @@
+config.sync:
+  title: 'Configuration management'
+  description: 'Import, export, or synchronize your site configuration.'
+  route_name: config.sync
+  parent: system.admin_config_development
diff --git a/core/modules/config/config.module b/core/modules/config/config.module
index 11bddb6743bf07bc4510e8fe52ae243cbb4b7bd3..900e1244444eb10238ef09b537d7f78800ca7c74 100644
--- a/core/modules/config/config.module
+++ b/core/modules/config/config.module
@@ -56,17 +56,3 @@ function config_file_download($uri) {
     );
   }
 }
-
-/**
- * Implements hook_menu_link_defaults().
- */
-function config_menu_link_defaults() {
-  $links['config.sync'] = array(
-    'link_title' => 'Configuration management',
-    'description' => 'Import, export, or synchronize your site configuration.',
-    'route_name' => 'config.sync',
-    'parent' => 'system.admin_config_development',
-  );
-
-  return $links;
-}
diff --git a/core/modules/config_translation/config_translation.menu_links.yml b/core/modules/config_translation/config_translation.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..8f53899296f847433495510291d0a1f7379be4e0
--- /dev/null
+++ b/core/modules/config_translation/config_translation.menu_links.yml
@@ -0,0 +1,6 @@
+config_translation.mapper_list:
+  title: 'Configuration translation'
+  parent: system.admin_config_regional
+  description: 'Translate the configuration.'
+  route_name: config_translation.mapper_list
+  weight: 30
diff --git a/core/modules/config_translation/config_translation.module b/core/modules/config_translation/config_translation.module
index 99bb8628b87dd6591d634ffb8daa5174f59c9d0a..0db169eb131164c9332554c8410f248bfc27e464 100644
--- a/core/modules/config_translation/config_translation.module
+++ b/core/modules/config_translation/config_translation.module
@@ -31,21 +31,6 @@ function config_translation_help($path) {
   }
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function config_translation_menu_link_defaults() {
-  $links['config_translation.mapper_list'] = array(
-    'link_title' => 'Configuration translation',
-    'parent' => 'system.admin_config_regional',
-    'description' => 'Translate the configuration.',
-    'route_name' => 'config_translation.mapper_list',
-    'weight' => 30,
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_permission().
  */
diff --git a/core/modules/contact/contact.menu_links.yml b/core/modules/contact/contact.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..683be6386d0088b4d20c32bbe560a2a7b02887a2
--- /dev/null
+++ b/core/modules/contact/contact.menu_links.yml
@@ -0,0 +1,10 @@
+contact.category_list:
+  title: 'Contact form categories'
+  parent: system.admin_structure
+  description: 'Create a system contact form and set up categories for the form to use.'
+  route_name: contact.category_list
+contact.site_page:
+  title: Contact
+  route_name: contact.site_page
+  menu_name: footer
+  hidden: 1
diff --git a/core/modules/contact/contact.module b/core/modules/contact/contact.module
index 84bd1cd121551d441d178dba9b07bd1f39d77364..17db732bd324a46dd320c6352562bc12e5d7343b 100644
--- a/core/modules/contact/contact.module
+++ b/core/modules/contact/contact.module
@@ -51,26 +51,6 @@ function contact_permission() {
   );
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function contact_menu_link_defaults() {
-  $links['contact.category_list'] = array(
-    'link_title' => 'Contact form categories',
-    'parent' => 'system.admin_structure',
-    'description' => 'Create a system contact form and set up categories for the form to use.',
-    'route_name' => 'contact.category_list',
-  );
-
-  $links['contact.site_page'] = array(
-    'link_title' => 'Contact',
-    'route_name' => 'contact.site_page',
-    'menu_name' => 'footer',
-    'type' => MENU_SUGGESTED_ITEM,
-  );
-  return $links;
-}
-
 /**
  * Implements hook_entity_type_alter().
  */
diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module
index bf2461b2090e72b5403c2cd3040404a71d21469e..dc405b90ecc1a69457ad283e008420e4dce7f546 100644
--- a/core/modules/content_translation/content_translation.module
+++ b/core/modules/content_translation/content_translation.module
@@ -204,7 +204,7 @@ function content_translation_entity_operation_alter(array &$operations, \Drupal\
  */
 function content_translation_menu_link_defaults_alter(array &$links) {
   // Clarify where translation settings are located.
-  $links['language.content_settings_page']['link_title'] = 'Content language and translation';
+  $links['language.content_settings_page']['title'] = 'Content language and translation';
   $links['language.content_settings_page']['description'] = 'Configure language and translation support for content.';
 }
 
diff --git a/core/modules/dblog/dblog.menu_links.yml b/core/modules/dblog/dblog.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e45a73721bb53c31a6ffcb5533c497ec1c92f191
--- /dev/null
+++ b/core/modules/dblog/dblog.menu_links.yml
@@ -0,0 +1,16 @@
+dblog.overview:
+  title: 'Recent log messages'
+  parent: system.admin_reports
+  description: 'View events that have recently been logged.'
+  route_name: dblog.overview
+  weight: -1
+dblog.page_not_found:
+  title: 'Top ''page not found'' errors'
+  route_name: dblog.page_not_found
+  parent: system.admin_reports
+  description: 'View ''page not found'' errors (404s).'
+dblog.access_denied:
+  title: 'Top ''access denied'' errors'
+  route_name: dblog.access_denied
+  description: 'View ''access denied'' errors (403s).'
+  parent: system.admin_reports
diff --git a/core/modules/dblog/dblog.module b/core/modules/dblog/dblog.module
index 5052a8a8e877fca6a02267989cb55a0f9b401ea2..392057964c0c91e60bf57f1b794d966e71162d77 100644
--- a/core/modules/dblog/dblog.module
+++ b/core/modules/dblog/dblog.module
@@ -36,32 +36,12 @@ function dblog_help($path, $arg) {
 }
 
 /**
- * Implements hook_menu_link_defaults().
+ * Implements hook_menu_link_defaults_alter().
  */
-function dblog_menu_link_defaults() {
-  $links['dblog.overview'] = array(
-    'link_title' => 'Recent log messages',
-    'parent' => 'system.admin_reports',
-    'description' => 'View events that have recently been logged.',
-    'route_name' => 'dblog.overview',
-    'weight' => -1,
-  );
-  $links['dblog.page_not_found'] = array(
-    'link_title' => "Top 'page not found' errors",
-    'route_name' => 'dblog.page_not_found',
-    'parent' => 'system.admin_reports',
-    'description' => "View 'page not found' errors (404s).",
-  );
-  $links['dblog.access_denied'] = array(
-    'link_title' => "Top 'access denied' errors",
-    'route_name' => 'dblog.access_denied',
-    'description' => "View 'access denied' errors (403s).",
-    'parent' => 'system.admin_reports',
-  );
-
+function dblog_menu_link_defaults_alter(&$links) {
   if (\Drupal::moduleHandler()->moduleExists('search')) {
     $links['dblog.search'] = array(
-      'link_title' => 'Top search phrases',
+      'title' => 'Top search phrases',
       'route_name' => 'dblog.search',
       'description' => 'View most popular search phrases.',
       'parent' => 'system.admin_reports',
diff --git a/core/modules/editor/editor.module b/core/modules/editor/editor.module
index 053a5df5358a6ea6bcc0c9b519156b48eefe3907..abebdb9e6fa52dbe202dacc915df66a16f23deed 100644
--- a/core/modules/editor/editor.module
+++ b/core/modules/editor/editor.module
@@ -44,7 +44,7 @@ function editor_help($path, $arg) {
  * of text editors.
  */
 function editor_menu_link_defaults_alter(array &$links) {
-  $links['filter.admin_overview']['link_title'] = 'Text formats and editors';
+  $links['filter.admin_overview']['title'] = 'Text formats and editors';
   $links['filter.admin_overview']['description'] = 'Configure how user-contributed content is filtered and formatted, as well as the text editor user interface (WYSIWYGs or toolbars).';
 }
 
diff --git a/core/modules/entity/entity.menu_links.yml b/core/modules/entity/entity.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..66c67c644c687992a5c76e988b53858e7a8f64d1
--- /dev/null
+++ b/core/modules/entity/entity.menu_links.yml
@@ -0,0 +1,15 @@
+entity.display_mode:
+  title: 'Display modes'
+  description: 'Configure what displays are available for your content and forms.'
+  route_name: entity.display_mode
+  parent: system.admin_structure
+entity.view_mode_list:
+  title: 'View modes'
+  description: 'Manage custom view modes.'
+  route_name: entity.view_mode_list
+  parent: entity.display_mode
+entity.form_mode_list:
+  title: 'Form modes'
+  description: 'Manage custom form modes.'
+  route_name: entity.form_mode_list
+  parent: entity.display_mode
diff --git a/core/modules/entity/entity.module b/core/modules/entity/entity.module
index e314b83ea493946cf4bc89a96c471bf1c85a8f4d..1fa38525d8218464d3274d93a1e23d94052ae654 100644
--- a/core/modules/entity/entity.module
+++ b/core/modules/entity/entity.module
@@ -44,36 +44,6 @@ function entity_permission() {
   );
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function entity_menu_link_defaults() {
-  $links['entity.display_mode'] = array(
-    'link_title' => 'Display modes',
-    'description' => 'Configure what displays are available for your content and forms.',
-    'route_name' => 'entity.display_mode',
-    'parent' => 'system.admin_structure',
-  );
-
-  // View modes.
-  $links['entity.view_mode_list'] = array(
-    'link_title' => 'View modes',
-    'description' => 'Manage custom view modes.',
-    'route_name' => 'entity.view_mode_list',
-    'parent' => 'entity.display_mode',
-  );
-
-  // Form modes.
-  $links['entity.form_mode_list'] = array(
-    'link_title' => 'Form modes',
-    'description' => 'Manage custom form modes.',
-    'route_name' => 'entity.form_mode_list',
-    'parent' => 'entity.display_mode',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_entity_bundle_rename().
  */
diff --git a/core/modules/field_ui/field_ui.menu_links.yml b/core/modules/field_ui/field_ui.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..a128f2ea00ef158ff992bbd134ec90b1fe91e7ed
--- /dev/null
+++ b/core/modules/field_ui/field_ui.menu_links.yml
@@ -0,0 +1,5 @@
+field_ui.list:
+  title: 'Field list'
+  description: 'Overview of fields on all entity types.'
+  route_name: field_ui.list
+  parent: system.admin_reports
diff --git a/core/modules/field_ui/field_ui.module b/core/modules/field_ui/field_ui.module
index 5a111f87e583bf7c2b5bd6401168efa0639bacc1..24e5221462136abe495e8928ade2f2da37b3fc52 100644
--- a/core/modules/field_ui/field_ui.module
+++ b/core/modules/field_ui/field_ui.module
@@ -52,20 +52,6 @@ function field_ui_help($path, $arg) {
   }
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function field_ui_menu_link_defaults() {
-  $links['field_ui.list'] = array(
-    'link_title' => 'Field list',
-    'description' => 'Overview of fields on all entity types.',
-    'route_name' => 'field_ui.list',
-    'parent' => 'system.admin_reports',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_permission().
  */
diff --git a/core/modules/filter/filter.menu_links.yml b/core/modules/filter/filter.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e71db5015c33c9ed94a7ef29e5d3b6d0f22f7074
--- /dev/null
+++ b/core/modules/filter/filter.menu_links.yml
@@ -0,0 +1,9 @@
+filter.tips_all:
+  title: 'Compose tips'
+  hidden: 1
+  route_name: filter.tips_all
+filter.admin_overview:
+  title: 'Text formats'
+  parent: system.admin_config_content
+  description: 'Configure how content input by users is filtered, including allowed HTML tags. Also allows enabling of module-provided filters.'
+  route_name: filter.admin_overview
diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module
index 166590e7d566282c315667ae74074dad5df63ed4..475bdbb3499f7beea6052fadbf4d175a19cdfd14 100644
--- a/core/modules/filter/filter.module
+++ b/core/modules/filter/filter.module
@@ -94,26 +94,6 @@ function filter_element_info() {
   return $type;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function filter_menu_link_defaults() {
-  $links['filter.tips_all'] = array(
-    'link_title' => 'Compose tips',
-    'type' => MENU_SUGGESTED_ITEM,
-    'route_name' => 'filter.tips_all',
-  );
-
-  $links['filter.admin_overview'] = array(
-    'link_title' => 'Text formats',
-    'parent' => 'system.admin_config_content',
-    'description' => 'Configure how content input by users is filtered, including allowed HTML tags. Also allows enabling of module-provided filters.',
-    'route_name' => 'filter.admin_overview',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_permission().
  */
diff --git a/core/modules/forum/forum.menu_links.yml b/core/modules/forum/forum.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4586a1202afdfc912e75fb4a7fc8d5293cec5872
--- /dev/null
+++ b/core/modules/forum/forum.menu_links.yml
@@ -0,0 +1,9 @@
+forum.index:
+  title: Forums
+  route_name: forum.index
+  menu_name: tools
+forum.overview:
+  title: Forums
+  parent: system.admin_structure
+  description: 'Control forum hierarchy settings.'
+  route_name: forum.overview
diff --git a/core/modules/forum/forum.module b/core/modules/forum/forum.module
index faca699ca35e8c36c34455fc906b250de68caeec..4e3cd527d3c430528e81c4a76508770496226b4c 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -95,24 +95,6 @@ function forum_theme() {
   );
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function forum_menu_link_defaults() {
-  $links['forum.index'] = array(
-    'link_title' => 'Forums',
-    'route_name' => 'forum.index',
-    'menu_name' => 'tools',
-  );
-  $links['forum.overview'] = array(
-    'link_title' => 'Forums',
-    'parent' => 'system.admin_structure',
-    'description' => 'Control forum hierarchy settings.',
-    'route_name' => 'forum.overview',
-  );
-  return $links;
-}
-
 /**
  * Implements hook_menu_local_tasks().
  */
diff --git a/core/modules/help/help.menu_links.yml b/core/modules/help/help.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..aa85addcc7eb539834e70340be7182ee569379d1
--- /dev/null
+++ b/core/modules/help/help.menu_links.yml
@@ -0,0 +1,6 @@
+help.main:
+  title: Help
+  description: 'Reference for usage, configuration, and modules.'
+  route_name: help.main
+  weight: 9
+  parent: system.admin
diff --git a/core/modules/help/help.module b/core/modules/help/help.module
index 89e0f3175dfa047e7f608e1243311137f81207bc..b02085a60e4daecc04f7d983200cb6e95d29d444 100644
--- a/core/modules/help/help.module
+++ b/core/modules/help/help.module
@@ -5,21 +5,6 @@
  * Manages displaying online help.
  */
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function help_menu_link_defaults() {
-  $links['help.main'] = array(
-    'link_title' => 'Help',
-    'description' => 'Reference for usage, configuration, and modules.',
-    'route_name' => 'help.main',
-    'weight' => 9,
-    'parent' => 'system.admin',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_help().
  */
diff --git a/core/modules/image/image.menu_links.yml b/core/modules/image/image.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..02b60e800d7d3cfa276e4b17a80da05199c06ae5
--- /dev/null
+++ b/core/modules/image/image.menu_links.yml
@@ -0,0 +1,5 @@
+image.style_list:
+  title: 'Image styles'
+  description: 'Configure styles that can be used for resizing or adjusting images on display.'
+  parent: system.admin_config_media
+  route_name: image.style_list
diff --git a/core/modules/image/image.module b/core/modules/image/image.module
index 472cad4a4516540a05ad78c93d8eacf0372a9f8f..a57fe55d724bf198e56844dd8ca9045eddb33f0f 100644
--- a/core/modules/image/image.module
+++ b/core/modules/image/image.module
@@ -83,20 +83,6 @@ function image_help($path, $arg) {
   }
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function image_menu_link_defaults() {
-  $links['image.style_list'] = array(
-    'link_title' => 'Image styles',
-    'description' => 'Configure styles that can be used for resizing or adjusting images on display.',
-    'parent' => 'system.admin_config_media',
-    'route_name' => 'image.style_list',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_theme().
  */
diff --git a/core/modules/language/language.menu_links.yml b/core/modules/language/language.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..26f9f9a7a9e998a2c20f0d8adce1137e0b796d92
--- /dev/null
+++ b/core/modules/language/language.menu_links.yml
@@ -0,0 +1,11 @@
+language.admin_overview:
+  title: Languages
+  description: 'Configure languages for content and the user interface.'
+  route_name: language.admin_overview
+  parent: system.admin_config_regional
+language.content_settings_page:
+  title: 'Content language'
+  description: 'Configure language support for content.'
+  route_name: language.content_settings_page
+  parent: system.admin_config_regional
+  weight: 10
diff --git a/core/modules/language/language.module b/core/modules/language/language.module
index 4d825089a457c80d608d43542e40bfc760f74717..41a8a6f24d76d206c73540cda4e0ad7b83fd1dfb 100644
--- a/core/modules/language/language.module
+++ b/core/modules/language/language.module
@@ -69,29 +69,6 @@ function language_help($path, $arg) {
   }
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function language_menu_link_defaults() {
-  // Base language management and configuration.
-  $links['language.admin_overview'] = array(
-    'link_title' => 'Languages',
-    'description' => 'Configure languages for content and the user interface.',
-    'route_name' => 'language.admin_overview',
-    'parent' => 'system.admin_config_regional',
-  );
-  // Content language settings.
-  $links['language.content_settings_page'] = array(
-    'link_title' => 'Content language',
-    'description' => 'Configure language support for content.',
-    'route_name' => 'language.content_settings_page',
-    'parent' => 'system.admin_config_regional',
-    'weight' => 10,
-  );
-
-  return $links;
-}
-
 /**
  * Editing or deleting locked languages should not be possible.
  *
diff --git a/core/modules/locale/locale.menu_links.yml b/core/modules/locale/locale.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..41665aefd38d30b925437cfe5bd946155a038067
--- /dev/null
+++ b/core/modules/locale/locale.menu_links.yml
@@ -0,0 +1,11 @@
+locale.translate_page:
+  title: 'User interface translation'
+  description: 'Translate the built-in user interface.'
+  route_name: locale.translate_page
+  parent: system.admin_config_regional
+  weight: 15
+locale.translate_status:
+  title: 'Available translation updates'
+  route_name: locale.translate_status
+  description: 'Get a status report about available interface translations for your installed modules and themes.'
+  parent: system.admin_reports
diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module
index 504a42e83522a895d572cef61ecaa8d6dd03b2d2..79bf982d4d889b4b42d243ac7497b0c526cfed8a 100644
--- a/core/modules/locale/locale.module
+++ b/core/modules/locale/locale.module
@@ -167,29 +167,6 @@ function locale_help($path, $arg) {
   }
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function locale_menu_link_defaults() {
-  // Translation functionality.
-  $links['locale.translate_page'] = array(
-    'link_title' => 'User interface translation',
-    'description' => 'Translate the built-in user interface.',
-    'route_name' => 'locale.translate_page',
-    'parent' => 'system.admin_config_regional',
-    'weight' => 15,
-  );
-
-  $links['locale.translate_status'] = array(
-    'link_title' => 'Available translation updates',
-    'route_name' => 'locale.translate_status',
-    'description' => 'Get a status report about available interface translations for your installed modules and themes.',
-    'parent' => 'system.admin_reports',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_permission().
  */
diff --git a/core/modules/menu/lib/Drupal/menu/Form/MenuDeleteForm.php b/core/modules/menu/lib/Drupal/menu/Form/MenuDeleteForm.php
index 31576dfbb0f1e1baaee13f3361990f24cbe793e3..bcd5611a5d797d418fddca98140421d39728bfb2 100644
--- a/core/modules/menu/lib/Drupal/menu/Form/MenuDeleteForm.php
+++ b/core/modules/menu/lib/Drupal/menu/Form/MenuDeleteForm.php
@@ -104,10 +104,11 @@ public function submit(array $form, array &$form_state) {
       return;
     }
 
-    // Reset all the menu links defined by the system via hook_menu_link_defaults().
+    // Reset all the menu links defined by the menu_link.static service.
     $result = \Drupal::entityQuery('menu_link')
       ->condition('menu_name', $this->entity->id())
-      ->condition('module', 'system')
+      ->condition('module', '', '>')
+      ->condition('machine_name', '', '>')
       ->sort('depth', 'ASC')
       ->execute();
     $menu_links = $this->storage->loadMultiple($result);
diff --git a/core/modules/menu/lib/Drupal/menu/MenuFormController.php b/core/modules/menu/lib/Drupal/menu/MenuFormController.php
index 720d43553ad30d9546113d37b55ebe950b47cb8b..d92f80079e3feb160762532b9d5f3de27495933f 100644
--- a/core/modules/menu/lib/Drupal/menu/MenuFormController.php
+++ b/core/modules/menu/lib/Drupal/menu/MenuFormController.php
@@ -302,7 +302,7 @@ protected function buildOverviewTreeForm($tree, $delta) {
         if ($item['hidden']) {
           $form[$mlid]['title']['#markup'] .= ' (' . t('disabled') . ')';
         }
-        elseif ($item['link_path'] == 'user' && $item['module'] == 'system') {
+        elseif ($item['link_path'] == 'user' && $item['module'] == 'user') {
           $form[$mlid]['title']['#markup'] .= ' (' . t('logged in users only') . ')';
         }
 
diff --git a/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php b/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php
index f6461c9e3961f40b3702047feae96aa4f35f3c4d..a95f25f1a421d397d78b7caef4d649349cc1e82a 100644
--- a/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php
+++ b/core/modules/menu/lib/Drupal/menu/Tests/MenuTest.php
@@ -781,8 +781,8 @@ private function getStandardMenuLink() {
     // Retrieve menu link id of the Log out menu link, which will always be on
     // the front page.
     $query = \Drupal::entityQuery('menu_link')
-      ->condition('module', 'system')
-      ->condition('link_path', 'user/logout');
+      ->condition('module', 'user')
+      ->condition('machine_name', 'user.logout');
     $result = $query->execute();
     if (!empty($result)) {
       $mlid = reset($result);
diff --git a/core/modules/menu/menu.menu_links.yml b/core/modules/menu/menu.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..c3ec08a1f1f2cb029d3b28c8123caa777464a176
--- /dev/null
+++ b/core/modules/menu/menu.menu_links.yml
@@ -0,0 +1,5 @@
+menu.overview_page:
+  title: Menus
+  description: 'Add new menus to your site, edit existing menus, and rename and reorganize menu links.'
+  route_name: menu.overview_page
+  parent: system.admin_structure
diff --git a/core/modules/menu/menu.module b/core/modules/menu/menu.module
index e89feb8724b5d8bfe86bf20e7438f9b97b6092cc..932a0edff425cf162870d96262359550356dd78c 100644
--- a/core/modules/menu/menu.module
+++ b/core/modules/menu/menu.module
@@ -63,19 +63,6 @@ function menu_permission() {
   );
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function menu_menu_link_defaults() {
-  $links['menu.overview_page'] = array(
-    'link_title' => 'Menus',
-    'description' => 'Add new menus to your site, edit existing menus, and rename and reorganize menu links.',
-    'route_name' => 'menu.overview_page',
-    'parent' => 'system.admin_structure',
-  );
-  return $links;
-}
-
 /**
  * Implements hook_entity_type_build().
  */
diff --git a/core/modules/menu_link/lib/Drupal/menu_link/Entity/MenuLink.php b/core/modules/menu_link/lib/Drupal/menu_link/Entity/MenuLink.php
index 0c7da034264b8f8e177663b92eb2858643068430..ebb28bf89435dbb231b571c3068e8a6c04e99014 100644
--- a/core/modules/menu_link/lib/Drupal/menu_link/Entity/MenuLink.php
+++ b/core/modules/menu_link/lib/Drupal/menu_link/Entity/MenuLink.php
@@ -64,7 +64,7 @@ class MenuLink extends Entity implements \ArrayAccess, MenuLinkInterface {
   public $mlid;
 
   /**
-   * An optional machine name if defined via hook_menu_link_defaults().
+   * An optional machine name if defined via the menu_link.static service.
    *
    * @var string
    */
@@ -358,17 +358,32 @@ public function setRouteObject(Route $route) {
    */
   public function reset() {
     // To reset the link to its original values, we need to retrieve its
-    // definition from hook_menu_link_defaults(). Otherwise, for example, the
-    // link's menu would not be reset, because properties like the original
+    // definition from the menu_link.static service. Otherwise, for example,
+    // the link's menu would not be reset, because properties like the original
     // 'menu_name' are not stored anywhere else. Since resetting a link happens
     // rarely and this is a one-time operation, retrieving the full set of
     // default menu links does little harm.
-    $all_links = menu_link_get_defaults();
+    $all_links = \Drupal::service('menu_link.static')->getLinks();
     $original = $all_links[$this->machine_name];
     $original['machine_name'] = $this->machine_name;
     /** @var \Drupal\menu_link\MenuLinkStorageInterface $storage */
     $storage = \Drupal::entityManager()->getStorage($this->entityTypeId);
     $new_link = $storage->createFromDefaultLink($original);
+    // Allow the menu to be determined by the parent
+    if (!empty($new_link['parent']) && !empty($all_links[$new_link['parent']])) {
+      // Walk up the tree to find the menu name.
+      $parent = $all_links[$new_link['parent']];
+      $existing_parent = db_select('menu_links')
+        ->fields('menu_links')
+        ->condition('machine_name', $parent['machine_name'])
+        ->execute()->fetchAssoc();
+      if ($existing_parent) {
+        /** @var \Drupal\Core\Entity\EntityInterface $existing_parent */
+        $existing_parent = $storage->create($existing_parent);
+        $new_link->menu_name = $existing_parent->menu_name;
+        $new_link->plid = $existing_parent->id();
+      }
+    }
     // Merge existing menu link's ID and 'has_children' property.
     foreach (array('mlid', 'has_children') as $key) {
       $new_link->{$key} = $this->{$key};
@@ -592,7 +607,7 @@ protected function setParents(MenuLinkInterface $parent) {
   protected function findParent(EntityStorageInterface $storage) {
     $parent = FALSE;
 
-    // This item is explicitely top-level, skip the rest of the parenting.
+    // This item is explicitly top-level, skip the rest of the parenting.
     if (isset($this->plid) && empty($this->plid)) {
       return $parent;
     }
diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkAccessController.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkAccessController.php
index 85cc796d40b6290162d078bbc5d39f6bd561838b..f316b207838e4e5531d8024604627b4074ec5f0a 100644
--- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkAccessController.php
+++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkAccessController.php
@@ -27,7 +27,7 @@ protected function checkAccess(EntityInterface $entity, $operation, $langcode, A
       switch ($operation) {
         case 'reset':
           // Reset allowed for items defined via hook_menu() and customized.
-          return $entity->module == 'system' && $entity->customized;
+          return !empty($entity->machine_name) && $entity->customized;
 
         case 'delete':
           // Only items created by the menu module can be deleted.
diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorage.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorage.php
index 64f7ed67461e92e51b03c8b9100ec39ee9c60211..ea0596897530ff4cb311260645f9b5fa4afef351 100644
--- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorage.php
+++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorage.php
@@ -152,15 +152,23 @@ public function loadUpdatedCustomized(array $router_paths) {
    * {@inheritdoc}
    */
   public function loadModuleAdminTasks() {
-    $query = $this->buildQuery(NULL);
+    // @todo - this code will move out of the menu link entity, so we are doing
+    //   a straight SQL query for expediency.
+    $result = $this->database->select('menu_links');
+    $result->condition('machine_name', 'system.admin');
+    $result->addField('menu_links', 'mlid');
+    $plid = $result->execute()->fetchField();
+
+    $query = $this->database->select('menu_links', 'base', array('fetch' => \PDO::FETCH_ASSOC));
+    $query->fields('base');
     $query
-      ->condition('base.link_path', 'admin/%', 'LIKE')
       ->condition('base.hidden', 0, '>=')
-      ->condition('base.module', 'system')
-      ->condition('base.route_name', 'system.admin', '<>');
-    $ids = $query->execute()->fetchCol(1);
+      ->condition('base.module', '', '>')
+      ->condition('base.machine_name', '', '>')
+      ->condition('base.p1', $plid);
+    $entities = $query->execute()->fetchAll();
 
-    return $this->loadMultiple($ids);
+    return $entities;
   }
 
   /**
@@ -304,16 +312,9 @@ public function getParentFromHierarchy(EntityInterface $entity) {
   public function createFromDefaultLink(array $item) {
     // Suggested items are disabled by default.
     $item += array(
-      'type' => MENU_NORMAL_ITEM,
       'hidden' => 0,
       'options' => empty($item['description']) ? array() : array('attributes' => array('title' => $item['description'])),
     );
-    if ($item['type'] == MENU_SUGGESTED_ITEM) {
-      $item['hidden'] = 1;
-    }
-    // Note, we set this as 'system', so that we can be sure to distinguish all
-    // the menu links generated automatically from hook_menu_link_defaults().
-    $item['module'] = 'system';
     return $this->create($item);
   }
 
diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageInterface.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageInterface.php
index 4a9d719c042f2499d9b193d607f1c4e1cb0eae33..3d3fd5d10c5165a1f7764835f32f03430a72b27a 100644
--- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageInterface.php
+++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageInterface.php
@@ -97,10 +97,10 @@ public function getParentFromHierarchy(EntityInterface $entity);
    * Builds a menu link entity from a default item.
    *
    * This function should only be called for link data from
-   * hook_menu_link_defaults().
+   * the menu_link.static service.
    *
    * @param array $item
-   *   An item returned from menu_links_get_defaults().
+   *   An item returned from the menu_link.static service.
    *
    * @return \Drupal\menu_link\MenuLinkInterface
    *   A menu link entity.
diff --git a/core/modules/menu_link/lib/Drupal/menu_link/StaticMenuLinks.php b/core/modules/menu_link/lib/Drupal/menu_link/StaticMenuLinks.php
new file mode 100644
index 0000000000000000000000000000000000000000..5e8e1ce57018b5a8d420a3bc87504aa6478dada7
--- /dev/null
+++ b/core/modules/menu_link/lib/Drupal/menu_link/StaticMenuLinks.php
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Menu\StaticMenuLinks.
+ */
+
+namespace Drupal\menu_link;
+use Drupal\Component\Discovery\YamlDiscovery;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+
+/**
+ * Provides a service which finds and alters default menu links in yml files.
+ */
+class StaticMenuLinks {
+
+  /**
+   * The module handler.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * Constructs a new StaticMenuLinks.
+   *
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler.
+   */
+  public function __construct(ModuleHandlerInterface $module_handler) {
+    $this->moduleHandler = $module_handler;
+  }
+
+  /**
+   * Gets the menu links defined in YAML files.
+   *
+   * @return array
+   *   An array of default menu links.
+   */
+  public function getLinks() {
+    $discovery = $this->getDiscovery();
+    foreach ($discovery->findAll() as $module => $menu_links) {
+      foreach ($menu_links as $machine_name => $menu_link) {
+        $all_links[$machine_name] = $menu_link;
+        $all_links[$machine_name]['machine_name'] = $machine_name;
+        $all_links[$machine_name]['module'] = $module;
+      }
+    }
+
+    $this->moduleHandler->alter('menu_link_defaults', $all_links);
+    // Change the key to match the DB column for now.
+    foreach ($all_links as $machine_name => $menu_link) {
+      $all_links[$machine_name]['link_title'] = $all_links[$machine_name]['title'];
+      unset($all_links[$machine_name]['title']);
+    }
+
+    return $all_links;
+  }
+
+  /**
+   * Creates a YAML discovery for menu links.
+   *
+   * @return \Drupal\Component\Discovery\YamlDiscovery
+   *   An YAML discovery instance.
+   */
+  protected function getDiscovery() {
+    return new YamlDiscovery('menu_links', $this->moduleHandler->getModuleDirectories());
+  }
+
+}
+
diff --git a/core/modules/menu_link/menu_link.api.php b/core/modules/menu_link/menu_link.api.php
index cf8ee6dfa38511380cdcdf92ad78c83136f19a38..b4e88928b57602a1a6b963bb2635042c61581c24 100644
--- a/core/modules/menu_link/menu_link.api.php
+++ b/core/modules/menu_link/menu_link.api.php
@@ -86,7 +86,7 @@ function hook_menu_link_presave(\Drupal\menu_link\Entity\MenuLink $menu_link) {
   // Flag a menu link to be altered by hook_menu_link_load(), but only if it is
   // derived from a menu router item; i.e., do not alter a custom menu link
   // pointing to the same path that has been created by a user.
-  if ($menu_link->link_path == 'user' && $menu_link->module == 'system') {
+  if ($menu_link->machine_name == 'user.page') {
     $menu_link->options['alter'] = TRUE;
   }
 }
diff --git a/core/modules/menu_link/menu_link.services.yml b/core/modules/menu_link/menu_link.services.yml
index 5793fa05970cdd427dd23c8141c3cd0d0a3fa49d..88f5037de194411e0c0e1ec356ad36e03289ed0d 100644
--- a/core/modules/menu_link/menu_link.services.yml
+++ b/core/modules/menu_link/menu_link.services.yml
@@ -2,3 +2,6 @@ services:
   menu_link.tree:
     class: Drupal\menu_link\MenuTree
     arguments: ['@database', '@cache.data', '@language_manager', '@request_stack', '@entity.manager', '@entity.query', '@state']
+  menu_link.static:
+    class: Drupal\menu_link\StaticMenuLinks
+    arguments: ['@module_handler']
diff --git a/core/modules/node/node.menu_links.yml b/core/modules/node/node.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..50fd002e399a8f8eb8a2bc9f8fcb802edffb2157
--- /dev/null
+++ b/core/modules/node/node.menu_links.yml
@@ -0,0 +1,14 @@
+node.content_overview:
+  title: Content
+  route_name: node.content_overview
+  parent: system.admin
+  description: 'Find and manage content.'
+  weight: -10
+node.overview_types:
+  title: 'Content types'
+  parent: system.admin_structure
+  description: 'Manage content types, including default status, front page promotion, comment settings, etc.'
+  route_name: node.overview_types
+node.add_page:
+  title: 'Add content'
+  route_name: node.add_page
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 21e88dd354711eef6b3ca985756a3419352667b7..63c2174dbc193c33f3b37afe999a826641744d50 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -922,31 +922,6 @@ function _node_revision_access(NodeInterface $node, $op = 'view', $account = NUL
   return \Drupal::service('access_check.node.revision')->checkAccess($node, $account, $op, $langcode);
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function node_menu_link_defaults() {
-  $links['node.content_overview'] = array(
-    'link_title' => 'Content',
-    'route_name' => 'node.content_overview',
-    'parent' => 'system.admin',
-    'description' => 'Find and manage content.',
-    'weight' => -10,
-  );
-
-  $links['node.overview_types'] = array(
-    'link_title' => 'Content types',
-    'parent' => 'system.admin_structure',
-    'description' => 'Manage content types, including default status, front page promotion, comment settings, etc.',
-    'route_name' => 'node.overview_types',
-  );
-  $links['node.add_page'] = array(
-    'link_title' => 'Add content',
-    'route_name' => 'node.add_page',
-  );
-  return $links;
-}
-
 /**
  * Title callback: Displays the node's title.
  *
diff --git a/core/modules/path/path.menu_links.yml b/core/modules/path/path.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..46bdd93d5d1577d4930f019744c3c893e0696fcc
--- /dev/null
+++ b/core/modules/path/path.menu_links.yml
@@ -0,0 +1,6 @@
+path.admin_overview:
+  title: 'URL aliases'
+  description: 'Change your site''s URL paths by aliasing them.'
+  route_name: path.admin_overview
+  parent: system.admin_config_search
+  weight: -5
diff --git a/core/modules/path/path.module b/core/modules/path/path.module
index cf20fa6b5f15075d3ac811e1b0da921cdbcda277..8880073eacea2d1f79aa4f3327376b232b27547e 100644
--- a/core/modules/path/path.module
+++ b/core/modules/path/path.module
@@ -54,21 +54,6 @@ function path_permission() {
   );
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function path_menu_link_defaults() {
-  $links['path.admin_overview'] = array(
-    'link_title' => 'URL aliases',
-    'description' => "Change your site's URL paths by aliasing them.",
-    'route_name' => 'path.admin_overview',
-    'parent' => 'system.admin_config_search',
-    'weight' => -5,
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_form_BASE_FORM_ID_alter() for node_form().
  *
diff --git a/core/modules/responsive_image/responsive_image.menu_links.yml b/core/modules/responsive_image/responsive_image.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..796e1f5c472b09506a732566909c92c293e74e07
--- /dev/null
+++ b/core/modules/responsive_image/responsive_image.menu_links.yml
@@ -0,0 +1,6 @@
+responsive_image.mapping_page:
+  title: 'Responsive image mappings'
+  description: 'Manage responsive image mappings'
+  weight: 10
+  route_name: responsive_image.mapping_page
+  parent: system.admin_config_media
diff --git a/core/modules/responsive_image/responsive_image.module b/core/modules/responsive_image/responsive_image.module
index 4b26fc30dd8e1fdbb80360621f083ef23bd76456..412dbc965ac5305e62ec1c30a436ae6b2b28581a 100644
--- a/core/modules/responsive_image/responsive_image.module
+++ b/core/modules/responsive_image/responsive_image.module
@@ -68,21 +68,6 @@ function responsive_image_menu() {
   return $items;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function responsive_image_menu_link_defaults() {
-  $links['responsive_image.mapping_page'] = array(
-    'link_title' => 'Responsive image mappings',
-    'description' => 'Manage responsive image mappings',
-    'weight' => 10,
-    'route_name' => 'responsive_image.mapping_page',
-    'parent' => 'system.admin_config_media',
-  );
-
-  return $links;
-}
-
 /**
  * Load one responsive image by its identifier.
  *
diff --git a/core/modules/search/search.menu_links.yml b/core/modules/search/search.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4648596a71e427b2fe4ae690efffe0eaea5b985f
--- /dev/null
+++ b/core/modules/search/search.menu_links.yml
@@ -0,0 +1,10 @@
+search.view:
+  title: Search
+  route_name: search.view
+  hidden: 1
+search.settings:
+  title: 'Search settings'
+  parent: system.admin_config_search
+  description: 'Configure relevance settings for search and other indexing options.'
+  route_name: search.settings
+  weight: -10
diff --git a/core/modules/search/search.module b/core/modules/search/search.module
index 62822e5d2b73fb48a2e0599426295d1019e51752..e6ea9f0d2cca83b5456ff8331a14446fda043d12 100644
--- a/core/modules/search/search.module
+++ b/core/modules/search/search.module
@@ -138,26 +138,6 @@ function search_preprocess_block(&$variables) {
   }
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function search_menu_link_defaults() {
-  $links['search.view'] = array(
-    'link_title' => 'Search',
-    'route_name' => 'search.view',
-    'type' => MENU_SUGGESTED_ITEM,
-  );
-  $links['search.settings'] = array(
-    'link_title' => 'Search settings',
-    'parent' => 'system.admin_config_search',
-    'description' => 'Configure relevance settings for search and other indexing options.',
-    'route_name' => 'search.settings',
-    'weight' => -10,
-  );
-
-  return $links;
-}
-
 /**
  * Clears either a part of, or the entire search index.
  *
diff --git a/core/modules/shortcut/shortcut.menu_links.yml b/core/modules/shortcut/shortcut.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..8c9ded56b8d7ef4b0287d5ccd0e5bc57002eeb3e
--- /dev/null
+++ b/core/modules/shortcut/shortcut.menu_links.yml
@@ -0,0 +1,5 @@
+shortcut.set_admin:
+  title: Shortcuts
+  description: 'Add and modify shortcut sets.'
+  route_name: shortcut.set_admin
+  parent: system.admin_config_ui
diff --git a/core/modules/shortcut/shortcut.module b/core/modules/shortcut/shortcut.module
index 3f8a261d3dbcfc37856e5bd119a0de1bad94af14..5f8444041b4f7f022c6763aa911ec4a2fdf0e029 100644
--- a/core/modules/shortcut/shortcut.module
+++ b/core/modules/shortcut/shortcut.module
@@ -61,20 +61,6 @@ function shortcut_permission() {
   );
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function shortcut_menu_link_defaults() {
-  $links['shortcut.set_admin'] = array(
-    'link_title' => 'Shortcuts',
-    'description' => 'Add and modify shortcut sets.',
-    'route_name' => 'shortcut.set_admin',
-    'parent' => 'system.admin_config_ui',
-  );
-
-  return $links;
-}
-
 /**
  * Access callback for editing a shortcut set.
  *
diff --git a/core/modules/simpletest/simpletest.menu_links.yml b/core/modules/simpletest/simpletest.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..9e1f007f577c321bef29a4e6431361012397de6a
--- /dev/null
+++ b/core/modules/simpletest/simpletest.menu_links.yml
@@ -0,0 +1,6 @@
+simpletest.test_form:
+  title: Testing
+  description: 'Run tests against Drupal core and your modules. These tests help assure that your site code is working as designed.'
+  route_name: simpletest.test_form
+  parent: system.admin_config_development
+  weight: -5
diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module
index 436adc3434b831da7dc15126514a61e0aec4d43d..7434bc4c8636f8ba8809f55385be446592f7aa09 100644
--- a/core/modules/simpletest/simpletest.module
+++ b/core/modules/simpletest/simpletest.module
@@ -34,21 +34,6 @@ function simpletest_help($path, $arg) {
   }
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function simpletest_menu_link_defaults() {
-  $links['simpletest.test_form'] = array(
-    'link_title' => 'Testing',
-    'description' => 'Run tests against Drupal core and your modules. These tests help assure that your site code is working as designed.',
-    'route_name' => 'simpletest.test_form',
-    'parent' => 'system.admin_config_development',
-    'weight' => -5,
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_permission().
  */
diff --git a/core/modules/statistics/statistics.menu_links.yml b/core/modules/statistics/statistics.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..b409c23d514aadcc9ebc611125523855d08e86c4
--- /dev/null
+++ b/core/modules/statistics/statistics.menu_links.yml
@@ -0,0 +1,6 @@
+statistics.settings:
+  title: Statistics
+  description: 'Control details about what and how your site logs content statistics.'
+  route_name: statistics.settings
+  parent: system.admin_config_system
+  weight: -15
diff --git a/core/modules/statistics/statistics.module b/core/modules/statistics/statistics.module
index decbc028d46460ea8e28c515f2e3ee0b3e085712..13c96446b865a745d53781c64aea9b8a83b735c2 100644
--- a/core/modules/statistics/statistics.module
+++ b/core/modules/statistics/statistics.module
@@ -78,20 +78,6 @@ function statistics_node_links_alter(array &$node_links, NodeInterface $entity,
   }
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function statistics_menu_link_defaults() {
-  $links['statistics.settings'] = array(
-    'link_title' => 'Statistics',
-    'description' => 'Control details about what and how your site logs content statistics.',
-    'route_name' => 'statistics.settings',
-    'parent' => 'system.admin_config_system',
-    'weight' => -15,
-  );
-  return $links;
-}
-
 /**
  * Implements hook_cron().
  */
diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php
index 1dfc1091413993eb07c2ac47e88fe1484222c14f..bda7efe97b35c13c08cf36dac4533c2e5b7a8c08 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Menu/BreadcrumbTest.php
@@ -89,8 +89,8 @@ function testBreadCrumbs() {
     $this->assertBreadcrumb('admin/structure/menu/manage/tools', $trail);
 
     $mlid_node_add = \Drupal::entityQuery('menu_link')
-      ->condition('link_path', 'node/add')
-      ->condition('module', 'system')
+      ->condition('machine_name', 'node.add_page')
+      ->condition('module', 'node')
       ->execute();
     $mlid_node_add = reset($mlid_node_add);
     $trail += array(
diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/LinksTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/LinksTest.php
index 4d7a73505599d67055a62e02d7cb191cbde3431f..8148c7ab6330e0f9eb2426c879cbf940904f465d 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Menu/LinksTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Menu/LinksTest.php
@@ -182,7 +182,9 @@ function testMenuLinkReparenting($module = 'menu_test') {
   }
 
   /**
-   * Tests automatic reparenting of menu links derived from hook_menu_link_defaults.
+   * Tests automatic reparenting.
+   *
+   * Runs tests on menu links defined by the menu_link.static service.
    */
   function testMenuLinkRouterReparenting() {
     // Run all the standard parenting tests on menu links derived from
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 822dddde9a64680defb81780e1e1731b34898ba8..03f8ca1cbd3483c135c9dd5560057d71d1f3843a 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterTest.php
@@ -10,7 +10,7 @@
 use Drupal\simpletest\WebTestBase;
 
 /**
- * Tests menu router and hook_menu_link_defaults() functionality.
+ * Tests menu router and default menu link functionality.
  */
 class MenuRouterTest extends WebTestBase {
 
@@ -45,7 +45,7 @@ class MenuRouterTest extends WebTestBase {
   public static function getInfo() {
     return array(
       'name' => 'Menu router',
-      'description' => 'Tests menu router and hook_menu_link_defaults() functionality.',
+      'description' => 'Tests menu router and default menu links functionality.',
       'group' => 'Menu',
     );
   }
@@ -165,7 +165,7 @@ protected function doTestMenuLinkMaintain() {
   }
 
   /**
-   * Tests for menu_name parameter for hook_menu_link_defaults().
+   * Tests for menu_name parameter for default menu links.
    */
   protected function doTestMenuName() {
     $admin_user = $this->drupalCreateUser(array('administer site configuration'));
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index 8bdfdc2721255da120779d026f3a94ef75bbe6a7..4c95a00d29883bece5e277729c5fb7d7ffaacdb5 100644
--- a/core/modules/system/system.api.php
+++ b/core/modules/system/system.api.php
@@ -411,7 +411,10 @@ function hook_page_build(&$page) {
 }
 
 /**
- * Define links for menus.
+ * Alter links for menus.
+ *
+ * @param array $links
+ *   The link definitions to be altered.
  *
  * @return array
  *   An array of default menu links. Each link has a key that is the machine
@@ -428,7 +431,7 @@ function hook_page_build(&$page) {
  *
  *   The value corresponding to each machine name key is an associative array
  *   that may contain the following key-value pairs:
- *   - link_title: (required) The untranslated title of the menu item.
+ *   - title: (required) The untranslated title of the menu link.
  *   - description: The untranslated description of the link.
  *   - route_name: (optional) The route name to be used to build the path.
  *     Either a route_name or a link_path must be provided.
@@ -446,45 +449,13 @@ function hook_page_build(&$page) {
  *     this menu item (as a result of other properties), then the menu link is
  *     always expanded, equivalent to its 'always expanded' checkbox being set
  *     in the UI.
- *   - type: (optional) A bitmask of flags describing properties of the menu
- *     item. The following two bitmasks are provided as constants in menu.inc:
- *     - MENU_NORMAL_ITEM: Normal menu items show up in the menu tree and can be
- *       moved/hidden by the administrator.
- *     - MENU_SUGGESTED_ITEM: Modules may "suggest" menu items that the
- *       administrator may enable.
- *     If the "type" element is omitted, MENU_NORMAL_ITEM is assumed.
  *   - options: (optional) An array of options to be passed to l() when
  *     generating a link from this menu item.
- *
- * @see hook_menu_link_defaults_alter()
- */
-function hook_menu_link_defaults() {
-  $links['user.page'] = array(
-    'link_title' => 'My account',
-    'weight' => -10,
-    'route_name' => 'user.page',
-    'menu_name' => 'account',
-  );
-
-  $links['user.logout'] = array(
-    'link_title' => 'Log out',
-    'route_name' => 'user.logout',
-    'weight' => 10,
-    'menu_name' => 'account',
-  );
-
-  return $links;
-}
-
-/**
- * Alter links for menus.
- *
- * @see hook_menu_link_defaults()
  */
 function hook_menu_link_defaults_alter(&$links) {
   // Change the weight and title of the user.logout link.
   $links['user.logout']['weight'] = -10;
-  $links['user.logout']['link_title'] = t('Logout');
+  $links['user.logout']['title'] = 'Logout';
 }
 
 /**
diff --git a/core/modules/system/system.menu_links.yml b/core/modules/system/system.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..4d883a7421e35bb5861b32e9d7797ef5a73127dd
--- /dev/null
+++ b/core/modules/system/system.menu_links.yml
@@ -0,0 +1,149 @@
+system.admin:
+  title: Administration
+  route_name: system.admin
+  weight: 9
+  menu_name: admin
+system.admin_structure:
+  route_name: system.admin_structure
+  parent: system.admin
+  description: 'Administer blocks, content types, menus, etc.'
+  title: Structure
+  weight: -8
+system.themes_page:
+  route_name: system.themes_page
+  title: Appearance
+  description: 'Select and configure your themes.'
+  parent: system.admin
+  weight: -6
+system.modules_list:
+  title: Extend
+  description: 'Add and enable modules to extend site functionality.'
+  parent: system.admin
+  route_name: system.modules_list
+  weight: -2
+system.admin_config:
+  title: Configuration
+  parent: system.admin
+  description: 'Administer settings.'
+  route_name: system.admin_config
+  weight: 0
+system.admin_config_media:
+  route_name: system.admin_config_media
+  parent: system.admin_config
+  title: Media
+  weight: -10
+system.file_system_settings:
+  title: 'File system'
+  description: 'Tell Drupal where to store uploaded files and how they are accessed.'
+  parent: system.admin_config_media
+  route_name: system.file_system_settings
+system.image_toolkit_settings:
+  title: 'Image toolkit'
+  parent: system.admin_config_media
+  route_name: system.image_toolkit_settings
+  description: 'Choose which image toolkit to use if you have installed optional toolkits.'
+  weight: 20
+system.admin_config_services:
+  title: 'Web services'
+  parent: system.admin_config
+  route_name: system.admin_config_services
+system.rss_feeds_settings:
+  title: 'RSS publishing'
+  parent: system.admin_config_services
+  description: 'Configure the site description, the number of items per feed and whether feeds should be titles/teasers/full-text.'
+  route_name: system.rss_feeds_settings
+system.admin_config_development:
+  route_name: system.admin_config_development
+  parent: system.admin_config
+  title: Development
+  description: 'Development tools.'
+  weight: -10
+system.site_maintenance_mode:
+  title: 'Maintenance mode'
+  parent: system.admin_config_development
+  description: 'Take the site offline for maintenance or bring it back online.'
+  route_name: system.site_maintenance_mode
+  weight: -10
+system.performance_settings:
+  title: Performance
+  parent: system.admin_config_development
+  description: 'Enable or disable page caching for anonymous users and set CSS and JS bandwidth optimization options.'
+  route_name: system.performance_settings
+  weight: -20
+system.logging_settings:
+  title: 'Logging and errors'
+  parent: system.admin_config_development
+  description: 'Settings for logging and alerts modules. Various modules can route Drupal''s system events to different destinations, such as syslog, database, email, etc.'
+  route_name: system.logging_settings
+  weight: -15
+system.admin_config_regional:
+  route_name: system.admin_config_regional
+  title: 'Regional and language'
+  parent: system.admin_config
+  description: 'Regional settings, localization and translation.'
+  weight: -5
+system.regional_settings:
+  title: 'Regional settings'
+  parent: system.admin_config_regional
+  description: 'Settings for the site''s default time zone and country.'
+  route_name: system.regional_settings
+  weight: -20
+system.date_format_list:
+  title: 'Date and time formats'
+  parent: system.admin_config_regional
+  description: 'Configure display format strings for date and time.'
+  route_name: system.date_format_list
+  weight: -9
+system.admin_config_search:
+  title: 'Search and metadata'
+  route_name: system.admin_config_search
+  parent: system.admin_config
+  description: 'Local site search, metadata and SEO.'
+  weight: -10
+system.admin_config_system:
+  title: System
+  route_name: system.admin_config_system
+  parent: system.admin_config
+  description: 'General system related configuration.'
+  weight: -20
+system.site_information_settings:
+  title: 'Site information'
+  parent: system.admin_config_system
+  description: 'Change site name, e-mail address, slogan, default front page, and number of posts per page, error pages.'
+  route_name: system.site_information_settings
+  weight: -20
+system.cron_settings:
+  title: Cron
+  parent: system.admin_config_system
+  description: 'Manage automatic site maintenance tasks.'
+  route_name: system.cron_settings
+  weight: 20
+system.admin_config_ui:
+  title: 'User interface'
+  route_name: system.admin_config_ui
+  parent: system.admin_config
+  description: 'Tools that enhance the user interface.'
+  weight: -15
+system.admin_config_workflow:
+  title: Workflow
+  route_name: system.admin_config_workflow
+  parent: system.admin_config
+  description: 'Content workflow, editorial workflow tools.'
+  weight: 5
+system.admin_config_content:
+  title: 'Content authoring'
+  route_name: system.admin_config_content
+  parent: system.admin_config
+  description: 'Settings related to formatting and authoring content.'
+  weight: -15
+system.admin_reports:
+  title: Reports
+  route_name: system.admin_reports
+  parent: system.admin
+  description: 'View reports, updates, and errors.'
+  weight: 5
+system.status:
+  title: 'Status report'
+  parent: system.admin_reports
+  description: 'Get a status report about your site''s operation and any detected problems.'
+  route_name: system.status
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index b746d01673168160a94c24d298683d438b2900bd..7b35fca5ec8e0f06dbfba6812ad62ed63c0b8d6a 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -650,209 +650,6 @@ function system_element_info() {
   return $types;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function system_menu_link_defaults() {
-  $links['system.admin'] = array(
-    'link_title' => 'Administration',
-    'route_name' => 'system.admin',
-    'weight' => 9,
-    'menu_name' => 'admin',
-  );
-
-  // Menu items that are basically just menu blocks.
-  $links['system.admin_structure'] = array(
-    'route_name' => 'system.admin_structure',
-    'parent' => 'system.admin',
-    'description' => 'Administer blocks, content types, menus, etc.',
-    'link_title' => 'Structure',
-    'weight' => -8,
-  );
-  // Appearance.
-  $links['system.themes_page'] = array(
-    'route_name' => 'system.themes_page',
-    'link_title' => 'Appearance',
-    'description' => 'Select and configure your themes.',
-    'parent' => 'system.admin',
-    'weight' => -6,
-  );
-  // Modules.
-  $links['system.modules_list'] = array(
-    'link_title' => 'Extend',
-    'description' => 'Add and enable modules to extend site functionality.',
-    'parent' => 'system.admin',
-    'route_name' => 'system.modules_list',
-    'weight' => -2,
-  );
-  // Configuration.
-  $links['system.admin_config'] = array(
-    'link_title' => 'Configuration',
-    'parent' => 'system.admin',
-    'description' => 'Administer settings.',
-    'route_name' => 'system.admin_config',
-    'weight' => 0,
-  );
-
-  // Media settings.
-  $links['system.admin_config_media'] = array(
-    'route_name' => 'system.admin_config_media',
-    'parent' => 'system.admin_config',
-    'link_title' => 'Media',
-    'weight' => -10,
-  );
-  $links['system.file_system_settings'] = array(
-    'link_title' => 'File system',
-    'description' => 'Tell Drupal where to store uploaded files and how they are accessed.',
-    'parent' => 'system.admin_config_media',
-    'route_name' => 'system.file_system_settings',
-  );
-  $links['system.image_toolkit_settings'] = array(
-    'link_title' => 'Image toolkit',
-    'parent' => 'system.admin_config_media',
-    'route_name' => 'system.image_toolkit_settings',
-    'description' => 'Choose which image toolkit to use if you have installed optional toolkits.',
-    'weight' => 20,
-  );
-
-  // Service settings.
-  $links['system.admin_config_services'] = array(
-    'link_title' => 'Web services',
-    'parent' => 'system.admin_config',
-    'route_name' => 'system.admin_config_services',
-  );
-  $links['system.rss_feeds_settings'] = array(
-    'link_title' => 'RSS publishing',
-    'parent' => 'system.admin_config_services',
-    'description' => 'Configure the site description, the number of items per feed and whether feeds should be titles/teasers/full-text.',
-    'route_name' => 'system.rss_feeds_settings',
-  );
-
-  // Development settings.
-  $links['system.admin_config_development'] = array(
-    'route_name' => 'system.admin_config_development',
-    'parent' => 'system.admin_config',
-    'link_title' => 'Development',
-    'description' => 'Development tools.',
-    'weight' => -10,
-  );
-  $links['system.site_maintenance_mode'] = array(
-    'link_title' => 'Maintenance mode',
-    'parent' => 'system.admin_config_development',
-    'description' => 'Take the site offline for maintenance or bring it back online.',
-    'route_name' => 'system.site_maintenance_mode',
-    'weight' => -10,
-  );
-  $links['system.performance_settings'] = array(
-    'link_title' => 'Performance',
-    'parent' => 'system.admin_config_development',
-    'description' => 'Enable or disable page caching for anonymous users and set CSS and JS bandwidth optimization options.',
-    'route_name' => 'system.performance_settings',
-    'weight' => -20,
-  );
-  $links['system.logging_settings'] = array(
-    'link_title' => 'Logging and errors',
-    'parent' => 'system.admin_config_development',
-    'description' => "Settings for logging and alerts modules. Various modules can route Drupal's system events to different destinations, such as syslog, database, email, etc.",
-    'route_name' => 'system.logging_settings',
-    'weight' => -15,
-  );
-
-  // Regional and date settings.
-  $links['system.admin_config_regional'] = array(
-    'route_name' => 'system.admin_config_regional',
-    'link_title' => 'Regional and language',
-    'parent' => 'system.admin_config',
-    'description' => 'Regional settings, localization and translation.',
-    'weight' => -5,
-  );
-  $links['system.regional_settings'] = array(
-    'link_title' => 'Regional settings',
-    'parent' => 'system.admin_config_regional',
-    'description' => "Settings for the site's default time zone and country.",
-    'route_name' => 'system.regional_settings',
-    'weight' => -20,
-  );
-  $links['system.date_format_list'] = array(
-    'link_title' => 'Date and time formats',
-    'parent' => 'system.admin_config_regional',
-    'description' => 'Configure display format strings for date and time.',
-    'route_name' => 'system.date_format_list',
-    'weight' => -9,
-  );
-
-  // Search settings.
-  $links['system.admin_config_search'] = array(
-    'link_title' => 'Search and metadata',
-    'route_name' => 'system.admin_config_search',
-    'parent' => 'system.admin_config',
-    'description' => 'Local site search, metadata and SEO.',
-    'weight' => -10,
-  );
-
-  // System settings.
-  $links['system.admin_config_system'] = array(
-    'link_title' => 'System',
-    'route_name' => 'system.admin_config_system',
-    'parent' => 'system.admin_config',
-    'description' => 'General system related configuration.',
-    'weight' => -20,
-  );
-  $links['system.site_information_settings'] = array(
-    'link_title' => 'Site information',
-    'parent' => 'system.admin_config_system',
-    'description' => 'Change site name, e-mail address, slogan, default front page, and number of posts per page, error pages.',
-    'route_name' => 'system.site_information_settings',
-    'weight' => -20,
-  );
-  $links['system.cron_settings'] = array(
-    'link_title' => 'Cron',
-    'parent' => 'system.admin_config_system',
-    'description' => 'Manage automatic site maintenance tasks.',
-    'route_name' => 'system.cron_settings',
-    'weight' => 20,
-  );
-  // Additional categories
-  $links['system.admin_config_ui'] = array(
-    'link_title' => 'User interface',
-    'route_name' => 'system.admin_config_ui',
-    'parent' => 'system.admin_config',
-    'description' => 'Tools that enhance the user interface.',
-    'weight' => -15,
-  );
-  $links['system.admin_config_workflow'] = array(
-    'link_title' => 'Workflow',
-    'route_name' => 'system.admin_config_workflow',
-    'parent' => 'system.admin_config',
-    'description' => 'Content workflow, editorial workflow tools.',
-    'weight' => 5,
-  );
-  $links['system.admin_config_content'] = array(
-    'link_title' => 'Content authoring',
-    'route_name' => 'system.admin_config_content',
-    'parent' => 'system.admin_config',
-    'description' => 'Settings related to formatting and authoring content.',
-    'weight' => -15,
-  );
-
-  // Reports.
-  $links['system.admin_reports'] = array(
-    'link_title' => 'Reports',
-    'route_name' => 'system.admin_reports',
-    'parent' => 'system.admin',
-    'description' => 'View reports, updates, and errors.',
-    'weight' => 5,
-  );
-  $links['system.status'] = array(
-    'link_title' => 'Status report',
-    'parent' => 'system.admin_reports',
-    'description' => "Get a status report about your site's operation and any detected problems.",
-    'route_name' => 'system.status',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_theme_suggestions_HOOK().
  */
@@ -1672,15 +1469,15 @@ function system_admin_compact_mode() {
 /**
  * Generate a list of tasks offered by a specified module.
  *
- * @param $module
+ * @param string $module
  *   Module name.
- * @param $info
+ * @param array $info
  *   The module's information, as provided by system_get_info().
  *
- * @return
+ * @return array
  *   An array of task links.
  */
-function system_get_module_admin_tasks($module, $info) {
+function system_get_module_admin_tasks($module, array $info) {
   $links = &drupal_static(__FUNCTION__);
 
   if (!isset($links)) {
@@ -1696,37 +1493,39 @@ function system_get_module_admin_tasks($module, $info) {
 
   $admin_tasks = array();
   $titles = array();
-  if ($menu = \Drupal::moduleHandler()->invoke($module, 'menu_link_defaults')) {
-    foreach ($menu as $machine_name => $item) {
-      if (isset($links[$machine_name])) {
-        $task = $links[$machine_name];
-        // The link description, either derived from 'description' in
-        // hook_menu() or customized via menu module is used as title attribute.
-        if (!empty($task['localized_options']['attributes']['title'])) {
-          $task['description'] = $task['localized_options']['attributes']['title'];
-          unset($task['localized_options']['attributes']['title']);
-        }
+  foreach ($links as $item) {
+    if ($item['module'] != $module) {
+      continue;
+    }
+    $machine_name = $item['machine_name'];
+    if (isset($links[$machine_name])) {
+      $task = $links[$machine_name];
+      // The link description, either derived from 'description' in the default
+      // menu link or customized via menu module is used as title attribute.
+      if (!empty($task['localized_options']['attributes']['title'])) {
+        $task['description'] = $task['localized_options']['attributes']['title'];
+        unset($task['localized_options']['attributes']['title']);
+      }
 
-        // Check the admin tasks for duplicate names. If one is found,
-        // append the parent menu item's title to differentiate.
-        $duplicate_path = array_search($task['title'], $titles);
-        if ($duplicate_path !== FALSE) {
-          if ($parent = menu_link_load($task['plid'])) {
-            // Append the parent item's title to this task's title.
-            $task['title'] = t('@original_title (@parent_title)', array('@original_title' => $task['title'], '@parent_title' => $parent['title']));
-          }
-          if ($parent = menu_link_load($admin_tasks[$duplicate_path]['plid'])) {
-            // Append the parent item's title to the duplicated task's title.
-            // We use $links[$duplicate_path] in case there are triplicates.
-            $admin_tasks[$duplicate_path]['title'] = t('@original_title (@parent_title)', array('@original_title' => $links[$duplicate_path]['title'], '@parent_title' => $parent['title']));
-          }
+      // Check the admin tasks for duplicate names. If one is found,
+      // append the parent menu item's title to differentiate.
+      $duplicate_path = array_search($task['title'], $titles);
+      if ($duplicate_path !== FALSE) {
+        if ($parent = menu_link_load($task['plid'])) {
+          // Append the parent item's title to this task's title.
+          $task['title'] = t('@original_title (@parent_title)', array('@original_title' => $task['title'], '@parent_title' => $parent['title']));
         }
-        else {
-          $titles[$machine_name] = $task['title'];
+        if ($parent = menu_link_load($admin_tasks[$duplicate_path]['plid'])) {
+          // Append the parent item's title to the duplicated task's title.
+          // We use $links[$duplicate_path] in case there are triplicates.
+          $admin_tasks[$duplicate_path]['title'] = t('@original_title (@parent_title)', array('@original_title' => $links[$duplicate_path]['title'], '@parent_title' => $parent['title']));
         }
-
-        $admin_tasks[$machine_name] = $task;
       }
+      else {
+        $titles[$machine_name] = $task['title'];
+      }
+
+      $admin_tasks[$machine_name] = $task;
     }
   }
 
diff --git a/core/modules/system/tests/modules/menu_test/menu_test.menu_links.yml b/core/modules/system/tests/modules/menu_test/menu_test.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..599dcf88d6d285af70ba7691637138da1afcc80b
--- /dev/null
+++ b/core/modules/system/tests/modules/menu_test/menu_test.menu_links.yml
@@ -0,0 +1,90 @@
+# The name of the menu changes during the course of the test. Using a $_GET.
+menu_test.menu_name_test:
+  title: 'Test menu_name router item'
+  route_name: menu_test.menu_name_test
+  menu_name: original
+# This item uses SystemController::systemAdminMenuBlockPage() to list child
+# items.
+menu_test.menu_callback_description:
+  title: 'Menu item title'
+  description: 'Menu item description parent'
+  route_name: menu_test.callback_description
+# This item tests the description key.
+menu_test.menu_callback_description.description-plain:
+  title: 'Menu item with a regular description'
+  description: 'Menu item description text'
+  route_name: menu_test.callback_description_plain
+  parent: menu_test.menu_callback_description
+menu_test.menu_no_title_callback:
+  title: 'A title with @placeholder'
+  route_name: menu_test.menu_no_title_callback
+# Hierarchical tests.
+menu_test.hierarchy_parent:
+  title: 'Parent menu router'
+  route_name: menu_test.hierarchy_parent
+menu_test.hierarchy_parent.child:
+  title: 'Child menu router'
+  route_name: menu_test.hierarchy_parent_child
+  parent: menu_test.hierarchy_parent
+menu_test.hierarchy_parent.child2.child:
+  title: 'Unattached subchild router'
+  route_name: menu_test.hierarchy_parent_child2
+  parent: menu_test.hierarchy_parent.child
+# Path containing "exotic" characters.
+menu_test.exotic_path:
+  title: '"Exotic" path'
+  route_name: menu_test.exotic_path
+  # "Special" ASCII characters. Characters that look like a percent-escaped
+  # string. Characters from various non-ASCII alphabets.
+  route_parameters: { exotic: ' -._~!$''"()*@[]?&+%#,;=:%23%25%26%2B%2F%3Féøïвβ中國書۞' }
+# Hidden tests; base parents.
+# Same structure as in Menu and Block modules. Since those structures can
+# change, we need to simulate our own in here.
+menu_test:
+  title: 'Menu test root'
+  route_name: menu_test.menu_test
+# Hidden tests; one dynamic argument.
+menu_test.hidden:
+  title: 'Hidden test root'
+  route_name: menu_test.hidden
+  parent: menu_test
+menu_test.hidden.menu:
+  title: Menus
+  route_name: menu_test.hidden_menu
+  parent: menu_test.hidden
+# Hidden tests; two dynamic arguments.
+menu_test.hidden.block:
+  title: Blocks
+  route_name: menu_test.hidden_block
+  parent: menu_test.hidden
+# Menu trail tests.
+# @see MenuTrailTestCase
+menu_test.menu-trail:
+  title: 'Menu trail - Case 1'
+  route_name: menu_test.menu_trail
+  parent: menu_test
+menu_test.admin.config.development.menu-trail:
+  title: 'Menu trail - Case 2'
+  description: 'Tests menu_tree_set_path()'
+  route_name: menu_test.menu_trail_admin
+  parent: system.admin_config_development
+menu_test.custom-403-page:
+  title: 'Custom 403 page'
+  route_name: menu_test.custom_403
+  parent: menu_test
+menu_test.custom-404-page:
+  title: 'Custom 404 page'
+  route_name: menu_test.custom_404
+  parent: menu_test
+menu_test.menu-title-test.case1:
+  title: 'Example title - Case 1'
+  route_name: menu_test.title_test_case1
+menu_test.menu-title-test.case2:
+  title: 'Example title'
+  route_name: menu_test.title_test_case2
+menu_test.menu-title-test.case3:
+  title: 'Bike sheds full of blue smurfs'
+  route_name: menu_test.title_test_case3
+menu_test.context:
+  title: ''
+  route_name: menu_test.context
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 3f34a1eb8ec6002f743e33e3f3119af5b5a18b3a..9e6f351f34760c79db2a834fdb719143c8eabc5b 100644
--- a/core/modules/system/tests/modules/menu_test/menu_test.module
+++ b/core/modules/system/tests/modules/menu_test/menu_test.module
@@ -8,134 +8,15 @@
 use Drupal\menu_link\Entity\MenuLink;
 
 /**
- * Implements hook_menu_link_defaults().
+ * Implements hook_menu_link_defaults_alter().
  *
  * Many of the machine names here are slightly different from the route name.
  * Since the machine name is arbitrary, this helps ensure that core does not
  * add mistaken assumptions about the correlation.
  */
-function menu_test_menu_link_defaults() {
-  // The name of the menu changes during the course of the test. Using a $_GET.
-  $links['menu_test.menu_name_test'] = array(
-    'link_title' => 'Test menu_name router item',
-    'route_name' => 'menu_test.menu_name_test',
-    'menu_name' => menu_test_menu_name(),
-  );
-  // This item uses SystemController::systemAdminMenuBlockPage() to list child
-  // items.
-  $links['menu_test.menu_callback_description'] = array(
-    'link_title' => 'Menu item title',
-    'description' => 'Menu item description parent',
-    'route_name' => 'menu_test.callback_description',
-  );
-  // This item tests the description key.
-  $links['menu_test.menu_callback_description.description-plain'] = array(
-    'link_title' => 'Menu item with a regular description',
-    'description' => 'Menu item description text',
-    'route_name' => 'menu_test.callback_description_plain',
-    'parent' => 'menu_test.menu_callback_description',
-  );
-
-  $links['menu_test.menu_no_title_callback'] = array(
-    'link_title' => 'A title with @placeholder',
-    'route_name' => 'menu_test.menu_no_title_callback',
-  );
-
-  // Hierarchical tests.
-  $links['menu_test.hierarchy_parent'] = array(
-    'link_title' => 'Parent menu router',
-    'route_name' => 'menu_test.hierarchy_parent',
-  );
-  $links['menu_test.hierarchy_parent.child'] = array(
-    'link_title' => 'Child menu router',
-    'route_name' => 'menu_test.hierarchy_parent_child',
-    'parent' => 'menu_test.hierarchy_parent',
-  );
-  $links['menu_test.hierarchy_parent.child2.child'] = array(
-    'link_title' => 'Unattached subchild router',
-    'route_name' => 'menu_test.hierarchy_parent_child2',
-    'parent' => 'menu_test.hierarchy_parent.child',
-  );
-  // Path containing "exotic" characters.
-  $exotic = " -._~!$'\"()*@[]?&+%#,;=:" . // "Special" ASCII characters.
-    "%23%25%26%2B%2F%3F" . // Characters that look like a percent-escaped string.
-    "éøïвβ中國書۞"; // Characters from various non-ASCII alphabets.
-  $links['menu_test.exotic_path'] = array(
-    'link_title' => '"Exotic" path',
-    'route_name' => 'menu_test.exotic_path',
-    'route_parameters' => array('exotic' => $exotic),
-  );
-
-  // Hidden tests; base parents.
-  // Same structure as in Menu and Block modules. Since those structures can
-  // change, we need to simulate our own in here.
-  $links['menu_test'] = array(
-    'link_title' => 'Menu test root',
-    'route_name' => 'menu_test.menu_test',
-  );
-  $links['menu_test.hidden'] = array(
-    'link_title' => 'Hidden test root',
-    'route_name' => 'menu_test.hidden',
-    'parent' => 'menu_test',
-  );
-
-  // Hidden tests; one dynamic argument.
-  $links['menu_test.hidden.menu'] = array(
-    'link_title' => 'Menus',
-    'route_name' => 'menu_test.hidden_menu',
-    'parent' => 'menu_test.hidden',
-  );
-
-  // Hidden tests; two dynamic arguments.
-  $links['menu_test.hidden.block'] = array(
-    'link_title' => 'Blocks',
-    'route_name' => 'menu_test.hidden_block',
-    'parent' => 'menu_test.hidden',
-  );
-
-  // Menu trail tests.
-  // @see MenuTrailTestCase
-  $links['menu_test.menu-trail'] = array(
-    'link_title' => 'Menu trail - Case 1',
-    'route_name' => 'menu_test.menu_trail',
-    'parent' => 'menu_test',
-  );
-  $links['menu_test.admin.config.development.menu-trail'] = array(
-    'link_title' => 'Menu trail - Case 2',
-    'description' => 'Tests menu_tree_set_path()',
-    'route_name' => 'menu_test.menu_trail_admin',
-    'parent' => 'system.admin_config_development',
-  );
-  $links['menu_test.custom-403-page'] = array(
-    'link_title' => 'Custom 403 page',
-    'route_name' => 'menu_test.custom_403',
-    'parent' => 'menu_test',
-  );
-  $links['menu_test.custom-404-page'] = array(
-    'link_title' => 'Custom 404 page',
-    'route_name' => 'menu_test.custom_404',
-    'parent' => 'menu_test',
-  );
-  // Test the access key.
-  $links['menu_test.menu-title-test.case1'] = array(
-    'link_title' => 'Example title - Case 1',
-    'route_name' => 'menu_test.title_test_case1',
-  );
-  $links['menu_test.menu-title-test.case2'] = array(
-    'link_title' => 'Example title',
-    'route_name' => 'menu_test.title_test_case2',
-  );
-  $links['menu_test.menu-title-test.case3'] = array(
-    // Title gets completely ignored. Good thing, too.
-    'link_title' => 'Bike sheds full of blue smurfs',
-    'route_name' => 'menu_test.title_test_case3',
-  );
-  $links['menu_test.context'] = array(
-    'link_title' => \Drupal::config('menu_test.menu_item')->get('title'),
-    'route_name' => 'menu_test.context',
-  );
-
-  return $links;
+function menu_test_menu_link_defaults_alter(&$links) {
+  $links['menu_test.menu_name_test']['menu_name'] = menu_test_menu_name();
+  $links['menu_test.context']['title'] = \Drupal::config('menu_test.menu_item')->get('title');
 }
 
 /**
diff --git a/core/modules/taxonomy/taxonomy.menu_links.yml b/core/modules/taxonomy/taxonomy.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..77bb6faa2ae7fda1d29b681fe743487503118a93
--- /dev/null
+++ b/core/modules/taxonomy/taxonomy.menu_links.yml
@@ -0,0 +1,5 @@
+taxonomy.vocabulary_list:
+  title: Taxonomy
+  parent: system.admin_structure
+  description: 'Manage tagging, categorization, and classification of your content.'
+  route_name: taxonomy.vocabulary_list
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index 20060cbab8028f3931a5c47491dd575fa2347fae..f7dceca326fd3fa70410089ff770b9bd5c699c7b 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -231,20 +231,6 @@ function taxonomy_theme() {
   );
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function taxonomy_menu_link_defaults() {
-  $links['taxonomy.vocabulary_list'] = array(
-    'link_title' => 'Taxonomy',
-    'parent' => 'system.admin_structure',
-    'description' => 'Manage tagging, categorization, and classification of your content.',
-    'route_name' => 'taxonomy.vocabulary_list',
-  );
-
-  return $links;
-}
-
 /**
  * Checks and updates the hierarchy flag of a vocabulary.
  *
diff --git a/core/modules/tour/tests/tour_test/tour_test.module b/core/modules/tour/tests/tour_test/tour_test.module
index 850bf9837524f3d9c6917f2e938a340190b27989..69a9a5413e2f845a5da874de73ba1228d8ed0a6a 100644
--- a/core/modules/tour/tests/tour_test/tour_test.module
+++ b/core/modules/tour/tests/tour_test/tour_test.module
@@ -7,22 +7,6 @@
 
 use Drupal\Core\Entity\EntityInterface;
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function tour_test_menu_link_defaults() {
-  $links['tour_test.1'] = array(
-    'route_name' => 'tour_test.1',
-    'link_title' => 'Tour test 1'
-  );
-  $links['tour_test.2'] = array(
-    'route_name' => 'tour_test.2',
-    'link_title' => 'Tour test 2'
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_ENTITY_TYPE_load() for tour.
  */
diff --git a/core/modules/tracker/tracker.menu_links.yml b/core/modules/tracker/tracker.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..fe12bc4794634c516c0b397059d8e8e9aaf1c707
--- /dev/null
+++ b/core/modules/tracker/tracker.menu_links.yml
@@ -0,0 +1,3 @@
+tracker.page:
+  title: 'Recent content'
+  route_name: tracker.page
diff --git a/core/modules/tracker/tracker.module b/core/modules/tracker/tracker.module
index e1a749a7355ba9e2137f34458dc9e8bbceee82a6..96aecbf01f7c27b2001f15a8760113a5915b1fee 100644
--- a/core/modules/tracker/tracker.module
+++ b/core/modules/tracker/tracker.module
@@ -31,18 +31,6 @@ function tracker_help($path, $arg) {
   }
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function tracker_menu_link_defaults() {
-  $links['tracker.page'] = array(
-    'link_title' => 'Recent content',
-    'route_name' => 'tracker.page',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_cron().
  *
diff --git a/core/modules/update/update.menu_links.yml b/core/modules/update/update.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..6b599df96bbbf7ea44a456ed6624518c6ff147bb
--- /dev/null
+++ b/core/modules/update/update.menu_links.yml
@@ -0,0 +1,6 @@
+update.status:
+  title: 'Available updates'
+  description: 'Get a status report about available updates for your installed modules and themes.'
+  route_name: update.status
+  parent: system.admin_reports
+  weight: -50
diff --git a/core/modules/update/update.module b/core/modules/update/update.module
index 2097a76b63891f3675ef2a3dfd125c6a1a8d57c1..6297ff40742ac5ecd551e9d7ecc046ef2829eee2 100644
--- a/core/modules/update/update.module
+++ b/core/modules/update/update.module
@@ -146,21 +146,6 @@ function update_page_build() {
   }
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function update_menu_link_defaults() {
-  $links['update.status'] = array(
-    'link_title' => 'Available updates',
-    'description' => 'Get a status report about available updates for your installed modules and themes.',
-    'route_name' => 'update.status',
-    'parent' => 'system.admin_reports',
-    'weight' => -50,
-  );
-
-  return $links;
-}
-
 /**
  * Access callback: Resolves if the current user can access updater menu items.
  *
diff --git a/core/modules/user/user.menu_links.yml b/core/modules/user/user.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..34554859c323bbc02cfa14b0db5ff3d232e379cb
--- /dev/null
+++ b/core/modules/user/user.menu_links.yml
@@ -0,0 +1,29 @@
+user.page:
+  title: 'My account'
+  weight: -10
+  route_name: user.page
+  menu_name: account
+user.logout:
+  title: 'Log out'
+  route_name: user.logout
+  weight: 10
+  menu_name: account
+user.admin_account:
+  title: People
+  route_name: user.admin_account
+  description: 'Manage user accounts, roles, and permissions.'
+  parent: system.admin
+  weight: 4
+user.admin_index:
+  title: People
+  route_name: user.admin_index
+  parent: system.admin_config
+  description: 'Configure user accounts.'
+  position: left
+  weight: -20
+user.account_settings:
+  title: 'Account settings'
+  parent: user.admin_index
+  description: 'Configure default behavior of users, including registration requirements, e-mails, and fields.'
+  weight: -10
+  route_name: user.account_settings
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index 238f83707280501ac89fbc9b1644a4dafbffd7a5..0ed9aa1d37ec323569ad06dd75a0dbafed2f6fee 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -690,55 +690,6 @@ function theme_username($variables) {
   return $output;
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function user_menu_link_defaults() {
-  // Registration and login pages.
-  $links['user.page'] = array(
-    'link_title' => 'My account',
-    'weight' => -10,
-    'route_name' => 'user.page',
-    'menu_name' => 'account',
-  );
-
-  $links['user.logout'] = array(
-    'link_title' => 'Log out',
-    'route_name' => 'user.logout',
-    'weight' => 10,
-    'menu_name' => 'account',
-  );
-
-  // User listing pages.
-  $links['user.admin_account'] = array(
-    'link_title' => 'People',
-    'route_name' => 'user.admin_account',
-    'description' => 'Manage user accounts, roles, and permissions.',
-    'parent' => 'system.admin',
-    'weight' => 4,
-  );
-
-  // Administration pages.
-  $links['user.admin_index'] = array(
-   'link_title' => 'People',
-   'route_name' => 'user.admin_index',
-   'parent' => 'system.admin_config',
-   'description' => 'Configure user accounts.',
-   'position' => 'left',
-   'weight' => -20,
-  );
-
-  $links['user.account_settings'] = array(
-    'link_title' => 'Account settings',
-    'parent' => 'user.admin_index',
-    'description' => 'Configure default behavior of users, including registration requirements, e-mails, and fields.',
-    'weight' => -10,
-    'route_name' => 'user.account_settings',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_menu_link_presave().
  */
@@ -747,7 +698,7 @@ function user_menu_link_presave(MenuLink $menu_link) {
   // for authenticated users. Authenticated users should see "My account", but
   // anonymous users should not see it at all. Therefore, invoke
   // user_menu_link_load() to conditionally hide the link.
-  if ($menu_link->machine_name == 'user.page' && $menu_link->module == 'system') {
+  if ($menu_link->machine_name == 'user.page') {
     $menu_link->options['alter'] = TRUE;
   }
 }
@@ -758,7 +709,7 @@ function user_menu_link_presave(MenuLink $menu_link) {
 function user_menu_breadcrumb_alter(&$active_trail, $item) {
   // Remove "My account" from the breadcrumb when $item is descendant-or-self
   // of system path user/%.
-  if (isset($active_trail[1]['module']) && $active_trail[1]['module'] == 'system' && $active_trail[1]['link_path'] == 'user' && strpos($item['path'], 'user/%') === 0) {
+  if (isset($active_trail[1]['module']) && $active_trail[1]['machine_name'] == 'user.page' && strpos($item['path'], 'user/%') === 0) {
     array_splice($active_trail, 1, 1);
   }
 }
@@ -768,7 +719,7 @@ function user_menu_breadcrumb_alter(&$active_trail, $item) {
  */
 function user_translated_menu_link_alter(MenuLink &$menu_link) {
   // Hide the "User account" link for anonymous users.
-  if ($menu_link->machine_name == 'user.page' && $menu_link->module == 'system' && \Drupal::currentUser()->isAnonymous()) {
+  if ($menu_link->machine_name == 'user.page' && \Drupal::currentUser()->isAnonymous()) {
     $menu_link->hidden = 1;
   }
 }
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php
index ac2f70695b14bdd92329fc15f4e8fea80344388c..f4e67e21d2076d877bb72f518f2edda4ac3a7cda 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/PathPluginBase.php
@@ -312,7 +312,7 @@ public function executeHookMenuLinkDefaults(array &$existing_links) {
           'load arguments'  => array($this->view->storage->id(), $this->display['id'], '%index'),
           'machine_name' => $menu_link_id,
         );
-        $links[$menu_link_id]['link_title'] = $menu['title'];
+        $links[$menu_link_id]['title'] = $menu['title'];
         $links[$menu_link_id]['description'] = $menu['description'];
 
         if (isset($menu['weight'])) {
diff --git a/core/modules/views_ui/views_ui.menu_links.yml b/core/modules/views_ui/views_ui.menu_links.yml
new file mode 100644
index 0000000000000000000000000000000000000000..e799469bb9036f1f91edf6bb172dad14ad7ebe96
--- /dev/null
+++ b/core/modules/views_ui/views_ui.menu_links.yml
@@ -0,0 +1,10 @@
+views_ui.list:
+  title: Views
+  parent: system.admin_structure
+  description: 'Manage customized lists of content.'
+  route_name: views_ui.list
+views_ui.reports_plugins:
+  title: 'Views plugins'
+  parent: system.admin_reports
+  description: 'Overview of plugins used in all views.'
+  route_name: views_ui.reports_plugins
diff --git a/core/modules/views_ui/views_ui.module b/core/modules/views_ui/views_ui.module
index fa381d62dcb239f5067f763556aeda5d51ac84a5..951321cb9af16da0a21d68b986286545bec32c8c 100644
--- a/core/modules/views_ui/views_ui.module
+++ b/core/modules/views_ui/views_ui.module
@@ -38,31 +38,6 @@ function views_ui_help($path, $arg) {
   }
 }
 
-/**
- * Implements hook_menu_link_defaults().
- */
-function views_ui_menu_link_defaults() {
-  $links = array();
-
-  // Top-level Views module pages (not tied to a particular View).
-  $links['views_ui.list'] = array(
-    'link_title' => 'Views',
-    'parent' => 'system.admin_structure',
-    'description' => 'Manage customized lists of content.',
-    'route_name' => 'views_ui.list',
-  );
-
-  // A page in the Reports section to show usage of plugins in all views.
-  $links['views_ui.reports_plugins'] = array(
-    'link_title' => 'Views plugins',
-    'parent' => 'system.admin_reports',
-    'description' => 'Overview of plugins used in all views.',
-    'route_name' => 'views_ui.reports_plugins',
-  );
-
-  return $links;
-}
-
 /**
  * Implements hook_entity_type_build().
  */
diff --git a/core/profiles/standard/standard.install b/core/profiles/standard/standard.install
index bec11078785e4e8f9131a13142f4be39de0a30bb..60c6f7204159b79d5a0e16fbe82ae7755192356a 100644
--- a/core/profiles/standard/standard.install
+++ b/core/profiles/standard/standard.install
@@ -56,7 +56,7 @@ function standard_install() {
   $menu_link->save();
 
   // Enable the Contact link in the footer menu.
-  menu_link_maintain('system', 'enable', 'contact');
+  menu_link_maintain('contact', 'enable', 'contact');
   user_role_grant_permissions(DRUPAL_ANONYMOUS_RID, array('access site-wide contact form'));
   user_role_grant_permissions(DRUPAL_AUTHENTICATED_RID, array('access site-wide contact form'));