diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index fc370bf..4226820 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -2762,10 +2762,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_container()->get('language_manager')->isMultilingual();
 }
 
 /**
@@ -2780,56 +2777,7 @@ function language_multilingual() {
  *   weight ascending and name ascending.
  */
 function language_list($flags = LANGUAGE_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.
-      $languages = db_query('SELECT * FROM {language} ORDER BY weight ASC, name ASC')->fetchAllAssoc('langcode', PDO::FETCH_ASSOC);
-
-      // Initialize default property so callers have an easy reference and can
-      // save the same object without data loss.
-      foreach ($languages as $langcode => $info) {
-        $info['default'] = ($langcode == $default->langcode);
-        $languages[$langcode] = new Language($info);
-      }
-    }
-    else {
-      // No language module, so use the default language only.
-      $languages = array($default->langcode => $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_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_LOCKED)) || (!$language->locked && !($flags & LANGUAGE_CONFIGURABLE))) {
-      continue;
-     }
-    $filtered_languages[$langcode] = $language;
-  }
-
-  return $filtered_languages;
+  return Drupal::service('language_manager')->getLanguageList($flags);
 }
 
 /**
@@ -2843,24 +2791,7 @@ function language_list($flags = LANGUAGE_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_NOT_SPECIFIED] = new Language(array(
-    'langcode' => LANGUAGE_NOT_SPECIFIED,
-    'name' => t('Not specified'),
-    'weight' => ++$weight,
-  ) + $locked_language);
-  $languages[LANGUAGE_NOT_APPLICABLE] = new Language(array(
-    'langcode' => LANGUAGE_NOT_APPLICABLE,
-    'name' => t('Not applicable'),
-    'weight' => ++$weight,
-  ) + $locked_language);
-  return $languages;
+  return drupal_container()->get('language_manager')->getDefaultLockedLanguages($weight);
 }
 
 /**
@@ -2921,33 +2852,8 @@ function language_is_locked($langcode) {
  *   A language object.
  */
 function language_default() {
-  $info = variable_get('language_default', array(
-    'langcode' => 'en',
-    'name' => 'English',
-    'direction' => 0,
-    'weight' => 0,
-    'locked' => 0,
-  ));
-  $info['default'] = TRUE;
-  return new Language($info);
-}
-
-/**
- * Stores or retrieves the path derived during language negotiation.
- *
- * @param string $new_path
- *   The altered path.
- *
- * @todo Replace this with a path processor in language module. See
- *   http://drupal.org/node/1888424.
- */
-function _language_resolved_path($new_path = NULL) {
-  $path = &drupal_static(__FUNCTION__, NULL);
-  if ($new_path === NULL) {
-    return $path;
-  }
-  $path = $new_path;
-  return $path;
+  $default_info = variable_get('language_default', Language::$DEFAULT_VALUES);
+  return new Language($default_info + array('default' => TRUE));
 }
 
 /**
diff --git a/core/includes/common.inc b/core/includes/common.inc
index 617d27b..e14ca8f 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -6464,6 +6464,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 74be642..c928239 100644
--- a/core/includes/install.core.inc
+++ b/core/includes/install.core.inc
@@ -6,6 +6,7 @@
 use Drupal\Core\Database\Database;
 use Drupal\Core\Database\Install\TaskException;
 use Drupal\Core\Language\Language;
+use Drupal\Core\Plugin\Core\LanguageNegotiation\LanguageNegotiationBrowser;
 
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\Reference;
@@ -336,6 +337,12 @@ function install_begin_request(&$install_state) {
     // @todo Move into a proper Drupal\Core\DependencyInjection\InstallContainerBuilder.
     $container = new ContainerBuilder();
 
+    $namespaces = array(
+      'Drupal\Core' => DRUPAL_ROOT . '/core/lib',
+      'Drupal\Component' => DRUPAL_ROOT . '/core/lib',
+    );
+    $container->setParameter('container.namespaces', $namespaces);
+
     $container->register('event_dispatcher', 'Symfony\Component\EventDispatcher\EventDispatcher');
 
     $container->register('config.storage', 'Drupal\Core\Config\InstallStorage');
@@ -351,7 +358,12 @@ function install_begin_request(&$install_state) {
       ->addArgument(new Reference('config.context'));
 
     // Register the 'language_manager' service.
-    $container->register('language_manager', 'Drupal\Core\Language\LanguageManager');
+    $container->register('plugin.manager.language_negotiation_method', 'Drupal\Core\Language\LanguageNegotiationMethodManager')
+      ->addArgument('%container.namespaces%');
+    $container->register('language_manager', 'Drupal\Core\Language\LanguageManager')
+      ->addArgument(array())
+      ->addArgument(new Reference('plugin.manager.language_negotiation_method'));
+
     foreach (array('bootstrap', 'config', 'cache', 'menu', 'page', 'path') as $bin) {
       $container
         ->register("cache.$bin", 'Drupal\Core\Cache\MemoryBackend')
@@ -1466,7 +1478,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' => st('Choose language'),
diff --git a/core/includes/language.inc b/core/includes/language.inc
index 74d5752..57eb996 100644
--- a/core/includes/language.inc
+++ b/core/includes/language.inc
@@ -101,41 +101,6 @@
  * @link http://drupal.org/node/1497272 Language Negotiation API @endlink
  */
 
