diff --git a/core/core.services.yml b/core/core.services.yml
index 465d145..c64c8af 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -197,9 +197,6 @@ services:
   http_kernel:
     class: Drupal\Core\HttpKernel
     arguments: ['@event_dispatcher', '@service_container', '@controller_resolver']
-  language_manager:
-    class: Drupal\Core\Language\LanguageManager
-    arguments: ['@state']
   string_translator.custom_strings:
     class: Drupal\Core\StringTranslation\Translator\CustomStrings
     arguments: ['@settings']
diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index 6d2dd8f..a042fd6 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -2369,37 +2369,13 @@ function language($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.
+ *
+ * @deprecated as of Drupal 8.0, use
+ *   \Drupal::languageManager()->isMultilingual() instead.
  */
 function language_multilingual() {
   return \Drupal::languageManager()->isMultilingual();
@@ -2418,65 +2394,7 @@ function language_multilingual() {
  *   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() || module_exists('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);
-    }
-    else {
-      // 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;
+  return Drupal::languageManager()->getLanguageList($flags);
 }
 
 /**
@@ -2490,24 +2408,7 @@ function language_list($flags = Language::STATE_CONFIGURABLE) {
  *   An array of language objects.
  */
 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;
+  return Drupal::languageManager()->getDefaultLockedLanguages($weight);
 }
 
 /**
@@ -2518,10 +2419,11 @@ function language_default_locked_languages($weight = 0) {
  *
  * @return \Drupal\core\Language\Language|null
  *   A fully-populated language object or NULL.
+ *
+ * @see \Drupal\Core\Language\LanguageManager::loadLanguage().
  */
 function language_load($langcode) {
-  $languages = language_list(Language::STATE_ALL);
-  return isset($languages[$langcode]) ? $languages[$langcode] : NULL;
+  return Drupal::languageManager()->loadLanguage($langcode);
 }
 
 /**
@@ -2568,15 +2470,8 @@ function language_is_locked($langcode) {
  *   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);
+  $default_info = variable_get('language_default', Language::$defaultValues);
+  return new Language($default_info + array('default' => TRUE));
 }
 
 /**
diff --git a/core/includes/common.inc b/core/includes/common.inc
index 77ea718..f8d4306 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -4839,6 +4839,14 @@ function drupal_flush_all_caches() {
 }
 
 /**
+ * Rebuild the container to store updated language negotiation settings.
+ */
+function drupal_rebuild_language_negotiation_settings() {
+  Drupal::service('plugin.manager.language_negotiation_method')->clearCachedDefinitions();
+  PhpStorageFactory::get('service_container')->deleteAll();
+}
+
+/**
  * Changes the dummy query string added to all CSS and JavaScript files.
  *
  * Changing the dummy query string appended to CSS and JavaScript files forces
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 9b0ef4c..d3bf3dc 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -10,6 +10,7 @@
 use Drupal\Core\Database\Install\TaskException;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Language\LanguageManager;
+use Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationBrowser;
 use Drupal\Core\StringTranslation\Translator\FileTranslation;
 
 use Drupal\Core\DependencyInjection\ContainerBuilder;
@@ -370,6 +371,14 @@ function install_begin_request(&$install_state) {
   else {
     // @todo Move into a proper Drupal\Core\DependencyInjection\InstallContainerBuilder.
     $container = new ContainerBuilder();
+    // Register Core's namespaces just for the installer.
+    $namespaces = array(
+      'Drupal\Core' => DRUPAL_ROOT . '/core/lib',
+      'Drupal\Component' => DRUPAL_ROOT . '/core/lib',
+    );
+    $container->register('container.namespaces', 'ArrayObject')
+      ->addArgument('%container.namespaces%');
+    $container->setParameter('container.namespaces', $namespaces);
 
     $container->register('event_dispatcher', 'Symfony\Component\EventDispatcher\EventDispatcher');
 
@@ -387,9 +396,12 @@ function install_begin_request(&$install_state) {
       ->addArgument(new Reference('config.context'));
 
     // Register the 'language_manager' service.
-    $container
-      ->register('language_manager', 'Drupal\Core\Language\LanguageManager')
-      ->addArgument(NULL);
+    $container->register('plugin.manager.language_negotiation_method', 'Drupal\Core\Language\LanguageNegotiationMethodManager')
+      ->addArgument(new Reference('container.namespaces'))
+      ->addArgument(new Reference('cache.cache'));
+    $container->register('language_manager', 'Drupal\Core\Language\InstallLanguageManager')
+      ->addArgument(array())
+      ->addArgument(new Reference('plugin.manager.language_negotiation_method'));
 
     // Register the translation services.
     install_register_translation_service($container);
@@ -1613,7 +1625,8 @@ function install_select_language_form($form, &$form_state, $files = array()) {
     }
   }
 
-  $browser_langcode = language_from_browser($browser_options);
+  $method = new LanguageNegotiationBrowser(array('browser' => array('mappings' => array())));
+  $browser_langcode = $method->negotiateLanguage($browser_options, Request::createFromGlobals());
   $form['langcode'] = array(
     '#type' => 'select',
     '#title' => t('Choose language'),
diff --git a/core/includes/language.inc b/core/includes/language.inc
index e057616..6b7329e 100644
--- a/core/includes/language.inc
+++ b/core/includes/language.inc
@@ -8,16 +8,8 @@
  */
 
 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';
+use Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationSelected;
+use Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUI;
 
 /**
  * @defgroup language_negotiation Language Negotiation API functionality
@@ -89,8 +81,8 @@
  * 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';
+ *   $negotiation_info[\Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUrl::METHOD_ID]['callbacks']['negotiation'] = 'mymodule_from_url';
+ *   $negotiation_info[\Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUrl::METHOD_ID]['file'] = drupal_get_path('module', 'mymodule') . '/mymodule.module';
  * }
  *
  * function mymodule_from_url($languages) {
@@ -113,42 +105,6 @@
  */
 
 /**
- * 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
@@ -206,8 +162,8 @@ function language_types_set(array $configurable_language_types) {
   // invocation of \Drupal\Core\Extension\ModuleHandler::install() or
   // \Drupal\Core\Extension\ModuleHandler::uninstall() could invalidate the
   // cached information.
+  \Drupal::service('plugin.manager.language_negotiation_method')->clearCachedDefinitions();
   drupal_static_reset('language_types_info');
-  drupal_static_reset('language_negotiation_info');
 
   $language_types = array();
   $negotiation_info = language_negotiation_info();
@@ -225,7 +181,7 @@ function language_types_set(array $configurable_language_types) {
       // 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(LanguageNegotiationUI::METHOD_ID);
         $method_weights = array_flip($method_weights);
         language_negotiation_set($type, $method_weights);
       }
@@ -256,10 +212,6 @@ function language_types_set(array $configurable_language_types) {
   $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');
 }
 
 /**
@@ -274,7 +226,7 @@ function language_types_set(array $configurable_language_types) {
  */
 function language_negotiation_method_get_first($type) {
   $negotiation = variable_get("language_negotiation_$type", array());
-  return empty($negotiation) ? LANGUAGE_NEGOTIATION_SELECTED : key($negotiation);
+  return empty($negotiation) ? LanguageNegotiationSelected::METHOD_ID : key($negotiation);
 }
 
 /**
@@ -313,30 +265,12 @@ function language_negotiation_method_enabled($method_id, $type = NULL) {
  *
  * @return
  *   A keyed array of links ready to be themed.
+ *
+ * @deprecated as of 8.0, use
+ *   \Drupal::languageManager()->getLanguageNegotiationSwitchLinks() instead.
  */
 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;
+  return Drupal::languageManager()->getLanguageNegotiationSwitchLinks($type, $path);
 }
 
 /**
@@ -347,14 +281,14 @@ function language_negotiation_purge() {
   // 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::service('plugin.manager.language_negotiation_method')->clearCachedDefinitions();
   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) {
+    foreach (variable_get("language_negotiation_$type", array()) as $method_id) {
       if (isset($negotiation_info[$method_id])) {
         $method_weights[$method_id] = $weight++;
       }
@@ -372,10 +306,7 @@ function language_negotiation_purge() {
  *   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();
+  $methods = array();
   $negotiation_info = language_negotiation_info();
   $default_types = language_types_get_configurable();
 
@@ -387,21 +318,15 @@ function language_negotiation_set($type, $method_weights) {
       $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);
+      $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])) {
-        $method_data = array();
-        foreach ($method_fields as $field) {
-          if (isset($method[$field])) {
-            $method_data[$field] = $method[$field];
-          }
-        }
-        $negotiation[$method_id] = $method_data;
+        $methods[$method_id] = $method_id;
       }
     }
   }
 
-  variable_set("language_negotiation_$type", $negotiation);
+  variable_set("language_negotiation_$type", $methods);
 }
 
 /**
@@ -409,140 +334,13 @@ function language_negotiation_set($type, $method_weights) {
  *
  * @return
  *   An array of language negotiation methods.
+ *
+ * @deprecated as of 8.0, use
+ *   Drupal::service('plugin.manager.language_negotiation_method')->getDefinitions()
+ *   instead.
  */
 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);
