diff --git a/core/core.services.yml b/core/core.services.yml
index 1b0d520..af92115 100644
--- a/core/core.services.yml
+++ b/core/core.services.yml
@@ -197,9 +197,6 @@ services:
   http_kernel:
     class: Drupal\Core\HttpKernel
     arguments: ['@event_dispatcher', '@service_container', '@controller_resolver']
-  language_manager:
-    class: Drupal\Core\Language\LanguageManager
-    arguments: ['@state']
   string_translator.custom_strings:
     class: Drupal\Core\StringTranslation\Translator\CustomStrings
     tags:
diff --git a/core/includes/bootstrap.inc b/core/includes/bootstrap.inc
index 32f47fa..7217bd4 100644
--- a/core/includes/bootstrap.inc
+++ b/core/includes/bootstrap.inc
@@ -2366,33 +2366,6 @@ function language($type) {
 }
 
 /**
- * Returns an array of the available language types.
- *
- * @return array
- *   An array of all language types where the keys of each are the language type
- *   name and its value is its configurability (TRUE/FALSE).
- */
-function language_types_get_all() {
-  $types = \Drupal::config('system.language.types')->get('all');
-  return $types ? $types : array_keys(language_types_get_default());
-}
-
-/**
- * Returns a list of the built-in language types.
- *
- * @return array
- *   An array of key-values pairs where the key is the language type name and
- *   the value is its configurability (TRUE/FALSE).
- */
-function language_types_get_default() {
-  return array(
-    Language::TYPE_INTERFACE => TRUE,
-    Language::TYPE_CONTENT => FALSE,
-    Language::TYPE_URL => FALSE,
-  );
-}
-
-/**
  * Returns TRUE if there is more than one language enabled.
  *
  * @return bool
@@ -2415,65 +2388,7 @@ function language_multilingual() {
  *   weight ascending and name ascending.
  */
 function language_list($flags = Language::STATE_CONFIGURABLE) {
-
-  $languages = &drupal_static(__FUNCTION__);
-
-  // Initialize master language list.
-  if (!isset($languages)) {
-    // Initialize local language list cache.
-   $languages = array();
-
-    // Fill in master language list based on current configuration.
-    $default = language_default();
-    if (language_multilingual() || module_exists('language')) {
-      // Use language module configuration if available.
-      $language_entities = config_get_storage_names_with_prefix('language.entity');
-
-      // Initialize default property so callers have an easy reference and can
-      // save the same object without data loss.
-      foreach ($language_entities as $langcode_config_name) {
-        $langcode = substr($langcode_config_name, strlen('language.entity.'));
-        $info = \Drupal::config($langcode_config_name)->get();
-        $languages[$langcode] = new Language(array(
-          'default' => ($info['id'] == $default->id),
-          'name' => $info['label'],
-          'id' => $info['id'],
-          'direction' => $info['direction'],
-          'locked' => $info['locked'],
-          'weight' => $info['weight'],
-        ));
-      }
-      Language::sort($languages);
-    }
-    else {
-      // No language module, so use the default language only.
-      $languages = array($default->id => $default);
-      // Add the special languages, they will be filtered later if needed.
-      $languages += language_default_locked_languages($default->weight);
-    }
-  }
-
-  // Filter the full list of languages based on the value of the $all flag. By
-  // default we remove the locked languages, but the caller may request for
-  // those languages to be added as well.
-  $filtered_languages = array();
-
-  // Add the site's default language if flagged as allowed value.
-  if ($flags & Language::STATE_SITE_DEFAULT) {
-    $default = isset($default) ? $default : language_default();
-    // Rename the default language.
-    $default->name = t("Site's default language (@lang_name)", array('@lang_name' => $default->name));
-    $filtered_languages['site_default'] = $default;
-  }
-
-  foreach ($languages as $langcode => $language) {
-    if (($language->locked && !($flags & Language::STATE_LOCKED)) || (!$language->locked && !($flags & Language::STATE_CONFIGURABLE))) {
-      continue;
-     }
-    $filtered_languages[$langcode] = $language;
-  }
-
-  return $filtered_languages;
+  return Drupal::languageManager()->getLanguageList($flags);
 }
 
 /**
@@ -2487,24 +2402,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);
 }
 
 /**
@@ -2515,10 +2413,11 @@ function language_default_locked_languages($weight = 0) {
  *
  * @return \Drupal\core\Language\Language|null
  *   A fully-populated language object or NULL.
+ *
+ * @see \Druoal\Core\Language\LanguageManager::loadLanguage().
  */
 function language_load($langcode) {
-  $languages = language_list(Language::STATE_ALL);
-  return isset($languages[$langcode]) ? $languages[$langcode] : NULL;
+  return Drupal::languageManager()->loadLanguage($langcode);
 }
 
 /**
@@ -2565,15 +2464,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 df552ed..b039c57 100644
--- a/core/includes/common.inc
+++ b/core/includes/common.inc
@@ -4838,6 +4838,14 @@ function drupal_flush_all_caches() {
 }
 
 /**
+ * Rebuild the container to store updated language negotiation settings.
+ */
+function drupal_rebuild_language_negotiation_settings() {
+  Drupal::service('plugin.manager.language_negotiation_method')->clearCachedDefinitions();
+  PhpStorageFactory::get('service_container')->deleteAll();
+}
+
+/**
  * Changes the dummy query string added to all CSS and JavaScript files.
  *
  * Changing the dummy query string appended to CSS and JavaScript files forces
diff --git a/core/includes/install.core.inc b/core/includes/install.core.inc
index 91a241f..9c74c52 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');
 
@@ -387,9 +396,12 @@ function install_begin_request(&$install_state) {
       ->addArgument(new Reference('config.context'));
 
     // Register the 'language_manager' service.
-    $container
-      ->register('language_manager', 'Drupal\Core\Language\LanguageManager')
-      ->addArgument(NULL);
+    $container->register('plugin.manager.language_negotiation_method', 'Drupal\Core\Language\LanguageNegotiationMethodManager')
+      ->addArgument(new Reference('container.namespaces'))
+      ->addArgument(new Reference('cache.cache'));
+    $container->register('language_manager', 'Drupal\Core\Language\InstallLanguageManager')
+      ->addArgument(array())
+      ->addArgument(new Reference('plugin.manager.language_negotiation_method'));
 
     // Register the translation services.
     install_register_translation_service($container);
@@ -1586,7 +1598,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 79bae62..af20cc9 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
@@ -315,28 +279,7 @@ function language_negotiation_method_enabled($method_id, $type = NULL) {
  *   A keyed array of links ready to be themed.
  */
 function language_negotiation_get_switch_links($type, $path) {
-  $links = FALSE;
-  $negotiation = variable_get("language_negotiation_$type", array());
-
-  foreach ($negotiation as $method_id => $method) {
-    if (isset($method['callbacks']['language_switch'])) {
-      if (isset($method['file'])) {
-        require_once DRUPAL_ROOT . '/' . $method['file'];
-      }
-
-      $callback = $method['callbacks']['language_switch'];
-      $result = $callback($type, $path);
-
-      if (!empty($result)) {
-        // Allow modules to provide translations for specific links.
-        drupal_alter('language_switch_links', $result, $type, $path);
-        $links = (object) array('links' => $result, 'method_id' => $method_id);
-        break;
-      }
-    }
-  }
-
-  return $links;
+  return Drupal::languageManager()->getLanguageNegotiationSwitchLinks($type, $path);
 }
 
 /**
@@ -411,129 +354,7 @@ function language_negotiation_set($type, $method_weights) {
  *   An array of language negotiation methods.
  */
 function language_negotiation_info() {
-  $negotiation_info = &drupal_static(__FUNCTION__);
-
-  if (!isset($negotiation_info)) {
-    // Collect all the module-defined language negotiation methods.
-    $negotiation_info = \Drupal::moduleHandler()->invokeAll('language_negotiation_info');
-    $languages = language_list();
-    $selected_language = $languages[language_from_selected($languages)];
-    $description = 'Language based on a selected language. ';
-    $description .= ($selected_language->id == language_default()->id) ? "(Site's default language (@language_name))" : '(@language_name)';
-    // Add the default language negotiation method.
-    $negotiation_info[LANGUAGE_NEGOTIATION_SELECTED] = array(
-      'callbacks' => array(
-        'negotiation' => 'language_from_selected',
-      ),
-      'weight' => 12,
-      'name' => t('Selected language'),
-      'description' => t($description, array('@language_name' => $selected_language->name)),
-      'config' => 'admin/config/regional/language/detection/selected',
-    );
-
-     // Let other modules alter the list of language negotiation methods.
-     drupal_alter('language_negotiation_info', $negotiation_info);
-  }
-
-  return $negotiation_info;
-}
-
-/**
- * Invokes a language negotiation method and caches the results.
- *
- * @param $method_id
- *   The language negotiation method's identifier.
- * @param $method
- *   (optional) An associative array of information about the method to be
- *   invoked (see hook_language_negotiation_info() for details). If not passed
- *   in, it will be loaded through language_negotiation_info().
- *
- * @param $request
- *   (optional) The HttpRequest object representing the current request.
- *
- * @return
- *   A language object representing the language chosen by the method.
- */
-function language_negotiation_method_invoke($method_id, $method = NULL, $request = NULL) {
-  $results = &drupal_static(__FUNCTION__);
-
-  if (!isset($results[$method_id])) {
-    global $user;
-
-    $languages = language_list();
-
-    if (!isset($method)) {
-      $negotiation_info = language_negotiation_info();
-      $method = $negotiation_info[$method_id];
-    }
-
-    if (isset($method['file'])) {
-      require_once DRUPAL_ROOT . '/' . $method['file'];
-    }
-    // If the language negotiation method has no cache preference or this is
-    // satisfied we can execute the callback.
-    $cache = !isset($method['cache']) || $user->isAuthenticated() || $method['cache'] == 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.
-  *
-  * @param $languages
-  *   An array of valid language objects.
-  *
-  * @return
-  *   A valid language code on success, FALSE otherwise.
-  */
-function language_from_selected($languages) {
-  $langcode = (string) \Drupal::config('language.negotiation')->get('selected_langcode');
-  // Replace the site's default langcode by its real value.
-  if ($langcode == 'site_default') {
-    $langcode = language_default()->id;
-  }
-  return isset($languages[$langcode]) ? $langcode : language_default()->id;
-}
-
-/**
- * Splits the given path into prefix and actual path.
- *
- * Parse the given path and return the language object identified by the prefix
- * and the actual path.
- *
- * @param $path
- *   The path to split.
- * @param $languages
- *   An array of valid languages.
- *
- * @return
- *   An array composed of:
- *    - A language object corresponding to the identified prefix on success,
- *      FALSE otherwise.
- *    - The path without the prefix on success, the given path otherwise.
- */
-function language_url_split_prefix($path, $languages) {
-  $args = empty($path) ? array() : explode('/', $path);
-  $prefix = array_shift($args);
-
-  // Search prefix within enabled languages.
-  $prefixes = language_negotiation_url_prefixes();
-  foreach ($languages as $language) {
-    if (isset($prefixes[$language->id]) && $prefixes[$language->id] == $prefix) {
-      // Rebuild $path with the language removed.
-      return array($language, implode('/', $args));
-    }
-  }
-
-  return array(FALSE, $path);
+  return Drupal::service('plugin.manager.language_negotiation_method')->getDefinitions();
 }
 
 /**
diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php
index 9cd8f5a..b7d30e0 100644
--- a/core/lib/Drupal/Core/CoreServiceProvider.php
+++ b/core/lib/Drupal/Core/CoreServiceProvider.php
@@ -13,6 +13,7 @@
 use Drupal\Core\DependencyInjection\Compiler\ModifyServiceDefinitionsPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterKernelListenersPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterAccessChecksPass;
+use Drupal\Core\DependencyInjection\Compiler\RegisterLanguageNegotiationPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterPathProcessorsPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterRouteFiltersPass;
 use Drupal\Core\DependencyInjection\Compiler\RegisterRouteEnhancersPass;
@@ -51,6 +52,7 @@ public function register(ContainerBuilder $container) {
     $this->registerTwig($container);
     $this->registerModuleHandler($container);
     $this->registerUuid($container);
+    $this->registerLanguage($container);
 
     $container->addCompilerPass(new RegisterRouteFiltersPass());
     // Add a compiler pass for registering event subscribers.
@@ -161,4 +163,24 @@ public static function registerUuid(ContainerBuilder $container) {
     return $uuid_class;
   }
 
+  /**
+   * Registers language related services.
+   */
+  protected function registerLanguage(ContainerBuilder $container) {
+    // @todo Refactor this when all language negotiation settings are converted
+    //   a single configuration object.
+    $storage = $container->get('kernel.config.storage');
+    $config = $storage->read('language.negotiation') ?: array();
+    $config += array('browser' => array('mappings' => $storage->read('language.mappings')));
+
+    // Register the 'language_manager' service.
+    $container->register('plugin.manager.language_negotiation_method', 'Drupal\Core\Language\LanguageNegotiationMethodManager')
+      ->addArgument(new Reference('container.namespaces'))
+      ->addArgument(new Reference('cache.cache'));
+    $container->register('language_manager', 'Drupal\Core\Language\LanguageManager')
+      ->addArgument($config)
+      ->addArgument(new Reference('plugin.manager.language_negotiation_method'))
+      ->addArgument(new Reference('state'));
+   }
+
 }
diff --git a/core/lib/Drupal/Core/DrupalKernel.php b/core/lib/Drupal/Core/DrupalKernel.php
index 884b271..cbecfeb 100644
--- a/core/lib/Drupal/Core/DrupalKernel.php
+++ b/core/lib/Drupal/Core/DrupalKernel.php
@@ -510,6 +510,8 @@ protected function buildContainer() {
     $container->register('class_loader')->setSynthetic(TRUE);
     $container->register('kernel', 'Symfony\Component\HttpKernel\KernelInterface')->setSynthetic(TRUE);
     $container->register('service_container', 'Symfony\Component\DependencyInjection\ContainerInterface')->setSynthetic(TRUE);
+    // Register the kernel-level config storage.
+    $container->set('kernel.config.storage', $this->configStorage);
     $yaml_loader = new YamlFileLoader($container);
     foreach ($this->serviceYamls as $filename) {
       $yaml_loader->load($filename);
diff --git a/core/lib/Drupal/Core/Language/InstallLanguageManager.php b/core/lib/Drupal/Core/Language/InstallLanguageManager.php
new file mode 100644
index 0000000..b70ee3f
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/InstallLanguageManager.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\InstallLanguageManager.
+ */
+
+namespace Drupal\Core\Language;
+
+use Drupal\Component\Plugin\PluginManagerInterface;
+use Symfony\Component\HttpFoundation\Request;
+use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
+
+/**
+ * Class responsible for initializing each language type.
+ */
+class InstallLanguageManager extends LanguageManager {
+
+  /**
+   * Constructs a new LanguageManager object.
+   *
+   * @param array $config
+   *   An array of configuration.
+   * @param \Drupal\Component\Plugin\PluginManagerInterface $negotiator_manager
+   *   The language negotiation methods plugin manager
+   */
+  public function __construct(array $config, PluginManagerInterface $negotiator_manager) {
+    $this->config = $config;
+    $this->negotiatorManager = $negotiator_manager;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function isMultilingual() {
+    // No state service in install time.
+    return FALSE;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Language/Language.php b/core/lib/Drupal/Core/Language/Language.php
index 7181b32..ceea20f 100644
--- a/core/lib/Drupal/Core/Language/Language.php
+++ b/core/lib/Drupal/Core/Language/Language.php
@@ -17,6 +17,23 @@
  * @see language_default()
  */
 class Language {
+
+  /**
+   * The values to use to instantiate the default language.
+   *
+   * @todo Remove once converted to config.
+   *
+   * @var array
+   */
+  public static $defaultValues = array(
+    'id' => 'en',
+    'name' => 'English',
+    'direction' => 0,
+    'weight' => 0,
+    'locked' => 0,
+    'default' => TRUE,
+  );
+
   // Properties within the Language are set up as the default language.
   public $name = '';
   public $id = '';
diff --git a/core/lib/Drupal/Core/Language/LanguageManager.php b/core/lib/Drupal/Core/Language/LanguageManager.php
index 7bc716d..8081cea 100644
--- a/core/lib/Drupal/Core/Language/LanguageManager.php
+++ b/core/lib/Drupal/Core/Language/LanguageManager.php
@@ -7,6 +7,7 @@
 
 namespace Drupal\Core\Language;
 
+use Drupal\Component\Plugin\PluginManagerInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
 
@@ -16,18 +17,16 @@
 class LanguageManager {
 
   /**
-   * A request object.
-   *
-   * @var \Symfony\Component\HttpFoundation\Request
+   * The language negotiation method id for the language manager.
    */
-  protected $request;
+  const METHOD_ID = 'language-default';
 
   /**
    * The Key/Value Store to use for state.
    *
    * @var \Drupal\Core\KeyValueStore\KeyValueStoreInterface
    */
-  protected $state = NULL;
+  protected $state;
 
   /**
    * An array of language objects keyed by language type.
@@ -37,6 +36,34 @@ class LanguageManager {
   protected $languages;
 
   /**
+   * An array of all the available languages keyed by language code.
+   *
+   * @var array
+   */
+  protected $languageList;
+
+  /**
+   * An array of configuration.
+   *
+   * @var array
+   */
+  protected $config;
+
+  /**
+   * The language negotiation method plugin manager.
+   *
+   * @var \Drupal\Component\Plugin\PluginManagerInterface
+   */
+  protected $negotiatorManager;
+
+  /**
+   * A request object.
+   *
+   * @var \Symfony\Component\HttpFoundation\Request
+   */
+  protected $request;
+
+  /**
    * Whether or not the language manager has been initialized.
    *
    * @var bool
@@ -46,20 +73,23 @@ class LanguageManager {
   /**
    * Whether already in the process of language initialization.
    *
-   * @todo This is only needed due to the circular dependency between language
-   *   and config. See http://drupal.org/node/1862202 for the plan to fix this.
-   *
    * @var bool
    */
   protected $initializing = FALSE;
 
   /**
-   * Constructs an LanguageManager object.
+   * Constructs a new LanguageManager object.
    *
-   * @param \Drupal\Core\KeyValueStore\KeyValueStoreInterface $state
-   *   The state keyvalue store.
+   * @param array $config
+   *   An array of configuration.
+   * @param \Drupal\Component\Plugin\PluginManagerInterface $negotiator_manager
+   *   The language negotiation methods plugin manager
+   * @param \Drupal\Core\KeyValueStoreInterface $state
+   *   The state key value store.
    */
-  public function __construct(KeyValueStoreInterface $state = NULL) {
+  public function __construct(array $config, PluginManagerInterface $negotiator_manager, KeyValueStoreInterface $state) {
+    $this->config = $config;
+    $this->negotiatorManager = $negotiator_manager;
     $this->state = $state;
   }
 
@@ -71,9 +101,11 @@ public function init() {
       return;
     }
     if ($this->isMultilingual()) {
-      foreach ($this->getLanguageTypes() as $type) {
-        $this->getLanguage($type);
-      }
+      // This is still assumed by various functions to be loaded.
+      include_once DRUPAL_ROOT . '/core/includes/language.inc';
+    }
+    foreach ($this->getLanguageTypes() as $type) {
+      $this->getLanguage($type);
     }
     $this->initialized = TRUE;
   }
@@ -105,31 +137,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->isAuthenticated() || $method['cache'] == \Drupal::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
@@ -141,6 +251,7 @@ public function reset($type = NULL) {
     if (!isset($type)) {
       $this->languages = array();
       $this->initialized = FALSE;
+      $this->languageList = NULL;
     }
     elseif (isset($this->languages[$type])) {
       unset($this->languages[$type]);
@@ -154,21 +265,70 @@ public function reset($type = NULL) {
    *   TRUE if more than one language is enabled, FALSE otherwise.
    */
   public function isMultilingual() {
-    if (!isset($this->state)) {
-      // No state service in install time.
-      return FALSE;
-    }
     return ($this->state->get('language_count') ?: 1) > 1;
   }
 
   /**
    * Returns an array of the available language types.
    *
-   * @return array()
-   *   An array of all language types.
+   * @return array
+   *   An array of all language types where the keys of each are the language type
+   *   name and its value is its configurability (TRUE/FALSE).
    */
-  protected function getLanguageTypes() {
-    return language_types_get_all();
+  public function getLanguageTypes() {
+    $types = \Drupal::config('system.language.types')->get('all');
+    return $types ? $types : array_keys($this->getTypeDefaults());
+  }
+
+  /**
+   * Returns a list of the built-in language types.
+   *
+   * @return array
+   *   An array of key-values pairs where the key is the language type name and
+   *   the value is its configurability (TRUE/FALSE).
+   */
+  public function getTypeDefaults() {
+    return array(
+      Language::TYPE_INTERFACE => TRUE,
+      Language::TYPE_CONTENT => FALSE,
+      Language::TYPE_URL => FALSE,
+    );
+  }
+
+  /**
+   * Returns the language switch links for the given language type.
+   *
+   * @param $type
+   *   The language type.
+   * @param $path
+   *   The internal path the switch links will be relative to.
+   *
+   * @return array
+   *   A keyed array of links ready to be themed.
+   */
+  function getLanguageNegotiationSwitchLinks($type, $path) {
+    $links = FALSE;
+    $negotiation = variable_get("language_negotiation_$type", array());
+
+    foreach ($negotiation as $method_id => $method) {
+      if (isset($method['callbacks']['language_switch'])) {
+        if (isset($method['file'])) {
+          require_once DRUPAL_ROOT . '/' . $method['file'];
+        }
+
+        $callback = $method['callbacks']['language_switch'];
+        $result = $callback($type, $path);
+
+        if (!empty($result)) {
+          // Allow modules to provide translations for specific links.
+          \Drupal::moduleHandler()->alter('language_switch_links', $result, $type, $path);
+          $links = (object) array('links' => $result, 'method_id' => $method_id);
+          break;
+        }
+      }
+    }
+
+    return $links;
   }
 
   /**
@@ -177,16 +337,104 @@ protected function getLanguageTypes() {
    * @return \Drupal\Core\Language\Language
    *   A language object.
    */
-  protected function getLanguageDefault() {
-    $default_info = variable_get('language_default', array(
-      'id' => 'en',
-      'name' => 'English',
-      'direction' => 0,
-      'weight' => 0,
-      'locked' => 0,
-    ));
-    $default_info['default'] = TRUE;
-    return new Language($default_info);
+  public function getLanguageDefault() {
+    // @todo convert to CMI https://drupal.org/node/1827038
+    $default_info = variable_get('language_default', Language::$defaultValues);
+    return new Language($default_info + array('default' => TRUE));
+  }
+
+  /**
+    * Returns a list of languages set up on the site.
+    *
+    * @param $flags
+    *   (optional) Specifies the state of the languages that have to be returned.
+    *   It can be: Language::STATE_CONFIGURABLE, Language::STATE_LOCKED, Language::STATE_ALL.
+    *
+    * @return array
+    *   An associative array of languages, keyed by the language code, ordered by
+    *   weight ascending and name ascending.
+    */
+  public function getLanguageList($flags = Language::STATE_CONFIGURABLE) {
+    // Initialize master language list.
+    if (!isset($this->languageList)) {
+      // Initialize local language list cache.
+      $this->languageList = array();
+      // Fill in master language list based on current configuration.
+      $default = $this->getLanguageDefault();
+      // No language module, so use the default language only.
+      $this->languageList = array($default->id => $default);
+      // Add the special languages, they will be filtered later if needed.
+      $this->languageList += $this->getDefaultLockedLanguages($default->weight);
+    }
+
+    // Filter the full list of languages based on the value of the $all flag. By
+    // default we remove the locked languages, but the caller may request for
+    // those languages to be added as well.
+    $filtered_languages = array();
+
+    // Add the site's default language if flagged as allowed value.
+    if ($flags & Language::STATE_SITE_DEFAULT) {
+      $default = isset($default) ? $default : $this->getLanguageDefault();
+      // Rename the default language.
+      $default->name = t("Site's default language (@lang_name)", array('@lang_name' => $default->name));
+      $filtered_languages['site_default'] = $default;
+    }
+
+    foreach ($this->languageList as $id => $language) {
+      if (($language->locked && !($flags & Language::STATE_LOCKED)) || (!$language->locked && !($flags & Language::STATE_CONFIGURABLE))) {
+        continue;
+       }
+      $filtered_languages[$id] = $language;
+    }
+
+    return $filtered_languages;
+  }
+
+  /**
+   * Loads a language object from the database.
+   *
+   * @param string $langcode
+   *   The language code.
+   *
+   * @return \Drupal\core\Language\Language|null
+   *   A fully-populated language object or NULL.
+   */
+  public function loadLanguage($langcode) {
+    $languages = $this->getLanguageList(Language::STATE_ALL);
+    return isset($languages[$langcode]) ? $languages[$langcode] : NULL;
+  }
+
+  /**
+   * Returns a list of the default locked languages.
+   *
+   * @param int $weight
+   *   (optional) An integer value that is used as the start value for the
+   *   weights of the locked languages.
+   *
+   * @return array
+   *   An array of language objects.
+   */
+  public function getDefaultLockedLanguages($weight = 0) {
+    $languages = array();
+
+    $locked_language = array(
+      'default' => FALSE,
+      'locked' => TRUE,
+      'enabled' => TRUE,
+     );
+    $languages[Language::LANGCODE_NOT_SPECIFIED] = new Language(array(
+      'id' => Language::LANGCODE_NOT_SPECIFIED,
+      'name' => t('Not specified'),
+      'weight' => ++$weight,
+    ) + $locked_language);
+
+    $languages[Language::LANGCODE_NOT_APPLICABLE] = new Language(array(
+      'id' => Language::LANGCODE_NOT_APPLICABLE,
+      'name' => t('Not applicable'),
+      'weight' => ++$weight,
+    ) + $locked_language);
+
+    return $languages;
   }
 
   /**
@@ -308,4 +556,14 @@ public static function getStandardLanguageList() {
     );
   }
 
+  /**
+   * Returns an array of the available language types.
+   *
+   * @return array
+   *   An array of all language types where the keys of each are the language type
+   *   name and its value is its configurability (TRUE/FALSE).
+   */
+  public function getTypes() {
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Language/LanguageNegotiationInterface.php b/core/lib/Drupal/Core/Language/LanguageNegotiationInterface.php
new file mode 100644
index 0000000..9b1c666
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/LanguageNegotiationInterface.php
@@ -0,0 +1,37 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\LanguageNegotiationInterface.
+ */
+
+namespace Drupal\Core\Language;
+
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Interface for language negotiation classes.
+ */
+interface LanguageNegotiationInterface {
+
+  /**
+   * Sets the language manager.
+   *
+   * @param LanguageManager $languageManager
+   *   The language manager to be used to retrieve the language list and the
+   *   already negotiated languages.
+   */
+  public function setLanguageManager(LanguageManager $languageManager);
+
+  /**
+   * Performs language negotiation.
+   *
+   * @param array $languages
+   *   An array of valid languages.
+   * @param \Symfony\Component\HttpFoundation\Request $request
+   *   (optional) The current request. Defaults to NULL if it has not been
+   *   initialized yet.
+   */
+  public function negotiateLanguage(array $languages, Request $request = NULL);
+
+}
diff --git a/core/lib/Drupal/Core/Language/LanguageNegotiationMethodBase.php b/core/lib/Drupal/Core/Language/LanguageNegotiationMethodBase.php
new file mode 100644
index 0000000..c2c7dce
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/LanguageNegotiationMethodBase.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\LanguageNegotiationMethodBase.
+ */
+
+namespace Drupal\Core\Language;
+
+use Drupal\Core\Language\LanguageManager;
+use Drupal\Core\Language\LanguageNegotiationInterface;
+
+/**
+ * Base class for language negotiation methods.
+ */
+abstract class LanguageNegotiationMethodBase implements LanguageNegotiationInterface {
+
+  /**
+   * The language manager.
+   *
+   * @var \Drupal\Core\Language\LanguageManager
+   */
+  protected $languageManager;
+
+  /**
+   * The language negotiation configuration.
+   *
+   * @var array
+   */
+  protected $config;
+
+  /**
+   * Constructs a new LanguageNegotiationMethodBase object.
+   *
+   * @param array $config
+   */
+  public function __construct(array $config) {
+    $this->config = $config;
+  }
+
+  /**
+   * Implements \Drupal\Core\Language\LanguageNegotiationInterface::setLanguageManager().
+   */
+  public function setLanguageManager(LanguageManager $languageManager) {
+    $this->languageManager = $languageManager;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Language/LanguageNegotiationMethodManager.php b/core/lib/Drupal/Core/Language/LanguageNegotiationMethodManager.php
new file mode 100644
index 0000000..0410d26
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/LanguageNegotiationMethodManager.php
@@ -0,0 +1,39 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\LanguageNegotiationMethodManager.
+ */
+
+namespace Drupal\Core\Language;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Plugin\DefaultPluginManager;
+
+/**
+ * Manages language negotiation methods.
+ */
+class LanguageNegotiationMethodManager extends DefaultPluginManager {
+
+  /**
+   * Constructs a new LanguageNegotiationMethodManager object.
+   *
+   * @param \Traversable $namespaces
+   *   An object that implements \Traversable which contains the root paths
+   *   keyed by the corresponding namespace to look for plugin implementations.
+   */
+  public function __construct(\Traversable $namespaces, CacheBackendInterface $cache_backend) {
+    parent::__construct('Plugin/LanguageNegotiation', $namespaces);
+    $this->cacheBackend = $cache_backend;
+    $this->cacheKeyPrefix = 'language_negotiation_plugins';
+    $this->cacheKey = 'language_negotiation_plugins';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function clearCachedDefinitions() {
+    $this->cacheBackend->delete($this->cacheKey);
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php
new file mode 100644
index 0000000..5b9a68d
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationBrowser.php
@@ -0,0 +1,148 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationBrowser.
+ */
+
+namespace Drupal\Core\Language\Plugin\LanguageNegotiation;
+
+use Drupal\Core\Language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class for identifying language from the Accept-language HTTP header we got.
+ *
+ * The algorithm works as follows:
+ * - map browser language codes to Drupal language codes.
+ * - order all browser language codes by qvalue from high to low.
+ * - add generic browser language codes if they aren't already specified
+ *   but with a slightly lower qvalue.
+ * - find the most specific Drupal language code with the highest qvalue.
+ * - if 2 or more languages are having the same qvalue, respect the order of
+ *   them inside the $languages array.
+ *
+ * We perform browser accept-language parsing only if page cache is disabled,
+ * otherwise we would cache a user-specific preference.
+ *
+ * @Plugin(
+ *   id = Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationBrowser::METHOD_ID,
+ *   weight = -2,
+ *   cache = 0,
+ *   name = @Translation("Browser"),
+ *   description = @Translation("Language from the browser's language settings."),
+ *   config = "admin/config/regional/language/detection/browser"
+ * )
+ */
+class LanguageNegotiationBrowser extends LanguageNegotiationMethodBase {
+
+  /**
+   * The language negotiation method id.
+   */
+  const METHOD_ID = 'language-browser';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function negotiateLanguage(array $languages, Request $request = NULL) {
+    if (!$request || !$request->server->get('HTTP_ACCEPT_LANGUAGE')) {
+      return FALSE;
+    }
+
+    // The Accept-Language header contains information about the language
+    // preferences configured in the user's browser / operating system. RFC 2616
+    // (section 14.4) defines the Accept-Language header as follows:
+    //   Accept-Language = "Accept-Language" ":"
+    //                  1#( language-range [ ";" "q" "=" qvalue ] )
+    //   language-range  = ( ( 1*8ALPHA *( "-" 1*8ALPHA ) ) | "*" )
+    // Samples: "hu, en-us;q=0.66, en;q=0.33", "hu,en-us;q=0.5"
+    $browser_langcodes = array();
+    if (preg_match_all('@(?<=[, ]|^)([a-zA-Z-]+|\*)(?:;q=([0-9.]+))?(?:$|\s*,\s*)@', trim($request->server->get('HTTP_ACCEPT_LANGUAGE')), $matches, PREG_SET_ORDER)) {
+      // Load custom mappings to support browsers that are sending non standard
+      // language codes.
+      $mappings = $this->config['browser']['mappings'];
+
+      foreach ($matches as $match) {
+        if ($mappings) {
+          $langcode = strtolower($match[1]);
+          foreach ($mappings as $browser_langcode => $drupal_langcode) {
+            if ($langcode == $browser_langcode) {
+              $match[1] = $drupal_langcode;
+            }
+          }
+        }
+        // We can safely use strtolower() here, tags are ASCII.
+        // RFC2616 mandates that the decimal part is no more than three digits,
+        // so we multiply the qvalue by 1000 to avoid floating point
+        // comparisons.
+        $langcode = strtolower($match[1]);
+        $qvalue = isset($match[2]) ? (float) $match[2] : 1;
+        $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..4c48934
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationSelected.php
@@ -0,0 +1,40 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationSelected.
+ */
+
+namespace Drupal\Core\Language\Plugin\LanguageNegotiation;
+
+use Drupal\Core\Language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class for identifying language from a selected language.
+ *
+ * @Plugin(
+ *   id = Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationSelected::METHOD_ID,
+ *   weight = 12,
+ *   name = @Translation("Selected language"),
+ *   description = @Translation("Language based on a selected language."),
+ *   config = "admin/config/regional/language/detection/selected"
+ * )
+ */
+class LanguageNegotiationSelected extends LanguageNegotiationMethodBase {
+
+  /**
+   * The language negotiation method id.
+   */
+  const METHOD_ID = 'language-selected';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function negotiateLanguage(array $languages, Request $request = NULL) {
+    // Replace the site's default langcode by its real value.
+    $langcode = isset($this->config['selected_langcode']) ? $this->config['selected_langcode'] : FALSE;
+    return $langcode != 'site_default' ? $langcode : $this->languageManager->getLanguageDefault()->id;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationSession.php b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationSession.php
new file mode 100644
index 0000000..759d5d4
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationSession.php
@@ -0,0 +1,56 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationSession.
+ */
+
+namespace Drupal\Core\Language\Plugin\LanguageNegotiation;
+
+use Drupal\Core\Language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Identify language from a request/session parameter.
+ *
+ * @Plugin(
+ *   id = Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationSession::METHOD_ID,
+ *   weight = -6,
+ *   name = @Translation("Session"),
+ *   description = @Translation("Language from a request/session parameter."),
+ *   config = "admin/config/regional/language/detection/session"
+ * )
+ */
+class LanguageNegotiationSession extends LanguageNegotiationMethodBase {
+
+  /**
+   * The language negotiation method id.
+   */
+  const METHOD_ID = 'language-session';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function negotiateLanguage(array $languages, Request $request = NULL) {
+    $param = $this->config['session']['parameter'];
+
+    // Request parameter: we need to update the session parameter only if we
+    // have an authenticated user.
+    $langcode = $request->query->get($param);
+    if ($langcode) {
+      global $user;
+      $languages = $this->languageManager->getLanguageList();
+      if ($user->isAuthenticated() && isset($languages[$langcode])) {
+        $_SESSION[$param] = $langcode;
+      }
+    }
+
+    // Session parameter.
+    if (isset($_SESSION[$param])) {
+      return $_SESSION[$param];
+    }
+
+    return FALSE;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUI.php b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUI.php
new file mode 100644
index 0000000..6492d6f
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUI.php
@@ -0,0 +1,38 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUI.
+ */
+
+namespace Drupal\Core\Language\Plugin\LanguageNegotiation;
+
+use Drupal\Core\Language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class for identifying the language from the current interface language.
+ *
+ * @Plugin(
+ *   id = Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUI::METHOD_ID,
+ *   types = {Drupal\Core\Language\Language::TYPE_CONTENT},
+ *   weight = 9,
+ *   name = @Translation("Interface"),
+ *   description = @Translation("Use the detected interface language.")
+ * )
+ */
+class LanguageNegotiationUI extends LanguageNegotiationMethodBase {
+
+  /**
+   * The language negotiation method id.
+   */
+  const METHOD_ID = 'language-interface';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function negotiateLanguage(array $languages, Request $request = NULL) {
+    return $this->languageManager->getLanguage()->id;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php
new file mode 100644
index 0000000..756d602
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUrl.php
@@ -0,0 +1,98 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUrl.
+ */
+
+namespace Drupal\Core\Language\Plugin\LanguageNegotiation;
+
+use Drupal\Core\Language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class for identifying language via URL prefix or domain.
+ *
+ * @Plugin(
+ *   id = Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUrl::METHOD_ID,
+ *   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..bd47eaa
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUrlFallback.php
@@ -0,0 +1,70 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUrlFallback.
+ */
+
+namespace Drupal\Core\Language\Plugin\LanguageNegotiation;
+
+use Drupal\Core\Language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class that determines the language to be assigned to URLs when none is
+ * detected.
+ *
+ * The language negotiation process has a fallback chain that ends with the
+ * default language negotiation method. Each built-in language type has a
+ * separate initialization:
+ * - Interface language, which is the only configurable one, always gets a valid
+ *   value. If no request-specific language is detected, the default language
+ *   will be used.
+ * - Content language merely inherits the interface language by default.
+ * - URL language is detected from the requested URL and will be used to rewrite
+ *   URLs appearing in the page being rendered. If no language can be detected,
+ *   there are two possibilities:
+ *   - If the default language has no configured path prefix or domain, then the
+ *     default language is used. This guarantees that (missing) URL prefixes are
+ *     preserved when navigating through the site.
+ *   - If the default language has a configured path prefix or domain, a
+ *     requested URL having an empty prefix or domain is an anomaly that must be
+ *     fixed. This is done by introducing a prefix or domain in the rendered
+ *     page matching the detected interface language.
+ *
+ * @Plugin(
+ *   id = Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUrlFallback::METHOD_ID,
+ *   types = {Drupal\Core\Language\Language::TYPE_URL},
+ *   weight = 8,
+ *   name = @Translation("URL fallback"),
+ *   description = @Translation("Use an already detected language for URLs if none is found..")
+ * )
+ */
+class LanguageNegotiationUrlFallback extends LanguageNegotiationMethodBase {
+
+  /**
+   * The language negotiation method id.
+   */
+  const METHOD_ID = 'language-url-fallback';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function negotiateLanguage(array $languages, Request $request = NULL) {
+    $default = $this->languageManager->getLanguageDefault();
+    $prefix = ($this->config['url']['source'] == LanguageNegotiationUrl::CONFIG_PATH_PREFIX);
+
+    // If the default language is not configured to convey language information,
+    // a missing URL language information indicates that URL language should be
+    // the default one, otherwise we fall back to an already detected language.
+    $domains = $this->config['url']['domains'];
+    $prefixes = $this->config['url']['prefixes'];
+    if (($prefix && empty($prefixes[$default->id])) || (!$prefix && empty($domains[$default->id]))) {
+      return $default->id;
+    }
+    else {
+      return $this->languageManager->getLanguage()->id;
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUser.php b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUser.php
new file mode 100644
index 0000000..7084d94
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUser.php
@@ -0,0 +1,45 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUrl.
+ */
+
+namespace Drupal\Core\Language\Plugin\LanguageNegotiation;
+
+use Drupal\Core\Language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Class for identifying language from the user preferences.
+ *
+ * @Plugin(
+ *   id = {Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUser::METHOD_ID},
+ *   weight = -4,
+ *   name = @Translation("User"),
+ *   description = @Translation("Follow the user's language preference.")
+ * )
+ */
+class LanguageNegotiationUser extends LanguageNegotiationMethodBase {
+
+  /**
+   * The language negotiation method id.
+   */
+  const METHOD_ID = 'language-user';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function negotiateLanguage(array $languages, Request $request = NULL) {
+    // User preference (only for authenticated users).
+    $user = $request->attributes->get('_account');
+
+    if ($user->isAuthenticated() && isset($languages[$user->getPreferredLangcode()])) {
+      return $user->getPreferredLangcode();
+    }
+
+    // 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..21ba800
--- /dev/null
+++ b/core/lib/Drupal/Core/Language/Plugin/LanguageNegotiation/LanguageNegotiationUserAdmin.php
@@ -0,0 +1,48 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUserAdmin.
+ */
+
+namespace Drupal\Core\Language\Plugin\LanguageNegotiation;
+
+use Drupal\Core\Language\LanguageNegotiationMethodBase;
+use Symfony\Component\HttpFoundation\Request;
+
+/**
+ * Identifies admin language from the user preferences.
+ *
+ * @Plugin(
+ *   id = Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationUserAdmin::METHOD_ID,
+ *   types = {Drupal\Core\Language\Language::TYPE_INTERFACE},
+ *   weight = 10,
+ *   name = @Translation("Account administration pages"),
+ *   description = @Translation("Account administration pages language setting.")
+ * )
+ */
+class LanguageNegotiationUserAdmin extends LanguageNegotiationMethodBase {
+
+  /**
+   * The language negotiation method id.
+   */
+  const METHOD_ID = 'language-user-admin';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function negotiateLanguage(array $languages, Request $request = NULL) {
+    // User preference (only for authenticated users).
+    $user = $request->attributes->get('_account');
+
+    // @todo Avoid calling _current_path() and path_is_admin() directly.
+    $request_path = $request ? urldecode(trim($request->getPathInfo(), '/')) : _current_path();
+    if ($user->isAuthenticated() && isset($languages[$user->getPreferredAdminLangcode()]) && path_is_admin($request_path)) {
+      return $user->getPreferredAdminLangcode();
+    }
+
+    // No language preference from the user or not on an admin path.
+    return FALSE;
+  }
+
+}
diff --git a/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php b/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php
index 915e4bb..0ecfb36 100644
--- a/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php
+++ b/core/modules/block/lib/Drupal/block/Tests/BlockLanguageTest.php
@@ -74,10 +74,9 @@ public function testLanguageBlockVisibility() {
     $this->drupalPostForm('admin/config/regional/settings', $edit, t('Save configuration'));
 
     // Reset the static cache of the language list.
-    drupal_static_reset('language_list');
-
+    $this->container->get('language_manager')->reset();
     // Check that a page has a block.
-    $this->drupalget('', array('language' => language_load('en')));
+    $this->drupalGet('', array('language' => language_load('en')));
     $this->assertText('Powered by Drupal', 'The body of the custom block appears on the page.');
 
     // Check that a page doesn't has a block for the current language anymore.
diff --git a/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php b/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php
index c35bc78..c344ad3 100644
--- a/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php
+++ b/core/modules/comment/lib/Drupal/comment/Tests/CommentLanguageTest.php
@@ -80,7 +80,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 f62b933..e3c5450 100644
--- a/core/modules/language/language.admin.inc
+++ b/core/modules/language/language.admin.inc
@@ -281,6 +281,9 @@ function language_negotiation_configure_form_submit($form, &$form_state) {
     \Drupal::service('plugin.manager.block')->clearCachedDefinitions();
   }
 
+  // Rebuild the container to update the submitted settings.
+  drupal_rebuild_language_negotiation_settings();
+
   $form_state['redirect'] = 'admin/config/regional/language/detection';
   drupal_set_message(t('Language negotiation configuration saved.'));
 }
diff --git a/core/modules/language/language.install b/core/modules/language/language.install
index 6a8c228..3bcb500 100644
--- a/core/modules/language/language.install
+++ b/core/modules/language/language.install
@@ -38,7 +38,7 @@ function language_uninstall() {
   variable_del('language_content_type_default');
   variable_del('language_content_type_negotiation');
 
-  foreach (language_types_get_all() as $type) {
+  foreach (Drupal::languageManager()->getLanguageTypes() as $type) {
     variable_del("language_negotiation_$type");
     variable_del("language_negotiation_methods_weight_$type");
   }
diff --git a/core/modules/language/language.module b/core/modules/language/language.module
index 32d38eb..c949bca 100644
--- a/core/modules/language/language.module
+++ b/core/modules/language/language.module
@@ -496,8 +496,8 @@ function language_save($language) {
     variable_set('language_default', (array) $language);
   }
 
-  // Kill the static cache in language_list().
-  drupal_static_reset('language_list');
+  // Reset the language information.
+  Drupal::languageManager()->reset();
 
   // Update language count based on unlocked language count.
   language_update_count();
@@ -550,13 +550,13 @@ function language_delete($langcode) {
     // Remove the language.
     entity_delete_multiple('language_entity', array($language->id));
 
-    drupal_static_reset('language_list');
-
     language_update_count();
 
     // Update weight of locked system languages.
     language_update_locked_weights();
 
+    Drupal::languageManager()->reset();
+
     $t_args = array('%language' => $language->name, '%langcode' => $language->id);
     watchdog('language', 'The %language (%langcode) language has been removed.', $t_args);
     return TRUE;
@@ -621,88 +621,6 @@ function language_language_types_info() {
 }
 
 /**
- * Implements hook_language_negotiation_info().
- */
-function language_language_negotiation_info() {
-  language_negotiation_include();
-  $file = drupal_get_path('module', 'language') . '/language.negotiation.inc';
-
-  $negotiation_info = array();
-  $negotiation_info[LANGUAGE_NEGOTIATION_URL] = array(
-    'types' => array(Language::TYPE_CONTENT, Language::TYPE_INTERFACE, Language::TYPE_URL),
-    'callbacks' => array(
-      'negotiation' => 'language_from_url',
-      'language_switch' => 'language_switcher_url',
-    ),
-    'file' => $file,
-    'weight' => -8,
-    'name' => t('URL'),
-    'description' => t('Language from the URL (Path prefix or domain).'),
-    'config' => 'admin/config/regional/language/detection/url',
-  );
-
-  $negotiation_info[LANGUAGE_NEGOTIATION_SESSION] = array(
-    'callbacks' => array(
-      'negotiation' => 'language_from_session',
-      'language_switch' => 'language_switcher_session',
-      'url_rewrite' => 'language_url_rewrite_session',
-    ),
-    'file' => $file,
-    'weight' => -6,
-    'name' => t('Session'),
-    'description' => t('Language from a request/session parameter.'),
-    'config' => 'admin/config/regional/language/detection/session',
-  );
-
-  $negotiation_info[LANGUAGE_NEGOTIATION_USER] = array(
-    'callbacks' => array('negotiation' => 'language_from_user'),
-    'file' => $file,
-    'weight' => -4,
-    'name' => t('Account preference for site'),
-    'description' => t("The language setting for the site in the user's account."),
-  );
-
-  $negotiation_info[LANGUAGE_NEGOTIATION_BROWSER] = array(
-    'callbacks' => array('negotiation' => 'language_from_browser'),
-    'file' => $file,
-    'weight' => -2,
-    'cache' => 0,
-    'name' => t('Browser'),
-    'description' => t("Language from the browser's language settings."),
-    'config' => 'admin/config/regional/language/detection/browser',
-  );
-
-  $negotiation_info[LANGUAGE_NEGOTIATION_INTERFACE] = array(
-    'types' => array(Language::TYPE_CONTENT),
-    'callbacks' => array('negotiation' => 'language_from_interface'),
-    'file' => $file,
-    'weight' => 8,
-    'name' => t('Interface'),
-    'description' => t('Use the detected interface language.'),
-  );
-
-  $negotiation_info[LANGUAGE_NEGOTIATION_URL_FALLBACK] = array(
-    'types' => array(Language::TYPE_URL),
-    'callbacks' => array('negotiation' => 'language_url_fallback'),
-    'file' => $file,
-    'weight' => 8,
-    'name' => t('URL fallback'),
-    'description' => t('Use an already detected language for URLs if none is found.'),
-  );
-
-  $negotiation_info[LANGUAGE_NEGOTIATION_USER_ADMIN] = array(
-    'types' => array(Language::TYPE_INTERFACE),
-    'callbacks' => array('negotiation' => 'language_from_user_admin'),
-    'file' => $file,
-    'weight' => 10,
-    'name' => t('Account preference for administration pages'),
-    'description' => t("The language setting for account administration pages in the user's account."),
-  );
-
-  return $negotiation_info;
-}
-
-/**
  * Include negotiation backend functionality.
  */
 function language_negotiation_include() {
@@ -853,4 +771,5 @@ function language_system_regional_settings_form_submit($form, &$form_state) {
   $language = $languages[$form_state['values']['site_default_language']];
   $language->default = TRUE;
   language_save($language);
+  drupal_rebuild_language_negotiation_settings();
 }
diff --git a/core/modules/language/language.negotiation.inc b/core/modules/language/language.negotiation.inc
index 43cd607..00840e8 100644
--- a/core/modules/language/language.negotiation.inc
+++ b/core/modules/language/language.negotiation.inc
@@ -50,338 +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;
-      // Take the highest qvalue for this langcode. Although the request
-      // supposedly contains unique langcodes, our mapping possibly resolves
-      // to the same langcode for different qvalues. Keep the highest.
-      $browser_langcodes[$langcode] = max(
-        (int) ($qvalue * 1000),
-        (isset($browser_langcodes[$langcode]) ? $browser_langcodes[$langcode] : 0)
-      );
-    }
-  }
-
-  // We should take pristine values from the HTTP headers, but Internet Explorer
-  // from version 7 sends only specific language tags (eg. fr-CA) without the
-  // corresponding generic tag (fr) unless explicitly configured. In that case,
-  // we assume that the lowest value of the specific tags is the value of the
-  // generic language to be as close to the HTTP 1.1 spec as possible.
-  // See http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.4 and
-  // http://blogs.msdn.com/b/ie/archive/2006/10/17/accept-language-header-for-internet-explorer-7.aspx
-  asort($browser_langcodes);
-  foreach ($browser_langcodes as $langcode => $qvalue) {
-    // For Chinese languages the generic tag is either zh-hans or zh-hant, so we
-    // need to handle this separately, we can not split $langcode on the
-    // first occurence of '-' otherwise we get a non-existing language zh.
-    // All other languages use a langcode without a '-', so we can safely split
-    // on the first occurence of it.
-    $generic_tag = '';
-    if (strlen($langcode) > 7 && (substr($langcode, 0, 7) == 'zh-hant' || substr($langcode, 0, 7) == 'zh-hans')) {
-      $generic_tag = substr($langcode, 0, 7);
-    }
-    else {
-      $generic_tag = strtok($langcode, '-');
-    }
-    if (!empty($generic_tag) && !isset($browser_langcodes[$generic_tag])) {
-      // Add the generic langcode, but make sure it has a lower qvalue as the
-      // more specific one, so the more specific one gets selected if it's
-      // defined by both the browser and Drupal.
-      $browser_langcodes[$generic_tag] = $qvalue - 0.1;
-    }
-  }
-
-  // Find the enabled language with the greatest qvalue, following the rules of
-  // RFC 2616 (section 14.4). If several languages have the same qvalue, prefer
-  // the one with the greatest weight.
-  $best_match_langcode = FALSE;
-  $max_qvalue = 0;
-  foreach ($languages as $langcode => $language) {
-    // Language tags are case insensitive (RFC2616, sec 3.10).
-    $langcode = strtolower($langcode);
-
-    // If nothing matches below, the default qvalue is the one of the wildcard
-    // language, if set, or is 0 (which will never match).
-    $qvalue = isset($browser_langcodes['*']) ? $browser_langcodes['*'] : 0;
-
-    // Find the longest possible prefix of the browser-supplied language ('the
-    // language-range') that matches this site language ('the language tag').
-    $prefix = $langcode;
-    do {
-      if (isset($browser_langcodes[$prefix])) {
-        $qvalue = $browser_langcodes[$prefix];
-        break;
-      }
-    }
-    while ($prefix = substr($prefix, 0, strrpos($prefix, '-')));
-
-    // Find the best match.
-    if ($qvalue > $max_qvalue) {
-      $best_match_langcode = $language->id;
-      $max_qvalue = $qvalue;
-    }
-  }
-
-  return $best_match_langcode;
-}
-
-/**
- * Identify language from the user preferences.
- *
- * @param $languages
- *   An array of valid language objects.
- *
- * @return
- *   A valid language code on success, FALSE otherwise.
- */
-function language_from_user($languages) {
-  // User preference (only for authenticated users).
-  global $user;
-
-  if ($user->id()) {
-    $langcode = $user->getPreferredLangcode();
-    $default_langcode = language_default()->id;
-    if (!empty($langcode) && $langcode != $default_langcode && isset($languages[$langcode])) {
-      return $langcode;
-    }
-  }
-
-  // No language preference from the user.
-  return FALSE;
-}
-
-/**
- * Identifies admin language from the user preferences.
- *
- * @param $languages
- *   An array of valid language objects.
- *
- * @param \Symfony\Component\HttpFoundation\Request|null $request
- *   (optional) The HttpRequest object representing the current request.
- *   Defaults to NULL.
- *
- * @return
- *   A valid language code on success, FALSE otherwise.
- */
-function language_from_user_admin(array $languages, Request $request = NULL) {
-  // User preference (only for authenticated users).
-  global $user;
-
-  if ($user->id()) {
-    $request_path = $request ? urldecode(trim($request->getPathInfo(), '/')) : _current_path();
-    $langcode = $user->getPreferredAdminLangcode();
-    $default_langcode = language_default()->id;
-    if (!empty($langcode) && $langcode != $default_langcode && isset($languages[$langcode]) && path_is_admin($request_path)) {
-      return $langcode;
-    }
-  }
-
-  // No language preference from the user or not on an admin path.
-  return FALSE;
-}
-
-/**
- * Identify language from a request/session parameter.
- *
- * @param $languages
- *   An array of valid language objects.
- *
- * @return
- *   A valid language code on success, FALSE otherwise.
- */
-function language_from_session($languages) {
-  $param = \Drupal::config('language.negotiation')->get('session.parameter');
-
-  // Request parameter: we need to update the session parameter only if we have
-  // an authenticated user.
-  if (isset($_GET[$param]) && isset($languages[$langcode = $_GET[$param]])) {
-    global $user;
-    if ($user->id()) {
-      $_SESSION[$param] = $langcode;
-    }
-    return $langcode;
-  }
-
-  // Session parameter.
-  if (isset($_SESSION[$param])) {
-    return $_SESSION[$param];
-  }
-
-  return FALSE;
-}
-
-/**
- * Identify language via URL prefix or domain.
- *
- * @param $languages
- *   An array of valid language objects.
- *
- * @param \Symfony\Component\HttpFoundation\Request|null $request
- *   (optional) The HttpRequest object representing the current request.
- *   Defaults to NULL.
- *
- * @return
- *   A valid language code on success, FALSE otherwise.
- */
-function language_from_url($languages, Request $request = NULL) {
-  $language_url = FALSE;
-
-  if (!language_negotiation_method_enabled(LANGUAGE_NEGOTIATION_URL) || !$request) {
-    return $language_url;
-  }
-
-  switch (\Drupal::config('language.negotiation')->get('url.source')) {
-    case LANGUAGE_NEGOTIATION_URL_PREFIX:
-
-      $request_path = urldecode(trim($request->getPathInfo(), '/'));
-      list($language, $path) = language_url_split_prefix($request_path, $languages);
-
-      if ($language !== FALSE) {
-        $language_url = $language->id;
-      }
-      break;
-
-    case LANGUAGE_NEGOTIATION_URL_DOMAIN:
-      // Get only the host, not the port.
-      $http_host= $_SERVER['HTTP_HOST'];
-      if (strpos($http_host, ':') !== FALSE) {
-        $http_host_tmp = explode(':', $http_host);
-        $http_host = current($http_host_tmp);
-      }
-      $domains = language_negotiation_url_domains();
-      foreach ($languages as $language) {
-        // Skip the check if the language doesn't have a domain.
-        if (!empty($domains[$language->id])) {
-          // Ensure that there is exactly one protocol in the URL when checking
-          // the hostname.
-          $host = 'http://' . str_replace(array('http://', 'https://'), '', $domains[$language->id]);
-          $host = parse_url($host, PHP_URL_HOST);
-          if ($http_host == $host) {
-            $language_url = $language->id;
-            break;
-          }
-        }
-      }
-      break;
-  }
-
-  return $language_url;
-}
-
-/**
- * Determines the language to be assigned to URLs when none is detected.
- *
- * The language negotiation process has a fallback chain that ends with the
- * default language negotiation method. Each built-in language type has a
- * separate initialization:
- * - Interface language, which is the only configurable one, always gets a valid
- *   value. If no request-specific language is detected, the default language
- *   will be used.
- * - Content language merely inherits the interface language by default.
- * - URL language is detected from the requested URL and will be used to rewrite
- *   URLs appearing in the page being rendered. If no language can be detected,
- *   there are two possibilities:
- *   - If the default language has no configured path prefix or domain, then the
- *     default language is used. This guarantees that (missing) URL prefixes are
- *     preserved when navigating through the site.
- *   - If the default language has a configured path prefix or domain, a
- *     requested URL having an empty prefix or domain is an anomaly that must be
- *     fixed. This is done by introducing a prefix or domain in the rendered
- *     page matching the detected interface language.
- *
- * @param $languages
- *   (optional) An array of valid language objects. This is passed by
- *   language_negotiation_method_invoke() to every language method callback,
- *   but it is not actually needed here. Defaults to NULL.
- *
- * @param $request
- *   (optional) The HttpRequest object representing the current request.
- *
- * @param $language_type
- *   (optional) The language type to fall back to. Defaults to the interface
- *   language.
- *
- * @return
- *   A valid language code.
- */
-function language_url_fallback($language = NULL, $request = NULL, $language_type = Language::TYPE_INTERFACE) {
-  $default = language_default();
-  $prefix = (\Drupal::config('language.negotiation')->get('url.source') == LANGUAGE_NEGOTIATION_URL_PREFIX);
-
-  // If the default language is not configured to convey language information,
-  // a missing URL language information indicates that URL language should be
-  // the default one, otherwise we fall back to an already detected language.
-  $domains = language_negotiation_url_domains();
-  $prefixes = language_negotiation_url_prefixes();
-  if (($prefix && empty($prefixes[$default->id])) || (!$prefix && empty($domains[$default->id]))) {
-    return $default->id;
-  }
-  else {
-    $langcode = language($language_type)->id;
-    return $langcode;
-  }
-}
-
-/**
  * Return links for the URL language switcher block.
  *
  * Translation links may be provided by other modules.
diff --git a/core/modules/language/lib/Drupal/language/Form/LanguageAddForm.php b/core/modules/language/lib/Drupal/language/Form/LanguageAddForm.php
index ccc833c..4cabb9b 100644
--- a/core/modules/language/lib/Drupal/language/Form/LanguageAddForm.php
+++ b/core/modules/language/lib/Drupal/language/Form/LanguageAddForm.php
@@ -91,6 +91,10 @@ public function submitForm(array &$form, array &$form_state) {
     }
     // Save the language and inform the user that it happened.
     $language = language_save($language);
+
+    // Rebuild the container to update the submitted settings.
+    drupal_rebuild_language_negotiation_settings();
+
     drupal_set_message($this->t('The language %language has been created and can now be used.', array('%language' => $language->name)));
 
     // Tell the user they have the option to add a language switcher block
diff --git a/core/modules/language/lib/Drupal/language/Form/LanguageEditForm.php b/core/modules/language/lib/Drupal/language/Form/LanguageEditForm.php
index 9d8b6aa..318f54c 100644
--- a/core/modules/language/lib/Drupal/language/Form/LanguageEditForm.php
+++ b/core/modules/language/lib/Drupal/language/Form/LanguageEditForm.php
@@ -54,6 +54,8 @@ public function submitForm(array &$form, array &$form_state) {
     $language->name = $form_state['values']['name'];
     $language->direction = $form_state['values']['direction'];
     language_save($language);
+    // Rebuild the container to update the submitted settings.
+    drupal_rebuild_language_negotiation_settings();
     $form_state['redirect'] = 'admin/config/regional/language';
   }
 
diff --git a/core/modules/language/lib/Drupal/language/Form/NegotiationBrowserDeleteForm.php b/core/modules/language/lib/Drupal/language/Form/NegotiationBrowserDeleteForm.php
index 29de4ce..9419ba7 100644
--- a/core/modules/language/lib/Drupal/language/Form/NegotiationBrowserDeleteForm.php
+++ b/core/modules/language/lib/Drupal/language/Form/NegotiationBrowserDeleteForm.php
@@ -66,6 +66,8 @@ public function submitForm(array &$form, array &$form_state) {
       language_set_browser_drupal_langcode_mappings($mappings);
     }
 
+    // Rebuild the container to update the submitted settings.
+    drupal_rebuild_language_negotiation_settings();
     $form_state['redirect'] = 'admin/config/regional/language/detection/browser';
   }
 
diff --git a/core/modules/language/lib/Drupal/language/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/LanguageListController.php b/core/modules/language/lib/Drupal/language/LanguageListController.php
index 669028f..14a53ad 100644
--- a/core/modules/language/lib/Drupal/language/LanguageListController.php
+++ b/core/modules/language/lib/Drupal/language/LanguageListController.php
@@ -95,8 +95,7 @@ public function buildForm(array $form, array &$form_state) {
   public function submitForm(array &$form, array &$form_state) {
     parent::submitForm($form, $form_state);
 
-    // Kill the static cache in language_list().
-    drupal_static_reset('language_list');
+    drupal_rebuild_language_negotiation_settings();
 
     // Update weight of locked system languages.
     language_update_locked_weights();
diff --git a/core/modules/language/lib/Drupal/language/LanguageManager.php b/core/modules/language/lib/Drupal/language/LanguageManager.php
new file mode 100644
index 0000000..6c744a2
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/LanguageManager.php
@@ -0,0 +1,77 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\LanguageManager.
+ */
+
+namespace Drupal\language;
+
+use Drupal\Core\Config\ConfigFactory;
+use Drupal\Core\Config\StorageInterface;
+use Drupal\Core\Database\Connection;
+use Drupal\Core\KeyValueStore\KeyValueStoreInterface;
+use Drupal\Core\Language\Language;
+use Drupal\Core\Language\LanguageManager as LanguageManagerBase;
+use Drupal\Component\Plugin\PluginManagerInterface;
+
+/**
+ * Overrides default LanguageManager to provide configured languages.
+ */
+class LanguageManager extends LanguageManagerBase {
+
+  /**
+   * The configuration factory.
+   *
+   * @var \Drupal\Core\Config\ConfigFactory;
+   */
+  protected $configFactory;
+
+  /**
+   * The configuration storage.
+   *
+   * @var \Drupal\Core\Config\StorageInterface;
+   */
+  protected $configStorage;
+
+  /**
+   * Constructs a new LanguageManager object.
+   *
+   * @param array $config
+   *   An array of configuration.
+   * @param \Drupal\Component\Plugin\PluginManagerInterface $negotiator_manager
+   *   The language negotiation methods plugin manager.
+   * @param \Drupal\Core\KeyValueStoreInterface $state
+   *   The state key value store.
+   * @param \Drupal\Core\Config\ConfigFactory $config_factory
+   *   The config factory service.
+   * @param \Drupal\Core\Config\StorageInterface $config_storage
+   *   The config storage service.
+   */
+  public function __construct(array $config, PluginManagerInterface $negotiator_manager, KeyValueStoreInterface $state, StorageInterface $config_storage) {
+    parent::__construct($config, $negotiator_manager, $state);
+    $this->configStorage = $config_storage;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLanguageList($flags = Language::STATE_CONFIGURABLE) {
+    if (!isset($this->languageList)) {
+      // Fill in master language list based on current configuration.
+      $default = $this->getLanguageDefault();
+
+      // Use language module configuration if available.
+      $language_ids = $this->configStorage->listAll('language.entity');
+      foreach (\Drupal::service('config.factory')->loadMultiple($language_ids) as $language_config) {
+        $langcode = $language_config->get('id');
+        $info = $language_config->get();
+        $info['default'] = ($langcode == $default->id);
+        $this->languageList[$langcode] = new Language($info);
+      }
+    }
+
+    return parent::getLanguageList($flags);
+  }
+
+}
diff --git a/core/modules/language/lib/Drupal/language/LanguageServiceProvider.php b/core/modules/language/lib/Drupal/language/LanguageServiceProvider.php
new file mode 100644
index 0000000..40e7670
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/LanguageServiceProvider.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\LanguageServiceProvider.
+ */
+
+namespace Drupal\language;
+
+use Drupal\Core\DependencyInjection\ContainerBuilder;
+use Drupal\Core\DependencyInjection\ServiceProviderInterface;
+use Drupal\Core\DependencyInjection\ServiceModifierInterface;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * Overrides the language_manager service to point to language's module one.
+ */
+class LanguageServiceProvider implements ServiceProviderInterface, ServiceModifierInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function register(ContainerBuilder $container) { }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function alter(ContainerBuilder $container) {
+    $definition = $container->getDefinition('language_manager');
+    $definition->setClass('Drupal\language\LanguageManager')
+      ->addArgument(new Reference('config.storage'));
+  }
+
+}
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php
index a369484..4fc8b42 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageBrowserDetectionUnitTest.php
@@ -9,6 +9,8 @@
 
 use Drupal\simpletest\WebTestBase;
 use Drupal\Core\Language\Language;
+use Drupal\Core\Language\Plugin\LanguageNegotiation\LanguageNegotiationBrowser;
+use Symfony\Component\HttpFoundation\Request;
 
 /**
  * Test browser language detection.
@@ -153,9 +155,11 @@ function testLanguageFromBrowser() {
       'zh-cht' => 'zh-hant',
     );
 
+    $mappings = $this->container->get('config.factory')->get('language.mappings')->get();
+    $language_neg = new LanguageNegotiationBrowser(array('browser' => array('mappings' => $mappings)));
     foreach ($test_cases as $accept_language => $expected_result) {
-      $_SERVER['HTTP_ACCEPT_LANGUAGE'] = $accept_language;
-      $result = language_from_browser($languages);
+      $request = Request::create('', 'GET', array(), array(), array(), array('HTTP_ACCEPT_LANGUAGE' => $accept_language));
+      $result = $language_neg->negotiateLanguage($languages, $request);
       $this->assertIdentical($result, $expected_result, format_string("Language selection '@accept-language' selects '@result', result = '@actual'", array('@accept-language' => $accept_language, '@result' => $expected_result, '@actual' => isset($result) ? $result : 'none')));
     }
   }
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationElementTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationElementTest.php
index d5a3c02..878f497 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationElementTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationElementTest.php
@@ -85,6 +85,7 @@ public function testDefaultLangcode() {
     $langcode = language_get_default_langcode('custom_type', 'custom_bundle');
     $language_interface = language(Language::TYPE_INTERFACE);
     $this->assertEqual($langcode, $language_interface->id);
+    drupal_rebuild_language_negotiation_settings();
 
     // Site's default.
     $old_default = language_default();
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php
index 1fb8c85..fa384d5 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageConfigurationTest.php
@@ -156,7 +156,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 8b2190d..29b75f0 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageListTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageListTest.php
@@ -117,14 +117,14 @@ function testLanguageList() {
     $this->drupalGet('admin/config/regional/language/delete/' . $langcode);
     $this->assertResponse(404, 'Language no longer found.');
     // Make sure the "language_count" state has been updated correctly.
-    drupal_static_reset('language_list');
+    $this->container->get('language_manager')->reset();
     $languages = language_list();
     $language_count =  $this->container->get('state')->get('language_count') ?: 1;
     $this->assertEqual($language_count, count($languages), 'Language count is correct.');
     // Delete French.
     $this->drupalPostForm('admin/config/regional/language/delete/fr', array(), t('Delete'));
     // Get the count of languages.
-    drupal_static_reset('language_list');
+    $this->container->get('language_manager')->reset();
     $languages = language_list();
     // We need raw here because %language and %langcode will add HTML.
     $t_args = array('%language' => 'French', '%langcode' => 'fr');
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php
index 6cff106..2c843dc 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageNegotiationInfoTest.php
@@ -96,7 +96,7 @@ function testInfoAlterations() {
     // Check language negotiation results.
     $this->drupalGet('');
     $last = \Drupal::state()->get('language_test.language_negotiation_last');
-    foreach (language_types_get_all() as $type) {
+    foreach (Drupal::languageManager()->getLanguageTypes() as $type) {
       $langcode = $last[$type];
       $value = $type == Language::TYPE_CONTENT || strpos($type, 'test') !== FALSE ? 'it' : 'en';
       $this->assertEqual($langcode, $value, format_string('The negotiated language for %type is %language', array('%type' => $type, '%language' => $value)));
@@ -107,7 +107,7 @@ function testInfoAlterations() {
     $this->languageNegotiationUpdate('uninstall');
 
     // Check that only the core language types are available.
-    foreach (language_types_get_all() as $type) {
+    foreach (Drupal::languageManager()->getLanguageTypes() as $type) {
       $this->assertTrue(strpos($type, 'test') === FALSE, format_string('The %type language is still available', array('%type' => $type)));
     }
 
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageUILanguageNegotiationTest.php
index e87a550..414e4a7 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.
@@ -235,7 +235,7 @@ function testUILanguageNegotiation() {
     }
 
     // Unknown language prefix should return 404.
-    variable_set('language_negotiation_' . Language::TYPE_INTERFACE, language_language_negotiation_info());
+    variable_set('language_negotiation_' . Language::TYPE_INTERFACE, language_negotiation_info());
     $this->drupalGet("$langcode_unknown/admin/config", array(), $http_header_browser_fallback);
     $this->assertResponse(404, "Unknown language path prefix should return 404");
 
diff --git a/core/modules/language/lib/Drupal/language/Tests/LanguageUrlRewritingTest.php b/core/modules/language/lib/Drupal/language/Tests/LanguageUrlRewritingTest.php
index 07ee9a9..c30f55b 100644
--- a/core/modules/language/lib/Drupal/language/Tests/LanguageUrlRewritingTest.php
+++ b/core/modules/language/lib/Drupal/language/Tests/LanguageUrlRewritingTest.php
@@ -46,8 +46,6 @@ function setUp() {
     $edit = array('language_interface[enabled][language-url]' => 1);
     $this->drupalPostForm('admin/config/regional/language/detection', $edit, t('Save settings'));
 
-    // Reset static caching.
-    drupal_static_reset('language_list');
   }
 
   /**
@@ -119,7 +117,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/language_test.module b/core/modules/language/tests/language_test/language_test.module
index 33c2b36..3c0fa2e 100644
--- a/core/modules/language/tests/language_test/language_test.module
+++ b/core/modules/language/tests/language_test/language_test.module
@@ -56,33 +56,6 @@ function language_test_language_types_info_alter(array &$language_types) {
 }
 
 /**
- * Implements hook_language_negotiation_info().
- */
-function language_test_language_negotiation_info() {
-  if (\Drupal::state()->get('language_test.language_negotiation_info')) {
-    $info = array(
-      'callbacks' => array(
-        'negotiation' => 'language_test_language_negotiation_method',
-      ),
-      'file' => drupal_get_path('module', 'language_test') .'/language_test.module',
-      'weight' => -10,
-      'description' => t('This is a test language negotiation method.'),
-    );
-
-    return array(
-      'test_language_negotiation_method' => array(
-        'name' => t('Test'),
-        'types' => array(Language::TYPE_CONTENT, 'test_language_type', 'fixed_test_language_type'),
-      ) + $info,
-      'test_language_negotiation_method_ts' => array(
-        'name' => t('Type-specific test'),
-        'types' => array('test_language_type'),
-      ) + $info,
-    );
-  }
-}
-
-/**
  * Implements hook_language_negotiation_info_alter().
  */
 function language_test_language_negotiation_info_alter(array &$negotiation_info) {
@@ -96,16 +69,9 @@ function language_test_language_negotiation_info_alter(array &$negotiation_info)
  */
 function language_test_store_language_negotiation() {
   $last = array();
-  print_r(language_types_get_all());
-  foreach (language_types_get_all() as $type) {
+  print_r(Drupal::languageManager()->getLanguageTypes());
+  foreach (Drupal::languageManager()->getLanguageTypes() as $type) {
     $last[$type] = language($type)->id;
   }
   \Drupal::state()->set('language_test.language_negotiation_last', $last);
 }
-
-/**
- * Provides a test language negotiation method.
- */
-function language_test_language_negotiation_method($languages) {
-  return 'it';
-}
diff --git a/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestManager.php b/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestManager.php
deleted file mode 100644
index 991755e..0000000
--- a/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestManager.php
+++ /dev/null
@@ -1,28 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\language_test\LanguageTestManager.
- */
-
-namespace Drupal\language_test;
-
-use Drupal\Core\Language\LanguageManager;
-use Symfony\Component\HttpFoundation\Request;
-
-/**
- * Defines a LanguageManager service to test URL negotiation.
- */
-class LanguageTestManager extends LanguageManager {
-
-  /**
-   * Overrides \Drupal\Core\Language\LanguageManager::init().
-   */
-  public function init() {
-    if ($test_domain = \Drupal::state()->get('language_test.domain')) {
-      $_SERVER['HTTP_HOST'] = $test_domain;
-    }
-    return parent::init();
-  }
-
-}
diff --git a/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestServiceProvider.php b/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestServiceProvider.php
deleted file mode 100644
index c8b1633..0000000
--- a/core/modules/language/tests/language_test/lib/Drupal/language_test/LanguageTestServiceProvider.php
+++ /dev/null
@@ -1,36 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\language_test\LanguageTestServiceProvider.
- */
-
-namespace Drupal\language_test;
-
-use Drupal\Core\DependencyInjection\ContainerBuilder;
-use Drupal\Core\DependencyInjection\ServiceModifierInterface;
-use Drupal\Core\DependencyInjection\ServiceProviderInterface;
-
-/**
- * Defines the LanguageTest service provider.
- */
-class LanguageTestServiceProvider implements ServiceProviderInterface, ServiceModifierInterface {
-
-  /**
-   * {@inheritdoc}
-   */
-  public function register(ContainerBuilder $container) {
-
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function alter(ContainerBuilder $container) {
-    // Overrides language_manager class to test domain language negotiation.
-    $definition = $container->getDefinition('language_manager');
-    $definition->setClass('Drupal\language_test\LanguageTestManager');
-  }
-
-}
-
diff --git a/core/modules/language/tests/language_test/lib/Drupal/language_test/Plugin/LanguageNegotiation/LanguageNegotiationTest.php b/core/modules/language/tests/language_test/lib/Drupal/language_test/Plugin/LanguageNegotiation/LanguageNegotiationTest.php
new file mode 100644
index 0000000..0f60de1
--- /dev/null
+++ b/core/modules/language/tests/language_test/lib/Drupal/language_test/Plugin/LanguageNegotiation/LanguageNegotiationTest.php
@@ -0,0 +1,41 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language_test\\Plugin\LanguageNegotiation\LanguageNegotiationTest.
+ */
+
+namespace Drupal\language_test\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 language from a selected language.
+ *
+ * @Plugin(
+ *   id = LanguageNegotiationTest::METHOD_ID,
+ *   weight = -10,
+ *   name = @Translation("Test"),
+ *   description = @Translation("This is a test language negotiation method."),
+ *   types = {Language::TYPE_CONTENT, "test_language_type", "fixed_test_language_type"}
+ * )
+ */
+class LanguageNegotiationTest extends LanguageNegotiationMethodBase {
+
+  /**
+   * The language negotiation method id.
+   */
+  const METHOD_ID = 'test_language_negotiation_method';
+
+  /**
+   * {@inheritdoc}
+   */
+  public function negotiateLanguage(array $languages, Request $request = NULL) {
+    return 'it';
+  }
+
+}
diff --git a/core/modules/language/tests/language_test/lib/Drupal/language_test/Plugin/LanguageNegotiation/LanguageNegotiationTestTs.php b/core/modules/language/tests/language_test/lib/Drupal/language_test/Plugin/LanguageNegotiation/LanguageNegotiationTestTs.php
new file mode 100644
index 0000000..5fa4208
--- /dev/null
+++ b/core/modules/language/tests/language_test/lib/Drupal/language_test/Plugin/LanguageNegotiation/LanguageNegotiationTestTs.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language_test\\Plugin\LanguageNegotiation\LanguageNegotiationTestTs.
+ */
+
+namespace Drupal\language_test\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 = LanguageNegotiationTestTs::METHOD_ID,
+ *   weight = -10,
+ *   name = @Translation("Type-specific test"),
+ *   description = @Translation("This is a test language negotiation method."),
+ *   types = {"test_language_type"}
+ * )
+ */
+class LanguageNegotiationTestTs extends LanguageNegotiationTest {
+
+  /**
+   * The language negotiation method id.
+   */
+  const METHOD_ID = 'test_language_negotiation_method_ts';
+
+}
diff --git a/core/modules/locale/lib/Drupal/locale/Form/TranslateEditForm.php b/core/modules/locale/lib/Drupal/locale/Form/TranslateEditForm.php
index 45e81c2..7fce2d9 100644
--- a/core/modules/locale/lib/Drupal/locale/Form/TranslateEditForm.php
+++ b/core/modules/locale/lib/Drupal/locale/Form/TranslateEditForm.php
@@ -29,7 +29,7 @@ public function buildForm(array $form, array &$form_state) {
     $filter_values = $this->translateFilterValues();
     $langcode = $filter_values['langcode'];
 
-    drupal_static_reset('language_list');
+    $this->languageManager->reset();
     $languages = language_list();
 
     $langname = isset($langcode) ? $languages[$langcode]->name : "- None -";
diff --git a/core/modules/locale/lib/Drupal/locale/Form/TranslateFormBase.php b/core/modules/locale/lib/Drupal/locale/Form/TranslateFormBase.php
index c33da33..da63c72 100644
--- a/core/modules/locale/lib/Drupal/locale/Form/TranslateFormBase.php
+++ b/core/modules/locale/lib/Drupal/locale/Form/TranslateFormBase.php
@@ -161,7 +161,7 @@ protected function translateFilters() {
     $filters = array();
 
     // Get all languages, except English.
-    drupal_static_reset('language_list');
+    $this->languageManager->reset();
     $languages = language_list();
     $language_options = array();
     foreach ($languages as $langcode => $language) {
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php
index 2b53ff3..d5cdd7f 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleContentTest.php
@@ -47,7 +47,6 @@ function testMachineNameLTR() {
     $edit = array();
     $edit['predefined_langcode'] = 'ar';
     $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
-    drupal_static_reset('language_list');
 
     $edit = array(
       'site_default_language' => 'ar',
@@ -86,7 +85,6 @@ function testContentTypeLanguageConfiguration() {
       'direction' => '0',
     );
     $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language'));
-    drupal_static_reset('language_list');
 
     // Set the content type to use multilingual support.
     $this->drupalGet("admin/structure/types/manage/{$type2->type}");
@@ -97,7 +95,7 @@ function testContentTypeLanguageConfiguration() {
     $this->drupalPostForm("admin/structure/types/manage/{$type2->type}", $edit, t('Save content type'));
     $this->assertRaw(t('The content type %type has been updated.', array('%type' => $type2->name)));
     $this->drupalLogout();
-    drupal_static_reset('language_list');
+    \Drupal::languageManager()->reset();
 
     // Verify language selection is not present on the node add form.
     $this->drupalLogin($web_user);
@@ -154,13 +152,12 @@ function testContentTypeDirLang() {
     $edit = array();
     $edit['predefined_langcode'] = 'ar';
     $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
-    drupal_static_reset('language_list');
+    \Drupal::languageManager()->reset();
 
     // Install Spanish language.
     $edit = array();
     $edit['predefined_langcode'] = 'es';
     $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
-    drupal_static_reset('language_list');
 
     // Set the content type to use multilingual support.
     $this->drupalGet("admin/structure/types/manage/{$type->type}");
@@ -224,7 +221,6 @@ function testNodeAdminLanguageFilter() {
     // Enable multiple languages.
     $this->drupalPostForm('admin/config/regional/language/edit/en', array('locale_translate_english' => TRUE), t('Save language'));
     $this->drupalPostForm('admin/config/regional/language/add', array('predefined_langcode' => 'zh-hant'), t('Add language'));
-    drupal_static_reset('language_list');
 
     // Create two nodes: English and Chinese.
     $node_en = $this->drupalCreateNode(array('langcode' => 'en'));
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationUiTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationUiTest.php
index 06b6ed4..ee2f9a3 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationUiTest.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleTranslationUiTest.php
@@ -230,7 +230,7 @@ function testJavaScriptTranslation() {
       'direction' => '0',
     );
     $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language'));
-    drupal_static_reset('language_list');
+    $this->container->get('language_manager')->reset();
 
     // Build the JavaScript translation file.
 
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php
index 875741b..9ef468c 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUninstallTest.php
@@ -97,10 +97,10 @@ function testUninstallProcess() {
 
     // Change language negotiation options.
     drupal_load('module', 'locale');
-    \Drupal::config('system.language.types')->set('configurable', language_types_get_default() + array('language_custom' => TRUE))->save();
-    variable_set('language_negotiation_' . Language::TYPE_INTERFACE, language_language_negotiation_info());
-    variable_set('language_negotiation_' . Language::TYPE_CONTENT, language_language_negotiation_info());
-    variable_set('language_negotiation_' . Language::TYPE_URL, language_language_negotiation_info());
+    \Drupal::config('system.language.types')->set('configurable', $this->container->get('language_manager')->getTypeDefaults() + array('language_custom' => TRUE))->save();
+    variable_set('language_negotiation_' . Language::TYPE_INTERFACE, language_negotiation_info());
+    variable_set('language_negotiation_' . Language::TYPE_CONTENT, language_negotiation_info());
+    variable_set('language_negotiation_' . Language::TYPE_URL, language_negotiation_info());
 
     // Change language negotiation settings.
     \Drupal::config('language.negotiation')
@@ -126,7 +126,7 @@ function testUninstallProcess() {
 
     // Check language negotiation.
     require_once DRUPAL_ROOT . '/core/includes/language.inc';
-    $this->assertTrue(count(language_types_get_all()) == count(language_types_get_default()), 'Language types reset');
+    $this->assertTrue(count($this->container->get('language_manager')->getLanguageTypes()) == count($this->container->get('language_manager')->getTypeDefaults()), 'Language types reset');
     $language_negotiation = language_negotiation_method_get_first(Language::TYPE_INTERFACE) == LANGUAGE_NEGOTIATION_SELECTED;
     $this->assertTrue($language_negotiation, String::format('Interface language negotiation: %setting', array('%setting' => $language_negotiation ? 'none' : 'set')));
     $language_negotiation = language_negotiation_method_get_first(Language::TYPE_CONTENT) == LANGUAGE_NEGOTIATION_SELECTED;
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateBase.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateBase.php
index d61128d..95c202e 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateBase.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleUpdateBase.php
@@ -82,7 +82,7 @@ protected function setTranslationsDirectory($path) {
   protected function addLanguage($langcode) {
     $edit = array('predefined_langcode' => $langcode);
     $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
-    drupal_static_reset('language_list');
+    $this->container->get('language_manager')->reset();
     $this->assertTrue(language_load($langcode), String::format('Language %langcode added.', array('%langcode' => $langcode)));
   }
 
diff --git a/core/modules/locale/locale.bulk.inc b/core/modules/locale/locale.bulk.inc
index 8a7d547..0c6da3a 100644
--- a/core/modules/locale/locale.bulk.inc
+++ b/core/modules/locale/locale.bulk.inc
@@ -21,7 +21,7 @@
  * @deprecated Use \Drupal\locale\Form\LocaleForm::import()
  */
 function locale_translate_import_form($form, &$form_state) {
-  drupal_static_reset('language_list');
+  Drupal::languageManager()->reset();
   $languages = language_list();
 
   // Initialize a language list to the ones available, including English if we
diff --git a/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php b/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php
index 9e94728..48f7587 100644
--- a/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php
+++ b/core/modules/path/lib/Drupal/path/Tests/PathLanguageTest.php
@@ -84,7 +84,7 @@ function testAliasTranslation() {
     $this->container->get('path.alias_manager')->cacheClear();
 
     // Languages are cached on many levels, and we need to clear those caches.
-    drupal_static_reset('language_list');
+    $this->container->get('language_manager')->reset();
     $this->rebuildContainer();
     $languages = language_list();
 
@@ -96,7 +96,10 @@ function testAliasTranslation() {
     $this->drupalGet('fr/' . $edit['path[alias]']);
     $this->assertText($french_node->label(), 'Alias for French translation works.');
 
-    // Confirm that the alias is returned by url().
+    // Confirm that the alias is returned by url(). Languages are cached on
+    // many levels, and we need to clear those caches.
+    $this->container->get('language_manager')->reset();
+    $languages = language_list();
     $url = $this->container->get('url_generator')->generateFromPath('node/' . $french_node->id(), array('language' => $languages[$french_node->language()->id]));
 
     $this->assertTrue(strpos($url, $edit['path[alias]']), 'URL contains the path alias.');
diff --git a/core/modules/system/language.api.php b/core/modules/system/language.api.php
index cfa42ec..a7e6d13 100644
--- a/core/modules/system/language.api.php
+++ b/core/modules/system/language.api.php
@@ -88,55 +88,6 @@ function hook_language_types_info_alter(array &$language_types) {
 }
 
 /**
- * Define language negotiation methods.
- *
- * @return
- *   An associative array of language negotiation method definitions. The keys
- *   are method identifiers, and the values are associative arrays definining
- *   each method, with the following elements:
- *   - types: An array of allowed language types. If a language negotiation
- *     method does not specify which language types it should be used with, it
- *     will be available for all the configurable language types.
- *   - callbacks: An associative array of functions that will be called to
- *     perform various tasks. Possible elements are:
- *     - negotiation: (required) Name of the callback function that determines
- *       the language value.
- *     - language_switch: (optional) Name of the callback function that
- *       determines links for a language switcher block associated with this
- *       method. See language_switcher_url() for an example.
- *     - url_rewrite: (optional) Name of the callback function that provides URL
- *       rewriting, if needed by this method.
- *   - file: The file where callback functions are defined (this file will be
- *     included before the callbacks are invoked).
- *   - weight: The default weight of the method.
- *   - name: The translated human-readable name for the method.
- *   - description: A translated longer description of the method.
- *   - config: An internal path pointing to the method's configuration page.
- *   - cache: The value Drupal's page cache should be set to for the current
- *     method to be invoked.
- *
- * @see hook_language_negotiation_info_alter()
- * @ingroup language_negotiation
- */
-function hook_language_negotiation_info() {
-  return array(
-    'custom_language_negotiation_method' => array(
-      'callbacks' => array(
-        'negotiation' => 'custom_negotiation_callback',
-        'language_switch' => 'custom_language_switch_callback',
-        'url_rewrite' => 'custom_url_rewrite_callback',
-      ),
-      'file' => drupal_get_path('module', 'custom') . '/custom.module',
-      'weight' => -4,
-      'types' => array('custom_language_type'),
-      'name' => t('Custom language negotiation method'),
-      'description' => t('This is a custom language negotiation method.'),
-      'cache' => 0,
-    ),
-  );
-}
-
-/**
  * Perform alterations on language negotiation methods.
  *
  * @param $negotiation_info
diff --git a/core/modules/system/lib/Drupal/system/Tests/Plugin/CacheDecoratorLanguageTest.php b/core/modules/system/lib/Drupal/system/Tests/Plugin/CacheDecoratorLanguageTest.php
index 4aa6270..ee4f5c0 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/system/lib/Drupal/system/Tests/Theme/TwigTransTest.php b/core/modules/system/lib/Drupal/system/Tests/Theme/TwigTransTest.php
index 48d1e1f..8ce7d1a 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Theme/TwigTransTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Theme/TwigTransTest.php
@@ -84,7 +84,7 @@ protected function setUp() {
     $this->drupalPostForm('admin/config/regional/settings', $edit, t('Save configuration'));
 
     // Reset the static cache of the language list.
-    drupal_static_reset('language_list');
+    $this->container->get('language_manager')->reset();
 
     // Check that lolspeak is the default language for the site.
     $this->assertEqual(language_default()->id, 'xx', 'Lolspeak is the default language');
@@ -251,6 +251,7 @@ protected function installLanguages() {
         drupal_unlink($filename);
       }
     }
+    $this->container->get('language_manager')->reset();
   }
 
   /**
diff --git a/core/modules/tour/lib/Drupal/tour/Tests/TourTest.php b/core/modules/tour/lib/Drupal/tour/Tests/TourTest.php
index fcae023..e8277c6 100644
--- a/core/modules/tour/lib/Drupal/tour/Tests/TourTest.php
+++ b/core/modules/tour/lib/Drupal/tour/Tests/TourTest.php
@@ -108,6 +108,7 @@ public function testTourFunctionality() {
     // Enable Italian language and navigate to it/tour-test1 and verify italian
     // version of tip is found.
     language_save(new Language(array('id' => 'it')));
+    drupal_rebuild_language_negotiation_settings();
     $this->drupalGet('it/tour-test-1');
 
     $elements = $this->xpath('//li[@data-id=:data_id and ./h2[contains(., :text)]]', array(
diff --git a/core/modules/translation/lib/Drupal/translation/Tests/TranslationTest.php b/core/modules/translation/lib/Drupal/translation/Tests/TranslationTest.php
index 8ef55a6..e1f6b7f 100644
--- a/core/modules/translation/lib/Drupal/translation/Tests/TranslationTest.php
+++ b/core/modules/translation/lib/Drupal/translation/Tests/TranslationTest.php
@@ -285,7 +285,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();
   }
 
@@ -319,7 +318,7 @@ function addLanguage($langcode) {
       $this->drupalPostForm('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.');
 
diff --git a/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php b/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php
index f49184f..b29aa18 100644
--- a/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php
+++ b/core/tests/Drupal/Tests/Core/PathProcessor/PathProcessorTest.php
@@ -46,10 +46,15 @@ public function setUp() {
     $this->languages = $languages;
 
     // Create a language manager stub.
-    $language_manager = $this->getMock('Drupal\Core\Language\LanguageManager');
+    $language_manager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager')
+      ->disableOriginalConstructor()
+      ->getMock();
     $language_manager->expects($this->any())
       ->method('getLanguage')
       ->will($this->returnValue($languages['en']));
+    $language_manager->expects($this->any())
+      ->method('getLanguageList')
+      ->will($this->returnValue($this->languages));
 
     $this->languageManager = $language_manager;
   }
@@ -95,7 +100,7 @@ function testProcessInbound() {
     $alias_processor = new PathProcessorAlias($alias_manager);
     $decode_processor = new PathProcessorDecode();
     $front_processor = new PathProcessorFront($config_factory_stub);
-    $language_processor = new PathProcessorLanguage($config_factory_stub, new Settings(array()), $this->languageManager, $this->languages);
+    $language_processor = new PathProcessorLanguage($config_factory_stub, new Settings(array()), $this->languageManager);
 
     // First, test the processor manager with the processors in the incorrect
     // order. The alias processor will run before the language processor, meaning
diff --git a/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php b/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
index 3e86b66..0e60267 100644
--- a/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
+++ b/core/tests/Drupal/Tests/Core/Plugin/DefaultPluginManagerTest.php
@@ -113,7 +113,9 @@ public function testDefaultPluginManagerWithEmptyCache() {
       ->with($cid . ':en', $this->expectedDefinitions);
 
     $language = new Language(array('id' => 'en'));
-    $language_manager = $this->getMock('Drupal\Core\Language\LanguageManager');
+    $language_manager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager')
+      ->disableOriginalConstructor()
+      ->getMock();
     $language_manager->expects($this->once())
       ->method('getLanguage')
       ->with(Language::TYPE_INTERFACE)
@@ -144,7 +146,9 @@ public function testDefaultPluginManagerWithFilledCache() {
       ->method('set');
 
     $language = new Language(array('id' => 'en'));
-    $language_manager = $this->getMock('Drupal\Core\Language\LanguageManager');
+    $language_manager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager')
+      ->disableOriginalConstructor()
+      ->getMock();
     $language_manager->expects($this->once())
       ->method('getLanguage')
       ->with(Language::TYPE_INTERFACE)
@@ -173,7 +177,9 @@ public function testCacheClearWithTags() {
       ->method('deleteMultiple');
 
     $language = new Language(array('id' => 'en'));
-    $language_manager = $this->getMock('Drupal\Core\Language\LanguageManager');
+    $language_manager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager')
+      ->disableOriginalConstructor()
+      ->getMock();
     $language_manager->expects($this->once())
       ->method('getLanguage')
       ->with(Language::TYPE_INTERFACE)
diff --git a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php
index 6b7334f..3e6c72f 100644
--- a/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php
+++ b/core/tests/Drupal/Tests/Core/Utility/LinkGeneratorTest.php
@@ -79,7 +79,10 @@ protected function setUp() {
 
     $this->urlGenerator = $this->getMock('\Drupal\Core\Routing\UrlGenerator', array(), array(), '', FALSE);
     $this->moduleHandler = $this->getMock('Drupal\Core\Extension\ModuleHandlerInterface');
-    $this->languageManager = $this->getMock('Drupal\Core\Language\LanguageManager');
+    $this->languageManager = $this->getMockBuilder('Drupal\Core\Language\LanguageManager')
+      ->disableOriginalConstructor()
+      ->getMock();
+
 
     $this->linkGenerator = new LinkGenerator($this->urlGenerator, $this->moduleHandler, $this->languageManager);
   }