-/**
- * Chooses a language based on language negotiation method settings.
- *
- * @param $type
- *   The language type key to find the language for.
- *
- * @param $request
- *   The HttpReqeust object representing the current request.
- *
- * @return
- *   The negotiated language object.
- */
-function language_types_initialize($type, $request = NULL) {
-  // Execute the language negotiation methods in the order they were set up and
-  // return the first valid language found.
-  $negotiation = variable_get("language_negotiation_$type", array());
-
-  foreach ($negotiation as $method_id => $method) {
-    // Skip negotiation methods not appropriate for this type.
-    if (isset($method['types']) && !in_array($type, $method['types'])) {
-      continue;
-    }
-    $language = language_negotiation_method_invoke($method_id, $method, $request);
-    if ($language) {
-      // Remember the method ID used to detect the language.
-      $language->method_id = $method_id;
-      return $language;
-    }
-  }
-
-  // If no other language was found use the default one.
-  $language = language_default();
-  $language->method_id = LANGUAGE_NEGOTIATION_SELECTED;
-  return $language;
-}
 
 /**
  * Returns information about all defined language types.
@@ -426,53 +391,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.
   *
@@ -492,39 +410,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->langcode]) && $prefixes[$language->langcode] == $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/CoreBundle.php b/core/lib/Drupal/Core/CoreBundle.php
index 8734772..728696d 100644
--- a/core/lib/Drupal/Core/CoreBundle.php
+++ b/core/lib/Drupal/Core/CoreBundle.php
@@ -15,6 +15,7 @@
 use Drupal\Core\DependencyInjection\Compiler\RegisterRouteFiltersPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterRouteEnhancersPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterParamConvertersPass;
+use Drupal\Core\DependencyInjection\Compiler\RegisterLanguageNegotiationPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterServicesForDestructionPass;
 use Symfony\Component\DependencyInjection\ContainerBuilder;
 use Symfony\Component\DependencyInjection\ContainerInterface;
@@ -46,6 +47,7 @@ public function build(ContainerBuilder $container) {
     $container->addScope(new Scope('request'));
     $this->registerTwig($container);
     $this->registerModuleHandler($container);
+    $this->registerLanguage($container);
 
     $container->addCompilerPass(new RegisterMatchersPass());
     $container->addCompilerPass(new RegisterRouteFiltersPass());
@@ -57,6 +59,7 @@ public function build(ContainerBuilder $container) {
     // Add a compiler pass for upcasting of entity route parameters.
     $container->addCompilerPass(new RegisterParamConvertersPass());
     $container->addCompilerPass(new RegisterRouteEnhancersPass());
+
     // Add a compiler pass for registering services needing destruction.
     $container->addCompilerPass(new RegisterServicesForDestructionPass());
     // Add the compiler pass that will process the tagged services.
@@ -116,6 +119,25 @@ public static function registerTwig(ContainerBuilder $container) {
       // @todo Figure out what to do about debugging functions.
       // @see http://drupal.org/node/1804998
       ->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('%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 884c99f..6a63495 100644
--- a/core/lib/Drupal/Core/DrupalKernel.php
+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -399,6 +399,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 4db85da..6ccb681 100644
--- a/core/lib/Drupal/Core/Language/Language.php
+++ b/core/lib/Drupal/Core/Language/Language.php
@@ -17,6 +17,21 @@
  * @see language_default()
  */
 class Language {
+
+  /**
+   * The values to use to instantiate the default language.
+   *
+   * @var array
+   */
+  public static $DEFAULT_VALUES = array(
+    'langcode' => '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 $langcode = '';
diff --git a/core/lib/Drupal/Core/Language/LanguageManager.php b/core/lib/Drupal/Core/Language/LanguageManager.php
index fa94205..fe9635a 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;
+
+  /**
+   * The configuration factory service.
+   *
+   * @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,13 +65,15 @@ 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;
 
+  public function __construct(array $config, PluginManagerInterface $negotiatorManager) {
+    $this->config = $config;
+    $this->negotiatorManager = $negotiatorManager;
+  }
+
   /**
    * Initializes each language type to a language object.
    */
@@ -53,9 +82,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;
   }
@@ -86,31 +117,105 @@ public function getLanguage($type) {
       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('langcode' => LANGUAGE_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) {
+    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 identifer of the language negotiation method to use to detect
+   *   language.
+   */
+  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'] == variable_get('cache', 0);
+      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
@@ -134,14 +239,17 @@ public function reset($type = NULL) {
    * @return bool
    *   TRUE if more than one language is enabled, FALSE otherwise.
    */
-  protected function isMultilingual() {
+  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.
     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() {
@@ -154,15 +262,106 @@ protected function getLanguageTypes() {
    * @return Drupal\Core\Language\Language
    *   A language object.
    */
-  protected function getLanguageDefault() {
-    $default_info = variable_get('language_default', array(
-      'langcode' => 'en',
-      'name' => 'English',
-      'direction' => 0,
-      'weight' => 0,
-      'locked' => 0,
-    ));
+  public function getLanguageDefault() {
+    $default_info = variable_get('language_default', Language::$DEFAULT_VALUES);
     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_CONFIGURABLE, LANGUAGE_LOCKED, LANGUAGE_ALL.
+   *
+   * @return array
+   *   An associative array of languages, keyed by the language code, ordered by
+   *   weight ascending and name ascending.
+   */
+  function getLanguageList($flags = LANGUAGE_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();
+      // @todo Make this method part of a language manager instance provided by
+      //   the Language module.
+      if ($this->isMultilingual() || module_exists('language')) {
+        // Use language module configuration if available.
+        $this->languageList = db_query('SELECT * FROM {language} ORDER BY weight ASC, name ASC')->fetchAllAssoc('langcode', \PDO::FETCH_ASSOC);
+
+        // Initialize default property so callers have an easy reference and can
+        // save the same object without data loss.
+        foreach ($this->languageList as $langcode => $info) {
+          $info['default'] = ($langcode == $default->langcode);
+          $this->languageList[$langcode] = new Language($info);
+        }
+      }
+      else {
+        // No language module, so use the default language only.
+        $this->languageList = array($default->langcode => $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_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 $langcode => $language) {
+      if (($language->locked && !($flags & LANGUAGE_LOCKED)) || (!$language->locked && !($flags & LANGUAGE_CONFIGURABLE))) {
+        continue;
+       }
+      $filtered_languages[$langcode] = $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_NOT_SPECIFIED] = new Language(array(
+      'langcode' => LANGUAGE_NOT_SPECIFIED,
+      'name' => t('Not specified'),
+      'weight' => ++$weight,
+    ) + $locked_language);
+
+    $languages[LANGUAGE_NOT_APPLICABLE] = new Language(array(
+      'langcode' => LANGUAGE_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..90eed26
--- /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 $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..83404bc
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/LanguageNegotiationMethodBase.php
@@ -0,0 +1,43 @@
+<?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;
+
+  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..a8da65e
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/LanguageNegotiationMethodManager.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\LanguageNegotiationMethodManager.
+ */
+
+namespace Drupal\Core\Language;
+
+use Drupal\Component\Plugin\PluginManagerBase;
+use Drupal\Component\Plugin\Factory\DefaultFactory;
+use Drupal\Core\Plugin\Discovery\AnnotatedClassDiscovery;
+use Drupal\Core\Plugin\Discovery\CacheDecorator;
+
+/**
+ * Manages language negotiation methods.
+ */
+class LanguageNegotiationMethodManager extends PluginManagerBase {
+
+  public function __construct(array $namespaces) {
+    $this->discovery = new AnnotatedClassDiscovery('Core', 'LanguageNegotiation', $namespaces);
+    $this->discovery = new CacheDecorator($this->discovery, 'language_negotiation_plugins');
+    $this->factory = new DefaultFactory($this->discovery);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Plugin/Core/LanguageNegotiation/LanguageNegotiationBrowser.php b/core/lib/Drupal/Core/Plugin/Core/LanguageNegotiation/LanguageNegotiationBrowser.php
new file mode 100644
index 0000000..83db7dd
--- /dev/null
+++ b/core/lib/Drupal/Core/Plugin/Core/LanguageNegotiation/LanguageNegotiationBrowser.php
@@ -0,0 +1,150 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Plugin\Core\LanguageNegotiation\LanguageNegotiationBrowser.
+ */
+
+namespace Drupal\Core\Plugin\Core\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';
+
+  /**
+   * Implements \Drupal\Core\Language\LanguageNegotiationInterface::negotiateLanguage().
+   */
+  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->langcode;
+        $max_qvalue = $qvalue;
+      }
+    }
+
+    return $best_match_langcode;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Plugin/Core/LanguageNegotiation/LanguageNegotiationSelected.php b/core/lib/Drupal/Core/Plugin/Core/LanguageNegotiation/LanguageNegotiationSelected.php
new file mode 100644
index 0000000..b578fb8
--- /dev/null
+++ b/core/lib/Drupal/Core/Plugin/Core/LanguageNegotiation/LanguageNegotiationSelected.php
@@ -0,0 +1,42 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Plugin\Core\LanguageNegotiation\LanguageNegotiationSelected.
+ */
+
+namespace Drupal\Core\Plugin\Core\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';
+
+  /**
+   * Implements \Drupal\Core\Language\LanguageNegotiationInterface::negotiateLanguage().
+   */
+  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()->langcode;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Plugin/Core/LanguageNegotiation/LanguageNegotiationSession.php b/core/lib/Drupal/Core/Plugin/Core/LanguageNegotiation/LanguageNegotiationSession.php
new file mode 100644
index 0000000..7d4491e
--- /dev/null
+++ b/core/lib/Drupal/Core/Plugin/Core/LanguageNegotiation/LanguageNegotiationSession.php
@@ -0,0 +1,58 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Plugin\Core\LanguageNegotiation\LanguageNegotiationSession.
+ */
+
+namespace Drupal\Core\Plugin\Core\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';
+
+  /**
+   * Implements \Drupal\Core\Language\LanguageNegotiationInterface::negotiateLanguage().
+   */
+  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/Plugin/Core/LanguageNegotiation/LanguageNegotiationUI.php b/core/lib/Drupal/Core/Plugin/Core/LanguageNegotiation/LanguageNegotiationUI.php
new file mode 100644
index 0000000..869ccf5
--- /dev/null
+++ b/core/lib/Drupal/Core/Plugin/Core/LanguageNegotiation/LanguageNegotiationUI.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Plugin\Core\LanguageNegotiation\LanguageNegotiationUI.
+ */
+
+namespace Drupal\Core\Plugin\Core\LanguageNegotiation;
+
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+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';
+
+  /**
+   * Implements \Drupal\Core\Language\LanguageNegotiationInterface::negotiateLanguage().
+   */
+  public function negotiateLanguage(array $languages, Request $request = NULL) {
+    return $this->languageManager->getLanguage(LANGUAGE_TYPE_INTERFACE)->langcode;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Plugin/Core/LanguageNegotiation/LanguageNegotiationUrl.php b/core/lib/Drupal/Core/Plugin/Core/LanguageNegotiation/LanguageNegotiationUrl.php
new file mode 100644
index 0000000..d0eba9f
--- /dev/null
+++ b/core/lib/Drupal/Core/Plugin/Core/LanguageNegotiation/LanguageNegotiationUrl.php
@@ -0,0 +1,100 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Plugin\Core\LanguageNegotiation\LanguageNegotiationUrl.
+ */
+
+namespace Drupal\Core\Plugin\Core\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';
+
+  /**
+   * Implements \Drupal\Core\Language\LanguageNegotiationInterface::negotiateLanguage().
+   */
+  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->langcode]) && $prefixes[$language->langcode] == $prefix) {
+            $negotiated_language = $language;
+            break;
+          }
+        }
+
+        if ($negotiated_language !== FALSE) {
+          $langcode = $negotiated_language->langcode;
+        }
+        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->langcode])) {
+            // Ensure that there is exactly one protocol in the URL when
+            // checking the hostname.
+            $host = 'http://' . str_replace(array('http://', 'https://'), '', $domains[$language->langcode]);
+            $host = parse_url($host, PHP_URL_HOST);
+            if ($http_host == $host) {
+              $langcode = $language->langcode;
+              break;
+            }
+          }
+        }
+        break;
+    }
+
+    return $langcode;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Plugin/Core/LanguageNegotiation/LanguageNegotiationUrlFallback.php b/core/lib/Drupal/Core/Plugin/Core/LanguageNegotiation/LanguageNegotiationUrlFallback.php
new file mode 100644
index 0000000..4d3cf32
--- /dev/null
+++ b/core/lib/Drupal/Core/Plugin/Core/LanguageNegotiation/LanguageNegotiationUrlFallback.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Plugin\Core\LanguageNegotiation\LanguageNegotiationUrlFallback.
+ */
+
+namespace Drupal\Core\Plugin\Core\LanguageNegotiation;
+
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+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';
+
+  /**
+   * Implements \Drupal\Core\Language\LanguageNegotiationInterface::negotiateLanguage().
+   */
+  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->langcode])) || (!$prefix && empty($domains[$default->langcode]))) {
+      return $default->langcode;
+    }
+    else {
+      return $this->languageManager->getLanguage(LANGUAGE_TYPE_INTERFACE)->langcode;
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Plugin/Core/LanguageNegotiation/LanguageNegotiationUser.php b/core/lib/Drupal/Core/Plugin/Core/LanguageNegotiation/LanguageNegotiationUser.php
new file mode 100644
index 0000000..b641759
--- /dev/null
+++ b/core/lib/Drupal/Core/Plugin/Core/LanguageNegotiation/LanguageNegotiationUser.php
@@ -0,0 +1,47 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Plugin\Core\LanguageNegotiation\LanguageNegotiationUrl.
+ */
+
+namespace Drupal\Core\Plugin\Core\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';
+
+  /**
+   * Implements \Drupal\Core\Language\LanguageNegotiationInterface::negotiateLanguage().
+   */
+  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/Plugin/Core/LanguageNegotiation/LanguageNegotiationUserAdmin.php b/core/lib/Drupal/Core/Plugin/Core/LanguageNegotiation/LanguageNegotiationUserAdmin.php
new file mode 100644
index 0000000..9e98def
--- /dev/null
+++ b/core/lib/Drupal/Core/Plugin/Core/LanguageNegotiation/LanguageNegotiationUserAdmin.php
@@ -0,0 +1,50 @@
+<?php
+
+/**
+ * @file
+ * Definition of \Drupal\Core\Plugin\Core\LanguageNegotiation\LanguageNegotiationUserAdmin.
+ */
+
+namespace Drupal\Core\Plugin\Core\LanguageNegotiation;
+
+use Drupal\Component\Annotation\Plugin;
+use Drupal\Core\Annotation\Translation;
+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';
+
+  /**
+   * Implements \Drupal\Core\Language\LanguageNegotiationInterface::negotiateLanguage().
+   */
+  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/language/language.admin.inc b/core/modules/language/language.admin.inc
index f59fb38..1a1c9c8 100644
--- a/core/modules/language/language.admin.inc
+++ b/core/modules/language/language.admin.inc
@@ -557,6 +557,9 @@ function language_negotiation_configure_form_submit($form, &$form_state) {
   // configuration.
   language_types_set();
 
+  // 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.'));
 }
@@ -735,6 +738,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 6bbb5a7..3ad1b25 100644
--- a/core/modules/language/language.module
+++ b/core/modules/language/language.module
@@ -501,9 +501,6 @@ function language_save($language) {
   // 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
diff --git a/core/modules/language/language.negotiation.inc b/core/modules/language/language.negotiation.inc
index ca41c78..49367a7 100644
--- a/core/modules/language/language.negotiation.inc
+++ b/core/modules/language/language.negotiation.inc
@@ -53,324 +53,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)->langcode;
-}
-
-/**
- * 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->langcode;
-      $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->langcode;
-      }
-      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->langcode])) {
-          // Ensure that there is exactly one protocol in the URL when checking
-          // the hostname.
-          $host = 'http://' . str_replace(array('http://', 'https://'), '', $domains[$language->langcode]);
-          $host = parse_url($host, PHP_URL_HOST);
-          if ($http_host == $host) {
-            $language_url = $language->langcode;
-            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->langcode])) || (!$prefix && empty($domains[$default->langcode]))) {
-    return $default->langcode;
-  }
-  else {
-    $langcode = language($language_type)->langcode;
-    return $langcode;
-  }
-}
-
-/**
  * Return links for the URL language switcher block.
  *
  * Translation links may be provided by other modules.
diff --git a/core/modules/language/language.services.yml b/core/modules/language/language.services.yml
index 576f4f8..c0de705 100644
--- a/core/modules/language/language.services.yml
+++ b/core/modules/language/language.services.yml
@@ -3,4 +3,4 @@ services:
     class: Drupal\language\HttpKernel\PathProcessorLanguage
     tags:
       - { name: path_processor_inbound, priority: 300 }
-    arguments: ['@config.factory']
+    arguments: ['@language_manager', '@config.factory']
diff --git a/core/modules/language/lib/Drupal/language/HttpKernel/PathProcessorLanguage.php b/core/modules/language/lib/Drupal/language/HttpKernel/PathProcessorLanguage.php
index 006b313..e82e449 100644
--- a/core/modules/language/lib/Drupal/language/HttpKernel/PathProcessorLanguage.php
+++ b/core/modules/language/lib/Drupal/language/HttpKernel/PathProcessorLanguage.php
@@ -8,6 +8,7 @@
 namespace Drupal\language\HttpKernel;
 
 use Drupal\Core\Config\ConfigFactory;
+use Drupal\Core\Language\LanguageManager;
 use Drupal\Core\PathProcessor\InboundPathProcessorInterface;
 use Symfony\Component\HttpKernel\HttpKernelInterface;
 use Symfony\Component\HttpFoundation\Request;
@@ -25,11 +26,12 @@ class PathProcessorLanguage implements InboundPathProcessorInterface {
   protected $config;
 
   /**
-   * An array of enabled languages.
+   * The language manager.
    *
-   * @var array
+   * @var \Drupal\Core\Language\LanguageManager
    */
-  protected $languages;
+  protected $languageManager;
+
 
   /**
    * Constructs a PathProcessorLanguage object.
@@ -41,31 +43,33 @@ class PathProcessorLanguage implements InboundPathProcessorInterface {
    *   An array of languages, keyed by language code, representing the languages
    *   currently enabled on the site.
    */
-  public function __construct(ConfigFactory $config, array $languages = array()) {
+  public function __construct(LanguageManager $language_manager, ConfigFactory $config) {
     $this->config = $config;
-    if (empty($languages)) {
-      $languages = language_list();
-    }
-    $this->languages = $languages;
+    $this->languageManager = $language_manager;
   }
 
   /**
    * 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->langcode]) && $prefixes[$language->langcode] == $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->langcode]) && $prefixes[$language->langcode] == $prefix) {
+        // Rebuild $path with the language removed.
+        $path = implode('/', $parts);
+        break;
       }
     }
+
     return $path;
   }
 
diff --git a/core/modules/language/lib/Drupal/language/Plugin/block/block/LanguageBlock.php b/core/modules/language/lib/Drupal/language/Plugin/block/block/LanguageBlock.php
index 53c58e7..79d991d 100644
--- a/core/modules/language/lib/Drupal/language/Plugin/block/block/LanguageBlock.php
+++ b/core/modules/language/lib/Drupal/language/Plugin/block/block/LanguageBlock.php
@@ -37,6 +37,7 @@ public function build() {
     $build = array();
     $path = drupal_is_front_page() ? '<front>' : current_path();
     list($plugin_id, $type) = explode(':', $this->getPluginId());
+    include_once DRUPAL_ROOT . '/core/includes/language.inc';
     $links = language_negotiation_get_switch_links($type, $path);
 
     if (isset($links->links)) {
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php
index 80a835d..2974c3d 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\Plugin\Core\LanguageNegotiation\LanguageNegotiationBrowser;
+use Symfony\Component\HttpFoundation\Request;
 
 /**
  * Test browser language detection.
@@ -154,8 +156,9 @@ function testLanguageFromBrowser() {
     );
 
     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));
+      $language_neg = new LanguageNegotiationBrowser(array('browser' => array('mappings' => array())));
+      $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/tests/language_test/lib/Drupal/language_test/LanguageTestBundle.php b/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestBundle.php
index b711507..c742134 100644
--- a/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestBundle.php
+++ b/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestBundle.php
@@ -22,7 +22,11 @@ public function build(ContainerBuilder $container) {
     // Overrides language_manager class to test domain language negotiation.
     $definition = $container->getDefinition('language_manager');
     $definition->setClass('Drupal\language_test\LanguageTestManager');
+
+    $container->register('language_test.language_negotiation', 'Drupal\language_test\LanguageTestLanguageNegotiation')
+      ->addTag('language_negotiation', array('method_id' => 'test_language_negotiation_method'));
+    $container->register('language_test.language_negotiation_ts', 'Drupal\language_test\LanguageTestLanguageNegotiationTS')
+      ->addTag('language_negotiation', array('method_id' => 'test_language_negotiation_method_ts'));
   }
 
 }
-
diff --git a/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestLanguageNegotiation.php b/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestLanguageNegotiation.php
new file mode 100644
index 0000000..1c237b9
--- /dev/null
+++ b/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestLanguageNegotiation.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\language\LanguageTestLanguageNegotiation.
+ */
+
+namespace Drupal\language_test;
+
+use Drupal\Core\Language\LanguageNegotiationInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class for identifying language via URL prefix or domain.
+ */
+class LanguageTestLanguageNegotiation implements LanguageNegotiationInterface {
+
+  protected $types = array(LANGUAGE_TYPE_CONTENT, 'test_language_type', 'fixed_test_language_type');
+
+  public function getTypes() {
+    return $this->types;
+  }
+
+  /**
+   * Overrides Drupal\Core\Language\LanguageNegotiationInterface::negotiateLanguage().
+   */
+  public function negotiateLanguage($languages = array(), $request = NULL) {
+    return 'it';
+  }
+
+}
diff --git a/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestLanguageNegotiationTS.php b/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestLanguageNegotiationTS.php
new file mode 100644
index 0000000..f8bfb17
--- /dev/null
+++ b/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestLanguageNegotiationTS.php
@@ -0,0 +1,31 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\language\LanguageTestLanguageNegotiation.
+ */
+
+namespace Drupal\language_test;
+
+use Drupal\Core\Language\LanguageNegotiationInterface;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class for identifying language via URL prefix or domain.
+ */
+class LanguageTestLanguageNegotiationTS implements LanguageNegotiationInterface {
+
+  protected $types = array('test_language_type');
+
+  public function getTypes() {
+    return $this->types;
+  }
+
+  /**
+   * Overrides Drupal\Core\Language\LanguageNegotiationInterface::negotiateLanguage().
+   */
+  public function negotiateLanguage($languages = array(), $request = NULL) {
+    return 'it';
+  }
+
+}
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 23d01fa..cb14788 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();
     }
   }
 
