diff --git a/core/lib/Drupal/Core/Entity/Entity.php b/core/lib/Drupal/Core/Entity/Entity.php
index 2461202..d00506f 100644
--- a/core/lib/Drupal/Core/Entity/Entity.php
+++ b/core/lib/Drupal/Core/Entity/Entity.php
@@ -173,7 +173,7 @@ public function uri($rel = 'canonical') {
       // If there is a template for the given relationship type, do the
       // placeholder replacement and use that as the path.
       $replacements = $this->uriPlaceholderReplacements();
-      $uri['path'] = str_replace(array_keys($replacements), array_values($replacements), $template);
+      $uri['path'] = \Drupal::urlGenerator()->getPathFromRoute($link_templates[$rel], $replacements);
 
       // @todo Remove this once http://drupal.org/node/1888424 is in and we can
       //   move the BC handling of / vs. no-/ to the generator.
@@ -243,12 +243,16 @@ protected function linkTemplates() {
   protected function uriPlaceholderReplacements() {
     if (empty($this->uriPlaceholderReplacements)) {
       $this->uriPlaceholderReplacements = array(
-        '{entityType}' => $this->entityType(),
-        '{bundle}' => $this->bundle(),
-        '{id}' => $this->id(),
-        '{uuid}' => $this->uuid(),
-        '{' . $this->entityType() . '}' => $this->id(),
+        'entityType' => $this->entityType(),
+        'bundle' => $this->bundle(),
+        'id' => $this->id(),
+        'uuid' => $this->uuid(),
+        $this->entityType() => $this->id(),
       );
+      $entity_info = \Drupal::entityManager()->getDefinition($this->entityType());
+      if (isset($entity_info['bundle_entity_type'])) {
+        $this->uriPlaceholderReplacements[$entity_info['bundle_entity_type']] = $this->bundle();
+      }
     }
     return $this->uriPlaceholderReplacements;
   }
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeAccessBaseTableTest.php b/core/modules/node/lib/Drupal/node/Tests/NodeAccessBaseTableTest.php
index e78b5bc..6831705 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeAccessBaseTableTest.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeAccessBaseTableTest.php
@@ -17,7 +17,7 @@ class NodeAccessBaseTableTest extends NodeTestBase {
    *
    * @var array
    */
-  public static $modules = array('node_access_test');
+  public static $modules = array('node_access_test', 'views');
 
   /**
    * The installation profile to use with this test.
diff --git a/core/modules/node/node.module b/core/modules/node/node.module
index b457cb9..2516658 100644
--- a/core/modules/node/node.module
+++ b/core/modules/node/node.module
@@ -1292,7 +1292,6 @@ function node_view_multiple($nodes, $view_mode = 'teaser', $langcode = NULL) {
  * default setting for number of posts to show on node listing pages.
  *
  * @see node_page_default()
- * @see taxonomy_term_page()
  * @see node_form_system_site_information_settings_form_submit()
  */
 function node_form_system_site_information_settings_form_alter(&$form, &$form_state, $form_id) {
diff --git a/core/modules/path/lib/Drupal/path/Tests/PathTaxonomyTermTest.php b/core/modules/path/lib/Drupal/path/Tests/PathTaxonomyTermTest.php
index c483d9e..d747ce2 100644
--- a/core/modules/path/lib/Drupal/path/Tests/PathTaxonomyTermTest.php
+++ b/core/modules/path/lib/Drupal/path/Tests/PathTaxonomyTermTest.php
@@ -17,7 +17,7 @@ class PathTaxonomyTermTest extends PathTestBase {
    *
    * @var array
    */
-  public static $modules = array('taxonomy');
+  public static $modules = array('taxonomy', 'views');
 
   public static function getInfo() {
     return array(
diff --git a/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyAttributesTest.php b/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyAttributesTest.php
index 268a247..87469f6 100644
--- a/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyAttributesTest.php
+++ b/core/modules/rdf/lib/Drupal/rdf/Tests/TaxonomyAttributesTest.php
@@ -19,7 +19,7 @@ class TaxonomyAttributesTest extends TaxonomyTestBase {
    *
    * @var array
    */
-  public static $modules = array('rdf');
+  public static $modules = array('rdf', 'views');
 
   public static function getInfo() {
     return array(
diff --git a/core/modules/taxonomy/config/views.view.taxonomy_term.yml b/core/modules/taxonomy/config/views.view.taxonomy_term.yml
new file mode 100644
index 0000000..dbcae0d
--- /dev/null
+++ b/core/modules/taxonomy/config/views.view.taxonomy_term.yml
@@ -0,0 +1,237 @@
+base_field: nid
+base_table: node
+core: '8'
+description: 'Content belonging to a certain taxonomy term.'
+status: '1'
+display:
+  default:
+    id: default
+    display_title: Master
+    display_plugin: default
+    position: 1
+    display_options:
+      query:
+        type: views_query
+        options:
+          query_comment: false
+          disable_sql_rewrite: false
+          distinct: false
+          slave: false
+          query_tags: {  }
+      access:
+        type: none
+        options: {  }
+      cache:
+        type: none
+        options: {  }
+      exposed_form:
+        type: basic
+        options:
+          submit_button: Apply
+          reset_button: false
+          reset_button_label: Reset
+          exposed_sorts_label: 'Sort by'
+          expose_sort_order: true
+          sort_asc_label: Asc
+          sort_desc_label: Desc
+      pager:
+        type: mini
+        options:
+          items_per_page: 10
+          offset: 0
+          id: 0
+          total_pages: 0
+          expose:
+            items_per_page: '0'
+            items_per_page_label: 'Items per page'
+            items_per_page_options: '5, 10, 20, 40, 60'
+            items_per_page_options_all: false
+            items_per_page_options_all_label: '- All -'
+            offset: false
+            offset_label: Offset
+          tags:
+            previous: ‹‹
+            next: ››
+      sorts:
+        sticky:
+          id: sticky
+          table: node_field_data
+          field: sticky
+          order: DESC
+          plugin_id: standard
+          relationship: none
+          group_type: group
+          admin_label: ''
+          exposed: false
+          expose:
+            label: ''
+          provider: views
+        created:
+          id: created
+          table: node_field_data
+          field: created
+          order: DESC
+          plugin_id: date
+          relationship: none
+          group_type: group
+          admin_label: ''
+          exposed: false
+          expose:
+            label: ''
+          granularity: second
+          provider: views
+      arguments:
+        term_node_tid_depth:
+          id: term_node_tid_depth
+          table: node
+          field: term_node_tid_depth
+          default_action: 'not found'
+          exception:
+            value: all
+            title_enable: true
+            title: All
+          title_enable: true
+          title: '%1'
+          default_argument_type: fixed
+          summary:
+            format: default_summary
+          specify_validation: true
+          validate:
+            type: 'entity:taxonomy_term'
+            fail: 'not found'
+          validate_options:
+            access: '1'
+            operation: view
+            multiple: '1'
+            bundles: {  }
+          depth: '0'
+          break_phrase: true
+          plugin_id: taxonomy_index_tid_depth
+          relationship: none
+          group_type: group
+          admin_label: ''
+          default_argument_options:
+            argument: ''
+          default_argument_skip_url: false
+          summary_options:
+            base_path: ''
+            count: '1'
+            items_per_page: '25'
+            override: false
+          provider: taxonomy
+        term_node_tid_depth_modifier:
+          id: term_node_tid_depth_modifier
+          table: node
+          field: term_node_tid_depth_modifier
+          exception:
+            title_enable: true
+          default_argument_type: fixed
+          summary:
+            format: default_summary
+          specify_validation: true
+          plugin_id: taxonomy_index_tid_depth_modifier
+          relationship: none
+          group_type: group
+          admin_label: ''
+          default_action: ignore
+          title_enable: false
+          title: ''
+          default_argument_options: {  }
+          default_argument_skip_url: false
+          summary_options: {  }
+          validate:
+            type: none
+            fail: 'not found'
+          validate_options: {  }
+          provider: taxonomy
+      filters:
+        status_extra:
+          id: status_extra
+          table: node_field_data
+          field: status_extra
+          group: '0'
+          expose:
+            operator: '0'
+          plugin_id: node_status
+          provider: node
+      style:
+        type: default
+        options:
+          grouping: {  }
+          row_class: ''
+          default_row_class: true
+          row_class_special: true
+          uses_fields: false
+      row:
+        type: 'entity:node'
+        options:
+          view_mode: teaser
+          links: '1'
+          comments: '0'
+      header: {  }
+      footer: {  }
+      empty: {  }
+      relationships: {  }
+      fields: {  }
+  page_1:
+    id: page_1
+    display_title: Page
+    display_plugin: page
+    position: 2
+    display_options:
+      query:
+        type: views_query
+        options: {  }
+      path: taxonomy/term/%
+  feed_1:
+    id: feed_1
+    display_title: Feed
+    display_plugin: feed
+    position: 3
+    display_options:
+      query:
+        type: views_query
+        options: {  }
+      pager:
+        type: full
+        options:
+          items_per_page: 15
+          offset: 0
+          id: 0
+          total_pages: 0
+          expose:
+            items_per_page: '0'
+            items_per_page_label: 'Items per page'
+            items_per_page_options: '5, 10, 20, 40, 60'
+            items_per_page_options_all: false
+            items_per_page_options_all_label: '- All -'
+            offset: false
+            offset_label: Offset
+          tags:
+            previous: '‹ previous'
+            next: 'next ›'
+            first: '« first'
+            last: 'last »'
+          quantity: '9'
+      path: taxonomy/term/%/%/feed
+      displays:
+        page: page
+        default: '0'
+      style:
+        type: rss
+        options:
+          description: ''
+          grouping: {  }
+          uses_fields: '0'
+      row:
+        type: node_rss
+        options:
+          relationship: none
+          item_length: default
+          links: '0'
+label: 'Taxonomy term'
+module: taxonomy
+id: taxonomy_term
+tag: default
+uuid: e0dea92e-a4c9-4442-a518-2499bfe17d73
+langcode: en
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TaxonomyController.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TaxonomyController.php
index cf49ec6..bb1b7ae 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TaxonomyController.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Controller/TaxonomyController.php
@@ -19,6 +19,19 @@
 class TaxonomyController extends ControllerBase {
 
   /**
+   * Title callback for term pages.
+   *
+   * @param \Drupal\taxonomy\TermInterface $term
+   *   A taxonomy term entity.
+   *
+   * @return
+   *   The term name to be used as the page title.
+   */
+  public function getTitle(TermInterface $term) {
+    return $term->label();
+  }
+
+  /**
    * Returns a rendered edit form to create a new term associated to the given vocabulary.
    *
    * @param \Drupal\taxonomy\VocabularyInterface $taxonomy_vocabulary
@@ -36,46 +49,81 @@ public function addForm(VocabularyInterface $taxonomy_vocabulary) {
   }
 
   /**
-   * @todo Remove taxonomy_term_page().
-   */
-  public function termPage(TermInterface $taxonomy_term) {
-    module_load_include('pages.inc', 'taxonomy');
-    return taxonomy_term_page($taxonomy_term);
-
-  }
-
-  /**
    * Route title callback.
    *
-   * @param \Drupal\taxonomy\VocabularyInterface $taxonomy_vocabulary
+   * @param \Drupal\taxonomy\TermInterface $taxonomy_term
    *   The taxonomy term.
    *
    * @return string
    *   The term label.
    */
-  public function vocabularyTitle(VocabularyInterface $taxonomy_vocabulary) {
-    return Xss::filter($taxonomy_vocabulary->label());
+  public function termTitle(TermInterface $taxonomy_term) {
+    return Xss::filter($taxonomy_term->label());
   }
 
   /**
-   * Route title callback.
+   * Displays all nodes associated with a term.
    *
-   * @param \Drupal\taxonomy\TermInterface $taxonomy_term
-   *   The taxonomy term.
+   * @param \Drupal\taxonomy\TermInterface $term
+   *   The taxonomy term entity.
    *
-   * @return string
-   *   The term label.
+   * @return
+   *   A structured array to be rendered by drupal_render().
    */
-  public function termTitle(TermInterface $taxonomy_term) {
-    return Xss::filter($taxonomy_term->label());
+  public function termPage(TermInterface $term) {
+    // Assign the term name as the page title.
+    drupal_set_title($term->label());
+
+    $build['#attached']['drupal_add_feed'][] = array('taxonomy/term/' . $term->id() . '/feed', 'RSS - ' . $term->label());
+
+    foreach ($term->uriRelationships() as $rel) {
+      $uri = $term->uri($rel);
+      // Set the term path as the canonical URL to prevent duplicate content.
+      drupal_add_html_head_link(array('rel' => $rel, 'href' => url($uri['path'], $uri['options'])), TRUE);
+
+      if ($rel == 'canonical') {
+        // Set the non-aliased canonical path as a default shortlink.
+        drupal_add_html_head_link(array('rel' => 'shortlink', 'href' => url($uri['path'], array_merge($uri['options'], array('alias' => TRUE)))), TRUE);
+      }
+    }
+
+    $build['taxonomy_terms'] = taxonomy_term_view_multiple(array($term->id() => $term));
+    if ($nids = taxonomy_select_nodes($term->id(), TRUE, \Drupal::config('node.settings')->get('items_per_page'))) {
+      $nodes = node_load_multiple($nids);
+      $build['nodes'] = node_view_multiple($nodes);
+      $build['pager'] = array(
+        '#theme' => 'pager',
+        '#weight' => 5,
+      );
+    }
+    else {
+      $build['no_content'] = array(
+        '#prefix' => '<p>',
+        '#markup' => t('There is currently no content classified with this term.'),
+        '#suffix' => '</p>',
+      );
+    }
+    return $build;
   }
 
   /**
-   * @todo Remove taxonomy_term_feed().
+   * Generate the content feed for a taxonomy term.
+   *
+   * @param \Drupal\taxonomy\TermInterface $term
+   *   The taxonomy term entity.
+   *
+   * @return \Symfony\Component\HttpFoundation\Response
+   *   A response object.
    */
-  public function termFeed(TermInterface $taxonomy_term) {
-    module_load_include('pages.inc', 'taxonomy');
-    return taxonomy_term_feed($taxonomy_term);
+  public function termFeed(TermInterface $term) {
+    $channel['link'] = url('taxonomy/term/' . $term->id(), array('absolute' => TRUE));
+    $channel['title'] = \Drupal::config('system.site')->get('name') . ' - ' . $term->label();
+    // Only display the description if we have a single term, to avoid clutter and confusion.
+    // HTML will be removed from feed description.
+    $channel['description'] = check_markup($term->description->value, $term->format->value, '', TRUE);
+    $nids = taxonomy_select_nodes($term->id(), FALSE, \Drupal::config('system.rss')->get('items.limit'));
+
+    return node_feed($nids, $channel);
   }
 
 }
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermIndexTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermIndexTest.php
index c1e9bb2..0ae0f05 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermIndexTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermIndexTest.php
@@ -207,6 +207,7 @@ function testTaxonomyIndex() {
    * Tests that there is a link to the parent term on the child term page.
    */
   function testTaxonomyTermHierarchyBreadcrumbs() {
+    \Drupal::moduleHandler()->install(array('views'));
     // Create two taxonomy terms and set term2 as the parent of term1.
     $term1 = $this->createTerm($this->vocabulary);
     $term2 = $this->createTerm($this->vocabulary);
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php
index 410ff5a..364644d 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermTest.php
@@ -295,6 +295,7 @@ function testTermAutocompletion() {
    * Save, edit and delete a term using the user interface.
    */
   function testTermInterface() {
+    \Drupal::moduleHandler()->install(array('views'));
     $edit = array(
       'name' => $this->randomName(12),
       'description[value]' => $this->randomName(100),
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyPermissionsTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyPermissionsTest.php
index 9b4611d..34a805a 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyPermissionsTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/VocabularyPermissionsTest.php
@@ -52,7 +52,7 @@ function testVocabularyPermissionsTaxonomyTerm() {
     // Edit the term.
     $this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
     $this->assertResponse(200);
-    $this->assertText($edit['name'], 'Edit taxonomy term form opened successfully.');
+    $this->assertRaw($edit['name'], 'Edit taxonomy term form opened successfully.');
 
     $edit['name'] = $this->randomName();
     $this->drupalPostForm(NULL, $edit, t('Save'));
@@ -80,7 +80,7 @@ function testVocabularyPermissionsTaxonomyTerm() {
     // Edit the term.
     $this->drupalGet('taxonomy/term/' . $term->id() . '/edit');
     $this->assertResponse(200);
-    $this->assertText($term->name->value, 'Edit taxonomy term form opened successfully.');
+    $this->assertRaw($term->name->value, 'Edit taxonomy term form opened successfully.');
 
     $edit['name'] = $this->randomName();
     $this->drupalPostForm(NULL, $edit, t('Save'));
diff --git a/core/modules/taxonomy/taxonomy.module b/core/modules/taxonomy/taxonomy.module
index fd08344..f57afbc 100644
--- a/core/modules/taxonomy/taxonomy.module
+++ b/core/modules/taxonomy/taxonomy.module
@@ -7,6 +7,9 @@
 
 use Drupal\Core\Entity\FieldableDatabaseStorageController;
 use Drupal\Core\Entity\EntityInterface;
+use Drupal\Core\Field\FieldDefinitionInterface;
+use Drupal\field\FieldInterface;
+use Drupal\field\FieldInstanceInterface;
 use Drupal\file\FileInterface;
 use Drupal\node\Entity\Node;
 use Drupal\taxonomy\Entity\Term;
@@ -252,6 +255,21 @@ function taxonomy_menu() {
     'type' => MENU_CALLBACK,
   );
 
+  $items['admin/structure/taxonomy/manage/%taxonomy_vocabulary'] = array(
+    'route_name' => 'taxonomy.overview_terms',
+    'title callback' => 'entity_page_label',
+    'title arguments' => array(4),
+  );
+  $items['admin/structure/taxonomy/manage/%taxonomy_vocabulary/list'] = array(
+    'title' => 'List',
+    'type' => MENU_DEFAULT_LOCAL_TASK,
+  );
+  $items['admin/structure/taxonomy/manage/%taxonomy_vocabulary/edit'] = array(
+    'title' => 'Edit',
+    'route_name' => 'taxonomy.vocabulary_edit',
+    'type' => MENU_LOCAL_TASK,
+  );
+
   return $items;
 }
 
@@ -394,6 +412,12 @@ function template_preprocess_taxonomy_term(&$variables) {
     $variables['content'][$key] = $variables['elements'][$key];
   }
 
+  // field_attach_preprocess() overwrites the $[field_name] variables with the
+  // values of the field in the language that was selected for display, instead
+  // of the raw values in $term->[field_name], which contain all values in all
+  // languages.
+  field_attach_preprocess($term, $variables['content'], $variables);
+
   // Gather classes, and clean up name so there are no underscores.
   $variables['attributes']['class'][] = 'taxonomy-term';
   $vocabulary_name_css = str_replace('_', '-', $term->bundle());
@@ -786,6 +810,39 @@ function taxonomy_implode_tags($tags, $vid = NULL) {
 }
 
 /**
+ * Implements hook_field_info().
+ *
+ * Field settings:
+ * - allowed_values: a list array of one or more vocabulary trees:
+ *   - vocabulary: a vocabulary machine name.
+ *   - parent: a term ID of a term whose children are allowed. This should be
+ *     '0' if all terms in a vocabulary are allowed. The allowed values do not
+ *     include the parent term.
+ *
+ */
+function taxonomy_field_info() {
+  return array(
+    'taxonomy_term_reference' => array(
+      'label' => t('Term reference'),
+      'description' => t('This field stores a reference to a taxonomy term.'),
+      'default_widget' => 'options_select',
+      'default_formatter' => 'taxonomy_term_reference_link',
+      'class' => 'Drupal\taxonomy\Type\TaxonomyTermReferenceItem',
+      'settings' => array(
+        'options_list_callback' => NULL,
+        'allowed_values' => array(
+          array(
+            'vocabulary' => '',
+            'parent' => '0',
+          ),
+        ),
+      ),
+      'list_class' => '\Drupal\taxonomy\Plugin\Field\FieldType\TaxonomyTermReferenceFieldItemList',
+    ),
+  );
+}
+
+/**
  * Implements hook_field_widget_info_alter().
  */
 function taxonomy_field_widget_info_alter(&$info) {
@@ -794,6 +851,105 @@ function taxonomy_field_widget_info_alter(&$info) {
 }
 
 /**
+ * Implements hook_options_list().
+ */
+function taxonomy_options_list(FieldDefinitionInterface $field_definition, EntityInterface $entity) {
+  $function = $field_definition->getFieldSetting('options_list_callback') ?: 'taxonomy_allowed_values';
+  return $function($field_definition, $entity);
+}
+
+/**
+ * Implements hook_field_validate().
+ *
+ * Taxonomy field settings allow for either a single vocabulary ID, multiple
+ * vocabulary IDs, or sub-trees of a vocabulary to be specified as allowed
+ * values, although only the first of these is supported via the field UI.
+ * Confirm that terms entered as values meet at least one of these conditions.
+ *
+ * Possible error codes:
+ * - 'taxonomy_term_illegal_value': The value is not part of the list of allowed values.
+ */
+function taxonomy_field_validate(EntityInterface $entity = NULL, FieldInterface $field, FieldInstanceInterface $instance, $langcode, $items, &$errors) {
+  // Build an array of existing term IDs so they can be loaded with
+  // entity_load_multiple('taxonomy_term');
+  foreach ($items as $delta => $item) {
+    if (!empty($item['target_id']) && $item['target_id'] != 'autocreate') {
+      $tids[] = $item['target_id'];
+    }
+  }
+  if (!empty($tids)) {
+    $terms = entity_load_multiple('taxonomy_term', $tids);
+
+    // Check each existing item to ensure it can be found in the
+    // allowed values for this field.
+    foreach ($items as $delta => $item) {
+      $validate = TRUE;
+      if (!empty($item['target_id']) && $item['target_id'] != 'autocreate') {
+        $validate = FALSE;
+        foreach ($instance->getFieldSetting('allowed_values') as $settings) {
+          // If no parent is specified, check if the term is in the vocabulary.
+          if (isset($settings['vocabulary']) && empty($settings['parent'])) {
+            if ($settings['vocabulary'] == $terms[$item['target_id']]->bundle()) {
+              $validate = TRUE;
+              break;
+            }
+          }
+          // If a parent is specified, then to validate it must appear in the
+          // array returned by taxonomy_term_load_parents_all().
+          elseif (!empty($settings['parent'])) {
+            $ancestors = taxonomy_term_load_parents_all($item['target_id']);
+            foreach ($ancestors as $ancestor) {
+              if ($ancestor->id() == $settings['parent']) {
+                $validate = TRUE;
+                break 2;
+              }
+            }
+          }
+        }
+      }
+      if (!$validate) {
+        $errors[$instance->getFieldName()][$langcode][$delta][] = array(
+          'error' => 'taxonomy_term_reference_illegal_value',
+          'message' => t('%name: illegal value.', array('%name' => $instance->getFieldLabel())),
+        );
+      }
+    }
+  }
+}
+
+/**
+ * Implements hook_field_is_empty().
+ */
+function taxonomy_field_is_empty($item, $field_type) {
+  return !is_array($item) || (empty($item['target_id']) && empty($item['entity']));
+}
+
+/**
+ * Returns the set of valid terms for a taxonomy field.
+ *
+ * @param \Drupal\Core\Field\FieldDefinitionInterface $field_definition
+ *   The field definition.
+ * @param \Drupal\Core\Entity\EntityInterface $entity
+ *   The entity object the field is attached to.
+ *
+ * @return
+ *   The array of valid terms for this field, keyed by term id.
+ */
+function taxonomy_allowed_values(FieldDefinitionInterface $field_definition, EntityInterface $entity) {
+  $options = array();
+  foreach ($field_definition->getFieldSetting('allowed_values') as $tree) {
+    if ($vocabulary = entity_load('taxonomy_vocabulary', $tree['vocabulary'])) {
+      if ($terms = taxonomy_get_tree($vocabulary->id(), $tree['parent'], NULL, TRUE)) {
+        foreach ($terms as $term) {
+          $options[$term->id()] = str_repeat('-', $term->depth) . $term->label();
+        }
+      }
+    }
+  }
+  return $options;
+}
+
+/**
  * Title callback for term pages.
  *
  * @param \Drupal\taxonomy\Entity\Term $term
@@ -820,6 +976,39 @@ function taxonomy_autocomplete_validate($element, &$form_state) {
 }
 
 /**
+ * Implements hook_field_settings_form().
+ */
+function taxonomy_field_settings_form($field, $instance) {
+  // Get proper values for 'allowed_values_function', which is a core setting.
+  $vocabularies = entity_load_multiple('taxonomy_vocabulary');
+  $options = array();
+  foreach ($vocabularies as $vocabulary) {
+    $options[$vocabulary->id()] = $vocabulary->name;
+  }
+  $form['allowed_values'] = array(
+    '#tree' => TRUE,
+  );
+
+  foreach ($field->getFieldSetting('allowed_values') as $delta => $tree) {
+    $form['allowed_values'][$delta]['vocabulary'] = array(
+      '#type' => 'select',
+      '#title' => t('Vocabulary'),
+      '#default_value' => $tree['vocabulary'],
+      '#options' => $options,
+      '#required' => TRUE,
+      '#description' => t('The vocabulary which supplies the options for this field.'),
+      '#disabled' => $field->hasData(),
+    );
+    $form['allowed_values'][$delta]['parent'] = array(
+      '#type' => 'value',
+      '#value' => $tree['parent'],
+    );
+  }
+
+  return $form;
+}
+
+/**
  * @defgroup taxonomy_index Taxonomy indexing
  * @{
  * Functions to maintain taxonomy indexing.
@@ -838,6 +1027,20 @@ function taxonomy_autocomplete_validate($element, &$form_state) {
  */
 
 /**
+ * Implements hook_field_presave().
+ *
+ * Create any new terms defined in a freetagging vocabulary.
+ */
+function taxonomy_field_presave(EntityInterface $entity, $field, $instance, $langcode, &$items) {
+  foreach ($items as $delta => $item) {
+    if (!$item['target_id'] && isset($item['target_id'])) {
+      $item['entity']->save();
+      $items[$delta]['target_id'] = $item['entity']->id();
+    }
+  }
+}
+
+/**
  * Implements hook_node_insert().
  */
 function taxonomy_node_insert(EntityInterface $node) {
diff --git a/core/modules/taxonomy/taxonomy.pages.inc b/core/modules/taxonomy/taxonomy.pages.inc
deleted file mode 100644
index 79d4472..0000000
--- a/core/modules/taxonomy/taxonomy.pages.inc
+++ /dev/null
@@ -1,80 +0,0 @@
-<?php
-
-/**
- * @file
- * Page callbacks for the taxonomy module.
- */
-
-use Drupal\taxonomy\Entity\Term;
-
-/**
- * Menu callback; displays all nodes associated with a term.
- *
- * @param \Drupal\taxonomy\Entity\Term $term
- *   The taxonomy term entity.
- *
- * @deprecated Use \Drupal\taxonomy\Controller\TaxonomyController::termPage()
- */
-function taxonomy_term_page(Term $term) {
-  $build['#attached']['drupal_add_feed'][] = array('taxonomy/term/' . $term->id() . '/feed', 'RSS - ' . $term->label());
-
-  foreach ($term->uriRelationships() as $rel) {
-    $uri = $term->uri($rel);
-    // Set the term path as the canonical URL to prevent duplicate content.
-    $build['#attached']['drupal_add_html_head_link'][] = array(
-      array(
-        'rel' => $rel,
-        'href' => url($uri['path'], $uri['options']),
-      ),
-      TRUE,
-    );
-
-    if ($rel == 'canonical') {
-      // Set the non-aliased canonical path as a default shortlink.
-      $build['#attached']['drupal_add_html_head_link'][] = array(
-        array(
-          'rel' => 'shortlink',
-          'href' => url($uri['path'], array_merge($uri['options'], array('alias' => TRUE))),
-        ),
-        TRUE,
-      );
-    }
-  }
-
-  $build['taxonomy_terms'] = taxonomy_term_view_multiple(array($term->id() => $term));
-  if ($nids = taxonomy_select_nodes($term->id(), TRUE, \Drupal::config('node.settings')->get('items_per_page'))) {
-    $nodes = node_load_multiple($nids);
-    $build['nodes'] = node_view_multiple($nodes);
-    $build['pager'] = array(
-      '#theme' => 'pager',
-      '#weight' => 5,
-    );
-  }
-  else {
-    $build['no_content'] = array(
-      '#prefix' => '<p>',
-      '#markup' => t('There is currently no content classified with this term.'),
-      '#suffix' => '</p>',
-    );
-  }
-  return $build;
-}
-
-/**
- * Generate the content feed for a taxonomy term.
- *
- * @param \Drupal\taxonomy\Entity\Term $term
- *   The taxonomy term entity.
- *
- * @deprecated Use \Drupal\taxonomy\Controller\TaxonomyController::termFeed()
- */
-function taxonomy_term_feed(Term $term) {
-  $channel['link'] = url('taxonomy/term/' . $term->id(), array('absolute' => TRUE));
-  $channel['title'] = \Drupal::config('system.site')->get('name') . ' - ' . $term->label();
-  // Only display the description if we have a single term, to avoid clutter and confusion.
-  // HTML will be removed from feed description.
-  $channel['description'] = $term->description->processed;
-  $nids = taxonomy_select_nodes($term->id(), FALSE, \Drupal::config('system.rss')->get('items.limit'));
-
-  return node_feed($nids, $channel);
-}
diff --git a/core/modules/taxonomy/taxonomy.views.inc b/core/modules/taxonomy/taxonomy.views.inc
index d114050..ffa6426 100644
--- a/core/modules/taxonomy/taxonomy.views.inc
+++ b/core/modules/taxonomy/taxonomy.views.inc
@@ -465,29 +465,23 @@ function taxonomy_field_views_data_views_data_alter(array &$data, FieldInterface
 
 /**
  * Helper function to set a breadcrumb for taxonomy.
+ *
+ * @param array &$breadcrumb
+ *   An array reference containing a reference to
+ *   $view->build_info['breadcrumb'].
+ * @param \Drupal\views\Plugin\views\argument\ArgumentPluginBase $argument
+ *   The argument handler instance to get the argument from.
+ *
  */
 function views_taxonomy_set_breadcrumb(&$breadcrumb, &$argument) {
-  if (empty($argument->options['set_breadcrumb'])) {
-    return;
-  }
-
-  $args = $argument->view->args;
-  $parents = taxonomy_get_parents_all($argument->argument);
-  foreach (array_reverse($parents) as $parent) {
-    // Unfortunately parents includes the current argument. Skip.
-    if ($parent->id() == $argument->argument) {
-      continue;
-    }
-    if (!empty($argument->options['use_taxonomy_term_path'])) {
-      $path = $parent->uri();
-      $path = $path['path'];
-    }
-    else {
-      $args[$argument->position] = $parent->id();
-      $path = $argument->view->getUrl($args);
-    }
-    $breadcrumb[$path] = check_plain($parent->label());
+  $breadcrumb = array();
+  $current = taxonomy_term_load($argument->argument);
+  while ($parents = taxonomy_term_load_parents($current->id())) {
+    $current = array_shift($parents);
+    $uri = $current->uri();
+    $breadcrumb[$uri['path']] = $current->label();
   }
+  $breadcrumb = array_reverse($breadcrumb);
 }
 
 /**
diff --git a/core/modules/views/config/views.view.taxonomy_term.yml b/core/modules/views/config/views.view.taxonomy_term.yml
deleted file mode 100644
index 40ca142..0000000
--- a/core/modules/views/config/views.view.taxonomy_term.yml
+++ /dev/null
@@ -1,237 +0,0 @@
-base_field: nid
-base_table: node
-core: '8'
-description: 'Content belonging to a certain taxonomy term.'
-status: false
-display:
-  default:
-    id: default
-    display_title: Master
-    display_plugin: default
-    position: 1
-    display_options:
-      query:
-        type: views_query
-        options:
-          query_comment: false
-          disable_sql_rewrite: false
-          distinct: false
-          slave: false
-          query_tags: {  }
-      access:
-        type: none
-        options: {  }
-      cache:
-        type: none
-        options: {  }
-      exposed_form:
-        type: basic
-        options:
-          submit_button: Apply
-          reset_button: false
-          reset_button_label: Reset
-          exposed_sorts_label: 'Sort by'
-          expose_sort_order: true
-          sort_asc_label: Asc
-          sort_desc_label: Desc
-      pager:
-        type: mini
-        options:
-          items_per_page: 10
-          offset: 0
-          id: 0
-          total_pages: 0
-          expose:
-            items_per_page: '0'
-            items_per_page_label: 'Items per page'
-            items_per_page_options: '5, 10, 20, 40, 60'
-            items_per_page_options_all: false
-            items_per_page_options_all_label: '- All -'
-            offset: false
-            offset_label: Offset
-          tags:
-            previous: ‹‹
-            next: ››
-      sorts:
-        sticky:
-          id: sticky
-          table: node_field_data
-          field: sticky
-          order: DESC
-          plugin_id: standard
-          relationship: none
-          group_type: group
-          admin_label: ''
-          exposed: false
-          expose:
-            label: ''
-          provider: views
-        created:
-          id: created
-          table: node_field_data
-          field: created
-          order: DESC
-          plugin_id: date
-          relationship: none
-          group_type: group
-          admin_label: ''
-          exposed: false
-          expose:
-            label: ''
-          granularity: second
-          provider: views
-      arguments:
-        term_node_tid_depth:
-          id: term_node_tid_depth
-          table: node
-          field: term_node_tid_depth
-          default_action: 'not found'
-          exception:
-            value: all
-            title_enable: true
-            title: All
-          title_enable: true
-          title: '%1'
-          default_argument_type: fixed
-          summary:
-            format: default_summary
-          specify_validation: true
-          validate:
-            type: 'entity:taxonomy_term'
-            fail: 'not found'
-          validate_options:
-            access: '1'
-            operation: view
-            multiple: '1'
-            bundles: {  }
-          depth: '0'
-          break_phrase: true
-          plugin_id: taxonomy_index_tid_depth
-          relationship: none
-          group_type: group
-          admin_label: ''
-          default_argument_options:
-            argument: ''
-          default_argument_skip_url: false
-          summary_options:
-            base_path: ''
-            count: '1'
-            items_per_page: '25'
-            override: false
-          provider: taxonomy
-        term_node_tid_depth_modifier:
-          id: term_node_tid_depth_modifier
-          table: node
-          field: term_node_tid_depth_modifier
-          exception:
-            title_enable: true
-          default_argument_type: fixed
-          summary:
-            format: default_summary
-          specify_validation: true
-          plugin_id: taxonomy_index_tid_depth_modifier
-          relationship: none
-          group_type: group
-          admin_label: ''
-          default_action: ignore
-          title_enable: false
-          title: ''
-          default_argument_options: {  }
-          default_argument_skip_url: false
-          summary_options: {  }
-          validate:
-            type: none
-            fail: 'not found'
-          validate_options: {  }
-          provider: taxonomy
-      filters:
-        status_extra:
-          id: status_extra
-          table: node_field_data
-          field: status_extra
-          group: '0'
-          expose:
-            operator: '0'
-          plugin_id: node_status
-          provider: node
-      style:
-        type: default
-        options:
-          grouping: {  }
-          row_class: ''
-          default_row_class: true
-          row_class_special: true
-          uses_fields: false
-      row:
-        type: 'entity:node'
-        options:
-          view_mode: teaser
-          links: '1'
-          comments: '0'
-      header: {  }
-      footer: {  }
-      empty: {  }
-      relationships: {  }
-      fields: {  }
-  page_1:
-    id: page_1
-    display_title: Page
-    display_plugin: page
-    position: 2
-    display_options:
-      query:
-        type: views_query
-        options: {  }
-      path: taxonomy/term/%
-  feed_1:
-    id: feed_1
-    display_title: Feed
-    display_plugin: feed
-    position: 3
-    display_options:
-      query:
-        type: views_query
-        options: {  }
-      pager:
-        type: full
-        options:
-          items_per_page: 15
-          offset: 0
-          id: 0
-          total_pages: 0
-          expose:
-            items_per_page: '0'
-            items_per_page_label: 'Items per page'
-            items_per_page_options: '5, 10, 20, 40, 60'
-            items_per_page_options_all: false
-            items_per_page_options_all_label: '- All -'
-            offset: false
-            offset_label: Offset
-          tags:
-            previous: '‹ previous'
-            next: 'next ›'
-            first: '« first'
-            last: 'last »'
-          quantity: '9'
-      path: taxonomy/term/%/%/feed
-      displays:
-        page: page
-        default: '0'
-      style:
-        type: rss
-        options:
-          description: ''
-          grouping: {  }
-          uses_fields: '0'
-      row:
-        type: node_rss
-        options:
-          relationship: none
-          item_length: default
-          links: '0'
-label: 'Taxonomy term'
-module: taxonomy
-id: taxonomy_term
-tag: default
-uuid: e0dea92e-a4c9-4442-a518-2499bfe17d73
-langcode: en
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 a84d603..2d1567f 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
@@ -256,8 +256,8 @@ public function alterRoutes(RouteCollection $collection) {
         // We assume that the numeric ids of the parameters match the one from
         // the view argument handlers.
         foreach ($parameters as $position => $parameter_name) {
-          $path = str_replace('arg_' . $position, $parameter_name, $path);
-          $argument_map['arg_' . $position] = $parameter_name;
+          $path = str_replace('{arg_' . $argument_ids[$position] . '}', '{' . $parameter_name . '}', $path);
+          $argument_map['arg_' . $argument_ids[$position]] = $parameter_name;
         }
         // Set the corrected path and the mapping to the route object.
         $route->setDefault('_view_argument_map', $argument_map);
@@ -284,6 +284,18 @@ public function executeHookMenu($callbacks) {
     $this->view->initHandlers();
     $view_arguments = $this->view->argument;
 
+    // Replace % with %views_arg for menu autoloading and add to the
+    // page arguments so the argument actually comes through.
+    foreach ($bits as $pos => $bit) {
+      if ($bit == '%') {
+        $argument = array_shift($view_arguments);
+        if (!empty($argument->options['specify_validation']) && $argument->options['validate']['type'] != 'none') {
+          $bits[$pos] = '%views_arg';
+        }
+        $page_arguments[] = $pos;
+      }
+    }
+
     $path = implode('/', $bits);
 
     $view_route_names = $this->state->get('views.view_route_names') ?: array();
diff --git a/core/modules/views/tests/Drupal/views/Tests/Plugin/display/PathPluginBaseTest.php b/core/modules/views/tests/Drupal/views/Tests/Plugin/display/PathPluginBaseTest.php
index 96ab152..d60d8d4 100644
--- a/core/modules/views/tests/Drupal/views/Tests/Plugin/display/PathPluginBaseTest.php
+++ b/core/modules/views/tests/Drupal/views/Tests/Plugin/display/PathPluginBaseTest.php
@@ -252,6 +252,43 @@ public function testAlterRoutesWithParameters() {
   }
 
   /**
+   * Tests alter routes with optional parameter in the overriding route.
+   */
+  public function testAlterRoutesWithOptionalParameters() {
+    $collection = new RouteCollection();
+    $collection->add('test_route', new Route('test_route/{parameter}', array('_controller' => 'Drupal\Tests\Core\Controller\TestController::content')));
+
+    list($view) = $this->setupViewExecutableAccessPlugin();
+
+    // Manually setup an argument handler.
+    $argument = $this->getMockBuilder('Drupal\views\Plugin\views\argument\ArgumentPluginBase')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $view->argument['test_id'] = $argument;
+    $view->argument['test_id2'] = $argument;
+
+    $display = array();
+    $display['display_plugin'] = 'page';
+    $display['id'] = 'page_1';
+    $display['display_options'] = array(
+      'path' => 'test_route/%',
+    );
+    $this->pathPlugin->initDisplay($view, $display);
+
+    $view_route_names = $this->pathPlugin->alterRoutes($collection);
+    $this->assertEquals(array('test_id.page_1' => 'test_route'), $view_route_names);
+
+    // Ensure that the test_route is overridden.
+    $route = $collection->get('test_route');
+    $this->assertInstanceOf('\Symfony\Component\Routing\Route', $route);
+    $this->assertEquals('test_id', $route->getDefault('view_id'));
+    $this->assertEquals('page_1', $route->getDefault('display_id'));
+    // Ensure that the path did not changed and placeholders are respected.
+    $this->assertEquals('/test_route/{parameter}/{arg_test_id2}', $route->getPath());
+    $this->assertEquals(array('arg_test_id' => 'parameter'), $route->getDefault('_view_argument_map'));
+  }
+
+  /**
    * Returns some mocked view entity, view executable, and access plugin.
    */
   protected function setupViewExecutableAccessPlugin() {
