diff --git a/core/core.services.yml b/core/core.services.yml
index da2d413..471f22b 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -185,8 +185,6 @@ services:
   http_kernel:
     class: Drupal\Core\HttpKernel
     arguments: ['@event_dispatcher', '@service_container', '@controller_resolver']
-  language_manager:
-    class: Drupal\Core\Language\LanguageManager
   string_translator.custom_strings:
     class: Drupal\Core\StringTranslation\Translator\CustomStrings
     tags:
diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index d9f44da..6409213 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -2435,10 +2435,7 @@ function language_types_get_default() {
  *   TRUE if more than one language is enabled.
  */
 function language_multilingual() {
-  // The "language_count" variable stores the number of enabled languages to
-  // avoid unnecessarily querying the database when building the list of
-  // enabled languages on monolingual sites.
-  return variable_get('language_count', 1) > 1;
+  return Drupal::languageManager()->isMultilingual();
 }
 
 /**
@@ -2454,65 +2451,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 = 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);
 }
 
 /**
@@ -2526,24 +2465,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);
 }
 
 /**
@@ -2604,15 +2526,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 4829751..f835bd1 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -4689,6 +4689,14 @@ function drupal_flush_all_caches() {
 }
 
 /**
+ * Rebuild the container to store updated language negotiation settings.
+ */
+function drupal_rebuild_language_negotiation_settings() {
+  cache()->delete('language_negotiation_plugins');
+  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 eefe926..f111e45 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -9,6 +9,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');
 
@@ -386,7 +395,11 @@ function install_begin_request(&$install_state) {
       ->addArgument(new Reference('config.context'));
 
     // Register the 'language_manager' service.
-    $container->register('language_manager', 'Drupal\Core\Language\LanguageManager');
+    $container->register('plugin.manager.language_negotiation_method', 'Drupal\Core\Language\LanguageNegotiationMethodManager')
+      ->addArgument(new Reference('container.namespaces'));
+    $container->register('language_manager', 'Drupal\Core\Language\LanguageManager')
+      ->addArgument(array())
+      ->addArgument(new Reference('plugin.manager.language_negotiation_method'));
 
     // Register the translation services.
     install_register_translation_service($container);
@@ -1572,7 +1585,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 5f7d823..05da66e 100644
--- a/core/includes/language.inc
+++ b/core/includes/language.inc
@@ -113,42 +113,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
@@ -436,53 +400,6 @@ function language_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'];
-    }
-    // If the language negotiation method has no cache preference or this is
-    // satisfied we can execute the callback.
-    $cache = !isset($method['cache']) || $user->uid || $method['cache'] == variable_get('cache', 0);
-    $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.
   *
@@ -502,39 +419,6 @@ function language_from_selected($languages) {
 }
 
 /**
- * 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);
-}
-
-/**
  * Returns the possible fallback languages ordered by language weight.
  *
  * @param
diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php
index c3b6994..cdc6731 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\RegisterMatchersPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterPathProcessorsPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterRouteFiltersPass;
@@ -51,6 +52,7 @@ public function register(ContainerBuilder $container) {
     $container->addScope(new Scope('request'));
     $this->registerTwig($container);
     $this->registerModuleHandler($container);
+    $this->registerLanguage($container);
 
     $container->addCompilerPass(new RegisterMatchersPass());
     $container->addCompilerPass(new RegisterRouteFiltersPass());
@@ -135,4 +137,22 @@ public static function registerTwig(ContainerBuilder $container) {
       ->addMethodCall('addExtension', array(new Definition('Twig_Extension_Debug')));
   }
 
+  /**
+   * 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'));
+    $container->register('language_manager', 'Drupal\Core\Language\LanguageManager')
+      ->addArgument($config)
+      ->addArgument(new Reference('plugin.manager.language_negotiation_method'));
+   }
+
 }
diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php
index af1fe52..bf61aad 100644
--- a/core/lib/Drupal/Core/DrupalKernel.php
+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -524,6 +524,8 @@ protected function buildContainer() {
     $container->register('class_loader', 'Symfony\Component\ClassLoader\ClassLoader')->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/Language.php b/core/lib/Drupal/Core/Language/Language.php
index 8296c18..ff3e394 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 a17ea10..8db8806 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;
 
 /**
@@ -15,11 +16,9 @@
 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';
 
   /**
    * An array of language objects keyed by language type.
@@ -29,6 +28,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
@@ -38,14 +65,24 @@ 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 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;
+  }
+
+  /**
    * Initializes each language type to a language object.
    */
   public function init() {
@@ -53,9 +90,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;
   }
@@ -87,31 +126,109 @@ 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 retireve 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 array_keys(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'])) {
+      // If the language negotiation method has no cache preference or this is
+      // satisfied we can execute the callback.
+      $cache = !isset($method['cache']) || $user->uid || $method['cache'] == config('system.performance')->get('cache.page.use_internal');
+      if ($cache) {
+        $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
@@ -123,6 +240,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]);
@@ -136,16 +254,21 @@ public function reset($type = NULL) {
    *   TRUE if more than one language is enabled, FALSE otherwise.
    */
   public function isMultilingual() {
+    // The "language_count" variable stores the number of enabled languages to
+    // avoid unnecessarily querying the database when building the list of
+    // enabled languages on monolingual sites.
+    // @todo convert to CMI https://drupal.org/node/1856976
     return variable_get('language_count', 1) > 1;
   }
 
   /**
    * Returns an array of the available language types.
    *
-   * @return array()
+   * @return array
    *   An array of all language types.
    */
   protected function getLanguageTypes() {
+    // @todo convert to CMI https://drupal.org/node/1827038
     return language_types_get_all();
   }
 
@@ -155,16 +278,90 @@ 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;
+  }
+
+  /**
+   * 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;
   }
 
   /**
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..380af4c
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/LanguageNegotiationMethodManager.php
@@ -0,0 +1,28 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\LanguageNegotiationMethodManager.
+ */
+
+namespace Drupal\Core\Language;
+
+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) {
+    parent::__construct('LanguageNegotiation', $namespaces);
+  }
+
+}
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..8e8ce12
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php
@@ -0,0 +1,150 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationBrowser.
+ */
+
+namespace Drupal\Core\Language\Plugin\LanguageNegotiation;
+
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+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 = 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;
+        $browser_langcodes[$langcode] = (int) ($qvalue * 1000);
+      }
+    }
+
+    // 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..8d70508
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationSelected.php
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationSelected.
+ */
+
+namespace Drupal\Core\Language\Plugin\LanguageNegotiation;
+
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class for identifying language from a selected language.
+ *
+ * @Plugin(
+ *   id = 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;
+    return $langcode != 'site_default' ? $langcode : language_default()->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..3fb04ce
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationSession.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationSession.
+ */
+
+namespace Drupal\Core\Language\Plugin\LanguageNegotiation;
+
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Identify language from a request/session parameter.
+ *
+ * @Plugin(
+ *   id = 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->uid && isset($languages[$langcode])) {
+        $_SESSION[$param] = $langcode;
+      }
+    }
+
+    // Session parameter.
+    if (isset($_SESSION[$param])) {
+      return $_SESSION[$param];
+    }
+
+    return FALSE;
+  }
+
+}
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..77ce0b1
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUI.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUI.
+ */
+
+namespace Drupal\Core\Language\Plugin\LanguageNegotiation;
+
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Language\Language;
+use Drupal\Core\Language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class for identifying the language from the current interface language.
+ *
+ * @Plugin(
+ *   id = LanguageNegotiationUI::METHOD_ID,
+ *   types = {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(Language::TYPE_INTERFACE)->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..02147bc
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php
@@ -0,0 +1,100 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUrl.
+ */
+
+namespace Drupal\Core\Language\Plugin\LanguageNegotiation;
+
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class for identifying language via URL prefix or domain.
+ *
+ * @Plugin(
+ *   id = LanguageNegotiationUrl::METHOD_ID,
+ *   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;
+  }
+
+}
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..ee43456
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUrlFallback.php
@@ -0,0 +1,74 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUrlFallback.
+ */
+
+namespace Drupal\Core\Language\Plugin\LanguageNegotiation;
+
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Language\Language;
+use Drupal\Core\Language\LanguageManager;
+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 = LanguageNegotiationUrlFallback::METHOD_ID,
+ *   types = {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(Language::TYPE_INTERFACE)->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..ba9741d
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUser.php
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUrl.
+ */
+
+namespace Drupal\Core\Language\Plugin\LanguageNegotiation;
+
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class for identifying language from the user preferences.
+ *
+ * @Plugin(
+ *   id = 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).
+    global $user;
+
+    if ($user->uid && !empty($user->preferred_langcode) && isset($languages[$user->preferred_langcode])) {
+      return $user->preferred_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..0f2d7a0
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php
@@ -0,0 +1,51 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUserAdmin.
+ */
+
+namespace Drupal\Core\Language\Plugin\LanguageNegotiation;
+
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+use Drupal\Core\Language\Language;
+use Drupal\Core\Language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Identifies admin language from the user preferences.
+ *
+ * @Plugin(
+ *   id = LanguageNegotiationUserAdmin::METHOD_ID,
+ *   types = {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).
+    global $user;
+
+    // @todo Avoid calling _current_path() and path_is_admin() directly.
+    $request_path = $request ? urldecode(trim($request->getPathInfo(), '/')) : _current_path();
+    if ($user->uid && !empty($user->preferred_admin_langcode) && isset($languages[$user->preferred_admin_langcode]) && path_is_admin($request_path)) {
+      return $user->preferred_admin_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 69d5ccd..c7dbdc3 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->drupalpost('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 47efe82..a8e2bd9 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php
@@ -81,7 +81,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/language/language.admin.inc b/core/modules/language/language.admin.inc
index aca6911..bc0e2bb 100644
--- a/core/modules/language/language.admin.inc
+++ b/core/modules/language/language.admin.inc
@@ -14,7 +14,6 @@
  * User interface for the language overview screen.
  */
 function language_admin_overview_form($form, &$form_state) {
-  drupal_static_reset('language_list');
   $languages = language_list();
   $default = language_default();
 
@@ -255,6 +254,7 @@ function language_admin_add_form_submit($form, &$form_state) {
   }
   // Save the language and inform the user that it happened.
   $language = language_save($language);
+  drupal_rebuild_language_negotiation_settings();
   drupal_set_message(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
@@ -604,6 +604,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.'));
 }
@@ -786,6 +789,8 @@ function language_negotiation_configure_browser_form_submit($form, &$form_state)
   if (!empty($mappings)) {
     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';
 }
 
diff --git a/core/modules/language/language.module b/core/modules/language/language.module
index b26105f..03f353f 100644
--- a/core/modules/language/language.module
+++ b/core/modules/language/language.module
@@ -522,15 +522,15 @@ function language_save($language) {
     variable_set('language_default', (array) $language);
   }
 
+  // Reset the language information.
+  Drupal::languageManager()->reset();
+
   // Update language count based on unlocked language count.
   language_update_count();
 
   // Update weight of locked system languages.
   language_update_locked_weights();
 
-  // Kill the static cache in language_list().
-  drupal_static_reset('language_list');
-
   language_negotiation_include();
 
   // Update URL Prefixes for all languages after the new default language is
@@ -581,7 +581,7 @@ function language_delete($langcode) {
     // Update weight of locked system languages.
     language_update_locked_weights();
 
-    drupal_static_reset('language_list');
+    Drupal::languageManager()->reset();
 
     $t_args = array('%language' => $language->name, '%langcode' => $language->id);
     watchdog('language', 'The %language (%langcode) language has been removed.', $t_args);
@@ -879,4 +879,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 78b9d27..14db4b8 100644
--- a/core/modules/language/language.negotiation.inc
+++ b/core/modules/language/language.negotiation.inc
@@ -50,324 +50,6 @@
 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;
-      $browser_langcodes[$langcode] = (int) ($qvalue * 1000);
-    }
-  }
-
-  // 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->uid && !empty($user->preferred_langcode) && isset($languages[$user->preferred_langcode])) {
-    return $user->preferred_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;
-
-  $request_path = $request ? urldecode(trim($request->getPathInfo(), '/')) : _current_path();
-  if ($user->uid && !empty($user->preferred_admin_langcode) && isset($languages[$user->preferred_admin_langcode]) && path_is_admin($request_path)) {
-    return $user->preferred_admin_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 = 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->uid) {
-      $_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 (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 = (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.
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;
+    }
 
-      // 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);
-        }
-      }
+    $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.
+    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/LanguageManager.php b/core/modules/language/lib/Drupal/language/LanguageManager.php
new file mode 100644
index 0000000..07d07e7
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/LanguageManager.php
@@ -0,0 +1,63 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\LanguageManager.
+ */
+
+namespace Drupal\language;
+
+use Drupal\Core\Database\Connection;
+use Drupal\Core\Language\Language;
+use Drupal\Core\Language\LanguageManager as LanguageManagerBase;
+use Drupal\Component\Plugin\PluginManagerInterface;
+
+/**
+ * Overrides default LanguageManager to provide languages from the database.
+ */
+class LanguageManager extends LanguageManagerBase {
+
+  /**
+   * The database connection.
+   *
+   * @var \Drupal\Core\Database\Connection;
+   */
+  protected $database;
+
+  /**
+   * 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\Database\Connection $database
+   *   The database connection.
+   */
+  public function __construct(array $config, PluginManagerInterface $negotiator_manager, Connection $database) {
+    parent::__construct($config, $negotiator_manager);
+    $this->database = $database;
+  }
+
+  /**
+   * {@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_entities = config_get_storage_names_with_prefix('language.entity');
+      foreach ($language_entities as $langcode_config_name) {
+        $langcode = substr($langcode_config_name, strlen('language.entity.'));
+        $info = config($langcode_config_name)->get();
+        $info['default'] = ($langcode == $default->id);
+        $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..0b44425
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/LanguageServiceProvider.php
@@ -0,0 +1,39 @@
+<?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('database'));
+  }
+
+}
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php
index 07ea3d5..126b114 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,10 @@ function testLanguageFromBrowser() {
       'zh-cht' => 'zh-hant',
     );
 
+    $language_neg = new LanguageNegotiationBrowser(array('browser' => array('mappings' => array())));
     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 fcec194..c4437f9 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);
+    $this->resetAll();
 
     // 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 4bce384..d83fc0a 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php
@@ -143,7 +143,7 @@ function testLanguageConfigurationWeight() {
    */
   protected function checkConfigurableLanguageWeight($state = 'by default') {
     // Reset language list.
-    drupal_static_reset('language_list');
+    $this->container->get('language_manager')->reset();
     $max_configurable_language_weight = $this->getHighestConfigurableLanguageWeight();
     $replacements = array('@event' => $state);
     foreach (language_list(Language::STATE_LOCKED) as $locked_language) {
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageListTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageListTest.php
index fa718da..4d8ebd2 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageListTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageListTest.php
@@ -117,13 +117,13 @@ function testLanguageList() {
     $this->drupalGet('admin/config/regional/language/delete/' . $langcode);
     $this->assertResponse(404, 'Language no longer found.');
     // Make sure the "language_count" variable has been updated correctly.
-    drupal_static_reset('language_list');
+    $this->container->get('language_manager')->reset();
     $languages = language_list();
     $this->assertEqual(variable_get('language_count', 1), count($languages), 'Language count is correct.');
     // Delete French.
     $this->drupalPost('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/LanguageUILanguageNegotiationTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php
index 555c198..aee3285 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php
@@ -106,7 +106,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.
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageUrlRewritingTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageUrlRewritingTest.php
index 486c631..8d694a7 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageUrlRewritingTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageUrlRewritingTest.php
@@ -45,9 +45,6 @@ function setUp() {
     // Enable URL language detection and selection.
     $edit = array('language_interface[enabled][language-url]' => 1);
     $this->drupalPost('admin/config/regional/language/detection', $edit, t('Save settings'));
-
-    // Reset static caching.
-    drupal_static_reset('language_list');
   }
 
   /**
@@ -119,7 +116,7 @@ function testDomainNameNegotiationPort() {
       ->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/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/locale/lib/Drupal/locale/Tests/LocaleContentTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php
index b53ba51..8212764 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->drupalPost('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->drupalPost('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}");
@@ -160,7 +158,6 @@ function testContentTypeDirLang() {
     $edit = array();
     $edit['predefined_langcode'] = 'es';
     $this->drupalPost('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->drupalPost('admin/config/regional/language/edit/en', array('locale_translate_english' => TRUE), t('Save language'));
     $this->drupalPost('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 63ef579..c08575c 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationUiTest.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationUiTest.php
@@ -229,7 +229,7 @@ function testJavaScriptTranslation() {
       'direction' => '0',
     );
     $this->drupalPost('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/locale.bulk.inc b/core/modules/locale/locale.bulk.inc
index 8a15656..9b15466 100644
--- a/core/modules/locale/locale.bulk.inc
+++ b/core/modules/locale/locale.bulk.inc
@@ -19,7 +19,7 @@
  * @ingroup forms
  */
 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.pages.inc b/core/modules/locale/locale.pages.inc
index 6326905..8d39b51 100644
--- a/core/modules/locale/locale.pages.inc
+++ b/core/modules/locale/locale.pages.inc
@@ -99,7 +99,7 @@ function locale_translate_filters() {
   $filters = array();
 
   // Get all languages, except English.
-  drupal_static_reset('language_list');
+  Drupal::languageManager()->reset();
   $languages = language_list();
   $language_options = array();
   foreach ($languages as $langcode => $language) {
@@ -255,7 +255,7 @@ function locale_translate_edit_form($form, &$form_state) {
   $filter_values = locale_translate_filter_values();
   $langcode = $filter_values['langcode'];
 
-  drupal_static_reset('language_list');
+  Drupal::languageManager()->reset();
   $languages = language_list();
 
   $langname = isset($langcode) ? $languages[$langcode]->name : "- None -";
diff --git a/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php b/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php
index 291b633..4461774 100644
--- a/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php
+++ b/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php
@@ -96,8 +96,7 @@ function testAliasTranslation() {
 
     // Confirm that the alias is returned by url(). Languages are cached on
     // many levels, and we need to clear those caches.
-    drupal_static_reset('language_list');
-    $this->rebuildContainer();
+    $this->container->get('language_manager')->reset();
     $languages = language_list();
     $url = $this->container->get('url_generator')->generateFromPath('node/' . $french_node->nid, array('language' => $languages[$french_node->langcode]));
 
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 1afeca5..e91eb19 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'];
       }
       variable_set('locale_custom_strings_' . $langcode, array('' => $custom_strings));
+      $this->rebuildContainer();
     }
   }
 
diff --git a/core/modules/translation/lib/Drupal/translation/Tests/TranslationTest.php b/core/modules/translation/lib/Drupal/translation/Tests/TranslationTest.php
index dff34a0..cd6b4ca 100644
--- a/core/modules/translation/lib/Drupal/translation/Tests/TranslationTest.php
+++ b/core/modules/translation/lib/Drupal/translation/Tests/TranslationTest.php
@@ -280,7 +280,6 @@ function testTranslateOwnContentRole() {
    * Resets static caches to make the test code match the client-side behavior.
    */
   function resetCaches() {
-    drupal_static_reset('language_list');
     $this->rebuildContainer();
   }
 
@@ -314,7 +313,7 @@ function addLanguage($langcode) {
       $this->drupalPost('admin/config/regional/language/add', $edit, t('Add language'));
 
       // Make sure we are not using a stale list.
-      drupal_static_reset('language_list');
+      $this->container->get('language_manager')->reset();
       $languages = language_list();
       $this->assertTrue(array_key_exists($langcode, $languages), 'Language was installed successfully.');
 
