diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 9a0d713..4d8e238 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -19,6 +19,7 @@
 use Drupal\Core\Utility\ThemeRegistry;
 use Drupal\Core\Theme\ThemeSettings;
 use Drupal\Component\Utility\NestedArray;
+use Drupal\Component\Utility\MapArray;
 
 /**
  * @defgroup content_flags Content markers
@@ -1645,8 +1646,6 @@ function template_preprocess_status_messages(&$variables) {
  *     http://www.w3.org/TR/WCAG-TECHS/H42.html for more information.
  */
 function theme_links($variables) {
-  $language_url = language(Language::TYPE_URL);
-
   $links = $variables['links'];
   $attributes = $variables['attributes'];
   $heading = $variables['heading'];
@@ -1678,9 +1677,17 @@ function theme_links($variables) {
 
     $num_links = count($links);
     $i = 0;
+    $active = \Drupal::linkGenerator()->getActive();
+
     foreach ($links as $key => $link) {
       $i++;
 
+      $link += array(
+        'href' => NULL,
+        'route_name' => NULL,
+        'route_parameters' => NULL,
+      );
+
       $class = array();
       // Use the array key as class name.
       $class[] = drupal_html_class($key);
@@ -1693,31 +1700,47 @@ function theme_links($variables) {
         $class[] = 'last';
       }
 
-      // Handle links.
-      if (isset($link['href'])) {
+      $link_element = array(
+        '#type' => 'link',
+        '#title' => $link['title'],
+        '#options' => array_diff_key($link, MapArray::copyValuesToKeys(array('title', 'href', 'route_name', 'route_parameters'))),
+        '#href' => $link['href'],
+        '#route_name' => $link['route_name'],
+        '#route_parameters' => $link['route_parameters'],
+      );
+
+      // @todo Reconcile Views usage of 'ajax' as a boolean with the rest of
+      //   core's usage of it as an array.
+      if (isset($link['ajax']) && is_array($link['ajax'])) {
+        $link_element['#ajax'] = $link['ajax'];
+      }
+
+      // Handle links and ensure that the active class is added on the LIs.
+      if (isset($link['route_name'])) {
+        $variables = array(
+          'options' => array(),
+        );
+        if (!empty($link['language'])) {
+          $variables['options']['language'] = $link['language'];
+        }
+
+        if (($link['route_name'] == $active['route_name'])
+        // The language of an active link is equal to the current language.
+        && (empty($variables['options']['language']) || ($variables['options']['language']->id == $active['language']))
+        && ($link['route_parameters'] == $active['parameters'])) {
+          $class[] = 'active';
+        }
+
+        $item = drupal_render($link_element);
+      }
+      elseif (isset($link['href'])) {
+        $language_url = language(Language::TYPE_URL);
         $is_current_path = ($link['href'] == current_path() || ($link['href'] == '<front>' && drupal_is_front_page()));
         $is_current_language = (empty($link['language']) || $link['language']->id == $language_url->id);
         if ($is_current_path && $is_current_language) {
           $class[] = 'active';
         }
-        // @todo Reconcile Views usage of 'ajax' as a boolean with the rest of
-        //   core's usage of it as an array.
-        if (isset($link['ajax']) && is_array($link['ajax'])) {
-          // To attach Ajax behavior, render a link element, rather than just
-          // call l().
-          $link_element = array(
-            '#type' => 'link',
-            '#title' => $link['title'],
-            '#href' => $link['href'],
-            '#ajax' => $link['ajax'],
-            '#options' => array_diff_key($link, drupal_map_assoc(array('title', 'href', 'ajax'))),
-          );
-          $item = drupal_render($link_element);
-        }
-        else {
-          // Pass in $link as $options, they share the same keys.
-          $item = l($link['title'], $link['href'], $link);
-        }
+        $item = drupal_render($link_element);
       }
       // Handle title-only text items.
       else {
diff --git a/core/lib/Drupal/Core/Utility/LinkGenerator.php b/core/lib/Drupal/Core/Utility/LinkGenerator.php
index 12dc3d6..3f63620 100644
--- a/core/lib/Drupal/Core/Utility/LinkGenerator.php
+++ b/core/lib/Drupal/Core/Utility/LinkGenerator.php
@@ -87,6 +87,13 @@ public function setRequest(Request $request) {
   /**
    * {@inheritdoc}
    */
+  public function getActive() {
+    return $this->active;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
   public function generate($text, $route_name, array $parameters = array(), array $options = array()) {
     // Start building a structured representation of our link to be altered later.
     $variables = array(
diff --git a/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php b/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php
index cf1b0d8..8bc7eb6 100644
--- a/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php
+++ b/core/lib/Drupal/Core/Utility/LinkGeneratorInterface.php
@@ -71,4 +71,16 @@
    */
   public function generate($text, $route_name, array $parameters = array(), array $options = array());
 
+  /**
+   * Returns information for the currently active route.
+   *
+   * @return array
+   *   An array of active route information, containing the following keys:
+   *     - route_name: The currently active route_name
+   *     - language: The current language
+   *     - parameters: An array of request parameters and any query string
+   *       parameters.
+   */
+  public function getActive();
+
 }
diff --git a/core/modules/aggregator/lib/Drupal/aggregator/Controller/AggregatorController.php b/core/modules/aggregator/lib/Drupal/aggregator/Controller/AggregatorController.php
index 4d5dd9a..932aba1 100644
--- a/core/modules/aggregator/lib/Drupal/aggregator/Controller/AggregatorController.php
+++ b/core/modules/aggregator/lib/Drupal/aggregator/Controller/AggregatorController.php
@@ -186,19 +186,23 @@ public function adminOverview() {
       $links = array();
       $links['edit'] = array(
         'title' => $this->t('Edit'),
-        'href' => "admin/config/services/aggregator/edit/feed/$feed->fid",
+        'route_name' => 'aggregator.feed_edit',
+        'route_parameters' => array('aggregator_feed' => $feed->fid),
       );
       $links['delete'] = array(
         'title' => $this->t('Delete'),
-        'href' => "admin/config/services/aggregator/delete/feed/$feed->fid",
+        'route_name' => 'aggregator.feed_delete',
+        'route_parameters' => array('aggregator_feed' => $feed->fid),
       );
       $links['remove'] = array(
         'title' => $this->t('Remove items'),
-        'href' => "admin/config/services/aggregator/remove/$feed->fid",
+        'route_name' => 'aggregator.feed_items_delete',
+        'route_parameters' => array('aggregator_feed' => $feed->fid),
       );
       $links['update'] = array(
         'title' => $this->t('Update items'),
-        'href' => "admin/config/services/aggregator/update/$feed->fid",
+        'route_name' => 'aggregator.feed_refresh',
+        'route_parameters' => array('aggregator_feed' => $feed->fid),
         'query' => array('token' => drupal_get_token("aggregator/update/$feed->fid")),
       );
       $row[] = array(
@@ -228,11 +232,13 @@ public function adminOverview() {
       $links = array();
       $links['edit'] = array(
         'title' => $this->t('Edit'),
-        'href' => "admin/config/services/aggregator/edit/category/$category->cid",
+        'route_name' => 'aggregator.category_admin_edit',
+        'route_parameters' => array('cid' => $category->cid),
       );
       $links['delete'] = array(
         'title' => $this->t('Delete'),
-        'href' => "admin/config/services/aggregator/delete/category/$category->cid",
+        'route_name' => 'aggregator.category_delete',
+        'route_parameters' => array('cid' => $category->cid),
       );
       $row[] = array(
         'data' => array(
diff --git a/core/modules/ban/lib/Drupal/ban/Form/BanAdmin.php b/core/modules/ban/lib/Drupal/ban/Form/BanAdmin.php
index f15f734..edfe0c2 100644
--- a/core/modules/ban/lib/Drupal/ban/Form/BanAdmin.php
+++ b/core/modules/ban/lib/Drupal/ban/Form/BanAdmin.php
@@ -63,7 +63,8 @@ public function buildForm(array $form, array &$form_state, $default_ip = '') {
       $links = array();
       $links['delete'] = array(
         'title' => $this->t('delete'),
-        'href' => "admin/config/people/ban/delete/$ip->iid",
+        'route_name' => 'ban.delete',
+        'route_parameters' => array('ban_id' => $ip->iid),
       );
       $row[] = array(
         'data' => array(
diff --git a/core/modules/book/book.admin.inc b/core/modules/book/book.admin.inc
index 1d7061b..0ffefc5 100644
--- a/core/modules/book/book.admin.inc
+++ b/core/modules/book/book.admin.inc
@@ -53,12 +53,14 @@ function theme_book_admin_table($variables) {
     if ($access) {
       $links['edit'] = array(
         'title' => t('Edit'),
-        'href' => "node/$nid/edit",
+        'route_name' => 'node.page_edit',
+        'route_parameters' => array('node' => $nid),
         'query' => $destination,
       );
       $links['delete'] = array(
         'title' => t('Delete'),
-        'href' => "node/$nid/delete",
+        'route_name' => 'node.delete_confirm',
+        'route_parameters' => array('node' => $nid),
         'query' => $destination,
       );
     }
diff --git a/core/modules/book/lib/Drupal/book/Controller/BookController.php b/core/modules/book/lib/Drupal/book/Controller/BookController.php
index 214a801..b9920d2 100644
--- a/core/modules/book/lib/Drupal/book/Controller/BookController.php
+++ b/core/modules/book/lib/Drupal/book/Controller/BookController.php
@@ -77,7 +77,8 @@ public function adminOverview() {
       $links = array();
       $links['edit'] = array(
         'title' => t('Edit order and titles'),
-        'href' => 'admin/structure/book/' . $book['nid'],
+        'route_name' => 'book.admin_edit',
+        'route_parameters' => array('node' => $book['nid']),
       );
       $row[] = array(
         'data' => array(
diff --git a/core/modules/comment/comment.admin.inc b/core/modules/comment/comment.admin.inc
index f17fa59..6893638 100644
--- a/core/modules/comment/comment.admin.inc
+++ b/core/modules/comment/comment.admin.inc
@@ -155,13 +155,16 @@ function comment_admin_overview($form, &$form_state, $arg) {
     $links = array();
     $links['edit'] = array(
       'title' => t('edit'),
-      'href' => 'comment/' . $comment->id() . '/edit',
+      'route_name' => 'comment.edit_page',
+      'route_parameters' => array('comment' => $comment->id()),
       'query' => $destination,
     );
     if (module_invoke('content_translation', 'translate_access', $comment)) {
       $links['translate'] = array(
         'title' => t('translate'),
         'href' => 'comment/' . $comment->id() . '/translations',
+        'route_name' => 'content_translation.translation_overview_comment',
+        'route_parameters' => array('comment' => $comment->id()),
         'query' => $destination,
       );
     }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/FunctionsTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/FunctionsTest.php
index 69d5ac6..d1930c9 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Theme/FunctionsTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Theme/FunctionsTest.php
@@ -13,6 +13,14 @@
  * Tests for common theme functions.
  */
 class FunctionsTest extends WebTestBase {
+
+  /**
+   * Modules to enable.
+   *
+   * @var array
+   */
+  public static $modules = array('router_test');
+
   public static function getInfo() {
     return array(
       'name' => 'Theme functions',
@@ -172,13 +180,19 @@ function testLinks() {
         'title' => 'Front page',
         'href' => '<front>',
       ),
+      'router-test' => array(
+        'title' => 'Test route',
+        'route_name' => 'router_test.1',
+        'route_parameters' => array(),
+      ),
     );
 
     $expected_links = '';
     $expected_links .= '<ul id="somelinks">';
     $expected_links .= '<li class="a-link odd first"><a href="' . url('a/link') . '">' . check_plain('A <link>') . '</a></li>';
     $expected_links .= '<li class="plain-text even">' . check_plain('Plain "text"') . '</li>';
-    $expected_links .= '<li class="front-page odd last active"><a href="' . url('<front>') . '" class="active">' . check_plain('Front page') . '</a></li>';
+    $expected_links .= '<li class="front-page odd active"><a href="' . url('<front>') . '" class="active">' . check_plain('Front page') . '</a></li>';
+    $expected_links .= '<li class="router-test even last"><a href="' . \Drupal::urlGenerator()->generate('router_test.1') . '">' . check_plain('Test route') . '</a></li>';
     $expected_links .= '</ul>';
 
     // Verify that passing a string as heading works.
@@ -210,7 +224,8 @@ function testLinks() {
     $expected_links .= '<ul id="somelinks">';
     $expected_links .= '<li class="a-link odd first"><a href="' . url('a/link') . '" class="a/class">' . check_plain('A <link>') . '</a></li>';
     $expected_links .= '<li class="plain-text even"><span class="a/class">' . check_plain('Plain "text"') . '</span></li>';
-    $expected_links .= '<li class="front-page odd last active"><a href="' . url('<front>') . '" class="active">' . check_plain('Front page') . '</a></li>';
+    $expected_links .= '<li class="front-page odd active"><a href="' . url('<front>') . '" class="active">' . check_plain('Front page') . '</a></li>';
+    $expected_links .= '<li class="router-test even last"><a href="' . \Drupal::urlGenerator()->generate('router_test.1') . '">' . check_plain('Test route') . '</a></li>';
     $expected_links .= '</ul>';
     $expected = $expected_heading . $expected_links;
     $this->assertThemeOutput('links', $variables, $expected);
