diff --git a/core/core.services.yml b/core/core.services.yml
index 6134359ddd14998515942ebca4b70b79e8ad9212..6abd05ce92011dacff4b323fe950b5aaa4d92636 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -225,7 +225,6 @@ services:
     arguments: ['@event_dispatcher', '@service_container', '@controller_resolver']
   language_manager:
     class: Drupal\Core\Language\LanguageManager
-    arguments: ['@state', '@module_handler']
   string_translator.custom_strings:
     class: Drupal\Core\StringTranslation\Translator\CustomStrings
     arguments: ['@settings']
@@ -233,6 +232,9 @@ services:
       - { name: string_translator, priority: 30 }
   string_translation:
     class: Drupal\Core\StringTranslation\TranslationManager
+    arguments: ['@language_manager']
+    calls:
+      - [initLanguageManager]
   database.slave:
     class: Drupal\Core\Database\Connection
     factory_class: Drupal\Core\Database\Database
@@ -518,11 +520,6 @@ services:
     tags:
       - { name: event_subscriber }
     arguments: ['@config.storage', '@config.storage.snapshot']
-  language_request_subscriber:
-    class: Drupal\Core\EventSubscriber\LanguageRequestSubscriber
-    tags:
-      - { name: event_subscriber }
-    arguments: ['@language_manager', '@string_translation']
   exception_controller:
     class: Drupal\Core\Controller\ExceptionController
     arguments: ['@content_negotiation', '@string_translation', '@title_resolver', '@html_page_renderer']
diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index 51f4446868e5821ae74fdb78c773fdf41a50c75e..c4b0cb4aac179d99c40a967f95c3e03f6ad74d70 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -2331,7 +2331,7 @@ function drupal_installation_attempted() {
 function drupal_language_initialize() {
   $language_manager = \Drupal::languageManager();
   $language_manager->init();
-  \Drupal::translation()->setDefaultLangcode($language_manager->getLanguage(Language::TYPE_INTERFACE)->id);
+  \Drupal::translation()->setDefaultLangcode($language_manager->getCurrentLanguage()->id);
 }
 
 /**
@@ -2343,47 +2343,10 @@ function drupal_language_initialize() {
  *   The type of language object needed, e.g. Language::TYPE_INTERFACE.
  *
  * @deprecated as of Drupal 8.0. Use
- *   \Drupal::languageManager()->getLanguage($type).
+ *   \Drupal::languageManager()->getCurrentLanguage().
  */
 function language($type) {
-  return \Drupal::languageManager()->getLanguage($type);
-}
-
-/**
- * Returns an array of the available language types.
- *
- * @return array
- *   An array of all language types where the keys of each are the language type
- *   name and its value is its configurability (TRUE/FALSE).
- */
-function language_types_get_all() {
-  $types = \Drupal::config('system.language.types')->get('all');
-  return $types ? $types : array_keys(language_types_get_default());
-}
-
-/**
- * Returns a list of the built-in language types.
- *
- * @return array
- *   An array of key-values pairs where the key is the language type name and
- *   the value is its configurability (TRUE/FALSE).
- */
-function language_types_get_default() {
-  return array(
-    Language::TYPE_INTERFACE => TRUE,
-    Language::TYPE_CONTENT => FALSE,
-    Language::TYPE_URL => FALSE,
-  );
-}
-
-/**
- * Returns TRUE if there is more than one language enabled.
- *
- * @return bool
- *   TRUE if more than one language is enabled.
- */
-function language_multilingual() {
-  return \Drupal::languageManager()->isMultilingual();
+  return \Drupal::languageManager()->getCurrentLanguage($type);
 }
 
 /**
@@ -2397,101 +2360,12 @@ function language_multilingual() {
  * @return array
  *   An associative array of languages, keyed by the language code, ordered by
  *   weight ascending and name ascending.
- */
-function language_list($flags = Language::STATE_CONFIGURABLE) {
-
-  $languages = &drupal_static(__FUNCTION__);
-
-  // Initialize master language list.
-  if (!isset($languages)) {
-    // Initialize local language list cache.
-    $languages = array();
-
-    // Fill in master language list based on current configuration.
-    $default = language_default();
-    if (language_multilingual() || \Drupal::moduleHandler()->moduleExists('language')) {
-      // Use language module configuration if available.
-      $language_entities = config_get_storage_names_with_prefix('language.entity.');
-
-      // Initialize default property so callers have an easy reference and can
-      // save the same object without data loss.
-      foreach ($language_entities as $langcode_config_name) {
-        $langcode = substr($langcode_config_name, strlen('language.entity.'));
-        $info = \Drupal::config($langcode_config_name)->get();
-        $languages[$langcode] = new Language(array(
-          'default' => ($info['id'] == $default->id),
-          'name' => $info['label'],
-          'id' => $info['id'],
-          'direction' => $info['direction'],
-          'locked' => $info['locked'],
-          'weight' => $info['weight'],
-        ));
-      }
-      Language::sort($languages);
-    }
-    // If the language module is enabled but the configuration has not been
-    // written yet, returning an empty language list will cause errors. For
-    // example the cache clear in search_module_preinstall().
-    if (empty($languages)) {
-      // No language module, so use the default language only.
-      $languages = array($default->id => $default);
-      // Add the special languages, they will be filtered later if needed.
-      $languages += language_default_locked_languages($default->weight);
-    }
-  }
-
-  // Filter the full list of languages based on the value of the $all flag. By
-  // default we remove the locked languages, but the caller may request for
-  // those languages to be added as well.
-  $filtered_languages = array();
-
-  // Add the site's default language if flagged as allowed value.
-  if ($flags & Language::STATE_SITE_DEFAULT) {
-    $default = isset($default) ? $default : language_default();
-    // Rename the default language.
-    $default->name = t("Site's default language (@lang_name)", array('@lang_name' => $default->name));
-    $filtered_languages['site_default'] = $default;
-  }
-
-  foreach ($languages as $langcode => $language) {
-    if (($language->locked && !($flags & Language::STATE_LOCKED)) || (!$language->locked && !($flags & Language::STATE_CONFIGURABLE))) {
-      continue;
-     }
-    $filtered_languages[$langcode] = $language;
-  }
-
-  return $filtered_languages;
-}
-
-/**
- * Returns a list of the default locked languages.
- *
- * @param int $weight
- *   An integer value that is used as the start value for the weights of the
- *   locked languages.
  *
- * @return array
- *   An array of language objects.
+ * @deprecated as of Drupal 8.0. Use
+ *   \Drupal::languageManager()->getLanguages() instead.
  */
-function language_default_locked_languages($weight = 0) {
-  $locked_language = array(
-    'default' => FALSE,
-    'locked' => TRUE,
-    'enabled' => TRUE,
-  );
-
-  $languages = array();
-  $languages[Language::LANGCODE_NOT_SPECIFIED] = new Language(array(
-    'id' => Language::LANGCODE_NOT_SPECIFIED,
-    'name' => t('Not specified'),
-    'weight' => ++$weight,
-  ) + $locked_language);
-  $languages[Language::LANGCODE_NOT_APPLICABLE] = new Language(array(
-    'id' => Language::LANGCODE_NOT_APPLICABLE,
-    'name' => t('Not applicable'),
-    'weight' => ++$weight,
-  ) + $locked_language);
-  return $languages;
+function language_list($flags = Language::STATE_CONFIGURABLE) {
+  return \Drupal::languageManager()->getLanguages($flags);
 }
 
 /**
@@ -2502,47 +2376,14 @@ function language_default_locked_languages($weight = 0) {
  *
  * @return \Drupal\core\Language\Language|null
  *   A fully-populated language object or NULL.
- */
-function language_load($langcode) {
-  $languages = language_list(Language::STATE_ALL);
-  return isset($languages[$langcode]) ? $languages[$langcode] : NULL;
-}
-
-/**
- * Produced the printed name for a language for display.
  *
- * @param string $langcode
- *   The language code.
+ * @see \Drupal\Core\Language\LanguageManager::getLanguage()
  *
- * @return string
- *   The printed name of the language.
+ * @deprecated as of Drupal 8.0. Use \Drupal::languageManager()->getLanguage()
+ *   instead.
  */
-function language_name($langcode) {
-  if ($langcode == Language::LANGCODE_NOT_SPECIFIED) {
-    return t('None');
-  }
-
-  if ($language = language_load($langcode)) {
-    return $language->name;
-  }
-  if (empty($langcode)) {
-    return t('Unknown');
-  }
-  return t('Unknown (@langcode)', array('@langcode' => $langcode));
-}
-
-/**
- * Checks if a language is locked.
- *
- * @param string $langcode
- *   The language code.
- *
- * @return bool
- *   Returns whether the language is locked.
- */
-function language_is_locked($langcode) {
-  $language = language_load($langcode);
-  return ($language ? $language->locked : FALSE);
+function language_load($langcode) {
+  return \Drupal::languageManager()->getLanguage($langcode);
 }
 
 /**
@@ -2550,35 +2391,12 @@ function language_is_locked($langcode) {
  *
  * @return \Drupal\Core\Language\Language
  *   A language object.
- */
-function language_default() {
-  $info = variable_get('language_default', array(
-    'id' => 'en',
-    'name' => 'English',
-    'direction' => 0,
-    'weight' => 0,
-    'locked' => 0,
-  ));
-  $info['default'] = TRUE;
-  return new Language($info);
-}
-
-/**
- * Stores or retrieves the path derived during language negotiation.
  *
- * @param string $new_path
- *   The altered path.
- *
- * @todo Replace this with a path processor in language module. See
- *   http://drupal.org/node/1888424.
+ * @deprecated as of Drupal 8.0. Use
+ *   \Drupal::languageManager()->getDefaultLanguage() instead.
  */
-function _language_resolved_path($new_path = NULL) {
-  $path = &drupal_static(__FUNCTION__, NULL);
-  if ($new_path === NULL) {
-    return $path;
-  }
-  $path = $new_path;
-  return $path;
+function language_default() {
+  return \Drupal::languageManager()->getDefaultLanguage();
 }
 
 /**
diff --git a/core/includes/common.inc b/core/includes/common.inc
index 9d03679746c4eff70772cb89ecb6dc3bed18059f..84e5edb9fb36728116979cf84f110bad795dc550 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -4531,11 +4531,12 @@ function drupal_render_cid_parts($granularity = NULL) {
   global $theme, $base_root, $user;
 
   $cid_parts[] = $theme;
-  // If Locale is enabled but we have only one language we do not need it as cid
-  // part.
-  if (language_multilingual()) {
-    foreach (language_types_get_configurable() as $language_type) {
-      $cid_parts[] = language($language_type)->id;
+
+  // If we have only one language enabled we do not need it as cid part.
+  $language_manager = \Drupal::languageManager();
+  if ($language_manager->isMultilingual()) {
+    foreach ($language_manager->getLanguageTypes() as $type) {
+      $cid_parts[] = $language_manager->getCurrentLanguage($type)->id;
     }
   }
 
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 6dfc2700e3b004c99f534a781370a09cfd5b0273..5a46c8cb45cd32fb52d618552e47af8a33084c85 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -1,5 +1,6 @@
 <?php
 
+use Drupal\Component\Utility\UserAgent;
 use Drupal\Component\Utility\Crypt;
 
 use Drupal\Component\Utility\Settings;
@@ -300,6 +301,9 @@ function install_begin_request(&$install_state) {
     exit;
   }
 
+  // Register the 'language_manager' service.
+  $container->register('language_manager', 'Drupal\Core\Language\LanguageManager');
+
   // If we have a language selected and it is not yet saved in the system
   // (eg. pre-database data screens we are unable to persistently store
   // the default language), we should set language_default so the proper
@@ -370,7 +374,6 @@ function install_begin_request(&$install_state) {
   else {
     // @todo Move into a proper Drupal\Core\DependencyInjection\InstallContainerBuilder.
     $container = new ContainerBuilder();
-
     $container->register('event_dispatcher', 'Symfony\Component\EventDispatcher\EventDispatcher');
 
     $container->register('config.storage', 'Drupal\Core\Config\InstallStorage');
@@ -388,9 +391,7 @@ function install_begin_request(&$install_state) {
       ->addArgument(new Reference('config.typed'));
 
     // Register the 'language_manager' service.
-    $container
-      ->register('language_manager', 'Drupal\Core\Language\LanguageManager')
-      ->addArgument(NULL);
+    $container->register('language_manager', 'Drupal\Core\Language\LanguageManager');
 
     // Register the translation services.
     install_register_translation_service($container);
@@ -1505,6 +1506,7 @@ function install_register_translation_service(ContainerBuilder $container) {
   $container->register('string_translator.custom_strings', 'Drupal\Core\StringTranslation\Translator\CustomStrings')
     ->addArgument(settings());
   $container->register('string_translation', 'Drupal\Core\StringTranslation\TranslationManager')
+    ->addArgument(new Reference('language_manager'))
     ->addMethodCall('addTranslator', array(new Reference('string_translator.file_translation')))
     ->addMethodCall('addTranslator', array(new Reference('string_translator.custom_strings')));
 }
@@ -1582,9 +1584,6 @@ function install_select_language(&$install_state) {
  * @ingroup forms
  */
 function install_select_language_form($form, &$form_state, $files = array()) {
-  include_once __DIR__ . '/../modules/language/language.module';
-  include_once __DIR__ . '/../modules/language/language.negotiation.inc';
-
   $standard_languages = LanguageManager::getStandardLanguageList();
   $select_options = array();
   $browser_options = array();
@@ -1596,22 +1595,19 @@ function install_select_language_form($form, &$form_state, $files = array()) {
     // Select lists based on available language files.
     foreach ($files as $langcode => $uri) {
       $select_options[$langcode] = isset($standard_languages[$langcode]) ? $standard_languages[$langcode][1] : $langcode;
-      $browser_options[$langcode] = new Language(array(
-        'id' => $langcode,
-      ));
+      $browser_options[] = $langcode;
     }
   }
   else {
     // Select lists based on all standard languages.
     foreach ($standard_languages as $langcode => $language_names) {
       $select_options[$langcode] = $language_names[1];
-      $browser_options[$langcode] = new Language(array(
-        'id' => $langcode,
-      ));
+      $browser_options[] = $langcode;
     }
   }
 
-  $browser_langcode = language_from_browser($browser_options);
+  $request = Request::createFromGlobals();
+  $browser_langcode = UserAgent::getBestMatchingLangcode($request->server->get('HTTP_ACCEPT_LANGUAGE'), $browser_options);
   $form['langcode'] = array(
     '#type' => 'select',
     '#title' => t('Choose language'),
diff --git a/core/includes/language.inc b/core/includes/language.inc
deleted file mode 100644
index 618e3a700280096121d910a9af71126b0904375b..0000000000000000000000000000000000000000
--- a/core/includes/language.inc
+++ /dev/null
@@ -1,551 +0,0 @@
-<?php
-
-/**
- * @file
- * Language Negotiation API.
- *
- * @see http://drupal.org/node/1497272
- */
-
-use Drupal\Core\Language\Language;
-
-/**
- * No language negotiation. The default language is used.
- */
-const LANGUAGE_NEGOTIATION_SELECTED = 'language-selected';
-
-/**
- * The language is determined using the current interface language.
- */
-const LANGUAGE_NEGOTIATION_INTERFACE = 'language-interface';
-
-/**
- * @defgroup language_negotiation Language Negotiation API functionality
- * @{
- * Functions to customize the language types and the negotiation process.
- *
- * The language negotiation API is based on two major concepts:
- * - Language types: types of translatable data (the types of data that a user
- *   can view or request).
- * - Language negotiation methods: functions for determining which language to
- *   use to present a particular piece of data to the user.
- * Both language types and language negotiation methods are customizable.
- *
- * Drupal defines three built-in language types:
- * - Interface language: The page's main language, used to present translated
- *   user interface elements such as titles, labels, help text, and messages.
- * - Content language: The language used to present content that is available
- *   in more than one language (see
- *   @link field_language Field Language API @endlink for details).
- * - URL language: The language associated with URLs. When generating a URL,
- *   this value will be used by url() as a default if no explicit preference is
- *   provided.
- * Modules can define additional language types through
- * hook_language_types_info(), and alter existing language type definitions
- * through hook_language_types_info_alter().
- *
- * Language types may be configurable or fixed. The language negotiation
- * methods associated with a configurable language type can be explicitly
- * set through the user interface. A fixed language type has predetermined
- * (module-defined) language negotiation settings and, thus, does not appear in
- * the configuration page. Here is a code snippet that makes the content
- * language (which by default inherits the interface language's values)
- * configurable:
- * @code
- * function mymodule_language_types_info_alter(&$language_types) {
- *   unset($language_types[Language::TYPE_CONTENT]['fixed']);
- * }
- * @endcode
- *
- * The locked configuration property prevents one language type from being
- * switched from customized to not customized, and vice versa.
- * @see language_types_set()
- *
- * Every language type can have a different set of language negotiation methods
- * assigned to it. Different language types often share the same language
- * negotiation settings, but they can have independent settings if needed. If
- * two language types are configured the same way, their language switcher
- * configuration will be functionally identical and the same settings will act
- * on both language types.
- *
- * Drupal defines the following built-in language negotiation methods:
- * - URL: Determine the language from the URL (path prefix or domain).
- * - Session: Determine the language from a request/session parameter.
- * - User: Follow the user's language preference.
- * - Browser: Determine the language from the browser's language settings.
- * - Default language: Use the default site language.
- * Language negotiation methods are simple callback functions that implement a
- * particular logic to return a language code. For instance, the URL method
- * searches for a valid path prefix or domain name in the current request URL.
- * If a language negotiation method does not return a valid language code, the
- * next method associated to the language type (based on method weight) is
- * invoked.
- *
- * Modules can define additional language negotiation methods through
- * hook_language_negotiation_info(), and alter existing methods through
- * hook_language_negotiation_info_alter(). Here is an example snippet that lets
- * path prefixes be ignored for administrative paths:
- * @code
- * function mymodule_language_negotiation_info_alter(&$negotiation_info) {
- *   // Replace the core function with our own function.
- *   module_load_include('language', 'inc', 'language.negotiation');
- *   $negotiation_info[LANGUAGE_NEGOTIATION_URL]['callbacks']['negotiation'] = 'mymodule_from_url';
- *   $negotiation_info[LANGUAGE_NEGOTIATION_URL]['file'] = drupal_get_path('module', 'mymodule') . '/mymodule.module';
- * }
- *
- * function mymodule_from_url($languages) {
- *   // Use the core URL language negotiation method to get a valid language
- *   // code.
- *   module_load_include('language', 'inc', 'language.negotiation');
- *   $langcode = language_from_url($languages);
- *
- *   // If we are on an administrative path, override with the default language.
- *   $query = \Drupal::request()->query;
- *   if ($query->has('q') && strtok($query->get('q'), '/') == 'admin') {
- *     return language_default()->id;
- *   }
- *   return $langcode;
- * }
- * ?>
- * @endcode
- *
- * For more information, see
- * @link http://drupal.org/node/1497272 Language Negotiation API @endlink
- */
-
-/**
- * Chooses a language based on language negotiation method settings.
- *
- * @param $type
- *   The language type key to find the language for.
- *
- * @param $request
- *   The HttpReqeust object representing the current request.
- *
- * @return
- *   The negotiated language object.
- */
-function language_types_initialize($type, $request = NULL) {
-  // Execute the language negotiation methods in the order they were set up and
-  // return the first valid language found.
-  $negotiation = variable_get("language_negotiation_$type", array());
-
-  foreach ($negotiation as $method_id => $method) {
-    // Skip negotiation methods not appropriate for this type.
-    if (isset($method['types']) && !in_array($type, $method['types'])) {
-      continue;
-    }
-    $language = language_negotiation_method_invoke($method_id, $method, $request);
-    if ($language) {
-      // Remember the method ID used to detect the language.
-      $language->method_id = $method_id;
-      return $language;
-    }
-  }
-
-  // If no other language was found use the default one.
-  $language = language_default();
-  $language->method_id = LANGUAGE_NEGOTIATION_SELECTED;
-  return $language;
-}
-
-/**
- * Returns information about all defined language types.
- *
- * @return
- *   An associative array of language type information arrays keyed by type
- *   names. Based on information from hook_language_types_info().
- *
- * @see hook_language_types_info().
- */
-function language_types_info() {
-  $language_types = &drupal_static(__FUNCTION__);
-
-  if (!isset($language_types)) {
-    $language_types = \Drupal::moduleHandler()->invokeAll('language_types_info');
-    // Let other modules alter the list of language types.
-    drupal_alter('language_types_info', $language_types);
-  }
-
-  return $language_types;
-}
-
-/**
- * Returns only the configurable language types.
- *
- * A language type maybe configurable or fixed. A fixed language type is a type
- * whose language negotiation methods are module-defined and not altered through
- * the user interface.
- *
- * @return
- *   An array of language type names.
- */
-function language_types_get_configurable() {
-  $configurable = \Drupal::config('system.language.types')->get('configurable');
-  return $configurable ? $configurable : array();
-}
-
-/**
- * Disables the given language types.
- *
- * @param $types
- *   An array of language types.
- */
-function language_types_disable($types) {
-  $configurable = language_types_get_configurable();
-  \Drupal::config('system.language.types')->set('configurable', array_diff($configurable, $types))->save();
-}
-
-/**
- * Updates the language type configuration.
- *
- * @param array $configurable_language_types
- *   An array of configurable language types.
- */
-function language_types_set(array $configurable_language_types) {
-  // Ensure that we are getting the defined language negotiation information. An
-  // invocation of \Drupal\Core\Extension\ModuleHandler::install() or
-  // \Drupal\Core\Extension\ModuleHandler::uninstall() could invalidate the
-  // cached information.
-  drupal_static_reset('language_types_info');
-  drupal_static_reset('language_negotiation_info');
-
-  $language_types = array();
-  $negotiation_info = language_negotiation_info();
-  $language_types_info = language_types_info();
-
-  foreach ($language_types_info as $type => $info) {
-    $configurable = in_array($type, $configurable_language_types);
-
-    // Check whether the language type is unlocked. Only the status of unlocked
-    // language types can be toggled between configurable and non-configurable.
-    // The default language negotiation settings, if available, are stored in
-    // $info['fixed'].
-    if (empty($info['locked'])) {
-      // If we have a non-locked non-configurable language type without default
-      // language negotiation settings, we use the values negotiated for the
-      // interface language which should always be available.
-      if (!$configurable && !empty($info['fixed'])) {
-        $method_weights = array(LANGUAGE_NEGOTIATION_INTERFACE);
-        $method_weights = array_flip($method_weights);
-        language_negotiation_set($type, $method_weights);
-      }
-    }
-    else {
-      // Locked language types with default settings are always considered
-      // non-configurable. In turn if default settings are missing, the language
-      // type is always considered configurable.
-      $configurable = empty($info['fixed']);
-
-      // If the language is non-configurable we need to store its language
-      // negotiation settings.
-      if (!$configurable) {
-        $method_weights = array();
-        foreach ($info['fixed'] as $weight => $method_id) {
-          if (isset($negotiation_info[$method_id])) {
-            $method_weights[$method_id] = $weight;
-          }
-        }
-        language_negotiation_set($type, $method_weights);
-      }
-    }
-
-    $language_types[$type] = $configurable;
-  }
-
-  // Store the language type configuration.
-  $config = \Drupal::config('system.language.types');
-  $config->set('configurable', array_keys(array_filter($language_types)))->save();
-  $config->set('all', array_keys($language_types))->save();
-
-  // Ensure that subsequent calls of language_types_get_configurable() return
-  // the updated language type information.
-  drupal_static_reset('language_types_get_configurable');
-}
-
-/**
- * Returns the ID of the language type's first language negotiation method.
- *
- * @param $type
- *   The language type.
- *
- * @return
- *   The identifier of the first language negotiation method for the given
- *   language type, or the default method if none exists.
- */
-function language_negotiation_method_get_first($type) {
-  $negotiation = variable_get("language_negotiation_$type", array());
-  return empty($negotiation) ? LANGUAGE_NEGOTIATION_SELECTED : key($negotiation);
-}
-
-/**
- * Checks whether a language negotiation method is enabled for a language type.
- *
- * @param $method_id
- *   The language negotiation method ID.
- * @param $type
- *   (optional) The language type. If none is passed, all the configurable
- *   language types will be inspected.
- *
- * @return
- *   TRUE if the method is enabled for at least one of the given language
- *   types, or FALSE otherwise.
- */
-function language_negotiation_method_enabled($method_id, $type = NULL) {
-  $language_types = !empty($type) ? array($type) : language_types_get_configurable();
-
-  foreach ($language_types as $type) {
-    $negotiation = variable_get("language_negotiation_$type", array());
-    if (isset($negotiation[$method_id])) {
-      return TRUE;
-    }
-  }
-
-  return FALSE;
-}
-
-/**
- * Returns the language switch links for the given language type.
- *
- * @param $type
- *   The language type.
- * @param $path
- *   The internal path the switch links will be relative to.
- *
- * @return
- *   A keyed array of links ready to be themed.
- */
-function language_negotiation_get_switch_links($type, $path) {
-  $links = FALSE;
-  $negotiation = variable_get("language_negotiation_$type", array());
-
-  foreach ($negotiation as $method_id => $method) {
-    if (isset($method['callbacks']['language_switch'])) {
-      if (isset($method['file'])) {
-        require_once DRUPAL_ROOT . '/' . $method['file'];
-      }
-
-      $callback = $method['callbacks']['language_switch'];
-      $result = $callback($type, $path);
-
-      if (!empty($result)) {
-        // Allow modules to provide translations for specific links.
-        drupal_alter('language_switch_links', $result, $type, $path);
-        $links = (object) array('links' => $result, 'method_id' => $method_id);
-        break;
-      }
-    }
-  }
-
-  return $links;
-}
-
-/**
- * Removes any language negotiation methods that are no longer defined.
- */
-function language_negotiation_purge() {
-  // Ensure that we are getting the defined language negotiation information. An
-  // invocation of \Drupal\Core\Extension\ModuleHandler::install() or
-  // \Drupal\Core\Extension\ModuleHandler::uninstall() could invalidate the
-  // cached information.
-  drupal_static_reset('language_negotiation_info');
-  drupal_static_reset('language_types_info');
-
-  $negotiation_info = language_negotiation_info();
-  foreach (language_types_info() as $type => $type_info) {
-    $weight = 0;
-    $method_weights = array();
-    foreach (variable_get("language_negotiation_$type", array()) as $method_id => $method) {
-      if (isset($negotiation_info[$method_id])) {
-        $method_weights[$method_id] = $weight++;
-      }
-    }
-    language_negotiation_set($type, $method_weights);
-  }
-}
-
-/**
- * Saves a list of language negotiation methods for a language type.
- *
- * @param $type
- *   The language type.
- * @param $method_weights
- *   An array of language negotiation method weights keyed by method ID.
- */
-function language_negotiation_set($type, $method_weights) {
-  // Save only the necessary fields.
-  $method_fields = array('callbacks', 'file', 'cache');
-
-  $negotiation = array();
-  $negotiation_info = language_negotiation_info();
-  $default_types = language_types_get_configurable();
-
-  // Order the language negotiation method list by weight.
-  asort($method_weights);
-
-  foreach ($method_weights as $method_id => $weight) {
-    if (isset($negotiation_info[$method_id])) {
-      $method = $negotiation_info[$method_id];
-      // If the language negotiation method does not express any preference
-      // about types, make it available for any configurable type.
-      $types = array_flip(isset($method['types']) ? $method['types'] : $default_types);
-      // Check whether the method is defined and has the right type.
-      if (isset($types[$type])) {
-        $method_data = array();
-        foreach ($method_fields as $field) {
-          if (isset($method[$field])) {
-            $method_data[$field] = $method[$field];
-          }
-        }
-        $negotiation[$method_id] = $method_data;
-      }
-    }
-  }
-
-  variable_set("language_negotiation_$type", $negotiation);
-}
-
-/**
- * Returns all defined language negotiation methods.
- *
- * @return
- *   An array of language negotiation methods.
- */
-function language_negotiation_info() {
-  $negotiation_info = &drupal_static(__FUNCTION__);
-
-  if (!isset($negotiation_info)) {
-    // Collect all the module-defined language negotiation methods.
-    $negotiation_info = \Drupal::moduleHandler()->invokeAll('language_negotiation_info');
-    $languages = language_list();
-    $selected_language = $languages[language_from_selected($languages)];
-    $description = 'Language based on a selected language. ';
-    $description .= ($selected_language->id == language_default()->id) ? "(Site's default language (@language_name))" : '(@language_name)';
-    // Add the default language negotiation method.
-    $negotiation_info[LANGUAGE_NEGOTIATION_SELECTED] = array(
-      'callbacks' => array(
-        'negotiation' => 'language_from_selected',
-      ),
-      'weight' => 12,
-      'name' => t('Selected language'),
-      'description' => t($description, array('@language_name' => $selected_language->name)),
-      'config' => 'admin/config/regional/language/detection/selected',
-    );
-
-     // Let other modules alter the list of language negotiation methods.
-     drupal_alter('language_negotiation_info', $negotiation_info);
-  }
-
-  return $negotiation_info;
-}
-
-/**
- * Invokes a language negotiation method and caches the results.
- *
- * @param $method_id
- *   The language negotiation method's identifier.
- * @param $method
- *   (optional) An associative array of information about the method to be
- *   invoked (see hook_language_negotiation_info() for details). If not passed
- *   in, it will be loaded through language_negotiation_info().
- *
- * @param $request
- *   (optional) The HttpRequest object representing the current request.
- *
- * @return
- *   A language object representing the language chosen by the method.
- */
-function language_negotiation_method_invoke($method_id, $method = NULL, $request = NULL) {
-  $results = &drupal_static(__FUNCTION__);
-
-  if (!isset($results[$method_id])) {
-    global $user;
-
-    $languages = language_list();
-
-    if (!isset($method)) {
-      $negotiation_info = language_negotiation_info();
-      $method = $negotiation_info[$method_id];
-    }
-
-    if (isset($method['file'])) {
-      require_once DRUPAL_ROOT . '/' . $method['file'];
-    }
-    // Check for a cache mode force from settings.php.
-    if (settings()->get('page_cache_without_database')) {
-      $cache_enabled = TRUE;
-    }
-    else {
-      drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES, FALSE);
-      $config = \Drupal::config('system.performance');
-      $cache_enabled = $config->get('cache.page.use_internal');
-    }
-    // If the language negotiation method has no cache preference or this is
-    // satisfied we can execute the callback.
-    $cache = !isset($method['cache']) || $user->isAuthenticated() || $method['cache'] == $cache_enabled;
-    $callback = isset($method['callbacks']['negotiation']) ? $method['callbacks']['negotiation'] : FALSE;
-    $langcode = $cache && function_exists($callback) ? $callback($languages, $request) : FALSE;
-    $results[$method_id] = isset($languages[$langcode]) ? $languages[$langcode] : FALSE;
-  }
-
-  // Since objects are resources, we need to return a clone to prevent the
-  // language negotiation method cache from being unintentionally altered. The
-  // same methods might be used with different language types based on
-  // configuration.
-  return !empty($results[$method_id]) ? clone($results[$method_id]) : $results[$method_id];
-}
-
- /**
-  * Identifies language from configuration.
-  *
-  * @param $languages
-  *   An array of valid language objects.
-  *
-  * @return
-  *   A valid language code on success, FALSE otherwise.
-  */
-function language_from_selected($languages) {
-  $langcode = (string) \Drupal::config('language.negotiation')->get('selected_langcode');
-  // Replace the site's default langcode by its real value.
-  if ($langcode == 'site_default') {
-    $langcode = language_default()->id;
-  }
-  return isset($languages[$langcode]) ? $langcode : language_default()->id;
-}
-
-/**
- * Splits the given path into prefix and actual path.
- *
- * Parse the given path and return the language object identified by the prefix
- * and the actual path.
- *
- * @param $path
- *   The path to split.
- * @param $languages
- *   An array of valid languages.
- *
- * @return
- *   An array composed of:
- *    - A language object corresponding to the identified prefix on success,
- *      FALSE otherwise.
- *    - The path without the prefix on success, the given path otherwise.
- */
-function language_url_split_prefix($path, $languages) {
-  $args = empty($path) ? array() : explode('/', $path);
-  $prefix = array_shift($args);
-
-  // Search prefix within enabled languages.
-  $prefixes = language_negotiation_url_prefixes();
-  foreach ($languages as $language) {
-    if (isset($prefixes[$language->id]) && $prefixes[$language->id] == $prefix) {
-      // Rebuild $path with the language removed.
-      return array($language, implode('/', $args));
-    }
-  }
-
-  return array(FALSE, $path);
-}
-
-/**
- * @} End of "language_negotiation"
- */
diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 676f2fa4793faa288ab5c53de33af6ae8d23fbfc..5ca4d61cdceeead115931ac2635d515e1c9d1896 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -1235,7 +1235,7 @@ function theme_links($variables) {
     $output .= '<ul' . new Attribute($attributes) . '>';
 
     $active = \Drupal::linkGenerator()->getActive();
-    $language_url = \Drupal::languageManager()->getLanguage(Language::TYPE_URL);
+    $language_url = \Drupal::languageManager()->getCurrentLanguage(Language::TYPE_URL);
 
     foreach ($links as $key => $link) {
       $link += array(
@@ -2072,7 +2072,7 @@ function template_preprocess_html(&$variables) {
   $variables['html_attributes'] = new Attribute;
 
   // HTML element attributes.
-  $language_interface = \Drupal::service('language_manager')->getLanguage();
+  $language_interface = \Drupal::service('language_manager')->getCurrentLanguage();
   $variables['html_attributes']['lang'] = $language_interface->id;
   $variables['html_attributes']['dir'] = $language_interface->direction ? 'rtl' : 'ltr';
 
diff --git a/core/includes/update.inc b/core/includes/update.inc
index 24f65832fde3663e21e6a872b9593766c8ead89e..c45e01391421e4fb1ae18d767f76a7b4beb22dff 100644
--- a/core/includes/update.inc
+++ b/core/includes/update.inc
@@ -478,9 +478,7 @@ function update_prepare_stored_includes() {
   foreach ($language_types as $language_type) {
     $negotiation = update_variable_get("language_negotiation_$language_type", array());
     foreach ($negotiation as &$method) {
-      if (isset($method['file']) && $method['file'] == 'includes/locale.inc') {
-        $method['file'] = 'core/modules/language/language.negotiation.inc';
-      }
+      unset($method['file']);
     }
     update_variable_set("language_negotiation_$language_type", $negotiation);
   }
@@ -525,9 +523,6 @@ function update_prepare_d8_language() {
     db_drop_field('languages', 'native');
     db_drop_field('languages', 'enabled');
 
-    // Update language count.
-    \Drupal::state()->set('language_count', db_query('SELECT COUNT(language) FROM {languages}')->fetchField());
-
     // Rename the languages table to language.
     db_rename_table('languages', 'language');
 
@@ -556,7 +551,7 @@ function update_prepare_d8_language() {
       db_add_field('language', 'locked', $locked_spec);
 
       $max_language_weight = db_query('SELECT MAX(weight) FROM {language}')->fetchField();
-      $languages = language_default_locked_languages($max_language_weight);
+      $languages = \Drupal::languageManager()->getDefaultLockedLanguages($max_language_weight);
       foreach ($languages as $language) {
         db_insert('language')
           ->fields(array(
@@ -1640,7 +1635,7 @@ function update_language_list($flags = Language::STATE_CONFIGURABLE) {
 
     // Fill in master language list based on current configuration.
     $default = language_default();
-    if (language_multilingual() || \Drupal::moduleHandler()->moduleExists('language')) {
+    if (\Drupal::languageManager()->isMultilingual() || \Drupal::moduleHandler()->moduleExists('language')) {
       // Use language module configuration if available. We can not use
       // entity_load_multiple() because this breaks during updates.
       $language_entities = config_get_storage_names_with_prefix('language.entity.');
@@ -1665,7 +1660,7 @@ function update_language_list($flags = Language::STATE_CONFIGURABLE) {
       // No language module, so use the default language only.
       $languages = array($default->id => $default);
       // Add the special languages, they will be filtered later if needed.
-      $languages += language_default_locked_languages($default->weight);
+      $languages += \Drupal::languageManager()->getDefaultLockedLanguages($default->weight);
     }
   }
 
diff --git a/core/lib/Drupal.php b/core/lib/Drupal.php
index 6af2411f1665feedfa20280b989f7ecfb6628b3d..0ec88d7dac579eaf13d0521a84f96c26aa259f75 100644
--- a/core/lib/Drupal.php
+++ b/core/lib/Drupal.php
@@ -533,7 +533,7 @@ public static function translation() {
   /**
    * Returns the language manager service.
    *
-   * @return \Drupal\Core\Language\LanguageManager
+   * @return \Drupal\Core\Language\LanguageManagerInterface
    *   The language manager.
    */
   public static function languageManager() {
diff --git a/core/lib/Drupal/Component/Utility/UserAgent.php b/core/lib/Drupal/Component/Utility/UserAgent.php
new file mode 100644
index 0000000000000000000000000000000000000000..361b057ce9f19970a44af597861419ba9f82903b
--- /dev/null
+++ b/core/lib/Drupal/Component/Utility/UserAgent.php
@@ -0,0 +1,142 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Component\Utility\UserAgent.
+ */
+
+namespace Drupal\Component\Utility;
+
+/**
+ * Provides user agent related utility functions.
+ */
+class UserAgent {
+
+  /**
+   * Identifies user agent language from the Accept-language HTTP header.
+   *
+   * The algorithm works as follows:
+   * - map user agent language codes to available language codes.
+   * - order all user agent language codes by qvalue from high to low.
+   * - add generic user agent language codes if they aren't already specified
+   *   but with a slightly lower qvalue.
+   * - find the most specific available language code with the highest qvalue.
+   * - if 2 or more languages are having the same qvalue, respect the order of
+   *   them inside the $languages array.
+   *
+   * We perform user agent accept-language parsing only if page cache is
+   * disabled, otherwise we would cache a user-specific preference.
+   *
+   * @param string $http_accept_language
+   *   The value of the "Accept-Language" HTTP header.
+   * @param array $langcodes
+   *   An array of available language codes to pick from.
+   * @param array $mappings
+   *   (optional) Custom mappings to support user agents that are sending non
+   *   standard language codes. No mapping is assumed by default.
+   *
+   * @return string
+   *   The selected language code or FALSE if no valid language can be
+   *   identified.
+   */
+  public static function getBestMatchingLangcode($http_accept_language, $langcodes, $mappings = array()) {
+    // The Accept-Language header contains information about the language
+    // preferences configured in the user's user agent / operating system.
+    // RFC 2616 (section 14.4) defines the Accept-Language header as follows:
+    //   Accept-Language = "Accept-Language" ":"
+    //                  1#( language-range [ ";" "q" "=" qvalue ] )
+    //   language-range  = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" )
+    // Samples: "hu, en-us;q=0.66, en;q=0.33", "hu,en-us;q=0.5"
+    $ua_langcodes = array();
+    if (preg_match_all('@(?<=[, ]|^)([a-zA-Z-]+|\*)(?:;q=([0-9.]+))?(?:$|\s*,\s*)@', trim($http_accept_language), $matches, PREG_SET_ORDER)) {
+      foreach ($matches as $match) {
+        if ($mappings) {
+          $langcode = strtolower($match[1]);
+          foreach ($mappings as $ua_langcode => $standard_langcode) {
+            if ($langcode == $ua_langcode) {
+              $match[1] = $standard_langcode;
+            }
+          }
+        }
+        // We can safely use strtolower() here, tags are ASCII.
+        // RFC2616 mandates that the decimal part is no more than three digits,
+        // so we multiply the qvalue by 1000 to avoid floating point
+        // comparisons.
+        $langcode = strtolower($match[1]);
+        $qvalue = isset($match[2]) ? (float) $match[2] : 1;
+        // Take the highest qvalue for this langcode. Although the request
+        // supposedly contains unique langcodes, our mapping possibly resolves
+        // to the same langcode for different qvalues. Keep the highest.
+        $ua_langcodes[$langcode] = max(
+          (int) ($qvalue * 1000),
+          (isset($ua_langcodes[$langcode]) ? $ua_langcodes[$langcode] : 0)
+        );
+      }
+    }
+
+    // We should take pristine values from the HTTP headers, but Internet
+    // Explorer from version 7 sends only specific language tags (eg. fr-CA)
+    // without the corresponding generic tag (fr) unless explicitly configured.
+    // In that case, we assume that the lowest value of the specific tags is the
+    // value of the generic language to be as close to the HTTP 1.1 spec as
+    // possible.
+    // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 and
+    // http://blogs.msdn.com/b/ie/archive/2006/10/17/accept-language-header-for-internet-explorer-7.aspx
+    asort($ua_langcodes);
+    foreach ($ua_langcodes as $langcode => $qvalue) {
+      // For Chinese languages the generic tag is either zh-hans or zh-hant, so
+      // we need to handle this separately, we can not split $langcode on the
+      // first occurrence of '-' otherwise we get a non-existing language zh.
+      // All other languages use a langcode without a '-', so we can safely
+      // split on the first occurrence of it.
+      $generic_tag = '';
+      if (strlen($langcode) > 7 && (substr($langcode, 0, 7) == 'zh-hant' || substr($langcode, 0, 7) == 'zh-hans')) {
+        $generic_tag = substr($langcode, 0, 7);
+      }
+      else {
+        $generic_tag = strtok($langcode, '-');
+      }
+      if (!empty($generic_tag) && !isset($ua_langcodes[$generic_tag])) {
+        // Add the generic langcode, but make sure it has a lower qvalue as the
+        // more specific one, so the more specific one gets selected if it's
+        // defined by both the user agent and us.
+        $ua_langcodes[$generic_tag] = $qvalue - 0.1;
+      }
+    }
+
+    // Find the enabled language with the greatest qvalue, following the rules
+    // of RFC 2616 (section 14.4). If several languages have the same qvalue,
+    // prefer the one with the greatest weight.
+    $best_match_langcode = FALSE;
+    $max_qvalue = 0;
+    foreach ($langcodes as $langcode_case_sensitive) {
+      // Language tags are case insensitive (RFC2616, sec 3.10).
+      $langcode = strtolower($langcode_case_sensitive);
+
+      // If nothing matches below, the default qvalue is the one of the wildcard
+      // language, if set, or is 0 (which will never match).
+      $qvalue = isset($ua_langcodes['*']) ? $ua_langcodes['*'] : 0;
+
+      // Find the longest possible prefix of the user agent supplied language
+      // ('the language-range') that matches this site language ('the language
+      // tag').
+      $prefix = $langcode;
+      do {
+        if (isset($ua_langcodes[$prefix])) {
+          $qvalue = $ua_langcodes[$prefix];
+          break;
+        }
+      }
+      while ($prefix = substr($prefix, 0, strrpos($prefix, '-')));
+
+      // Find the best match.
+      if ($qvalue > $max_qvalue) {
+        $best_match_langcode = $langcode_case_sensitive;
+        $max_qvalue = $qvalue;
+      }
+    }
+
+    return $best_match_langcode;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Datetime/Date.php b/core/lib/Drupal/Core/Datetime/Date.php
index 34b863227f9be921af28e2722494390d0d811377..18f2425fdc7cd5498ded3bc8438f50f8cd848f06 100644
--- a/core/lib/Drupal/Core/Datetime/Date.php
+++ b/core/lib/Drupal/Core/Datetime/Date.php
@@ -12,7 +12,7 @@
 use Drupal\Core\Datetime\DrupalDateTime;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Language\Language;
-use Drupal\Core\Language\LanguageManager;
+use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\StringTranslation\TranslationInterface;
 
 /**
@@ -37,7 +37,7 @@ class Date {
   /**
    * Language manager for retrieving the default langcode when none is specified.
    *
-   * @var \Drupal\Core\Language\LanguageManager
+   * @var \Drupal\Core\Language\LanguageManagerInterface
    */
   protected $languageManager;
 
@@ -75,12 +75,14 @@ class Date {
    *
    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager.
-   * @param \Drupal\Core\Language\LanguageManager $language_manager
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
    *   The language manager.
    * @param \Drupal\Core\StringTranslation\TranslationInterface $translation
    *   The string translation.
+   * @param \Drupal\Core\Config\ConfigFactory $config_factory
+   *   The configuration factory.
    */
-  public function __construct(EntityManagerInterface $entity_manager, LanguageManager $language_manager, TranslationInterface $translation, ConfigFactory $config_factory) {
+  public function __construct(EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager, TranslationInterface $translation, ConfigFactory $config_factory) {
     $this->dateFormatStorage = $entity_manager->getStorageController('date_format');
     $this->languageManager = $language_manager;
     $this->stringTranslation = $translation;
@@ -128,7 +130,7 @@ public function format($timestamp, $type = 'medium', $format = '', $timezone = N
     }
 
     if (empty($langcode)) {
-      $langcode = $this->languageManager->getLanguage(Language::TYPE_INTERFACE)->id;
+      $langcode = $this->languageManager->getCurrentLanguage()->id;
     }
 
     // Create a DrupalDateTime object from the timestamp and timezone.
diff --git a/core/lib/Drupal/Core/Entity/EntityManager.php b/core/lib/Drupal/Core/Entity/EntityManager.php
index 5652085611de69ecea5c77dfda313448efa394d6..9185c3222b9702b3d0f2ed70780051c39c03ec03 100644
--- a/core/lib/Drupal/Core/Entity/EntityManager.php
+++ b/core/lib/Drupal/Core/Entity/EntityManager.php
@@ -142,7 +142,7 @@ public function __construct(\Traversable $namespaces, ContainerInterface $contai
     $this->discovery = new AnnotatedClassDiscovery('Entity', $namespaces, 'Drupal\Core\Entity\Annotation\EntityType');
     $this->discovery = new InfoHookDecorator($this->discovery, 'entity_info');
     $this->discovery = new AlterDecorator($this->discovery, 'entity_info');
-    $this->discovery = new CacheDecorator($this->discovery, 'entity_info:' . $this->languageManager->getLanguage(Language::TYPE_INTERFACE)->id, 'cache', CacheBackendInterface::CACHE_PERMANENT, array('entity_info' => TRUE));
+    $this->discovery = new CacheDecorator($this->discovery, 'entity_info:' . $this->languageManager->getCurrentLanguage()->id, 'cache', CacheBackendInterface::CACHE_PERMANENT, array('entity_info' => TRUE));
     $this->factory = new DefaultFactory($this->discovery);
     $this->container = $container;
   }
@@ -321,7 +321,7 @@ public function getAdminRouteInfo($entity_type, $bundle) {
   public function getFieldDefinitions($entity_type, $bundle = NULL) {
     if (!isset($this->entityFieldInfo[$entity_type])) {
       // First, try to load from cache.
-      $cid = 'entity_field_definitions:' . $entity_type . ':' . $this->languageManager->getLanguage(Language::TYPE_INTERFACE)->id;
+      $cid = 'entity_field_definitions:' . $entity_type . ':' . $this->languageManager->getCurrentLanguage()->id;
       if ($cache = $this->cache->get($cid)) {
         $this->entityFieldInfo[$entity_type] = $cache->data;
       }
@@ -420,7 +420,7 @@ public function getBundleInfo($entity_type) {
    */
   public function getAllBundleInfo() {
     if (!isset($this->bundleInfo)) {
-      $langcode = $this->languageManager->getLanguage(Language::TYPE_INTERFACE)->id;
+      $langcode = $this->languageManager->getCurrentLanguage()->id;
       if ($cache = $this->cache->get("entity_bundle_info:$langcode")) {
         $this->bundleInfo = $cache->data;
       }
@@ -460,7 +460,7 @@ public function getTranslationFromContext(EntityInterface $entity, $langcode = N
 
     if ($entity instanceof TranslatableInterface) {
       if (empty($langcode)) {
-        $langcode = $this->languageManager->getLanguage(Language::TYPE_CONTENT)->id;
+        $langcode = $this->languageManager->getCurrentLanguage(Language::TYPE_CONTENT)->id;
       }
 
       // Retrieve language fallback candidates to perform the entity language
diff --git a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
index 1f09b3b53f69287c0e720b9dea8ff5d8724d0d24..644257701534e52a635caa093f28f3ba8db8c83c 100644
--- a/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
+++ b/core/lib/Drupal/Core/Entity/EntityViewBuilder.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Entity\Display\EntityViewDisplayInterface;
 use Drupal\Core\Language\Language;
+use Drupal\Core\Language\LanguageManagerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -47,6 +48,13 @@ class EntityViewBuilder implements EntityControllerInterface, EntityViewBuilderI
    */
   protected $cacheBin = 'cache';
 
+  /**
+   * The language manager.
+   *
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   */
+  protected $languageManager;
+
   /**
    * Constructs a new EntityViewBuilder.
    *
@@ -54,11 +62,14 @@ class EntityViewBuilder implements EntityControllerInterface, EntityViewBuilderI
    *   The entity information array.
    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager service.
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   *   The language manager.
    */
-  public function __construct(EntityTypeInterface $entity_info, EntityManagerInterface $entity_manager) {
+  public function __construct(EntityTypeInterface $entity_info, EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager) {
     $this->entityType = $entity_info->id();
     $this->entityInfo = $entity_info;
     $this->entityManager = $entity_manager;
+    $this->languageManager = $language_manager;
   }
 
   /**
@@ -67,7 +78,8 @@ public function __construct(EntityTypeInterface $entity_info, EntityManagerInter
   public static function createInstance(ContainerInterface $container, EntityTypeInterface $entity_info) {
     return new static(
       $entity_info,
-      $container->get('entity.manager')
+      $container->get('entity.manager'),
+      $container->get('language_manager')
     );
   }
 
@@ -174,7 +186,7 @@ public function view(EntityInterface $entity, $view_mode = 'full', $langcode = N
    */
   public function viewMultiple(array $entities = array(), $view_mode = 'full', $langcode = NULL) {
     if (!isset($langcode)) {
-      $langcode = language(Language::TYPE_CONTENT)->id;
+      $langcode = $this->languageManager->getCurrentLanguage(Language::TYPE_CONTENT)->id;
     }
 
     // Build the view modes and display objects.
diff --git a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php
index 112644f560335f5814d0223eb63ab9d4aa193c84..8e4596f12795535adf2428cbbf16b9058aba3c88 100644
--- a/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php
+++ b/core/lib/Drupal/Core/EventSubscriber/FinishResponseSubscriber.php
@@ -55,7 +55,7 @@ public function onRespond(FilterResponseEvent $event) {
     $response->headers->set('X-UA-Compatible', 'IE=edge,chrome=1', FALSE);
 
     // Set the Content-language header.
-    $response->headers->set('Content-language', $this->languageManager->getLanguage(Language::TYPE_INTERFACE)->id);
+    $response->headers->set('Content-language', $this->languageManager->getCurrentLanguage()->id);
 
     // Because pages are highly dynamic, set the last-modified time to now
     // since the page is in fact being regenerated right now.
diff --git a/core/lib/Drupal/Core/Language/Language.php b/core/lib/Drupal/Core/Language/Language.php
index 8a5015725efb18280516315d80366c6a588137e0..f0c36ffae871eb3720064c899b80380cdc8700be 100644
--- a/core/lib/Drupal/Core/Language/Language.php
+++ b/core/lib/Drupal/Core/Language/Language.php
@@ -18,6 +18,23 @@
  */
 class Language {
 
+  /**
+   * The values to use to instantiate the default language.
+   *
+   * @todo Remove once the default language is converted to config. See
+   *   https://drupal.org/node/2108599.
+   *
+   * @var array
+   */
+  public static $defaultValues = array(
+    'id' => 'en',
+    'name' => 'English',
+    'direction' => 0,
+    'weight' => 0,
+    'locked' => 0,
+    'default' => TRUE,
+  );
+
   // Properties within the Language are set up as the default language.
 
   /**
diff --git a/core/lib/Drupal/Core/Language/LanguageManager.php b/core/lib/Drupal/Core/Language/LanguageManager.php
index a7dfbc0bbdefc7f59c833376d4f8926976a8b422..d21cb9f1ef6d1193b5d0c3f933f70ee54fcb8f1a 100644
--- a/core/lib/Drupal/Core/Language/LanguageManager.php
+++ b/core/lib/Drupal/Core/Language/LanguageManager.php
@@ -7,245 +7,198 @@
 
 namespace Drupal\Core\Language;
 
-use Drupal\Component\Utility\MapArray;
-use Drupal\Core\Extension\ModuleHandlerInterface;
-use Drupal\Core\KeyValueStore\StateInterface;
-use Symfony\Component\HttpFoundation\Request;
+use Drupal\Component\Utility\String;
+use Drupal\Core\StringTranslation\TranslationInterface;
 
 /**
- * Class responsible for initializing each language type.
+ * Class responsible for providing language support on language-unaware sites.
  */
-class LanguageManager {
+class LanguageManager implements LanguageManagerInterface {
 
   /**
-   * A request object.
+   * The string translation service.
    *
-   * @var \Symfony\Component\HttpFoundation\Request
+   * @var \Drupal\Core\StringTranslation\TranslationInterface
    */
-  protected $request;
+  protected $translation;
 
   /**
-   * The Key/Value Store to use for state.
+   * An array of all the available languages keyed by language code.
    *
-   * @var \Drupal\Core\KeyValueStore\StateInterface
+   * @var array
    */
-  protected $state = NULL;
+  protected $languages;
 
   /**
-   * The module handler service.
+   * The default language object.
    *
-   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   * @var \Drupal\Core\Language\Language
    */
-  protected $moduleHandler;
+  protected $defaultLanguage;
 
   /**
-   * An array of language objects keyed by language type.
-   *
-   * @var array
+   * {@inheritdoc}
    */
-  protected $languages;
+  function setTranslation(TranslationInterface $translation) {
+    $this->translation = $translation;
+  }
 
   /**
-   * Whether or not the language manager has been initialized.
+   * Translates a string to the current language or to a given language.
    *
-   * @var bool
+   * @see \Drupal\Core\StringTranslation\TranslationInterface()
    */
-  protected $initialized = FALSE;
+  protected function t($string, array $args = array(), array $options = array()) {
+    return $this->translation ? $this->translation->translate($string, $args, $options) : String::format($string, $args);
+  }
 
   /**
-   * Whether already in the process of language initialization.
-   *
-   * @todo This is only needed due to the circular dependency between language
-   *   and config. See http://drupal.org/node/1862202 for the plan to fix this.
-   *
-   * @var bool
+   * {@inheritdoc}
    */
-  protected $initializing = FALSE;
+  public function init() {
+  }
 
   /**
-   * Constructs an LanguageManager object.
-   *
-   * @param \Drupal\Core\KeyValueStore\StateInterface $state
-   *   (optional) The state keyvalue store. Defaults to NULL.
-   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
-   *   (optional) The module handler service. Defaults to NULL.
+   * {@inheritdoc}
    */
-  public function __construct(StateInterface $state = NULL, ModuleHandlerInterface $module_handler = NULL) {
-    $this->state = $state;
-    $this->moduleHandler = $module_handler;
+  public function isMultilingual() {
+    return FALSE;
   }
 
   /**
-   * Initializes each language type to a language object.
+   * {@inheritdoc}
    */
-  public function init() {
-    if ($this->initialized) {
-      return;
-    }
-    if ($this->isMultilingual()) {
-      foreach ($this->getLanguageTypes() as $type) {
-        $this->getLanguage($type);
-      }
-    }
-    $this->initialized = TRUE;
+  public function getLanguageTypes() {
+    return array(Language::TYPE_INTERFACE, Language::TYPE_CONTENT, Language::TYPE_URL);
   }
 
   /**
-   * Sets the $request property and resets all language types.
-   *
-   * @param \Symfony\Component\HttpFoundation\Request $request
-   *   The HttpRequest object representing the current request.
+   * {@inheritdoc}
    */
-  public function setRequest(Request $request) {
-    $this->request = $request;
-    $this->reset();
-    $this->init();
+  public function getCurrentLanguage($type = Language::TYPE_INTERFACE) {
+    return $this->getDefaultLanguage();
   }
 
   /**
-   * Returns a language object for the given type.
-   *
-   * @param string $type
-   *   (optional) The language type, e.g. the interface or the content language.
-   *   Defaults to \Drupal\Core\Language\Language::TYPE_INTERFACE.
-   *
-   * @return \Drupal\Core\Language\Language
-   *   A language object for the given type.
+   * {@inheritdoc}
    */
-  public function getLanguage($type = Language::TYPE_INTERFACE) {
-    if (isset($this->languages[$type])) {
-      return $this->languages[$type];
-    }
+  public function reset($type = NULL) {
+  }
 
-    if ($this->isMultilingual() && $this->request) {
-      if (!$this->initializing) {
-        $this->initializing = TRUE;
-        // @todo Objectify the language system so that we don't have to load an
-        //   include file and call out to procedural code. See
-        //   http://drupal.org/node/1862202
-        include_once DRUPAL_ROOT . '/core/includes/language.inc';
-        $this->languages[$type] = language_types_initialize($type, $this->request);
-        $this->initializing = FALSE;
-      }
-      else {
-        // Config has called getLanguage() during initialization of a language
-        // type. Simply return the default language without setting it on the
-        // $this->languages property. See the TODO in the docblock for the
-        // $initializing property.
-        return $this->getLanguageDefault();
-      }
-    }
-    else {
-      $this->languages[$type] = $this->getLanguageDefault();
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefaultLanguage() {
+    if (!isset($this->defaultLanguage)) {
+      $this->defaultLanguage = new Language(Language::$defaultValues);
     }
-    return $this->languages[$type];
+    return $this->defaultLanguage;
   }
 
   /**
-   * Resets the given language type or all types if none specified.
-   *
-   * @param string|null $type
-   *   (optional) The language type to reset as a string, e.g.,
-   *   Language::TYPE_INTERFACE, or NULL to reset all language types. Defaults
-   *   to NULL.
+   * {@inheritdoc}
    */
-  public function reset($type = NULL) {
-    if (!isset($type)) {
-      $this->languages = array();
-      $this->initialized = FALSE;
+  public function getLanguages($flags = Language::STATE_CONFIGURABLE) {
+    // Initialize master language list.
+    if (!isset($this->languages)) {
+      // No language module, so use the default language only.
+      $default = $this->getDefaultLanguage();
+      $this->languages = array($default->id => $default);
+      // Add the special languages, they will be filtered later if needed.
+      $this->languages += $this->getDefaultLockedLanguages($default->weight);
     }
-    elseif (isset($this->languages[$type])) {
-      unset($this->languages[$type]);
+
+    // Filter the full list of languages based on the value of the $all flag. By
+    // default we remove the locked languages, but the caller may request for
+    // those languages to be added as well.
+    $filtered_languages = array();
+
+    // Add the site's default language if flagged as allowed value.
+    if ($flags & Language::STATE_SITE_DEFAULT) {
+      $default = isset($default) ? $default : $this->getDefaultLanguage();
+      // Rename the default language.
+      $default->name = $this->t("Site's default language (@lang_name)", array('@lang_name' => $default->name));
+      $filtered_languages['site_default'] = $default;
+    }
+
+    foreach ($this->languages as $id => $language) {
+      if (($language->locked && ($flags & Language::STATE_LOCKED)) || (!$language->locked && ($flags & Language::STATE_CONFIGURABLE))) {
+        $filtered_languages[$id] = $language;
+      }
     }
+
+    return $filtered_languages;
   }
 
   /**
-   * Returns whether or not the site has more than one language enabled.
-   *
-   * @return bool
-   *   TRUE if more than one language is enabled, FALSE otherwise.
+   * {@inheritdoc}
    */
-  public function isMultilingual() {
-    if (!isset($this->state)) {
-      // No state service in install time.
-      return FALSE;
+  public function getLanguage($langcode) {
+    $languages = $this->getLanguages(Language::STATE_ALL);
+    return isset($languages[$langcode]) ? $languages[$langcode] : NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function getLanguageName($langcode) {
+    if ($langcode == Language::LANGCODE_NOT_SPECIFIED) {
+      return $this->t('None');
     }
-    return ($this->state->get('language_count') ?: 1) > 1;
+    if ($language = $this->getLanguage($langcode)) {
+      return $language->name;
+    }
+    if (empty($langcode)) {
+      return $this->t('Unknown');
+    }
+    return $this->t('Unknown (@langcode)', array('@langcode' => $langcode));
   }
 
   /**
-   * Returns the language fallback candidates for a given context.
-   *
-   * @param string $langcode
-   *   (optional) The language of the current context. Defaults to NULL.
-   * @param array $context
-   *   (optional) An associative array of data that can be useful to determine
-   *   the fallback sequence. The following keys are used in core:
-   *   - langcode: The desired language.
-   *   - operation: The name of the operation indicating the context where
-   *     language fallback is being applied, e.g. 'entity_view'.
-   *   - data: An arbitrary data structure that makes sense in the provided
-   *     context, e.g. an entity.
-   *
-   * @return array
-   *   An array of language codes sorted by priority: first values should be
-   *   tried first.
+   * {@inheritdoc}
    */
-  public function getFallbackCandidates($langcode = NULL, array $context = array()) {
-    if ($this->isMultilingual()) {
-      // Get languages ordered by weight, add Language::LANGCODE_NOT_SPECIFIED at
-      // the end.
-      $candidates = array_keys(language_list());
-      $candidates[] = Language::LANGCODE_NOT_SPECIFIED;
-      $candidates = MapArray::copyValuesToKeys($candidates);
+  public function getDefaultLockedLanguages($weight = 0) {
+    $languages = array();
 
-      // The first candidate should always be the desired language if specified.
-      if (!empty($langcode)) {
-        $candidates = array($langcode => $langcode) + $candidates;
-      }
+    $locked_language = array(
+      'default' => FALSE,
+      'locked' => TRUE,
+     );
+    $languages[Language::LANGCODE_NOT_SPECIFIED] = new Language(array(
+      'id' => Language::LANGCODE_NOT_SPECIFIED,
+      'name' => $this->t('Not specified'),
+      'weight' => ++$weight,
+    ) + $locked_language);
 
-      // Let other modules hook in and add/change candidates.
-      $type = 'language_fallback_candidates';
-      $types = array();
-      if (!empty($context['operation'])) {
-        $types[] = $type . '_' .  $context['operation'];
-      }
-      $types[] = $type;
-      $this->moduleHandler->alter($types, $candidates, $context);
-    }
-    else {
-      $candidates = array(Language::LANGCODE_DEFAULT);
-    }
+    $languages[Language::LANGCODE_NOT_APPLICABLE] = new Language(array(
+      'id' => Language::LANGCODE_NOT_APPLICABLE,
+      'name' => $this->t('Not applicable'),
+      'weight' => ++$weight,
+    ) + $locked_language);
 
-    return $candidates;
+    return $languages;
   }
 
   /**
-   * Returns an array of the available language types.
-   *
-   * @return array()
-   *   An array of all language types.
+   * {@inheritdoc}
    */
-  protected function getLanguageTypes() {
-    return language_types_get_all();
+  public function isLanguageLocked($langcode) {
+    $language = $this->getLanguage($langcode);
+    return ($language ? $language->locked : FALSE);
   }
 
   /**
-   * Returns a language object representing the site's default language.
-   *
-   * @return \Drupal\Core\Language\Language
-   *   A language object.
+   * {@inheritdoc}
+   */
+  public function getFallbackCandidates($langcode = NULL, array $context = array()) {
+    return array(Language::LANGCODE_DEFAULT);
+  }
+
+  /**
+   * {@inheritdoc}
    */
-  public function getLanguageDefault() {
-    $default_info = variable_get('language_default', array(
-      'id' => 'en',
-      'name' => 'English',
-      'direction' => 0,
-      'weight' => 0,
-      'locked' => 0,
-    ));
-    $default_info['default'] = TRUE;
-    return new Language($default_info);
+  public function getLanguageSwitchLinks($type, $path) {
+    return array();
   }
 
   /**
diff --git a/core/lib/Drupal/Core/Language/LanguageManagerInterface.php b/core/lib/Drupal/Core/Language/LanguageManagerInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..159c39c63297325ddca58a7721e5a03f9d209d9b
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/LanguageManagerInterface.php
@@ -0,0 +1,168 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\LanguageManagerInterface.
+ */
+
+namespace Drupal\Core\Language;
+
+use Drupal\Core\StringTranslation\TranslationInterface;
+
+/**
+ * Common interface for the language manager service.
+ */
+interface LanguageManagerInterface {
+
+  /**
+   * Injects the string translation service.
+   *
+   * @param \use Drupal\Core\StringTranslation\TranslationInterface $translation
+   *   The string translation service.
+   */
+  function setTranslation(TranslationInterface $translation);
+
+  /**
+   * Initializes each language type to a language object.
+   */
+  public function init();
+
+  /**
+   * Returns whether or not the site has more than one language enabled.
+   *
+   * @return bool
+   *   TRUE if more than one language is enabled, FALSE otherwise.
+   */
+  public function isMultilingual();
+
+  /**
+   * Returns an array of the available language types.
+   *
+   * @return array
+   *   An array of language type names.
+   */
+  public function getLanguageTypes();
+
+  /**
+   * Returns the current language for the given type.
+   *
+   * @param string $type
+   *   (optional) The language type, e.g. the interface or the content language.
+   *   Defaults to \Drupal\Core\Language\Language::TYPE_INTERFACE.
+   *
+   * @return \Drupal\Core\Language\Language
+   *   A language object for the given type.
+   */
+  public function getCurrentLanguage($type = Language::TYPE_INTERFACE);
+
+  /**
+   * Resets the given language type or all types if none specified.
+   *
+   * @param string|null $type
+   *   (optional) The language type to reset as a string, e.g.,
+   *   Language::TYPE_INTERFACE, or NULL to reset all language types. Defaults
+   *   to NULL.
+   */
+  public function reset($type = NULL);
+
+  /**
+   * Returns a language object representing the site's default language.
+   *
+   * @return \Drupal\Core\Language\Language
+   *   A language object.
+   */
+  public function getDefaultLanguage();
+
+  /**
+    * Returns a list of languages set up on the site.
+    *
+    * @param $flags
+    *   (optional) Specifies the state of the languages that have to be
+    *   returned. It can be: Language::STATE_CONFIGURABLE,
+    *   Language::STATE_LOCKED, Language::STATE_ALL.
+    *
+    * @return array
+    *   An associative array of languages, keyed by the language code, ordered
+    *   by weight ascending and name ascending.
+    */
+  public function getLanguages($flags = Language::STATE_CONFIGURABLE);
+
+  /**
+   * Returns a language object from the given language code.
+   *
+   * @param string $langcode
+   *   The language code.
+   *
+   * @return \Drupal\core\Language\Language|null
+   *   A fully-populated language object or NULL.
+   */
+  public function getLanguage($langcode);
+
+  /**
+   * Produced the printed name for a language for display.
+   *
+   * @param string $langcode
+   *   The language code.
+   *
+   * @return string
+   *   The printed name of the language.
+   */
+  function getLanguageName($langcode);
+
+  /**
+   * Returns a list of the default locked languages.
+   *
+   * @param int $weight
+   *   (optional) An integer value that is used as the start value for the
+   *   weights of the locked languages.
+   *
+   * @return array
+   *   An array of language objects.
+   */
+  public function getDefaultLockedLanguages($weight = 0);
+
+  /**
+   * Checks whether a language is locked.
+   *
+   * @param string $langcode
+   *   The language code.
+   *
+   * @return bool
+   *   Returns whether the language is locked.
+   */
+  public function isLanguageLocked($langcode);
+
+  /**
+   * Returns the language fallback candidates for a given context.
+   *
+   * @param string $langcode
+   *   (optional) The language of the current context. Defaults to NULL.
+   * @param array $context
+   *   (optional) An associative array of data that can be useful to determine
+   *   the fallback sequence. The following keys are used in core:
+   *   - langcode: The desired language.
+   *   - operation: The name of the operation indicating the context where
+   *     language fallback is being applied, e.g. 'entity_view'.
+   *   - data: An arbitrary data structure that makes sense in the provided
+   *     context, e.g. an entity.
+   *
+   * @return array
+   *   An array of language codes sorted by priority: first values should be
+   *   tried first.
+   */
+  public function getFallbackCandidates($langcode = NULL, array $context = array());
+
+  /**
+   * Returns the language switch links for the given language type.
+   *
+   * @param $type
+   *   The language type.
+   * @param $path
+   *   The internal path the switch links will be relative to.
+   *
+   * @return array
+   *   A keyed array of links ready to be themed.
+   */
+  function getLanguageSwitchLinks($type, $path);
+
+}
diff --git a/core/lib/Drupal/Core/Page/DefaultHtmlPageRenderer.php b/core/lib/Drupal/Core/Page/DefaultHtmlPageRenderer.php
index 6543fbc990ee3cc1656a5fb3ed5df3681c52463e..ad29a5b08403de5923a49500c5147f06f0f43771 100644
--- a/core/lib/Drupal/Core/Page/DefaultHtmlPageRenderer.php
+++ b/core/lib/Drupal/Core/Page/DefaultHtmlPageRenderer.php
@@ -90,7 +90,7 @@ public function preparePage(HtmlPage $page, &$page_array) {
     $page_array['#page'] = $page;
 
     // HTML element attributes.
-    $language_interface = $this->languageManager->getLanguage(Language::TYPE_INTERFACE);
+    $language_interface = $this->languageManager->getCurrentLanguage();
     $html_attributes = $page->getHtmlAttributes();
     $html_attributes['lang'] = $language_interface->id;
     $html_attributes['dir'] = $language_interface->direction ? 'rtl' : 'ltr';
diff --git a/core/lib/Drupal/Core/Path/AliasManager.php b/core/lib/Drupal/Core/Path/AliasManager.php
index 151bb23d509cdbcdee958c8c8b342e2f1437c034..a9470681719bfa998b4080c945a067568c0654bc 100644
--- a/core/lib/Drupal/Core/Path/AliasManager.php
+++ b/core/lib/Drupal/Core/Path/AliasManager.php
@@ -96,7 +96,7 @@ public function getSystemPath($path, $path_language = NULL) {
     // language. If we used a language different from the one conveyed by the
     // requested URL, we might end up being unable to check if there is a path
     // alias matching the URL path.
-    $path_language = $path_language ?: $this->languageManager->getLanguage(Language::TYPE_URL)->id;
+    $path_language = $path_language ?: $this->languageManager->getCurrentLanguage(Language::TYPE_URL)->id;
     // Lookup the path alias first.
     if (!empty($path) && $source = $this->lookupPathSource($path, $path_language)) {
       $path = $source;
@@ -113,7 +113,7 @@ public function getPathAlias($path, $path_language = NULL) {
     // language. If we used a language different from the one conveyed by the
     // requested URL, we might end up being unable to check if there is a path
     // alias matching the URL path.
-    $path_language = $path_language ?: $this->languageManager->getLanguage(Language::TYPE_URL)->id;
+    $path_language = $path_language ?: $this->languageManager->getCurrentLanguage(Language::TYPE_URL)->id;
     $result = $path;
     if (!empty($path) && $alias = $this->lookupPathAlias($path, $path_language)) {
       $result = $alias;
diff --git a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
index ce93bab2140fe26c10ef12763abf8044e92c2cfa..712c5eec4b6cbed6cbebd9aff50d3940d7955e4e 100644
--- a/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
+++ b/core/lib/Drupal/Core/Plugin/DefaultPluginManager.php
@@ -14,7 +14,7 @@
 use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Cache\CacheBackendInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
-use Drupal\Core\Language\LanguageManager;
+use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
 use Drupal\Core\Plugin\Factory\ContainerFactory;
@@ -84,7 +84,7 @@ class DefaultPluginManager extends PluginManagerBase implements PluginManagerInt
   /**
    * The language manager.
    *
-   * @var \Drupal\Core\Language\LanguageManager
+   * @var \Drupal\Core\Language\LanguageManagerInterface
    */
   protected $languageManager;
 
@@ -115,7 +115,7 @@ public function __construct($subdir, \Traversable $namespaces, $plugin_definitio
    *
    * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
    *   Cache backend instance to use.
-   * @param \Drupal\Core\Language\LanguageManager
+   * @param \Drupal\Core\Language\LanguageManagerInterface
    *   The language manager.
    * @param string $cache_key_prefix
    *   Cache key prefix to use, the language code will be appended
@@ -129,11 +129,11 @@ public function __construct($subdir, \Traversable $namespaces, $plugin_definitio
    *   clearCachedDefinitions() method. Only use cache tags when cached plugin
    *   definitions should be cleared along with other, related cache entries.
    */
-  public function setCacheBackend(CacheBackendInterface $cache_backend, LanguageManager $language_manager, $cache_key_prefix, array $cache_tags = array()) {
+  public function setCacheBackend(CacheBackendInterface $cache_backend, LanguageManagerInterface $language_manager, $cache_key_prefix, array $cache_tags = array()) {
     $this->languageManager = $language_manager;
     $this->cacheBackend = $cache_backend;
     $this->cacheKeyPrefix = $cache_key_prefix;
-    $this->cacheKey = $cache_key_prefix . ':' . $language_manager->getLanguage(Language::TYPE_INTERFACE)->id;
+    $this->cacheKey = $cache_key_prefix . ':' . $language_manager->getCurrentLanguage()->id;
     $this->cacheTags = $cache_tags;
   }
 
@@ -186,14 +186,16 @@ public function clearCachedDefinitions() {
         // Use the cache tags to clear the cache.
         $this->cacheBackend->deleteTags($this->cacheTags);
       }
-      else {
+      elseif ($this->languageManager) {
         $cache_keys = array();
-        // @todo: Use $this->languageManager->languageList() after http://drupal.org/node/1862202 is in.
-        foreach (language_list() as $langcode => $language) {
-          $cache_keys[] = $this->cacheKeyPrefix . ':' .$langcode;
+        foreach ($this->languageManager->getLanguages() as $langcode => $language) {
+          $cache_keys[] = $this->cacheKeyPrefix . ':' . $langcode;
         }
         $this->cacheBackend->deleteMultiple($cache_keys);
       }
+      else {
+        $this->cacheBackend->delete($this->cacheKey);
+      }
     }
     $this->definitions = NULL;
   }
diff --git a/core/lib/Drupal/Core/StringTranslation/TranslationManager.php b/core/lib/Drupal/Core/StringTranslation/TranslationManager.php
index 191ece45a91223f00eebc7a6bb3ce1831da53ecd..bd1b3b3a019d1a301ee3bedd3497f411ea70a7b7 100644
--- a/core/lib/Drupal/Core/StringTranslation/TranslationManager.php
+++ b/core/lib/Drupal/Core/StringTranslation/TranslationManager.php
@@ -7,14 +7,22 @@
 
 namespace Drupal\Core\StringTranslation;
 
-use Drupal\Core\StringTranslation\Translator\TranslatorInterface;
 use Drupal\Component\Utility\String;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\StringTranslation\Translator\TranslatorInterface;
 
 /**
  * Defines a chained translation implementation combining multiple translators.
  */
 class TranslationManager implements TranslationInterface, TranslatorInterface {
 
+  /**
+   * The language manager.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface
+   */
+  protected $languageManager;
+
   /**
    * An array of active translators keyed by priority.
    *
@@ -46,11 +54,24 @@ class TranslationManager implements TranslationInterface, TranslatorInterface {
 
   /**
    * Constructs a TranslationManager object.
+   *
+   * @param \Drupal\Core\Language\LanguageManagerInterface
+   *   The language manager.
+   */
+  public function __construct(LanguageManagerInterface $language_manager) {
+    $this->languageManager = $language_manager;
+    $this->defaultLangcode = $language_manager->getDefaultLanguage()->id;
+  }
+
+  /**
+   * Initializes the injected language manager with the translation manager.
+   *
+   * This should be called right after instantiating the translation manager to
+   * make it available to the language manager without introducing a circular
+   * dependency.
    */
-  public function __construct() {
-    // @todo Inject language_manager or config system after language_default
-    //   variable is converted to CMI.
-    $this->defaultLangcode = language_default()->id;
+  public function initLanguageManager() {
+    $this->languageManager->setTranslation($this);
   }
 
   /**
diff --git a/core/lib/Drupal/Core/StringTranslation/Translator/CustomStrings.php b/core/lib/Drupal/Core/StringTranslation/Translator/CustomStrings.php
index 3debb1f9a9143e3c7439e8b20ba6dddfe0ee888d..667c3eb66ba0048c937f079f2ad1c24bb06cef6a 100644
--- a/core/lib/Drupal/Core/StringTranslation/Translator/CustomStrings.php
+++ b/core/lib/Drupal/Core/StringTranslation/Translator/CustomStrings.php
@@ -38,7 +38,7 @@ public function __construct(Settings $settings) {
   /**
    * {@inheritdoc}
    */
-  protected function loadLanguage($langcode) {
+  protected function getLanguage($langcode) {
     return $this->settings->get('locale_custom_strings_' . $langcode, array());
   }
 
diff --git a/core/lib/Drupal/Core/StringTranslation/Translator/FileTranslation.php b/core/lib/Drupal/Core/StringTranslation/Translator/FileTranslation.php
index 3e162b13fc1de6b5207b3a8bbb6cad1802933f4f..ecd5a61634e40fca6ab8e5f5fac2b75546162316 100644
--- a/core/lib/Drupal/Core/StringTranslation/Translator/FileTranslation.php
+++ b/core/lib/Drupal/Core/StringTranslation/Translator/FileTranslation.php
@@ -41,7 +41,7 @@ public function __construct($directory) {
   /**
    * {@inheritdoc}
    */
-  protected function loadLanguage($langcode) {
+  protected function getLanguage($langcode) {
     // If the given langcode was selected, there should be at least one .po
     // file with its name in the pattern drupal-$version.$langcode.po.
     // This might or might not be the entire filename. It is also possible
diff --git a/core/lib/Drupal/Core/StringTranslation/Translator/StaticTranslation.php b/core/lib/Drupal/Core/StringTranslation/Translator/StaticTranslation.php
index fd72f5e1cfb76beb2c362efab3b62c2456198519..1b88a7a16ae110551a8b543bf667294d845fe9e9 100644
--- a/core/lib/Drupal/Core/StringTranslation/Translator/StaticTranslation.php
+++ b/core/lib/Drupal/Core/StringTranslation/Translator/StaticTranslation.php
@@ -37,7 +37,7 @@ public function __construct($translations = array()) {
    */
   public function getStringTranslation($langcode, $string, $context) {
     if (!isset($this->translations[$langcode])) {
-      $this->translations[$langcode] = $this->loadLanguage($langcode);
+      $this->translations[$langcode] = $this->getLanguage($langcode);
     }
     if (isset($this->translations[$langcode][$context][$string])) {
       return $this->translations[$langcode][$context][$string];
@@ -60,7 +60,7 @@ public function reset() {
    * @param string $langcode
    *   The langcode of the language.
    */
-  protected function loadLanguage($langcode) {
+  protected function getLanguage($langcode) {
     // This class is usually a base class but we do not declare as abstract
     // because it can be used on its own, by passing a simple array on the
     // constructor. This can be useful while testing, but it does not support
diff --git a/core/lib/Drupal/Core/Utility/LinkGenerator.php b/core/lib/Drupal/Core/Utility/LinkGenerator.php
index 3f6362064ed5689274de64901423301adb3232d9..1628e635e9e4def7388d0eae90326b777cf388e6 100644
--- a/core/lib/Drupal/Core/Utility/LinkGenerator.php
+++ b/core/lib/Drupal/Core/Utility/LinkGenerator.php
@@ -10,7 +10,7 @@
 use Drupal\Component\Utility\String;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Language\Language;
-use Drupal\Core\Language\LanguageManager;
+use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Template\Attribute;
 use Drupal\Core\Routing\UrlGeneratorInterface;
 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
@@ -45,7 +45,7 @@ class LinkGenerator implements LinkGeneratorInterface {
   /**
    * The language manager.
    *
-   * @var \Drupal\Core\Language\LanguageManager
+   * @var \Drupal\Core\Language\LanguageManagerInterface
    */
   protected $languageManager;
 
@@ -56,10 +56,10 @@ class LinkGenerator implements LinkGeneratorInterface {
    *   The url generator.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    *   The module handler.
-   * @param \Drupal\Core\Language\LanguageManager $language_manager
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
    *   The language manager.
    */
-  public function __construct(UrlGeneratorInterface $url_generator, ModuleHandlerInterface $module_handler, LanguageManager $language_manager) {
+  public function __construct(UrlGeneratorInterface $url_generator, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager) {
     $this->urlGenerator = $url_generator;
     $this->moduleHandler = $module_handler;
     $this->languageManager = $language_manager;
@@ -79,7 +79,7 @@ public function setRequest(Request $request) {
     $parameters = $raw_variables ? $raw_variables->all() : array();
     $this->active = array(
       'route_name' => $request->attributes->get(RouteObjectInterface::ROUTE_NAME),
-      'language' => $this->languageManager->getLanguage(Language::TYPE_URL)->id,
+      'language' => $this->languageManager->getCurrentLanguage(Language::TYPE_URL)->id,
       'parameters' => $parameters + (array) $request->query->all(),
     );
   }
diff --git a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockFormController.php b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockFormController.php
index 3befde0bf6858b956686653cb675a05aea2b0227..b3d2e615222b4bd15006f25287eb79773ea5a742 100644
--- a/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockFormController.php
+++ b/core/modules/block/custom_block/lib/Drupal/custom_block/CustomBlockFormController.php
@@ -111,7 +111,7 @@ public function form(array $form, array &$form_state) {
 
       // Set the correct default language.
       if ($block->isNew()) {
-        $language_default = $this->languageManager->getLanguage($language_configuration['langcode']);
+        $language_default = $this->languageManager->getCurrentLanguage($language_configuration['langcode']);
         $block->langcode->value = $language_default->id;
       }
     }
diff --git a/core/modules/block/lib/Drupal/block/BlockFormController.php b/core/modules/block/lib/Drupal/block/BlockFormController.php
index 92bbe88ccc12a4c75e5cc378063a5c423be80689..fba5659e3121cbddacdc9534492a9dd252d2f68c 100644
--- a/core/modules/block/lib/Drupal/block/BlockFormController.php
+++ b/core/modules/block/lib/Drupal/block/BlockFormController.php
@@ -13,7 +13,8 @@
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Entity\Query\QueryFactory;
 use Drupal\Core\Language\Language;
-use Drupal\Core\Language\LanguageManager;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\language\ConfigurableLanguageManagerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -45,7 +46,7 @@ class BlockFormController extends EntityFormController {
   /**
    * The language manager.
    *
-   * @var \Drupal\Core\Language\LanguageManager
+   * @var \Drupal\Core\Language\LanguageManagerInterface
    */
   protected $languageManager;
 
@@ -63,12 +64,12 @@ class BlockFormController extends EntityFormController {
    *   The entity manager.
    * @param \Drupal\Core\Entity\Query\QueryFactory $entity_query_factory
    *   The entity query factory.
-   * @param \Drupal\Core\Language\LanguageManager $language_manager
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
    *   The language manager.
    * @param \Drupal\Core\Config\ConfigFactory $config_factory
    *   The config factory.
    */
-  public function __construct(EntityManagerInterface $entity_manager, QueryFactory $entity_query_factory, LanguageManager $language_manager, ConfigFactory $config_factory) {
+  public function __construct(EntityManagerInterface $entity_manager, QueryFactory $entity_query_factory, LanguageManagerInterface $language_manager, ConfigFactory $config_factory) {
     $this->storageController = $entity_manager->getStorageController('block');
     $this->entityQueryFactory = $entity_query_factory;
     $this->languageManager = $language_manager;
@@ -168,11 +169,11 @@ public function form(array $form, array &$form_state) {
     }
 
     // Configure the block visibility per language.
-    if ($this->moduleHandler->moduleExists('language') && $this->languageManager->isMultilingual()) {
-      $configurable_language_types = language_types_get_configurable();
+    if ($this->languageManager->isMultilingual() && $this->languageManager instanceof ConfigurableLanguageManagerInterface) {
+      $language_types = $this->languageManager->getLanguageTypes();
 
       // Fetch languages.
-      $languages = language_list(Language::STATE_ALL);
+      $languages = $this->languageManager->getLanguages(Language::STATE_ALL);
       $langcodes_options = array();
       foreach ($languages as $language) {
         // @todo $language->name is not wrapped with t(), it should be replaced
@@ -189,16 +190,16 @@ public function form(array $form, array &$form_state) {
       // If there are multiple configurable language types, let the user pick
       // which one should be applied to this visibility setting. This way users
       // can limit blocks by interface language or content language for example.
-      $language_types = language_types_info();
+      $info = $this->languageManager->getDefinedLanguageTypesInfo();
       $language_type_options = array();
-      foreach ($configurable_language_types as $type_key) {
-        $language_type_options[$type_key] = $language_types[$type_key]['name'];
+      foreach ($language_types as $type_key) {
+        $language_type_options[$type_key] = $info[$type_key]['name'];
       }
       $form['visibility']['language']['language_type'] = array(
         '#type' => 'radios',
         '#title' => $this->t('Language type'),
         '#options' => $language_type_options,
-        '#default_value' => !empty($visibility['language']['language_type']) ? $visibility['language']['language_type'] : $configurable_language_types[0],
+        '#default_value' => !empty($visibility['language']['language_type']) ? $visibility['language']['language_type'] : reset($language_types),
         '#access' => count($language_type_options) > 1,
       );
       $form['visibility']['language']['langcodes'] = array(
diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php
index 2e4a4c6e5b63883453ec18dbf8f50346c1176f86..e2bb9dac73c586255cf2a71fb58272f81cde8438 100644
--- a/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php
+++ b/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php
@@ -74,14 +74,13 @@ public function testLanguageBlockVisibility() {
     $this->drupalPostForm('admin/config/regional/settings', $edit, t('Save configuration'));
 
     // Reset the static cache of the language list.
-    drupal_static_reset('language_list');
-
+    $this->container->get('language_manager')->reset();
     // Check that a page has a block.
-    $this->drupalget('', array('language' => language_load('en')));
+    $this->drupalGet('en');
     $this->assertText('Powered by Drupal', 'The body of the custom block appears on the page.');
 
     // Check that a page doesn't has a block for the current language anymore.
-    $this->drupalGet('', array('language' => language_load('fr')));
+    $this->drupalGet('fr');
     $this->assertNoText('Powered by Drupal', 'The body of the custom block does not appear on the page.');
   }
 
diff --git a/core/modules/block/tests/Drupal/block/Tests/BlockFormControllerTest.php b/core/modules/block/tests/Drupal/block/Tests/BlockFormControllerTest.php
index 69b0a1bf1d44016bee6a79448282fcf09d6a4f26..f3ae6ef66a3417d9e26644dcc24d2b4296f68380 100644
--- a/core/modules/block/tests/Drupal/block/Tests/BlockFormControllerTest.php
+++ b/core/modules/block/tests/Drupal/block/Tests/BlockFormControllerTest.php
@@ -66,9 +66,7 @@ public function testGetUniqueMachineName() {
       ->method('getStorageController')
       ->will($this->returnValue($block_storage));
 
-    $language_manager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager')
-      ->disableOriginalConstructor()
-      ->getMock();
+    $language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
 
     $config_factory = $this->getMockBuilder('Drupal\Core\Config\ConfigFactory')
       ->disableOriginalConstructor()
diff --git a/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/Editor/CKEditor.php b/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/Editor/CKEditor.php
index ae1ef56f5cc0a3df8b364baeacdb460f80a7f0df..837554959efd638294269bf66fada485972e1306 100644
--- a/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/Editor/CKEditor.php
+++ b/core/modules/ckeditor/lib/Drupal/ckeditor/Plugin/Editor/CKEditor.php
@@ -265,7 +265,7 @@ public function getJSSettings(EditorEntity $editor) {
 
     // Map the interface language code to a CKEditor translation.
     $ckeditor_langcodes = $this->getLangcodes();
-    $language_interface = $this->languageManager->getLanguage(Language::TYPE_INTERFACE);
+    $language_interface = $this->languageManager->getCurrentLanguage();
     if (isset($ckeditor_langcodes[$language_interface->id])) {
       $display_langcode = $ckeditor_langcodes[$language_interface->id];
     }
diff --git a/core/modules/comment/lib/Drupal/comment/CommentFormController.php b/core/modules/comment/lib/Drupal/comment/CommentFormController.php
index 3f88b515f0a4a4bf3944f02abc1f6859a09a1ecd..0adb4fc6845b0cc6010328c92d1db1a964c5e3e9 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentFormController.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentFormController.php
@@ -66,7 +66,7 @@ protected function init(array &$form_state) {
     // Make the comment inherit the current content language unless specifically
     // set.
     if ($comment->isNew()) {
-      $language_content = \Drupal::languageManager()->getLanguage(Language::TYPE_CONTENT);
+      $language_content = \Drupal::languageManager()->getCurrentLanguage(Language::TYPE_CONTENT);
       $comment->langcode->value = $language_content->id;
     }
 
diff --git a/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php b/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php
index 7dd6517f4b1f2640f4f7a010fd6485135dccc580..d23ee11ac5b733472c57330d4b0a38f126b909ab 100644
--- a/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php
+++ b/core/modules/comment/lib/Drupal/comment/CommentViewBuilder.php
@@ -16,6 +16,7 @@
 use Drupal\Core\Entity\EntityViewBuilderInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Entity\EntityViewBuilder;
+use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\field\FieldInfo;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
@@ -52,6 +53,7 @@ public static function createInstance(ContainerInterface $container, EntityTypeI
     return new static(
       $entity_info,
       $container->get('entity.manager'),
+      $container->get('language_manager'),
       $container->get('field.info'),
       $container->get('module_handler'),
       $container->get('csrf_token')
@@ -65,6 +67,8 @@ public static function createInstance(ContainerInterface $container, EntityTypeI
    *   The entity information array.
    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager service.
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   *   The language manager.
    * @param \Drupal\field\FieldInfo $field_info
    *   The field info service.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
@@ -72,8 +76,8 @@ public static function createInstance(ContainerInterface $container, EntityTypeI
    * @param \Drupal\Core\Access\CsrfTokenGenerator $csrf_token
    *   The CSRF token manager service.
    */
-  public function __construct(EntityTypeInterface $entity_info, EntityManagerInterface $entity_manager, FieldInfo $field_info, ModuleHandlerInterface $module_handler, CsrfTokenGenerator $csrf_token) {
-    parent::__construct($entity_info, $entity_manager);
+  public function __construct(EntityTypeInterface $entity_info, EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager, FieldInfo $field_info, ModuleHandlerInterface $module_handler, CsrfTokenGenerator $csrf_token) {
+    parent::__construct($entity_info, $entity_manager, $language_manager);
     $this->fieldInfo = $field_info;
     $this->moduleHandler = $module_handler;
     $this->csrfToken = $csrf_token;
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php
index c76dd41c6aa927fc3d3ce8dec808150cfdb53b11..3e738ce7e086fb2631155284b450313f5aa80b1f 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php
@@ -83,7 +83,6 @@ function setUp() {
    * Test that comment language is properly set.
    */
   function testCommentLanguage() {
-    drupal_static_reset('language_list');
 
     // Create two nodes, one for english and one for french, and comment each
     // node using both english and french as content language by changing URL
diff --git a/core/modules/comment/tests/modules/comment_test/comment_test.module b/core/modules/comment/tests/modules/comment_test/comment_test.module
index 110e8c5e2d99f83ca54a571dbb8fd87dd662721f..10f521b6c84ba90c9810737fc498a8daf0d86fc7 100644
--- a/core/modules/comment/tests/modules/comment_test/comment_test.module
+++ b/core/modules/comment/tests/modules/comment_test/comment_test.module
@@ -13,7 +13,7 @@
  */
 function comment_test_entity_info_alter(&$entity_info) {
   /** @var $entity_info \Drupal\Core\Entity\EntityTypeInterface[] */
-  if (language_multilingual()) {
+  if (\Drupal::languageManager()->isMultilingual()) {
     // Enable language handling for comment fields.
     $translation = $entity_info['comment']->get('translation');
     $translation['comment_test'] = TRUE;
diff --git a/core/modules/config_translation/lib/Drupal/config_translation/ConfigMapperManager.php b/core/modules/config_translation/lib/Drupal/config_translation/ConfigMapperManager.php
index edb431c14570bf2725d83a71050b25cc9c3e10a4..4331d6f73970e20d5dabba383d5a4c6848d49515 100644
--- a/core/modules/config_translation/lib/Drupal/config_translation/ConfigMapperManager.php
+++ b/core/modules/config_translation/lib/Drupal/config_translation/ConfigMapperManager.php
@@ -13,7 +13,7 @@
 use Drupal\Core\Config\Schema\ArrayElement;
 use Drupal\Core\Config\TypedConfigManager;
 use Drupal\Core\Extension\ModuleHandlerInterface;
-use Drupal\Core\Language\LanguageManager;
+use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\Plugin\DefaultPluginManager;
 use Drupal\Core\Plugin\Discovery\InfoHookDecorator;
 use Drupal\Core\Plugin\Discovery\YamlDiscovery;
@@ -49,14 +49,14 @@ class ConfigMapperManager extends DefaultPluginManager implements ConfigMapperMa
    *
    * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
    *   The cache backend.
-   * @param \Drupal\Core\Language\LanguageManager $language_manager
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
    *   The language manager.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    *   The module handler.
    * @param \Drupal\Core\Config\TypedConfigManager $typed_config_manager
    *   The typed config manager.
    */
-  public function __construct(CacheBackendInterface $cache_backend, LanguageManager $language_manager, ModuleHandlerInterface $module_handler, TypedConfigManager $typed_config_manager) {
+  public function __construct(CacheBackendInterface $cache_backend, LanguageManagerInterface $language_manager, ModuleHandlerInterface $module_handler, TypedConfigManager $typed_config_manager) {
     $this->typedConfigManager = $typed_config_manager;
 
     // Look at all themes and modules.
diff --git a/core/modules/config_translation/lib/Drupal/config_translation/Controller/ConfigTranslationController.php b/core/modules/config_translation/lib/Drupal/config_translation/Controller/ConfigTranslationController.php
index 3eb0216f94f5733c9ba3b3dfa5b60dcb18775d34..03c2a20afca28ad8b8242adcf6958366793486e3 100644
--- a/core/modules/config_translation/lib/Drupal/config_translation/Controller/ConfigTranslationController.php
+++ b/core/modules/config_translation/lib/Drupal/config_translation/Controller/ConfigTranslationController.php
@@ -12,6 +12,7 @@
 use Drupal\Core\Controller\ControllerBase;
 use Drupal\Core\DependencyInjection\ContainerInjectionInterface;
 use Drupal\Core\Language\Language;
+use Drupal\Core\Language\LanguageManagerInterface;
 use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
 use Drupal\Core\Session\AccountInterface;
 use Symfony\Cmf\Component\Routing\RouteObjectInterface;
@@ -61,6 +62,13 @@ class ConfigTranslationController extends ControllerBase implements ContainerInj
    */
   protected $account;
 
+  /**
+   * The language manager.
+   *
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   */
+  protected $languageManager;
+
   /**
    * Constructs a ConfigTranslationController.
    *
@@ -74,13 +82,16 @@ class ConfigTranslationController extends ControllerBase implements ContainerInj
    *   The inbound path processor.
    * @param \Drupal\Core\Session\AccountInterface $account
    *   The current user.
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   *   The language manager.
    */
-  public function __construct(ConfigMapperManagerInterface $config_mapper_manager, AccessManager $access_manager, RequestMatcherInterface $router, InboundPathProcessorInterface $path_processor, AccountInterface $account) {
+  public function __construct(ConfigMapperManagerInterface $config_mapper_manager, AccessManager $access_manager, RequestMatcherInterface $router, InboundPathProcessorInterface $path_processor, AccountInterface $account, LanguageManagerInterface $language_manager) {
     $this->configMapperManager = $config_mapper_manager;
     $this->accessManager = $access_manager;
     $this->router = $router;
     $this->pathProcessor = $path_processor;
     $this->account = $account;
+    $this->languageManager = $language_manager;
   }
 
   /**
@@ -92,7 +103,8 @@ public static function create(ContainerInterface $container) {
       $container->get('access_manager'),
       $container->get('router'),
       $container->get('path_processor_manager'),
-      $container->get('current_user')
+      $container->get('current_user'),
+      $container->get('language_manager')
     );
   }
 
@@ -119,10 +131,10 @@ public function itemPage(Request $request, $plugin_id) {
     // not on the system. For example, the configuration shipped in English but
     // the site has no English configured. Represent the original language in
     // the table even if it is not currently configured.
-    $languages = language_list();
+    $languages = $this->languageManager->getLanguages();
     $original_langcode = $mapper->getLangcode();
     if (!isset($languages[$original_langcode])) {
-      $language_name = language_name($original_langcode);
+      $language_name = $this->languageManager->getLanguageName($original_langcode);
       if ($original_langcode == 'en') {
         $language_name = $this->t('Built-in English');
       }
diff --git a/core/modules/config_translation/tests/Drupal/config_translation/Tests/ConfigMapperManagerTest.php b/core/modules/config_translation/tests/Drupal/config_translation/Tests/ConfigMapperManagerTest.php
index b07e54259c1a8ffaa3b05f0715aa925551304955..51575070139dbf91bd739df05f411e097b3b55ff 100644
--- a/core/modules/config_translation/tests/Drupal/config_translation/Tests/ConfigMapperManagerTest.php
+++ b/core/modules/config_translation/tests/Drupal/config_translation/Tests/ConfigMapperManagerTest.php
@@ -47,9 +47,9 @@ public static function getInfo() {
 
   public function setUp() {
     $language = new Language(array('id' => 'en'));
-    $language_manager = $this->getMock('Drupal\Core\Language\LanguageManager');
+    $language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
     $language_manager->expects($this->once())
-      ->method('getLanguage')
+      ->method('getCurrentLanguage')
       ->with(Language::TYPE_INTERFACE)
       ->will($this->returnValue($language));
 
diff --git a/core/modules/content_translation/content_translation.admin.inc b/core/modules/content_translation/content_translation.admin.inc
index a39d8f4e98c88a78a09ef9dce8dd4a525f6527cd..f509752e839463e7da6a74a06baa54498016e32d 100644
--- a/core/modules/content_translation/content_translation.admin.inc
+++ b/core/modules/content_translation/content_translation.admin.inc
@@ -275,7 +275,7 @@ function content_translation_form_language_content_settings_validate(array $form
         }
 
         $values = $bundle_settings['settings']['language'];
-        if (language_is_locked($values['langcode']) && empty($values['language_show'])) {
+        if (empty($values['language_show']) && \Drupal::languageManager()->isLanguageLocked($values['langcode'])) {
           foreach (language_list(Language::STATE_LOCKED) as $language) {
             $locked_languages[] = $language->name;
           }
diff --git a/core/modules/content_translation/content_translation.install b/core/modules/content_translation/content_translation.install
index bd817bcb0426cdcfb111fb8f48167f63fde41340..e474d693d8eb597f4bddc254e047f9d5aa465d48 100644
--- a/core/modules/content_translation/content_translation.install
+++ b/core/modules/content_translation/content_translation.install
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Core\Language\Language;
+use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
 
 /**
  * Implements hook_schema().
@@ -85,8 +86,7 @@ function content_translation_install() {
   // Assign a fairly low weight to ensure our implementation of
   // hook_module_implements_alter() is run among the last ones.
   module_set_weight('content_translation', 10);
-  language_negotiation_include();
-  language_negotiation_set(Language::TYPE_CONTENT, array(LANGUAGE_NEGOTIATION_URL => 0));
+  \Drupal::service('language_negotiator')->saveConfiguration(Language::TYPE_CONTENT, array(LanguageNegotiationUrl::METHOD_ID => 0));
 }
 
 /**
diff --git a/core/modules/content_translation/content_translation.module b/core/modules/content_translation/content_translation.module
index 70d20dc35bb49dba846c02cdf968117b205cad0e..21b315a81c10e2c2d943feb26e234edc03764e59 100644
--- a/core/modules/content_translation/content_translation.module
+++ b/core/modules/content_translation/content_translation.module
@@ -41,7 +41,7 @@ function content_translation_help($path, $arg) {
 
     case 'admin/config/regional/content-language':
       $output = '';
-      if (!language_multilingual()) {
+      if (!\Drupal::languageManager()->isMultilingual()) {
         $output .= '<br/>' . t('Before you can translate content, there must be at least two languages added on the <a href="!url">languages administration</a> page.', array('!url' => url('admin/config/regional/language')));
       }
       return $output;
@@ -331,7 +331,7 @@ function _content_translation_menu_strip_loaders($path) {
  *   The entity whose translation overview should be displayed.
  */
 function content_translation_translate_access(EntityInterface $entity) {
-  return $entity instanceof ContentEntityInterface && empty($entity->getUntranslated()->language()->locked) && language_multilingual() && $entity->isTranslatable() &&
+  return $entity instanceof ContentEntityInterface && empty($entity->getUntranslated()->language()->locked) && \Drupal::languageManager()->isMultilingual() && $entity->isTranslatable() &&
     (user_access('create content translations') || user_access('update content translations') || user_access('delete content translations'));
 }
 
@@ -955,7 +955,7 @@ function content_translation_language_configuration_element_process(array $eleme
 function content_translation_language_configuration_element_validate($element, array &$form_state, array $form) {
   $key = $form_state['content_translation']['key'];
   $values = $form_state['values'][$key];
-  if (language_is_locked($values['langcode']) && !$values['language_show'] && $values['content_translation']) {
+  if (!$values['language_show'] && $values['content_translation'] && \Drupal::languageManager()->isLanguageLocked($values['langcode'])) {
     foreach (language_list(Language::STATE_LOCKED) as $language) {
       $locked_languages[] = $language->name;
     }
diff --git a/core/modules/content_translation/content_translation.pages.inc b/core/modules/content_translation/content_translation.pages.inc
index 43e53318e828af96b0e0ec2fb10e5c266c4679c6..aa1b1f3890c1147771dda8ebe2f15bd63dbebf84 100644
--- a/core/modules/content_translation/content_translation.pages.inc
+++ b/core/modules/content_translation/content_translation.pages.inc
@@ -33,7 +33,7 @@ function content_translation_overview(EntityInterface $entity) {
   $header = array(t('Language'), t('Translation'), t('Source language'), t('Status'), t('Operations'));
   $rows = array();
 
-  if (language_multilingual()) {
+  if (\Drupal::languageManager()->isMultilingual()) {
     // If we have a view path defined for the current entity get the switch
     // links based on it.
     if (!empty($rel['canonical'])) {
@@ -159,12 +159,12 @@ function content_translation_overview(EntityInterface $entity) {
  *   A renderable array of language switch links.
  */
 function _content_translation_get_switch_links($path) {
-  $links = language_negotiation_get_switch_links(Language::TYPE_CONTENT, $path);
+  $links = \Drupal::languageManager()->getLanguageSwitchLinks(Language::TYPE_CONTENT, $path);
   if (empty($links)) {
     // If content language is set up to fall back to the interface language,
     // then there will be no switch links for Language::TYPE_CONTENT, ergo we
     // also need to use interface switch links.
-    $links = language_negotiation_get_switch_links(Language::TYPE_INTERFACE, $path);
+    $links = \Drupal::languageManager()->getLanguageSwitchLinks(Language::TYPE_INTERFACE, $path);
   }
   return $links;
 }
diff --git a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
index 6c6b35083c5f558e595ebe526405a8fae2422b48..cc3dacca637f07af53514581d5a7f316173cb807 100644
--- a/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
+++ b/core/modules/field/lib/Drupal/field/Plugin/views/field/Field.php
@@ -257,7 +257,7 @@ public function query($use_groupby = FALSE) {
         $default_langcode = language_default()->id;
         $langcode = str_replace(
           array('***CURRENT_LANGUAGE***', '***DEFAULT_LANGUAGE***'),
-          array($this->languageManager->getLanguage(Language::TYPE_CONTENT), $default_langcode),
+          array($this->languageManager->getCurrentLanguage(Language::TYPE_CONTENT), $default_langcode),
           $this->view->display_handler->options['field_langcode']
         );
         $placeholder = $this->placeholder();
@@ -857,7 +857,7 @@ function field_langcode(EntityInterface $entity) {
       $default_langcode = language_default()->id;
       $langcode = str_replace(
         array('***CURRENT_LANGUAGE***', '***DEFAULT_LANGUAGE***'),
-        array($this->languageManager->getLanguage(Language::TYPE_CONTENT), $default_langcode),
+        array($this->languageManager->getCurrentLanguage(Language::TYPE_CONTENT), $default_langcode),
         $this->view->display_handler->options['field_language']
       );
 
diff --git a/core/modules/filter/filter.module b/core/modules/filter/filter.module
index 4962044ffd49ae79963249045dd21c4b24a5818e..f206e621d3f5a2e49f26bdd9575f5e4bd7c3bbf8 100644
--- a/core/modules/filter/filter.module
+++ b/core/modules/filter/filter.module
@@ -193,7 +193,7 @@ function filter_formats(AccountInterface $account = NULL) {
 
   // All available formats are cached for performance.
   if (!isset($formats['all'])) {
-    $language_interface = \Drupal::languageManager()->getLanguage(Language::TYPE_INTERFACE);
+    $language_interface = \Drupal::languageManager()->getCurrentLanguage();
     if ($cache = \Drupal::cache()->get("filter_formats:{$language_interface->id}")) {
       $formats['all'] = $cache->data;
     }
diff --git a/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php b/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php
index 3c0d72958d8dab5b2db70abfbb76c57a06cf279d..596eb5cd3772489e919cc0871cf4b1c5229b9582 100644
--- a/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php
+++ b/core/modules/hal/lib/Drupal/hal/Tests/NormalizerTestBase.php
@@ -73,6 +73,7 @@ function setUp() {
     $german = new Language(array(
       'id' => 'de',
       'name' => 'Deutsch',
+      'weight' => -1,
     ));
     language_save($german);
 
diff --git a/core/modules/system/config/system.language.types.yml b/core/modules/language/config/language.types.yml
similarity index 100%
rename from core/modules/system/config/system.language.types.yml
rename to core/modules/language/config/language.types.yml
diff --git a/core/modules/language/config/schema/language.schema.yml b/core/modules/language/config/schema/language.schema.yml
index 1b251c97b80823e6cc7b2586c701b6b32d205245..9a31be732142bdb7baea116199fcc981f6fa67a7 100644
--- a/core/modules/language/config/schema/language.schema.yml
+++ b/core/modules/language/config/schema/language.schema.yml
@@ -1,5 +1,22 @@
 # Schema for the configuration files of the Language module.
 
+language.types:
+  type: mapping
+  label: 'Language types'
+  mapping:
+    all:
+      type: sequence
+      label: 'All language types'
+      sequence:
+        - type: string
+          label: 'Language type'
+    configurable:
+      type: sequence
+      label: 'Configurable language types'
+      sequence:
+        - type: string
+          label: 'Language type'
+
 language.detection:
   type: mapping
   label: 'Language detection settings'
diff --git a/core/modules/language/language.api.php b/core/modules/language/language.api.php
index d0a31c4992a903bcafc4cb7199956365f58ee328..e93f508dc925efa87379ee7956cf59b0f056da6d 100644
--- a/core/modules/language/language.api.php
+++ b/core/modules/language/language.api.php
@@ -10,6 +10,73 @@
  * @{
  */
 
+/**
+ * Define language types.
+ *
+ * @return
+ *   An associative array of language type definitions. The keys are the
+ *   identifiers, which are also used as names for global variables representing
+ *   the types in the bootstrap phase. The values are associative arrays that
+ *   may contain the following elements:
+ *   - name: The human-readable language type identifier.
+ *   - description: A description of the language type.
+ *   - locked: A boolean indicating if the user can choose wether to configure
+ *     the language type or not using the UI.
+ *   - fixed: A fixed array of language negotiation method identifiers to use to
+ *     initialize this language. If locked is set to TRUE and fixed is set, it
+ *     will always use the specified methods in the given priority order. If not
+ *     present and locked is TRUE then language-interface will be
+ *     used.
+ *
+ *  @todo Rename the 'fixed' key to something more meaningful, for instance
+ *     'negotiation settings'. See https://drupal.org/node/2166879.
+ *
+ * @see hook_language_types_info_alter()
+ * @ingroup language_negotiation
+ */
+function hook_language_types_info() {
+  return array(
+    'custom_language_type' => array(
+      'name' => t('Custom language'),
+      'description' => t('A custom language type.'),
+      'locked' => FALSE,
+    ),
+    'fixed_custom_language_type' => array(
+      'locked' => TRUE,
+      'fixed' => array('custom_language_negotiation_method'),
+    ),
+  );
+}
+
+/**
+ * Perform alterations on language types.
+ *
+ * @param $language_types
+ *   Array of language type definitions.
+ *
+ * @see hook_language_types_info()
+ * @ingroup language_negotiation
+ */
+function hook_language_types_info_alter(array &$language_types) {
+  if (isset($language_types['custom_language_type'])) {
+    $language_types['custom_language_type_custom']['description'] = t('A far better description.');
+  }
+}
+
+/**
+ * Perform alterations on language negotiation methods.
+ *
+ * @param $negotiation_info
+ *   Array of language negotiation method definitions.
+ *
+ * @ingroup language_negotiation
+ */
+function hook_language_negotiation_info_alter(array &$negotiation_info) {
+  if (isset($negotiation_info['custom_language_method'])) {
+    $negotiation_info['custom_language_method']['config'] = 'admin/config/regional/language/detection/custom-language-method';
+  }
+}
+
 /**
  * React to a language about to be added or updated in the system.
  *
@@ -66,7 +133,7 @@ function hook_language_delete($language) {
  * @param array $context
  *   A language fallback context.
  *
- * @see \Drupal\Core\Language\LanguageManager::getFallbackCandidates()
+ * @see \Drupal\Core\Language\LanguageManagerInterface::getFallbackCandidates()
  */
 function hook_language_fallback_candidates_alter(array &$candidates, array $context) {
   $candidates = array_reverse($candidates);
@@ -81,7 +148,7 @@ function hook_language_fallback_candidates_alter(array &$candidates, array $cont
  * @param array $context
  *   A language fallback context.
  *
- * @see \Drupal\Core\Language\LanguageManager::getFallbackCandidates()
+ * @see \Drupal\Core\Language\LanguageManagerInterface::getFallbackCandidates()
  */
 function hook_language_fallback_candidates_OPERATION_alter(array &$candidates, array $context) {
   // We know that the current OPERATION deals with entities so no need to check
diff --git a/core/modules/language/language.install b/core/modules/language/language.install
index 51033894cc58c3f8d403ba3aa66082b24176cbd0..37388311d445d066119e3920265fd7971540381a 100644
--- a/core/modules/language/language.install
+++ b/core/modules/language/language.install
@@ -6,6 +6,8 @@
  */
 
 use Drupal\Core\Language\Language;
+use Drupal\language\ConfigurableLanguageManagerInterface;
+use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
 
 /**
  * Implements hook_install().
@@ -14,15 +16,16 @@
  * system on multilingual sites without needing any preliminary configuration.
  */
 function language_install() {
-  // Enable URL language detection for each configurable language type.
-  require_once DRUPAL_ROOT . '/core/includes/language.inc';
-  foreach (language_types_get_configurable() as $type) {
-    module_load_include('inc', 'language', 'language.negotiation');
-    language_negotiation_set($type, array(LANGUAGE_NEGOTIATION_URL => 0));
+  $language_manager = \Drupal::languageManager();
+  if ($language_manager instanceof ConfigurableLanguageManagerInterface) {
+    $negotiator = \Drupal::service('language_negotiator');
+    $types = $language_manager->getLanguageTypes();
+    $negotiator->updateConfiguration($types);
+    // Enable URL language detection for each configurable language type.
+    foreach ($types as $type) {
+      $negotiator->saveConfiguration($type, array(LanguageNegotiationUrl::METHOD_ID => 0));
+    }
   }
-
-  // Update the language count.
-  language_update_count();
 }
 
 /**
@@ -31,12 +34,9 @@ function language_install() {
 function language_uninstall() {
   // Clear variables.
   variable_del('language_default');
-  \Drupal::state()->delete('language_count');
 
   // Clear variables.
-  variable_del('language_types');
-
-  foreach (language_types_get_all() as $type) {
+  foreach (\Drupal::languageManager()->getDefinedLanguageTypes() as $type) {
     variable_del("language_negotiation_$type");
     variable_del("language_negotiation_methods_weight_$type");
   }
@@ -44,19 +44,4 @@ function language_uninstall() {
   // Re-initialize the language system so successive calls to t() and other
   // functions will not expect languages to be present.
   drupal_language_initialize();
-
-  // Force the language_count state to be 1, so that when checking if the
-  // site is multilingual (for example in language_multilingual()), the result
-  // will be FALSE, because the language module is not installed.
-  \Drupal::state()->set('language_count', 1);
-}
-
-/**
- * Implements hook_requirements().
- */
-function language_requirements($phase) {
-  if ($phase == 'update') {
-    // Load the include files to make constants available for updates.
-    language_negotiation_include();
-  }
 }
diff --git a/core/modules/language/language.module b/core/modules/language/language.module
index 07a4aad7a4aa4d0d73468f7ed7b5e9b8186f6a09..883ab881742ed574c7f6a4e045bcc2a98ae0226e 100644
--- a/core/modules/language/language.module
+++ b/core/modules/language/language.module
@@ -5,8 +5,13 @@
  * Add language handling functionality to Drupal.
  */
 
-use Drupal\node\NodeTypeInterface;
 use Drupal\Core\Language\Language;
+use Drupal\language\ConfigurableLanguageManager;
+use Drupal\language\ConfigurableLanguageManagerInterface;
+use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUI;
+use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
+use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrlFallback;
+use Drupal\node\NodeTypeInterface;
 
 /**
  * Implements hook_help().
@@ -451,6 +456,7 @@ function language_save($language) {
   $language_entity->weight = isset($language->weight) ? $language->weight : 0;
 
   // Save the record and inform others about the change.
+  $multilingual = \Drupal::languageManager()->isMultilingual();
   $language_entity->save();
   $t_args = array('%language' => $language->name, '%langcode' => $language->id);
   if ($language->is_new) {
@@ -467,39 +473,23 @@ function language_save($language) {
     variable_set('language_default', (array) $language);
   }
 
-  // Kill the static cache in language_list().
-  drupal_static_reset('language_list');
-
-  // Update language count based on unlocked language count.
-  language_update_count();
-
-  // Update weight of locked system languages.
-  language_update_locked_weights();
-
-  language_negotiation_include();
+  $language_manager = \Drupal::languageManager();
+  $language_manager->reset();
+  if ($language_manager instanceof ConfigurableLanguageManagerInterface) {
+    $language_manager->updateLockedLanguageWeights();
+  }
 
   // Update URL Prefixes for all languages after the new default language is
   // propagated and the language_list() cache is flushed.
   language_negotiation_url_prefixes_update();
 
-  return $language;
-}
-
-/**
- * Updates the language_count state.
- *
- * This is used to check if a site is multilingual or not.
- *
- * @see language_multilingual()
- */
-function language_update_count() {
-  $count = 0;
-  foreach (entity_load_multiple('language_entity') as $language) {
-    if (!$language->locked) {
-      $count++;
-    }
+  // If after adding this language the site will become multilingual, we need to
+  // rebuild language services.
+  if (!$multilingual && $language->is_new) {
+    ConfigurableLanguageManager::rebuildServices();
   }
-  \Drupal::state()->set('language_count', $count);
+
+  return $language;
 }
 
 /**
@@ -521,12 +511,17 @@ function language_delete($langcode) {
     // Remove the language.
     entity_delete_multiple('language_entity', array($language->id));
 
-    drupal_static_reset('language_list');
-
-    language_update_count();
+    $language_manager = \Drupal::languageManager();
+    $language_manager->reset();
+    if ($language_manager instanceof ConfigurableLanguageManagerInterface) {
+      $language_manager->updateLockedLanguageWeights();
+    }
 
-    // Update weight of locked system languages.
-    language_update_locked_weights();
+    // If after deleting this language the site will become monolingual, we need
+    // to rebuild language services.
+    if (!\Drupal::languageManager()->isMultilingual()) {
+      ConfigurableLanguageManager::rebuildServices();
+    }
 
     $t_args = array('%language' => $language->name, '%langcode' => $language->id);
     watchdog('language', 'The %language (%langcode) language has been removed.', $t_args);
@@ -570,8 +565,6 @@ function language_library_info() {
  *   language if none is specified.
  */
 function language_language_types_info() {
-  language_negotiation_include();
-
   return array(
     Language::TYPE_INTERFACE => array(
       'name' => t('User interface text'),
@@ -581,114 +574,75 @@ function language_language_types_info() {
     Language::TYPE_CONTENT => array(
       'name' => t('Content'),
       'description' => t('Order of language detection methods for content. If a version of content is available in the detected language, it will be displayed.'),
-      'fixed' => array(LANGUAGE_NEGOTIATION_INTERFACE),
+      'fixed' => array(LanguageNegotiationUI::METHOD_ID),
       'locked' => TRUE,
     ),
     Language::TYPE_URL => array(
-      'fixed' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_URL_FALLBACK),
+      'fixed' => array(LanguageNegotiationUrl::METHOD_ID, LanguageNegotiationUrlFallback::METHOD_ID),
       'locked' => TRUE,
     ),
   );
 }
 
 /**
- * Implements hook_language_negotiation_info().
+ * Reads language prefixes and uses the langcode if no prefix is set.
  */
-function language_language_negotiation_info() {
-  language_negotiation_include();
-  $file = drupal_get_path('module', 'language') . '/language.negotiation.inc';
-
-  $negotiation_info = array();
-  $negotiation_info[LANGUAGE_NEGOTIATION_URL] = array(
-    'types' => array(Language::TYPE_CONTENT, Language::TYPE_INTERFACE, Language::TYPE_URL),
-    'callbacks' => array(
-      'negotiation' => 'language_from_url',
-      'language_switch' => 'language_switcher_url',
-    ),
-    'file' => $file,
-    'weight' => -8,
-    'name' => t('URL'),
-    'description' => t('Language from the URL (Path prefix or domain).'),
-    'config' => 'admin/config/regional/language/detection/url',
-  );
-
-  $negotiation_info[LANGUAGE_NEGOTIATION_SESSION] = array(
-    'callbacks' => array(
-      'negotiation' => 'language_from_session',
-      'language_switch' => 'language_switcher_session',
-      'url_rewrite' => 'language_url_rewrite_session',
-    ),
-    'file' => $file,
-    'weight' => -6,
-    'name' => t('Session'),
-    'description' => t('Language from a request/session parameter.'),
-    'config' => 'admin/config/regional/language/detection/session',
-  );
-
-  $negotiation_info[LANGUAGE_NEGOTIATION_USER] = array(
-    'callbacks' => array('negotiation' => 'language_from_user'),
-    'file' => $file,
-    'weight' => -4,
-    'name' => t('Account preference for site'),
-    'description' => t("The language setting for the site in the user's account."),
-  );
-
-  $negotiation_info[LANGUAGE_NEGOTIATION_BROWSER] = array(
-    'callbacks' => array('negotiation' => 'language_from_browser'),
-    'file' => $file,
-    'weight' => -2,
-    'cache' => 0,
-    'name' => t('Browser'),
-    'description' => t("Language from the browser's language settings."),
-    'config' => 'admin/config/regional/language/detection/browser',
-  );
-
-  $negotiation_info[LANGUAGE_NEGOTIATION_INTERFACE] = array(
-    'types' => array(Language::TYPE_CONTENT),
-    'callbacks' => array('negotiation' => 'language_from_interface'),
-    'file' => $file,
-    'weight' => 8,
-    'name' => t('Interface'),
-    'description' => t('Use the detected interface language.'),
-  );
+function language_negotiation_url_prefixes() {
+  return \Drupal::config('language.negotiation')->get('url.prefixes');
+}
 
-  $negotiation_info[LANGUAGE_NEGOTIATION_URL_FALLBACK] = array(
-    'types' => array(Language::TYPE_URL),
-    'callbacks' => array('negotiation' => 'language_url_fallback'),
-    'file' => $file,
-    'weight' => 8,
-    'name' => t('URL fallback'),
-    'description' => t('Use an already detected language for URLs if none is found.'),
-  );
+/**
+ * Update the list of prefixes from the installed languages.
+ */
+function language_negotiation_url_prefixes_update() {
+  $prefixes = language_negotiation_url_prefixes();
+  foreach (language_list() as $language) {
+    // The prefix for this language should be updated if it's not assigned yet
+    // or the prefix is set to the empty string.
+    if (empty($prefixes[$language->id])) {
+      // For the default language, set the prefix to the empty string,
+      // otherwise use the langcode.
+      $prefixes[$language->id] = !empty($language->default) ? '' : $language->id;
+    }
+    // Otherwise we keep the configured prefix.
+  }
+  language_negotiation_url_prefixes_save($prefixes);
+}
 
-  $negotiation_info[LANGUAGE_NEGOTIATION_USER_ADMIN] = array(
-    'types' => array(Language::TYPE_INTERFACE),
-    'callbacks' => array('negotiation' => 'language_from_user_admin'),
-    'file' => $file,
-    'weight' => 10,
-    'name' => t('Account preference for administration pages'),
-    'description' => t("The language setting for account administration pages in the user's account."),
-  );
+/**
+ * Saves language prefix settings.
+ */
+function language_negotiation_url_prefixes_save(array $prefixes) {
+  \Drupal::config('language.negotiation')
+  ->set('url.prefixes', $prefixes)
+  ->save();
+}
 
-  return $negotiation_info;
+/**
+ * Reads language domains.
+ */
+function language_negotiation_url_domains() {
+  return \Drupal::config('language.negotiation')->get('url.domains');
 }
 
 /**
- * Include negotiation backend functionality.
+ * Saves the language domain settings.
  */
-function language_negotiation_include() {
-  include_once DRUPAL_ROOT . '/core/includes/language.inc';
-  include_once __DIR__ . '/language.negotiation.inc';
+function language_negotiation_url_domains_save(array $domains) {
+  \Drupal::config('language.negotiation')
+  ->set('url.domains', $domains)
+  ->save();
 }
 
 /**
  * Implements hook_modules_installed().
  */
 function language_modules_installed($modules) {
-  include_once DRUPAL_ROOT . '/core/includes/language.inc';
-  // Load configurability options from configuration.
-  language_types_set(array());
-  language_negotiation_purge();
+  if (!in_array('language', $modules)) {
+    $negotiator = \Drupal::service('language_negotiator');
+    $negotiator->updateConfiguration(array());
+    $negotiator->purgeConfiguration();
+  }
 }
 
 /**
@@ -706,8 +660,6 @@ function language_language_insert($language) {
     return;
   }
 
-  language_negotiation_include();
-
   // Add language to the list of language domains.
   $domains = language_negotiation_url_domains();
   $domains[$language->id] = '';
@@ -718,8 +670,6 @@ function language_language_insert($language) {
  * Implements hook_language_delete().
  */
 function language_language_delete($language) {
-  language_negotiation_include();
-
   // Remove language from language prefix list.
   $prefixes = language_negotiation_url_prefixes();
   unset($prefixes[$language->id]);
@@ -768,29 +718,6 @@ function language_set_browser_drupal_langcode_mappings($mappings) {
   $config->save();
 }
 
-/**
- * Updates locked system language weights.
- */
-function language_update_locked_weights() {
-  $max_weight = 0;
-
-  // Get maximum weight to update the system languages to keep them on bottom.
-  foreach (language_list(Language::STATE_CONFIGURABLE) as $language) {
-    if (!$language->locked && $language->weight > $max_weight) {
-      $max_weight = $language->weight;
-    }
-  }
-
-  // Loop locked languages to maintain the existing order.
-  foreach (language_list(Language::STATE_LOCKED) as $language) {
-    $max_weight++;
-    // Update system languages weight.
-    \Drupal::config('language.entity.' . $language->id)
-      ->set('weight', $max_weight)
-      ->save();
-  }
-}
-
 /**
  * Implements hook_form_FORM_ID_alter for system_regional_settings().
  *
diff --git a/core/modules/language/language.negotiation.inc b/core/modules/language/language.negotiation.inc
deleted file mode 100644
index f87284d76f27c1fd82e5cce5da29fb28a6c6e68d..0000000000000000000000000000000000000000
--- a/core/modules/language/language.negotiation.inc
+++ /dev/null
@@ -1,529 +0,0 @@
-<?php
-
-use Drupal\Component\Utility\String;
-use \Symfony\Component\HttpFoundation\Request;
-
-/**
- * @file
- * Language negotiation functions.
- */
-
-use Drupal\Core\Language\Language;
-
-/**
- * The language is determined using path prefix or domain.
- */
-const LANGUAGE_NEGOTIATION_URL = 'language-url';
-
-/**
- * The language is set based on the browser language settings.
- */
-const LANGUAGE_NEGOTIATION_BROWSER = 'language-browser';
-
-/**
- * If no URL language, language is determined using an already detected one.
- */
-const LANGUAGE_NEGOTIATION_URL_FALLBACK = 'language-url-fallback';
-
-/**
- * The language is set based on the user language settings.
- */
-const LANGUAGE_NEGOTIATION_USER = 'language-user';
-
-/**
- * The language is set based on the user admin language settings.
- */
-const LANGUAGE_NEGOTIATION_USER_ADMIN = 'language-user-admin';
-
-/**
- * The language is set based on the request/session parameters.
- */
-const LANGUAGE_NEGOTIATION_SESSION = 'language-session';
-
-/**
- * URL language negotiation: use the path prefix as URL language indicator.
- */
-const LANGUAGE_NEGOTIATION_URL_PREFIX = 'path_prefix';
-
-/**
- * URL language negotiation: use the domain as URL language indicator.
- */
-const LANGUAGE_NEGOTIATION_URL_DOMAIN = 'domain';
-
-/**
- * Identifies the language from the current interface language.
- *
- * @return
- *   The current interface language code.
- */
-function language_from_interface() {
-  return language(Language::TYPE_INTERFACE)->id;
-}
-
-/**
- * Identify language from the Accept-language HTTP header we got.
- *
- * The algorithm works as follows:
- * - map browser language codes to Drupal language codes.
- * - order all browser language codes by qvalue from high to low.
- * - add generic browser language codes if they aren't already specified
- *   but with a slightly lower qvalue.
- * - find the most specific Drupal language code with the highest qvalue.
- * - if 2 or more languages are having the same qvalue, respect the order of
- *   them inside the $languages array.
- *
- * We perform browser accept-language parsing only if page cache is disabled,
- * otherwise we would cache a user-specific preference.
- *
- * @param $languages
- *   An array of language objects for enabled languages ordered by weight.
- *
- * @return
- *   A valid language code on success, FALSE otherwise.
- */
-function language_from_browser($languages) {
-  $accept_language = \Drupal::request()->server->get('HTTP_ACCEPT_LANGUAGE');
-  if (empty($accept_language)) {
-    return FALSE;
-  }
-
-  // The Accept-Language header contains information about the language
-  // preferences configured in the user's browser / operating system. RFC 2616
-  // (section 14.4) defines the Accept-Language header as follows:
-  //   Accept-Language = "Accept-Language" ":"
-  //                  1#( language-range [ ";" "q" "=" qvalue ] )
-  //   language-range  = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" )
-  // Samples: "hu, en-us;q=0.66, en;q=0.33", "hu,en-us;q=0.5"
-  $browser_langcodes = array();
-  if (preg_match_all('@(?<=[, ]|^)([a-zA-Z-]+|\*)(?:;q=([0-9.]+))?(?:$|\s*,\s*)@', trim($accept_language), $matches, PREG_SET_ORDER)) {
-    // Load custom mappings to support browsers that are sending non standard
-    // language codes.
-    $mappings = language_get_browser_drupal_langcode_mappings();
-    foreach ($matches as $match) {
-      if ($mappings) {
-        $langcode = strtolower($match[1]);
-        foreach ($mappings as $browser_langcode => $drupal_langcode) {
-          if ($langcode == $browser_langcode) {
-            $match[1] = $drupal_langcode;
-          }
-        }
-      }
-      // We can safely use strtolower() here, tags are ASCII.
-      // RFC2616 mandates that the decimal part is no more than three digits,
-      // so we multiply the qvalue by 1000 to avoid floating point comparisons.
-      $langcode = strtolower($match[1]);
-      $qvalue = isset($match[2]) ? (float) $match[2] : 1;
-      // Take the highest qvalue for this langcode. Although the request
-      // supposedly contains unique langcodes, our mapping possibly resolves
-      // to the same langcode for different qvalues. Keep the highest.
-      $browser_langcodes[$langcode] = max(
-        (int) ($qvalue * 1000),
-        (isset($browser_langcodes[$langcode]) ? $browser_langcodes[$langcode] : 0)
-      );
-    }
-  }
-
-  // We should take pristine values from the HTTP headers, but Internet Explorer
-  // from version 7 sends only specific language tags (eg. fr-CA) without the
-  // corresponding generic tag (fr) unless explicitly configured. In that case,
-  // we assume that the lowest value of the specific tags is the value of the
-  // generic language to be as close to the HTTP 1.1 spec as possible.
-  // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 and
-  // http://blogs.msdn.com/b/ie/archive/2006/10/17/accept-language-header-for-internet-explorer-7.aspx
-  asort($browser_langcodes);
-  foreach ($browser_langcodes as $langcode => $qvalue) {
-    // For Chinese languages the generic tag is either zh-hans or zh-hant, so we
-    // need to handle this separately, we can not split $langcode on the
-    // first occurrence of '-' otherwise we get a non-existing language zh.
-    // All other languages use a langcode without a '-', so we can safely split
-    // on the first occurrence of it.
-    $generic_tag = '';
-    if (strlen($langcode) > 7 && (substr($langcode, 0, 7) == 'zh-hant' || substr($langcode, 0, 7) == 'zh-hans')) {
-      $generic_tag = substr($langcode, 0, 7);
-    }
-    else {
-      $generic_tag = strtok($langcode, '-');
-    }
-    if (!empty($generic_tag) && !isset($browser_langcodes[$generic_tag])) {
-      // Add the generic langcode, but make sure it has a lower qvalue as the
-      // more specific one, so the more specific one gets selected if it's
-      // defined by both the browser and Drupal.
-      $browser_langcodes[$generic_tag] = $qvalue - 0.1;
-    }
-  }
-
-  // Find the enabled language with the greatest qvalue, following the rules of
-  // RFC 2616 (section 14.4). If several languages have the same qvalue, prefer
-  // the one with the greatest weight.
-  $best_match_langcode = FALSE;
-  $max_qvalue = 0;
-  foreach ($languages as $langcode => $language) {
-    // Language tags are case insensitive (RFC2616, sec 3.10).
-    $langcode = strtolower($langcode);
-
-    // If nothing matches below, the default qvalue is the one of the wildcard
-    // language, if set, or is 0 (which will never match).
-    $qvalue = isset($browser_langcodes['*']) ? $browser_langcodes['*'] : 0;
-
-    // Find the longest possible prefix of the browser-supplied language ('the
-    // language-range') that matches this site language ('the language tag').
-    $prefix = $langcode;
-    do {
-      if (isset($browser_langcodes[$prefix])) {
-        $qvalue = $browser_langcodes[$prefix];
-        break;
-      }
-    }
-    while ($prefix = substr($prefix, 0, strrpos($prefix, '-')));
-
-    // Find the best match.
-    if ($qvalue > $max_qvalue) {
-      $best_match_langcode = $language->id;
-      $max_qvalue = $qvalue;
-    }
-  }
-
-  return $best_match_langcode;
-}
-
-/**
- * Identify language from the user preferences.
- *
- * @param $languages
- *   An array of valid language objects.
- *
- * @return
- *   A valid language code on success, FALSE otherwise.
- */
-function language_from_user($languages) {
-  // User preference (only for authenticated users).
-  $user = \Drupal::currentUser();
-
-  if ($user->id()) {
-    $langcode = $user->getPreferredLangcode();
-    $default_langcode = language_default()->id;
-    if (!empty($langcode) && $langcode != $default_langcode && isset($languages[$langcode])) {
-      return $langcode;
-    }
-  }
-
-  // No language preference from the user.
-  return FALSE;
-}
-
-/**
- * Identifies admin language from the user preferences.
- *
- * @param $languages
- *   An array of valid language objects.
- *
- * @param \Symfony\Component\HttpFoundation\Request|null $request
- *   (optional) The HttpRequest object representing the current request.
- *   Defaults to NULL.
- *
- * @return
- *   A valid language code on success, FALSE otherwise.
- */
-function language_from_user_admin(array $languages, Request $request = NULL) {
-  // User preference (only for authenticated users).
-  $user = \Drupal::currentUser();
-
-  if ($user->id()) {
-    $request_path = $request ? urldecode(trim($request->getPathInfo(), '/')) : _current_path();
-    $langcode = $user->getPreferredAdminLangcode();
-    $default_langcode = language_default()->id;
-    if (!empty($langcode) && $langcode != $default_langcode && isset($languages[$langcode]) && path_is_admin($request_path)) {
-      return $langcode;
-    }
-  }
-
-  // No language preference from the user or not on an admin path.
-  return FALSE;
-}
-
-/**
- * Identify language from a request/session parameter.
- *
- * @param $languages
- *   An array of valid language objects.
- *
- * @return
- *   A valid language code on success, FALSE otherwise.
- */
-function language_from_session($languages) {
-  $param = \Drupal::config('language.negotiation')->get('session.parameter');
-  $query = \Drupal::request()->query;
-
-  // Request parameter: we need to update the session parameter only if we have
-  // an authenticated user.
-  if ($query->has($param) && isset($languages[$langcode = $query->get($param)])) {
-    $user = \Drupal::currentUser();
-    if ($user->id()) {
-      $_SESSION[$param] = $langcode;
-    }
-    return $langcode;
-  }
-
-  // Session parameter.
-  if (isset($_SESSION[$param])) {
-    return $_SESSION[$param];
-  }
-
-  return FALSE;
-}
-
-/**
- * Identify language via URL prefix or domain.
- *
- * @param $languages
- *   An array of valid language objects.
- *
- * @param \Symfony\Component\HttpFoundation\Request|null $request
- *   (optional) The HttpRequest object representing the current request.
- *   Defaults to NULL.
- *
- * @return
- *   A valid language code on success, FALSE otherwise.
- */
-function language_from_url($languages, Request $request = NULL) {
-  $language_url = FALSE;
-
-  if (!language_negotiation_method_enabled(LANGUAGE_NEGOTIATION_URL) || !$request) {
-    return $language_url;
-  }
-
-  switch (\Drupal::config('language.negotiation')->get('url.source')) {
-    case LANGUAGE_NEGOTIATION_URL_PREFIX:
-
-      $request_path = urldecode(trim($request->getPathInfo(), '/'));
-      list($language, $path) = language_url_split_prefix($request_path, $languages);
-
-      if ($language !== FALSE) {
-        $language_url = $language->id;
-      }
-      break;
-
-    case LANGUAGE_NEGOTIATION_URL_DOMAIN:
-      // Get only the host, not the port.
-      $http_host= \Drupal::request()->server->get('HTTP_HOST');
-      if (strpos($http_host, ':') !== FALSE) {
-        $http_host_tmp = explode(':', $http_host);
-        $http_host = current($http_host_tmp);
-      }
-      $domains = language_negotiation_url_domains();
-      foreach ($languages as $language) {
-        // Skip the check if the language doesn't have a domain.
-        if (!empty($domains[$language->id])) {
-          // Ensure that there is exactly one protocol in the URL when checking
-          // the hostname.
-          $host = 'http://' . str_replace(array('http://', 'https://'), '', $domains[$language->id]);
-          $host = parse_url($host, PHP_URL_HOST);
-          if ($http_host == $host) {
-            $language_url = $language->id;
-            break;
-          }
-        }
-      }
-      break;
-  }
-
-  return $language_url;
-}
-
-/**
- * Determines the language to be assigned to URLs when none is detected.
- *
- * The language negotiation process has a fallback chain that ends with the
- * default language negotiation method. Each built-in language type has a
- * separate initialization:
- * - Interface language, which is the only configurable one, always gets a valid
- *   value. If no request-specific language is detected, the default language
- *   will be used.
- * - Content language merely inherits the interface language by default.
- * - URL language is detected from the requested URL and will be used to rewrite
- *   URLs appearing in the page being rendered. If no language can be detected,
- *   there are two possibilities:
- *   - If the default language has no configured path prefix or domain, then the
- *     default language is used. This guarantees that (missing) URL prefixes are
- *     preserved when navigating through the site.
- *   - If the default language has a configured path prefix or domain, a
- *     requested URL having an empty prefix or domain is an anomaly that must be
- *     fixed. This is done by introducing a prefix or domain in the rendered
- *     page matching the detected interface language.
- *
- * @param $languages
- *   (optional) An array of valid language objects. This is passed by
- *   language_negotiation_method_invoke() to every language method callback,
- *   but it is not actually needed here. Defaults to NULL.
- *
- * @param $request
- *   (optional) The HttpRequest object representing the current request.
- *
- * @param $language_type
- *   (optional) The language type to fall back to. Defaults to the interface
- *   language.
- *
- * @return
- *   A valid language code.
- */
-function language_url_fallback($language = NULL, $request = NULL, $language_type = Language::TYPE_INTERFACE) {
-  $default = language_default();
-  $prefix = (\Drupal::config('language.negotiation')->get('url.source') == LANGUAGE_NEGOTIATION_URL_PREFIX);
-
-  // If the default language is not configured to convey language information,
-  // a missing URL language information indicates that URL language should be
-  // the default one, otherwise we fall back to an already detected language.
-  $domains = language_negotiation_url_domains();
-  $prefixes = language_negotiation_url_prefixes();
-  if (($prefix && empty($prefixes[$default->id])) || (!$prefix && empty($domains[$default->id]))) {
-    return $default->id;
-  }
-  else {
-    $langcode = language($language_type)->id;
-    return $langcode;
-  }
-}
-
-/**
- * Return links for the URL language switcher block.
- *
- * Translation links may be provided by other modules.
- */
-function language_switcher_url($type, $path) {
-  $languages = language_list();
-  $links = array();
-
-  foreach ($languages as $language) {
-    $links[$language->id] = array(
-      'href'       => $path,
-      'title'      => $language->name,
-      'language'   => $language,
-      'attributes' => array('class' => array('language-link')),
-    );
-  }
-
-  return $links;
-}
-
-/**
- * Return the session language switcher block.
- */
-function language_switcher_session($type, $path) {
-  $param = \Drupal::config('language.negotiation')->get('session.parameter');
-  $language_query = isset($_SESSION[$param]) ? $_SESSION[$param] : language($type)->id;
-
-  $languages = language_list();
-  $links = array();
-
-  $query = \Drupal::request()->query->all();
-
-  foreach ($languages as $language) {
-    $langcode = $language->id;
-    $links[$langcode] = array(
-      'href'       => $path,
-      'title'      => $language->name,
-      'attributes' => array('class' => array('language-link')),
-      'query'      => $query,
-    );
-    if ($language_query != $langcode) {
-      $links[$langcode]['query'][$param] = $langcode;
-    }
-    else {
-      $links[$langcode]['attributes']['class'][] = ' session-active';
-    }
-  }
-
-  return $links;
-}
-
-/**
- * Reads language prefixes and uses the langcode if no prefix is set.
- */
-function language_negotiation_url_prefixes() {
-  return \Drupal::config('language.negotiation')->get('url.prefixes');
-}
-
-/**
- * Update the list of prefixes from the installed languages.
- */
-function language_negotiation_url_prefixes_update() {
-  $prefixes = language_negotiation_url_prefixes();
-  foreach (language_list() as $language) {
-    // The prefix for this language should be updated if it's not assigned yet
-    // or the prefix is set to the empty string.
-    if (empty($prefixes[$language->id])) {
-      // For the default language, set the prefix to the empty string,
-      // otherwise use the langcode.
-      $prefixes[$language->id] = !empty($language->default) ? '' : $language->id;
-    }
-    // Otherwise we keep the configured prefix.
-  }
-  language_negotiation_url_prefixes_save($prefixes);
-}
-
-/**
- * Saves language prefix settings.
- */
-function language_negotiation_url_prefixes_save(array $prefixes) {
-  \Drupal::config('language.negotiation')
-    ->set('url.prefixes', $prefixes)
-    ->save();
-}
-
-/**
- * Reads language domains.
- */
-function language_negotiation_url_domains() {
-  return \Drupal::config('language.negotiation')->get('url.domains');
-}
-
-/**
- * Saves the language domain settings.
- */
-function language_negotiation_url_domains_save(array $domains) {
-  \Drupal::config('language.negotiation')
-    ->set('url.domains', $domains)
-    ->save();
-}
-
-/**
- * Rewrite URLs for the Session language negotiation method.
- */
-function language_url_rewrite_session(&$path, &$options) {
-  static $query_rewrite, $query_param, $query_value;
-
-  // The following values are not supposed to change during a single page
-  // request processing.
-  if (!isset($query_rewrite)) {
-    $user = \Drupal::currentUser();
-    if (!$user->id()) {
-      $languages = language_list();
-      $query_param = String::checkPlain(\Drupal::config('language.negotiation')->get('session.parameter'));
-      $query = \Drupal::request()->query;
-      if ($query->has($query_param)) {
-        $query_value = String::checkPlain(\Drupal::request()->query->get($query_param));
-      }
-      else {
-        return FALSE;
-      }
-      $query_rewrite = isset($languages[$query_value]) && language_negotiation_method_enabled(LANGUAGE_NEGOTIATION_SESSION);
-    }
-    else {
-      $query_rewrite = FALSE;
-    }
-  }
-
-  // If the user is anonymous, the user language negotiation method is enabled,
-  // and the corresponding option has been set, we must preserve any explicit
-  // user language preference even with cookies disabled.
-  if ($query_rewrite) {
-    if (is_string($options['query'])) {
-      $query = array();
-      parse_str($options['query'], $query);
-      $options['query'] = $query;
-    }
-    if (!isset($options['query'][$query_param])) {
-      $options['query'][$query_param] = $query_value;
-    }
-  }
-}
diff --git a/core/modules/language/language.services.yml b/core/modules/language/language.services.yml
index 05b4798c6d21246735afd8db2ec4b2810c9c4d46..81aede72a9c27c133062f38c5f9338c8f32c6450 100644
--- a/core/modules/language/language.services.yml
+++ b/core/modules/language/language.services.yml
@@ -1,12 +1,9 @@
 services:
-  path_processor_language:
-    class: Drupal\language\HttpKernel\PathProcessorLanguage
-    arguments: ['@config.factory', '@settings', '@language_manager']
-    tags:
-      - { name: path_processor_inbound, priority: 300 }
-      - { name: path_processor_outbound, priority: 100 }
-  language_config_subscriber:
-    class: Drupal\language\LanguageConfigSubscriber
-    tags:
-      - { name: event_subscriber }
-    arguments: ['@language_manager', '@config.factory']
+  plugin.manager.language_negotiation_method:
+    class: Drupal\language\LanguageNegotiationMethodManager
+    arguments: ['@container.namespaces', '@cache.cache', '@module_handler']
+  language_negotiator:
+    class: Drupal\language\LanguageNegotiator
+    arguments: ['@language_manager', '@plugin.manager.language_negotiation_method', '@config.factory', '@settings']
+    calls:
+      - [initLanguageManager]
diff --git a/core/modules/language/lib/Drupal/language/ConfigurableLanguageManager.php b/core/modules/language/lib/Drupal/language/ConfigurableLanguageManager.php
new file mode 100644
index 0000000000000000000000000000000000000000..49c7ea42ac3afe73617c84460824671bedbb52e7
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/ConfigurableLanguageManager.php
@@ -0,0 +1,386 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\ConfigurableLanguageManager.
+ */
+
+namespace Drupal\language;
+
+use Drupal\Component\PhpStorage\PhpStorageFactory;
+use Drupal\Component\Utility\MapArray;
+use Drupal\Core\Config\ConfigFactory;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Language\Language;
+use Drupal\Core\Language\LanguageManager;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Overrides default LanguageManager to provide configured languages.
+ */
+class ConfigurableLanguageManager extends LanguageManager implements ConfigurableLanguageManagerInterface {
+
+  /**
+   * The configuration storage service.
+   *
+   * @var \Drupal\Core\Config\ConfigFactory
+   */
+  protected $configFactory;
+
+  /**
+   * The module handler service.
+   *
+   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   */
+  protected $moduleHandler;
+
+  /**
+   * The request object.
+   *
+   * @var \Symfony\Component\HttpFoundation\Request
+   */
+  protected $request;
+
+  /**
+   * The language negotiator.
+   *
+   * @var \Drupal\language\LanguageNegotiatorInterface
+   */
+  protected $negotiator;
+
+  /**
+   * Local cache for language type configuration data.
+   *
+   * @var array
+   */
+  protected $languageTypes;
+
+  /**
+   * Local cache for language type information.
+   *
+   * @var array
+   */
+  protected $languageTypesInfo;
+
+  /**
+   * An array of language objects keyed by language type.
+   *
+   * @var array
+   */
+  protected $negotiatedLanguages;
+
+  /**
+   * Whether or not the language manager has been initialized.
+   *
+   * @var bool
+   */
+  protected $initialized = FALSE;
+
+  /**
+   * Whether already in the process of language initialization.
+   *
+   * @var bool
+   */
+  protected $initializing = FALSE;
+
+  /**
+   * Rebuild the container to register services needed on multilingual sites.
+   */
+  public static function rebuildServices() {
+    PhpStorageFactory::get('service_container')->deleteAll();
+  }
+
+  /**
+   * Constructs a new ConfigurableLanguageManager object.
+   *
+   * @param \Drupal\Core\Config\ConfigFactory $config_factory
+   *   The configuration storage service.
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   The module handler service.
+   */
+  public function __construct(ConfigFactory $config_factory, ModuleHandlerInterface $module_handler) {
+    $this->configFactory = $config_factory;
+    $this->moduleHandler = $module_handler;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function initConfigOverrides() {
+    $this->configFactory->setLanguage($this->getCurrentLanguage());
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function init() {
+    if (!$this->initialized) {
+      foreach ($this->getDefinedLanguageTypes() as $type) {
+        $this->getCurrentLanguage($type);
+      }
+      $this->initialized = TRUE;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isMultilingual() {
+    return count($this->getLanguages(Language::STATE_CONFIGURABLE)) > 1;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLanguageTypes() {
+    $this->loadLanguageTypesConfiguration();
+    return $this->languageTypes['configurable'];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefinedLanguageTypes() {
+    $this->loadLanguageTypesConfiguration();
+    return $this->languageTypes['all'];
+  }
+
+  /**
+   * Retrieves language types from the configuration storage.
+   *
+   * @return array
+   *   An array of language type names.
+   */
+  protected function loadLanguageTypesConfiguration() {
+    if (!$this->languageTypes) {
+      $this->languageTypes = $this->configFactory->get('language.types')->get() ?: array('configurable' => array(), 'all' => parent::getLanguageTypes());
+    }
+    return $this->languageTypes;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefinedLanguageTypesInfo() {
+    if (!isset($this->languageTypesInfo)) {
+      $info = $this->moduleHandler->invokeAll('language_types_info');
+      // Let other modules alter the list of language types.
+      $this->moduleHandler->alter('language_types_info', $info);
+      $this->languageTypesInfo = $info;
+    }
+    return $this->languageTypesInfo;
+  }
+
+  /**
+   * Stores language types configuration.
+   */
+  public function saveLanguageTypesConfiguration(array $config) {
+    $this->configFactory->get('language.types')->setData($config)->save();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCurrentLanguage($type = Language::TYPE_INTERFACE) {
+    if (!isset($this->negotiatedLanguages[$type])) {
+      // Ensure we have a valid value for this language type.
+      $this->negotiatedLanguages[$type] = $this->getDefaultLanguage();
+
+      if ($this->negotiator && $this->isMultilingual()) {
+        if (!$this->initializing) {
+          $this->initializing = TRUE;
+          $this->negotiatedLanguages[$type] = $this->negotiator->initializeType($type);
+          $this->initializing = FALSE;
+        }
+        // If the current interface language needs to be retrieved during
+        // initialization we return the system language. This way string
+        // translation calls happening during initialization will return the
+        // original strings which can be translated by calling them again
+        // afterwards. This can happen for instance while parsing negotiation
+        // method definitions.
+        elseif ($type == Language::TYPE_INTERFACE) {
+          return new Language(array('id' => Language::LANGCODE_SYSTEM));
+        }
+      }
+    }
+
+    return $this->negotiatedLanguages[$type];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function reset($type = NULL) {
+    if (!isset($type)) {
+      $this->initialized = FALSE;
+      $this->negotiatedLanguages = array();
+      $this->languageTypes = NULL;
+      $this->languageTypesInfo = NULL;
+      $this->languages = NULL;
+      $this->defaultLanguage = NULL;
+      if ($this->negotiator) {
+        $this->negotiator->reset();
+      }
+    }
+    elseif (isset($this->negotiatedLanguages[$type])) {
+      unset($this->negotiatedLanguages[$type]);
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setRequest(Request $request) {
+    $this->request = $request;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getNegotiator() {
+    return $this->negotiator;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setNegotiator(LanguageNegotiatorInterface $negotiator) {
+    $this->negotiator = $negotiator;
+    $this->initialized = FALSE;
+    $this->negotiatedLanguages = array();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getDefaultLanguage() {
+    if (!isset($this->defaultLanguage)) {
+      // @todo Convert to CMI https://drupal.org/node/1827038 and
+      //   https://drupal.org/node/2108599.
+      $default_info = variable_get('language_default', Language::$defaultValues);
+      $this->defaultLanguage = new Language($default_info + array('default' => TRUE));
+    }
+    return $this->defaultLanguage;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLanguages($flags = Language::STATE_CONFIGURABLE) {
+    if (!isset($this->languages)) {
+      // Prepopulate the language list with the default language to keep things
+      // working even if we have no configuration.
+      $default = $this->getDefaultLanguage();
+      $this->languages = array($default->id => $default);
+
+      // Retrieve the config storage to list available languages.
+      $prefix = 'language.entity.';
+      $storage = $this->configFactory->get($prefix . Language::LANGCODE_NOT_SPECIFIED)->getStorage();
+      $config_ids = $storage->listAll($prefix);
+
+      // Instantiate languages from config objects.
+      $weight = 0;
+      foreach ($this->configFactory->loadMultiple($config_ids) as $config) {
+        $data = $config->get();
+        $langcode = $data['id'];
+        // Initialize default property so callers have an easy reference and can
+        // save the same object without data loss.
+        $data['default'] = ($langcode == $default->id);
+        $data['name'] = $data['label'];
+        $this->languages[$langcode] = new Language($data);
+        $weight = max(array($weight, $this->languages[$langcode]->weight));
+      }
+
+      // Add locked languages, they will be filtered later if needed.
+      $this->languages += $this->getDefaultLockedLanguages($weight);
+
+      // Sort the language list by weight.
+      Language::sort($this->languages);
+    }
+
+    return parent::getLanguages($flags);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function updateLockedLanguageWeights() {
+    $max_weight = 0;
+
+    // Get maximum weight to update the system languages to keep them on bottom.
+    foreach ($this->getLanguages(Language::STATE_CONFIGURABLE) as $language) {
+      if (!$language->locked && $language->weight > $max_weight) {
+        $max_weight = $language->weight;
+      }
+    }
+
+    // Loop locked languages to maintain the existing order.
+    $locked_languages = $this->getLanguages(Language::STATE_LOCKED);
+    $config_ids = array_map(function($language) { return 'language.entity.' . $language->id; }, $locked_languages);
+    foreach ($this->configFactory->loadMultiple($config_ids) as $config_id => $config) {
+      // Update system languages weight.
+      $max_weight++;
+      $config->set('weight', $max_weight);
+      $config->save();
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getFallbackCandidates($langcode = NULL, array $context = array()) {
+    if ($this->isMultilingual()) {
+      // Get languages ordered by weight, add Language::LANGCODE_NOT_SPECIFIED
+      // at the end.
+      $candidates = array_keys($this->getLanguages());
+      $candidates[] = Language::LANGCODE_NOT_SPECIFIED;
+      $candidates = MapArray::copyValuesToKeys($candidates);
+
+      // The first candidate should always be the desired language if specified.
+      if (!empty($langcode)) {
+        $candidates = array($langcode => $langcode) + $candidates;
+      }
+
+      // Let other modules hook in and add/change candidates.
+      $type = 'language_fallback_candidates';
+      $types = array();
+      if (!empty($context['operation'])) {
+        $types[] = $type . '_' .  $context['operation'];
+      }
+      $types[] = $type;
+      $this->moduleHandler->alter($types, $candidates, $context);
+    }
+    else {
+      $candidates = parent::getFallbackCandidates($langcode, $context);
+    }
+
+    return $candidates;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLanguageSwitchLinks($type, $path) {
+    $links = FALSE;
+
+    if ($this->negotiator) {
+      foreach ($this->negotiator->getNegotiationMethods($type) as $method_id => $method) {
+        $reflector = new \ReflectionClass($method['class']);
+
+        if ($reflector->implementsInterface('\Drupal\language\LanguageSwitcherInterface')) {
+          $result = $this->negotiator->getNegotiationMethodInstance($method_id)->getLanguageSwitchLinks($this->request, $type, $path);
+
+          if (!empty($result)) {
+            // Allow modules to provide translations for specific links.
+            $this->moduleHandler->alter('language_switch_links', $result, $type, $path);
+            $links = (object) array('links' => $result, 'method_id' => $method_id);
+            break;
+          }
+        }
+      }
+    }
+
+    return $links;
+  }
+
+}
diff --git a/core/modules/language/lib/Drupal/language/ConfigurableLanguageManagerInterface.php b/core/modules/language/lib/Drupal/language/ConfigurableLanguageManagerInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..c9a13d655d4daf2c20fe543c62a0046154f55135
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/ConfigurableLanguageManagerInterface.php
@@ -0,0 +1,86 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\ConfigurableLanguageManagerInterface
+ */
+
+namespace Drupal\language;
+
+use Drupal\Core\Language\Language;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Common interface for language negotiation services.
+ */
+interface ConfigurableLanguageManagerInterface extends LanguageManagerInterface {
+
+  /**
+   * Injects the request object.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request
+   *   The request object.
+   */
+  public function setRequest(Request $request);
+
+  /**
+   * Returns the language negotiator.
+   *
+   * @retun \Drupal\language\LanguageNegotiatorInterface
+   *   The language negotiator.
+   */
+  public function getNegotiator();
+
+  /**
+   * Injects the language negotiator.
+   *
+   * @param \Drupal\language\LanguageNegotiatorInterface $negotiator
+   *   The language negotiator.
+   */
+  public function setNegotiator(LanguageNegotiatorInterface $negotiator);
+
+  /**
+   * Returns all the defined language types including fixed ones.
+   *
+   * A language type maybe configurable or fixed. A fixed language type is a
+   * type whose language negotiation methods are module-defined and not altered
+   * through the user interface.
+   *
+   * @return array
+   *   An array of language type names.
+   */
+  public function getDefinedLanguageTypes();
+
+  /**
+   * Returns information about all defined language types.
+   *
+   * @return array
+   *   An associative array of language type information arrays keyed by type
+   *   names. Based on information from hook_language_types_info().
+   *
+   * @see hook_language_types_info()
+   */
+  public function getDefinedLanguageTypesInfo();
+
+  /**
+   * Stores language types configuration.
+   *
+   * @param array
+   *   An indexed array with the following keys_
+   *   - configurable: an array of configurable language type names.
+   *   - all: an array of all the defined language type names.
+   */
+  public function saveLanguageTypesConfiguration(array $config);
+
+  /**
+   * Updates locked system language weights.
+   */
+  public function updateLockedLanguageWeights();
+
+  /**
+   * Initializes per-language overrides for configuration.
+   */
+  public function initConfigOverrides();
+
+}
diff --git a/core/modules/language/lib/Drupal/language/Entity/Language.php b/core/modules/language/lib/Drupal/language/Entity/Language.php
index 6ae43852b8b9c8dc79e411ba455394741d3854a9..60b06a149a9010138ccfb78194929ea26334a2db 100644
--- a/core/modules/language/lib/Drupal/language/Entity/Language.php
+++ b/core/modules/language/lib/Drupal/language/Entity/Language.php
@@ -9,7 +9,6 @@
 
 use Drupal\Core\Config\Entity\ConfigEntityBase;
 use Drupal\Core\Entity\EntityStorageControllerInterface;
-use Drupal\Core\Language\LanguageManager;
 use Drupal\language\LanguageInterface;
 
 /**
diff --git a/core/lib/Drupal/Core/EventSubscriber/LanguageRequestSubscriber.php b/core/modules/language/lib/Drupal/language/EventSubscriber/LanguageRequestSubscriber.php
similarity index 53%
rename from core/lib/Drupal/Core/EventSubscriber/LanguageRequestSubscriber.php
rename to core/modules/language/lib/Drupal/language/EventSubscriber/LanguageRequestSubscriber.php
index d50022dae164491416c780c45ab517dfee6133a6..c9551f440fed06ed0706838b4489fc4cb09a1c4d 100644
--- a/core/lib/Drupal/Core/EventSubscriber/LanguageRequestSubscriber.php
+++ b/core/modules/language/lib/Drupal/language/EventSubscriber/LanguageRequestSubscriber.php
@@ -2,14 +2,16 @@
 
 /**
  * @file
- * Contains \Drupal\Core\EventSubscriber\LanguageRequestSubscriber.
+ * Contains \Drupal\language\EventSubscriber\LanguageRequestSubscriber.
  */
 
-namespace Drupal\Core\EventSubscriber;
+namespace Drupal\language\EventSubscriber;
 
 use Drupal\Core\Language\Language;
-use Drupal\Core\Language\LanguageManager;
+use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\StringTranslation\Translator\TranslatorInterface;
+use Drupal\language\ConfigurableLanguageManagerInterface;
+use Drupal\language\LanguageNegotiatorInterface;
 use Symfony\Component\HttpKernel\HttpKernelInterface;
 use Symfony\Component\HttpKernel\KernelEvents;
 use Symfony\Component\HttpKernel\Event\GetResponseEvent;
@@ -23,10 +25,17 @@ class LanguageRequestSubscriber implements EventSubscriberInterface {
   /**
    * The language manager service.
    *
-   * @var \Drupal\Core\Language\LanguageManager
+   * @var \Drupal\language\ConfigurableLanguageManagerInterface
    */
   protected $languageManager;
 
+  /**
+   * The language negotiator.
+   *
+   * @var \Drupal\language\LanguageNegotiatorInterface
+   */
+  protected $negotiator;
+
   /**
    * The translation service.
    *
@@ -34,18 +43,30 @@ class LanguageRequestSubscriber implements EventSubscriberInterface {
    */
   protected $translation;
 
+  /**
+   * The current active user.
+   *
+   * @return \Drupal\Core\Session\AccountInterface
+   */
+  protected $currentUser;
+
   /**
    * Constructs a LanguageRequestSubscriber object.
    *
-   * @param \Drupal\Core\Language\LanguageManager $language_manager
+   * @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager
    *   The language manager service.
-   *
+   * @param \Drupal\language\LanguageNegotiatorInterface
+   *   The language negotiator.
    * @param \Drupal\Core\Translation\Translator\TranslatorInterface $translation
    *   The translation service.
+   * @param \Drupal\Core\Session\AccountInterface $current_user
+   *   The current active user.
    */
-  public function __construct(LanguageManager $language_manager, TranslatorInterface $translation) {
+  public function __construct(ConfigurableLanguageManagerInterface $language_manager, LanguageNegotiatorInterface $negotiator, TranslatorInterface $translation, AccountInterface $current_user) {
     $this->languageManager = $language_manager;
+    $this->negotiator = $negotiator;
     $this->translation = $translation;
+    $this->currentUser = $current_user;
   }
 
   /**
@@ -56,10 +77,17 @@ public function __construct(LanguageManager $language_manager, TranslatorInterfa
    */
   public function onKernelRequestLanguage(GetResponseEvent $event) {
     if ($event->getRequestType() == HttpKernelInterface::MASTER_REQUEST) {
-      $this->languageManager->setRequest($event->getRequest());
+      $request = $event->getRequest();
+      $this->negotiator->setCurrentUser($this->currentUser);
+      $this->negotiator->setRequest($request);
+      if ($this->languageManager instanceof ConfigurableLanguageManagerInterface) {
+        $this->languageManager->setNegotiator($this->negotiator);
+        $this->languageManager->setRequest($request);
+        $this->languageManager->initConfigOverrides();
+      }
       // After the language manager has initialized, set the default langcode
       // for the string translations.
-      $langcode = $this->languageManager->getLanguage(Language::TYPE_INTERFACE)->id;
+      $langcode = $this->languageManager->getCurrentLanguage()->id;
       $this->translation->setDefaultLangcode($langcode);
     }
   }
diff --git a/core/modules/language/lib/Drupal/language/Form/LanguageAddForm.php b/core/modules/language/lib/Drupal/language/Form/LanguageAddForm.php
index bca5523c5f76ebe05fa7ba77b19ab982b7a59ccc..aa65f91bb168ea625643b3f3180b35f979a050b6 100644
--- a/core/modules/language/lib/Drupal/language/Form/LanguageAddForm.php
+++ b/core/modules/language/lib/Drupal/language/Form/LanguageAddForm.php
@@ -98,6 +98,7 @@ public function submitForm(array &$form, array &$form_state) {
     }
     // Save the language and inform the user that it happened.
     $language = language_save($language);
+
     drupal_set_message($this->t('The language %language has been created and can now be used.', array('%language' => $language->name)));
 
     // Tell the user they have the option to add a language switcher block
diff --git a/core/modules/language/lib/Drupal/language/Form/LanguageEditForm.php b/core/modules/language/lib/Drupal/language/Form/LanguageEditForm.php
index 66e1ed17df3a0b4bc5c7987029722920e2a3cbcf..2dacdc2785b5d61626bb94bbfa1e34521e1dec9c 100644
--- a/core/modules/language/lib/Drupal/language/Form/LanguageEditForm.php
+++ b/core/modules/language/lib/Drupal/language/Form/LanguageEditForm.php
@@ -54,6 +54,7 @@ public function submitForm(array &$form, array &$form_state) {
     $language->name = $form_state['values']['name'];
     $language->direction = $form_state['values']['direction'];
     language_save($language);
+
     $form_state['redirect_route']['route_name'] = 'language.admin_overview';
   }
 
diff --git a/core/modules/language/lib/Drupal/language/Form/NegotiationConfigureForm.php b/core/modules/language/lib/Drupal/language/Form/NegotiationConfigureForm.php
index 01dec5bbcf78b6ed79ce1b765f2b5fc16bfe68db..c5b2a667d46a4ccb2032d717834355bf6e22c066 100644
--- a/core/modules/language/lib/Drupal/language/Form/NegotiationConfigureForm.php
+++ b/core/modules/language/lib/Drupal/language/Form/NegotiationConfigureForm.php
@@ -14,6 +14,9 @@
 use Drupal\Core\Config\ConfigFactory;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Form\FormBase;
+use Drupal\language\ConfigurableLanguageManagerInterface;
+use Drupal\language\LanguageNegotiatorInterface;
+use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationSelected;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -22,36 +25,49 @@
 class NegotiationConfigureForm extends FormBase {
 
   /**
-   * Stores the configuration object for system.language.types.
+   * Stores the configuration object for language.types.
    *
    * @var \Drupal\Core\Config\Config
    */
-  protected $languageTypesConfig;
+  protected $languageTypes;
 
   /**
-   * The block manager.
+   * The language manager.
    *
-   * @var \Drupal\block\Plugin\Type\BlockManager
+   * @var \Drupal\language\ConfigurableLanguageManagerInterface
    */
-  protected $blockManager;
+  protected $languageManager;
 
   /**
-   * The module handler.
+   * The language negotiator.
    *
-   * @var \Drupal\Core\Extension\ModuleHandlerInterface
+   * @var \Drupal\language\LanguageNegotiatorInterface
    */
-  protected $moduleHandler;
+  protected $negotiator;
+
+  /**
+   * The block manager.
+   *
+   * @var \Drupal\block\Plugin\Type\BlockManager
+   */
+  protected $blockManager;
 
   /**
    * Constructs a NegotiationConfigureForm object.
    *
    * @param \Drupal\Core\Config\ConfigFactory $config_factory
    *   The factory for configuration objects.
+   * @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager
+   *   The language manager.
+   * @param \Drupal\language\LanguageNegotiatorInterface $negotiator
+   *   The language negotiation methods manager.
    * @param \Drupal\block\Plugin\Type\BlockManager $block_manager
    *   The block manager, or NULL if not available.
    */
-  public function __construct(ConfigFactory $config_factory, BlockManager $block_manager = NULL) {
-    $this->languageTypesConfig = $config_factory->get('system.language.types');
+  public function __construct(ConfigFactory $config_factory, ConfigurableLanguageManagerInterface $language_manager, LanguageNegotiatorInterface $negotiator, BlockManager $block_manager = NULL) {
+    $this->languageTypes = $config_factory->get('language.types');
+    $this->languageManager = $language_manager;
+    $this->negotiator = $negotiator;
     $this->blockManager = $block_manager;
   }
 
@@ -61,6 +77,8 @@ public function __construct(ConfigFactory $config_factory, BlockManager $block_m
   public static function create(ContainerInterface $container) {
     return new static(
       $container->get('config.factory'),
+      $container->get('language_manager'),
+      $container->get('language_negotiator'),
       $container->has('plugin.manager.block') ? $container->get('plugin.manager.block') : NULL
     );
   }
@@ -76,14 +94,12 @@ public function getFormID() {
    * {@inheritdoc}
    */
   public function buildForm(array $form, array &$form_state) {
-    language_negotiation_include();
-
-    $configurable = $this->languageTypesConfig->get('configurable');
+    $configurable = $this->languageTypes->get('configurable');
 
     $form = array(
       '#theme' => 'language_negotiation_configure_form',
-      '#language_types_info' => language_types_info(),
-      '#language_negotiation_info' => language_negotiation_info(),
+      '#language_types_info' => $this->languageManager->getDefinedLanguageTypesInfo(),
+      '#language_negotiation_info' => $this->negotiator->getNegotiationMethods(),
     );
     $form['#language_types'] = array();
 
@@ -111,10 +127,9 @@ public function buildForm(array $form, array &$form_state) {
    * {@inheritdoc}
    */
   public function submitForm(array &$form, array &$form_state) {
-    language_negotiation_include();
     $configurable_types = $form['#language_types'];
 
-    $stored_values = $this->languageTypesConfig->get('configurable');
+    $stored_values = $this->languageTypes->get('configurable');
     $customized = array();
     $method_weights_type = array();
 
@@ -122,7 +137,7 @@ public function submitForm(array &$form, array &$form_state) {
       $customized[$type] = in_array($type, $stored_values);
       $method_weights = array();
       $enabled_methods = $form_state['values'][$type]['enabled'];
-      $enabled_methods[LANGUAGE_NEGOTIATION_SELECTED] = TRUE;
+      $enabled_methods[LanguageNegotiationSelected::METHOD_ID] = TRUE;
       $method_weights_input = $form_state['values'][$type]['weight'];
       if (isset($form_state['values'][$type]['configurable'])) {
         $customized[$type] = !empty($form_state['values'][$type]['configurable']);
@@ -141,11 +156,11 @@ public function submitForm(array &$form, array &$form_state) {
 
     // Update non-configurable language types and the related language
     // negotiation configuration.
-    language_types_set(array_keys(array_filter($customized)));
+    $this->negotiator->updateConfiguration(array_keys(array_filter($customized)));
 
     // Update the language negotiations after setting the configurability.
     foreach ($method_weights_type as $type => $method_weights) {
-      language_negotiation_set($type, $method_weights);
+      $this->negotiator->saveConfiguration($type, $method_weights);
     }
 
     // Clear block definitions cache since the available blocks and their names
@@ -183,7 +198,7 @@ protected function configureFormTable(array &$form, $type)  {
     );
     // Only show configurability checkbox for the unlocked language types.
     if (empty($info['locked'])) {
-      $configurable = $this->languageTypesConfig->get('configurable');
+      $configurable = $this->languageTypes->get('configurable');
       $table_form['configurable'] = array(
         '#type' => 'checkbox',
         '#title' => $this->t('Customize %language_name language detection to differ from User interface text language detection settings.', array('%language_name' => $info['name'])),
@@ -246,7 +261,7 @@ protected function configureFormTable(array &$form, $type)  {
           '#title_display' => 'invisible',
           '#default_value' => $enabled,
         );
-        if ($method_id === LANGUAGE_NEGOTIATION_SELECTED) {
+        if ($method_id === LanguageNegotiationSelected::METHOD_ID) {
           $table_form['enabled'][$method_id]['#default_value'] = TRUE;
           $table_form['enabled'][$method_id]['#attributes'] = array('disabled' => 'disabled');
         }
@@ -254,10 +269,10 @@ protected function configureFormTable(array &$form, $type)  {
         $table_form['description'][$method_id] = array('#markup' => Xss::filterAdmin($method['description']));
 
         $config_op = array();
-        if (isset($method['config'])) {
+        if (isset($method['config_path'])) {
           $config_op['configure'] = array(
             'title' => $this->t('Configure'),
-            'href' => $method['config'],
+            'href' => $method['config_path'],
           );
           // If there is at least one operation enabled show the operation
           // column.
diff --git a/core/modules/language/lib/Drupal/language/Form/NegotiationUrlForm.php b/core/modules/language/lib/Drupal/language/Form/NegotiationUrlForm.php
index ac709e30b95218331ffe1fe049087acf58732321..172e6b0ab0ea8bb63ce3413eed0b5b96ea68e709 100644
--- a/core/modules/language/lib/Drupal/language/Form/NegotiationUrlForm.php
+++ b/core/modules/language/lib/Drupal/language/Form/NegotiationUrlForm.php
@@ -8,6 +8,7 @@
 namespace Drupal\language\Form;
 
 use Drupal\Core\Form\ConfigFormBase;
+use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
 
 /**
  * Configure the URL language negotiation method for this site.
@@ -27,14 +28,13 @@ public function getFormId() {
   public function buildForm(array $form, array &$form_state) {
     global $base_url;
     $config = $this->configFactory->get('language.negotiation');
-    language_negotiation_include();
 
     $form['language_negotiation_url_part'] = array(
       '#title' => t('Part of the URL that determines language'),
       '#type' => 'radios',
       '#options' => array(
-        LANGUAGE_NEGOTIATION_URL_PREFIX => t('Path prefix'),
-        LANGUAGE_NEGOTIATION_URL_DOMAIN => t('Domain'),
+        LanguageNegotiationUrl::CONFIG_PATH_PREFIX => t('Path prefix'),
+        LanguageNegotiationUrl::CONFIG_DOMAIN => t('Domain'),
       ),
       '#default_value' => $config->get('url.source'),
     );
@@ -47,7 +47,7 @@ public function buildForm(array $form, array &$form_state) {
       '#states' => array(
         'visible' => array(
           ':input[name="language_negotiation_url_part"]' => array(
-            'value' => (string) LANGUAGE_NEGOTIATION_URL_PREFIX,
+            'value' => (string) LanguageNegotiationUrl::CONFIG_PATH_PREFIX,
           ),
         ),
       ),
@@ -60,7 +60,7 @@ public function buildForm(array $form, array &$form_state) {
       '#states' => array(
         'visible' => array(
           ':input[name="language_negotiation_url_part"]' => array(
-            'value' => (string) LANGUAGE_NEGOTIATION_URL_DOMAIN,
+            'value' => (string) LanguageNegotiationUrl::CONFIG_DOMAIN,
           ),
         ),
       ),
@@ -103,7 +103,7 @@ public function validateForm(array &$form, array &$form_state) {
       $value = $form_state['values']['prefix'][$langcode];
 
       if ($value === '') {
-        if (!$language->default && $form_state['values']['language_negotiation_url_part'] == LANGUAGE_NEGOTIATION_URL_PREFIX) {
+        if (!$language->default && $form_state['values']['language_negotiation_url_part'] == LanguageNegotiationUrl::CONFIG_PATH_PREFIX) {
           // Throw a form error if the prefix is blank for a non-default language,
           // although it is required for selected negotiation type.
           $this->setFormError("prefix][$langcode", $form_state, t('The prefix may only be left blank for the default language.'));
@@ -127,7 +127,7 @@ public function validateForm(array &$form, array &$form_state) {
       $value = $form_state['values']['domain'][$langcode];
 
       if ($value === '') {
-        if (!$language->default && $form_state['values']['language_negotiation_url_part'] == LANGUAGE_NEGOTIATION_URL_DOMAIN) {
+        if (!$language->default && $form_state['values']['language_negotiation_url_part'] == LanguageNegotiationUrl::CONFIG_DOMAIN) {
           // Throw a form error if the domain is blank for a non-default language,
           // although it is required for selected negotiation type.
           $this->setFormError("domain][$langcode", $form_state, t('The domain may only be left blank for the default language.'));
diff --git a/core/modules/language/lib/Drupal/language/HttpKernel/PathProcessorLanguage.php b/core/modules/language/lib/Drupal/language/HttpKernel/PathProcessorLanguage.php
index d212c7b2896d0b584ecb28d212260ddecf2aa218..71a5cfe3b68550ab73be4d248005ca9c69790a74 100644
--- a/core/modules/language/lib/Drupal/language/HttpKernel/PathProcessorLanguage.php
+++ b/core/modules/language/lib/Drupal/language/HttpKernel/PathProcessorLanguage.php
@@ -8,13 +8,14 @@
 namespace Drupal\language\HttpKernel;
 
 use Drupal\Component\Utility\Settings;
+use Drupal\Component\Utility\Unicode;
 use Drupal\Core\Config\ConfigFactory;
-use Drupal\Core\Language\Language;
-use Drupal\Core\Language\LanguageManager;
 use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
 use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
-use Symfony\Component\HttpKernel\HttpKernelInterface;
+use Drupal\language\ConfigurableLanguageManagerInterface;
+use Drupal\language\LanguageNegotiatorInterface;
 use Symfony\Component\HttpFoundation\Request;
+use Drupal\Core\Session\AccountInterface;
 
 /**
  * Processes the inbound path using path alias lookups.
@@ -38,129 +39,114 @@ class PathProcessorLanguage implements InboundPathProcessorInterface, OutboundPa
   /**
    * Language manager for retrieving the url language type.
    *
-   * @var \Drupal\Core\Language\LanguageManager
+   * @var \Drupal\language\ConfigurableLanguageManagerInterface
    */
   protected $languageManager;
 
   /**
-   * An array of enabled languages.
+   * The language negotiator.
+   *
+   * @var \Drupal\language\LanguageNegotiatorInterface
+   */
+  protected $negotiator;
+
+  /**
+   * Local cache for language path processors.
    *
    * @var array
    */
-  protected $languages;
+  protected $processors;
 
+  /**
+   * Flag indicating whether the site is multilingual.
+   *
+   * @var bool
+   */
+  protected $multilingual;
 
   /**
    * Constructs a PathProcessorLanguage object.
    *
    * @param \Drupal\Core\Config\ConfigFactory $config
    *   A config factory object for retrieving configuration settings.
-   *
-   * @param array $languages
-   *   An array of languages, keyed by language code, representing the languages
-   *   currently enabled on the site.
+   * @param \Drupal\Component\Utility\Settings $settings
+   *   The settings instance.
+   * @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager
+   *   The configurable language manager.
+   * @param \Drupal\language\LanguageNegotiatorInterface
+   *   The language negotiator.
+   * @param \Drupal\Core\Session\AccountInterface $current_user
+   *   The current active user.
    */
-  public function __construct(ConfigFactory $config, Settings $settings, LanguageManager $language_manager, array $languages = array()) {
+  public function __construct(ConfigFactory $config, Settings $settings, ConfigurableLanguageManagerInterface $language_manager, LanguageNegotiatorInterface $negotiator, AccountInterface $current_user) {
     $this->config = $config;
     $this->mixedModeSessions = $settings->get('mixed_mode_sessions', FALSE);
     $this->languageManager = $language_manager;
-    if (empty($languages)) {
-      $languages = language_list();
-    }
-    $this->languages = $languages;
+    $this->negotiator = $negotiator;
+    $this->negotiator->setCurrentUser($current_user);
   }
 
   /**
-   * Implements Drupal\Core\PathProcessor\InboundPathProcessorInterface::processInbound().
+   * {@inheritdoc}
    */
   public function processInbound($path, Request $request) {
     if (!empty($path)) {
-      $args = explode('/', $path);
-      $prefix = array_shift($args);
-
-      // Search prefix within enabled languages.
-      $prefixes = $this->config->get('language.negotiation')->get('url.prefixes');
-      foreach ($this->languages as $language) {
-        if (isset($prefixes[$language->id]) && $prefixes[$language->id] == $prefix) {
-          // Rebuild $path with the language removed.
-          return implode('/', $args);
-        }
+      $scope = 'inbound';
+      if (!isset($this->processors[$scope])) {
+        $this->initProcessors($scope);
+      }
+      foreach ($this->processors[$scope] as $instance) {
+        $path = $instance->processInbound($path, $request);
       }
     }
     return $path;
   }
 
   /**
-   * Implements Drupal\Core\PathProcessor\InboundPathProcessorInterface::processOutbound().
+   * {@inheritdoc}
    */
   public function processOutbound($path, &$options = array(), Request $request = NULL) {
-    if (!$this->languageManager->isMultilingual()) {
-      return $path;
-    }
-    $url_scheme = 'http';
-    $port = 80;
-    if ($request) {
-      $url_scheme = $request->getScheme();
-      $port = $request->getPort();
-    }
-    $languages = array_flip(array_keys($this->languages));
-    // Language can be passed as an option, or we go for current URL language.
-    if (!isset($options['language'])) {
-      $language_url = $this->languageManager->getLanguage(Language::TYPE_URL);
-      $options['language'] = $language_url;
-    }
-    // We allow only enabled languages here.
-    elseif (is_object($options['language']) && !isset($languages[$options['language']->id])) {
-      return $path;
+    if (!isset($this->multilingual)) {
+      $this->multilingual = $this->languageManager->isMultilingual();
     }
-    $url_source = $this->config->get('language.negotiation')->get('url.source');
-    // @todo Go back to using a constant instead of the string 'path_prefix' once we can use a class
-    //   constant.
-    if ($url_source == 'path_prefix') {
-      $prefixes = $this->config->get('language.negotiation')->get('url.prefixes');
-      if (is_object($options['language']) && !empty($prefixes[$options['language']->id])) {
-        return empty($path) ? $prefixes[$options['language']->id] : $prefixes[$options['language']->id] . '/' . $path;
+    if ($this->multilingual) {
+      $this->negotiator->setRequest($request);
+      $scope = 'outbound';
+      if (!isset($this->processors[$scope])) {
+        $this->initProcessors($scope);
+      }
+      // Execute outbound language processors.
+      $options['mixed_mode_sessions'] = $this->mixedModeSessions;
+      foreach ($this->processors[$scope] as $instance) {
+        $path = $instance->processOutbound($path, $options, $request);
+      }
+      // No language dependent path allowed in this mode.
+      if (empty($this->processors[$scope])) {
+        unset($options['language']);
       }
     }
-    elseif ($url_source == 'domain') {
-      $domains = $this->config->get('language.negotiation')->get('url.domains');
-      if (is_object($options['language']) && !empty($domains[$options['language']->id])) {
-
-        // Save the original base URL. If it contains a port, we need to
-        // retain it below.
-        if (!empty($options['base_url'])) {
-          // The colon in the URL scheme messes up the port checking below.
-          $normalized_base_url = str_replace(array('https://', 'http://'), '', $options['base_url']);
-        }
-
-        // Ask for an absolute URL with our modified base URL.
-        $options['absolute'] = TRUE;
-        $options['base_url'] = $url_scheme . '://' . $domains[$options['language']->id];
-
-        // In case either the original base URL or the HTTP host contains a
-        // port, retain it.
-        if (isset($normalized_base_url) && strpos($normalized_base_url, ':') !== FALSE) {
-          list( , $port) = explode(':', $normalized_base_url);
-          $options['base_url'] .= ':' . $port;
-        }
-        elseif ($port != 80) {
-          $options['base_url'] .= ':' . $port;
-        }
+    return $path;
+  }
 
-        if (isset($options['https']) && $this->mixedModeSessions) {
-          if ($options['https'] === TRUE) {
-            $options['base_url'] = str_replace('http://', 'https://', $options['base_url']);
-          }
-          elseif ($options['https'] === FALSE) {
-            $options['base_url'] = str_replace('https://', 'http://', $options['base_url']);
+  /**
+   * Initializes the local cache for language path processors.
+   *
+   * @param string $scope
+   *   The scope of the processors: "inbound" or "outbound".
+   */
+  protected function initProcessors($scope) {
+    $interface = '\Drupal\Core\PathProcessor\\' . Unicode::ucfirst($scope) . 'PathProcessorInterface';
+    $this->processors[$scope] = array();
+    foreach ($this->languageManager->getLanguageTypes() as $type) {
+      foreach ($this->negotiator->getNegotiationMethods($type) as $method_id => $method) {
+        if (!isset($this->processors[$scope][$method_id])) {
+          $reflector = new \ReflectionClass($method['class']);
+          if ($reflector->implementsInterface($interface)) {
+            $this->processors[$scope][$method_id] = $this->negotiator->getNegotiationMethodInstance($method_id);
           }
         }
-
-        // Add Drupal's subfolder from the base_path if there is one.
-        $options['base_url'] .= rtrim(base_path(), '/');
       }
     }
-    return $path;
   }
 
 }
diff --git a/core/modules/language/lib/Drupal/language/LanguageConfigSubscriber.php b/core/modules/language/lib/Drupal/language/LanguageConfigSubscriber.php
deleted file mode 100644
index 702b85760ac73320a543933c769a770d2b26578a..0000000000000000000000000000000000000000
--- a/core/modules/language/lib/Drupal/language/LanguageConfigSubscriber.php
+++ /dev/null
@@ -1,68 +0,0 @@
-<?php
-/**
- * @file
- * Contains \Drupal\language\LanguageConfigSubscriber.
- */
-
-namespace Drupal\language;
-
-use Drupal\Core\Config\ConfigFactory;
-use Drupal\Core\Config\StorageDispatcher;
-use Drupal\Core\Language\LanguageManager;
-use Symfony\Component\HttpKernel\KernelEvents;
-use Symfony\Component\HttpKernel\Event\GetResponseEvent;
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-
-/**
- * Language event subscriber to set language on configuration factory service.
- */
-class LanguageConfigSubscriber implements EventSubscriberInterface {
-
-  /**
-   * The language manager.
-   *
-   * @var \Drupal\Core\Language\LanguageManager
-   */
-  protected $languageManager;
-
-  /**
-   * The configuration factory.
-   *
-   * @var \Drupal\Core\Config\ConfigFactory
-   */
-  protected $configFactory;
-
-  /**
-   * Constructs a LanguageConfigSubscriber object.
-   *
-   * @param \Drupal\Core\Language\LanguageManager $language_manager
-   *   The language manager service.
-   * @param \Drupal\Core\Config\ConfigFactory $config_factory
-   *   The configuration object factory.
-   */
-  public function __construct(LanguageManager $language_manager, ConfigFactory $config_factory) {
-    $this->languageManager = $language_manager;
-    $this->configFactory = $config_factory;
-  }
-
-  /**
-   * Sets the negotiated interface language on the configuration factory.
-   *
-   * @param \Symfony\Component\HttpKernel\Event\GetResponseEvent $event
-   *   Kernel event to respond to.
-   */
-  public function onKernelRequestSetDefaultConfigLanguage(GetResponseEvent $event) {
-    if ($this->languageManager->isMultiLingual()) {
-      $this->configFactory->setLanguage($this->languageManager->getLanguage());
-    }
-  }
-
-  /**
-   * Implements EventSubscriberInterface::getSubscribedEvents().
-   */
-  static function getSubscribedEvents() {
-    $events[KernelEvents::REQUEST][] = array('onKernelRequestSetDefaultConfigLanguage', 48);
-    return $events;
-  }
-}
-
diff --git a/core/modules/language/lib/Drupal/language/LanguageListController.php b/core/modules/language/lib/Drupal/language/LanguageListController.php
index 9d65146b0efa1faafc2f2e48e2ae68d83479fb3d..601d0598de8dac7207733098cf38580cc63116d5 100644
--- a/core/modules/language/lib/Drupal/language/LanguageListController.php
+++ b/core/modules/language/lib/Drupal/language/LanguageListController.php
@@ -95,11 +95,11 @@ public function buildForm(array $form, array &$form_state) {
   public function submitForm(array &$form, array &$form_state) {
     parent::submitForm($form, $form_state);
 
-    // Kill the static cache in language_list().
-    drupal_static_reset('language_list');
-
-    // Update weight of locked system languages.
-    language_update_locked_weights();
+    $language_manager = \Drupal::languageManager();
+    $language_manager->reset();
+    if ($language_manager instanceof ConfigurableLanguageManagerInterface) {
+      $language_manager->updateLockedLanguageWeights();
+    }
 
     drupal_set_message(t('Configuration saved.'));
   }
diff --git a/core/modules/language/lib/Drupal/language/LanguageNegotiationMethodBase.php b/core/modules/language/lib/Drupal/language/LanguageNegotiationMethodBase.php
new file mode 100644
index 0000000000000000000000000000000000000000..5dc497bd745f5f612414144f35c3d36ac8fe0dbd
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/LanguageNegotiationMethodBase.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\LanguageNegotiationMethodBase.
+ */
+
+namespace Drupal\language;
+
+use Drupal\Core\Config\ConfigFactory;
+use Drupal\Core\Language\Language;
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Base class for language negotiation methods.
+ */
+abstract class LanguageNegotiationMethodBase implements LanguageNegotiationMethodInterface {
+
+  /**
+   * The language manager.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface
+   */
+  protected $languageManager;
+
+  /**
+   * The configuration factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactory
+   */
+  protected $config;
+
+  /**
+   * The current active user.
+   *
+   * @return \Drupal\Core\Session\AccountInterface
+   */
+  protected $currentUser;
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setLanguageManager(ConfigurableLanguageManagerInterface $language_manager) {
+    $this->languageManager = $language_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setConfig(ConfigFactory $config) {
+    $this->config = $config;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setCurrentUser(AccountInterface $current_user) {
+    $this->currentUser = $current_user;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function persist(Language $language) {
+    // Remember the method ID used to detect the language.
+    $language->method_id = static::METHOD_ID;
+  }
+
+}
diff --git a/core/modules/language/lib/Drupal/language/LanguageNegotiationMethodInterface.php b/core/modules/language/lib/Drupal/language/LanguageNegotiationMethodInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..3ac20c9e0300b6ad50e3fb67b1d4fc9ac66ce297
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/LanguageNegotiationMethodInterface.php
@@ -0,0 +1,65 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\LanguageNegotiationMethodInterface.
+ */
+
+namespace Drupal\language;
+
+use Drupal\Core\Config\ConfigFactory;
+use Drupal\Core\Language\Language;
+use Drupal\Core\Session\AccountInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Interface for language negotiation classes.
+ */
+interface LanguageNegotiationMethodInterface {
+
+  /**
+   * Injects the language manager.
+   *
+   * @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager
+   *   The language manager to be used to retrieve the language list and the
+   *   already negotiated languages.
+   */
+  public function setLanguageManager(ConfigurableLanguageManagerInterface $language_manager);
+
+  /**
+   * Injects the configuration factory.
+   *
+   * @param \Drupal\Core\Config\ConfigFactory $config_factory
+   *   The configuration factory.
+   */
+  public function setConfig(ConfigFactory $config);
+
+  /**
+   * Injects the current user.
+   *
+   * @param \Drupal\Core\Session\AccountInterface $current_user
+   *   The current active user.
+   */
+  public function setCurrentUser(AccountInterface $current_user);
+
+  /**
+   * Performs language negotiation.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   (optional) The current request. Defaults to NULL if it has not been
+   *   initialized yet.
+   *
+   * @return string
+   *   A valid language code or FALSE if the negotitation was unsuccessful.
+   */
+  public function getLangcode(Request $request = NULL);
+
+  /**
+   * Notifies the plugin that the language code it returned has been accepted.
+   *
+   * @param \Drupal\Core\Language\Language $language
+   *   The accepted language.
+   */
+  public function persist(Language $language);
+
+}
diff --git a/core/modules/language/lib/Drupal/language/LanguageNegotiationMethodManager.php b/core/modules/language/lib/Drupal/language/LanguageNegotiationMethodManager.php
new file mode 100644
index 0000000000000000000000000000000000000000..1480bd6ac9bd1d35e518ea8a6a6c919d02c9d608
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/LanguageNegotiationMethodManager.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\LanguageNegotiationMethodManager.
+ */
+
+namespace Drupal\language;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Extension\ModuleHandlerInterface;
+use Drupal\Core\Plugin\DefaultPluginManager;
+
+/**
+ * Manages language negotiation methods.
+ */
+class LanguageNegotiationMethodManager extends DefaultPluginManager {
+
+  /**
+   * Constructs a new LanguageNegotiationMethodManager object.
+   *
+   * @param \Traversable $namespaces
+   *   An object that implements \Traversable which contains the root paths
+   *   keyed by the corresponding namespace to look for plugin implementations.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache_backend
+   *   An object that implements CacheBackendInterface
+   * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
+   *   An object that implements ModuleHandlerInterface
+   */
+  public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler) {
+    parent::__construct('Plugin/LanguageNegotiation', $namespaces);
+    $this->cacheBackend = $cache_backend;
+    $this->cacheKeyPrefix = 'language_negotiation_plugins';
+    $this->cacheKey = 'language_negotiation_plugins';
+    $this->alterInfo($module_handler, 'language_negotiation_info');
+  }
+
+}
diff --git a/core/modules/language/lib/Drupal/language/LanguageNegotiator.php b/core/modules/language/lib/Drupal/language/LanguageNegotiator.php
new file mode 100644
index 0000000000000000000000000000000000000000..f2a74f6c7ce744cf359475498059db7e8196f04c
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/LanguageNegotiator.php
@@ -0,0 +1,372 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\LanguageNegotiator.
+ */
+
+namespace Drupal\language;
+
+use Drupal\Component\Plugin\PluginManagerInterface;
+use Drupal\Component\Utility\Settings;
+use Drupal\Core\Config\ConfigFactory;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Session\AccountInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class responsible for performing language negotiation.
+ */
+class LanguageNegotiator implements LanguageNegotiatorInterface {
+
+  /**
+   * The language negotiation method plugin manager.
+   *
+   * @var \Drupal\Component\Plugin\PluginManagerInterface
+   */
+  protected $negotiatorManager;
+
+  /**
+   * The language manager.
+   *
+   * @var \Drupal\language\ConfigurableLanguageManagerInterface
+   */
+  protected $languageManager;
+
+  /**
+   * The configuration factory.
+   *
+   * @var \Drupal\Core\Config\config
+   */
+  protected $configFactory;
+
+  /**
+   * The settings instance.
+   *
+   * @return \Drupal\Component\Utility\Settings
+   */
+  protected $settings;
+
+  /**
+   * The request object.
+   *
+   * @var \Symfony\Component\HttpFoundation\Request
+   */
+  protected $request;
+
+  /**
+   * The current active user.
+   *
+   * @return \Drupal\Core\Session\AccountInterface
+   */
+  protected $currentUser;
+
+  /**
+   * Local cache for language negotiation method instances.
+   *
+   * @var array
+   */
+  protected $methods;
+
+  /**
+   * An array of language objects keyed by method id.
+   *
+   * @var array
+   */
+  protected $negotiatedLanguages;
+
+  /**
+   * Constructs a new LanguageNegotiator object.
+   *
+   * @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager
+   *    The language manager.
+   * @param \Drupal\Component\Plugin\PluginManagerInterface $negotiator_manager
+   *   The language negotiation methods plugin manager
+   * @param \Drupal\Core\Config\ConfigFactory $config_factory
+   *   The configuration factory.
+   * @param \Drupal\Component\Utility\Settings $settings
+   *   The settings instance.
+   */
+  public function __construct(ConfigurableLanguageManagerInterface $language_manager, PluginManagerInterface $negotiator_manager, ConfigFactory $config_factory, Settings $settings) {
+    $this->languageManager = $language_manager;
+    $this->negotiatorManager = $negotiator_manager;
+    $this->configFactory = $config_factory;
+    $this->settings = $settings;
+  }
+
+  /**
+   * Initializes the injected language manager with the negotiator.
+   *
+   * This should be called right after instantiating the negotiator to make it
+   * available to the language manager without introducing a circular
+   * dependency.
+   */
+  public function initLanguageManager() {
+    $this->languageManager->setNegotiator($this);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function reset() {
+    $this->negotiatedLanguages = array();
+    $this->methods = array();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setCurrentUser(AccountInterface $current_user) {
+    $this->currentUser = $current_user;
+    $this->reset();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setRequest(Request $request) {
+    $this->request = $request;
+    $this->reset();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function initializeType($type) {
+    $language = NULL;
+
+    if ($this->currentUser && $this->request) {
+      // Execute the language negotiation methods in the order they were set up
+      // and return the first valid language found.
+      foreach ($this->getConfiguration($type) as $method_id => $info) {
+        if (!isset($this->negotiatedLanguages[$method_id])) {
+          $this->negotiatedLanguages[$method_id] = $this->negotiateLanguage($type, $method_id);
+        }
+
+        // Since objects are references, we need to return a clone to prevent
+        // the language negotiation method cache from being unintentionally
+        // altered. The same methods might be used with different language types
+        // based on configuration.
+        $language = !empty($this->negotiatedLanguages[$method_id]) ? clone($this->negotiatedLanguages[$method_id]) : NULL;
+
+        if ($language) {
+          $this->getNegotiationMethodInstance($method_id)->persist($language);
+          break;
+        }
+      }
+    }
+
+    if (!$language) {
+      // If no other language was found use the default one.
+      $language = $this->languageManager->getDefaultLanguage();
+      $language->method_id = LanguageNegotiatorInterface::METHOD_ID;
+    }
+
+    return $language;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getConfiguration($type) {
+    // @todo convert to CMI https://drupal.org/node/1827038 and
+    //   https://drupal.org/node/2102477
+    drupal_bootstrap(DRUPAL_BOOTSTRAP_VARIABLES, FALSE);
+    return variable_get("language_negotiation_$type", array());
+  }
+
+  /**
+   * Performs language negotiation using the specified negotiation method.
+   *
+   * @param string $type
+   *   The language type to be initialized.
+   * @param string $method_id
+   *   The string identifier of the language negotiation method to use to detect
+   *   language.
+   *
+   * @return \Drupal\Core\Language\Language|NULL
+   *   Negotiated language object for given type and method, FALSE otherwise.
+   */
+  protected function negotiateLanguage($type, $method_id) {
+    $langcode = NULL;
+    $method = $this->negotiatorManager->getDefinition($method_id);
+
+    if (!isset($method['types']) || in_array($type, $method['types'])) {
+
+      // Check for a cache mode force from settings.php.
+      if ($this->settings->get('page_cache_without_database')) {
+        $cache_enabled = TRUE;
+      }
+      else {
+        $cache_enabled = $this->configFactory->get('system.performance')->get('cache.page.use_internal');
+      }
+
+      // If the language negotiation method has no cache preference or this is
+      // satisfied we can execute the callback.
+      if ($cache = !isset($method['cache']) || $this->currentUser->isAuthenticated() || $method['cache'] == $cache_enabled) {
+        $langcode = $this->getNegotiationMethodInstance($method_id)->getLangcode($this->request);
+      }
+    }
+
+    $languages = $this->languageManager->getLanguages();
+    return isset($languages[$langcode]) ? $languages[$langcode] : NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getNegotiationMethods($type = NULL) {
+    $definitions = $this->negotiatorManager->getDefinitions();
+    if (isset($type)) {
+      $config = $this->getConfiguration($type);
+      $definitions = array_intersect_key($definitions, $config);
+    }
+    return $definitions;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getNegotiationMethodInstance($method_id) {
+    if (!isset($this->methods[$method_id])) {
+      $instance = $this->negotiatorManager->createInstance($method_id, array());
+      $instance->setLanguageManager($this->languageManager);
+      $instance->setConfig($this->configFactory);
+      $instance->setCurrentUser($this->currentUser);
+      $this->methods[$method_id] = $instance;
+    }
+    return $this->methods[$method_id];
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getPrimaryNegotiationMethod($type) {
+    $config = $this->getConfiguration($type);
+    return empty($config) ? LanguageNegotiatorInterface::METHOD_ID : key($config);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isNegotiationMethodEnabled($method_id, $type = NULL) {
+    $enabled = FALSE;
+    $language_types = !empty($type) ? array($type) : $this->languageManager->getLanguageTypes();
+
+    foreach ($language_types as $type) {
+      $config = $this->getConfiguration($type);
+      if (isset($config[$method_id])) {
+        $enabled = TRUE;
+        break;
+      }
+    }
+
+    return $enabled;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function saveConfiguration($type, $method_weights) {
+    $definitions = $this->getNegotiationMethods();
+    $default_types = $this->languageManager->getLanguageTypes();
+
+    // Order the language negotiation method list by weight.
+    asort($method_weights);
+    foreach ($method_weights as $method_id => $weight) {
+      if (isset($definitions[$method_id])) {
+        $method = $definitions[$method_id];
+        // If the language negotiation method does not express any preference
+        // about types, make it available for any configurable type.
+        $types = array_flip(!empty($method['types']) ? $method['types'] : $default_types);
+        // Check whether the method is defined and has the right type.
+        if (!isset($types[$type])) {
+          unset($method_weights[$method_id]);
+        }
+      }
+      else {
+        unset($method_weights[$method_id]);
+      }
+    }
+
+    variable_set("language_negotiation_$type", $method_weights);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function purgeConfiguration() {
+    // Ensure that we are getting the defined language negotiation information.
+    // An invocation of \Drupal\Core\Extension\ModuleHandler::install() or
+    // \Drupal\Core\Extension\ModuleHandler::uninstall() could invalidate the
+    // cached information.
+    $this->negotiatorManager->clearCachedDefinitions();
+    $this->languageManager->reset();
+    foreach ($this->languageManager->getDefinedLanguageTypesInfo() as $type => $info) {
+      $this->saveConfiguration($type, $this->getConfiguration($type));
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function updateConfiguration(array $types) {
+    // Ensure that we are getting the defined language negotiation information.
+    // An invocation of \Drupal\Core\Extension\ModuleHandler::install() or
+    // \Drupal\Core\Extension\ModuleHandler::uninstall() could invalidate the
+    // cached information.
+    $this->negotiatorManager->clearCachedDefinitions();
+    $this->languageManager->reset();
+
+    $language_types = array();
+    $language_types_info = $this->languageManager->getDefinedLanguageTypesInfo();
+    $method_definitions = $this->getNegotiationMethods();
+
+    foreach ($language_types_info as $type => $info) {
+      $configurable = in_array($type, $types);
+
+      // Check whether the language type is unlocked. Only the status of
+      // unlocked language types can be toggled between configurable and
+      // non-configurable. The default language negotiation settings, if
+      // available, are stored in $info['fixed'].
+      if (empty($info['locked'])) {
+        // If we have a non-locked non-configurable language type without
+        // default language negotiation settings, we use the values negotiated
+        // for the interface language which should always be available.
+        if (!$configurable && !empty($info['fixed'])) {
+          $method_weights = array(LanguageNegotiationUI::METHOD_ID);
+          $method_weights = array_flip($method_weights);
+          $this->saveConfiguration($type, $method_weights);
+        }
+      }
+      else {
+        // Locked language types with default settings are always considered
+        // non-configurable. In turn if default settings are missing, the
+        // language type is always considered configurable.
+        $configurable = empty($info['fixed']);
+
+        // If the language is non-configurable we need to store its language
+        // negotiation settings.
+        if (!$configurable) {
+          $method_weights = array();
+          foreach ($info['fixed'] as $weight => $method_id) {
+            if (isset($method_definitions[$method_id])) {
+              $method_weights[$method_id] = $weight;
+            }
+          }
+          $this->saveConfiguration($type, $method_weights);
+        }
+      }
+
+      $language_types[$type] = $configurable;
+    }
+
+    // Store the language type configuration.
+    $config = array(
+      'configurable' => array_keys(array_filter($language_types)),
+      'all' => array_keys($language_types),
+    );
+    $this->languageManager->saveLanguageTypesConfiguration($config);
+  }
+
+}
diff --git a/core/modules/language/lib/Drupal/language/LanguageNegotiatorInterface.php b/core/modules/language/lib/Drupal/language/LanguageNegotiatorInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..0654fdec798b55fcab9ce65793e7954831a34631
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/LanguageNegotiatorInterface.php
@@ -0,0 +1,217 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\LanguageNegotiatorInterface
+ */
+
+namespace Drupal\language;
+
+use Drupal\Core\Session\AccountInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Common interface for language negotiation services.
+ *
+ * The language negotiation API is based on two major concepts:
+ * - Language types: types of translatable data (the types of data that a user
+ *   can view or request).
+ * - Language negotiation methods: responsible for determining which language to
+ *   use to present a particular piece of data to the user.
+ * Both language types and language negotiation methods are customizable.
+ *
+ * Drupal defines three built-in language types:
+ * - Interface language: The page's main language, used to present translated
+ *   user interface elements such as titles, labels, help text, and messages.
+ * - Content language: The language used to present content that is available
+ *   in more than one language.
+ * - URL language: The language associated with URLs. When generating a URL,
+ *   this value will be used by url() as a default if no explicit preference is
+ *   provided.
+ * Modules can define additional language types through
+ * hook_language_types_info(), and alter existing language type definitions
+ * through hook_language_types_info_alter().
+ *
+ * Language types may be configurable or fixed. The language negotiation
+ * methods associated with a configurable language type can be explicitly
+ * set through the user interface. A fixed language type has predetermined
+ * (module-defined) language negotiation settings and, thus, does not appear in
+ * the configuration page. Here is a code snippet that makes the content
+ * language (which by default inherits the interface language's values)
+ * configurable:
+ * @code
+ * function mymodule_language_types_info_alter(&$language_types) {
+ *   unset($language_types[Language::TYPE_CONTENT]['fixed']);
+ * }
+ * @endcode
+ *
+ * The locked configuration property prevents one language type from being
+ * switched from customized to not customized, and vice versa.
+ * @see \Drupal\language\LanguageNegotiator::updateConfiguration()
+ *
+ * Every language type can have a different set of language negotiation methods
+ * assigned to it. Different language types often share the same language
+ * negotiation settings, but they can have independent settings if needed. If
+ * two language types are configured the same way, their language switcher
+ * configuration will be functionally identical and the same settings will act
+ * on both language types.
+ *
+ * Drupal defines the following built-in language negotiation methods:
+ * - URL: Determine the language from the URL (path prefix or domain).
+ * - Session: Determine the language from a request/session parameter.
+ * - User: Follow the user's language preference.
+ * - User admin language: Identifie admin language from the user preferences.
+ * - Browser: Determine the language from the browser's language settings.
+ * - Selected language: Use the default site language.
+ * Language negotiation methods are simple plugin classes that implement a
+ * particular logic to return a language code. For instance, the URL method
+ * searches for a valid path prefix or domain name in the current request URL.
+ * If a language negotiation method does not return a valid language code, the
+ * next method associated to the language type (based on method weight) is
+ * invoked.
+ *
+ * Modules can define additional language negotiation methods by simply provide
+ * the related plugins, and alter existing methods through
+ * hook_language_negotiation_info_alter(). Here is an example snippet that lets
+ * path prefixes be ignored for administrative paths:
+ * @code
+ * function mymodule_language_negotiation_info_alter(&$negotiation_info) {
+ *   // Replace the original plugin with our own implementation.
+ *   $method_id = \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl::METHOD_ID;
+ *   $negotiation_info[$method_id]['class'] = 'Drupal\my_module\Plugin\LanguageNegotiation\MyLanguageNegotiationUrl';
+ * }
+ *
+ * class MyLanguageNegotiationUrl extends LanguageNegotiationUrl {
+ *   public function getCurrentLanguage(Request $request = NULL) {
+ *     if ($request) {
+ *       // Use the original URL language negotiation method to get a valid
+ *       // language code.
+ *       $langcode = parent::getCurrentLanguage($request);
+ *
+ *       // If we are on an administrative path, override with the default
+ *       language.
+ *       if ($request->query->has('q') && strtok($request->query->get('q'), '/') == 'admin') {
+ *         return $this->languageManager->getDefaultLanguage()->id;
+ *       }
+ *       return $langcode;
+ *     }
+ *   }
+ * }
+ * ?>
+ * @endcode
+ *
+ * For more information, see
+ * @link http://drupal.org/node/1497272 Language Negotiation API @endlink
+ */
+interface LanguageNegotiatorInterface {
+
+  /**
+   * The language negotiation method id for the language negotiator itself.
+   */
+  const METHOD_ID = 'language-default';
+
+  /**
+   * Resets the negotiated languages and the method instances.
+   */
+  public function reset();
+
+  /**
+   * Sets the current active user and resets all language types.
+   *
+   * @param \Drupal\Core\Session\AccountInterface $current_user
+   *   The current active user.
+   */
+  public function setCurrentUser(AccountInterface $current_user);
+
+  /**
+   * Sets the active request and resets all language types.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The HttpRequest object representing the current request.
+   */
+  public function setRequest(Request $request);
+
+  /**
+   * Initializes the specified language type.
+   *
+   * @param string $type
+   *   The language type to be initialized.
+   *
+   * @return \Drupal\Core\Language\Language
+   *   Return either the language of the specified type or the default language.
+   */
+  public function initializeType($type);
+
+  /**
+   * Returns the language negotiation methods enabled for a language type.
+   *
+   * @param string $type
+   *   (optional) The language type. If no type is specified all the method
+   *   definitions are returned.
+   *
+   * @return array
+   *   An array of language negotiation method definitions keyed by method id.
+   */
+  public function getNegotiationMethods($type = NULL);
+
+  /**
+   * Returns an instance of the specified language negotiation method.
+   *
+   * @param string $method_id
+   *   The method identifier.
+   *
+   * @return \Drupal\language\LanguageNegotiationMethodInterface
+   */
+  public function getNegotiationMethodInstance($method_id);
+
+  /**
+   * Returns the ID of the language type's primary language negotiation method.
+   *
+   * @param $type
+   *   The language type.
+   *
+   * @return
+   *   The identifier of the primary language negotiation method for the given
+   *   language type, or the default method if none exists.
+   */
+  public function getPrimaryNegotiationMethod($type);
+
+  /**
+   * Checks whether a language negotiation method is enabled for a language type.
+   *
+   * @param $method_id
+   *   The language negotiation method ID.
+   * @param $type
+   *   (optional) The language type. If none is passed, all the configurable
+   *   language types will be inspected.
+   *
+   * @return
+   *   TRUE if the method is enabled for at least one of the given language
+   *   types, or FALSE otherwise.
+   */
+  public function isNegotiationMethodEnabled($method_id, $type = NULL);
+
+  /**
+   * Saves a list of language negotiation methods for a language type.
+   *
+   * @param string $type
+   *   The language type.
+   * @param array $method_weights
+   *   An array of language negotiation method weights keyed by method ID.
+   */
+  function saveConfiguration($type, $method_weights);
+
+  /**
+   * Resave the configuration to purge missing negotiation methods.
+   */
+  function purgeConfiguration();
+
+  /**
+   * Updates the configuration based on the given language types.
+   *
+   * @param array $types
+   *   An array of configurable language types.
+   */
+  function updateConfiguration(array $types);
+
+}
diff --git a/core/modules/language/lib/Drupal/language/LanguageServiceProvider.php b/core/modules/language/lib/Drupal/language/LanguageServiceProvider.php
new file mode 100644
index 0000000000000000000000000000000000000000..28a719dc28a443d0eb56e594f9a5342ae9b65d72
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/LanguageServiceProvider.php
@@ -0,0 +1,73 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\LanguageServiceProvider.
+ */
+
+namespace Drupal\language;
+
+use Drupal\Core\Config\BootstrapConfigStorageFactory;
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\DependencyInjection\ServiceProviderBase;
+use Drupal\Core\Language\Language;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * Overrides the language_manager service to point to language's module one.
+ */
+class LanguageServiceProvider extends ServiceProviderBase {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function register(ContainerBuilder $container) {
+    // The following services are needed only on multilingual sites.
+    if ($this->isMultilingual()) {
+      $container->register('language_request_subscriber', 'Drupal\language\EventSubscriber\LanguageRequestSubscriber')
+        ->addTag('event_subscriber')
+        ->addArgument(new Reference('language_manager'))
+        ->addArgument(new Reference('language_negotiator'))
+        ->addArgument(new Reference('string_translation'))
+        ->addArgument(new Reference('current_user'));
+
+      $container->register('path_processor_language', 'Drupal\language\HttpKernel\PathProcessorLanguage')
+        ->addTag('path_processor_inbound', array('priority' => 300))
+        ->addTag('path_processor_outbound', array('priority' => 100))
+        ->addArgument(new Reference('config.factory'))
+        ->addArgument(new Reference('settings'))
+        ->addArgument(new Reference('language_manager'))
+        ->addArgument(new Reference('language_negotiator'))
+        ->addArgument(new Reference('current_user'));
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function alter(ContainerBuilder $container) {
+    $definition = $container->getDefinition('language_manager');
+    $definition->setClass('Drupal\language\ConfigurableLanguageManager')
+      ->addArgument(new Reference('config.factory'))
+      ->addArgument(new Reference('module_handler'));
+  }
+
+  /**
+   * Checks whether the site is multilingual.
+   *
+   * @return bool
+   *   TRUE if the site is multilingual, FALSE otherwise.
+   */
+  protected function isMultilingual() {
+    $prefix = 'language.entity.';
+    // @todo Try to swap out for config.storage to take advantage of database
+    //   and caching. This might prove difficult as this is called before the
+    //   container has finished building.
+    $config_storage = BootstrapConfigStorageFactory::get();
+    $config_ids = array_filter($config_storage->listAll($prefix), function($config_id) use ($prefix) {
+      return $config_id != $prefix . Language::LANGCODE_NOT_SPECIFIED && $config_id != $prefix . Language::LANGCODE_NOT_APPLICABLE;
+    });
+    return count($config_ids) > 1;
+  }
+
+}
diff --git a/core/modules/language/lib/Drupal/language/LanguageSwitcherInterface.php b/core/modules/language/lib/Drupal/language/LanguageSwitcherInterface.php
new file mode 100644
index 0000000000000000000000000000000000000000..d6edd5b2e6c3b67be417c21ea5cfa8466db8f53f
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/LanguageSwitcherInterface.php
@@ -0,0 +1,32 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\LanguageSwitcherInterface.
+ */
+
+namespace Drupal\language;
+
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Interface for language switcher classes.
+ */
+interface LanguageSwitcherInterface {
+
+  /**
+   * Returns language switch links.
+   *
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   The current request.
+   * @param string $type
+   *   The language type.
+   * @param string $path
+   *   The path links should point to.
+   *
+   * @return array
+   *   An array of link arrays keyed by language code.
+   */
+  public function getLanguageSwitchLinks(Request $request, $type, $path);
+
+}
diff --git a/core/modules/language/lib/Drupal/language/Plugin/Block/LanguageBlock.php b/core/modules/language/lib/Drupal/language/Plugin/Block/LanguageBlock.php
index f53c81b072adc0030723c42ed93ba305a5fcefca..3ccae225053768c358686d559d464ff1899b5c95 100644
--- a/core/modules/language/lib/Drupal/language/Plugin/Block/LanguageBlock.php
+++ b/core/modules/language/lib/Drupal/language/Plugin/Block/LanguageBlock.php
@@ -9,6 +9,9 @@
 
 use Drupal\block\BlockBase;
 use Drupal\Core\Session\AccountInterface;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Plugin\ContainerFactoryPluginInterface;
+use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
  * Provides a 'Language switcher' block.
@@ -20,13 +23,51 @@
  *   derivative = "Drupal\language\Plugin\Derivative\LanguageBlock"
  * )
  */
-class LanguageBlock extends BlockBase {
+class LanguageBlock extends BlockBase implements ContainerFactoryPluginInterface {
+
+  /**
+   * The language manager.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface
+   */
+  protected $languageManager;
+
+  /**
+   * Constructs an LanguageBlock object.
+   *
+   * @param array $configuration
+   *   A configuration array containing information about the plugin instance.
+   * @param string $plugin_id
+   *   The plugin_id for the plugin instance.
+   * @param array $plugin_definition
+   *   The plugin implementation definition.
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   *   The language manager.
+   */
+  public function __construct(array $configuration, $plugin_id, array $plugin_definition, LanguageManagerInterface $language_manager) {
+    parent::__construct($configuration, $plugin_id, $plugin_definition);
+    $this->languageManager = $language_manager;
+  }
+
+
+  /**
+   * {@inheritdoc}
+   */
+  public static function create(ContainerInterface $container, array $configuration, $plugin_id, array $plugin_definition) {
+    return new static(
+      $configuration,
+      $plugin_id,
+      $plugin_definition,
+      $container->get('language_manager')
+    );
+  }
+
 
   /**
    * {@inheritdoc}
    */
   function access(AccountInterface $account) {
-    return language_multilingual();
+    return $this->languageManager->isMultilingual();
   }
 
   /**
@@ -36,7 +77,7 @@ public function build() {
     $build = array();
     $path = drupal_is_front_page() ? '<front>' : current_path();
     $type = $this->getDerivativeId();
-    $links = language_negotiation_get_switch_links($type, $path);
+    $links = $this->languageManager->getLanguageSwitchLinks($type, $path);
 
     if (isset($links->links)) {
       $build = array(
diff --git a/core/modules/language/lib/Drupal/language/Plugin/Condition/Language.php b/core/modules/language/lib/Drupal/language/Plugin/Condition/Language.php
index 7f4289ea524a8def64047572319d869165b4c717..9dda57177aff6a422a8c1aae5451f34dd201f0ae 100644
--- a/core/modules/language/lib/Drupal/language/Plugin/Condition/Language.php
+++ b/core/modules/language/lib/Drupal/language/Plugin/Condition/Language.php
@@ -30,7 +30,7 @@ class Language extends ConditionPluginBase {
    */
   public function buildForm(array $form, array &$form_state) {
     $form = parent::buildForm($form, $form_state);
-    if (language_multilingual()) {
+    if (\Drupal::languageManager()->isMultilingual()) {
       // Fetch languages.
       $languages = language_list(Lang::STATE_ALL);
       $langcodes_options = array();
diff --git a/core/modules/language/lib/Drupal/language/Plugin/Derivative/LanguageBlock.php b/core/modules/language/lib/Drupal/language/Plugin/Derivative/LanguageBlock.php
index a329c3b1954bd6f66d4a400bc382d2cedf3c2050..acf276fb7e449ecaa0c584cd0d17490cb60d9a95 100644
--- a/core/modules/language/lib/Drupal/language/Plugin/Derivative/LanguageBlock.php
+++ b/core/modules/language/lib/Drupal/language/Plugin/Derivative/LanguageBlock.php
@@ -8,28 +8,35 @@
 namespace Drupal\language\Plugin\Derivative;
 
 use Drupal\Component\Plugin\Derivative\DerivativeBase;
+use Drupal\language\ConfigurableLanguageManagerInterface;
 
 /**
  * Provides language switcher block plugin definitions for all languages.
  */
 class LanguageBlock extends DerivativeBase {
+
   /**
    * {@inheritdoc}
    */
   public function getDerivativeDefinitions(array $base_plugin_definition) {
-    include_once DRUPAL_ROOT . '/core/includes/language.inc';
-    $info = language_types_info();
-    $configurable_types = language_types_get_configurable();
-    foreach ($configurable_types as $type) {
-      $this->derivatives[$type] = $base_plugin_definition;
-      $this->derivatives[$type]['admin_label'] = t('Language switcher (!type)', array('!type' => $info[$type]['name']));
-      $this->derivatives[$type]['cache'] = DRUPAL_NO_CACHE;
-    }
-    // If there is just one configurable type then change the title of the
-    // block.
-    if (count($configurable_types) == 1) {
-      $this->derivatives[reset($configurable_types)]['admin_label'] = t('Language switcher');
+    $language_manager = \Drupal::languageManager();
+
+    if ($language_manager instanceof ConfigurableLanguageManagerInterface) {
+      $info = $language_manager->getDefinedLanguageTypesInfo();
+      $configurable_types = $language_manager->getLanguageTypes();
+      foreach ($configurable_types as $type) {
+        $this->derivatives[$type] = $base_plugin_definition;
+        $this->derivatives[$type]['admin_label'] = t('Language switcher (!type)', array('!type' => $info[$type]['name']));
+        $this->derivatives[$type]['cache'] = DRUPAL_NO_CACHE;
+      }
+      // If there is just one configurable type then change the title of the
+      // block.
+      if (count($configurable_types) == 1) {
+        $this->derivatives[reset($configurable_types)]['admin_label'] = t('Language switcher');
+      }
     }
+
     return parent::getDerivativeDefinitions($base_plugin_definition);
   }
+
 }
diff --git a/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php
new file mode 100644
index 0000000000000000000000000000000000000000..0ce2c86c69457b3e7b8f3d0b6cd300de0261725b
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationBrowser.
+ */
+
+namespace Drupal\language\Plugin\LanguageNegotiation;
+
+use Drupal\Component\Utility\UserAgent;
+use Drupal\language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class for identifying language from the browser Accept-language HTTP header.
+ *
+ * @Plugin(
+ *   id = \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationBrowser::METHOD_ID,
+ *   weight = -2,
+ *   cache = 0,
+ *   name = @Translation("Browser"),
+ *   description = @Translation("Language from the browser's language settings."),
+ *   config_path = "admin/config/regional/language/detection/browser"
+ * )
+ */
+class LanguageNegotiationBrowser extends LanguageNegotiationMethodBase {
+
+  /**
+   * The language negotiation method id.
+   */
+  const METHOD_ID = 'language-browser';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLangcode(Request $request = NULL) {
+    $langcode = NULL;
+
+    if ($this->languageManager && $request && $request->server->get('HTTP_ACCEPT_LANGUAGE')) {
+      $http_accept_language = $request->server->get('HTTP_ACCEPT_LANGUAGE');
+      $langcodes = array_keys($this->languageManager->getLanguages());
+      $mappings = $this->config->get('language.mappings')->get();
+      $langcode = UserAgent::getBestMatchingLangcode($http_accept_language, $langcodes, $mappings);
+    }
+
+    return $langcode;
+  }
+
+}
diff --git a/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationSelected.php b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationSelected.php
new file mode 100644
index 0000000000000000000000000000000000000000..bdbedc526cd6cab5f70cceb75d3a6c560f723e12
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationSelected.php
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationSelected.
+ */
+
+namespace Drupal\language\Plugin\LanguageNegotiation;
+
+use Drupal\language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class for identifying language from a selected language.
+ *
+ * @Plugin(
+ *   id = Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationSelected::METHOD_ID,
+ *   weight = 12,
+ *   name = @Translation("Selected language"),
+ *   description = @Translation("Language based on a selected language."),
+ *   config_path = "admin/config/regional/language/detection/selected"
+ * )
+ */
+class LanguageNegotiationSelected extends LanguageNegotiationMethodBase {
+
+  /**
+   * The language negotiation method id.
+   */
+  const METHOD_ID = 'language-selected';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLangcode(Request $request = NULL) {
+    $langcode = NULL;
+
+    if ($this->languageManager) {
+      $languages = $this->languageManager->getLanguages();
+      $langcode = $this->config->get('language.negotiation')->get('selected_langcode');
+    }
+
+    return $langcode;
+  }
+
+}
diff --git a/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationSession.php b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationSession.php
new file mode 100644
index 0000000000000000000000000000000000000000..e56278d6c76885d75607d357160f5943a14690d7
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationSession.php
@@ -0,0 +1,153 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationSession.
+ */
+
+namespace Drupal\language\Plugin\LanguageNegotiation;
+
+use Drupal\Core\Language\Language;
+use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
+use Drupal\language\LanguageNegotiationMethodBase;
+use Drupal\language\LanguageSwitcherInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Identify language from a request/session parameter.
+ *
+ * @Plugin(
+ *   id = Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationSession::METHOD_ID,
+ *   weight = -6,
+ *   name = @Translation("Session"),
+ *   description = @Translation("Language from a request/session parameter."),
+ *   config_path = "admin/config/regional/language/detection/session"
+ * )
+ */
+class LanguageNegotiationSession extends LanguageNegotiationMethodBase implements OutboundPathProcessorInterface, LanguageSwitcherInterface {
+
+  /**
+   * Flag used to determine whether query rewriting is active.
+   *
+   * @var bool
+   */
+  protected $queryRewrite;
+
+  /**
+   * The query parameter name to rewrite.
+   *
+   * @var string
+   */
+  protected $queryParam;
+
+  /**
+   * The query parameter value to be set.
+   *
+   * @var string
+   */
+  protected $queryValue;
+
+  /**
+   * The language negotiation method id.
+   */
+  const METHOD_ID = 'language-session';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLangcode(Request $request = NULL) {
+    $config = $this->config->get('language.negotiation')->get('session');
+    $param = $config['parameter'];
+    $langcode = $request && $request->query->get($param) ? $request->query->get($param) : NULL;
+    if (!$langcode && isset($_SESSION[$param])) {
+      $langcode = $_SESSION[$param];
+    }
+    return $langcode;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function persist(Language $language) {
+    parent::persist($language);
+
+    // We need to update the session parameter with the request value only if we
+    // have an authenticated user.
+    $langcode = $language->id;
+    if ($langcode && $this->languageManager) {
+      $languages = $this->languageManager->getLanguages();
+      if ($this->currentUser->isAuthenticated() && isset($languages[$langcode])) {
+        $config = $this->config->get('language.negotiation')->get('session');
+        $_SESSION[$config['parameter']] = $langcode;
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function processOutbound($path, &$options = array(), Request $request = NULL) {
+    if ($request) {
+      // The following values are not supposed to change during a single page
+      // request processing.
+      if (!isset($this->queryRewrite)) {
+        if ($this->currentUser->isAnonymous()) {
+          $languages = $this->languageManager->getLanguages();
+          $config = $this->config->get('language.negotiation')->get('session');
+          $this->queryParam = $config['parameter'];
+          $this->queryValue = $request->query->has($this->queryParam) ? $request->query->get($this->queryParam) : NULL;
+          $this->queryRewrite = isset($languages[$this->queryValue]);
+        }
+        else {
+          $this->queryRewrite = FALSE;
+        }
+      }
+
+      // If the user is anonymous, the user language negotiation method is
+      // enabled, and the corresponding option has been set, we must preserve
+      // any explicit user language preference even with cookies disabled.
+      if ($this->queryRewrite) {
+        if (isset($options['query']) && is_string($options['query'])) {
+          $query = array();
+          parse_str($options['query'], $query);
+          $options['query'] = $query;
+        }
+        if (!isset($options['query'][$this->queryParam])) {
+          $options['query'][$this->queryParam] = $this->queryValue;
+        }
+      }
+    }
+    return $path;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function getLanguageSwitchLinks(Request $request, $type, $path) {
+    $links = array();
+    $config = $this->config->get('language.negotiation')->get('session');
+    $param = $config['parameter'];
+    $language_query = isset($_SESSION[$param]) ? $_SESSION[$param] : $this->languageManager->getCurrentLanguage($type)->id;
+    $query = array();
+    parse_str($request->getQueryString(), $query);
+
+    foreach ($this->languageManager->getLanguages() as $language) {
+      $langcode = $language->id;
+      $links[$langcode] = array(
+        'href' => $path,
+        'title' => $language->name,
+        'attributes' => array('class' => array('language-link')),
+        'query' => $query,
+      );
+      if ($language_query != $langcode) {
+        $links[$langcode]['query'][$param] = $langcode;
+      }
+      else {
+        $links[$langcode]['attributes']['class'][] = 'session-active';
+      }
+    }
+
+    return $links;
+  }
+
+}
diff --git a/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationUI.php b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationUI.php
new file mode 100644
index 0000000000000000000000000000000000000000..3b649741c48761c7528774fdbd8b55447f598a10
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationUI.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUI.
+ */
+
+namespace Drupal\language\Plugin\LanguageNegotiation;
+
+use Drupal\language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class for identifying the language from the current interface language.
+ *
+ * @Plugin(
+ *   id = Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUI::METHOD_ID,
+ *   types = {Drupal\Core\Language\Language::TYPE_CONTENT},
+ *   weight = 9,
+ *   name = @Translation("Interface"),
+ *   description = @Translation("Use the detected interface language.")
+ * )
+ */
+class LanguageNegotiationUI extends LanguageNegotiationMethodBase {
+
+  /**
+   * The language negotiation method id.
+   */
+  const METHOD_ID = 'language-interface';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLangcode(Request $request = NULL) {
+    return $this->languageManager ? $this->languageManager->getCurrentLanguage()->id : NULL;
+  }
+
+}
diff --git a/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php
new file mode 100644
index 0000000000000000000000000000000000000000..a72ca444c8fb58519bec3f6b9ffe2883be37b36e
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php
@@ -0,0 +1,203 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl.
+ */
+
+namespace Drupal\language\Plugin\LanguageNegotiation;
+
+use Drupal\Core\Language\Language;
+use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
+use Drupal\Core\PathProcessor\OutboundPathProcessorInterface;
+use Drupal\language\LanguageNegotiationMethodBase;
+use Drupal\language\LanguageSwitcherInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class for identifying language via URL prefix or domain.
+ *
+ * @Plugin(
+ *   id = \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl::METHOD_ID,
+ *   types = {\Drupal\Core\Language\Language::TYPE_INTERFACE, \Drupal\Core\Language\Language::TYPE_CONTENT, \Drupal\Core\Language\Language::TYPE_URL},
+ *   weight = -8,
+ *   name = @Translation("URL"),
+ *   description = @Translation("Language from the URL (Path prefix or domain)."),
+ *   config_path = "admin/config/regional/language/detection/url"
+ * )
+ */
+class LanguageNegotiationUrl extends LanguageNegotiationMethodBase implements InboundPathProcessorInterface, OutboundPathProcessorInterface, LanguageSwitcherInterface {
+
+  /**
+   * The language negotiation method id.
+   */
+  const METHOD_ID = 'language-url';
+
+  /**
+   * URL language negotiation: use the path prefix as URL language indicator.
+   */
+  const CONFIG_PATH_PREFIX = 'path_prefix';
+
+  /**
+   * URL language negotiation: use the domain as URL language indicator.
+   */
+  const CONFIG_DOMAIN = 'domain';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLangcode(Request $request = NULL) {
+    $langcode = NULL;
+
+    if ($request && $this->languageManager) {
+      $languages = $this->languageManager->getLanguages();
+      $config = $this->config->get('language.negotiation')->get('url');
+
+      switch ($config['source']) {
+        case LanguageNegotiationUrl::CONFIG_PATH_PREFIX:
+          $request_path = urldecode(trim($request->getPathInfo(), '/'));
+          $path_args = explode('/', $request_path);
+          $prefix = array_shift($path_args);
+
+          // Search prefix within enabled languages.
+          $negotiated_language = FALSE;
+          foreach ($languages as $language) {
+            if (isset($config['prefixes'][$language->id]) && $config['prefixes'][$language->id] == $prefix) {
+              $negotiated_language = $language;
+              break;
+            }
+          }
+
+          if ($negotiated_language) {
+            $langcode = $negotiated_language->id;
+          }
+          break;
+
+        case LanguageNegotiationUrl::CONFIG_DOMAIN:
+          // Get only the host, not the port.
+          $http_host = $request->getHost();
+          foreach ($languages as $language) {
+            // Skip the check if the language doesn't have a domain.
+            if (!empty($config['domains'][$language->id])) {
+              // Ensure that there is exactly one protocol in the URL when
+              // checking the hostname.
+              $host = 'http://' . str_replace(array('http://', 'https://'), '', $config['domains'][$language->id]);
+              $host = parse_url($host, PHP_URL_HOST);
+              if ($http_host == $host) {
+                $langcode = $language->id;
+                break;
+              }
+            }
+          }
+          break;
+      }
+    }
+
+    return $langcode;
+  }
+
+  /**
+   * Implements Drupal\Core\PathProcessor\InboundPathProcessorInterface::processInbound().
+   */
+  public function processInbound($path, Request $request) {
+    $config = $this->config->get('language.negotiation')->get('url');
+    $parts = explode('/', $path);
+    $prefix = array_shift($parts);
+
+    // Search prefix within enabled languages.
+    foreach ($this->languageManager->getLanguages() as $language) {
+      if (isset($config['prefixes'][$language->id]) && $config['prefixes'][$language->id] == $prefix) {
+        // Rebuild $path with the language removed.
+        $path = implode('/', $parts);
+        break;
+      }
+    }
+
+    return $path;
+  }
+
+  /**
+   * Implements Drupal\Core\PathProcessor\InboundPathProcessorInterface::processOutbound().
+   */
+  public function processOutbound($path, &$options = array(), Request $request = NULL) {
+    $url_scheme = 'http';
+    $port = 80;
+    if ($request) {
+      $url_scheme = $request->getScheme();
+      $port = $request->getPort();
+    }
+    $languages = array_flip(array_keys($this->languageManager->getLanguages()));
+    // Language can be passed as an option, or we go for current URL language.
+    if (!isset($options['language'])) {
+      $language_url = $this->languageManager->getCurrentLanguage(Language::TYPE_URL);
+      $options['language'] = $language_url;
+    }
+    // We allow only enabled languages here.
+    elseif (!is_object($options['language']) || !isset($languages[$options['language']->id])) {
+      return $path;
+    }
+    $config = $this->config->get('language.negotiation')->get('url');
+    if ($config['source'] == LanguageNegotiationUrl::CONFIG_PATH_PREFIX) {
+      if (is_object($options['language']) && !empty($config['prefixes'][$options['language']->id])) {
+        return empty($path) ? $config['prefixes'][$options['language']->id] : $config['prefixes'][$options['language']->id] . '/' . $path;
+      }
+    }
+    elseif ($config['source'] ==  LanguageNegotiationUrl::CONFIG_DOMAIN) {
+      if (is_object($options['language']) && !empty($config['domains'][$options['language']->id])) {
+
+        // Save the original base URL. If it contains a port, we need to
+        // retain it below.
+        if (!empty($options['base_url'])) {
+          // The colon in the URL scheme messes up the port checking below.
+          $normalized_base_url = str_replace(array('https://', 'http://'), '', $options['base_url']);
+        }
+
+        // Ask for an absolute URL with our modified base URL.
+        $options['absolute'] = TRUE;
+        $options['base_url'] = $url_scheme . '://' . $config['domains'][$options['language']->id];
+
+        // In case either the original base URL or the HTTP host contains a
+        // port, retain it.
+        if (isset($normalized_base_url) && strpos($normalized_base_url, ':') !== FALSE) {
+          list(, $port) = explode(':', $normalized_base_url);
+          $options['base_url'] .= ':' . $port;
+        }
+        elseif ($port != 80) {
+          $options['base_url'] .= ':' . $port;
+        }
+
+        if (isset($options['https']) && !empty($options['mixed_mode_sessions'])) {
+          if ($options['https'] === TRUE) {
+            $options['base_url'] = str_replace('http://', 'https://', $options['base_url']);
+          }
+          elseif ($options['https'] === FALSE) {
+            $options['base_url'] = str_replace('https://', 'http://', $options['base_url']);
+          }
+        }
+
+        // Add Drupal's subfolder from the base_path if there is one.
+        $options['base_url'] .= rtrim(base_path(), '/');
+      }
+    }
+    return $path;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function getLanguageSwitchLinks(Request $request, $type, $path) {
+    $links = array();
+
+    foreach ($this->languageManager->getLanguages() as $language) {
+      $links[$language->id] = array(
+        'href' => $path,
+        'title' => $language->name,
+        'language' => $language,
+        'attributes' => array('class' => array('language-link')),
+      );
+    }
+
+    return $links;
+  }
+
+}
diff --git a/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationUrlFallback.php b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationUrlFallback.php
new file mode 100644
index 0000000000000000000000000000000000000000..2310153a071b979b909651c25bdfbe3fff97db8c
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/Plugin/LanguageNegotiation/LanguageNegotiationUrlFallback.php
@@ -0,0 +1,76 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrlFallback.
+ */
+
+namespace Drupal\language\Plugin\LanguageNegotiation;
+
+use Drupal\language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class that determines the language to be assigned to URLs when none is
+ * detected.
+ *
+ * The language negotiation process has a fallback chain that ends with the
+ * default language negotiation method. Each built-in language type has a
+ * separate initialization:
+ * - Interface language, which is the only configurable one, always gets a valid
+ *   value. If no request-specific language is detected, the default language
+ *   will be used.
+ * - Content language merely inherits the interface language by default.
+ * - URL language is detected from the requested URL and will be used to rewrite
+ *   URLs appearing in the page being rendered. If no language can be detected,
+ *   there are two possibilities:
+ *   - If the default language has no configured path prefix or domain, then the
+ *     default language is used. This guarantees that (missing) URL prefixes are
+ *     preserved when navigating through the site.
+ *   - If the default language has a configured path prefix or domain, a
+ *     requested URL having an empty prefix or domain is an anomaly that must be
+ *     fixed. This is done by introducing a prefix or domain in the rendered
+ *     page matching the detected interface language.
+ *
+ * @Plugin(
+ *   id = Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrlFallback::METHOD_ID,
+ *   types = {Drupal\Core\Language\Language::TYPE_URL},
+ *   weight = 8,
+ *   name = @Translation("URL fallback"),
+ *   description = @Translation("Use an already detected language for URLs if none is found.")
+ * )
+ */
+class LanguageNegotiationUrlFallback extends LanguageNegotiationMethodBase {
+
+  /**
+   * The language negotiation method id.
+   */
+  const METHOD_ID = 'language-url-fallback';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLangcode(Request $request = NULL) {
+    $langcode = NULL;
+
+    if ($this->languageManager) {
+      $default = $this->languageManager->getDefaultLanguage();
+      $config = $this->config->get('language.negotiation')->get('url');
+      $prefix = ($config['source'] == LanguageNegotiationUrl::CONFIG_PATH_PREFIX);
+
+      // If the default language is not configured to convey language
+      // information, a missing URL language information indicates that URL
+      // language should be the default one, otherwise we fall back to an
+      // already detected language.
+      if (($prefix && empty($config['prefixes'][$default->id])) || (!$prefix && empty($config['domains'][$default->id]))) {
+        $langcode = $default->id;
+      }
+      else {
+        $langcode = $this->languageManager->getCurrentLanguage()->id;
+      }
+    }
+
+    return $langcode;
+  }
+
+}
diff --git a/core/modules/language/lib/Drupal/language/Tests/Condition/LanguageConditionTest.php b/core/modules/language/lib/Drupal/language/Tests/Condition/LanguageConditionTest.php
index 19449d89c1b3b1229c23726a406161fdac5a553a..ed029fa74fdbc2041dfc506fe06f9cc3661f8fe0 100644
--- a/core/modules/language/lib/Drupal/language/Tests/Condition/LanguageConditionTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/Condition/LanguageConditionTest.php
@@ -26,7 +26,7 @@ class LanguageConditionTest extends DrupalUnitTestBase {
   /**
    * The language manager.
    *
-   * @var \Drupal\Core\Language\LanguageManager
+   * @var \Drupal\Core\Language\LanguageManagerInterface
    */
   protected $languageManager;
 
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php
index 679d6f973eba9f2ac49ba64a8248605d62930baf..55716687e9ca3941ec2973d556da7c6571b201b5 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php
@@ -7,8 +7,10 @@
 
 namespace Drupal\language\Tests;
 
-use Drupal\simpletest\WebTestBase;
+use Drupal\Component\Utility\UserAgent;
 use Drupal\Core\Language\Language;
+use Drupal\simpletest\WebTestBase;
+use Symfony\Component\HttpFoundation\Request;
 
 /**
  * Test browser language detection.
@@ -153,9 +155,9 @@ function testLanguageFromBrowser() {
       'zh-cht' => 'zh-hant',
     );
 
+    $mappings = $this->container->get('config.factory')->get('language.mappings')->get();
     foreach ($test_cases as $accept_language => $expected_result) {
-      \Drupal::request()->server->set('HTTP_ACCEPT_LANGUAGE', $accept_language);
-      $result = language_from_browser($languages);
+      $result = UserAgent::getBestMatchingLangcode($accept_language, array_keys($languages), $mappings);
       $this->assertIdentical($result, $expected_result, format_string("Language selection '@accept-language' selects '@result', result = '@actual'", array('@accept-language' => $accept_language, '@result' => $expected_result, '@actual' => isset($result) ? $result : 'none')));
     }
   }
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php
index 1fb8c8537885d0cac69aa0429f403b385eb8bd2e..6f9c5523d47feba64377d99aa83184d6344cebf7 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php
@@ -72,7 +72,7 @@ function testLanguageConfiguration() {
     );
     $this->drupalPostForm(NULL, $edit, t('Save configuration'));
     $this->assertOptionSelected('edit-site-default-language', 'fr', 'Default language updated.');
-    $this->assertEqual($this->getUrl(), url('admin/config/regional/settings', array('absolute' => TRUE)), 'Correct page redirection.');
+    $this->assertEqual($this->getUrl(), url('fr/admin/config/regional/settings', array('absolute' => TRUE)), 'Correct page redirection.');
 
     // Check if a valid language prefix is added after changing the default
     // language.
@@ -156,10 +156,10 @@ function testLanguageConfigurationWeight() {
    */
   protected function checkConfigurableLanguageWeight($state = 'by default') {
     // Reset language list.
-    drupal_static_reset('language_list');
+    \Drupal::languageManager()->reset();
     $max_configurable_language_weight = $this->getHighestConfigurableLanguageWeight();
     $replacements = array('@event' => $state);
-    foreach (language_list(Language::STATE_LOCKED) as $locked_language) {
+    foreach (\Drupal::languageManager()->getLanguages(Language::STATE_LOCKED) as $locked_language) {
       $replacements['%language'] = $locked_language->name;
       $this->assertTrue($locked_language->weight > $max_configurable_language_weight, format_string('System language %language has higher weight than configurable languages @event', $replacements));
     }
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageDependencyInjectionTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageDependencyInjectionTest.php
index ca496731726a81e4e66388a5d9f28b35927f0cfb..969a836ccaa013821b31ed5aa2a29d4ccc1e83a0 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageDependencyInjectionTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageDependencyInjectionTest.php
@@ -9,36 +9,23 @@
 
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\Language\Language;
-use Drupal\simpletest\WebTestBase;
 
 /**
  * Test for dependency injected language object.
  */
-class LanguageDependencyInjectionTest extends WebTestBase {
+class LanguageDependencyInjectionTest extends LanguageTestBase {
 
   /**
-   * Modules to enable.
-   *
-   * @var array
+   * {@inheritdoc}
    */
-  public static $modules = array('language');
-
   public static function getInfo() {
     return array(
-        'name' => 'Language dependency injection',
-        'description' => 'Compares the default language from $GLOBALS against the dependency injected language object.',
-        'group' => 'Language',
+      'name' => 'Language dependency injection',
+      'description' => 'Compares the default language from $GLOBALS against the dependency injected language object.',
+      'group' => 'Language',
     );
   }
 
-  function setUp() {
-    parent::setUp();
-
-    // Ensure we are building a new Language object for each test.
-    $this->container->get('language_manager')->reset();
-  }
-
-
   /**
    * Test dependency injected languages against a new Language object.
    *
@@ -48,8 +35,8 @@ function testDependencyInjectedNewLanguage() {
     // Initialize the language system.
     drupal_language_initialize();
 
-    $expected = language_default();
-    $result = language(Language::TYPE_INTERFACE);
+    $expected = $this->languageManager->getDefaultLanguage();
+    $result = $this->languageManager->getCurrentLanguage();
     foreach ($expected as $property => $value) {
       $this->assertEqual($expected->$property, $result->$property, format_string('The dependency injected language object %prop property equals the new Language object %prop property.', array('%prop' => $property)));
     }
@@ -74,12 +61,12 @@ function testDependencyInjectedNewDefaultLanguage() {
     variable_set('language_default', $new_language_default);
 
     // Initialize the language system.
-    drupal_language_initialize();
+    $this->languageManager->init();
 
     // The language system creates a Language object which contains the
     // same properties as the new default language object.
     $expected = new Language($new_language_default);
-    $result = language(Language::TYPE_INTERFACE);
+    $result = $this->languageManager->getCurrentLanguage();
     foreach ($expected as $property => $value) {
       $this->assertEqual($expected->$property, $result->$property, format_string('The dependency injected language object %prop property equals the default language object %prop property.', array('%prop' => $property)));
     }
@@ -87,4 +74,5 @@ function testDependencyInjectedNewDefaultLanguage() {
     // Delete the language_default variable we previously set.
     variable_del('language_default');
   }
+
 }
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageFallbackTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageFallbackTest.php
index 4123fc0208514a639e56d98664c37bc37764737d..fb621d8fafd702e14847a5d8922fff6c5cd2ed07 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageFallbackTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageFallbackTest.php
@@ -8,12 +8,11 @@
 namespace Drupal\language\Tests;
 
 use Drupal\Core\Language\Language;
-use Drupal\simpletest\DrupalUnitTestBase;
 
 /**
  * Tests the language fallback behavior.
  */
-class LanguageFallbackTest extends DrupalUnitTestBase {
+class LanguageFallbackTest extends LanguageTestBase {
 
   public static function getInfo() {
     return array(
@@ -23,24 +22,12 @@ public static function getInfo() {
     );
   }
 
-  /**
-   * The state storage service.
-   *
-   * @var \Drupal\Core\KeyValueStore\StateInterface
-   */
-  protected $state;
-
   /**
    * {@inheritdoc}
    */
   protected function setUp() {
     parent::setUp();
 
-    $this->enableModules(array('language', 'language_test'));
-    $this->installConfig(array('language'));
-
-    $this->state = $this->container->get('state');
-
     for ($i = 0; $i < 3; $i++) {
       $language = new Language();
       $language->id = $this->randomName(2);
@@ -53,18 +40,18 @@ protected function setUp() {
    * Tests language fallback candidates.
    */
   public function testCandidates() {
-    $manager = $this->getLanguageManager();
-    $expected = array_keys(language_list() + array(Language::LANGCODE_NOT_SPECIFIED => NULL));
+    $language_list = $this->languageManager->getLanguages();
+    $expected = array_keys($language_list + array(Language::LANGCODE_NOT_SPECIFIED => NULL));
 
     // Check that language fallback candidates by default are all the available
     // languages sorted by weight.
-    $candidates = $manager->getFallbackCandidates();
+    $candidates = $this->languageManager->getFallbackCandidates();
     $this->assertEqual(array_values($candidates), $expected, 'Language fallback candidates are properly returned.');
 
     // Check that candidates are alterable.
     $this->state->set('language_test.fallback_alter.candidates', TRUE);
     $expected = array_slice($expected, 0, count($expected) - 1);
-    $candidates = $manager->getFallbackCandidates();
+    $candidates = $this->languageManager->getFallbackCandidates();
     $this->assertEqual(array_values($candidates), $expected, 'Language fallback candidates are alterable.');
 
     // Check that candidates are alterable for specific operations.
@@ -72,28 +59,18 @@ public function testCandidates() {
     $this->state->set('language_test.fallback_operation_alter.candidates', TRUE);
     $expected[] = Language::LANGCODE_NOT_SPECIFIED;
     $expected[] = Language::LANGCODE_NOT_APPLICABLE;
-    $candidates = $manager->getFallbackCandidates(NULL, array('operation' => 'test'));
+    $candidates = $this->languageManager->getFallbackCandidates(NULL, array('operation' => 'test'));
     $this->assertEqual(array_values($candidates), $expected, 'Language fallback candidates are alterable for specific operations.');
 
     // Check that when the site is monolingual no language fallback is applied.
-    $default_langcode = language_default()->id;
-    foreach (language_list() as $langcode => $language) {
+    $default_langcode = $this->languageManager->getDefaultLanguage()->id;
+    foreach ($language_list as $langcode => $language) {
       if ($langcode != $default_langcode) {
         language_delete($langcode);
       }
     }
-    $candidates = $this->getLanguageManager()->getFallbackCandidates();
+    $candidates = $this->languageManager->getFallbackCandidates();
     $this->assertEqual(array_values($candidates), array(Language::LANGCODE_DEFAULT), 'Language fallback is not applied when the Language module is not enabled.');
   }
 
-  /**
-   * Returns the language manager service.
-   *
-   * @return \Drupal\Core\Language\LanguageManager
-   *   The language manager.
-   */
-  protected function getLanguageManager() {
-    return $this->container->get('language_manager');
-  }
-
 }
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageListTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageListTest.php
index 8b2190d4746812eb5fbb4111b543625727448f43..a475c04cae7314138a3d2fb61b11c3b4c6b79213 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageListTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageListTest.php
@@ -72,11 +72,11 @@ function testLanguageList() {
     );
     $this->drupalPostForm(NULL, $edit, t('Save configuration'));
     $this->assertNoOptionSelected('edit-site-default-language', 'en', 'Default language updated.');
-    $this->assertEqual($this->getUrl(), url($path, array('absolute' => TRUE)), 'Correct page redirection.');
+    $this->assertEqual($this->getUrl(), url($langcode . '/' . $path, array('absolute' => TRUE)), 'Correct page redirection.');
 
     // Ensure we can't delete the default language.
     $this->drupalGet('admin/config/regional/language/delete/' . $langcode);
-    $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.');
+    $this->assertEqual($this->getUrl(), url($langcode . '/admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.');
     $this->assertText(t('The default language cannot be deleted.'), 'Failed to delete the default language.');
 
     // Ensure 'Edit' link works.
@@ -89,7 +89,7 @@ function testLanguageList() {
     );
     $this->drupalPostForm('admin/config/regional/language/edit/' . $langcode, $edit, t('Save language'));
     $this->assertRaw($name, 'The language has been updated.');
-    $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.');
+    $this->assertEqual($this->getUrl(), url($langcode . '/admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.');
 
     // Change back the default language.
     $edit = array(
@@ -104,7 +104,7 @@ function testLanguageList() {
     $this->drupalGet('admin/config/regional/language/delete/' . $langcode);
     // First test the 'cancel' link.
     $this->clickLink(t('Cancel'));
-    $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.');
+    $this->assertEqual($this->getUrl(), url('en/admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.');
     $this->assertRaw($name, 'The language was not deleted.');
     // Delete the language for real. This a confirm form, we do not need any
     // fields changed.
@@ -112,19 +112,17 @@ function testLanguageList() {
     // We need raw here because %language and %langcode will add HTML.
     $t_args = array('%language' => $name, '%langcode' => $langcode);
     $this->assertRaw(t('The %language (%langcode) language has been removed.', $t_args), 'The test language has been removed.');
-    $this->assertEqual($this->getUrl(), url('admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.');
+    $this->assertEqual($this->getUrl(), url('en/admin/config/regional/language', array('absolute' => TRUE)), 'Correct page redirection.');
     // Verify that language is no longer found.
     $this->drupalGet('admin/config/regional/language/delete/' . $langcode);
     $this->assertResponse(404, 'Language no longer found.');
     // Make sure the "language_count" state has been updated correctly.
-    drupal_static_reset('language_list');
+    $this->container->get('language_manager')->reset();
     $languages = language_list();
-    $language_count =  $this->container->get('state')->get('language_count') ?: 1;
-    $this->assertEqual($language_count, count($languages), 'Language count is correct.');
     // Delete French.
     $this->drupalPostForm('admin/config/regional/language/delete/fr', array(), t('Delete'));
     // Get the count of languages.
-    drupal_static_reset('language_list');
+    $this->container->get('language_manager')->reset();
     $languages = language_list();
     // We need raw here because %language and %langcode will add HTML.
     $t_args = array('%language' => 'French', '%langcode' => 'fr');
@@ -134,8 +132,6 @@ function testLanguageList() {
     $this->drupalGet('admin/config/regional/language/delete/fr');
     $this->assertResponse(404, 'Language no longer found.');
     // Make sure the "language_count" state has not changed.
-    $language_count = $this->container->get('state')->get('language_count') ?: 1;
-    $this->assertEqual($language_count, count($languages), 'Language count is correct.');
 
     // Ensure we can delete the English language. Right now English is the only
     // language so we must add a new language and make it the default before
@@ -162,7 +158,7 @@ function testLanguageList() {
     );
     $this->drupalPostForm(NULL, $edit, t('Save configuration'));
     $this->assertNoOptionSelected('edit-site-default-language', 'en', 'Default language updated.');
-    $this->assertEqual($this->getUrl(), url($path, array('absolute' => TRUE)), 'Correct page redirection.');
+    $this->assertEqual($this->getUrl(), url($langcode . '/' . $path, array('absolute' => TRUE)), 'Correct page redirection.');
 
     $this->drupalPostForm('admin/config/regional/language/delete/en', array(), t('Delete'));
     // We need raw here because %language and %langcode will add HTML.
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php
index 6cff10616b8e212790bb9918add9f793d247854e..471dab28ee0ffd0d3757827880c61d106bbca7cb 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php
@@ -8,6 +8,7 @@
 namespace Drupal\language\Tests;
 
 use Drupal\Core\Language\Language;
+use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUI;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -22,6 +23,16 @@ class LanguageNegotiationInfoTest extends WebTestBase {
    */
   public static $modules = array('language');
 
+  /**
+   * The language manager.
+   *
+   * @var \Drupal\language\ConfigurableLanguageManagerInterface
+   */
+  protected $languageManager;
+
+  /**
+   * {@inheritdoc}
+   */
   public static function getInfo() {
     return array(
       'name' => 'Language negotiation info',
@@ -30,9 +41,12 @@ public static function getInfo() {
     );
   }
 
+  /**
+   * {@inheritdoc}
+   */
   function setUp() {
     parent::setUp();
-    require_once DRUPAL_ROOT .'/core/includes/language.inc';
+    $this->languageManager = $this->container->get('language_manager');
     $admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages', 'view the administration theme'));
     $this->drupalLogin($admin_user);
     $this->drupalPostForm('admin/config/regional/language/add', array('predefined_langcode' => 'it'), t('Add language'));
@@ -56,13 +70,13 @@ function testInfoAlterations() {
     \Drupal::state()->set('language_test.content_language_type', TRUE);
     $this->languageNegotiationUpdate();
     $type = Language::TYPE_CONTENT;
-    $language_types = language_types_get_configurable();
+    $language_types = $this->languageManager->getLanguageTypes();
     $this->assertTrue(in_array($type, $language_types), 'Content language type is configurable.');
 
     // Enable some core and custom language negotiation methods. The test
     // language type is supposed to be configurable.
     $test_type = 'test_language_type';
-    $interface_method_id = LANGUAGE_NEGOTIATION_INTERFACE;
+    $interface_method_id = LanguageNegotiationUI::METHOD_ID;
     $test_method_id = 'test_language_negotiation_method';
     $form_field = $type . '[enabled]['. $interface_method_id .']';
     $edit = array(
@@ -83,7 +97,7 @@ function testInfoAlterations() {
 
     // Check that type-specific language negotiation methods can be assigned
     // only to the corresponding language types.
-    foreach (language_types_get_configurable() as $type) {
+    foreach ($this->languageManager->getLanguageTypes() as $type) {
       $form_field = $type . '[enabled][test_language_negotiation_method_ts]';
       if ($type == $test_type) {
         $this->assertFieldByXPath("//input[@name=\"$form_field\"]", NULL, format_string('Type-specific test language negotiation method available for %type.', array('%type' => $type)));
@@ -96,7 +110,7 @@ function testInfoAlterations() {
     // Check language negotiation results.
     $this->drupalGet('');
     $last = \Drupal::state()->get('language_test.language_negotiation_last');
-    foreach (language_types_get_all() as $type) {
+    foreach ($this->languageManager->getDefinedLanguageTypes() as $type) {
       $langcode = $last[$type];
       $value = $type == Language::TYPE_CONTENT || strpos($type, 'test') !== FALSE ? 'it' : 'en';
       $this->assertEqual($langcode, $value, format_string('The negotiated language for %type is %language', array('%type' => $type, '%language' => $value)));
@@ -107,7 +121,7 @@ function testInfoAlterations() {
     $this->languageNegotiationUpdate('uninstall');
 
     // Check that only the core language types are available.
-    foreach (language_types_get_all() as $type) {
+    foreach ($this->languageManager->getDefinedLanguageTypes() as $type) {
       $this->assertTrue(strpos($type, 'test') === FALSE, format_string('The %type language is still available', array('%type' => $type)));
     }
 
@@ -139,17 +153,16 @@ protected function languageNegotiationUpdate($op = 'install') {
     // Install/uninstall language_test only if we did not already before.
     if ($last_op != $op) {
       call_user_func(array($this->container->get('module_handler'), $op), $modules);
-      // Reset hook implementation cache.
-      $this->container->get('module_handler')->resetImplementations();
+      $last_op = $op;
     }
-
-    drupal_static_reset('language_types_info');
-    drupal_static_reset('language_negotiation_info');
-    $function = "language_modules_{$op}ed";
-    if (function_exists($function)) {
-      $function($modules);
+    else {
+      $function = "language_modules_{$op}ed";
+      if (function_exists($function)) {
+        $function($modules);
+      }
     }
 
+    $this->languageManager->reset();
     $this->drupalGet('admin/config/regional/language/detection');
   }
 
@@ -157,9 +170,8 @@ protected function languageNegotiationUpdate($op = 'install') {
    * Check that language negotiation for fixed types matches the stored one.
    */
   protected function checkFixedLanguageTypes() {
-    drupal_static_reset('language_types_info');
-    $configurable = language_types_get_configurable();
-    foreach (language_types_info() as $type => $info) {
+    $configurable = $this->languageManager->getLanguageTypes();
+    foreach ($this->languageManager->getDefinedLanguageTypesInfo() as $type => $info) {
       if (!in_array($type, $configurable) && isset($info['fixed'])) {
         $negotiation = variable_get("language_negotiation_$type", array());
         $equal = count($info['fixed']) == count($negotiation);
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguagePathMonolingualTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguagePathMonolingualTest.php
index 731ccf0a2bb7d463e48565b46418d1113f2aab2c..840f8fc5071d3b2afc1822f64ed1d6f17cb7e116 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguagePathMonolingualTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguagePathMonolingualTest.php
@@ -51,7 +51,8 @@ function setUp() {
     $this->drupalPostForm('admin/config/regional/language/delete/en', array(), t('Delete'));
 
     // Verify that French is the only language.
-    $this->assertFalse(language_multilingual(), 'Site is mono-lingual');
+    $this->container->get('language_manager')->reset();
+    $this->assertFalse(\Drupal::languageManager()->isMultilingual(), 'Site is mono-lingual');
     $this->assertEqual(language_default()->id, 'fr', 'French is the default language');
 
     // Set language detection to URL.
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageTestBase.php b/core/modules/language/lib/Drupal/language/Tests/LanguageTestBase.php
new file mode 100644
index 0000000000000000000000000000000000000000..70a512204f8af11e51ea2c46832d446aae41da7d
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageTestBase.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\language\Tests\LanguageTestBase.
+ */
+
+namespace Drupal\language\Tests;
+
+use Drupal\simpletest\DrupalUnitTestBase;
+
+/**
+ * Test for dependency injected language object.
+ */
+abstract class LanguageTestBase extends DrupalUnitTestBase {
+
+  /**
+   * The language manager.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface
+   */
+  protected $languageManager;
+
+  /**
+   * The state storage service.
+   *
+   * @var \Drupal\Core\KeyValueStore\StateInterface
+   */
+  protected $state;
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUp() {
+    parent::setUp();
+
+    $this->enableModules(array('system', 'language', 'language_test'));
+    $this->installSchema('system', array('variable'));
+    $this->installConfig(array('language'));
+
+    $this->state = $this->container->get('state');
+
+    // Ensure we are building a new Language object for each test.
+    $this->languageManager = $this->container->get('language_manager');
+    $this->languageManager->reset();
+  }
+
+}
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php
index 7c037b5f64187f9579ab1937192d51a0c590a455..e8b3b2661f9ac8e87380a18a1ee4b242c99fc8f1 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php
@@ -7,9 +7,15 @@
 
 namespace Drupal\language\Tests;
 
+use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationBrowser;
+use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationSelected;
+use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
+use Drupal\user\Plugin\LanguageNegotiation\LanguageNegotiationUser;
+use Drupal\user\Plugin\LanguageNegotiation\LanguageNegotiationUserAdmin;
 use Drupal\simpletest\WebTestBase;
 use Drupal\Core\Language\Language;
 use Symfony\Component\HttpFoundation\Request;
+use Drupal\language\LanguageNegotiatorInterface;
 
 /**
  * Test UI language negotiation
@@ -48,12 +54,12 @@ class LanguageUILanguageNegotiationTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('locale', 'language_test', 'block');
+  public static $modules = array('locale', 'language_test', 'block', 'user');
 
   public static function getInfo() {
     return array(
       'name' => 'UI language negotiation',
-      'description' => 'Test UI language switching by URL path prefix and domain.',
+      'description' => 'Test UI language switching.',
       'group' => 'Language',
     );
   }
@@ -64,7 +70,6 @@ function setUp() {
     $this->request = Request::create('http://example.com/');
     $this->container->set('request', $this->request);
 
-    require_once DRUPAL_ROOT . '/core/includes/language.inc';
     $admin_user = $this->drupalCreateUser(array('administer languages', 'translate interface', 'access administration pages', 'administer blocks'));
     $this->drupalLogin($admin_user);
   }
@@ -106,7 +111,7 @@ function testUILanguageNegotiation() {
     // into database when seen by t(). Without doing this, our target string
     // is for some reason not found when doing translate search. This might
     // be some bug.
-    drupal_static_reset('language_list');
+    $this->container->get('language_manager')->reset();
     $languages = language_list();
     variable_set('language_default', (array) $languages['vi']);
     // First visit this page to make sure our target string is searchable.
@@ -142,17 +147,14 @@ function testUILanguageNegotiation() {
     );
     $this->drupalPostForm('admin/config/regional/translate', $edit, t('Save translations'));
 
-    // Configure URL language rewrite.
-    variable_set('language_negotiation_url_type', Language::TYPE_INTERFACE);
-
     // Configure selected language negotiation to use zh-hans.
     $edit = array('selected_langcode' => $langcode);
     $this->drupalPostForm('admin/config/regional/language/detection/selected', $edit, t('Save configuration'));
     $test = array(
-      'language_negotiation' => array(LANGUAGE_NEGOTIATION_SELECTED),
+      'language_negotiation' => array(LanguageNegotiationSelected::METHOD_ID),
       'path' => 'admin/config',
       'expect' => $language_string,
-      'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED,
+      'expected_method_id' => LanguageNegotiationSelected::METHOD_ID,
       'http_header' => $http_header_browser_fallback,
       'message' => 'SELECTED: UI language is switched based on selected language.',
     );
@@ -161,10 +163,10 @@ function testUILanguageNegotiation() {
     // An invalid language is selected.
     \Drupal::config('language.negotiation')->set('selected_langcode', NULL)->save();
     $test = array(
-      'language_negotiation' => array(LANGUAGE_NEGOTIATION_SELECTED),
+      'language_negotiation' => array(LanguageNegotiationSelected::METHOD_ID),
       'path' => 'admin/config',
       'expect' => $default_string,
-      'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED,
+      'expected_method_id' => LanguageNegotiatorInterface::METHOD_ID,
       'http_header' => $http_header_browser_fallback,
       'message' => 'SELECTED > DEFAULT: UI language is switched based on selected language.',
     );
@@ -173,10 +175,10 @@ function testUILanguageNegotiation() {
     // No selected language is available.
     \Drupal::config('language.negotiation')->set('selected_langcode', $langcode_unknown)->save();
     $test = array(
-      'language_negotiation' => array(LANGUAGE_NEGOTIATION_SELECTED),
+      'language_negotiation' => array(LanguageNegotiationSelected::METHOD_ID),
       'path' => 'admin/config',
       'expect' => $default_string,
-      'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED,
+      'expected_method_id' => LanguageNegotiatorInterface::METHOD_ID,
       'http_header' => $http_header_browser_fallback,
       'message' => 'SELECTED > DEFAULT: UI language is switched based on selected language.',
     );
@@ -185,46 +187,46 @@ function testUILanguageNegotiation() {
     $tests = array(
       // Default, browser preference should have no influence.
       array(
-        'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_SELECTED),
+        'language_negotiation' => array(LanguageNegotiationUrl::METHOD_ID, LanguageNegotiationSelected::METHOD_ID),
         'path' => 'admin/config',
         'expect' => $default_string,
-        'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED,
+        'expected_method_id' => LanguageNegotiatorInterface::METHOD_ID,
         'http_header' => $http_header_browser_fallback,
         'message' => 'URL (PATH) > DEFAULT: no language prefix, UI language is default and the browser language preference setting is not used.',
       ),
       // Language prefix.
       array(
-        'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_SELECTED),
+        'language_negotiation' => array(LanguageNegotiationUrl::METHOD_ID, LanguageNegotiationSelected::METHOD_ID),
         'path' => "$langcode/admin/config",
         'expect' => $language_string,
-        'expected_method_id' => LANGUAGE_NEGOTIATION_URL,
+        'expected_method_id' => LanguageNegotiationUrl::METHOD_ID,
         'http_header' => $http_header_browser_fallback,
         'message' => 'URL (PATH) > DEFAULT: with language prefix, UI language is switched based on path prefix',
       ),
       // Default, go by browser preference.
       array(
-        'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_BROWSER),
+        'language_negotiation' => array(LanguageNegotiationUrl::METHOD_ID, LanguageNegotiationBrowser::METHOD_ID),
         'path' => 'admin/config',
         'expect' => $language_browser_fallback_string,
-        'expected_method_id' => LANGUAGE_NEGOTIATION_BROWSER,
+        'expected_method_id' => LanguageNegotiationBrowser::METHOD_ID,
         'http_header' => $http_header_browser_fallback,
         'message' => 'URL (PATH) > BROWSER: no language prefix, UI language is determined by browser language preference',
       ),
       // Prefix, switch to the language.
       array(
-        'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_BROWSER),
+        'language_negotiation' => array(LanguageNegotiationUrl::METHOD_ID, LanguageNegotiationBrowser::METHOD_ID),
         'path' => "$langcode/admin/config",
         'expect' => $language_string,
-        'expected_method_id' => LANGUAGE_NEGOTIATION_URL,
+        'expected_method_id' => LanguageNegotiationUrl::METHOD_ID,
         'http_header' => $http_header_browser_fallback,
         'message' => 'URL (PATH) > BROWSER: with language prefix, UI language is based on path prefix',
       ),
       // Default, browser language preference is not one of site's lang.
       array(
-        'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_BROWSER, LANGUAGE_NEGOTIATION_SELECTED),
+        'language_negotiation' => array(LanguageNegotiationUrl::METHOD_ID, LanguageNegotiationBrowser::METHOD_ID, LanguageNegotiationSelected::METHOD_ID),
         'path' => 'admin/config',
         'expect' => $default_string,
-        'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED,
+        'expected_method_id' => LanguageNegotiatorInterface::METHOD_ID,
         'http_header' => $http_header_blah,
         'message' => 'URL (PATH) > BROWSER > DEFAULT: no language prefix and browser language preference set to unknown language should use default language',
       ),
@@ -235,7 +237,8 @@ function testUILanguageNegotiation() {
     }
 
     // Unknown language prefix should return 404.
-    variable_set('language_negotiation_' . Language::TYPE_INTERFACE, language_language_negotiation_info());
+    $definitions = \Drupal::languageManager()->getNegotiator()->getNegotiationMethods();
+    variable_set('language_negotiation_' . Language::TYPE_INTERFACE, array_flip(array_keys($definitions)));
     $this->drupalGet("$langcode_unknown/admin/config", array(), $http_header_browser_fallback);
     $this->assertResponse(404, "Unknown language path prefix should return 404");
 
@@ -245,10 +248,10 @@ function testUILanguageNegotiation() {
     $account->save();
 
     $test = array(
-      'language_negotiation' => array(LANGUAGE_NEGOTIATION_USER, LANGUAGE_NEGOTIATION_SELECTED),
+      'language_negotiation' => array(LanguageNegotiationUser::METHOD_ID, LanguageNegotiationSelected::METHOD_ID),
       'path' => 'admin/config',
       'expect' => $default_string,
-      'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED,
+      'expected_method_id' => LanguageNegotiatorInterface::METHOD_ID,
       'http_header' => array(),
       'message' => 'USER > DEFAULT: no preferred user language setting, the UI language is default',
     );
@@ -260,10 +263,10 @@ function testUILanguageNegotiation() {
     $account->save();
 
     $test = array(
-      'language_negotiation' => array(LANGUAGE_NEGOTIATION_USER, LANGUAGE_NEGOTIATION_SELECTED),
+      'language_negotiation' => array(LanguageNegotiationUser::METHOD_ID, LanguageNegotiationSelected::METHOD_ID),
       'path' => 'admin/config',
       'expect' => $default_string,
-      'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED,
+      'expected_method_id' => LanguageNegotiatorInterface::METHOD_ID,
       'http_header' => array(),
       'message' => 'USER > DEFAULT: invalid preferred user language setting, the UI language is default',
     );
@@ -274,10 +277,10 @@ function testUILanguageNegotiation() {
     $account->save();
 
     $test = array(
-      'language_negotiation' => array(LANGUAGE_NEGOTIATION_USER, LANGUAGE_NEGOTIATION_SELECTED),
+      'language_negotiation' => array(LanguageNegotiationUser::METHOD_ID, LanguageNegotiationSelected::METHOD_ID),
       'path' => 'admin/config',
       'expect' => $language_string,
-      'expected_method_id' => LANGUAGE_NEGOTIATION_USER,
+      'expected_method_id' => LanguageNegotiationUser::METHOD_ID,
       'http_header' => array(),
       'message' => 'USER > DEFAULT: defined prefereed user language setting, the UI language is based on user setting',
     );
@@ -288,10 +291,10 @@ function testUILanguageNegotiation() {
     $account->save();
 
     $test = array(
-      'language_negotiation' => array(LANGUAGE_NEGOTIATION_USER_ADMIN, LANGUAGE_NEGOTIATION_SELECTED),
+      'language_negotiation' => array(LanguageNegotiationUserAdmin::METHOD_ID, LanguageNegotiationSelected::METHOD_ID),
       'path' => 'admin/config',
       'expect' => $default_string,
-      'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED,
+      'expected_method_id' => LanguageNegotiatorInterface::METHOD_ID,
       'http_header' => array(),
       'message' => 'USER ADMIN > DEFAULT: no preferred user admin language setting, the UI language is default',
     );
@@ -302,10 +305,10 @@ function testUILanguageNegotiation() {
     $account->save();
 
     $test = array(
-      'language_negotiation' => array(LANGUAGE_NEGOTIATION_USER_ADMIN, LANGUAGE_NEGOTIATION_SELECTED),
+      'language_negotiation' => array(LanguageNegotiationUserAdmin::METHOD_ID, LanguageNegotiationSelected::METHOD_ID),
       'path' => 'admin/config',
       'expect' => $default_string,
-      'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED,
+      'expected_method_id' => LanguageNegotiatorInterface::METHOD_ID,
       'http_header' => array(),
       'message' => 'USER ADMIN > DEFAULT: invalid preferred user admin language setting, the UI language is default',
     );
@@ -316,56 +319,20 @@ function testUILanguageNegotiation() {
     $account->save();
 
     $test = array(
-      'language_negotiation' => array(LANGUAGE_NEGOTIATION_USER_ADMIN, LANGUAGE_NEGOTIATION_SELECTED),
+      'language_negotiation' => array(LanguageNegotiationUserAdmin::METHOD_ID, LanguageNegotiationSelected::METHOD_ID),
       'path' => 'admin/config',
       'expect' => $language_string,
-      'expected_method_id' => LANGUAGE_NEGOTIATION_USER_ADMIN,
+      'expected_method_id' => LanguageNegotiationUserAdmin::METHOD_ID,
       'http_header' => array(),
       'message' => 'USER ADMIN > DEFAULT: defined prefereed user admin language setting, the UI language is based on user setting',
     );
     $this->runTest($test);
-
-    // Setup for domain negotiation, first configure the language to have domain
-    // URL.
-    $edit = array("domain[$langcode]" => $language_domain);
-    $this->drupalPostForm("admin/config/regional/language/detection/url", $edit, t('Save configuration'));
-    // Set the site to use domain language negotiation.
-
-    $tests = array(
-      // Default domain, browser preference should have no influence.
-      array(
-        'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_SELECTED),
-        'language_negotiation_url_part' => LANGUAGE_NEGOTIATION_URL_DOMAIN,
-        'path' => 'admin/config',
-        'expect' => $default_string,
-        'expected_method_id' => LANGUAGE_NEGOTIATION_SELECTED,
-        'http_header' => $http_header_browser_fallback,
-        'message' => 'URL (DOMAIN) > DEFAULT: default domain should get default language',
-      ),
-      // Language domain specific URL, we set the 'HTTP_HOST' property of
-      // \Drupal::request()->server in \Drupal\language_test\LanguageTestManager
-      // to simulate this.
-      array(
-        'language_negotiation' => array(LANGUAGE_NEGOTIATION_URL, LANGUAGE_NEGOTIATION_SELECTED),
-        'language_negotiation_url_part' => LANGUAGE_NEGOTIATION_URL_DOMAIN,
-        'language_test_domain' => $language_domain . ':88',
-        'path' => 'admin/config',
-        'expect' => $language_string,
-        'expected_method_id' => LANGUAGE_NEGOTIATION_URL,
-        'http_header' => $http_header_browser_fallback,
-        'message' => 'URL (DOMAIN) > DEFAULT: domain example.cn should switch to Chinese',
-      ),
-    );
-
-    foreach ($tests as $test) {
-      $this->runTest($test);
-    }
   }
 
   protected function runTest($test) {
     if (!empty($test['language_negotiation'])) {
       $method_weights = array_flip($test['language_negotiation']);
-      language_negotiation_set(Language::TYPE_INTERFACE, $method_weights);
+      $this->container->get('language_negotiator')->saveConfiguration(Language::TYPE_INTERFACE, $method_weights);
     }
     if (!empty($test['language_negotiation_url_part'])) {
       \Drupal::config('language.negotiation')
@@ -449,7 +416,7 @@ function testLanguageDomain() {
 
     // Change the domain for the Italian language.
     $edit = array(
-      'language_negotiation_url_part' => LANGUAGE_NEGOTIATION_URL_DOMAIN,
+      'language_negotiation_url_part' => LanguageNegotiationUrl::CONFIG_DOMAIN,
       'domain[it]' => 'it.example.com',
     );
     $this->drupalPostForm('admin/config/regional/language/detection/url', $edit, t('Save configuration'));
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageUrlRewritingTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageUrlRewritingTest.php
index 07ee9a9762f32d8b2643496fee1374fb4245c1fc..847348e9b9132cb1d58e687f0bf4a6727c9adac5 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageUrlRewritingTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageUrlRewritingTest.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\language\Tests;
 
+use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
 use Drupal\simpletest\WebTestBase;
 use Symfony\Component\HttpFoundation\Request;
 
@@ -46,8 +47,6 @@ function setUp() {
     $edit = array('language_interface[enabled][language-url]' => 1);
     $this->drupalPostForm('admin/config/regional/language/detection', $edit, t('Save settings'));
 
-    // Reset static caching.
-    drupal_static_reset('language_list');
   }
 
   /**
@@ -105,7 +104,7 @@ private function checkUrl($language, $message1, $message2) {
   function testDomainNameNegotiationPort() {
     $language_domain = 'example.fr';
     $edit = array(
-      'language_negotiation_url_part' => LANGUAGE_NEGOTIATION_URL_DOMAIN,
+      'language_negotiation_url_part' => LanguageNegotiationUrl::CONFIG_DOMAIN,
       'domain[fr]' => $language_domain
     );
     $this->drupalPostForm('admin/config/regional/language/detection/url', $edit, t('Save configuration'));
@@ -115,11 +114,11 @@ function testDomainNameNegotiationPort() {
 
     // Enable domain configuration.
     \Drupal::config('language.negotiation')
-      ->set('url.source', LANGUAGE_NEGOTIATION_URL_DOMAIN)
+      ->set('url.source', LanguageNegotiationUrl::CONFIG_DOMAIN)
       ->save();
 
     // Reset static caching.
-    drupal_static_reset('language_list');
+    $this->container->get('language_manager')->reset();
 
     // In case index.php is part of the URLs, we need to adapt the asserted
     // URLs as well.
diff --git a/core/modules/language/tests/Drupal/language/Tests/LanguageNegotiationUrlTest.php b/core/modules/language/tests/Drupal/language/Tests/LanguageNegotiationUrlTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..48a39a155fdc6a762a7ff3e2b140ffa2ff2b379c
--- /dev/null
+++ b/core/modules/language/tests/Drupal/language/Tests/LanguageNegotiationUrlTest.php
@@ -0,0 +1,165 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\Tests\LanguageNegotiationUrlTest.
+ */
+
+namespace Drupal\language\Tests;
+
+use Drupal\Core\Session\UserSession;
+use Drupal\Tests\UnitTestCase;
+use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Tests the URL and domain language negotiation.
+ *
+ * @group Language
+ *
+ * @see \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl
+ */
+class LanguageNegotiationUrlTest extends UnitTestCase {
+
+  protected $languageManager;
+  protected $user;
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Language negotiation URL',
+      'description' => 'Tests the URL/domain Language negotiation plugin',
+      'group' => 'Language',
+    );
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setUp() {
+
+    // Set up some languages to be used by the language-based path processor.
+    $languages = array(
+      'de' => (object) array(
+        'id' => 'de',
+      ),
+      'en' => (object) array(
+        'id' => 'en',
+      ),
+    );
+
+    // Create a language manager stub.
+    $language_manager = $this->getMockBuilder('Drupal\language\ConfigurableLanguageManagerInterface')
+      ->getMock();
+    $language_manager->expects($this->any())
+      ->method('getCurrentLanguage')
+      ->will($this->returnValue($languages['en']));
+    $language_manager->expects($this->any())
+      ->method('getLanguages')
+      ->will($this->returnValue($languages));
+    $this->languageManager = $language_manager;
+
+    // Create a user stub.
+    $this->user = $this->getMockBuilder('Drupal\Core\Session\AccountInterface')
+      ->getMock();
+  }
+
+  /**
+   * Test domain language negotiation.
+   *
+   * @dataProvider providerTestDomain
+   */
+  public function testDomain($http_host, $domains, $expected_langcode) {
+    $config_data = array(
+      'source' => LanguageNegotiationUrl::CONFIG_DOMAIN,
+      'domains' => $domains,
+    );
+
+    $config_object = $this->getMockBuilder('Drupal\Core\Config\Config')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $config_object->expects($this->any())
+      ->method('get')
+      ->with('url')
+      ->will($this->returnValue($config_data));
+
+    $config = $this->getMockBuilder('Drupal\Core\Config\ConfigFactory')
+      ->disableOriginalConstructor()
+      ->getMock();
+    $config->expects($this->any())
+      ->method('get')
+      ->with('language.negotiation')
+      ->will($this->returnValue($config_object));
+
+    $request = Request::create('', 'GET', array(), array(), array(), array('HTTP_HOST' => $http_host));
+    $method = new LanguageNegotiationUrl();
+    $method->setLanguageManager($this->languageManager);
+    $method->setConfig($config);
+    $method->setCurrentUser($this->user);
+    $this->assertEquals($expected_langcode, $method->getLangcode($request));
+  }
+
+  /**
+   * Provides data for the domain test.
+   *
+   * @return array
+   *   An array of data for checking domain negotation.
+   */
+  public function providerTestDomain() {
+
+    $domain_configuration[] = array(
+      'http_host' => 'example.de',
+      'domains' => array(
+        'de' => 'http://example.de',
+      ),
+      'expected_langocde' => 'de',
+    );
+    // No configuration.
+    $domain_configuration[] = array(
+      'http_host' => 'example.de',
+      'domains' => array(),
+      'expected_langocde' => FALSE,
+    );
+    // HTTP host with a port.
+    $domain_configuration[] = array(
+      'http_host' => 'example.de:8080',
+      'domains' => array(
+        'de' => 'http://example.de',
+      ),
+      'expected_langocde' => 'de',
+    );
+    // Domain configuration with https://.
+    $domain_configuration[] = array(
+      'http_host' => 'example.de',
+      'domains' => array(
+        'de' => 'https://example.de',
+      ),
+      'expected_langocde' => 'de',
+    );
+    // Non-matching HTTP host.
+    $domain_configuration[] = array(
+      'http_host' => 'example.com',
+      'domains' => array(
+        'de' => 'http://example.com',
+      ),
+      'expected_langocde' => 'de',
+    );
+    // Testing a non-existing language.
+    $domain_configuration[] = array(
+      'http_host' => 'example.com',
+      'domains' => array(
+        'it' => 'http://example.it',
+      ),
+      'expected_langocde' => FALSE,
+    );
+    // Multiple domain configurations.
+    $domain_configuration[] = array(
+      'http_host' => 'example.com',
+      'domains' => array(
+        'de' => 'http://example.de',
+        'en' => 'http://example.com',
+      ),
+      'expected_langocde' => 'en',
+    );
+    return $domain_configuration;
+  }
+}
diff --git a/core/modules/language/tests/language_test/language_test.module b/core/modules/language/tests/language_test/language_test.module
index 31c0a848f651f8d7502f300b599f9ef803bbcef6..38171374a7c2dab0b53b1e52e715d60da13f0ad3 100644
--- a/core/modules/language/tests/language_test/language_test.module
+++ b/core/modules/language/tests/language_test/language_test.module
@@ -5,10 +5,8 @@
  * Mock module for language layer tests.
  */
 
-use Symfony\Component\HttpFoundation\Request;
-use Symfony\Component\HttpKernel\HttpKernelInterface;
-
 use Drupal\Core\Language\Language;
+use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUI;
 
 /**
  * Implements hook_page_build().
@@ -47,47 +45,20 @@ function language_test_language_types_info_alter(array &$language_types) {
     unset($language_types[Language::TYPE_CONTENT]['fixed']);
     // By default languages are not configurable. Make Language::TYPE_CONTENT
     // configurable.
-    $configurable = \Drupal::config('system.language.types')->get('configurable');
+    $configurable = \Drupal::config('language.types')->get('configurable');
     if (!in_array(Language::TYPE_CONTENT, $configurable)) {
       $configurable[] = Language::TYPE_CONTENT;
-      \Drupal::config('system.language.types')->set('configurable', $configurable)->save();
+      \Drupal::config('language.types')->set('configurable', $configurable)->save();
     }
   }
 }
 
-/**
- * Implements hook_language_negotiation_info().
- */
-function language_test_language_negotiation_info() {
-  if (\Drupal::state()->get('language_test.language_negotiation_info')) {
-    $info = array(
-      'callbacks' => array(
-        'negotiation' => 'language_test_language_negotiation_method',
-      ),
-      'file' => drupal_get_path('module', 'language_test') .'/language_test.module',
-      'weight' => -10,
-      'description' => t('This is a test language negotiation method.'),
-    );
-
-    return array(
-      'test_language_negotiation_method' => array(
-        'name' => t('Test'),
-        'types' => array(Language::TYPE_CONTENT, 'test_language_type', 'fixed_test_language_type'),
-      ) + $info,
-      'test_language_negotiation_method_ts' => array(
-        'name' => t('Type-specific test'),
-        'types' => array('test_language_type'),
-      ) + $info,
-    );
-  }
-}
-
 /**
  * Implements hook_language_negotiation_info_alter().
  */
 function language_test_language_negotiation_info_alter(array &$negotiation_info) {
   if (\Drupal::state()->get('language_test.language_negotiation_info_alter')) {
-    unset($negotiation_info[LANGUAGE_NEGOTIATION_INTERFACE]);
+    unset($negotiation_info[LanguageNegotiationUI::METHOD_ID]);
   }
 }
 
@@ -95,19 +66,13 @@ function language_test_language_negotiation_info_alter(array &$negotiation_info)
  * Store the last negotiated languages.
  */
 function language_test_store_language_negotiation() {
-  $last = array();
-  print_r(language_types_get_all());
-  foreach (language_types_get_all() as $type) {
-    $last[$type] = language($type)->id;
+  if (\Drupal::moduleHandler()->moduleExists('language')) {
+    $last = array();
+    foreach (\Drupal::languageManager()->getDefinedLanguageTypes() as $type) {
+      $last[$type] = \Drupal::languageManager()->getCurrentLanguage($type)->id;
+    }
+    \Drupal::state()->set('language_test.language_negotiation_last', $last);
   }
-  \Drupal::state()->set('language_test.language_negotiation_last', $last);
-}
-
-/**
- * Provides a test language negotiation method.
- */
-function language_test_language_negotiation_method($languages) {
-  return 'it';
 }
 
 /**
diff --git a/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestManager.php b/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestManager.php
deleted file mode 100644
index 3c460b658a0271ad2d2d7c07fa1f0db85d9a005a..0000000000000000000000000000000000000000
--- a/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestManager.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\language_test\LanguageTestManager.
- */
-
-namespace Drupal\language_test;
-
-use Drupal\Core\Language\LanguageManager;
-use Symfony\Component\HttpFoundation\Request;
-
-/**
- * Defines a LanguageManager service to test URL negotiation.
- */
-class LanguageTestManager extends LanguageManager {
-
-  /**
-   * Overrides \Drupal\Core\Language\LanguageManager::init().
-   */
-  public function init() {
-    if ($test_domain = \Drupal::state()->get('language_test.domain')) {
-      \Drupal::request()->server->set('HTTP_HOST', $test_domain);
-    }
-    return parent::init();
-  }
-
-}
diff --git a/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestServiceProvider.php b/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestServiceProvider.php
deleted file mode 100644
index 39aef04470d5b036097d3c538fa6da6cdf250a34..0000000000000000000000000000000000000000
--- a/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestServiceProvider.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\language_test\LanguageTestServiceProvider.
- */
-
-namespace Drupal\language_test;
-
-use Drupal\Core\DependencyInjection\ContainerBuilder;
-use Drupal\Core\DependencyInjection\ServiceModifierInterface;
-
-/**
- * Defines the LanguageTest service provider.
- */
-class LanguageTestServiceProvider implements ServiceModifierInterface {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function alter(ContainerBuilder $container) {
-    // Overrides language_manager class to test domain language negotiation.
-    $definition = $container->getDefinition('language_manager');
-    $definition->setClass('Drupal\language_test\LanguageTestManager');
-  }
-
-}
-
diff --git a/core/modules/language/tests/language_test/lib/Drupal/language_test/Plugin/LanguageNegotiation/LanguageNegotiationTest.php b/core/modules/language/tests/language_test/lib/Drupal/language_test/Plugin/LanguageNegotiation/LanguageNegotiationTest.php
new file mode 100644
index 0000000000000000000000000000000000000000..a726c5cf5850a79506bcea4aa2cf92a874037d26
--- /dev/null
+++ b/core/modules/language/tests/language_test/lib/Drupal/language_test/Plugin/LanguageNegotiation/LanguageNegotiationTest.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language_test\\Plugin\LanguageNegotiation\LanguageNegotiationTest.
+ */
+
+namespace Drupal\language_test\Plugin\LanguageNegotiation;
+
+use Drupal\language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class for identifying language from a selected language.
+ *
+ * @Plugin(
+ *   id = "test_language_negotiation_method",
+ *   weight = -10,
+ *   name = @Translation("Test"),
+ *   description = @Translation("This is a test language negotiation method."),
+ *   types = {Drupal\Core\Language\Language::TYPE_CONTENT, "test_language_type", "fixed_test_language_type"}
+ * )
+ */
+class LanguageNegotiationTest extends LanguageNegotiationMethodBase {
+
+  /**
+   * The language negotiation method id.
+   */
+  const METHOD_ID = 'test_language_negotiation_method';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLangcode(Request $request = NULL) {
+    return 'it';
+  }
+
+}
diff --git a/core/modules/language/tests/language_test/lib/Drupal/language_test/Plugin/LanguageNegotiation/LanguageNegotiationTestTs.php b/core/modules/language/tests/language_test/lib/Drupal/language_test/Plugin/LanguageNegotiation/LanguageNegotiationTestTs.php
new file mode 100644
index 0000000000000000000000000000000000000000..cef125c804b80b5955c3a606316d7291953304f2
--- /dev/null
+++ b/core/modules/language/tests/language_test/lib/Drupal/language_test/Plugin/LanguageNegotiation/LanguageNegotiationTestTs.php
@@ -0,0 +1,30 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language_test\\Plugin\LanguageNegotiation\LanguageNegotiationTestTs.
+ */
+
+namespace Drupal\language_test\Plugin\LanguageNegotiation;
+
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class for identifying language from a selected language.
+ *
+ * @Plugin(
+ *   id = "test_language_negotiation_method_ts",
+ *   weight = -10,
+ *   name = @Translation("Type-specific test"),
+ *   description = @Translation("This is a test language negotiation method."),
+ *   types = {"test_language_type"}
+ * )
+ */
+class LanguageNegotiationTestTs extends LanguageNegotiationTest {
+
+  /**
+   * The language negotiation method id.
+   */
+  const METHOD_ID = 'test_language_negotiation_method_ts';
+
+}
diff --git a/core/modules/locale/lib/Drupal/locale/Form/TranslateEditForm.php b/core/modules/locale/lib/Drupal/locale/Form/TranslateEditForm.php
index 34c93bede91aca338041ce3b611697faf3d9b973..d6e4b0963f1cfd827bf016dc81b263771cdfd6ce 100644
--- a/core/modules/locale/lib/Drupal/locale/Form/TranslateEditForm.php
+++ b/core/modules/locale/lib/Drupal/locale/Form/TranslateEditForm.php
@@ -29,7 +29,7 @@ public function buildForm(array $form, array &$form_state) {
     $filter_values = $this->translateFilterValues();
     $langcode = $filter_values['langcode'];
 
-    drupal_static_reset('language_list');
+    $this->languageManager->reset();
     $languages = language_list();
 
     $langname = isset($langcode) ? $languages[$langcode]->name : "- None -";
diff --git a/core/modules/locale/lib/Drupal/locale/Form/TranslateFormBase.php b/core/modules/locale/lib/Drupal/locale/Form/TranslateFormBase.php
index ea4c9ce052ddc40e608ce444b016b01ffff48864..c5447e02f4fc93dd6327c1877941708cf5974feb 100644
--- a/core/modules/locale/lib/Drupal/locale/Form/TranslateFormBase.php
+++ b/core/modules/locale/lib/Drupal/locale/Form/TranslateFormBase.php
@@ -161,7 +161,7 @@ protected function translateFilters() {
     $filters = array();
 
     // Get all languages, except English.
-    drupal_static_reset('language_list');
+    $this->languageManager->reset();
     $languages = language_list();
     $language_options = array();
     foreach ($languages as $langcode => $language) {
@@ -171,7 +171,7 @@ protected function translateFilters() {
     }
 
     // Pick the current interface language code for the filter.
-    $default_langcode = $this->languageManager->getLanguage(Language::TYPE_INTERFACE)->id;
+    $default_langcode = $this->languageManager->getCurrentLanguage()->id;
     if (!isset($language_options[$default_langcode])) {
       $available_langcodes = array_keys($language_options);
       $default_langcode = array_shift($available_langcodes);
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php
index 87febe49f26268b6f14e858181ff2dad7493bc6d..1c2c5972758610c94b7d5224ec04d5dc1e191198 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php
@@ -47,7 +47,6 @@ function testMachineNameLTR() {
     $edit = array();
     $edit['predefined_langcode'] = 'ar';
     $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
-    drupal_static_reset('language_list');
 
     $edit = array(
       'site_default_language' => 'ar',
@@ -84,7 +83,6 @@ function testContentTypeLanguageConfiguration() {
       'direction' => '0',
     );
     $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language'));
-    drupal_static_reset('language_list');
 
     // Set the content type to use multilingual support.
     $this->drupalGet("admin/structure/types/manage/{$type2->type}");
@@ -95,7 +93,7 @@ function testContentTypeLanguageConfiguration() {
     $this->drupalPostForm("admin/structure/types/manage/{$type2->type}", $edit, t('Save content type'));
     $this->assertRaw(t('The content type %type has been updated.', array('%type' => $type2->name)));
     $this->drupalLogout();
-    drupal_static_reset('language_list');
+    \Drupal::languageManager()->reset();
 
     // Verify language selection is not present on the node add form.
     $this->drupalLogin($web_user);
@@ -152,13 +150,12 @@ function testContentTypeDirLang() {
     $edit = array();
     $edit['predefined_langcode'] = 'ar';
     $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
-    drupal_static_reset('language_list');
+    \Drupal::languageManager()->reset();
 
     // Install Spanish language.
     $edit = array();
     $edit['predefined_langcode'] = 'es';
     $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
-    drupal_static_reset('language_list');
 
     // Set the content type to use multilingual support.
     $this->drupalGet("admin/structure/types/manage/{$type->type}");
@@ -222,7 +219,6 @@ function testNodeAdminLanguageFilter() {
     // Enable multiple languages.
     $this->drupalPostForm('admin/config/regional/language/edit/en', array('locale_translate_english' => TRUE), t('Save language'));
     $this->drupalPostForm('admin/config/regional/language/add', array('predefined_langcode' => 'zh-hant'), t('Add language'));
-    drupal_static_reset('language_list');
 
     // Create two nodes: English and Chinese.
     $node_en = $this->drupalCreateNode(array('langcode' => 'en'));
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationUiTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationUiTest.php
index 5c67d4ea449a8087a5cad9ef8b3b7158fae6edfa..91e4c8146cda3a9958751b07072af32dbd01399f 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationUiTest.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationUiTest.php
@@ -224,7 +224,7 @@ function testJavaScriptTranslation() {
       'direction' => '0',
     );
     $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language'));
-    drupal_static_reset('language_list');
+    $this->container->get('language_manager')->reset();
 
     // Build the JavaScript translation file.
 
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php
index cd8ec733c601ceed7bf508ac4ae314e079760c0f..989ac42907b4257f5bbfc34ae7a5bb131146d4cf 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php
@@ -7,9 +7,13 @@
 
 namespace Drupal\locale\Tests;
 
-use Drupal\simpletest\WebTestBase;
-use Drupal\Core\Language\Language;
 use Drupal\Component\Utility\String;
+use Drupal\Core\Language\Language;
+use Drupal\Core\Language\LanguageManager;
+use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationSelected;
+use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
+use Drupal\simpletest\WebTestBase;
+use Symfony\Component\DependencyInjection\Exception\InvalidArgumentException;
 
 /**
  * Locale uninstall with English UI functional test.
@@ -90,14 +94,18 @@ function testUninstallProcess() {
 
     // Change language negotiation options.
     drupal_load('module', 'locale');
-    \Drupal::config('system.language.types')->set('configurable', language_types_get_default() + array('language_custom' => TRUE))->save();
-    variable_set('language_negotiation_' . Language::TYPE_INTERFACE, language_language_negotiation_info());
-    variable_set('language_negotiation_' . Language::TYPE_CONTENT, language_language_negotiation_info());
-    variable_set('language_negotiation_' . Language::TYPE_URL, language_language_negotiation_info());
+    // Pick only core language types.
+    $language_manager = new LanguageManager();
+    $default_types = $language_manager->getLanguageTypes();
+    \Drupal::config('language.types')->set('configurable', $default_types + array('language_custom' => TRUE))->save();
+    $config = array_flip(array_keys(\Drupal::service('plugin.manager.language_negotiation_method')->getDefinitions()));
+    variable_set('language_negotiation_' . Language::TYPE_INTERFACE, $config);
+    variable_set('language_negotiation_' . Language::TYPE_CONTENT, $config);
+    variable_set('language_negotiation_' . Language::TYPE_URL, $config);
 
     // Change language negotiation settings.
     \Drupal::config('language.negotiation')
-      ->set('url.source', LANGUAGE_NEGOTIATION_URL_PREFIX)
+      ->set('url.source', LanguageNegotiationUrl::CONFIG_PATH_PREFIX)
       ->set('session.parameter', TRUE)
       ->save();
 
@@ -113,19 +121,16 @@ function testUninstallProcess() {
     // Check JavaScript files deletion.
     $this->assertTrue($result = !file_exists($js_file), String::format('JavaScript file deleted: %file', array('%file' => $result ? $js_file : 'found')));
 
-    // Check language count.
-    $language_count = $this->container->get('state')->get('language_count') ?: 1;
-    $this->assertEqual($language_count, 1, String::format('Language count: %count', array('%count' => $language_count)));
-
     // Check language negotiation.
-    require_once DRUPAL_ROOT . '/core/includes/language.inc';
-    $this->assertTrue(count(language_types_get_all()) == count(language_types_get_default()), 'Language types reset');
-    $language_negotiation = language_negotiation_method_get_first(Language::TYPE_INTERFACE) == LANGUAGE_NEGOTIATION_SELECTED;
-    $this->assertTrue($language_negotiation, String::format('Interface language negotiation: %setting', array('%setting' => $language_negotiation ? 'none' : 'set')));
-    $language_negotiation = language_negotiation_method_get_first(Language::TYPE_CONTENT) == LANGUAGE_NEGOTIATION_SELECTED;
-    $this->assertTrue($language_negotiation, String::format('Content language negotiation: %setting', array('%setting' => $language_negotiation ? 'none' : 'set')));
-    $language_negotiation = language_negotiation_method_get_first(Language::TYPE_URL) == LANGUAGE_NEGOTIATION_SELECTED;
-    $this->assertTrue($language_negotiation, String::format('URL language negotiation: %setting', array('%setting' => $language_negotiation ? 'none' : 'set')));
+    try {
+      $message = 'Language negotiation is not available.';
+      $this->assertTrue(count($this->container->get('language_manager')->getLanguageTypes()) == count($default_types), 'Language types reset');
+      \Drupal::service('language_negotiator');
+      $this->fail($message);
+    }
+    catch (InvalidArgumentException $e) {
+      $this->pass($message);
+    }
 
     // Check language negotiation method settings.
     $this->assertFalse(\Drupal::config('language.negotiation')->get('url.source'), 'URL language negotiation method indicator settings cleared.');
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateBase.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateBase.php
index 64e933319812d8d5a7147685dd65b3f36adf67ac..8ae5a8ed18a3d56c1b4e10f6c78d779c270e8a5d 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateBase.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateBase.php
@@ -80,7 +80,7 @@ protected function setTranslationsDirectory($path) {
   protected function addLanguage($langcode) {
     $edit = array('predefined_langcode' => $langcode);
     $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
-    drupal_static_reset('language_list');
+    $this->container->get('language_manager')->reset();
     $this->assertTrue(language_load($langcode), String::format('Language %langcode added.', array('%langcode' => $langcode)));
   }
 
diff --git a/core/modules/locale/locale.bulk.inc b/core/modules/locale/locale.bulk.inc
index 525624432988c5080f0fe8b07adc0384b1b3a40c..eb0dfe495ce717c4d4002ee4f4eab1d7c5bb30f6 100644
--- a/core/modules/locale/locale.bulk.inc
+++ b/core/modules/locale/locale.bulk.inc
@@ -21,7 +21,7 @@
  * @deprecated Use \Drupal\locale\Form\LocaleForm::import()
  */
 function locale_translate_import_form($form, &$form_state) {
-  drupal_static_reset('language_list');
+  Drupal::languageManager()->reset();
   $languages = language_list();
 
   // Initialize a language list to the ones available, including English if we
diff --git a/core/modules/locale/locale.install b/core/modules/locale/locale.install
index 960381c86700e0039430e788eb876ae2ed2ee6e1..708fe6a7704ed76a094588e71c0e016db8ee266d 100644
--- a/core/modules/locale/locale.install
+++ b/core/modules/locale/locale.install
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Core\Language\Language;
+use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationSelected;
 
 /**
  * Implements hook_install().
@@ -500,20 +501,10 @@ function locale_update_8004() {
   $types = update_variable_get('language_types', NULL);
   if (!empty($types)) {
     foreach ($types as $type => $configurable) {
-      // Rename the negotiation and language switch callback keys.
+      // Change the structure of the language negotiation configuration.
       $negotiation = update_variable_get('language_negotiation_' . $type, NULL);
       if (!empty($negotiation)) {
-        foreach ($negotiation as &$method) {
-          if (isset($method['callbacks']['language'])) {
-            $method['callbacks']['negotiation'] = $method['callbacks']['language'];
-            unset($method['callbacks']['language']);
-          }
-          if (isset($method['callbacks']['switcher'])) {
-            $method['callbacks']['language_switch'] = $method['callbacks']['switcher'];
-            unset($method['callbacks']['switcher']);
-          }
-        }
-        update_variable_set('language_negotiation_' . $type, $negotiation);
+        update_variable_set('language_negotiation_' . $type, array_keys($negotiation));
       }
 
       // Rename the language negotiation methods weight variable.
@@ -646,16 +637,11 @@ function locale_update_8005() {
 }
 
 /**
- * Convert language_negotiation_* variables to use the new callbacks.
+ * Convert language_negotiation_* variables.
  *
  * @ingroup config_upgrade
  */
 function locale_update_8007() {
-  $variable_names = array(
-    'language_negotiation_language_interface',
-    'language_negotiation_language_content',
-    'language_negotiation_language_url',
-  );
   // Add all language type weight variables. As the function language_types()
   // is not available its functionality is rebuild.
   $language_types = update_variable_get('language_types', array(
@@ -664,20 +650,9 @@ function locale_update_8007() {
     Language::TYPE_URL => FALSE,
   ));
   foreach ($language_types as $language_type => $configurable) {
-    $variable_names[] = 'language_negotiation_methods_weight_' . $language_type;
+    $variable_names['language_negotiation_' . $language_type] = 'language_negotiation_methods_weight_' . $language_type;
   }
-  $callback_map = array(
-    'locale_language_from_url' => 'language_from_url',
-    'locale_language_switcher_url' =>  'language_switcher_url',
-    'locale_language_url_rewrite_url' =>  'language_url_rewrite_url',
-    'locale_language_from_session' =>  'language_from_session',
-    'locale_language_switcher_session' =>  'language_switcher_session',
-    'locale_language_url_rewrite_session' => 'language_url_rewrite_session',
-    'locale_language_from_user' => 'language_from_user',
-    'locale_language_from_browser' => 'language_from_browser',
-    'locale_language_url_fallback' => 'language_url_fallback',
-    'locale_language_from_interface' => 'language_from_interface',
-  );
+
   $type_map = array(
     'locale-interface' => 'language-interface',
     'locale-url' => 'language-url',
@@ -686,37 +661,24 @@ function locale_update_8007() {
     'locale-user' => 'language-user',
     'locale-session' => 'language-session',
   );
-  foreach ($variable_names as $variable_name) {
-    $value = update_variable_get($variable_name);
+  foreach ($variable_names as $variable_name => $weight_variable_name) {
+    $value = update_variable_get($weight_variable_name);
     // Skip processing if the variable is not stored in the db.
     if ($value === NULL) {
+      update_variable_del($variable_name);
       continue;
     }
-    $new_value = $value;
-    foreach ($value as $type => $type_settings) {
-      // Convert the file.
-      if (isset($type_settings['file']) && (strpos($type_settings['file'], 'core/includes/locale.inc') !== FALSE)) {
-        $new_value[$type]['file'] = 'core/modules/language/language.negotiation.inc';
-      }
-      // Convert the callbacks.
-      if (is_array($type_settings) && isset($type_settings['callbacks'])) {
-        foreach ($type_settings['callbacks'] as $key => $callback) {
-          if (isset($callback_map[$callback])) {
-            $new_value[$type]['callbacks'][$key] = $callback_map[$callback];
-          }
-        }
-      }
+    $new_value = array();
+    foreach ($value as $type => $weight) {
       // Convert the type.
       if (isset($type_map[$type])) {
-        $new_value[$type_map[$type]] = $new_value[$type];
-        unset($new_value[$type]);
+        $type = $type_map[$type];
       }
+      $new_value[$type] = $weight;
     }
-    // If necessary maintain the order of the values / keys of the variable.
-    if (stristr($variable_name, 'language_negotiation_methods_weight_') !== FALSE) {
-      asort($new_value);
-    }
+    asort($new_value);
     update_variable_set($variable_name, $new_value);
+    update_variable_del($weight_variable_name);
   }
 }
 
@@ -845,25 +807,11 @@ function locale_update_8011() {
  * Renames language_default language negotiation method to language_selected.
  */
 function locale_update_8013() {
-  // @todo We only need language.inc here because LANGUAGE_NEGOTIATION_SELECTED
-  //   is defined there. Remove this line once that has been converted to a class
-  //   constant.
-  require_once DRUPAL_ROOT . '/core/includes/language.inc';
-  $weight = update_variable_get('language_negotiation_methods_weight_language_interface', NULL);
+  $weight = update_variable_get('language_negotiation_language_interface', NULL);
   if ($weight !== NULL) {
-    $weight[LANGUAGE_NEGOTIATION_SELECTED] = $weight['language-default'];
+    $weight[LanguageNegotiationSelected::METHOD_ID] = $weight['language-default'];
     unset($weight['language-default']);
-    update_variable_set('language_negotiation_methods_weight_language_interface', $weight);
-  }
-
-  $negotiation_interface = update_variable_get('language_negotiation_language_interface', NULL);
-  if ($negotiation_interface !== NULL) {
-    if (isset($negotiation_interface['language-default'])) {
-      $negotiation_interface[LANGUAGE_NEGOTIATION_SELECTED] = $negotiation_interface['language-default'];
-      $negotiation_interface[LANGUAGE_NEGOTIATION_SELECTED]['callbacks']['negotiation'] = 'language_from_selected';
-      unset($negotiation_interface['language-default']);
-      update_variable_set('language_negotiation_language_interface', $negotiation_interface);
-    }
+    update_variable_set('language_negotiation_language_interface', $weight);
   }
 }
 
diff --git a/core/modules/node/lib/Drupal/node/Controller/NodeController.php b/core/modules/node/lib/Drupal/node/Controller/NodeController.php
index a60696ccbb150851e39a6fff7010b99b34669072..126332f8302d763ecd6c2f7e6b04284b8ad59c09 100644
--- a/core/modules/node/lib/Drupal/node/Controller/NodeController.php
+++ b/core/modules/node/lib/Drupal/node/Controller/NodeController.php
@@ -69,7 +69,7 @@ public function add(NodeTypeInterface $node_type) {
       'uid' => $account->id(),
       'name' => $account->getUsername() ?: '',
       'type' => $node_type->type,
-      'langcode' => $langcode ? $langcode : $this->languageManager()->getLanguage()->id,
+      'langcode' => $langcode ? $langcode : $this->languageManager()->getCurrentLanguage()->id,
     ));
 
     $form = $this->entityManager()->getForm($node);
diff --git a/core/modules/node/lib/Drupal/node/Entity/Node.php b/core/modules/node/lib/Drupal/node/Entity/Node.php
index c22dbeda5995abbcb7f95a4f1304ab4dbaa776b0..90d4fac4b9ae5dff4572fb927f32d57ca9050e6f 100644
--- a/core/modules/node/lib/Drupal/node/Entity/Node.php
+++ b/core/modules/node/lib/Drupal/node/Entity/Node.php
@@ -199,7 +199,7 @@ public function prepareLangcode() {
       // Load languages the node exists in.
       $node_translations = $this->getTranslationLanguages();
       // Load the language from content negotiation.
-      $content_negotiation_langcode = \Drupal::languageManager()->getLanguage(Language::TYPE_CONTENT)->id;
+      $content_negotiation_langcode = \Drupal::languageManager()->getCurrentLanguage(Language::TYPE_CONTENT)->id;
       // If there is a translation available, use it.
       if (isset($node_translations[$content_negotiation_langcode])) {
         $langcode = $content_negotiation_langcode;
diff --git a/core/modules/node/lib/Drupal/node/NodeGrantDatabaseStorage.php b/core/modules/node/lib/Drupal/node/NodeGrantDatabaseStorage.php
index 9efe421941c6e497139e475595fb6004760b9104..c9b54c50c9678ae293ed5e87928312ea6da711ac 100644
--- a/core/modules/node/lib/Drupal/node/NodeGrantDatabaseStorage.php
+++ b/core/modules/node/lib/Drupal/node/NodeGrantDatabaseStorage.php
@@ -159,7 +159,7 @@ public function alterQuery($query, array $tables, $op, AccountInterface $account
         $subquery->condition('na.grant_' . $op, 1, '>=');
 
         // Add langcode-based filtering if this is a multilingual site.
-        if (language_multilingual()) {
+        if (\Drupal::languageManager()->isMultilingual()) {
           // If no specific langcode to check for is given, use the grant entry
           // which is set as a fallback.
           // If a specific langcode is given, use the grant entry for it.
diff --git a/core/modules/node/lib/Drupal/node/NodeListController.php b/core/modules/node/lib/Drupal/node/NodeListController.php
index 8d488ca015f038da446fe43c59ac4abe918fab25..b9582ae05e4533dfc3560da170188892c1f14c83 100644
--- a/core/modules/node/lib/Drupal/node/NodeListController.php
+++ b/core/modules/node/lib/Drupal/node/NodeListController.php
@@ -81,7 +81,7 @@ public function buildHeader() {
         'class' => array(RESPONSIVE_PRIORITY_LOW),
       ),
     );
-    if (language_multilingual()) {
+    if (\Drupal::languageManager()->isMultilingual()) {
       $header['language_name'] = array(
         'data' => $this->t('Language'),
         'class' => array(RESPONSIVE_PRIORITY_LOW),
@@ -114,8 +114,9 @@ public function buildRow(EntityInterface $entity) {
     );
     $row['status'] = $entity->isPublished() ? $this->t('published') : $this->t('not published');
     $row['changed'] = $this->dateService->format($entity->getChangedTime(), 'short');
-    if (language_multilingual()) {
-      $row['language_name'] = language_name($langcode);
+    $language_manager = \Drupal::languageManager();
+    if ($language_manager->isMultilingual()) {
+      $row['language_name'] = $language_manager->getLanguageName($langcode);
     }
     $row['operations']['data'] = $this->buildOperations($entity);
     return $row + parent::buildRow($entity);
diff --git a/core/modules/node/lib/Drupal/node/NodeViewBuilder.php b/core/modules/node/lib/Drupal/node/NodeViewBuilder.php
index eda9b4c81261eac4ae4dc2b77766b1888c839c40..fcaeaede7e98d018b42489e400ef4e9c799deed4 100644
--- a/core/modules/node/lib/Drupal/node/NodeViewBuilder.php
+++ b/core/modules/node/lib/Drupal/node/NodeViewBuilder.php
@@ -50,7 +50,7 @@ public function buildContent(array $entities, array $displays, $view_mode, $lang
         $entity->content['langcode'] = array(
           '#type' => 'item',
           '#title' => t('Language'),
-          '#markup' => language_name($langcode),
+          '#markup' => $this->languageManager->getLanguageName($langcode),
           '#prefix' => '<div id="field-language-display">',
           '#suffix' => '</div>'
         );
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeFieldMultilingualTestCase.php b/core/modules/node/lib/Drupal/node/Tests/NodeFieldMultilingualTestCase.php
index f53518225caf6056f415a58f0c1d77cea99bed86..67b34e7c4f73254488a5e58003f6e727ede71513 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeFieldMultilingualTestCase.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeFieldMultilingualTestCase.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\node\Tests;
 
+use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
 use Drupal\simpletest\WebTestBase;
 use Drupal\Core\Language\Language;
 
@@ -99,7 +100,7 @@ function testMultilingualNodeForm() {
     $this->assertTrue($node->language()->id == $langcode && $node->body->value == $body_value, 'Field language correctly changed.');
 
     // Enable content language URL detection.
-    language_negotiation_set(Language::TYPE_CONTENT, array(LANGUAGE_NEGOTIATION_URL => 0));
+    $this->container->get('language_negotiator')->saveConfiguration(Language::TYPE_CONTENT, array(LanguageNegotiationUrl::METHOD_ID => 0));
 
     // Test multilingual field language fallback logic.
     $this->drupalGet("it/node/{$node->id()}");
diff --git a/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php b/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php
index 4f94f43cceb81bfc7c5e4c25b603e59871693f7d..065374316bf32f1195b3848454499ca7bebb4cd5 100644
--- a/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php
+++ b/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php
@@ -104,7 +104,7 @@ function testAliasTranslation() {
     $this->container->get('path.alias_manager')->cacheClear();
 
     // Languages are cached on many levels, and we need to clear those caches.
-    drupal_static_reset('language_list');
+    $this->container->get('language_manager')->reset();
     $this->rebuildContainer();
     $languages = language_list();
 
@@ -117,7 +117,10 @@ function testAliasTranslation() {
     $this->drupalGet('fr/' . $edit['path[alias]']);
     $this->assertText($french_node->body->value, 'Alias for French translation works.');
 
-    // Confirm that the alias is returned by url().
+    // Confirm that the alias is returned by url(). Languages are cached on
+    // many levels, and we need to clear those caches.
+    $this->container->get('language_manager')->reset();
+    $languages = language_list();
     $url = $this->container->get('url_generator')->generateFromPath('node/' . $french_node->id(), array('language' => $languages['fr']));
 
     $this->assertTrue(strpos($url, $edit['path[alias]']), 'URL contains the path alias.');
diff --git a/core/modules/path/path.admin.inc b/core/modules/path/path.admin.inc
index b2941a0328c5d424275ef8dcf403190699c1c91e..6574208051687bdd7989a238d7aa8025948b19cb 100644
--- a/core/modules/path/path.admin.inc
+++ b/core/modules/path/path.admin.inc
@@ -57,7 +57,7 @@ function path_admin_overview($keys = NULL) {
       'attributes' => array('title' => $data->source),
     ));
     if ($multilanguage) {
-      $row['data']['language_name'] = language_name($data->langcode);
+      $row['data']['language_name'] = \Drupal::languageManager()->getLanguageName($data->langcode);
     }
 
     $operations = array();
diff --git a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
index 560afb1ec159cf87964751730a83080b1b5c099c..669c6150b819f1c637d04f04f140b2bd022aad9c 100644
--- a/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
+++ b/core/modules/simpletest/lib/Drupal/simpletest/TestBase.php
@@ -20,6 +20,7 @@
 use Drupal\Core\StreamWrapper\PublicStream;
 use Drupal\Core\Utility\Error;
 use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\DependencyInjection\Reference;
 
 /**
  * Base class for Drupal tests.
@@ -969,8 +970,11 @@ protected function prepareEnvironment() {
 
     // Reset and create a new service container.
     $this->container = new ContainerBuilder();
-     // @todo Remove this once this class has no calls to t() and format_plural()
-    $this->container->register('string_translation', 'Drupal\Core\StringTranslation\TranslationManager');
+
+    // @todo Remove this once this class has no calls to t() and format_plural()
+    $this->container->register('language_manager', 'Drupal\Core\Language\LanguageManager');
+    $this->container->register('string_translation', 'Drupal\Core\StringTranslation\TranslationManager')
+      ->addArgument(new Reference('language_manager'));
 
     // Register info parser.
     $this->container->register('info_parser', 'Drupal\Core\Extension\InfoParser');
diff --git a/core/modules/system/config/schema/system.schema.yml b/core/modules/system/config/schema/system.schema.yml
index 9d585f08da9429745daeba12eb500bc7718d930b..9acf83dbc2673887dccde27c4016102f6eb08d1f 100644
--- a/core/modules/system/config/schema/system.schema.yml
+++ b/core/modules/system/config/schema/system.schema.yml
@@ -175,23 +175,6 @@ system.filter:
         - type: string
           label: 'Protocol'
 
-system.language.types:
-  type: mapping
-  label: 'Language types'
-  mapping:
-    all:
-      type: sequence
-      label: 'All language types'
-      sequence:
-        - type: string
-          label: 'Language type'
-    configurable:
-      type: sequence
-      label: 'Configurable language types'
-      sequence:
-        - type: string
-          label: 'Language type'
-
 system.logging:
   type: mapping
   label: 'Logging settings'
diff --git a/core/modules/system/language.api.php b/core/modules/system/language.api.php
index baafb7ae7ab6b220c24058d472dcdafd6691cdeb..8684406741efb2e75473c7e88090da2a38ec2dd9 100644
--- a/core/modules/system/language.api.php
+++ b/core/modules/system/language.api.php
@@ -34,123 +34,6 @@ function hook_language_switch_links_alter(array &$links, $type, $path) {
   }
 }
 
-/**
- * Define language types.
- *
- * @return
- *   An associative array of language type definitions. The keys are the
- *   identifiers, which are also used as names for global variables representing
- *   the types in the bootstrap phase. The values are associative arrays that
- *   may contain the following elements:
- *   - name: The human-readable language type identifier.
- *   - description: A description of the language type.
- *   - locked: A boolean indicating if the user can choose wether to configure
- *     the language type or not using the UI.
- *   - fixed: A fixed array of language negotiation method identifiers to use to
- *     initialize this language. If locked is set to TRUE and fixed is set, it
- *     will always use the specified methods in the given priority order. If not
- *     present and locked is TRUE then LANGUAGE_NEGOTIATION_INTERFACE will be
- *     used.
- *
- *  @todo Rename the 'fixed' key to something more meaningful, for instance
- *     'negotiation settings'.
- *
- * @see hook_language_types_info_alter()
- * @ingroup language_negotiation
- */
-function hook_language_types_info() {
-  return array(
-    'custom_language_type' => array(
-      'name' => t('Custom language'),
-      'description' => t('A custom language type.'),
-      'locked' => FALSE,
-    ),
-    'fixed_custom_language_type' => array(
-      'locked' => TRUE,
-      'fixed' => array('custom_language_negotiation_method'),
-    ),
-  );
-}
-
-/**
- * Perform alterations on language types.
- *
- * @param $language_types
- *   Array of language type definitions.
- *
- * @see hook_language_types_info()
- * @ingroup language_negotiation
- */
-function hook_language_types_info_alter(array &$language_types) {
-  if (isset($language_types['custom_language_type'])) {
-    $language_types['custom_language_type_custom']['description'] = t('A far better description.');
-  }
-}
-
-/**
- * Define language negotiation methods.
- *
- * @return
- *   An associative array of language negotiation method definitions. The keys
- *   are method identifiers, and the values are associative arrays defining
- *   each method, with the following elements:
- *   - types: An array of allowed language types. If a language negotiation
- *     method does not specify which language types it should be used with, it
- *     will be available for all the configurable language types.
- *   - callbacks: An associative array of functions that will be called to
- *     perform various tasks. Possible elements are:
- *     - negotiation: (required) Name of the callback function that determines
- *       the language value.
- *     - language_switch: (optional) Name of the callback function that
- *       determines links for a language switcher block associated with this
- *       method. See language_switcher_url() for an example.
- *     - url_rewrite: (optional) Name of the callback function that provides URL
- *       rewriting, if needed by this method.
- *   - file: The file where callback functions are defined (this file will be
- *     included before the callbacks are invoked).
- *   - weight: The default weight of the method.
- *   - name: The translated human-readable name for the method.
- *   - description: A translated longer description of the method.
- *   - config: An internal path pointing to the method's configuration page.
- *   - cache: The value Drupal's page cache should be set to for the current
- *     method to be invoked.
- *
- * @see hook_language_negotiation_info_alter()
- * @ingroup language_negotiation
- */
-function hook_language_negotiation_info() {
-  return array(
-    'custom_language_negotiation_method' => array(
-      'callbacks' => array(
-        'negotiation' => 'custom_negotiation_callback',
-        'language_switch' => 'custom_language_switch_callback',
-        'url_rewrite' => 'custom_url_rewrite_callback',
-      ),
-      'file' => drupal_get_path('module', 'custom') . '/custom.module',
-      'weight' => -4,
-      'types' => array('custom_language_type'),
-      'name' => t('Custom language negotiation method'),
-      'description' => t('This is a custom language negotiation method.'),
-      'cache' => 0,
-    ),
-  );
-}
-
-/**
- * Perform alterations on language negotiation methods.
- *
- * @param $negotiation_info
- *   Array of language negotiation method definitions.
- *
- * @see hook_language_negotiation_info()
- * @ingroup language_negotiation
- */
-function hook_language_negotiation_info_alter(array &$negotiation_info) {
-  if (isset($negotiation_info['custom_language_method'])) {
-    $negotiation_info['custom_language_method']['config'] = 'admin/config/regional/language/detection/custom-language-method';
-  }
-}
-
 /**
  * @} End of "addtogroup hooks".
  */
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php
index 373c90be95a6c5e804b24a139aa8b0d9de48c226..663ef4c4cdefd9dad7d80002081e947d08d42991 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityAccessTest.php
@@ -15,9 +15,7 @@
 /**
  * Tests the entity access controller.
  */
-class EntityAccessTest extends EntityUnitTestBase  {
-
-  public static $modules = array('language', 'locale');
+class EntityAccessTest extends EntityLanguageTestBase  {
 
   public static function getInfo() {
     return array(
@@ -29,16 +27,7 @@ public static function getInfo() {
 
   function setUp() {
     parent::setUp();
-    $this->installSchema('system', array('variable', 'url_alias'));
-    $this->installConfig(array('language'));
-
-    // Create the default languages.
-    $default_language = language_save(language_default());
-    $languages = language_default_locked_languages($default_language->weight);
-    foreach ($languages as $language) {
-      language_save($language);
-    }
-
+    $this->installSchema('system', 'url_alias');
   }
 
   /**
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php
index dbf4f9cf0666bccb5ec8233056293d459d105365..ee1c0d02ffea8954fe2864650f18b02906a23ff8 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityLanguageTestBase.php
@@ -15,6 +15,18 @@
  */
 abstract class EntityLanguageTestBase extends EntityUnitTestBase {
 
+  /**
+   * The language manager service.
+   *
+   * @var \Drupal\Core\Language\LanguageManagerInterface
+   */
+  protected $languageManager;
+
+  /**
+   * The available language codes.
+   *
+   * @var array
+   */
   protected $langcodes;
 
   /**
@@ -36,6 +48,8 @@ abstract class EntityLanguageTestBase extends EntityUnitTestBase {
   function setUp() {
     parent::setUp();
 
+    $this->languageManager = $this->container->get('language_manager');
+
     $this->installSchema('system', 'variable');
     $this->installSchema('entity_test', array(
       'entity_test_mul',
@@ -92,8 +106,8 @@ function setUp() {
     }
 
     // Create the default languages.
-    $default_language = language_save(language_default());
-    $languages = language_default_locked_languages($default_language->weight);
+    $default_language = language_save($this->languageManager->getDefaultLanguage());
+    $languages = $this->languageManager->getDefaultLockedLanguages($default_language->weight);
     foreach ($languages as $language) {
       language_save($language);
     }
diff --git a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php
index a084ebf9afdb1ac15f1e34c86c4559c08d7c7a71..eb234b4166f16c38875195bb89b002c0e2721e2b 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Entity/EntityTranslationTest.php
@@ -446,7 +446,7 @@ function testEntityTranslationAPI() {
    * Tests language fallback applied to field and entity translations.
    */
   function testLanguageFallback() {
-    $current_langcode = $this->container->get('language_manager')->getLanguage(Language::TYPE_CONTENT)->id;
+    $current_langcode = $this->languageManager->getCurrentLanguage(Language::TYPE_CONTENT)->id;
     $this->langcodes[] = $current_langcode;
 
     $values = array();
@@ -473,7 +473,7 @@ function testLanguageFallback() {
     $this->assertEqual($translation->language()->id, $default_langcode, 'The current translation language matches the expected one.');
 
     // Check that language fallback respects language weight by default.
-    $languages = language_list();
+    $languages = $this->languageManager->getLanguages();
     $languages[$langcode]->weight = -1;
     language_save($languages[$langcode]);
     $translation = $this->entityManager->getTranslationFromContext($entity, $langcode2);
diff --git a/core/modules/system/lib/Drupal/system/Tests/Form/LanguageSelectElementTest.php b/core/modules/system/lib/Drupal/system/Tests/Form/LanguageSelectElementTest.php
index dcb700bdb7a40b63c0e25479c0514101856e6738..f6a59f9e80b84c49db88cf86aca1311602f47208 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Form/LanguageSelectElementTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Form/LanguageSelectElementTest.php
@@ -47,6 +47,8 @@ function testLanguageSelectElementOptions() {
     ));
     language_save($language);
 
+    \Drupal::languageManager()->reset();
+
     $this->drupalGet('form-test/language_select');
     // Check that the language fields were rendered on the page.
     $ids = array('edit-languages-all' => Language::STATE_ALL,
diff --git a/core/modules/system/lib/Drupal/system/Tests/Plugin/CacheDecoratorLanguageTest.php b/core/modules/system/lib/Drupal/system/Tests/Plugin/CacheDecoratorLanguageTest.php
index 10de2a8accc056e3cbf67f3900b98f42695d3c7f..2e734ef601a8202cb288011c5a12652b95f4b5e7 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Plugin/CacheDecoratorLanguageTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Plugin/CacheDecoratorLanguageTest.php
@@ -69,6 +69,7 @@ public function setUp() {
         $custom_strings[$definition['label']] = $langcode . ' ' . $definition['label'];
       }
       $this->addCustomTranslations($langcode, array('' => $custom_strings));
+      $this->rebuildContainer();
     }
     // Write test settings.php with new translations.
     $this->writeCustomTranslations();
diff --git a/core/modules/system/lib/Drupal/system/Tests/Theme/TwigTransTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/TwigTransTest.php
index 48d1e1f02e311ada2a2171dcdf5610b1668b53e4..8ce7d1a6fdde5bd630b9aa49447a32e1520088b6 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Theme/TwigTransTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Theme/TwigTransTest.php
@@ -84,7 +84,7 @@ protected function setUp() {
     $this->drupalPostForm('admin/config/regional/settings', $edit, t('Save configuration'));
 
     // Reset the static cache of the language list.
-    drupal_static_reset('language_list');
+    $this->container->get('language_manager')->reset();
 
     // Check that lolspeak is the default language for the site.
     $this->assertEqual(language_default()->id, 'xx', 'Lolspeak is the default language');
@@ -251,6 +251,7 @@ protected function installLanguages() {
         drupal_unlink($filename);
       }
     }
+    $this->container->get('language_manager')->reset();
   }
 
   /**
diff --git a/core/modules/system/system.install b/core/modules/system/system.install
index 4fd03afa41b083991a2d36c1ea4a057e16f8b09d..7d121af6230ecfeccb2a6374f7cf63f2a923774f 100644
--- a/core/modules/system/system.install
+++ b/core/modules/system/system.install
@@ -2252,7 +2252,7 @@ function system_update_8058() {
  * @ingroup config_upgrade
  */
 function system_update_8059() {
-  update_variables_to_config('system.language.types', array(
+  update_variables_to_config('language.types', array(
     'language_interface' => 'configurable.language_interface',
     'language_content' => 'configurable.language_content',
     'language_url' => 'configurable.language_content',
diff --git a/core/modules/toolbar/toolbar.module b/core/modules/toolbar/toolbar.module
index 082e050268c665b99e775bc6e39ec6a06bf08658..096c4894634e69d706072955ed8317e7ba6618af 100644
--- a/core/modules/toolbar/toolbar.module
+++ b/core/modules/toolbar/toolbar.module
@@ -637,7 +637,7 @@ function toolbar_user_role_update(RoleInterface $role) {
  *   A unique cache ID for the user.
  */
 function _toolbar_get_user_cid($uid) {
-  return 'toolbar_' . $uid . ':' . \Drupal::languageManager()->getLanguage(Language::TYPE_INTERFACE)->id;
+  return 'toolbar_' . $uid . ':' . \Drupal::languageManager()->getCurrentLanguage()->id;
 }
 
 /**
diff --git a/core/modules/user/lib/Drupal/user/AccountFormController.php b/core/modules/user/lib/Drupal/user/AccountFormController.php
index 4983b0f0bedda8936ce76fc6c7c4e7d52691a462..e67520b0d272c8660052d7320d8e307d41f79a99 100644
--- a/core/modules/user/lib/Drupal/user/AccountFormController.php
+++ b/core/modules/user/lib/Drupal/user/AccountFormController.php
@@ -10,7 +10,10 @@
 use Drupal\Core\Entity\ContentEntityFormController;
 use Drupal\Core\Entity\EntityManagerInterface;
 use Drupal\Core\Language\Language;
-use Drupal\Core\Language\LanguageManager;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\language\ConfigurableLanguageManagerInterface;
+use Drupal\user\Plugin\LanguageNegotiation\LanguageNegotiationUserAdmin;
+use Drupal\user\Plugin\LanguageNegotiation\LanguageNegotiationUser;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 
 /**
@@ -21,7 +24,7 @@ abstract class AccountFormController extends ContentEntityFormController {
   /**
    * The language manager.
    *
-   * @var \Drupal\Core\Language\LanguageManager
+   * @var \Drupal\Core\Language\LanguageManagerInterface
    */
   protected $languageManager;
 
@@ -30,10 +33,10 @@ abstract class AccountFormController extends ContentEntityFormController {
    *
    * @param \Drupal\Core\Entity\EntityManagerInterface $entity_manager
    *   The entity manager.
-   * @param \Drupal\Core\Language\LanguageManager $language_manager
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
    *   The language manager.
    */
-  public function __construct(EntityManagerInterface $entity_manager, LanguageManager $language_manager) {
+  public function __construct(EntityManagerInterface $entity_manager, LanguageManagerInterface $language_manager) {
     parent::__construct($entity_manager);
     $this->languageManager = $language_manager;
   }
@@ -214,9 +217,12 @@ public function form(array $form, array &$form_state) {
 
     $user_preferred_admin_langcode = $register ? $language_interface->id : $account->getPreferredAdminLangcode();
 
-    // Is default the interface language?
-    include_once DRUPAL_ROOT . '/core/includes/language.inc';
-    $interface_language_is_default = language_negotiation_method_get_first(Language::TYPE_INTERFACE) != LANGUAGE_NEGOTIATION_SELECTED;
+    // Is the user preferred language enabled?
+    $user_language_enabled = FALSE;
+    if ($this->languageManager instanceof ConfigurableLanguageManagerInterface) {
+      $negotiator = $this->languageManager->getNegotiator();
+      $user_language_enabled = $negotiator && $negotiator->isNegotiationMethodEnabled(LanguageNegotiationUser::METHOD_ID, Language::TYPE_INTERFACE);
+    }
     $form['language'] = array(
       '#type' => $this->languageManager->isMultilingual() ? 'details' : 'container',
       '#title' => $this->t('Language settings'),
@@ -230,26 +236,22 @@ public function form(array $form, array &$form_state) {
       '#title' => $this->t('Site language'),
       '#languages' => Language::STATE_CONFIGURABLE,
       '#default_value' => $user_preferred_langcode,
-      '#description' => $interface_language_is_default ? $this->t("This account's preferred language for e-mails and site presentation.") : $this->t("This account's preferred language for e-mails."),
+      '#description' => $user_language_enabled ? $this->t("This account's preferred language for e-mails and site presentation.") : $this->t("This account's preferred language for e-mails."),
     );
 
     // Only show the account setting for Administration pages language to users
     // if one of the detection and selection methods uses it.
     $show_admin_language = FALSE;
-    if ($this->moduleHandler->moduleExists('language') && $this->languageManager->isMultilingual()) {
-      foreach (language_types_info() as $type_key => $language_type) {
-        $negotiation_settings = variable_get("language_negotiation_{$type_key}", array());
-        if ($show_admin_language = isset($negotiation_settings[LANGUAGE_NEGOTIATION_USER_ADMIN])) {
-          break;
-        }
-      }
+    if ($account->hasPermission('access administration pages') && $this->languageManager instanceof ConfigurableLanguageManagerInterface) {
+      $negotiator = $this->languageManager->getNegotiator();
+      $show_admin_language = $negotiator && $negotiator->isNegotiationMethodEnabled(LanguageNegotiationUserAdmin::METHOD_ID);
     }
     $form['language']['preferred_admin_langcode'] = array(
       '#type' => 'language_select',
       '#title' => $this->t('Administration pages language'),
       '#languages' => Language::STATE_CONFIGURABLE,
       '#default_value' => $user_preferred_admin_langcode,
-      '#access' => $show_admin_language && user_access('access administration pages', $account),
+      '#access' => $show_admin_language,
     );
     // User entities contain both a langcode property (for identifying the
     // language of the entity data) and a preferred_langcode property (see
diff --git a/core/modules/user/lib/Drupal/user/Form/UserPasswordForm.php b/core/modules/user/lib/Drupal/user/Form/UserPasswordForm.php
index 236b293eb028653155d9c39a631c7e542d3290e0..f7f7cfe9d9578b8e04b3014ec35bc5c8493c9bc2 100644
--- a/core/modules/user/lib/Drupal/user/Form/UserPasswordForm.php
+++ b/core/modules/user/lib/Drupal/user/Form/UserPasswordForm.php
@@ -128,7 +128,7 @@ public function validateForm(array &$form, array &$form_state) {
    * {@inheritdoc}
    */
   public function submitForm(array &$form, array &$form_state) {
-    $langcode = $this->languageManager->getLanguage(Language::TYPE_INTERFACE)->id;
+    $langcode = $this->languageManager->getCurrentLanguage()->id;
 
     $account = $form_state['values']['account'];
     // Mail one time login URL and instructions using current language.
diff --git a/core/modules/user/lib/Drupal/user/Plugin/LanguageNegotiation/LanguageNegotiationUser.php b/core/modules/user/lib/Drupal/user/Plugin/LanguageNegotiation/LanguageNegotiationUser.php
new file mode 100644
index 0000000000000000000000000000000000000000..f2c99fe5d2e81710f619ebcf27ffde7772136625
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/Plugin/LanguageNegotiation/LanguageNegotiationUser.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl.
+ */
+
+namespace Drupal\user\Plugin\LanguageNegotiation;
+
+use Drupal\language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class for identifying language from the user preferences.
+ *
+ * @Plugin(
+ *   id = \Drupal\user\Plugin\LanguageNegotiation\LanguageNegotiationUser::METHOD_ID,
+ *   weight = -4,
+ *   name = @Translation("User"),
+ *   description = @Translation("Follow the user's language preference.")
+ * )
+ */
+class LanguageNegotiationUser extends LanguageNegotiationMethodBase {
+
+  /**
+   * The language negotiation method id.
+   */
+  const METHOD_ID = 'language-user';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLangcode(Request $request = NULL) {
+    $langcode = NULL;
+
+    // User preference (only for authenticated users).
+    if ($this->languageManager && $this->currentUser->isAuthenticated()) {
+      $preferred_langcode = $this->currentUser->getPreferredLangcode();
+      $default_langcode = $this->languageManager->getDefaultLanguage()->id;
+      $languages = $this->languageManager->getLanguages();
+      if (!empty($preferred_langcode) && $preferred_langcode != $default_langcode && isset($languages[$preferred_langcode])) {
+        $langcode = $preferred_langcode;
+      }
+    }
+
+    // No language preference from the user.
+    return $langcode;
+  }
+
+}
diff --git a/core/modules/user/lib/Drupal/user/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php b/core/modules/user/lib/Drupal/user/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php
new file mode 100644
index 0000000000000000000000000000000000000000..c7a42eb87f6a6f8e3c4ae700ed92acd36f7d0511
--- /dev/null
+++ b/core/modules/user/lib/Drupal/user/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php
@@ -0,0 +1,69 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\user\Plugin\LanguageNegotiation\LanguageNegotiationUserAdmin.
+ */
+
+namespace Drupal\user\Plugin\LanguageNegotiation;
+
+use Drupal\language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Identifies admin language from the user preferences.
+ *
+ * @Plugin(
+ *   id = Drupal\user\Plugin\LanguageNegotiation\LanguageNegotiationUserAdmin::METHOD_ID,
+ *   types = {Drupal\Core\Language\Language::TYPE_INTERFACE},
+ *   weight = 10,
+ *   name = @Translation("Account administration pages"),
+ *   description = @Translation("Account administration pages language setting.")
+ * )
+ */
+class LanguageNegotiationUserAdmin extends LanguageNegotiationMethodBase {
+
+  /**
+   * The language negotiation method id.
+   */
+  const METHOD_ID = 'language-user-admin';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLangcode(Request $request = NULL) {
+    $langcode = NULL;
+
+    // User preference (only for authenticated users).
+    if ($this->languageManager && $this->currentUser->isAuthenticated() && $this->isAdminPath($request)) {
+      $preferred_admin_langcode = $this->currentUser->getPreferredAdminLangcode();
+      $default_langcode = $this->languageManager->getDefaultLanguage()->id;
+      $languages = $this->languageManager->getLanguages();
+      if (!empty($preferred_admin_langcode) && $preferred_admin_langcode != $default_langcode && isset($languages[$preferred_admin_langcode])) {
+        $langcode = $preferred_admin_langcode;
+      }
+    }
+
+    // No language preference from the user or not on an admin path.
+    return $langcode;
+  }
+
+  /**
+   * Checks whether the given path is an administrative one.
+   *
+   * @param string $path
+   *   A Drupal path.
+   *
+   * @return bool
+   *   TRUE if the path is administrative, FALSE otherwise.
+   */
+  public function isAdminPath(Request $request) {
+    $result = FALSE;
+    if ($request && function_exists('path_is_admin')) {
+      $path = urldecode(trim($request->getPathInfo(), '/'));
+      $result = path_is_admin($path);
+    }
+    return $result;
+  }
+
+}
diff --git a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
index 847da687ca6933f404282f9bcbaa8e0e856df9ef..83e226f2244e2c708fcf1e4bf51e1968c72a9c7e 100644
--- a/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
+++ b/core/modules/views/lib/Drupal/views/Plugin/views/display/DisplayPluginBase.php
@@ -157,7 +157,7 @@ public function initDisplay(ViewExecutable $view, array &$display, array &$optio
     $skip_cache = \Drupal::config('views.settings')->get('skip_cache');
 
     if (empty($view->editing) || !$skip_cache) {
-      $cid = 'unpackOptions:' . hash('sha256', serialize(array($this->options, $options))) . ':' . \Drupal::languageManager()->getLanguage()->id;
+      $cid = 'unpackOptions:' . hash('sha256', serialize(array($this->options, $options))) . ':' . \Drupal::languageManager()->getCurrentLanguage()->id;
       if (empty(static::$unpackOptions[$cid])) {
         $cache = \Drupal::cache('views_info')->get($cid);
         if (!empty($cache->data)) {
diff --git a/core/modules/views/lib/Drupal/views/ViewsData.php b/core/modules/views/lib/Drupal/views/ViewsData.php
index 988c3c800fc5332da8f246a13b1d445dd8508b8f..c297c0335a695a9db132ab64d0012a5a34879112 100644
--- a/core/modules/views/lib/Drupal/views/ViewsData.php
+++ b/core/modules/views/lib/Drupal/views/ViewsData.php
@@ -11,7 +11,7 @@
 use Drupal\Core\Config\ConfigFactory;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Language\Language;
-use Drupal\Core\Language\LanguageManager;
+use Drupal\Core\Language\LanguageManagerInterface;
 
 /**
  * Class to manage and lazy load cached views data.
@@ -75,7 +75,7 @@ class ViewsData {
   /**
    * The language manager
    *
-   * @var \Drupal\Core\Language\LanguageManager
+   * @var \Drupal\Core\Language\LanguageManagerInterface
    */
   protected $languageManager;
 
@@ -88,15 +88,15 @@ class ViewsData {
    *   The configuration factory object to use.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    *   The module handler class to use for invoking hooks.
-   * @param \Drupal\Core\Language\LanguageManager $language_manager
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
    *   The language manager.
    */
-  public function __construct(CacheBackendInterface $cache_backend, ConfigFactory $config, ModuleHandlerInterface $module_handler, LanguageManager $language_manager) {
+  public function __construct(CacheBackendInterface $cache_backend, ConfigFactory $config, ModuleHandlerInterface $module_handler, LanguageManagerInterface $language_manager) {
     $this->cacheBackend = $cache_backend;
     $this->moduleHandler = $module_handler;
     $this->languageManager = $language_manager;
 
-    $this->langcode = $this->languageManager->getLanguage(Language::TYPE_INTERFACE)->id;
+    $this->langcode = $this->languageManager->getCurrentLanguage()->id;
     $this->skipCache = $config->get('views.settings')->get('skip_cache');
   }
 
diff --git a/core/modules/views/tests/Drupal/views/Tests/ViewsDataTest.php b/core/modules/views/tests/Drupal/views/Tests/ViewsDataTest.php
index b6e688013efa22d674278305bd904be02da16422..1850903831d781a2874f4db7c7be5a31d9d3dd0a 100644
--- a/core/modules/views/tests/Drupal/views/Tests/ViewsDataTest.php
+++ b/core/modules/views/tests/Drupal/views/Tests/ViewsDataTest.php
@@ -76,11 +76,9 @@ protected function setUp() {
     $configs['views.settings']['skip_cache'] = FALSE;
     $this->configFactory = $this->getConfigFactoryStub($configs);
     $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
-    $this->languageManager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager')
-      ->disableOriginalConstructor()
-      ->getMock();
+    $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
     $this->languageManager->expects($this->any())
-      ->method('getLanguage')
+      ->method('getCurrentLanguage')
       ->will($this->returnValue(new Language(array('id' => 'en'))));
 
     $this->viewsData = new ViewsData($this->cacheBackend, $this->configFactory, $this->moduleHandler, $this->languageManager);
diff --git a/core/tests/Drupal/Tests/Core/Datetime/DateTest.php b/core/tests/Drupal/Tests/Core/Datetime/DateTest.php
index 55f24713c47c8f71b9513767026940253cacf09d..57cfe001401d6679bd88d7508c9e16dff3d9faea 100644
--- a/core/tests/Drupal/Tests/Core/Datetime/DateTest.php
+++ b/core/tests/Drupal/Tests/Core/Datetime/DateTest.php
@@ -60,9 +60,7 @@ public static function getInfo() {
 
   protected function setUp() {
     $this->entityManager = $this->getMock('Drupal\Core\Entity\EntityManagerInterface');
-    $this->languageManager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager')
-      ->disableOriginalConstructor()
-      ->getMock();
+    $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
     $this->stringTranslation = $this->getMock('Drupal\Core\StringTranslation\TranslationInterface');
 
     $this->date = new Date($this->entityManager, $this->languageManager, $this->stringTranslation, $this->getConfigFactoryStub());
diff --git a/core/tests/Drupal/Tests/Core/Menu/ContextualLinkManagerTest.php b/core/tests/Drupal/Tests/Core/Menu/ContextualLinkManagerTest.php
index fa27a3e2cc77974cf50ab89f70d76727d4508fa6..0e52b9f5988aea646729671d7c6a8c2df24ae70f 100644
--- a/core/tests/Drupal/Tests/Core/Menu/ContextualLinkManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Menu/ContextualLinkManagerTest.php
@@ -113,11 +113,9 @@ protected function setUp() {
     $property->setAccessible(TRUE);
     $property->setValue($this->contextualLinkManager, $this->accessManager);
 
-    $language_manager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager')
-      ->disableOriginalConstructor()
-      ->getMock();
+    $language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
     $language_manager->expects($this->any())
-      ->method('getLanguage')
+      ->method('getCurrentLanguage')
       ->will($this->returnValue(new Language(array('id' => 'en'))));
 
     $this->moduleHandler = $this->getMock('\Drupal\Core\Extension\ModuleHandlerInterface');
diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalTaskIntegrationTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalTaskIntegrationTest.php
index 47384aca2d5f565a19ff6fc5e89ba8adc2074ff4..aaaa6259fc9a76b7ed4509f32a3360ddfeaccdd4 100644
--- a/core/tests/Drupal/Tests/Core/Menu/LocalTaskIntegrationTest.php
+++ b/core/tests/Drupal/Tests/Core/Menu/LocalTaskIntegrationTest.php
@@ -102,11 +102,9 @@ protected function getLocalTaskManager($module_dirs, $route_name, $route_params)
     $property->setAccessible(TRUE);
     $property->setValue($manager, $factory);
 
-    $language_manager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager')
-      ->disableOriginalConstructor()
-      ->getMock();
+    $language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
     $language_manager->expects($this->any())
-      ->method('getLanguage')
+      ->method('getCurrentLanguage')
       ->will($this->returnValue(new Language(array('id' => 'en'))));
 
     $cache_backend = $this->getMock('Drupal\Core\Cache\CacheBackendInterface');
diff --git a/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php b/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php
index da5f90d6e25022a9bc9cb9d74caf1c455610bff7..6fc7c1ceea1b84c6ed73cb34c4589b5f49e84117 100644
--- a/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Menu/LocalTaskManagerTest.php
@@ -265,11 +265,9 @@ protected function setupLocalTaskManager() {
     $property->setAccessible(TRUE);
     $property->setValue($this->manager, $this->factory);
 
-    $language_manager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager')
-      ->disableOriginalConstructor()
-      ->getMock();
+    $language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
     $language_manager->expects($this->any())
-      ->method('getLanguage')
+      ->method('getCurrentLanguage')
       ->will($this->returnValue(new Language(array('id' => 'en'))));
 
     $this->manager->setCacheBackend($this->cacheBackend, $language_manager, 'local_task', array('local_task' => 1));
diff --git a/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php b/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php
index f49184f83a951f9ef903e8e85edc4db1977d9c97..9197085666ee80d0915bbc294ceaf6ad48554be5 100644
--- a/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php
+++ b/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php
@@ -8,17 +8,21 @@
 namespace Drupal\Tests\Core\PathProcessor;
 
 use Drupal\Component\Utility\Settings;
+use Drupal\Core\Language\Language;
 use Drupal\Core\PathProcessor\PathProcessorAlias;
 use Drupal\Core\PathProcessor\PathProcessorDecode;
 use Drupal\Core\PathProcessor\PathProcessorFront;
 use Drupal\Core\PathProcessor\PathProcessorManager;
 use Drupal\language\HttpKernel\PathProcessorLanguage;
+use Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
 use Symfony\Component\HttpFoundation\Request;
 
 use Drupal\Tests\UnitTestCase;
 
 /**
  * Tests path processor functionality.
+ *
+ * @group PathApi
  */
 class PathProcessorTest extends UnitTestCase {
 
@@ -45,12 +49,44 @@ public function setUp() {
     }
     $this->languages = $languages;
 
+    // Create a stub configuration.
+    $language_prefixes = array_keys($this->languages);
+    $config = array(
+      'url' => array(
+        'prefixes' => array_combine($language_prefixes, $language_prefixes)
+      )
+    );
+
+    // Create a URL-based language negotiation method definition.
+    $method_definitions = array(
+      LanguageNegotiationUrl::METHOD_ID => array(
+        'class' => '\Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl',
+      ),
+    );
+
+    // Create a URL-based language negotiation method.
+    $method_instance = new LanguageNegotiationUrl($config);
+
     // Create a language manager stub.
-    $language_manager = $this->getMock('Drupal\Core\Language\LanguageManager');
+    $language_manager = $this->getMockBuilder('Drupal\language\ConfigurableLanguageManagerInterface')
+      ->getMock();
     $language_manager->expects($this->any())
-      ->method('getLanguage')
+      ->method('getCurrentLanguage')
       ->will($this->returnValue($languages['en']));
+    $language_manager->expects($this->any())
+      ->method('getLanguages')
+      ->will($this->returnValue($this->languages));
+    $language_manager->expects($this->any())
+      ->method('getLanguageTypes')
+      ->will($this->returnValue(array(Language::TYPE_INTERFACE)));
+    $language_manager->expects($this->any())
+      ->method('getNegotiationMethods')
+      ->will($this->returnValue($method_definitions));
+    $language_manager->expects($this->any())
+      ->method('getNegotiationMethodInstance')
+      ->will($this->returnValue($method_instance));
 
+    $method_instance->setLanguageManager($language_manager);
     $this->languageManager = $language_manager;
   }
 
@@ -79,23 +115,41 @@ function testProcessInbound() {
 
     // Create a stub config factory with all config settings that will be checked
     // during this test.
-    $language_prefixes = array_keys($this->languages);
     $config_factory_stub = $this->getConfigFactoryStub(
       array(
         'system.site' => array(
           'page.front' => 'user'
         ),
         'language.negotiation' => array(
-          'url.prefixes' => array_combine($language_prefixes, $language_prefixes)
-        )
+          'url' => array(
+            'prefixes' => array('fr' => 'fr'),
+          ),
+        ),
       )
     );
 
+    // Create a language negotiator stub.
+    $negotiator = $this->getMockBuilder('Drupal\language\LanguageNegotiatorInterface')
+      ->getMock();
+    $negotiator->expects($this->any())
+      ->method('getNegotiationMethods')
+      ->will($this->returnValue(array(LanguageNegotiationUrl::METHOD_ID => array('class' => 'Drupal\language\Plugin\LanguageNegotiation\LanguageNegotiationUrl'))));
+    $method = new LanguageNegotiationUrl();
+    $method->setConfig($config_factory_stub);
+    $method->setLanguageManager($this->languageManager);
+    $negotiator->expects($this->any())
+      ->method('getNegotiationMethodInstance')
+      ->will($this->returnValue($method));
+
+    // Create a user stub.
+    $current_user = $this->getMockBuilder('Drupal\Core\Session\AccountInterface')
+      ->getMock();
+
     // Create the processors.
     $alias_processor = new PathProcessorAlias($alias_manager);
     $decode_processor = new PathProcessorDecode();
     $front_processor = new PathProcessorFront($config_factory_stub);
-    $language_processor = new PathProcessorLanguage($config_factory_stub, new Settings(array()), $this->languageManager, $this->languages);
+    $language_processor = new PathProcessorLanguage($config_factory_stub, new Settings(array()), $this->languageManager, $negotiator, $current_user);
 
     // First, test the processor manager with the processors in the incorrect
     // order. The alias processor will run before the language processor, meaning
diff --git a/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php b/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
index 3e86b66db6514fb960ff54e9386adc7cf5082c97..ec84708fa8822c9d0561d15ae05995662ee59616 100644
--- a/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
@@ -113,9 +113,9 @@ public function testDefaultPluginManagerWithEmptyCache() {
       ->with($cid . ':en', $this->expectedDefinitions);
 
     $language = new Language(array('id' => 'en'));
-    $language_manager = $this->getMock('Drupal\Core\Language\LanguageManager');
+    $language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
     $language_manager->expects($this->once())
-      ->method('getLanguage')
+      ->method('getCurrentLanguage')
       ->with(Language::TYPE_INTERFACE)
       ->will($this->returnValue($language));
 
@@ -144,9 +144,9 @@ public function testDefaultPluginManagerWithFilledCache() {
       ->method('set');
 
     $language = new Language(array('id' => 'en'));
-    $language_manager = $this->getMock('Drupal\Core\Language\LanguageManager');
+    $language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
     $language_manager->expects($this->once())
-      ->method('getLanguage')
+      ->method('getCurrentLanguage')
       ->with(Language::TYPE_INTERFACE)
       ->will($this->returnValue($language));
 
@@ -173,9 +173,9 @@ public function testCacheClearWithTags() {
       ->method('deleteMultiple');
 
     $language = new Language(array('id' => 'en'));
-    $language_manager = $this->getMock('Drupal\Core\Language\LanguageManager');
+    $language_manager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
     $language_manager->expects($this->once())
-      ->method('getLanguage')
+      ->method('getCurrentLanguage')
       ->with(Language::TYPE_INTERFACE)
       ->will($this->returnValue($language));
 
diff --git a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php
index 6b7334fa395ae85e9b9cb216387ca5d94eb3fbe8..469b0e39f58bd0b7a8e484b526f7fc9f86b43362 100644
--- a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php
+++ b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php
@@ -79,7 +79,7 @@ protected function setUp() {
 
     $this->urlGenerator = $this->getMock('\Drupal\Core\Routing\UrlGenerator', array(), array(), '', FALSE);
     $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
-    $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManager');
+    $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManagerInterface');
 
     $this->linkGenerator = new LinkGenerator($this->urlGenerator, $this->moduleHandler, $this->languageManager);
   }
@@ -89,7 +89,7 @@ protected function setUp() {
    */
   public function setUpLanguageManager() {
     $this->languageManager->expects($this->any())
-      ->method('getLanguage')
+      ->method('getCurrentLanguage')
       ->will($this->returnValue(new Language(array('id' => 'en'))));
   }