diff --git a/core/includes/menu.inc b/core/includes/menu.inc
index 987d9d0..2962411 100644
--- a/core/includes/menu.inc
+++ b/core/includes/menu.inc
@@ -5,12 +5,14 @@
  * API for the Drupal menu system.
  */
 
+use Drupal\Component\Discovery\YamlDiscovery;
 use Drupal\Component\Utility\String;
 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;
+use Symfony\Component\Yaml\Yaml;
 
 /**
  * @defgroup menu Menu and routing system
@@ -1005,7 +1007,16 @@ function _menu_link_save_recursive($controller, $machine_name, &$children, &$lin
  */
 function menu_link_get_defaults() {
   $module_handler = \Drupal::moduleHandler();
-  $all_links = $module_handler->invokeAll('menu_link_defaults');
+
+  $discovery = new YamlDiscovery('menu_links', $module_handler->getModuleDirectories());
+  $all_links = array();
+  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]['module'] = $module;
+    }
+  }
+
   // Fill in the machine name from the array key.
   foreach ($all_links as $machine_name => &$link) {
     $link['machine_name'] = $machine_name;
diff --git a/core/modules/action/action.menu_links.yml b/core/modules/action/action.menu_links.yml
new file mode 100644
index 0000000..42c90d9
--- /dev/null
+++ b/core/modules/action/action.menu_links.yml
@@ -0,0 +1,5 @@
+action.admin:
+  link_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 a88e0ed..3712b14 100644
--- a/core/modules/action/action.module
+++ b/core/modules/action/action.module
@@ -45,20 +45,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().
  */
 function action_entity_type_build(array &$entity_types) {
diff --git a/core/modules/aggregator/aggregator.menu_links.yml b/core/modules/aggregator/aggregator.menu_links.yml
new file mode 100644
index 0000000..fe8edc5
--- /dev/null
+++ b/core/modules/aggregator/aggregator.menu_links.yml
@@ -0,0 +1,16 @@
+aggregator.admin_overview:
+  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
+aggregator.page_last:
+  link_title: 'Feed aggregator'
+  weight: 5
+  route_name: aggregator.page_last
+aggregator.sources:
+  link_title: Sources
+  route_name: aggregator.sources
+aggregator.feed_add:
+  link_title: 'Add feed'
+  route_name: aggregator.feed_add
diff --git a/core/modules/aggregator/aggregator.module b/core/modules/aggregator/aggregator.module
index 0bbbf28..e8e7b3e 100644
--- a/core/modules/aggregator/aggregator.module
+++ b/core/modules/aggregator/aggregator.module
@@ -88,35 +88,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().
  */
 function aggregator_permission() {
diff --git a/core/modules/ban/ban.menu_links.yml b/core/modules/ban/ban.menu_links.yml
new file mode 100644
index 0000000..e8410b9
--- /dev/null
+++ b/core/modules/ban/ban.menu_links.yml
@@ -0,0 +1,6 @@
+ban.admin_page:
+  link_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 03201c9..5e6ba65 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 0000000..6f56c9e
--- /dev/null
+++ b/core/modules/block/block.menu_links.yml
@@ -0,0 +1,5 @@
+block.admin_display:
+  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
diff --git a/core/modules/block/block.module b/core/modules/block/block.module
index c217c9f..73514c2 100644
--- a/core/modules/block/block.module
+++ b/core/modules/block/block.module
@@ -94,20 +94,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().
  *
  * Renders blocks into their regions.
diff --git a/core/modules/book/book.menu_links.yml b/core/modules/book/book.menu_links.yml
new file mode 100644
index 0000000..34596f6
--- /dev/null
+++ b/core/modules/book/book.menu_links.yml
@@ -0,0 +1,9 @@
+book.admin:
+  link_title: Books
+  description: 'Manage your site''s book outlines.'
+  parent: system.admin_structure
+  route_name: book.admin
+book.render:
+  link_title: Books
+  route_name: book.render
+  hidden: 1
diff --git a/core/modules/book/book.module b/core/modules/book/book.module
index fca7d44..fca17ab 100644
--- a/core/modules/book/book.module
+++ b/core/modules/book/book.module
@@ -148,26 +148,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.
  *
  * @todo Remove in favor of BookManager Service. http://drupal.org/node/1963894
diff --git a/core/modules/comment/comment.menu_links.yml b/core/modules/comment/comment.menu_links.yml
new file mode 100644
index 0000000..456da78
--- /dev/null
+++ b/core/modules/comment/comment.menu_links.yml
@@ -0,0 +1,10 @@
+comment.admin:
+  link_title: Comments
+  route_name: comment.admin
+  parent: system.admin
+  description: 'List and edit site comments and the comment approval queue.'
+comment.bundle_list:
+  link_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 5445fb9..3b41ac5 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()
  */
 function comment_menu_link_defaults_alter(&$links) {
@@ -191,6 +171,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 0000000..462a98d
--- /dev/null
+++ b/core/modules/config/config.menu_links.yml
@@ -0,0 +1,5 @@
+config.sync:
+  link_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 11bddb6..900e124 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 0000000..e783bb6
--- /dev/null
+++ b/core/modules/config_translation/config_translation.menu_links.yml
@@ -0,0 +1,6 @@
+config_translation.mapper_list:
+  link_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 e6f8081..139e90e 100644
--- a/core/modules/config_translation/config_translation.module
+++ b/core/modules/config_translation/config_translation.module
@@ -32,21 +32,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().
  */
 function config_translation_permission() {
diff --git a/core/modules/contact/contact.menu_links.yml b/core/modules/contact/contact.menu_links.yml
new file mode 100644
index 0000000..89f0d38
--- /dev/null
+++ b/core/modules/contact/contact.menu_links.yml
@@ -0,0 +1,10 @@
+contact.category_list:
+  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
+contact.site_page:
+  link_title: Contact
+  route_name: contact.site_page
+  menu_name: footer
+  type: 16
diff --git a/core/modules/contact/contact.module b/core/modules/contact/contact.module
index 3894e8f..ef4edd8 100644
--- a/core/modules/contact/contact.module
+++ b/core/modules/contact/contact.module
@@ -52,26 +52,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().
  */
 function contact_entity_type_alter(array &$entity_types) {
diff --git a/core/modules/dblog/dblog.menu_links.yml b/core/modules/dblog/dblog.menu_links.yml
new file mode 100644
index 0000000..d457ba4
--- /dev/null
+++ b/core/modules/dblog/dblog.menu_links.yml
@@ -0,0 +1,16 @@
+dblog.overview:
+  link_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:
+  link_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:
+  link_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 5052a8a..8233efd 100644
--- a/core/modules/dblog/dblog.module
+++ b/core/modules/dblog/dblog.module
@@ -36,29 +36,9 @@ 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',
diff --git a/core/modules/entity/entity.menu_links.yml b/core/modules/entity/entity.menu_links.yml
new file mode 100644
index 0000000..131b13e
--- /dev/null
+++ b/core/modules/entity/entity.menu_links.yml
@@ -0,0 +1,15 @@
+entity.display_mode:
+  link_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:
+  link_title: 'View modes'
+  description: 'Manage custom view modes.'
+  route_name: entity.view_mode_list
+  parent: entity.display_mode
+entity.form_mode_list:
+  link_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 cc22891..3ab67ac 100644
--- a/core/modules/entity/entity.module
+++ b/core/modules/entity/entity.module
@@ -45,36 +45,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().
  */
 function entity_entity_bundle_rename($entity_type_id, $bundle_old, $bundle_new) {
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 0000000..9d93396
--- /dev/null
+++ b/core/modules/field_ui/field_ui.menu_links.yml
@@ -0,0 +1,5 @@
+field_ui.list:
+  link_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 40c8f28..e40d1ee 100644
--- a/core/modules/field_ui/field_ui.module
+++ b/core/modules/field_ui/field_ui.module
@@ -53,20 +53,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().
  */
 function field_ui_permission() {
diff --git a/core/modules/filter/filter.menu_links.yml b/core/modules/filter/filter.menu_links.yml
new file mode 100644
index 0000000..ca2773c
--- /dev/null
+++ b/core/modules/filter/filter.menu_links.yml
@@ -0,0 +1,9 @@
+filter.tips_all:
+  link_title: 'Compose tips'
+  hidden: 1
+  route_name: filter.tips_all
+filter.admin_overview:
+  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
diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module
index 062361b..1c736eb 100644
--- a/core/modules/filter/filter.module
+++ b/core/modules/filter/filter.module
@@ -95,26 +95,6 @@ function filter_element_info() {
 }
 
 /**
- * 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().
  */
 function filter_permission() {
diff --git a/core/modules/forum/forum.menu_links.yml b/core/modules/forum/forum.menu_links.yml
new file mode 100644
index 0000000..1eb6eb5
--- /dev/null
+++ b/core/modules/forum/forum.menu_links.yml
@@ -0,0 +1,9 @@
+forum.index:
+  link_title: Forums
+  route_name: forum.index
+  menu_name: tools
+forum.overview:
+  link_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 faca699..4e3cd52 100644
--- a/core/modules/forum/forum.module
+++ b/core/modules/forum/forum.module
@@ -96,24 +96,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().
  */
 function forum_menu_local_tasks(&$data, $route_name) {
diff --git a/core/modules/help/help.menu_links.yml b/core/modules/help/help.menu_links.yml
new file mode 100644
index 0000000..bad3779
--- /dev/null
+++ b/core/modules/help/help.menu_links.yml
@@ -0,0 +1,6 @@
+help.main:
+  link_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 89e0f31..b02085a 100644
--- a/core/modules/help/help.module
+++ b/core/modules/help/help.module
@@ -6,21 +6,6 @@
  */
 
 /**
- * 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().
  */
 function help_help($path, $arg) {
diff --git a/core/modules/image/image.menu_links.yml b/core/modules/image/image.menu_links.yml
new file mode 100644
index 0000000..896408c
--- /dev/null
+++ b/core/modules/image/image.menu_links.yml
@@ -0,0 +1,5 @@
+image.style_list:
+  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
diff --git a/core/modules/image/image.module b/core/modules/image/image.module
index 472cad4..a57fe55 100644
--- a/core/modules/image/image.module
+++ b/core/modules/image/image.module
@@ -84,20 +84,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().
  */
 function image_theme() {
diff --git a/core/modules/language/language.menu_links.yml b/core/modules/language/language.menu_links.yml
new file mode 100644
index 0000000..79ae7b7
--- /dev/null
+++ b/core/modules/language/language.menu_links.yml
@@ -0,0 +1,11 @@
+language.admin_overview:
+  link_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:
+  link_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 4d82508..41a8a6f 100644
--- a/core/modules/language/language.module
+++ b/core/modules/language/language.module
@@ -70,29 +70,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.
  *
  * @deprecated in Drupal 8.x-dev, will be removed before Drupal 8.0.
diff --git a/core/modules/locale/locale.menu_links.yml b/core/modules/locale/locale.menu_links.yml
new file mode 100644
index 0000000..7b689a3
--- /dev/null
+++ b/core/modules/locale/locale.menu_links.yml
@@ -0,0 +1,11 @@
+locale.translate_page:
+  link_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:
+  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
diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module
index 72ff183..28ee474 100644
--- a/core/modules/locale/locale.module
+++ b/core/modules/locale/locale.module
@@ -169,29 +169,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().
  */
 function locale_permission() {
diff --git a/core/modules/menu/lib/Drupal/menu/MenuFormController.php b/core/modules/menu/lib/Drupal/menu/MenuFormController.php
index fd3f123..d54b77f 100644
--- a/core/modules/menu/lib/Drupal/menu/MenuFormController.php
+++ b/core/modules/menu/lib/Drupal/menu/MenuFormController.php
@@ -303,7 +303,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/menu.menu_links.yml b/core/modules/menu/menu.menu_links.yml
new file mode 100644
index 0000000..3a50d1b
--- /dev/null
+++ b/core/modules/menu/menu.menu_links.yml
@@ -0,0 +1,5 @@
+menu.overview_page:
+  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
diff --git a/core/modules/menu/menu.module b/core/modules/menu/menu.module
index 245e888..496eb28 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().
  */
 function menu_entity_type_build(array &$entity_types) {
diff --git a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php
index 4c420c3..e6c6544 100644
--- a/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php
+++ b/core/modules/menu_link/lib/Drupal/menu_link/MenuLinkStorageController.php
@@ -152,15 +152,21 @@ public function loadUpdatedCustomized(array $router_paths) {
    * {@inheritdoc}
    */
   public function loadModuleAdminTasks() {
-    $query = $this->buildQuery(NULL);
+    $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;
   }
 
   /**
@@ -311,9 +317,6 @@ public function createFromDefaultLink(array $item) {
     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/node/node.menu_links.yml b/core/modules/node/node.menu_links.yml
new file mode 100644
index 0000000..8ae6692
--- /dev/null
+++ b/core/modules/node/node.menu_links.yml
@@ -0,0 +1,14 @@
+node.content_overview:
+  link_title: Content
+  route_name: node.content_overview
+  parent: system.admin
+  description: 'Find and manage content.'
+  weight: -10
+node.overview_types:
+  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
+node.add_page:
+  link_title: 'Add content'
+  route_name: node.add_page
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index 0c0c99d..476b542 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -924,31 +924,6 @@ function _node_revision_access(NodeInterface $node, $op = 'view', $account = NUL
 }
 
 /**
- * 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.
  *
  * @param \Drupal\node\NodeInterface $node
diff --git a/core/modules/path/path.menu_links.yml b/core/modules/path/path.menu_links.yml
new file mode 100644
index 0000000..7b03d26
--- /dev/null
+++ b/core/modules/path/path.menu_links.yml
@@ -0,0 +1,6 @@
+path.admin_overview:
+  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
diff --git a/core/modules/path/path.module b/core/modules/path/path.module
index cf20fa6..8880073 100644
--- a/core/modules/path/path.module
+++ b/core/modules/path/path.module
@@ -55,21 +55,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().
  *
  * @see path_form_element_validate()
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 0000000..39d438e
--- /dev/null
+++ b/core/modules/responsive_image/responsive_image.menu_links.yml
@@ -0,0 +1,6 @@
+responsive_image.mapping_page:
+  link_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 6865a81..33eff41 100644
--- a/core/modules/responsive_image/responsive_image.module
+++ b/core/modules/responsive_image/responsive_image.module
@@ -69,21 +69,6 @@ function responsive_image_menu() {
 }
 
 /**
- * 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.
  *
  * @param int $id
diff --git a/core/modules/search/search.menu_links.yml b/core/modules/search/search.menu_links.yml
new file mode 100644
index 0000000..3b677e5
--- /dev/null
+++ b/core/modules/search/search.menu_links.yml
@@ -0,0 +1,10 @@
+search.view:
+  link_title: Search
+  route_name: search.view
+  hidden: 1
+search.settings:
+  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
diff --git a/core/modules/search/search.module b/core/modules/search/search.module
index 62822e5..e6ea9f0 100644
--- a/core/modules/search/search.module
+++ b/core/modules/search/search.module
@@ -139,26 +139,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.
  *
  * @param $sid
diff --git a/core/modules/shortcut/shortcut.menu_links.yml b/core/modules/shortcut/shortcut.menu_links.yml
new file mode 100644
index 0000000..0761302
--- /dev/null
+++ b/core/modules/shortcut/shortcut.menu_links.yml
@@ -0,0 +1,5 @@
+shortcut.set_admin:
+  link_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 c3d4197..49f7e40 100644
--- a/core/modules/shortcut/shortcut.module
+++ b/core/modules/shortcut/shortcut.module
@@ -62,20 +62,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.
  *
  * @param Drupal\shortcut\ShortcutSetInterface $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 0000000..afb79cf
--- /dev/null
+++ b/core/modules/simpletest/simpletest.menu_links.yml
@@ -0,0 +1,6 @@
+simpletest.test_form:
+  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
diff --git a/core/modules/simpletest/simpletest.module b/core/modules/simpletest/simpletest.module
index 436adc3..7434bc4 100644
--- a/core/modules/simpletest/simpletest.module
+++ b/core/modules/simpletest/simpletest.module
@@ -35,21 +35,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().
  */
 function simpletest_permission() {
diff --git a/core/modules/statistics/statistics.menu_links.yml b/core/modules/statistics/statistics.menu_links.yml
new file mode 100644
index 0000000..3703de3
--- /dev/null
+++ b/core/modules/statistics/statistics.menu_links.yml
@@ -0,0 +1,6 @@
+statistics.settings:
+  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
diff --git a/core/modules/statistics/statistics.module b/core/modules/statistics/statistics.module
index decbc02..13c9644 100644
--- a/core/modules/statistics/statistics.module
+++ b/core/modules/statistics/statistics.module
@@ -79,20 +79,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().
  */
 function statistics_cron() {
diff --git a/core/modules/system/system.api.php b/core/modules/system/system.api.php
index 8083f89..bb82bab 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
@@ -455,36 +458,11 @@ function hook_page_build(&$page) {
  *     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']['link_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 0000000..3ece816
--- /dev/null
+++ b/core/modules/system/system.menu_links.yml
@@ -0,0 +1,149 @@
+system.admin:
+  link_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.'
+  link_title: Structure
+  weight: -8
+system.themes_page:
+  route_name: system.themes_page
+  link_title: Appearance
+  description: 'Select and configure your themes.'
+  parent: system.admin
+  weight: -6
+system.modules_list:
+  link_title: Extend
+  description: 'Add and enable modules to extend site functionality.'
+  parent: system.admin
+  route_name: system.modules_list
+  weight: -2
+system.admin_config:
+  link_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
+  link_title: Media
+  weight: -10
+system.file_system_settings:
+  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
+system.image_toolkit_settings:
+  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
+system.admin_config_services:
+  link_title: 'Web services'
+  parent: system.admin_config
+  route_name: system.admin_config_services
+system.rss_feeds_settings:
+  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
+system.admin_config_development:
+  route_name: system.admin_config_development
+  parent: system.admin_config
+  link_title: Development
+  description: 'Development tools.'
+  weight: -10
+system.site_maintenance_mode:
+  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
+system.performance_settings:
+  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
+system.logging_settings:
+  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
+system.admin_config_regional:
+  route_name: system.admin_config_regional
+  link_title: 'Regional and language'
+  parent: system.admin_config
+  description: 'Regional settings, localization and translation.'
+  weight: -5
+system.regional_settings:
+  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
+system.date_format_list:
+  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
+system.admin_config_search:
+  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.admin_config_system:
+  link_title: System
+  route_name: system.admin_config_system
+  parent: system.admin_config
+  description: 'General system related configuration.'
+  weight: -20
+system.site_information_settings:
+  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
+system.cron_settings:
+  link_title: Cron
+  parent: system.admin_config_system
+  description: 'Manage automatic site maintenance tasks.'
+  route_name: system.cron_settings
+  weight: 20
+system.admin_config_ui:
+  link_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:
+  link_title: Workflow
+  route_name: system.admin_config_workflow
+  parent: system.admin_config
+  description: 'Content workflow, editorial workflow tools.'
+  weight: 5
+system.admin_config_content:
+  link_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:
+  link_title: Reports
+  route_name: system.admin_reports
+  parent: system.admin
+  description: 'View reports, updates, and errors.'
+  weight: 5
+system.status:
+  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
diff --git a/core/modules/system/system.module b/core/modules/system/system.module
index 081d68d..5fcfbf1 100644
--- a/core/modules/system/system.module
+++ b/core/modules/system/system.module
@@ -651,209 +651,6 @@ function system_element_info() {
 }
 
 /**
- * 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().
  */
 function system_theme_suggestions_html(array $variables) {
@@ -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
+      // 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']);
+      }
 
-        // 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 0000000..d4ad17a
--- /dev/null
+++ b/core/modules/system/tests/modules/menu_test/menu_test.menu_links.yml
@@ -0,0 +1,87 @@
+# The name of the menu changes during the course of the test. Using a $_GET.
+menu_test.menu_name_test:
+  link_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:
+  link_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:
+  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
+menu_test.menu_no_title_callback:
+  link_title: 'A title with @placeholder'
+  route_name: menu_test.menu_no_title_callback
+# Hierarchical tests.
+menu_test.hierarchy_parent:
+  link_title: 'Parent menu router'
+  route_name: menu_test.hierarchy_parent
+menu_test.hierarchy_parent.child:
+  link_title: 'Child menu router'
+  route_name: menu_test.hierarchy_parent_child
+  parent: menu_test.hierarchy_parent
+menu_test.hierarchy_parent.child2.child:
+  link_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:
+  link_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:
+  link_title: 'Menu test root'
+  route_name: menu_test.menu_test
+# Hidden tests; one dynamic argument.
+menu_test.hidden:
+  link_title: 'Hidden test root'
+  route_name: menu_test.hidden
+  parent: menu_test
+menu_test.hidden.menu:
+  link_title: Menus
+  route_name: menu_test.hidden_menu
+  parent: menu_test.hidden
+# Hidden tests; two dynamic arguments.
+menu_test.hidden.block:
+  link_title: Blocks
+  route_name: menu_test.hidden_block
+  parent: menu_test.hidden
+# Menu trail tests.
+# @see MenuTrailTestCase
+menu_test.menu-trail:
+  link_title: 'Menu trail - Case 1'
+  route_name: menu_test.menu_trail
+  parent: menu_test
+menu_test.admin.config.development.menu-trail:
+  link_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:
+  link_title: 'Custom 403 page'
+  route_name: menu_test.custom_403
+  parent: menu_test
+menu_test.custom-404-page:
+  link_title: 'Custom 404 page'
+  route_name: menu_test.custom_404
+  parent: menu_test
+menu_test.menu-title-test.case1:
+  link_title: 'Example title - Case 1'
+  route_name: menu_test.title_test_case1
+menu_test.menu-title-test.case2:
+  link_title: 'Example title'
+  route_name: menu_test.title_test_case2
+menu_test.menu-title-test.case3:
+  link_title: 'Bike sheds full of blue smurfs'
+  route_name: menu_test.title_test_case3
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 3f34a1e..d758a0c 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,18 @@
 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',
-  );
+function menu_test_menu_link_defaults_alter(&$link) {
+  $links['menu_test.menu_name_test']['menu_name'] = menu_test_menu_name();
   $links['menu_test.context'] = array(
     'link_title' => \Drupal::config('menu_test.menu_item')->get('title'),
     'route_name' => 'menu_test.context',
   );
-
-  return $links;
 }
 
 /**
diff --git a/core/modules/taxonomy/taxonomy.menu_links.yml b/core/modules/taxonomy/taxonomy.menu_links.yml
new file mode 100644
index 0000000..b3267af
--- /dev/null
+++ b/core/modules/taxonomy/taxonomy.menu_links.yml
@@ -0,0 +1,5 @@
+taxonomy.vocabulary_list:
+  link_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 62952af..5d6121e 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -234,20 +234,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.
  *
  * Checks the current parents of all terms in a vocabulary and updates the
diff --git a/core/modules/tour/tests/tour_test/tour_test.module b/core/modules/tour/tests/tour_test/tour_test.module
index 850bf98..69a9a54 100644
--- a/core/modules/tour/tests/tour_test/tour_test.module
+++ b/core/modules/tour/tests/tour_test/tour_test.module
@@ -8,22 +8,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.
  */
 function tour_test_tour_load($entities) {
diff --git a/core/modules/tracker/tracker.menu_links.yml b/core/modules/tracker/tracker.menu_links.yml
new file mode 100644
index 0000000..153c8f6
--- /dev/null
+++ b/core/modules/tracker/tracker.menu_links.yml
@@ -0,0 +1,3 @@
+tracker.page:
+  link_title: 'Recent content'
+  route_name: tracker.page
diff --git a/core/modules/tracker/tracker.module b/core/modules/tracker/tracker.module
index e1a749a..96aecbf 100644
--- a/core/modules/tracker/tracker.module
+++ b/core/modules/tracker/tracker.module
@@ -32,18 +32,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().
  *
  * Updates tracking information for any items still to be tracked. The state
diff --git a/core/modules/update/update.menu_links.yml b/core/modules/update/update.menu_links.yml
new file mode 100644
index 0000000..284f670
--- /dev/null
+++ b/core/modules/update/update.menu_links.yml
@@ -0,0 +1,6 @@
+update.status:
+  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
diff --git a/core/modules/update/update.module b/core/modules/update/update.module
index 2097a76..6297ff4 100644
--- a/core/modules/update/update.module
+++ b/core/modules/update/update.module
@@ -147,21 +147,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.
  *
  * It both enforces the 'administer software updates' permission and the global
diff --git a/core/modules/user/user.menu_links.yml b/core/modules/user/user.menu_links.yml
new file mode 100644
index 0000000..f7de735
--- /dev/null
+++ b/core/modules/user/user.menu_links.yml
@@ -0,0 +1,29 @@
+user.page:
+  link_title: 'My account'
+  weight: -10
+  route_name: user.page
+  menu_name: account
+user.logout:
+  link_title: 'Log out'
+  route_name: user.logout
+  weight: 10
+  menu_name: account
+user.admin_account:
+  link_title: People
+  route_name: user.admin_account
+  description: 'Manage user accounts, roles, and permissions.'
+  parent: system.admin
+  weight: 4
+user.admin_index:
+  link_title: People
+  route_name: user.admin_index
+  parent: system.admin_config
+  description: 'Configure user accounts.'
+  position: left
+  weight: -20
+user.account_settings:
+  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
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index a8322b8..73c1cf1 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -693,55 +693,6 @@ function theme_username($variables) {
 }
 
 /**
- * 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().
  */
 function user_menu_link_presave(MenuLink $menu_link) {
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 0000000..1562bbe
--- /dev/null
+++ b/core/modules/views_ui/views_ui.menu_links.yml
@@ -0,0 +1,10 @@
+views_ui.list:
+  link_title: Views
+  parent: system.admin_structure
+  description: 'Manage customized lists of content.'
+  route_name: views_ui.list
+views_ui.reports_plugins:
+  link_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 fa381d6..951321c 100644
--- a/core/modules/views_ui/views_ui.module
+++ b/core/modules/views_ui/views_ui.module
@@ -39,31 +39,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().
  */
 function views_ui_entity_type_build(array &$entity_types) {