+  return Drupal::service('plugin.manager.language_negotiation_method')->getDefinitions();
 }
 
 /**
diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php
index 9cd8f5a..c1bae45 100644
--- a/core/lib/Drupal/Core/CoreServiceProvider.php
+++ b/core/lib/Drupal/Core/CoreServiceProvider.php
@@ -13,6 +13,7 @@
 use Drupal\Core\DependencyInjection\Compiler\ModifyServiceDefinitionsPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterKernelListenersPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterAccessChecksPass;
+use Drupal\Core\DependencyInjection\Compiler\RegisterLanguageNegotiationPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterPathProcessorsPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterRouteFiltersPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterRouteEnhancersPass;
@@ -51,6 +52,7 @@ public function register(ContainerBuilder $container) {
     $this->registerTwig($container);
     $this->registerModuleHandler($container);
     $this->registerUuid($container);
+    $this->registerLanguage($container);
 
     $container->addCompilerPass(new RegisterRouteFiltersPass());
     // Add a compiler pass for registering event subscribers.
@@ -161,4 +163,25 @@ public static function registerUuid(ContainerBuilder $container) {
     return $uuid_class;
   }
 
+  /**
+   * Registers language related services.
+   */
+  protected function registerLanguage(ContainerBuilder $container) {
+    // @todo Refactor this when all language negotiation settings are converted
+    //   a single configuration object.
+    $storage = $container->get('kernel.config.storage');
+    $config = $storage->read('language.negotiation') ?: array();
+    $config += array('browser' => array('mappings' => $storage->read('language.mappings')));
+
+    // Register the 'language_manager' service.
+    $container->register('plugin.manager.language_negotiation_method', 'Drupal\Core\Language\LanguageNegotiationMethodManager')
+      ->addArgument(new Reference('container.namespaces'))
+      ->addArgument(new Reference('cache.cache'))
+      ->addArgument(new Reference('module_handler'));
+    $container->register('language_manager', 'Drupal\Core\Language\LanguageManager')
+      ->addArgument($config)
+      ->addArgument(new Reference('plugin.manager.language_negotiation_method'))
+      ->addArgument(new Reference('state'));
+   }
+
 }
diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php
index 884b271..cbecfeb 100644
--- a/core/lib/Drupal/Core/DrupalKernel.php
+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -510,6 +510,8 @@ protected function buildContainer() {
     $container->register('class_loader')->setSynthetic(TRUE);
     $container->register('kernel', 'Symfony\Component\HttpKernel\KernelInterface')->setSynthetic(TRUE);
     $container->register('service_container', 'Symfony\Component\DependencyInjection\ContainerInterface')->setSynthetic(TRUE);
+    // Register the kernel-level config storage.
+    $container->set('kernel.config.storage', $this->configStorage);
     $yaml_loader = new YamlFileLoader($container);
     foreach ($this->serviceYamls as $filename) {
       $yaml_loader->load($filename);
diff --git a/core/lib/Drupal/Core/Language/InstallLanguageManager.php b/core/lib/Drupal/Core/Language/InstallLanguageManager.php
new file mode 100644
index 0000000..b70ee3f
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/InstallLanguageManager.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\InstallLanguageManager.
+ */
+
+namespace Drupal\Core\Language;
+
+use Drupal\Component\Plugin\PluginManagerInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
+
+/**
+ * Class responsible for initializing each language type.
+ */
+class InstallLanguageManager extends LanguageManager {
+
+  /**
+   * Constructs a new LanguageManager object.
+   *
+   * @param array $config
+   *   An array of configuration.
+   * @param \Drupal\Component\Plugin\PluginManagerInterface $negotiator_manager
+   *   The language negotiation methods plugin manager
+   */
+  public function __construct(array $config, PluginManagerInterface $negotiator_manager) {
+    $this->config = $config;
+    $this->negotiatorManager = $negotiator_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isMultilingual() {
+    // No state service in install time.
+    return FALSE;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Language/Language.php b/core/lib/Drupal/Core/Language/Language.php
index 7181b32..ceea20f 100644
--- a/core/lib/Drupal/Core/Language/Language.php
+++ b/core/lib/Drupal/Core/Language/Language.php
@@ -17,6 +17,23 @@
  * @see language_default()
  */
 class Language {
+
+  /**
+   * The values to use to instantiate the default language.
+   *
+   * @todo Remove once converted to config.
+   *
+   * @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.
   public $name = '';
   public $id = '';
diff --git a/core/lib/Drupal/Core/Language/LanguageManager.php b/core/lib/Drupal/Core/Language/LanguageManager.php
index 7bc716d..371454b 100644
--- a/core/lib/Drupal/Core/Language/LanguageManager.php
+++ b/core/lib/Drupal/Core/Language/LanguageManager.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Language;
 
+use Drupal\Component\Plugin\PluginManagerInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
 
@@ -16,18 +17,16 @@
 class LanguageManager {
 
   /**
-   * A request object.
-   *
-   * @var \Symfony\Component\HttpFoundation\Request
+   * The language negotiation method id for the language manager.
    */
-  protected $request;
+  const METHOD_ID = 'language-default';
 
   /**
    * The Key/Value Store to use for state.
    *
    * @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface
    */
-  protected $state = NULL;
+  protected $state;
 
   /**
    * An array of language objects keyed by language type.
@@ -37,6 +36,34 @@ class LanguageManager {
   protected $languages;
 
   /**
+   * An array of all the available languages keyed by language code.
+   *
+   * @var array
+   */
+  protected $languageList;
+
+  /**
+   * An array of configuration.
+   *
+   * @var array
+   */
+  protected $config;
+
+  /**
+   * The language negotiation method plugin manager.
+   *
+   * @var \Drupal\Component\Plugin\PluginManagerInterface
+   */
+  protected $negotiatorManager;
+
+  /**
+   * A request object.
+   *
+   * @var \Symfony\Component\HttpFoundation\Request
+   */
+  protected $request;
+
+  /**
    * Whether or not the language manager has been initialized.
    *
    * @var bool
@@ -46,20 +73,23 @@ class LanguageManager {
   /**
    * 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
    */
   protected $initializing = FALSE;
 
   /**
-   * Constructs an LanguageManager object.
+   * Constructs a new LanguageManager object.
    *
-   * @param \Drupal\Core\KeyValueStore\KeyValueStoreInterface $state
-   *   The state keyvalue store.
+   * @param array $config
+   *   An array of configuration.
+   * @param \Drupal\Component\Plugin\PluginManagerInterface $negotiator_manager
+   *   The language negotiation methods plugin manager
+   * @param \Drupal\Core\KeyValueStoreInterface $state
+   *   The state key value store.
    */
-  public function __construct(KeyValueStoreInterface $state = NULL) {
+  public function __construct(array $config, PluginManagerInterface $negotiator_manager, KeyValueStoreInterface $state) {
+    $this->config = $config;
+    $this->negotiatorManager = $negotiator_manager;
     $this->state = $state;
   }
 
@@ -71,9 +101,11 @@ public function init() {
       return;
     }
     if ($this->isMultilingual()) {
-      foreach ($this->getLanguageTypes() as $type) {
-        $this->getLanguage($type);
-      }
+      // This is still assumed by various functions to be loaded.
+      include_once DRUPAL_ROOT . '/core/includes/language.inc';
+    }
+    foreach ($this->getLanguageTypes() as $type) {
+      $this->getLanguage($type);
     }
     $this->initialized = TRUE;
   }
@@ -105,31 +137,119 @@ public function getLanguage($type = Language::TYPE_INTERFACE) {
       return $this->languages[$type];
     }
 
+    // Ensure we have a valid value for this language type.
+    $this->languages[$type] = $this->getLanguageDefault();
+
     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->languages[$type] = $this->initializeType($type);
         $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();
+      // If the current interface language needs to be retrieved during
+      // initialization we return the system language. This way t() calls
+      // happening during initialization will return the original strings which
+      // can be translated by calling t() 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));
       }
     }
-    else {
-      $this->languages[$type] = $this->getLanguageDefault();
-    }
+
     return $this->languages[$type];
   }
 
   /**
+   * Initializes the specified language type.
+   *
+   * @param string $type
+   *   The language type to be initialized.
+   */
+  public function initializeType($type) {
+    // Execute the language negotiation methods in the order they were set up
+    // and return the first valid language found.
+    foreach ($this->getNegotiationForType($type) as $method_id) {
+      if (!isset($this->negotiated[$method_id])) {
+        $this->negotiated[$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->negotiated[$method_id]) ? clone($this->negotiated[$method_id]) : FALSE;
+
+      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 = $this->getLanguageDefault();
+    $language->method_id = LanguageManager::METHOD_ID;
+    return $language;
+  }
+
+  /**
+   * Returns the negotiation settings for the specified language type.
+   *
+   * @param string $type
+   *   The type of the language to retrieve the negotiation settings for.
+   *
+   * @returns array
+   *   An array of language negotiation method identifiers ordered by method
+   *   weight.
+   */
+  protected function getNegotiationForType($type) {
+    // @todo convert to CMI https://drupal.org/node/1827038
+    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|FALSE
+   *   Negotiated language object for given type and method, FALSE otherwise.
+   */
+  protected function negotiateLanguage($type, $method_id) {
+    global $user;
+    $langcode = FALSE;
+    $languages = $this->getLanguageList();
+    $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 (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.
+      if ($cache = !isset($method['cache']) || $user->isAuthenticated() || $method['cache'] == $cache_enabled) {
+        $negotiator = $this->negotiatorManager->createInstance($method_id, $this->config);
+        $negotiator->setLanguageManager($this);
+        $langcode = $negotiator->negotiateLanguage($languages, $this->request);
+      }
+    }
+
+    return isset($languages[$langcode]) ? $languages[$langcode] : FALSE;
+  }
+
+  /**
    * Resets the given language type or all types if none specified.
    *
    * @param string|null $type
@@ -141,6 +261,7 @@ public function reset($type = NULL) {
     if (!isset($type)) {
       $this->languages = array();
       $this->initialized = FALSE;
+      $this->languageList = NULL;
     }
     elseif (isset($this->languages[$type])) {
       unset($this->languages[$type]);
@@ -154,21 +275,67 @@ public function reset($type = NULL) {
    *   TRUE if more than one language is enabled, FALSE otherwise.
    */
   public function isMultilingual() {
-    if (!isset($this->state)) {
-      // No state service in install time.
-      return FALSE;
-    }
     return ($this->state->get('language_count') ?: 1) > 1;
   }
 
   /**
    * Returns an array of the available language types.
    *
-   * @return array()
-   *   An array of all 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).
    */
-  protected function getLanguageTypes() {
-    return language_types_get_all();
+  public function getLanguageTypes() {
+    $types = \Drupal::config('system.language.types')->get('all');
+    return $types ? $types : array_keys($this->getTypeDefaults());
+  }
+
+  /**
+   * 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).
+   */
+  public function getTypeDefaults() {
+    return array(
+      Language::TYPE_INTERFACE => TRUE,
+      Language::TYPE_CONTENT => FALSE,
+      Language::TYPE_URL => 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 array
+   *   A keyed array of links ready to be themed.
+   */
+  function getLanguageNegotiationSwitchLinks($type, $path) {
+    $links = FALSE;
+    $negotiation = $this->getNegotiationForType($type);
+
+    foreach ($negotiation as $method_id) {
+      $definition = $this->negotiatorManager->getDefinition($method_id);
+      if (method_exists($definition['class'], 'languageSwitchLinks')) {
+        $instance = $this->negotiatorManager->createInstance($method_id, $this->config);
+        $result = $instance->languageSwitchLinks($type, $path);
+
+        if (!empty($result)) {
+          // Allow modules to provide translations for specific links.
+          \Drupal::moduleHandler()->alter('language_switch_links', $result, $type, $path);
+          $links = (object) array('links' => $result, 'method_id' => $method_id);
+          break;
+        }
+      }
+    }
+
+    return $links;
   }
 
   /**
@@ -177,16 +344,104 @@ protected function getLanguageTypes() {
    * @return \Drupal\Core\Language\Language
    *   A language object.
    */
-  protected 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 getLanguageDefault() {
+    // @todo convert to CMI https://drupal.org/node/1827038
+    $default_info = variable_get('language_default', Language::$defaultValues);
+    return new Language($default_info + array('default' => TRUE));
+  }
+
+  /**
+    * 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 getLanguageList($flags = Language::STATE_CONFIGURABLE) {
+    // Initialize master language list.
+    if (!isset($this->languageList)) {
+      // Initialize local language list cache.
+      $this->languageList = array();
+      // Fill in master language list based on current configuration.
+      $default = $this->getLanguageDefault();
+      // No language module, so use the default language only.
+      $this->languageList = array($default->id => $default);
+      // Add the special languages, they will be filtered later if needed.
+      $this->languageList += $this->getDefaultLockedLanguages($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 : $this->getLanguageDefault();
+      // Rename the default language.
+      $default->name = t("Site's default language (@lang_name)", array('@lang_name' => $default->name));
+      $filtered_languages['site_default'] = $default;
+    }
+
+    foreach ($this->languageList as $id => $language) {
+      if (($language->locked && !($flags & Language::STATE_LOCKED)) || (!$language->locked && !($flags & Language::STATE_CONFIGURABLE))) {
+        continue;
+       }
+      $filtered_languages[$id] = $language;
+    }
+
+    return $filtered_languages;
+  }
+
+  /**
+   * Loads a language object from the database.
+   *
+   * @param string $langcode
+   *   The language code.
+   *
+   * @return \Drupal\core\Language\Language|null
+   *   A fully-populated language object or NULL.
+   */
+  public function loadLanguage($langcode) {
+    $languages = $this->getLanguageList(Language::STATE_ALL);
+    return isset($languages[$langcode]) ? $languages[$langcode] : NULL;
+  }
+
+  /**
+   * 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) {
+    $languages = array();
+
+    $locked_language = array(
+      'default' => FALSE,
+      'locked' => TRUE,
+      'enabled' => TRUE,
+     );
+    $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;
   }
 
   /**
@@ -308,4 +563,14 @@ public static function getStandardLanguageList() {
     );
   }
 
+  /**
+   * 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).
+   */
+  public function getTypes() {
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Language/LanguageNegotiationInterface.php b/core/lib/Drupal/Core/Language/LanguageNegotiationInterface.php
new file mode 100644
index 0000000..9b1c666
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/LanguageNegotiationInterface.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\LanguageNegotiationInterface.
+ */
+
+namespace Drupal\Core\Language;
+
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Interface for language negotiation classes.
+ */
+interface LanguageNegotiationInterface {
+
+  /**
+   * Sets the language manager.
+   *
+   * @param LanguageManager $languageManager
+   *   The language manager to be used to retrieve the language list and the
+   *   already negotiated languages.
+   */
+  public function setLanguageManager(LanguageManager $languageManager);
+
+  /**
+   * Performs language negotiation.
+   *
+   * @param array $languages
+   *   An array of valid languages.
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   (optional) The current request. Defaults to NULL if it has not been
+   *   initialized yet.
+   */
+  public function negotiateLanguage(array $languages, Request $request = NULL);
+
+}
diff --git a/core/lib/Drupal/Core/Language/LanguageNegotiationMethodBase.php b/core/lib/Drupal/Core/Language/LanguageNegotiationMethodBase.php
new file mode 100644
index 0000000..c2c7dce
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/LanguageNegotiationMethodBase.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\LanguageNegotiationMethodBase.
+ */
+
+namespace Drupal\Core\Language;
+
+use Drupal\Core\Language\LanguageManager;
+use Drupal\Core\Language\LanguageNegotiationInterface;
+
+/**
+ * Base class for language negotiation methods.
+ */
+abstract class LanguageNegotiationMethodBase implements LanguageNegotiationInterface {
+
+  /**
+   * The language manager.
+   *
+   * @var \Drupal\Core\Language\LanguageManager
+   */
+  protected $languageManager;
+
+  /**
+   * The language negotiation configuration.
+   *
+   * @var array
+   */
+  protected $config;
+
+  /**
+   * Constructs a new LanguageNegotiationMethodBase object.
+   *
+   * @param array $config
+   */
+  public function __construct(array $config) {
+    $this->config = $config;
+  }
+
+  /**
+   * Implements \Drupal\Core\Language\LanguageNegotiationInterface::setLanguageManager().
+   */
+  public function setLanguageManager(LanguageManager $languageManager) {
+    $this->languageManager = $languageManager;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Language/LanguageNegotiationMethodManager.php b/core/lib/Drupal/Core/Language/LanguageNegotiationMethodManager.php
new file mode 100644
index 0000000..26d042e
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/LanguageNegotiationMethodManager.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\LanguageNegotiationMethodManager.
+ */
+
+namespace Drupal\Core\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.
+   */
+  public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend, ModuleHandlerInterface $module_handler = NULL) {
+    parent::__construct('Plugin/LanguageNegotiation', $namespaces);
+    $this->cacheBackend = $cache_backend;
+    $this->cacheKeyPrefix = 'language_negotiation_plugins';
+    $this->cacheKey = 'language_negotiation_plugins';
+    if ($module_handler) {
+      $this->alterInfo($module_handler, 'language_negotiation_info');
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function clearCachedDefinitions() {
+    $this->definitions = NULL;
+    $this->cacheBackend->delete($this->cacheKey);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php
new file mode 100644
index 0000000..8d0f062
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php
@@ -0,0 +1,153 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationBrowser.
+ */
+
+namespace Drupal\Core\Language\Plugin\LanguageNegotiation;
+
+use Drupal\Core\Language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class for identifying 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.
+ *
+ * @Plugin(
+ *   id = Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationBrowser::METHOD_ID,
+ *   weight = -2,
+ *   cache = 0,
+ *   name = @Translation("Browser"),
+ *   description = @Translation("Language from the browser's language settings."),
+ *   config = "admin/config/regional/language/detection/browser"
+ * )
+ */
+class LanguageNegotiationBrowser extends LanguageNegotiationMethodBase {
+
+  /**
+   * The language negotiation method id.
+   */
+  const METHOD_ID = 'language-browser';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function negotiateLanguage(array $languages, Request $request = NULL) {
+    if (!$request || !$request->server->get('HTTP_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($request->server->get('HTTP_ACCEPT_LANGUAGE')), $matches, PREG_SET_ORDER)) {
+      // Load custom mappings to support browsers that are sending non standard
+      // language codes.
+      $mappings = $this->config['browser']['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 occurence 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 occurence 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;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationSelected.php b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationSelected.php
new file mode 100644
index 0000000..b037bf5
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationSelected.php
@@ -0,0 +1,44 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationSelected.
+ */
+
+namespace Drupal\Core\Language\Plugin\LanguageNegotiation;
+
+use Drupal\Core\Language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class for identifying language from a selected language.
+ *
+ * @Plugin(
+ *   id = Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationSelected::METHOD_ID,
+ *   weight = 12,
+ *   name = @Translation("Selected language"),
+ *   description = @Translation("Language based on a selected language."),
+ *   config = "admin/config/regional/language/detection/selected"
+ * )
+ */
+class LanguageNegotiationSelected extends LanguageNegotiationMethodBase {
+
+  /**
+   * The language negotiation method id.
+   */
+  const METHOD_ID = 'language-selected';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function negotiateLanguage(array $languages, Request $request = NULL) {
+    // Replace the site's default langcode by its real value.
+    $langcode = isset($this->config['selected_langcode']) ? $this->config['selected_langcode'] : FALSE;
+    // Replace the site's default langcode by its real value.
+    if ($langcode == 'site_default') {
+     $langcode = language_default()->id;
+    }
+    return isset($languages[$langcode]) ? $langcode : $this->languageManager->getLanguageDefault()->id;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationSession.php b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationSession.php
new file mode 100644
index 0000000..8072069
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationSession.php
@@ -0,0 +1,87 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationSession.
+ */
+
+namespace Drupal\Core\Language\Plugin\LanguageNegotiation;
+
+use Drupal\Core\Language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Identify language from a request/session parameter.
+ *
+ * @Plugin(
+ *   id = Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationSession::METHOD_ID,
+ *   weight = -6,
+ *   name = @Translation("Session"),
+ *   description = @Translation("Language from a request/session parameter."),
+ *   config = "admin/config/regional/language/detection/session"
+ * )
+ */
+class LanguageNegotiationSession extends LanguageNegotiationMethodBase {
+
+  /**
+   * The language negotiation method id.
+   */
+  const METHOD_ID = 'language-session';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function negotiateLanguage(array $languages, Request $request = NULL) {
+    $param = $this->config['session']['parameter'];
+
+    // Request parameter: we need to update the session parameter only if we
+    // have an authenticated user.
+    $langcode = $request->query->get($param);
+    if ($langcode) {
+      global $user;
+      $languages = $this->languageManager->getLanguageList();
+      if ($user->isAuthenticated() && isset($languages[$langcode])) {
+        $_SESSION[$param] = $langcode;
+      }
+    }
+
+    // Session parameter.
+    if (isset($_SESSION[$param])) {
+      return $_SESSION[$param];
+    }
+
+    return FALSE;
+  }
+
+  /**
+   * Return the session language switcher block.
+   */
+  function languageSwitchLinks($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 = $_GET;
+
+    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;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUI.php b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUI.php
new file mode 100644
index 0000000..6492d6f
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUI.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUI.
+ */
+
+namespace Drupal\Core\Language\Plugin\LanguageNegotiation;
+
+use Drupal\Core\Language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class for identifying the language from the current interface language.
+ *
+ * @Plugin(
+ *   id = Drupal\Core\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 negotiateLanguage(array $languages, Request $request = NULL) {
+    return $this->languageManager->getLanguage()->id;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php
new file mode 100644
index 0000000..c0a9df7
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php
@@ -0,0 +1,120 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUrl.
+ */
+
+namespace Drupal\Core\Language\Plugin\LanguageNegotiation;
+
+use Drupal\Core\Language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class for identifying language via URL prefix or domain.
+ *
+ * @Plugin(
+ *   id = Drupal\Core\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 = "admin/config/regional/language/detection/url"
+ * )
+ */
+class LanguageNegotiationUrl extends LanguageNegotiationMethodBase {
+
+  /**
+   * 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 negotiateLanguage(array $languages, Request $request = NULL) {
+    if (!$request) {
+      return FALSE;
+    }
+
+    $langcode = FALSE;
+    switch ($this->config['url']['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.
+        $prefixes = $this->config['url']['prefixes'];
+        $negotiated_language = FALSE;
+        foreach ($languages as $language) {
+          if (isset($prefixes[$language->id]) && $prefixes[$language->id] == $prefix) {
+            $negotiated_language = $language;
+            break;
+          }
+        }
+
+        if ($negotiated_language !== FALSE) {
+          $langcode = $negotiated_language->id;
+        }
+        break;
+
+      case LanguageNegotiationUrl::CONFIG_DOMAIN:
+        // Get only the host, not the port.
+        $http_host = $request->server->get('HTTP_HOST');
+        if (strpos($http_host, ':') !== FALSE) {
+          $http_host_tmp = explode(':', $http_host);
+          $http_host = current($http_host_tmp);
+        }
+        $domains = $this->config['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) {
+              $langcode = $language->id;
+              break;
+            }
+          }
+        }
+        break;
+    }
+
+    return $langcode;
+  }
+
+  /**
+   * Return links for the URL language switcher block.
+   *
+   * Translation links may be provided by other modules.
+   */
+  function languageSwitchLinks($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;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUrlFallback.php b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUrlFallback.php
new file mode 100644
index 0000000..7a5d67f
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUrlFallback.php
@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUrlFallback.
+ */
+
+namespace Drupal\Core\Language\Plugin\LanguageNegotiation;
+
+use Drupal\Core\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\Core\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 negotiateLanguage(array $languages, Request $request = NULL) {
+    $default = $this->languageManager->getLanguageDefault();
+    $prefix = ($this->config['url']['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.
+    $domains = $this->config['url']['domains'];
+    $prefixes = $this->config['url']['prefixes'];
+    if (($prefix && empty($prefixes[$default->id])) || (!$prefix && empty($domains[$default->id]))) {
+      return $default->id;
+    }
+    else {
+      return $this->languageManager->getLanguage()->id;
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUser.php b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUser.php
new file mode 100644
index 0000000..a3124c8
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUser.php
@@ -0,0 +1,49 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUrl.
+ */
+
+namespace Drupal\Core\Language\Plugin\LanguageNegotiation;
+
+use Drupal\Core\Language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class for identifying language from the user preferences.
+ *
+ * @Plugin(
+ *   id = Drupal\Core\Language\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 negotiateLanguage(array $languages, Request $request = NULL) {
+    // User preference (only for authenticated users).
+    $user = \Drupal::currentUser();
+
+    if ($user->isAuthenticated()) {
+      $langcode = $user->getPreferredLangcode();
+      $default_langcode = $this->languageManager->getLanguageDefault()->id;
+      if (!empty($langcode) && $langcode != $default_langcode && isset($languages[$langcode])) {
+        return $langcode;
+      }
+    }
+
+    // No language preference from the user.
+    return FALSE;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php
new file mode 100644
index 0000000..b30c7a8
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUserAdmin.
+ */
+
+namespace Drupal\Core\Language\Plugin\LanguageNegotiation;
+
+use Drupal\Core\Language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Identifies admin language from the user preferences.
+ *
+ * @Plugin(
+ *   id = Drupal\Core\Language\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 negotiateLanguage(array $languages, Request $request = NULL) {
+    // User preference (only for authenticated users).
+    $user = \Drupal::currentUser();
+
+    // @todo Avoid calling _current_path() and path_is_admin() directly.
+    $request_path = $request ? urldecode(trim($request->getPathInfo(), '/')) : _current_path();
+    if ($user->isAuthenticated() && path_is_admin($request_path)) {
+      $langcode = $user->getPreferredAdminLangcode();
+      $default_langcode = $this->languageManager->getLanguageDefault()->id;
+      if (!empty($langcode) && $langcode != $default_langcode && isset($languages[$langcode])) {
+        return $langcode;
+      }
+    }
+
+    // No language preference from the user or not on an admin path.
+    return FALSE;
+  }
+
+}
diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php
index 915e4bb..0ecfb36 100644
--- a/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php
+++ b/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php
@@ -74,10 +74,9 @@ 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('', array('language' => language_load('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.
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php
index 8b885fe..fd1bf1f 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/content_translation/content_translation.install b/core/modules/content_translation/content_translation.install
index bd817bc..a1ac73a 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\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
 
 /**
  * Implements hook_schema().
@@ -85,8 +86,6 @@ 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));
 }
 
 /**
diff --git a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationUITest.php b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationUITest.php
index 21c8465..fe70f16 100644
--- a/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationUITest.php
+++ b/core/modules/content_translation/lib/Drupal/content_translation/Tests/ContentTranslationUITest.php
@@ -36,7 +36,7 @@
   function testTranslationUI() {
     $this->assertBasicTranslation();
     $this->doTestTranslationOverview();
-    $this->assertOutdatedStatus();
+    $this->doOutdatedStatus();
     $this->assertPublishedStatus();
     $this->assertAuthoringInfo();
     $this->assertTranslationDeletion();
@@ -117,7 +117,7 @@ protected function doTestTranslationOverview() {
   /**
    * Tests up-to-date status tracking.
    */
-  protected function assertOutdatedStatus() {
+  protected function doOutdatedStatus() {
     $entity = entity_load($this->entityType, $this->entityId, TRUE);
     $langcode = 'fr';
     $default_langcode = $this->langcodes[0];
diff --git a/core/modules/language/language.admin.inc b/core/modules/language/language.admin.inc
index f62b933..d6dd22c 100644
--- a/core/modules/language/language.admin.inc
+++ b/core/modules/language/language.admin.inc
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Core\Language\LanguageManager;
+use Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationSelected;
 
 /**
  * Prepare a language code list for unused predefined languages.
@@ -138,7 +139,7 @@ function language_negotiation_configure_form_table(&$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');
       }
@@ -246,7 +247,7 @@ function language_negotiation_configure_form_submit($form, &$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']);
@@ -281,6 +282,9 @@ function language_negotiation_configure_form_submit($form, &$form_state) {
     \Drupal::service('plugin.manager.block')->clearCachedDefinitions();
   }
 
+  // Rebuild the container to update the submitted settings.
+  drupal_rebuild_language_negotiation_settings();
+
   $form_state['redirect'] = 'admin/config/regional/language/detection';
   drupal_set_message(t('Language negotiation configuration saved.'));
 }
diff --git a/core/modules/language/language.install b/core/modules/language/language.install
index 5103389..847dc40 100644
--- a/core/modules/language/language.install
+++ b/core/modules/language/language.install
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Core\Language\Language;
+use Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
 
 /**
  * Implements hook_install().
@@ -18,7 +19,7 @@ function language_install() {
   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_negotiation_set($type, array(LanguageNegotiationUrl::METHOD_ID => 0));
   }
 
   // Update the language count.
@@ -34,9 +35,7 @@ function language_uninstall() {
   \Drupal::state()->delete('language_count');
 
   // Clear variables.
-  variable_del('language_types');
-
-  foreach (language_types_get_all() as $type) {
+  foreach (Drupal::languageManager()->getLanguageTypes() as $type) {
     variable_del("language_negotiation_$type");
     variable_del("language_negotiation_methods_weight_$type");
   }
diff --git a/core/modules/language/language.module b/core/modules/language/language.module
index 9a67e37..05f5b56 100644
--- a/core/modules/language/language.module
+++ b/core/modules/language/language.module
@@ -5,6 +5,9 @@
  * Add language handling functionality to Drupal.
  */
 
+use Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUI;
+use Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
+use Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUrlFallback;
 use Drupal\node\NodeTypeInterface;
 use Drupal\Core\Language\Language;
 
@@ -511,8 +514,8 @@ function language_save($language) {
     variable_set('language_default', (array) $language);
   }
 
-  // Kill the static cache in language_list().
-  drupal_static_reset('language_list');
+  // Reset the language information.
+  Drupal::languageManager()->reset();
 
   // Update language count based on unlocked language count.
   language_update_count();
@@ -565,13 +568,13 @@ function language_delete($langcode) {
     // Remove the language.
     entity_delete_multiple('language_entity', array($language->id));
 
-    drupal_static_reset('language_list');
-
     language_update_count();
 
     // Update weight of locked system languages.
     language_update_locked_weights();
 
+    Drupal::languageManager()->reset();
+
     $t_args = array('%language' => $language->name, '%langcode' => $language->id);
     watchdog('language', 'The %language (%langcode) language has been removed.', $t_args);
     return TRUE;
@@ -625,99 +628,17 @@ 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().
- */
-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.'),
-  );
-
-  $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.'),
-  );
-
-  $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."),
-  );
-
-  return $negotiation_info;
-}
-
-/**
  * Include negotiation backend functionality.
  */
 function language_negotiation_include() {
@@ -868,4 +789,5 @@ function language_system_regional_settings_form_submit($form, &$form_state) {
   $language = $languages[$form_state['values']['site_default_language']];
   $language->default = TRUE;
   language_save($language);
+  drupal_rebuild_language_negotiation_settings();
 }
diff --git a/core/modules/language/language.negotiation.inc b/core/modules/language/language.negotiation.inc
index 43cd607..4e1ff69 100644
--- a/core/modules/language/language.negotiation.inc
+++ b/core/modules/language/language.negotiation.inc
@@ -1,438 +1,12 @@
 <?php
 
-use \Symfony\Component\HttpFoundation\Request;
+use Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationSession;
 
 /**
  * @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) {
-  if (empty($_SERVER['HTTP_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($_SERVER['HTTP_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 occurence 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 occurence 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).
-  global $user;
-
-  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).
-  global $user;
-
-  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');
-
-  // Request parameter: we need to update the session parameter only if we have
-  // an authenticated user.
-  if (isset($_GET[$param]) && isset($languages[$langcode = $_GET[$param]])) {
-    global $user;
-    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= $_SERVER['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 = $_GET;
-
-  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.
  */
@@ -497,7 +71,7 @@ function language_url_rewrite_session(&$path, &$options) {
       $languages = language_list();
       $query_param = check_plain(\Drupal::config('language.negotiation')->get('session.parameter'));
       $query_value = isset($_GET[$query_param]) ? check_plain($_GET[$query_param]) : NULL;
-      $query_rewrite = isset($languages[$query_value]) && language_negotiation_method_enabled(LANGUAGE_NEGOTIATION_SESSION);
+      $query_rewrite = isset($languages[$query_value]) && language_negotiation_method_enabled(LanguageNegotiationSession::METHOD_ID);
     }
     else {
       $query_rewrite = FALSE;
diff --git a/core/modules/language/lib/Drupal/language/Form/LanguageAddForm.php b/core/modules/language/lib/Drupal/language/Form/LanguageAddForm.php
index ccc833c..4cabb9b 100644
--- a/core/modules/language/lib/Drupal/language/Form/LanguageAddForm.php
+++ b/core/modules/language/lib/Drupal/language/Form/LanguageAddForm.php
@@ -91,6 +91,10 @@ public function submitForm(array &$form, array &$form_state) {
     }
     // Save the language and inform the user that it happened.
     $language = language_save($language);
+
+    // Rebuild the container to update the submitted settings.
+    drupal_rebuild_language_negotiation_settings();
+
     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 9d8b6aa..318f54c 100644
--- a/core/modules/language/lib/Drupal/language/Form/LanguageEditForm.php
+++ b/core/modules/language/lib/Drupal/language/Form/LanguageEditForm.php
@@ -54,6 +54,8 @@ public function submitForm(array &$form, array &$form_state) {
     $language->name = $form_state['values']['name'];
     $language->direction = $form_state['values']['direction'];
     language_save($language);
+    // Rebuild the container to update the submitted settings.
+    drupal_rebuild_language_negotiation_settings();
     $form_state['redirect'] = 'admin/config/regional/language';
   }
 
diff --git a/core/modules/language/lib/Drupal/language/Form/NegotiationBrowserDeleteForm.php b/core/modules/language/lib/Drupal/language/Form/NegotiationBrowserDeleteForm.php
index 29de4ce..9419ba7 100644
--- a/core/modules/language/lib/Drupal/language/Form/NegotiationBrowserDeleteForm.php
+++ b/core/modules/language/lib/Drupal/language/Form/NegotiationBrowserDeleteForm.php
@@ -66,6 +66,8 @@ public function submitForm(array &$form, array &$form_state) {
       language_set_browser_drupal_langcode_mappings($mappings);
     }
 
+    // Rebuild the container to update the submitted settings.
+    drupal_rebuild_language_negotiation_settings();
     $form_state['redirect'] = 'admin/config/regional/language/detection/browser';
   }
 
diff --git a/core/modules/language/lib/Drupal/language/Form/NegotiationBrowserForm.php b/core/modules/language/lib/Drupal/language/Form/NegotiationBrowserForm.php
index b801b2b..01ae858 100644
--- a/core/modules/language/lib/Drupal/language/Form/NegotiationBrowserForm.php
+++ b/core/modules/language/lib/Drupal/language/Form/NegotiationBrowserForm.php
@@ -177,6 +177,7 @@ public function submitForm(array &$form, array &$form_state) {
       $config = $this->configFactory->get('language.mappings');
       $config->setData($mappings);
       $config->save();
+      drupal_rebuild_language_negotiation_settings();
     }
     $form_state['redirect'] = 'admin/config/regional/language/detection';
 
diff --git a/core/modules/language/lib/Drupal/language/Form/NegotiationSelectedForm.php b/core/modules/language/lib/Drupal/language/Form/NegotiationSelectedForm.php
index 4271861..f3a2a91 100644
--- a/core/modules/language/lib/Drupal/language/Form/NegotiationSelectedForm.php
+++ b/core/modules/language/lib/Drupal/language/Form/NegotiationSelectedForm.php
@@ -45,6 +45,8 @@ public function submitForm(array &$form, array &$form_state) {
       ->set('selected_langcode', $form_state['values']['selected_langcode'])
       ->save();
 
+    drupal_rebuild_language_negotiation_settings();
+
     parent::submitForm($form, $form_state);
   }
 
diff --git a/core/modules/language/lib/Drupal/language/Form/NegotiationSessionForm.php b/core/modules/language/lib/Drupal/language/Form/NegotiationSessionForm.php
index 1c73492..b937086c 100644
--- a/core/modules/language/lib/Drupal/language/Form/NegotiationSessionForm.php
+++ b/core/modules/language/lib/Drupal/language/Form/NegotiationSessionForm.php
@@ -46,6 +46,8 @@ public function submitForm(array &$form, array &$form_state) {
       ->set('session.parameter', $form_state['values']['language_negotiation_session_param'])
       ->save();
 
+    drupal_rebuild_language_negotiation_settings();
+
     parent::submitForm($form, $form_state);
   }
 
diff --git a/core/modules/language/lib/Drupal/language/Form/NegotiationUrlForm.php b/core/modules/language/lib/Drupal/language/Form/NegotiationUrlForm.php
index 6855a24..5c8fb7d 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\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
 
 /**
  * Configure the URL language negotiation method for this site.
@@ -33,8 +34,8 @@ public function buildForm(array $form, array &$form_state) {
       '#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 +48,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 +61,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 +104,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.
           form_error($form['prefix'][$langcode], t('The prefix may only be left blank for the default language.'));
@@ -127,7 +128,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.
           form_error($form['domain'][$langcode], 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 aa3cc2b..c9593f0 100644
--- a/core/modules/language/lib/Drupal/language/HttpKernel/PathProcessorLanguage.php
+++ b/core/modules/language/lib/Drupal/language/HttpKernel/PathProcessorLanguage.php
@@ -42,51 +42,43 @@ class PathProcessorLanguage implements InboundPathProcessorInterface, OutboundPa
    */
   protected $languageManager;
 
-  /**
-   * An array of enabled languages.
-   *
-   * @var array
-   */
-  protected $languages;
-
 
   /**
    * 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 LanguageManager $language_manager
+   *   A LanguageManager object.
    */
-  public function __construct(ConfigFactory $config, Settings $settings, LanguageManager $language_manager, array $languages = array()) {
+  public function __construct(ConfigFactory $config, Settings $settings, LanguageManager $language_manager) {
     $this->config = $config;
     $this->mixedModeSessions = $settings->get('mixed_mode_sessions', FALSE);
     $this->languageManager = $language_manager;
-    if (empty($languages)) {
-      $languages = language_list();
-    }
-    $this->languages = $languages;
   }
 
   /**
    * Implements Drupal\Core\PathProcessor\InboundPathProcessorInterface::processInbound().
    */
   public function processInbound($path, Request $request) {
-    if (!empty($path)) {
-      $args = explode('/', $path);
-      $prefix = array_shift($args);
+    if (empty($path)) {
+      return $path;
+    }
+
+    $languages = $this->languageManager->getLanguageList();
+    $prefixes = $this->config->get('language.negotiation')->get('url.prefixes');
+    $parts = explode('/', $path);
+    $prefix = array_shift($parts);
 
-      // 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);
-        }
-      }
+    // Search prefix within enabled languages.
+    foreach ($languages as $language) {
+      if (isset($prefixes[$language->id]) && $prefixes[$language->id] == $prefix) {
+        // Rebuild $path with the language removed.
+        $path = implode('/', $parts);
+        break;
+       }
     }
+
     return $path;
   }
 
@@ -103,7 +95,7 @@ public function processOutbound($path, &$options = array(), Request $request = N
       $url_scheme = $request->getScheme();
       $port = $request->getPort();
     }
-    $languages = array_flip(array_keys($this->languages));
+    $languages = array_flip(array_keys($this->languageManager->getLanguageList()));
     // 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);
diff --git a/core/modules/language/lib/Drupal/language/LanguageListController.php b/core/modules/language/lib/Drupal/language/LanguageListController.php
index 48b806f..c90c9cb 100644
--- a/core/modules/language/lib/Drupal/language/LanguageListController.php
+++ b/core/modules/language/lib/Drupal/language/LanguageListController.php
@@ -95,8 +95,9 @@ 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');
+    drupal_rebuild_language_negotiation_settings();
+
+    \Drupal::languageManager()->reset();
 
     // Update weight of locked system languages.
     language_update_locked_weights();
diff --git a/core/modules/language/lib/Drupal/language/LanguageManager.php b/core/modules/language/lib/Drupal/language/LanguageManager.php
new file mode 100644
index 0000000..72197dc
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/LanguageManager.php
@@ -0,0 +1,78 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\LanguageManager.
+ */
+
+namespace Drupal\language;
+
+use Drupal\Core\Config\ConfigFactory;
+use Drupal\Core\Config\StorageInterface;
+use Drupal\Core\Database\Connection;
+use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
+use Drupal\Core\Language\Language;
+use Drupal\Core\Language\LanguageManager as LanguageManagerBase;
+use Drupal\Component\Plugin\PluginManagerInterface;
+
+/**
+ * Overrides default LanguageManager to provide configured languages.
+ */
+class LanguageManager extends LanguageManagerBase {
+
+  /**
+   * The configuration factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactory;
+   */
+  protected $configFactory;
+
+  /**
+   * The configuration storage.
+   *
+   * @var \Drupal\Core\Config\StorageInterface;
+   */
+  protected $configStorage;
+
+  /**
+   * Constructs a new LanguageManager object.
+   *
+   * @param array $config
+   *   An array of configuration.
+   * @param \Drupal\Component\Plugin\PluginManagerInterface $negotiator_manager
+   *   The language negotiation methods plugin manager.
+   * @param \Drupal\Core\KeyValueStoreInterface $state
+   *   The state key value store.
+   * @param \Drupal\Core\Config\ConfigFactory $config_factory
+   *   The config factory service.
+   * @param \Drupal\Core\Config\StorageInterface $config_storage
+   *   The config storage service.
+   */
+  public function __construct(array $config, PluginManagerInterface $negotiator_manager, KeyValueStoreInterface $state, StorageInterface $config_storage) {
+    parent::__construct($config, $negotiator_manager, $state);
+    $this->configStorage = $config_storage;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLanguageList($flags = Language::STATE_CONFIGURABLE) {
+    if (!isset($this->languageList)) {
+      // Fill in master language list based on current configuration.
+      $default = $this->getLanguageDefault();
+
+      // Use language module configuration if available.
+      $language_ids = $this->configStorage->listAll('language.entity');
+      foreach (\Drupal::service('config.factory')->loadMultiple($language_ids) as $language_config) {
+        $langcode = $language_config->get('id');
+        $info = $language_config->get();
+        $info['default'] = ($langcode == $default->id);
+        $info['name'] = $info['label'];
+        $this->languageList[$langcode] = new Language($info);
+      }
+    }
+
+    return parent::getLanguageList($flags);
+  }
+
+}
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 0000000..40e7670
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/LanguageServiceProvider.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\LanguageServiceProvider.
+ */
+
+namespace Drupal\language;
+
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\DependencyInjection\ServiceProviderInterface;
+use Drupal\Core\DependencyInjection\ServiceModifierInterface;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * Overrides the language_manager service to point to language's module one.
+ */
+class LanguageServiceProvider implements ServiceProviderInterface, ServiceModifierInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function register(ContainerBuilder $container) { }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function alter(ContainerBuilder $container) {
+    $definition = $container->getDefinition('language_manager');
+    $definition->setClass('Drupal\language\LanguageManager')
+      ->addArgument(new Reference('config.storage'));
+  }
+
+}
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 5279dd6..3a5bbed 100644
--- a/core/modules/language/lib/Drupal/language/Plugin/Block/LanguageBlock.php
+++ b/core/modules/language/lib/Drupal/language/Plugin/Block/LanguageBlock.php
@@ -10,6 +10,9 @@
 use Drupal\block\BlockBase;
 use Drupal\block\Annotation\Block;
 use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Language\LanguageManager;
+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\LanguageManager
+   */
+  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\LanguageManager $language_manager
+   *   The language manager.
+   */
+  public function __construct(array $configuration, $plugin_id, array $plugin_definition, LanguageManager $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')
+    );
+  }
+
 
   /**
    * Overrides \Drupal\block\BlockBase::access().
    */
   function access() {
-    return language_multilingual();
+    return $this->languageManager->isMultilingual();
   }
 
   /**
@@ -36,7 +77,7 @@ public function build() {
     $build = array();
     $path = drupal_is_front_page() ? '<front>' : current_path();
     list(, $type) = explode(':', $this->getPluginId());
-    $links = language_negotiation_get_switch_links($type, $path);
+    $links = $this->languageManager->getLanguageNegotiationSwitchLinks($type, $path);
 
     if (isset($links->links)) {
       $build = array(
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php
index a369484..4fc8b42 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php
@@ -9,6 +9,8 @@
 
 use Drupal\simpletest\WebTestBase;
 use Drupal\Core\Language\Language;
+use Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationBrowser;
+use Symfony\Component\HttpFoundation\Request;
 
 /**
  * Test browser language detection.
@@ -153,9 +155,11 @@ function testLanguageFromBrowser() {
       'zh-cht' => 'zh-hant',
     );
 
+    $mappings = $this->container->get('config.factory')->get('language.mappings')->get();
+    $language_neg = new LanguageNegotiationBrowser(array('browser' => array('mappings' => $mappings)));
     foreach ($test_cases as $accept_language => $expected_result) {
-      $_SERVER['HTTP_ACCEPT_LANGUAGE'] = $accept_language;
-      $result = language_from_browser($languages);
+      $request = Request::create('', 'GET', array(), array(), array(), array('HTTP_ACCEPT_LANGUAGE' => $accept_language));
+      $result = $language_neg->negotiateLanguage($languages, $request);
       $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/LanguageConfigurationElementTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationElementTest.php
index d5a3c02..878f497 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationElementTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationElementTest.php
@@ -85,6 +85,7 @@ public function testDefaultLangcode() {
     $langcode = language_get_default_langcode('custom_type', 'custom_bundle');
     $language_interface = language(Language::TYPE_INTERFACE);
     $this->assertEqual($langcode, $language_interface->id);
+    drupal_rebuild_language_negotiation_settings();
 
     // Site's default.
     $old_default = language_default();
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php
index 1fb8c85..e42d271 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php
@@ -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()->getLanguageList(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/LanguageListTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageListTest.php
index 8b2190d..29b75f0 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageListTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageListTest.php
@@ -117,14 +117,14 @@ function testLanguageList() {
     $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');
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php
index 6cff106..bf25a46 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\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUI;
 use Drupal\simpletest\WebTestBase;
 
 /**
@@ -62,7 +63,7 @@ function testInfoAlterations() {
     // 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(
@@ -96,7 +97,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 (\Drupal::languageManager()->getLanguageTypes() 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 +108,7 @@ function testInfoAlterations() {
     $this->languageNegotiationUpdate('uninstall');
 
     // Check that only the core language types are available.
-    foreach (language_types_get_all() as $type) {
+    foreach (\Drupal::languageManager()->getLanguageTypes() as $type) {
       $this->assertTrue(strpos($type, 'test') === FALSE, format_string('The %type language is still available', array('%type' => $type)));
     }
 
@@ -139,15 +140,13 @@ 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->drupalGet('admin/config/regional/language/detection');
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php
index e87a550..0bcebb0 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php
@@ -7,6 +7,11 @@
 
 namespace Drupal\language\Tests;
 
+use Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationBrowser;
+use Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationSelected;
+use Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
+use Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUser;
+use Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUserAdmin;
 use Drupal\simpletest\WebTestBase;
 use Drupal\Core\Language\Language;
 use Symfony\Component\HttpFoundation\Request;
@@ -53,7 +58,7 @@ class LanguageUILanguageNegotiationTest extends WebTestBase {
   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',
     );
   }
@@ -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,100 +147,99 @@ 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.',
     );
-    $this->runTest($test);
+    $this->assertNegotation($test);
 
     // An invalid language is selected.
     \Drupal::config('language.negotiation')->set('selected_langcode', NULL)->save();
+    drupal_rebuild_language_negotiation_settings();
     $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' => LanguageNegotiationSelected::METHOD_ID,
       'http_header' => $http_header_browser_fallback,
       'message' => 'SELECTED > DEFAULT: UI language is switched based on selected language.',
     );
-    $this->runTest($test);
+    $this->assertNegotation($test);
 
     // No selected language is available.
     \Drupal::config('language.negotiation')->set('selected_langcode', $langcode_unknown)->save();
+    drupal_rebuild_language_negotiation_settings();
     $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' => LanguageNegotiationSelected::METHOD_ID,
       'http_header' => $http_header_browser_fallback,
       'message' => 'SELECTED > DEFAULT: UI language is switched based on selected language.',
     );
-    $this->runTest($test);
+    $this->assertNegotation($test);
 
     $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' => LanguageNegotiationSelected::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' => LanguageNegotiationSelected::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',
       ),
     );
 
     foreach ($tests as $test) {
-      $this->runTest($test);
+      $this->assertNegotation($test);
     }
 
     // Unknown language prefix should return 404.
-    variable_set('language_negotiation_' . Language::TYPE_INTERFACE, language_language_negotiation_info());
+    variable_set('language_negotiation_' . Language::TYPE_INTERFACE, array_keys(language_negotiation_info()));
     $this->drupalGet("$langcode_unknown/admin/config", array(), $http_header_browser_fallback);
     $this->assertResponse(404, "Unknown language path prefix should return 404");
 
@@ -245,14 +249,14 @@ 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' => LanguageNegotiationSelected::METHOD_ID,
       'http_header' => array(),
       'message' => 'USER > DEFAULT: no preferred user language setting, the UI language is default',
     );
-    $this->runTest($test);
+    $this->assertNegotation($test);
 
     // Set preferred langcode for user to unknown language.
     $account = $this->loggedInUser;
@@ -260,108 +264,74 @@ 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' => LanguageNegotiationSelected::METHOD_ID,
       'http_header' => array(),
       'message' => 'USER > DEFAULT: invalid preferred user language setting, the UI language is default',
     );
-    $this->runTest($test);
+    $this->assertNegotation($test);
 
     // Set preferred langcode for user to non default.
     $account->preferred_langcode = $langcode;
     $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',
     );
-    $this->runTest($test);
+    $this->assertNegotation($test);
 
     // Set preferred admin langcode for user to NULL.
     $account->preferred_admin_langcode = NULL;
     $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' => LanguageNegotiationSelected::METHOD_ID,
       'http_header' => array(),
       'message' => 'USER ADMIN > DEFAULT: no preferred user admin language setting, the UI language is default',
     );
-    $this->runTest($test);
+    $this->assertNegotation($test);
 
     // Set preferred admin langcode for user to unknown language.
     $account->preferred_admin_langcode = $langcode_unknown;
     $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' => LanguageNegotiationSelected::METHOD_ID,
       'http_header' => array(),
       'message' => 'USER ADMIN > DEFAULT: invalid preferred user admin language setting, the UI language is default',
     );
-    $this->runTest($test);
+    $this->assertNegotation($test);
 
     // Set preferred admin langcode for user to non default.
     $account->preferred_admin_langcode = $langcode;
     $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);
+    $this->assertNegotation($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 $_SERVER['HTTP_HOST'] 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) {
+  protected function assertNegotation($test) {
     if (!empty($test['language_negotiation'])) {
       $method_weights = array_flip($test['language_negotiation']);
       language_negotiation_set(Language::TYPE_INTERFACE, $method_weights);
@@ -370,6 +340,7 @@ protected function runTest($test) {
       \Drupal::config('language.negotiation')
         ->set('url.source', $test['language_negotiation_url_part'])
         ->save();
+      drupal_rebuild_language_negotiation_settings();
     }
     if (!empty($test['language_test_domain'])) {
       \Drupal::state()->set('language_test.domain', $test['language_test_domain']);
@@ -448,7 +419,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 07ee9a9..5c9b91c 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\Core\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/language_elements_test/language_elements_test.info.yml b/core/modules/language/tests/language_elements_test/language_elements_test.info.yml
index 254c5f6..9fc44c1 100644
--- a/core/modules/language/tests/language_elements_test/language_elements_test.info.yml
+++ b/core/modules/language/tests/language_elements_test/language_elements_test.info.yml
@@ -4,4 +4,4 @@ description: 'Support module for the language form elements tests.'
 core: 8.x
 package: Testing
 version: VERSION
-hidden: true
+#hidden: true
diff --git a/core/modules/language/tests/language_test/language_test.module b/core/modules/language/tests/language_test/language_test.module
index 33c2b36..3b6669c 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\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUI;
 
 /**
  * Implements hook_page_build().
@@ -56,38 +54,11 @@ function language_test_language_types_info_alter(array &$language_types) {
 }
 
 /**
- * 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]);
   }
 }
 
@@ -96,16 +67,9 @@ function language_test_language_negotiation_info_alter(array &$negotiation_info)
  */
 function language_test_store_language_negotiation() {
   $last = array();
-  print_r(language_types_get_all());
-  foreach (language_types_get_all() as $type) {
+  print_r(Drupal::languageManager()->getLanguageTypes());
+  foreach (Drupal::languageManager()->getLanguageTypes() as $type) {
     $last[$type] = language($type)->id;
   }
   \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 991755e..0000000
--- 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')) {
-      $_SERVER['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 c8b1633..0000000
--- a/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestServiceProvider.php
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\language_test\LanguageTestServiceProvider.
- */
-
-namespace Drupal\language_test;
-
-use Drupal\Core\DependencyInjection\ContainerBuilder;
-use Drupal\Core\DependencyInjection\ServiceModifierInterface;
-use Drupal\Core\DependencyInjection\ServiceProviderInterface;
-
-/**
- * Defines the LanguageTest service provider.
- */
-class LanguageTestServiceProvider implements ServiceProviderInterface, ServiceModifierInterface {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function register(ContainerBuilder $container) {
-
-  }
-
-  /**
-   * {@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 0000000..226e1fe
--- /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\Core\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 negotiateLanguage(array $languages, 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 0000000..cef125c
--- /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 45e81c2..7fce2d9 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 c33da33..da63c72 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) {
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php
index 2b53ff3..d5cdd7f 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',
@@ -86,7 +85,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}");
@@ -97,7 +95,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);
@@ -154,13 +152,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}");
@@ -224,7 +221,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 06b6ed4..ee2f9a3 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationUiTest.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationUiTest.php
@@ -230,7 +230,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 cd8ec73..98e8573 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php
@@ -7,6 +7,8 @@
 
 namespace Drupal\locale\Tests;
 
+use Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationSelected;
+use Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
 use Drupal\simpletest\WebTestBase;
 use Drupal\Core\Language\Language;
 use Drupal\Component\Utility\String;
@@ -90,14 +92,14 @@ 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());
+    \Drupal::config('system.language.types')->set('configurable', $this->container->get('language_manager')->getTypeDefaults() + array('language_custom' => TRUE))->save();
+    variable_set('language_negotiation_' . Language::TYPE_INTERFACE, language_negotiation_info());
+    variable_set('language_negotiation_' . Language::TYPE_CONTENT, language_negotiation_info());
+    variable_set('language_negotiation_' . Language::TYPE_URL, language_negotiation_info());
 
     // 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();
 
@@ -119,12 +121,12 @@ function testUninstallProcess() {
 
     // 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(count($this->container->get('language_manager')->getLanguageTypes()) == count($this->container->get('language_manager')->getTypeDefaults()), 'Language types reset');
+    $language_negotiation = language_negotiation_method_get_first(Language::TYPE_INTERFACE) == LanguageNegotiationSelected::METHOD_ID;
     $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;
+    $language_negotiation = language_negotiation_method_get_first(Language::TYPE_CONTENT) == LanguageNegotiationSelected::METHOD_ID;
     $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;
+    $language_negotiation = language_negotiation_method_get_first(Language::TYPE_URL) == LanguageNegotiationSelected::METHOD_ID;
     $this->assertTrue($language_negotiation, String::format('URL language negotiation: %setting', array('%setting' => $language_negotiation ? 'none' : 'set')));
 
     // Check language negotiation method settings.
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateBase.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateBase.php
index d61128d..95c202e 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateBase.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateBase.php
@@ -82,7 +82,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 1a53771..4199ec7 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 34c4f91..d1a77d7 100644
--- a/core/modules/locale/locale.install
+++ b/core/modules/locale/locale.install
@@ -6,6 +6,7 @@
  */
 
 use Drupal\Core\Language\Language;
+use Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationSelected;
 
 /**
  * Implements hook_install().
@@ -845,13 +846,9 @@ 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);
   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);
   }
@@ -859,8 +856,8 @@ function locale_update_8013() {
   $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';
+      $negotiation_interface[LanguageNegotiationSelected::METHOD_ID] = $negotiation_interface['language-default'];
+      $negotiation_interface[LanguageNegotiationSelected::METHOD_ID]['callbacks']['negotiation'] = 'language_from_selected';
       unset($negotiation_interface['language-default']);
       update_variable_set('language_negotiation_language_interface', $negotiation_interface);
     }
diff --git a/core/modules/node/lib/Drupal/node/Tests/NodeFieldMultilingualTestCase.php b/core/modules/node/lib/Drupal/node/Tests/NodeFieldMultilingualTestCase.php
index e6626ca..e5216b3 100644
--- a/core/modules/node/lib/Drupal/node/Tests/NodeFieldMultilingualTestCase.php
+++ b/core/modules/node/lib/Drupal/node/Tests/NodeFieldMultilingualTestCase.php
@@ -99,7 +99,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));
+    language_negotiation_set(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/overlay/overlay.module b/core/modules/overlay/overlay.module
index 912e29d..2e67a93 100644
--- a/core/modules/overlay/overlay.module
+++ b/core/modules/overlay/overlay.module
@@ -5,6 +5,7 @@
  * Displays the Drupal administration interface in an overlay.
  */
 
+use Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
 use Drupal\Core\Template\RenderWrapper;
 use Symfony\Component\HttpFoundation\RedirectResponse;
 use Symfony\Component\HttpFoundation\Response;
@@ -503,7 +504,7 @@ function overlay_overlay_parent_initialize() {
   $path_prefixes = array();
   if (module_exists('language')) {
     language_negotiation_include();
-    if (\Drupal::config('language.negotiation')->get('url.source') == LANGUAGE_NEGOTIATION_URL_PREFIX) {
+    if (\Drupal::config('language.negotiation')->get('url.source') == LanguageNegotiationUrl::CONFIG_PATH_PREFIX) {
       // Skip the empty string indicating the default language. We always accept
       // paths without a prefix.
       $path_prefixes = language_negotiation_url_prefixes();
diff --git a/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php b/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php
index 63ebf66..19e6f64 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/system/language.api.php b/core/modules/system/language.api.php
index cfa42ec..e7328d3 100644
--- a/core/modules/system/language.api.php
+++ b/core/modules/system/language.api.php
@@ -49,7 +49,7 @@ function hook_language_switch_links_alter(array &$links, $type, $path) {
  *   - 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
+ *     present and locked is TRUE then language-interface will be
  *     used.
  *
  *  @todo Rename the 'fixed' key to something more meaningful, for instance
@@ -88,55 +88,6 @@ function hook_language_types_info_alter(array &$language_types) {
 }
 
 /**
- * Define language negotiation methods.
- *
- * @return
- *   An associative array of language negotiation method definitions. The keys
- *   are method identifiers, and the values are associative arrays definining
- *   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
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 34e3889..46e95bb 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 48d1e1f..8ce7d1a 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/lib/Drupal/system/Tests/Upgrade/LanguageUpgradePathTest.php b/core/modules/system/lib/Drupal/system/Tests/Upgrade/LanguageUpgradePathTest.php
index df27013..358a2f9 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Upgrade/LanguageUpgradePathTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Upgrade/LanguageUpgradePathTest.php
@@ -10,6 +10,7 @@
 use Drupal\Core\Database\DatabaseException;
 
 use Drupal\Core\Language\Language;
+use Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationSelected;
 
 /**
  * Tests upgrading a filled database with language data.
@@ -126,14 +127,9 @@ public function testLanguageUpgrade() {
     $this->assertTrue(isset($current_weights['language-selected']), 'Language-selected is present.');
     $this->assertFalse(isset($current_weights['language-default']), 'Language-default is not present.');
 
-    // @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';
-
     // Check that negotiation callback was added to language_negotiation_language_interface.
     $language_negotiation_language_interface = update_variable_get('language_negotiation_language_interface', NULL);
-    $this->assertTrue(isset($language_negotiation_language_interface[LANGUAGE_NEGOTIATION_SELECTED]['callbacks']['negotiation']), 'Negotiation callback was added to language_negotiation_language_interface.');
+    $this->assertTrue(isset($language_negotiation_language_interface[LanguageNegotiationSelected::METHOD_ID]['callbacks']['negotiation']), 'Negotiation callback was added to language_negotiation_language_interface.');
 
     // Look up migrated plural string.
     $source_string = db_query('SELECT * FROM {locales_source} WHERE lid = 22')->fetchObject();
diff --git a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermLanguageTest.php b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermLanguageTest.php
index 6cac09c..ab02d53 100644
--- a/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermLanguageTest.php
+++ b/core/modules/taxonomy/lib/Drupal/taxonomy/Tests/TermLanguageTest.php
@@ -79,6 +79,9 @@ function testTermLanguage() {
   }
 
   function testDefaultTermLanguage() {
+    // @todo: Why is this necessary?
+    drupal_rebuild_language_negotiation_settings();
+
     // Configure the vocabulary to not hide the language selector, and make the
     // default language of the terms fixed.
     $edit = array(
diff --git a/core/modules/tour/lib/Drupal/tour/Tests/TourTest.php b/core/modules/tour/lib/Drupal/tour/Tests/TourTest.php
index fcae023..e8277c6 100644
--- a/core/modules/tour/lib/Drupal/tour/Tests/TourTest.php
+++ b/core/modules/tour/lib/Drupal/tour/Tests/TourTest.php
@@ -108,6 +108,7 @@ public function testTourFunctionality() {
     // Enable Italian language and navigate to it/tour-test1 and verify italian
     // version of tip is found.
     language_save(new Language(array('id' => 'it')));
+    drupal_rebuild_language_negotiation_settings();
     $this->drupalGet('it/tour-test-1');
 
     $elements = $this->xpath('//li[@data-id=:data_id and ./h2[contains(., :text)]]', array(
diff --git a/core/modules/user/lib/Drupal/user/AccountFormController.php b/core/modules/user/lib/Drupal/user/AccountFormController.php
index b9fe6e3..35c749e 100644
--- a/core/modules/user/lib/Drupal/user/AccountFormController.php
+++ b/core/modules/user/lib/Drupal/user/AccountFormController.php
@@ -9,6 +9,7 @@
 
 use Drupal\Core\Entity\ContentEntityFormController;
 use Drupal\Core\Language\Language;
+use Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationSelected;
 
 /**
  * Form controller for the user account forms.
@@ -183,7 +184,7 @@ public function form(array $form, array &$form_state) {
 
     // 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;
+    $interface_language_is_default = language_negotiation_method_get_first(Language::TYPE_INTERFACE) != LanguageNegotiationSelected::METHOD_ID;
     $form['language'] = array(
       '#type' => language_multilingual() ? 'details' : 'container',
       '#title' => $this->t('Language settings'),
diff --git a/core/tests/Drupal/Tests/Core/Language/LanguageNegotiationUrlTest.php b/core/tests/Drupal/Tests/Core/Language/LanguageNegotiationUrlTest.php
new file mode 100644
index 0000000..3188853
--- /dev/null
+++ b/core/tests/Drupal/Tests/Core/Language/LanguageNegotiationUrlTest.php
@@ -0,0 +1,132 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Tests\Core\Language\LanguageNegotiationUrlTest.
+ */
+
+namespace Drupal\Tests\Core\Language;
+
+use Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUrl;
+use Drupal\Tests\UnitTestCase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Tests the URL and domain language negotiation.
+ *
+ * @group Language
+ *
+ * @see \Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUrl
+ */
+class LanguageNegotiationUrlTest extends UnitTestCase {
+
+  public static function getInfo() {
+    return array(
+      'name' => 'Language negotiation URL',
+      'description' => 'Tests the URL/domain Language negotiation plugin',
+      'group' => 'Language',
+    );
+  }
+
+  /**
+   * Test domain language negotiation.
+   *
+   * @dataProvider providerTestDomain
+   */
+  public function testDomain($http_host, $domains, $languages, $expected_langcode) {
+    $config = array(
+      'url' => array(
+        'source' => LanguageNegotiationUrl::CONFIG_DOMAIN,
+        'domains' => $domains,
+      ),
+    );
+
+    $request = Request::create('', 'GET', array(), array(), array(), array('HTTP_HOST' => $http_host));
+    $negotiation = new LanguageNegotiationUrl($config);
+    $this->assertEquals($expected_langcode, $negotiation->negotiateLanguage($languages, $request));
+  }
+
+  /**
+   * Provides data for the domain test.
+   *
+   * @return array
+   *   An array of data for checking domain negotation.
+   */
+  public function providerTestDomain() {
+    $default_languages = array(
+      'de' => (object) array(
+        'id' => 'de',
+      ),
+      'en' => (object) array(
+        'id' => 'en',
+      ),
+    );
+
+    $domain_configuration[] = array(
+      'http_host' => 'example.de',
+      'domains' => array(
+        'de' => 'http://example.de',
+      ),
+      'languages' => $default_languages,
+      'expected_langocde' => 'de',
+    );
+    // No configuration.
+    $domain_configuration[] = array(
+      'http_host' => 'example.de',
+      'domains' => array(),
+      'languages' => $default_languages,
+      'expected_langocde' => FALSE,
+    );
+    // HTTP host with a port.
+    $domain_configuration[] = array(
+      'http_host' => 'example.de:8080',
+      'domains' => array(
+        'de' => 'http://example.de',
+      ),
+      'languages' => $default_languages,
+      'expected_langocde' => 'de',
+    );
+    // Domain configuration with https://.
+    $domain_configuration[] = array(
+      'http_host' => 'example.de',
+      'domains' => array(
+        'de' => 'https://example.de',
+      ),
+      'languages' => $default_languages,
+      'expected_langocde' => 'de',
+    );
+    // Non-matching HTTP host.
+    $domain_configuration[] = array(
+      'http_host' => 'example.com',
+      'domains' => array(
+        'de' => 'http://example.com',
+      ),
+      'languages' => $default_languages,
+      'expected_langocde' => 'de',
+    );
+    // Testing a non-existing language.
+    $domain_configuration[] = array(
+      'http_host' => 'example.com',
+      'domains' => array(
+        'de' => 'http://example.com',
+      ),
+      'languages' => array(
+        'en' => (object) array(
+          'id' => 'en',
+        ),
+      ),
+      'expected_langocde' => FALSE,
+    );
+    // Multiple domain configurations.
+    $domain_configuration[] = array(
+      'http_host' => 'example.com',
+      'domains' => array(
+        'de' => 'http://example.de',
+        'en' => 'http://example.com',
+      ),
+      'languages' => $default_languages,
+      'expected_langocde' => 'en',
+    );
+    return $domain_configuration;
+  }
+}
diff --git a/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php b/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php
index f49184f..b29aa18 100644
--- a/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php
+++ b/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php
@@ -46,10 +46,15 @@ public function setUp() {
     $this->languages = $languages;
 
     // Create a language manager stub.
-    $language_manager = $this->getMock('Drupal\Core\Language\LanguageManager');
+    $language_manager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager')
+      ->disableOriginalConstructor()
+      ->getMock();
     $language_manager->expects($this->any())
       ->method('getLanguage')
       ->will($this->returnValue($languages['en']));
+    $language_manager->expects($this->any())
+      ->method('getLanguageList')
+      ->will($this->returnValue($this->languages));
 
     $this->languageManager = $language_manager;
   }
@@ -95,7 +100,7 @@ function testProcessInbound() {
     $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);
 
     // 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 3e86b66..0e60267 100644
--- a/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
@@ -113,7 +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->getMockBuilder('Drupal\Core\Language\LanguageManager')
+      ->disableOriginalConstructor()
+      ->getMock();
     $language_manager->expects($this->once())
       ->method('getLanguage')
       ->with(Language::TYPE_INTERFACE)
@@ -144,7 +146,9 @@ public function testDefaultPluginManagerWithFilledCache() {
       ->method('set');
 
     $language = new Language(array('id' => 'en'));
-    $language_manager = $this->getMock('Drupal\Core\Language\LanguageManager');
+    $language_manager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager')
+      ->disableOriginalConstructor()
+      ->getMock();
     $language_manager->expects($this->once())
       ->method('getLanguage')
       ->with(Language::TYPE_INTERFACE)
@@ -173,7 +177,9 @@ public function testCacheClearWithTags() {
       ->method('deleteMultiple');
 
     $language = new Language(array('id' => 'en'));
-    $language_manager = $this->getMock('Drupal\Core\Language\LanguageManager');
+    $language_manager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager')
+      ->disableOriginalConstructor()
+      ->getMock();
     $language_manager->expects($this->once())
       ->method('getLanguage')
       ->with(Language::TYPE_INTERFACE)
diff --git a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php
index 6b7334f..3e6c72f 100644
--- a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php
+++ b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php
@@ -79,7 +79,10 @@ 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->getMockBuilder('Drupal\Core\Language\LanguageManager')
+      ->disableOriginalConstructor()
+      ->getMock();
+
 
     $this->linkGenerator = new LinkGenerator($this->urlGenerator, $this->moduleHandler, $this->languageManager);
   }
