diff --git a/core/includes/theme.inc b/core/includes/theme.inc
index 81ecd11..2caf789 100644
--- a/core/includes/theme.inc
+++ b/core/includes/theme.inc
@@ -928,17 +928,17 @@ function theme_get_setting($setting_name, $theme = NULL) {
       }
       foreach ($theme_keys as $theme_key) {
         if (!empty($themes[$theme_key]->info['settings'])) {
-          $cache[$theme]->mergeData($themes[$theme_key]->info['settings']);
+          $cache[$theme]->merge($themes[$theme_key]->info['settings']);
         }
       }
     }
 
     // Get the global settings from configuration.
-    $cache[$theme]->mergeData(\Drupal::config('system.theme.global')->get());
+    $cache[$theme]->merge(\Drupal::config('system.theme.global')->get());
 
     if ($theme) {
       // Get the saved theme-specific settings from the configuration system.
-      $cache[$theme]->mergeData(\Drupal::config($theme . '.settings')->get());
+      $cache[$theme]->merge(\Drupal::config($theme . '.settings')->get());
 
       // If the theme does not support a particular feature, override the global
       // setting and set the value to NULL.
diff --git a/core/lib/Drupal/Core/Config/CachedStorage.php b/core/lib/Drupal/Core/Config/CachedStorage.php
index 1ff4046..5af3346 100644
--- a/core/lib/Drupal/Core/Config/CachedStorage.php
+++ b/core/lib/Drupal/Core/Config/CachedStorage.php
@@ -67,7 +67,7 @@ public function exists($name) {
    * Implements Drupal\Core\Config\StorageInterface::read().
    */
   public function read($name) {
-    if ($cache = $this->cache->get($name)) {
+    if ($cache = $this->cache->get($this->getCacheKey($name))) {
       // The cache contains either the cached configuration data or FALSE
       // if the configuration file does not exist.
       return $cache->data;
@@ -75,7 +75,7 @@ public function read($name) {
     // Read from the storage on a cache miss and cache the data. Also cache
     // information about missing configuration objects.
     $data = $this->storage->read($name);
-    $this->cache->set($name, $data);
+    $this->cache->set($this->getCacheKey($name), $data);
     return $data;
   }
 
@@ -87,20 +87,22 @@ public function readMultiple(array $names) {
     // The names array is passed by reference and will only contain the names of
     // config object not found after the method call.
     // @see \Drupal\Core\Cache\CacheBackendInterface::getMultiple()
-    $cached_list = $this->cache->getMultiple($names);
+    $cids = $this->getCacheKeys($names);
+    $cached_list = $this->cache->getMultiple($cids);
 
-    if (!empty($names)) {
+    if (!empty($cids)) {
+      $names = $this->getNamesFromCacheKeys($cids);
       $list = $this->storage->readMultiple($names);
       // Cache configuration objects that were loaded from the storage, cache
       // missing configuration objects as an explicit FALSE.
       foreach ($names as $name) {
-        $this->cache->set($name, isset($list[$name]) ? $list[$name] : FALSE);
+        $this->cache->set($this->getCacheKey($name), isset($list[$name]) ? $list[$name] : FALSE);
       }
     }
 
     // Add the configuration objects from the cache to the list.
-    foreach ($cached_list as $name => $cache) {
-      $list[$name] = $cache->data;
+    foreach ($cached_list as $cid => $cache) {
+      $list[$this->getNameFromCacheKey($cid)] = $cache->data;
     }
 
     // Ensure that only existing configuration objects are returned, filter out
@@ -115,7 +117,7 @@ public function write($name, array $data) {
     if ($this->storage->write($name, $data)) {
       // While not all written data is read back, setting the cache instead of
       // just deleting it avoids cache rebuild stampedes.
-      $this->cache->set($name, $data);
+      $this->cache->set($this->getCacheKey($name), $data);
       Cache::deleteTags(array($this::FIND_BY_PREFIX_CACHE_TAG => TRUE));
       $this->findByPrefixCache = array();
       return TRUE;
@@ -130,7 +132,7 @@ public function delete($name) {
     // If the cache was the first to be deleted, another process might start
     // rebuilding the cache before the storage is gone.
     if ($this->storage->delete($name)) {
-      $this->cache->delete($name);
+      $this->cache->delete($this->getCacheKey($name));
       Cache::deleteTags(array($this::FIND_BY_PREFIX_CACHE_TAG => TRUE));
       $this->findByPrefixCache = array();
       return TRUE;
@@ -145,8 +147,8 @@ public function rename($name, $new_name) {
     // If the cache was the first to be deleted, another process might start
     // rebuilding the cache before the storage is renamed.
     if ($this->storage->rename($name, $new_name)) {
-      $this->cache->delete($name);
-      $this->cache->delete($new_name);
+      $this->cache->delete($this->getCacheKey($name));
+      $this->cache->delete($this->getCacheKey($new_name));
       Cache::deleteTags(array($this::FIND_BY_PREFIX_CACHE_TAG => TRUE));
       $this->findByPrefixCache = array();
       return TRUE;
@@ -199,13 +201,14 @@ protected function findByPrefix($prefix) {
     if (!isset($this->findByPrefixCache[$prefix])) {
       // The : character is not allowed in config file names, so this can not
       // conflict.
-      if ($cache = $this->cache->get('find:' . $prefix)) {
+      $cache_key = $this->getCacheKey('find:' . $prefix);
+      if ($cache = $this->cache->get($cache_key)) {
         $this->findByPrefixCache[$prefix] = $cache->data;
       }
       else {
         $this->findByPrefixCache[$prefix] = $this->storage->listAll($prefix);
         $this->cache->set(
-          'find:' . $prefix,
+          $cache_key,
           $this->findByPrefixCache[$prefix],
           Cache::PERMANENT,
           array($this::FIND_BY_PREFIX_CACHE_TAG => TRUE)
@@ -221,7 +224,7 @@ protected function findByPrefix($prefix) {
   public function deleteAll($prefix = '') {
     // If the cache was the first to be deleted, another process might start
     // rebuilding the cache before the storage is renamed.
-    $cids = $this->storage->listAll($prefix);
+    $cids = $this->getCacheKeys($this->storage->listAll($prefix));
     if ($this->storage->deleteAll($prefix)) {
       $this->cache->deleteMultiple($cids);
       return TRUE;
@@ -235,4 +238,64 @@ public function deleteAll($prefix = '') {
   public function resetListCache() {
     $this->findByPrefixCache = array();
   }
+
+  /**
+   * Gets the cache key for a configuration object name.
+   *
+   * In the default implementation of CachedStorage this simply returns the
+   * name. In more complex implementations this allows different versions of
+   * the same configuration object to cached. Since the maximum length of a
+   * configuration object name is 250 characters this should never add more than
+   * 5 characters to the cache key.
+   *
+   * @see \Drupal\language\Config\LanguageOverrideStorage::getCacheKey()
+   *
+   * @param string $name
+   *   The configuration object name.
+   *
+   * @return string
+   *   The cache key.
+   */
+  protected function getCacheKey($name) {
+    return $name;
+  }
+
+  /**
+   * Gets the cache keys for an array of configuration object names.
+   *
+   * @param array $names
+   *   The configuration object names.
+   *
+   * @return array
+   *   An array of cache keys.
+   */
+  protected function getCacheKeys(array $names) {
+    return $names;
+  }
+
+  /**
+   * Converts a cache key into a configuration object name.
+   *
+   * @param string $key
+   *   The cache key.
+   *
+   * @return string
+   *   The configuration object name.
+   */
+  protected function getNameFromCacheKey($key) {
+    return $key;
+  }
+
+  /**
+   * Converts an array of cache keys into an array of configuration object names.
+   *
+   * @param string $key
+   *   The cache key.
+   *
+   * @return string
+   *   The configuration object name.
+   */
+  protected function getNamesFromCacheKeys($keys) {
+    return $keys;
+  }
 }
diff --git a/core/lib/Drupal/Core/Config/Config.php b/core/lib/Drupal/Core/Config/Config.php
index e000200..15ec667 100644
--- a/core/lib/Drupal/Core/Config/Config.php
+++ b/core/lib/Drupal/Core/Config/Config.php
@@ -8,32 +8,12 @@
 namespace Drupal\Core\Config;
 
 use Drupal\Component\Utility\NestedArray;
-use Drupal\Component\Utility\String;
-use Drupal\Core\Config\ConfigNameException;
-use Drupal\Core\Config\Schema\SchemaIncompleteException;
-use Drupal\Core\DependencyInjection\DependencySerialization;
-use Drupal\Core\TypedData\PrimitiveInterface;
-use Drupal\Core\TypedData\Type\FloatInterface;
-use Drupal\Core\TypedData\Type\IntegerInterface;
-use Drupal\Core\Language\Language;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 
 /**
  * Defines the default configuration object.
  */
-class Config extends DependencySerialization {
-
-  /**
-   * The maximum length of a configuration object name.
-   *
-   * Many filesystems (including HFS, NTFS, and ext4) have a maximum file name
-   * length of 255 characters. To ensure that no configuration objects
-   * incompatible with this limitation are created, we enforce a maximum name
-   * length of 250 characters (leaving 5 characters for the file extension).
-   *
-   * @see http://en.wikipedia.org/wiki/Comparison_of_file_systems
-   */
-  const MAX_NAME_LENGTH = 250;
+class Config extends StorableConfigBase {
 
   /**
    * An event dispatcher instance to use for configuration events.
@@ -43,13 +23,6 @@ class Config extends DependencySerialization {
   protected $eventDispatcher;
 
   /**
-   * The language object used to override configuration data.
-   *
-   * @var Drupal\Core\Language\Language
-   */
-  protected $language;
-
-  /**
    * The name of the configuration object.
    *
    * @var string
@@ -57,44 +30,16 @@ class Config extends DependencySerialization {
   protected $name;
 
   /**
-   * Whether the configuration object is new or has been saved to the storage.
-   *
-   * @var bool
-   */
-  protected $isNew = TRUE;
-
-  /**
-   * The data of the configuration object.
-   *
-   * @var array
-   */
-  protected $data = array();
-
-  /**
-   * The original data of the configuration object.
-   *
-   * @var array
-   */
-  protected $originalData = array();
-
-  /**
    * The current runtime data.
    *
-   * The configuration data from storage merged with language, module and
-   * settings overrides.
+   * The configuration data from storage merged with module and settings
+   * overrides.
    *
    * @var array
    */
   protected $overriddenData;
 
   /**
-   * The current language overrides.
-   *
-   * @var array
-   */
-  protected $languageOverrides;
-
-  /**
    * The current module overrides.
    *
    * @var array
@@ -102,25 +47,11 @@ class Config extends DependencySerialization {
   protected $moduleOverrides;
 
   /**
-   * The storage used to load and save this configuration object.
-   *
-   * @var \Drupal\Core\Config\StorageInterface
-   */
-  protected $storage;
-
-  /**
-   * The config schema wrapper object for this configuration object.
-   *
-   * @var \Drupal\Core\Config\Schema\Element
-   */
-  protected $schemaWrapper;
-
-  /**
-   * The typed config manager.
+   * The current settings overrides.
    *
-   * @var \Drupal\Core\Config\TypedConfigManager
+   * @var array
    */
-  protected $typedConfigManager;
+  protected $settingsOverrides;
 
   /**
    * Constructs a configuration object.
@@ -134,123 +65,28 @@ class Config extends DependencySerialization {
    *   An event dispatcher instance to use for configuration events.
    * @param \Drupal\Core\Config\TypedConfigManager $typed_config
    *   The typed configuration manager service.
-   * @param \Drupal\Core\Language\Language $language
-   *   The language object used to override configuration data.
    */
-  public function __construct($name, StorageInterface $storage, EventDispatcherInterface $event_dispatcher, TypedConfigManager $typed_config, Language $language = NULL) {
+  public function __construct($name, StorageInterface $storage, EventDispatcherInterface $event_dispatcher, TypedConfigManager $typed_config) {
     $this->name = $name;
     $this->storage = $storage;
     $this->eventDispatcher = $event_dispatcher;
     $this->typedConfigManager = $typed_config;
-    $this->language = $language;
   }
 
   /**
-   * Initializes a configuration object with pre-loaded data.
-   *
-   * @param array $data
-   *   Array of loaded data for this configuration object.
-   *
-   * @return \Drupal\Core\Config\Config
-   *   The configuration object.
+   * {@inheritdoc}
    */
   public function initWithData(array $data) {
     $this->settingsOverrides = array();
-    $this->languageOverrides = array();
     $this->moduleOverrides = array();
     $this->isNew = FALSE;
-    $this->replaceData($data);
+    $this->setData($data);
     $this->originalData = $this->data;
     return $this;
   }
 
   /**
-   * Returns the name of this configuration object.
-   *
-   * @return string
-   *   The name of the configuration object.
-   */
-  public function getName() {
-    return $this->name;
-  }
-
-  /**
-   * Sets the name of this configuration object.
-   *
-   * @param string $name
-   *  The name of the configuration object.
-   *
-   * @return \Drupal\Core\Config\Config
-   *   The configuration object.
-   */
-  public function setName($name) {
-    $this->name = $name;
-    return $this;
-  }
-
-  /**
-   * Validates the configuration object name.
-   *
-   * @param string $name
-   *  The name of the configuration object.
-   *
-   * @throws \Drupal\Core\Config\ConfigNameException
-   *
-   * @see Config::MAX_NAME_LENGTH
-   */
-  public static function validateName($name) {
-    // The name must be namespaced by owner.
-    if (strpos($name, '.') === FALSE) {
-      throw new ConfigNameException(format_string('Missing namespace in Config object name @name.', array(
-        '@name' => $name,
-      )));
-    }
-    // The name must be shorter than Config::MAX_NAME_LENGTH characters.
-    if (strlen($name) > self::MAX_NAME_LENGTH) {
-      throw new ConfigNameException(format_string('Config object name @name exceeds maximum allowed length of @length characters.', array(
-        '@name' => $name,
-        '@length' => self::MAX_NAME_LENGTH,
-      )));
-    }
-
-    // The name must not contain any of the following characters:
-    // : ? * < > " ' / \
-    if (preg_match('/[:?*<>"\'\/\\\\]/', $name)) {
-      throw new ConfigNameException(format_string('Invalid character in Config object name @name.', array(
-        '@name' => $name,
-      )));
-    }
-  }
-
-  /**
-   * Returns whether this configuration object is new.
-   *
-   * @return bool
-   *   TRUE if this configuration object does not exist in storage.
-   */
-  public function isNew() {
-    return $this->isNew;
-  }
-
-  /**
-   * Gets data from this configuration object.
-   *
-   * @param string $key
-   *   A string that maps to a key within the configuration data.
-   *   For instance in the following configuration array:
-   *   @code
-   *   array(
-   *     'foo' => array(
-   *       'bar' => 'baz',
-   *     ),
-   *   );
-   *   @endcode
-   *   A key of 'foo.bar' would return the string 'baz'. However, a key of 'foo'
-   *   would return array('bar' => 'baz').
-   *   If no key is specified, then the entire data array is returned.
-   *
-   * @return mixed
-   *   The data that was requested.
+   * {@inheritdoc}
    */
   public function get($key = '') {
     if (!isset($this->overriddenData)) {
@@ -272,36 +108,11 @@ public function get($key = '') {
   }
 
   /**
-   * Replaces the data of this configuration object.
-   *
-   * @param array $data
-   *   The new configuration data.
-   *
-   * @return \Drupal\Core\Config\Config
-   *   The configuration object.
+   * {@inheritdoc}
    */
   public function setData(array $data) {
-    $this->replaceData($data);
-    return $this;
-  }
-
-  /**
-   * Replaces the data of this configuration object.
-   *
-   * This function is separate from setData() to avoid load() state tracking.
-   * A load() would destroy the replaced data (for example on import). Do not
-   * call set() when inside load().
-   *
-   * @param array $data
-   *   The new configuration data.
-   *
-   * @return \Drupal\Core\Config\Config
-   *   The configuration object.
-   */
-  protected function replaceData(array $data) {
     $this->data = $data;
-    $this->resetOverriddenData();
-    return $this;
+    return $this->resetOverriddenData();
   }
 
   /**
@@ -337,36 +148,19 @@ public function setModuleOverride(array $data) {
   }
 
   /**
-   * Sets language overrides for this configuration object.
-   *
-   * @param array $data
-   *   The overridden values of the configuration data.
-   *
-   * @return \Drupal\Core\Config\Config
-   *   The configuration object.
-   */
-  public function setLanguageOverride(array $data) {
-    $this->languageOverrides = $data;
-    $this->resetOverriddenData();
-    return $this;
-  }
-
-  /**
    * Sets the current data for this configuration object.
    *
-   * Configuration overrides operate at three distinct layers: language, modules
-   * and settings.php, with the last of these taking precedence. Overrides in
-   * settings.php take precedence over values provided by modules. Overrides
-   * provided by modules take precedence over language.
+   * Configuration overrides operate at two distinct layers: modules and
+   * settings.php, with the last of these taking precedence. Overrides in
+   * settings.php take precedence over values provided by modules. Precedence
+   * or different module overrides is determined by the priority of the
+   * config.factory.override tagged service.
    *
    * @return \Drupal\Core\Config\Config
    *   The configuration object.
    */
   protected function setOverriddenData() {
     $this->overriddenData = $this->data;
-    if (isset($this->languageOverrides) && is_array($this->languageOverrides)) {
-      $this->overriddenData = NestedArray::mergeDeepArray(array($this->overriddenData, $this->languageOverrides), TRUE);
-    }
     if (isset($this->moduleOverrides) && is_array($this->moduleOverrides)) {
       $this->overriddenData = NestedArray::mergeDeepArray(array($this->overriddenData, $this->moduleOverrides), TRUE);
     }
@@ -391,56 +185,23 @@ protected function resetOverriddenData() {
   }
 
   /**
-   * Sets a value in this configuration object.
-   *
-   * @param string $key
-   *   Identifier to store value in configuration.
-   * @param mixed $value
-   *   Value to associate with identifier.
-   *
-   * @return \Drupal\Core\Config\Config
-   *   The configuration object.
+   * {@inheritdoc}
    */
   public function set($key, $value) {
-    // The dot/period is a reserved character; it may appear between keys, but
-    // not within keys.
-    $parts = explode('.', $key);
-    if (count($parts) == 1) {
-      $this->data[$key] = $value;
-    }
-    else {
-      NestedArray::setValue($this->data, $parts, $value);
-    }
-    $this->resetOverriddenData();
-    return $this;
+    parent::set($key, $value);
+    return $this->resetOverriddenData();
   }
 
   /**
-   * Unsets a value in this configuration object.
-   *
-   * @param string $key
-   *   Name of the key whose value should be unset.
-   *
-   * @return \Drupal\Core\Config\Config
-   *   The configuration object.
+   * {@inheritdoc}
    */
   public function clear($key) {
-    $parts = explode('.', $key);
-    if (count($parts) == 1) {
-      unset($this->data[$key]);
-    }
-    else {
-      NestedArray::unsetValue($this->data, $parts);
-    }
-    $this->resetOverriddenData();
-    return $this;
+    parent::clear($key);
+    return $this->resetOverriddenData();
   }
 
   /**
-   * Saves the configuration object.
-   *
-   * @return \Drupal\Core\Config\Config
-   *   The configuration object.
+   * {@inheritdoc}
    */
   public function save() {
     // Validate the configuration object name before saving.
@@ -464,10 +225,7 @@ public function save() {
   }
 
   /**
-   * Deletes the configuration object.
-   *
-   * @return \Drupal\Core\Config\Config
-   *   The configuration object.
+   * {@inheritdoc}
    */
   public function delete() {
     // @todo Consider to remove the pruning of data for Config::delete().
@@ -481,120 +239,6 @@ public function delete() {
   }
 
   /**
-   * Retrieves the storage used to load and save this configuration object.
-   *
-   * @return \Drupal\Core\Config\StorageInterface
-   *   The configuration storage object.
-   */
-  public function getStorage() {
-    return $this->storage;
-  }
-
-  /**
-   * Merges data into a configuration object.
-   *
-   * @param array $data_to_merge
-   *   An array containing data to merge.
-   *
-   * @return \Drupal\Core\Config\Config
-   *   The configuration object.
-   */
-  public function merge(array $data_to_merge) {
-    // Preserve integer keys so that configuration keys are not changed.
-    $this->replaceData(NestedArray::mergeDeepArray(array($this->data, $data_to_merge), TRUE));
-    return $this;
-  }
-
-  /**
-   * Gets the schema wrapper for the whole configuration object.
-   *
-   * The schema wrapper is dependent on the configuration name and the whole
-   * data structure, so if the name or the data changes in any way, the wrapper
-   * should be reset.
-   *
-   * @return \Drupal\Core\Config\Schema\Element
-   */
-  protected function getSchemaWrapper() {
-    if (!isset($this->schemaWrapper)) {
-      $definition = $this->typedConfigManager->getDefinition($this->name);
-      $this->schemaWrapper = $this->typedConfigManager->create($definition, $this->data);
-    }
-    return $this->schemaWrapper;
-  }
-
-  /**
-   * Casts the value to correct data type using the configuration schema.
-   *
-   * @param string $key
-   *   A string that maps to a key within the configuration data.
-   * @param string $value
-   *   Value to associate with the key.
-   *
-   * @return mixed
-   *   The value cast to the type indicated in the schema.
-   *
-   * @throws \Drupal\Core\Config\UnsupportedDataTypeConfigException
-   *   Exception on unsupported/undefined data type deducted.
-   */
-  protected function castValue($key, $value) {
-    if ($value === NULL) {
-      $value = NULL;
-    }
-    elseif (is_scalar($value)) {
-      try {
-        $element = $this->getSchemaWrapper()->get($key);
-        if ($element instanceof PrimitiveInterface) {
-          // Special handling for integers and floats since the configuration
-          // system is primarily concerned with saving values from the Form API
-          // we have to special case the meaning of an empty string for numeric
-          // types. In PHP this would be casted to a 0 but for the purposes of
-          // configuration we need to treat this as a NULL.
-          if ($value === '' && ($element instanceof IntegerInterface || $element instanceof FloatInterface)) {
-            $value = NULL;
-          }
-          else {
-            $value = $element->getCastedValue();
-          }
-        }
-        else {
-          // Config only supports primitive data types. If the config schema
-          // does define a type $element will be an instance of
-          // \Drupal\Core\Config\Schema\Property. Convert it to string since it
-          // is the safest possible type.
-          $value = $element->getString();
-        }
-      }
-      catch (SchemaIncompleteException $e) {
-        // @todo throw an exception due to an incomplete schema.
-        // Fix as part of https://drupal.org/node/2183983.
-      }
-    }
-    else {
-      // Throw exception on any non-scalar or non-array value.
-      if (!is_array($value)) {
-        throw new UnsupportedDataTypeConfigException(String::format('Invalid data type for config element @name:@key', array(
-          '@name' => $this->getName(),
-          '@key' => $key,
-        )));
-      }
-      // Recurse into any nested keys.
-      foreach ($value as $nested_value_key => $nested_value) {
-        $value[$nested_value_key] = $this->castValue($key . '.' . $nested_value_key, $nested_value);
-      }
-    }
-    return $value;
-  }
-
-  /**
-   * Returns the language object for this Config object.
-   *
-   * @return \Drupal\Core\Language\Language
-   */
-  public function getLanguage() {
-    return $this->language;
-  }
-
-  /**
    * Gets the raw data without overrides.
    *
    * @return array
@@ -625,9 +269,6 @@ public function getOriginal($key = '', $apply_overrides = TRUE) {
     $original_data = $this->originalData;
     if ($apply_overrides) {
       // Apply overrides.
-      if (isset($this->languageOverrides) && is_array($this->languageOverrides)) {
-        $original_data = NestedArray::mergeDeepArray(array($original_data, $this->languageOverrides), TRUE);
-      }
       if (isset($this->moduleOverrides) && is_array($this->moduleOverrides)) {
         $original_data = NestedArray::mergeDeepArray(array($original_data, $this->moduleOverrides), TRUE);
       }
@@ -650,5 +291,5 @@ public function getOriginal($key = '', $apply_overrides = TRUE) {
       }
     }
   }
-}
 
+}
diff --git a/core/lib/Drupal/Core/Config/ConfigBase.php b/core/lib/Drupal/Core/Config/ConfigBase.php
new file mode 100644
index 0000000..6240c7b
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/ConfigBase.php
@@ -0,0 +1,214 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Config\ConfigBase.
+ */
+
+namespace Drupal\Core\Config;
+
+use Drupal\Component\Utility\NestedArray;
+use Drupal\Component\Utility\String;
+use \Drupal\Core\DependencyInjection\DependencySerialization;
+
+abstract class ConfigBase extends DependencySerialization {
+
+  /**
+   * The name of the configuration object.
+   *
+   * @var string
+   */
+  protected $name;
+
+  /**
+   * The data of the configuration object.
+   *
+   * @var array
+   */
+  protected $data = array();
+
+  /**
+   * Whether the configuration object is new or has been saved to the storage.
+   *
+   * @var bool
+   */
+  protected $isNew = TRUE;
+
+  /**
+   * The maximum length of a configuration object name.
+   *
+   * Many filesystems (including HFS, NTFS, and ext4) have a maximum file name
+   * length of 255 characters. To ensure that no configuration objects
+   * incompatible with this limitation are created, we enforce a maximum name
+   * length of 250 characters (leaving 5 characters for the file extension).
+   *
+   * @see http://en.wikipedia.org/wiki/Comparison_of_file_systems
+   */
+  const MAX_NAME_LENGTH = 250;
+
+  /**
+   * Returns the name of this configuration object.
+   *
+   * @return string
+   *   The name of the configuration object.
+   */
+  public function getName() {
+    return $this->name;
+  }
+
+  /**
+   * Sets the name of this configuration object.
+   *
+   * @param string $name
+   *  The name of the configuration object.
+   *
+   * @return $this
+   *   The configuration object.
+   */
+  public function setName($name) {
+    $this->name = $name;
+    return $this;
+  }
+
+  /**
+   * Validates the configuration object name.
+   *
+   * @param string $name
+   *  The name of the configuration object.
+   *
+   * @throws \Drupal\Core\Config\ConfigNameException
+   *
+   * @see Config::MAX_NAME_LENGTH
+   */
+  public static function validateName($name) {
+    // The name must be namespaced by owner.
+    if (strpos($name, '.') === FALSE) {
+      throw new ConfigNameException(String::format('Missing namespace in Config object name @name.', array(
+        '@name' => $name,
+      )));
+    }
+    // The name must be shorter than Config::MAX_NAME_LENGTH characters.
+    if (strlen($name) > self::MAX_NAME_LENGTH) {
+      throw new ConfigNameException(String::format('Config object name @name exceeds maximum allowed length of @length characters.', array(
+        '@name' => $name,
+        '@length' => self::MAX_NAME_LENGTH,
+      )));
+    }
+
+    // The name must not contain any of the following characters:
+    // : ? * < > " ' / \
+    if (preg_match('/[:?*<>"\'\/\\\\]/', $name)) {
+      throw new ConfigNameException(String::format('Invalid character in Config object name @name.', array(
+        '@name' => $name,
+      )));
+    }
+  }
+
+  /**
+   * Gets data from this configuration object.
+   *
+   * @param string $key
+   *   A string that maps to a key within the configuration data.
+   *   For instance in the following configuration array:
+   *   @code
+   *   array(
+   *     'foo' => array(
+   *       'bar' => 'baz',
+   *     ),
+   *   );
+   *   @endcode
+   *   A key of 'foo.bar' would return the string 'baz'. However, a key of 'foo'
+   *   would return array('bar' => 'baz').
+   *   If no key is specified, then the entire data array is returned.
+   *
+   * @return mixed
+   *   The data that was requested.
+   */
+  public function get($key = '') {
+    if (empty($key)) {
+      return $this->data;
+    }
+    else {
+      $parts = explode('.', $key);
+      if (count($parts) == 1) {
+        return isset($this->data[$key]) ? $this->data[$key] : NULL;
+      }
+      else {
+        $value = NestedArray::getValue($this->data, $parts, $key_exists);
+        return $key_exists ? $value : NULL;
+      }
+    }
+  }
+
+  /**
+   * Replaces the data of this configuration object.
+   *
+   * @param array $data
+   *   The new configuration data.
+   *
+   * @return $this
+   *   The configuration object.
+   */
+  public function setData(array $data) {
+    $this->data = $data;
+    return $this;
+  }
+
+  /**
+   * Sets a value in this configuration object.
+   *
+   * @param string $key
+   *   Identifier to store value in configuration.
+   * @param mixed $value
+   *   Value to associate with identifier.
+   *
+   * @return $this
+   *   The configuration object.
+   */
+  public function set($key, $value) {
+    // The dot/period is a reserved character; it may appear between keys, but
+    // not within keys.
+    $parts = explode('.', $key);
+    if (count($parts) == 1) {
+      $this->data[$key] = $value;
+    }
+    else {
+      NestedArray::setValue($this->data, $parts, $value);
+    }
+    return $this;
+  }
+
+  /**
+   * Unsets a value in this configuration object.
+   *
+   * @param string $key
+   *   Name of the key whose value should be unset.
+   *
+   * @return $this
+   *   The configuration object.
+   */
+  public function clear($key) {
+    $parts = explode('.', $key);
+    if (count($parts) == 1) {
+      unset($this->data[$key]);
+    }
+    else {
+      NestedArray::unsetValue($this->data, $parts);
+    }
+    return $this;
+  }
+
+  /**
+   * Merges data into a configuration object.
+   *
+   * @param array $data_to_merge
+   *   An array containing data to merge.
+   *
+   * @return $this
+   *   The configuration object.
+   */
+  public function merge ($data) {
+    return $this->setData(NestedArray::mergeDeepArray(array($this->data, $data), TRUE));
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Config/ConfigEvents.php b/core/lib/Drupal/Core/Config/ConfigEvents.php
index 0b0bb3a..8f61193 100644
--- a/core/lib/Drupal/Core/Config/ConfigEvents.php
+++ b/core/lib/Drupal/Core/Config/ConfigEvents.php
@@ -35,13 +35,6 @@
   const RENAME = 'config.rename';
 
   /**
-   * Name of event fired when collecting overrides for configuration objects.
-   *
-   * @see \Drupal\Core\Config\ConfigFactory::loadModuleOverrides().
-   */
-  const MODULE_OVERRIDES = 'config.module.overrides';
-
-  /**
    * Name of event fired when validating in the configuration import process.
    *
    * @see \Drupal\Core\Config\ConfigImporter::validate().
diff --git a/core/lib/Drupal/Core/Config/ConfigFactory.php b/core/lib/Drupal/Core/Config/ConfigFactory.php
index 3d26c78..04acb73 100644
--- a/core/lib/Drupal/Core/Config/ConfigFactory.php
+++ b/core/lib/Drupal/Core/Config/ConfigFactory.php
@@ -7,9 +7,7 @@
 
 namespace Drupal\Core\Config;
 
-use Drupal\Core\Language\Language;
-use Drupal\Core\Language\LanguageDefault;
-use Symfony\Component\EventDispatcher\EventDispatcher;
+use Drupal\Component\Utility\NestedArray;
 use Symfony\Component\EventDispatcher\EventDispatcherInterface;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
@@ -50,13 +48,6 @@ class ConfigFactory implements ConfigFactoryInterface, EventSubscriberInterface
   protected $useOverrides = TRUE;
 
   /**
-   * The language object used to override configuration data.
-   *
-   * @var \Drupal\Core\Language\Language
-   */
-  protected $language;
-
-  /**
    * Cached configuration objects.
    *
    * @var \Drupal\Core\Config\Config[]
@@ -71,6 +62,20 @@ class ConfigFactory implements ConfigFactoryInterface, EventSubscriberInterface
   protected $typedConfigManager;
 
   /**
+   * An array of config factory override objects grouped by priority.
+   *
+   * @var array
+   */
+  protected $configFactoryOverrides = array();
+
+  /**
+   * An array of sorted config factory override objects.
+   *
+   * @var array|\Drupal\Core\Config\ConfigFactoryOverrideInterface[]
+   */
+  protected $sortedConfigFactoryOverrides;
+
+  /**
    * Constructs the Config factory.
    *
    * @param \Drupal\Core\Config\StorageInterface $storage
@@ -118,23 +123,13 @@ public function get($name) {
         // If the configuration object does not exist in the configuration
         // storage or static cache create a new object and add it to the static
         // cache.
-        $this->cache[$cache_key] = new Config($name, $this->storage, $this->eventDispatcher, $this->typedConfigManager, $this->language);
+        $this->cache[$cache_key] = new Config($name, $this->storage, $this->eventDispatcher, $this->typedConfigManager);
 
-        if ($this->canOverride($name)) {
-          // Get and apply any language overrides.
-          if ($this->language) {
-            $language_overrides = $this->storage->read($this->getLanguageConfigName($this->language->id, $name));
-          }
-          else {
-            $language_overrides = FALSE;
-          }
-          if (is_array($language_overrides)) {
-            $this->cache[$cache_key]->setLanguageOverride($language_overrides);
-          }
-          // Get and apply any module overrides.
-          $module_overrides = $this->loadModuleOverrides(array($name));
-          if (isset($module_overrides[$name])) {
-            $this->cache[$cache_key]->setModuleOverride($module_overrides[$name]);
+        if ($this->useOverrides) {
+          // Get and apply any overrides.
+          $overrides = $this->loadOverrides(array($name));
+          if (isset($overrides[$name])) {
+            $this->cache[$cache_key]->setModuleOverride($overrides[$name]);
           }
           // Apply any settings.php overrides.
           if (isset($GLOBALS['config'][$name])) {
@@ -166,38 +161,19 @@ public function loadMultiple(array $names) {
     if (!empty($names)) {
       // Initialise override information.
       $module_overrides = array();
-      $language_names = array();
-
-      if ($this->useOverrides) {
-        // In order to make just one call to storage, add in language names.
-        // Keep track of them separately, so we can get language override data
-        // returned from storage and set it on new Config objects.
-        $language_names = $this->getLanguageConfigNames($names);
-      }
-
-      $storage_data = $this->storage->readMultiple(array_merge($names, array_values($language_names)));
+      $storage_data = $this->storage->readMultiple($names);
 
       if ($this->useOverrides && !empty($storage_data)) {
-        // Only fire module override event if we have configuration to override.
-        $module_overrides = $this->loadModuleOverrides($names);
+        // Only get module overrides if we have configuration to override.
+        $module_overrides = $this->loadOverrides($names);
       }
 
       foreach ($storage_data as $name => $data) {
-        if (in_array($name, $language_names)) {
-          // Language override configuration is used to override other
-          // configuration. Therefore, when it has been added to the
-          // $storage_data it is not statically cached in the config factory or
-          // overridden in any way.
-          continue;
-        }
         $cache_key = $this->getCacheKey($name);
 
-        $this->cache[$cache_key] = new Config($name, $this->storage, $this->eventDispatcher, $this->typedConfigManager, $this->language);
+        $this->cache[$cache_key] = new Config($name, $this->storage, $this->eventDispatcher, $this->typedConfigManager);
         $this->cache[$cache_key]->initWithData($data);
-        if ($this->canOverride($name)) {
-          if (isset($language_names[$name]) && isset($storage_data[$language_names[$name]])) {
-            $this->cache[$cache_key]->setLanguageOverride($storage_data[$language_names[$name]]);
-          }
+        if ($this->useOverrides) {
           if (isset($module_overrides[$name])) {
             $this->cache[$cache_key]->setModuleOverride($module_overrides[$name]);
           }
@@ -221,10 +197,14 @@ public function loadMultiple(array $names) {
    * @return array
    *   An array of overrides keyed by the configuration object name.
    */
-  protected function loadModuleOverrides(array $names) {
-    $configOverridesEvent = new ConfigModuleOverridesEvent($names, $this->language);
-    $this->eventDispatcher->dispatch(ConfigEvents::MODULE_OVERRIDES, $configOverridesEvent);
-    return $configOverridesEvent->getOverrides();
+  protected function loadOverrides(array $names) {
+    $overrides = array();
+    foreach ($this->getSortedConfigFactoryOverrides() as $override) {
+      // Existing overrides take precedence since these will have been added
+      // by events with a higher priority.
+      $overrides = NestedArray::mergeDeepArray(array($override->loadOverrides($names), $overrides), TRUE);
+    }
+    return $overrides;
   }
 
   /**
@@ -268,11 +248,14 @@ public function rename($old_name, $new_name) {
    * {@inheritdoc}
    */
   public function getCacheKey($name) {
-    $can_override = $this->canOverride($name);
-    $cache_key = $name . ':' . ($can_override ? 'overrides' : 'raw');
-
-    if ($can_override && isset($this->language)) {
-      $cache_key =  $cache_key . ':' . $this->language->id;
+    if ($this->useOverrides) {
+      $cache_key = $name . ':overrides';
+      foreach($this->getSortedConfigFactoryOverrides() as $override) {
+        $cache_key =  $cache_key . ':' . $override->getCacheSuffix();
+      }
+    }
+    else {
+      $cache_key = $name . ':raw';
     }
     return $cache_key;
   }
@@ -298,74 +281,11 @@ public function clearStaticCache() {
   /**
    * {@inheritdoc}
    */
-  public function setLanguage(Language $language = NULL) {
-    $this->language = $language;
-    return $this;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function setLanguageFromDefault(LanguageDefault $language_default) {
-    $this->language = $language_default->get();
-    return $this;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getLanguage() {
-    return $this->language;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getLanguageConfigNames(array $names) {
-    $language_names = array();
-    if (isset($this->language)) {
-      foreach ($names as $name) {
-        if ($language_name = $this->getLanguageConfigName($this->language->id, $name)) {
-          $language_names[$name] = $language_name;
-        }
-      }
-    }
-    return $language_names;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getLanguageConfigName($langcode, $name) {
-    if (strpos($name, static::LANGUAGE_CONFIG_PREFIX) === 0) {
-      return FALSE;
-    }
-    return static::LANGUAGE_CONFIG_PREFIX . '.' . $langcode . '.' . $name;
-  }
-
-  /**
-   * {@inheritdoc}
-   */
   public function listAll($prefix = '') {
     return $this->storage->listAll($prefix);
   }
 
   /**
-   * Determines if a particular configuration object can be overridden.
-   *
-   * Language override configuration should not be overridden.
-   *
-   * @param string $name
-   *   The name of the configuration object.
-   *
-   * @return bool
-   *   TRUE if the configuration object can be overridden.
-   */
-  protected function canOverride($name) {
-    return $this->useOverrides && !(strpos($name, static::LANGUAGE_CONFIG_PREFIX) === 0);
-  }
-
-  /**
    * Removes stale static cache entries when configuration is saved.
    *
    * @param ConfigCrudEvent $event
@@ -392,4 +312,30 @@ static function getSubscribedEvents() {
     return $events;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function addOverride(ConfigFactoryOverrideInterface $config_factory_override, $priority) {
+    $this->configFactoryOverrides[$priority][] = $config_factory_override;
+    // Force the overrides to be re-sorted.
+    $this->sortedConfigFactoryOverrides = NULL;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getSortedConfigFactoryOverrides() {
+    if (!isset($this->sortedConfigFactoryOverrides)) {
+      // Sort the override objects according to priority.
+      krsort($this->configFactoryOverrides);
+      // Merge nested override objects from $this->configFactoryOverrides into
+      // $this->sortedConfigFactoryOverrides.
+      $this->sortedConfigFactoryOverrides = array();
+      foreach ($this->configFactoryOverrides as $overrides) {
+        $this->sortedConfigFactoryOverrides = array_merge($this->sortedConfigFactoryOverrides, $overrides);
+      }
+    }
+    return $this->sortedConfigFactoryOverrides;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Config/ConfigFactoryInterface.php b/core/lib/Drupal/Core/Config/ConfigFactoryInterface.php
index e836a2b..a1e0a01 100644
--- a/core/lib/Drupal/Core/Config/ConfigFactoryInterface.php
+++ b/core/lib/Drupal/Core/Config/ConfigFactoryInterface.php
@@ -7,20 +7,12 @@
 
 namespace Drupal\Core\Config;
 
-use Drupal\Core\Language\Language;
-use Drupal\Core\Language\LanguageDefault;
-
 /**
  * Defines the interface for a configuration object factory.
  */
 interface ConfigFactoryInterface {
 
   /**
-   * Prefix for all language configuration files.
-   */
-  const LANGUAGE_CONFIG_PREFIX = 'language.config';
-
-  /**
    * Sets the override state.
    *
    * @param bool $state
@@ -117,78 +109,35 @@ public function getCacheKeys($name);
   public function clearStaticCache();
 
   /**
-   * Sets the language to be used in configuration overrides.
-   *
-   * @param \Drupal\Core\Language\Language|null $language
-   *   The language object to be set on the config factory. Used to override
-   *   configuration by language.
-   *
-   * @return $this
-   */
-  public function setLanguage(Language $language = NULL);
-
-  /**
-   * Sets the language for configuration overrides using the default language.
-   *
-   * @param \Drupal\Core\Language\LanguageDefault $language_default
-   *   The default language service. This sets the initial language on the
-   *   config factory to the site's default. The language can be used to
-   *   override configuration data if language overrides are available.
-   *
-   * @return $this
-   */
-  public function setLanguageFromDefault(LanguageDefault $language_default);
-
-  /**
-   * Gets the language Used to override configuration.
-   *
-   * @return \Drupal\Core\Language\Language
-   */
-  public function getLanguage();
-
-  /**
-   * Gets configuration names for this language.
+   * Gets configuration object names starting with a given prefix.
    *
-   * It will be the same name with a prefix depending on language code:
-   * language.config.LANGCODE.NAME
+   * @see \Drupal\Core\Config\StorageInterface::listAll()
    *
-   * @param array $names
-   *   A list of configuration object names.
+   * @param string $prefix
+   *   (optional) The prefix to search for. If omitted, all configuration object
+   *   names that exist are returned.
    *
    * @return array
-   *   The localized config names, keyed by configuration object name.
+   *   An array containing matching configuration object names.
    */
-  public function getLanguageConfigNames(array $names);
+  public function listAll($prefix = '');
 
   /**
-   * Gets configuration name for the provided language.
-   *
-   * The name will be the same name with a prefix depending on language code:
-   * language.config.LANGCODE.NAME
-   *
-   * @param string $langcode
-   *   The language code.
-   * @param string $name
-   *   The name of the configuration object.
+   * Adds config factory override service.
    *
-   * @return bool|string
-   *   The configuration name for configuration object providing overrides.
-   *   Returns false if the name already starts with the language config prefix.
+   * @param \Drupal\Core\Config\ConfigFactoryOverrideInterface $config_factory_override
+   *   The config factory to add.
+   * @param int $priority
+   *   Priority of the config factory override.
    */
-  public function getLanguageConfigName($langcode, $name);
+  public function addOverride(ConfigFactoryOverrideInterface $config_factory_override, $priority);
 
   /**
-   * Gets configuration object names starting with a given prefix.
-   *
-   * @see \Drupal\Core\Config\StorageInterface::listAll()
-   *
-   * @param string $prefix
-   *   (optional) The prefix to search for. If omitted, all configuration object
-   *   names that exist are returned.
+   * Returns the sorted array of config factory override objects.
    *
-   * @return array
-   *   An array containing matching configuration object names.
+   * @return array|\Drupal\Core\Config\ConfigFactoryOverrideInterface[]
+   *   An array of config factory override objects.
    */
-  public function listAll($prefix = '');
+  public function getSortedConfigFactoryOverrides();
 
 }
diff --git a/core/lib/Drupal/Core/Config/ConfigFactoryOverrideInterface.php b/core/lib/Drupal/Core/Config/ConfigFactoryOverrideInterface.php
new file mode 100644
index 0000000..75298b3
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/ConfigFactoryOverrideInterface.php
@@ -0,0 +1,52 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Config\ConfigFactoryOverrideInterface.
+ */
+
+namespace Drupal\Core\Config;
+
+/**
+ * Defines the interface for a configuration factory override object.
+ */
+interface ConfigFactoryOverrideInterface {
+
+  /**
+   * @param array $names
+   *   A list of configuration names that is being loaded.
+   *
+   * @return array
+   *   An array keyed by configuration name of override data.
+   */
+  public function loadOverrides($names);
+
+  /**
+   * The string to append to the configuration static cache.
+   *
+   * @return string
+   *   A string to append to the configuration static cache.
+   */
+  public function getCacheSuffix();
+
+  /**
+   * Reacts to default configuration installation during extension install.
+   *
+   * @param string $type
+   *   The type of extension being installed. Either 'module' or 'theme'.
+   * @param string $name
+   *   The name of the extension.
+   */
+  public function install($type, $name);
+
+  /**
+   * Reacts to configuration removal during extension uninstallation.
+   *
+   * @param string $type
+   *   The type of extension being uninstalled. Either 'module' or 'theme'.
+   * @param string $name
+   *   The name of the extension.
+   */
+  public function uninstall($type, $name);
+
+}
diff --git a/core/lib/Drupal/Core/Config/ConfigFactoryOverridePass.php b/core/lib/Drupal/Core/Config/ConfigFactoryOverridePass.php
new file mode 100644
index 0000000..beae669
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/ConfigFactoryOverridePass.php
@@ -0,0 +1,33 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Config\ConfigFactoryOverridePass.
+ */
+
+namespace Drupal\Core\Config;
+
+use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
+use Symfony\Component\DependencyInjection\ContainerBuilder;
+use Symfony\Component\DependencyInjection\Reference;
+
+/**
+ * Adds services to the config factory service.
+ *
+ * @see \Drupal\Core\Config\ConfigFactory
+ * @see \Drupal\Core\Config\ConfigFactoryOverrideInterface
+ */
+class ConfigFactoryOverridePass implements CompilerPassInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function process(ContainerBuilder $container) {
+    $manager = $container->getDefinition('config.factory');
+    foreach ($container->findTaggedServiceIds('config.factory.override') as $id => $attributes) {
+      $priority = isset($attributes[0]['priority']) ? $attributes[0]['priority'] : 0;
+      $manager->addMethodCall('addOverride', array(new Reference($id), $priority));
+    }
+  }
+
+}
diff --git a/core/lib/Drupal/Core/Config/ConfigInstaller.php b/core/lib/Drupal/Core/Config/ConfigInstaller.php
index 2bb06b3..06477ec 100644
--- a/core/lib/Drupal/Core/Config/ConfigInstaller.php
+++ b/core/lib/Drupal/Core/Config/ConfigInstaller.php
@@ -107,18 +107,18 @@ public function installDefaultConfig($type, $name) {
     if (!empty($config_to_install)) {
       $old_state = $this->configFactory->getOverrideState();
       $this->configFactory->setOverrideState(FALSE);
-      foreach ($config_to_install as $name) {
+      foreach ($config_to_install as $config_name) {
         // Only import new config.
-        if ($this->activeStorage->exists($name)) {
+        if ($this->activeStorage->exists($config_name)) {
           continue;
         }
 
-        $new_config = new Config($name, $this->activeStorage, $this->eventDispatcher, $this->typedConfig);
-        $data = $source_storage->read($name);
+        $new_config = new Config($config_name, $this->activeStorage, $this->eventDispatcher, $this->typedConfig);
+        $data = $source_storage->read($config_name);
         if ($data !== FALSE) {
           $new_config->setData($data);
         }
-        if ($entity_type = $this->configManager->getEntityTypeIdByName($name)) {
+        if ($entity_type = $this->configManager->getEntityTypeIdByName($config_name)) {
           $this->configManager
             ->getEntityManager()
             ->getStorageController($entity_type)
@@ -131,6 +131,11 @@ public function installDefaultConfig($type, $name) {
       }
       $this->configFactory->setOverrideState($old_state);
     }
+
+    // Allow configFactory overrides to respond to installation.
+    foreach ($this->configFactory->getSortedConfigFactoryOverrides() as $config_override) {
+      $config_override->install($type, $name);
+    }
   }
 
 }
diff --git a/core/lib/Drupal/Core/Config/ConfigManager.php b/core/lib/Drupal/Core/Config/ConfigManager.php
index d52e806..2abc7af 100644
--- a/core/lib/Drupal/Core/Config/ConfigManager.php
+++ b/core/lib/Drupal/Core/Config/ConfigManager.php
@@ -129,6 +129,11 @@ public function uninstall($type, $name) {
     foreach ($config_names as $config_name) {
       $this->configFactory->get($config_name)->delete();
     }
+    // Allow configFactory overrides to respond to uninstallation.
+    foreach ($this->configFactory->getSortedConfigFactoryOverrides() as $config_override) {
+      $config_override->uninstall($type, $name);
+    }
+
     $schema_dir = drupal_get_path($type, $name) . '/config/schema';
     if (is_dir($schema_dir)) {
       // Refresh the schema cache if uninstalling an extension that provides
diff --git a/core/lib/Drupal/Core/Config/FileStorage.php b/core/lib/Drupal/Core/Config/FileStorage.php
index 2df9fbd..b139374 100644
--- a/core/lib/Drupal/Core/Config/FileStorage.php
+++ b/core/lib/Drupal/Core/Config/FileStorage.php
@@ -241,4 +241,15 @@ public function deleteAll($prefix = '') {
 
     return $success;
   }
+
+  /**
+   * Sets the filesystem path for configuration objects.
+   *
+   * @param string $directory
+   *   The filesystem path for configuration objects.
+   */
+  public function setDirectory($directory) {
+    $this->directory = $directory;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Config/StorableConfigBase.php b/core/lib/Drupal/Core/Config/StorableConfigBase.php
new file mode 100644
index 0000000..8b05f95
--- /dev/null
+++ b/core/lib/Drupal/Core/Config/StorableConfigBase.php
@@ -0,0 +1,185 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\Core\Config\StorableConfigBase.
+ */
+
+namespace Drupal\Core\Config;
+
+use Drupal\Component\Utility\String;
+use Drupal\Core\Config\Schema\SchemaIncompleteException;
+use Drupal\Core\TypedData\PrimitiveInterface;
+use Drupal\Core\TypedData\Type\FloatInterface;
+use Drupal\Core\TypedData\Type\IntegerInterface;
+
+abstract class StorableConfigBase extends ConfigBase {
+
+  /**
+   * The storage used to load and save this configuration object.
+   *
+   * @var \Drupal\Core\Config\StorageInterface
+   */
+  protected $storage;
+
+  /**
+   * The config schema wrapper object for this configuration object.
+   *
+   * @var \Drupal\Core\Config\Schema\Element
+   */
+  protected $schemaWrapper;
+
+  /**
+   * The typed config manager.
+   *
+   * @var \Drupal\Core\Config\TypedConfigManager
+   */
+  protected $typedConfigManager;
+
+  /**
+   * Whether the configuration object is new or has been saved to the storage.
+   *
+   * @var bool
+   */
+  protected $isNew = TRUE;
+
+  /**
+   * The data of the configuration object.
+   *
+   * @var array
+   */
+  protected $originalData = array();
+
+  /**
+   * Saves the configuration object.
+   *
+   * @return \Drupal\Core\Config\Config
+   *   The configuration object.
+   */
+  abstract public function save();
+
+  /**
+   * Deletes the configuration object.
+   *
+   * @return \Drupal\Core\Config\Config
+   *   The configuration object.
+   */
+  abstract public function delete();
+
+  /**
+   * Initializes a configuration object with pre-loaded data.
+   *
+   * @param array $data
+   *   Array of loaded data for this configuration object.
+   *
+   * @return $this
+   *   The configuration object.
+   */
+  public function initWithData(array $data) {
+    $this->isNew = FALSE;
+    $this->setData($data);
+    $this->originalData = $this->data;
+    return $this;
+  }
+
+  /**
+   * Returns whether this configuration object is new.
+   *
+   * @return bool
+   *   TRUE if this configuration object does not exist in storage.
+   */
+  public function isNew() {
+    return $this->isNew;
+  }
+
+  /**
+   * Retrieves the storage used to load and save this configuration object.
+   *
+   * @return \Drupal\Core\Config\StorageInterface
+   *   The configuration storage object.
+   */
+  public function getStorage() {
+    return $this->storage;
+  }
+
+  /**
+   * Gets the schema wrapper for the whole configuration object.
+   *
+   * The schema wrapper is dependent on the configuration name and the whole
+   * data structure, so if the name or the data changes in any way, the wrapper
+   * should be reset.
+   *
+   * @return \Drupal\Core\Config\Schema\Element
+   */
+  protected function getSchemaWrapper() {
+    if (!isset($this->schemaWrapper)) {
+      $definition = $this->typedConfigManager->getDefinition($this->name);
+      $this->schemaWrapper = $this->typedConfigManager->create($definition, $this->data);
+    }
+    return $this->schemaWrapper;
+  }
+
+  /**
+   * Casts the value to correct data type using the configuration schema.
+   *
+   * @param string $key
+   *   A string that maps to a key within the configuration data.
+   * @param string $value
+   *   Value to associate with the key.
+   *
+   * @return mixed
+   *   The value cast to the type indicated in the schema.
+   *
+   * @throws \Drupal\Core\Config\UnsupportedDataTypeConfigException
+   *   Exception on unsupported/undefined data type deducted.
+   */
+  protected function castValue($key, $value) {
+    if ($value === NULL) {
+      $value = NULL;
+    }
+    elseif (is_scalar($value)) {
+      try {
+        $element = $this->getSchemaWrapper()->get($key);
+        if ($element instanceof PrimitiveInterface) {
+          // Special handling for integers and floats since the configuration
+          // system is primarily concerned with saving values from the Form API
+          // we have to special case the meaning of an empty string for numeric
+          // types. In PHP this would be casted to a 0 but for the purposes of
+          // configuration we need to treat this as a NULL.
+          if ($value === '' && ($element instanceof IntegerInterface || $element instanceof FloatInterface)) {
+            $value = NULL;
+          }
+          else {
+            $value = $element->getCastedValue();
+          }
+        }
+        else {
+          // Config only supports primitive data types. If the config schema
+          // does define a type $element will be an instance of
+          // \Drupal\Core\Config\Schema\Property. Convert it to string since it
+          // is the safest possible type.
+          $value = $element->getString();
+        }
+      }
+      catch (SchemaIncompleteException $e) {
+        // @todo throw an exception due to an incomplete schema.
+        // Fix as part of https://drupal.org/node/2183983.
+      }
+    }
+    else {
+      // Throw exception on any non-scalar or non-array value.
+      if (!is_array($value)) {
+        throw new UnsupportedDataTypeConfigException(String::format('Invalid data type for config element @name:@key', array(
+          '@name' => $this->getName(),
+          '@key' => $key,
+        )));
+      }
+      // Recurse into any nested keys.
+      foreach ($value as $nested_value_key => $nested_value) {
+        $value[$nested_value_key] = $this->castValue($key . '.' . $nested_value_key, $nested_value);
+      }
+    }
+    return $value;
+  }
+
+}
diff --git a/core/lib/Drupal/Core/CoreServiceProvider.php b/core/lib/Drupal/Core/CoreServiceProvider.php
index 847960c..5c3526f 100644
--- a/core/lib/Drupal/Core/CoreServiceProvider.php
+++ b/core/lib/Drupal/Core/CoreServiceProvider.php
@@ -8,6 +8,7 @@
 namespace Drupal\Core;
 
 use Drupal\Core\Cache\ListCacheBinsPass;
+use Drupal\Core\Config\ConfigFactoryOverridePass;
 use Drupal\Core\DependencyInjection\ServiceProviderInterface;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\DependencyInjection\Compiler\ModifyServiceDefinitionsPass;
@@ -78,6 +79,9 @@ public function register(ContainerBuilder $container) {
     // Add the compiler pass that will process the tagged theme negotiator
     // service.
     $container->addCompilerPass(new ThemeNegotiatorPass());
+    // Add the compiler pass that will process the tagged config factory
+    // override services.
+    $container->addCompilerPass(new ConfigFactoryOverridePass());
     // Add the compiler pass that will process tagged authentication services.
     $container->addCompilerPass(new RegisterAuthenticationPass());
     // Register Twig extensions.
diff --git a/core/lib/Drupal/Core/Datetime/Date.php b/core/lib/Drupal/Core/Datetime/Date.php
index 5c2b92b..c908ca8 100644
--- a/core/lib/Drupal/Core/Datetime/Date.php
+++ b/core/lib/Drupal/Core/Datetime/Date.php
@@ -215,11 +215,10 @@ protected function t($string, array $args = array(), array $options = array()) {
    */
   protected function dateFormat($format, $langcode) {
     if (!isset($this->dateFormats[$format][$langcode])) {
-      // Enter a language specific context so the right date format is loaded.
-      $original_language = $this->configFactory->getLanguage();
-      $this->configFactory->setLanguage(new Language(array('id' => $langcode)));
+      $original_language = $this->languageManager->getConfigOverrideLanguage();
+      $this->languageManager->setConfigOverrideLanguage(new Language(array('id' => $langcode)));
       $this->dateFormats[$format][$langcode] = $this->dateFormatStorage->load($format);
-      $this->configFactory->setLanguage($original_language);
+      $this->languageManager->setConfigOverrideLanguage($original_language);
     }
     return $this->dateFormats[$format][$langcode];
   }
diff --git a/core/lib/Drupal/Core/Language/LanguageManager.php b/core/lib/Drupal/Core/Language/LanguageManager.php
index 6ddf673..783ec20 100644
--- a/core/lib/Drupal/Core/Language/LanguageManager.php
+++ b/core/lib/Drupal/Core/Language/LanguageManager.php
@@ -329,4 +329,24 @@ public static function getStandardLanguageList() {
     );
   }
 
+  /**
+   * {@inheritdoc}
+   *
+   * This function is a noop since the configuration can not be overridden by
+   * language unless the Language module is enabled. This replaces the default
+   * language manger with a configurable language manager.
+   *
+   * @see \Drupal\language\ConfigurableLanguageManager::setConfigOverrideLanguage()
+   */
+  public function setConfigOverrideLanguage(Language $language) {
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfigOverrideLanguage() {
+    return $this->getCurrentLanguage();
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Language/LanguageManagerInterface.php b/core/lib/Drupal/Core/Language/LanguageManagerInterface.php
index 16c7c01..764cf0b 100644
--- a/core/lib/Drupal/Core/Language/LanguageManagerInterface.php
+++ b/core/lib/Drupal/Core/Language/LanguageManagerInterface.php
@@ -168,4 +168,23 @@ public function getFallbackCandidates($langcode = NULL, array $context = array()
    */
   function getLanguageSwitchLinks($type, $path);
 
+  /**
+   * Sets the configuration override language.
+   *
+   * @param \Drupal\Core\Language\Language $language
+   *   The language to override configuration with.
+   *
+   * @return $this
+   *   The language manager.
+   */
+  public function setConfigOverrideLanguage(Language $language);
+
+  /**
+   * Gets the current configuration override language.
+   *
+   * @return \Drupal\Core\Language\Language $language
+   *   The current configuration override language.
+   */
+  public function getConfigOverrideLanguage();
+
 }
diff --git a/core/lib/Drupal/Core/Theme/ThemeSettings.php b/core/lib/Drupal/Core/Theme/ThemeSettings.php
index d20b843..9f94b56 100644
--- a/core/lib/Drupal/Core/Theme/ThemeSettings.php
+++ b/core/lib/Drupal/Core/Theme/ThemeSettings.php
@@ -7,12 +7,12 @@
 
 namespace Drupal\Core\Theme;
 
-use Drupal\Component\Utility\NestedArray;
+use Drupal\Core\Config\ConfigBase;
 
 /**
  * Defines the default theme settings object.
  */
-class ThemeSettings {
+class ThemeSettings extends ConfigBase {
 
   /**
    * The theme of the theme settings object.
@@ -22,13 +22,6 @@ class ThemeSettings {
   protected $theme;
 
   /**
-   * The data of the theme settings object.
-   *
-   * @var array
-   */
-  protected $data;
-
-  /**
    * Constructs a theme settings object.
    *
    * @param string $name
@@ -36,7 +29,6 @@ class ThemeSettings {
    */
   public function __construct($theme) {
     $this->theme = $theme;
-    $this->data = array();
   }
 
   /**
@@ -49,112 +41,4 @@ public function getTheme() {
     return $this->theme;
   }
 
-  /**
-   * Gets data from this theme settings object.
-   *
-   * @param string $key
-   *   A string that maps to a key within the theme settings data.
-   *   For instance in the following theme settings array:
-   *   @code
-   *   array(
-   *     'foo' => array(
-   *       'bar' => 'baz',
-   *     ),
-   *   );
-   *   @endcode
-   *   A key of 'foo.bar' would return the string 'baz'. However, a key of 'foo'
-   *   would return array('bar' => 'baz').
-   *   If no key is specified, then the entire data array is returned.
-   *
-   *
-   * @return mixed
-   *   The data that was requested.
-   */
-  public function get($key = '') {
-    if (empty($key)) {
-      return $this->data;
-    }
-    else {
-      $parts = explode('.', $key);
-      if (count($parts) == 1) {
-        return isset($this->data[$key]) ? $this->data[$key] : NULL;
-      }
-      else {
-        $value = NestedArray::getValue($this->data, $parts, $key_exists);
-        return $key_exists ? $value : NULL;
-      }
-    }
-  }
-
-  /**
-   * Replaces the data of this theme settings object.
-   *
-   * @param array $data
-   *   The new theme settings data.
-   *
-   * @return \Drupal\Core\Theme\ThemeSettings
-   *   The theme settings object.
-   */
-  public function setData(array $data) {
-    $this->data = $data;
-    return $this;
-  }
-
-  /**
-   * Sets value in this theme settings object.
-   *
-   * @param string $key
-   *   Identifier to store value in theme settings.
-   * @param string $value
-   *   Value to associate with identifier.
-   *
-   * @return \Drupal\Core\Theme\ThemeSettings
-   *   The theme settings object.
-   */
-  public function set($key, $value) {
-    // The dot/period is a reserved character; it may appear between keys, but
-    // not within keys.
-    $parts = explode('.', $key);
-    if (count($parts) == 1) {
-      $this->data[$key] = $value;
-    }
-    else {
-      NestedArray::setValue($this->data, $parts, $value);
-    }
-    return $this;
-  }
-
-  /**
-   * Unsets value in this theme settings object.
-   *
-   * @param string $key
-   *   Name of the key whose value should be unset.
-   *
-   * @return \Drupal\Core\Theme\ThemeSettings
-   *   The theme settings object.
-   */
-  public function clear($key) {
-    $parts = explode('.', $key);
-    if (count($parts) == 1) {
-      unset($this->data[$key]);
-    }
-    else {
-      NestedArray::unsetValue($this->data, $parts);
-    }
-    return $this;
-  }
-
-  /**
-   * Merges the data into this theme settings object.
-   *
-   * @param array $data
-   *   Theme settings data to merge.
-   *
-   * @return \Drupal\Core\Theme\ThemeSettings
-   *   The theme settings object.
-   */
-  public function mergeData ($data) {
-    $this->data = NestedArray::mergeDeep($this->data, $data);
-    return $this;
-  }
 }
diff --git a/core/modules/ckeditor/lib/Drupal/ckeditor/Tests/CKEditorTest.php b/core/modules/ckeditor/lib/Drupal/ckeditor/Tests/CKEditorTest.php
index 7a6f3cc..81853af 100644
--- a/core/modules/ckeditor/lib/Drupal/ckeditor/Tests/CKEditorTest.php
+++ b/core/modules/ckeditor/lib/Drupal/ckeditor/Tests/CKEditorTest.php
@@ -345,7 +345,7 @@ function testLanguages() {
    * Tests that CKEditor plugins participate in JS translation.
    */
   function testJSTranslation() {
-    $this->enableModules(array('locale'));
+    $this->enableModules(array('language', 'locale'));
     $this->installSchema('locale', 'locales_source');
     $this->installSchema('locale', 'locales_location');
     $editor = entity_load('editor', 'filtered_html');
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigLanguageOverride.php b/core/modules/config/lib/Drupal/config/Tests/ConfigLanguageOverrideTest.php
similarity index 60%
rename from core/modules/config/lib/Drupal/config/Tests/ConfigLanguageOverride.php
rename to core/modules/config/lib/Drupal/config/Tests/ConfigLanguageOverrideTest.php
index 12ff3cd..e0d5b6b 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigLanguageOverride.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigLanguageOverrideTest.php
@@ -2,7 +2,7 @@
 
 /**
  * @file
- * Contains \Drupal\config\Tests\ConfigLanguageOverride.
+ * Contains \Drupal\config\Tests\ConfigLanguageOverrideTest.
  */
 
 namespace Drupal\config\Tests;
@@ -13,14 +13,14 @@
 /**
  * Tests language config override.
  */
-class ConfigLanguageOverride extends DrupalUnitTestBase {
+class ConfigLanguageOverrideTest extends DrupalUnitTestBase {
 
   /**
    * Modules to enable.
    *
    * @var array
    */
-  public static $modules = array('config_test', 'user', 'language', 'system', 'field');
+  public static $modules = array('user', 'language', 'config_test',  'system', 'field');
 
   public static function getInfo() {
     return array(
@@ -39,16 +39,10 @@ public function setUp() {
    * Tests locale override based on language.
    */
   function testConfigLanguageOverride() {
-    // The default configuration factory does not have the default language
-    // injected unless the Language module is enabled.
-    $config = \Drupal::config('config_test.system');
-    $this->assertIdentical($config->get('foo'), 'bar');
-
-    // \Drupal\language\LanguageServiceProvider::alter() calls
-    // \Drupal\Core\Config\ConfigFactory::setLanguageFromDefault() to set the
-    // language when the Language module is enabled. This test ensures that
+    // The language module implements a config factory override object that
+    // overrides configuration when the Language module is enabled. This test ensures that
     // English overrides work.
-    \Drupal::configFactory()->setLanguageFromDefault(\Drupal::service('language.default'));
+    \Drupal::languageManager()->setConfigOverrideLanguage(language_load('en'));
     $config = \Drupal::config('config_test.system');
     $this->assertIdentical($config->get('foo'), 'en bar');
 
@@ -65,20 +59,21 @@ function testConfigLanguageOverride() {
       'id' => 'de',
     )));
 
-    \Drupal::configFactory()->setLanguage(language_load('fr'));
+    \Drupal::languageManager()->setConfigOverrideLanguage(language_load('fr'));
     $config = \Drupal::config('config_test.system');
     $this->assertIdentical($config->get('foo'), 'fr bar');
 
-    \Drupal::configFactory()->setLanguage(language_load('de'));
+    \Drupal::languageManager()->setConfigOverrideLanguage(language_load('de'));
     $config = \Drupal::config('config_test.system');
     $this->assertIdentical($config->get('foo'), 'de bar');
 
     // Test overrides of completely new configuration objects. In normal runtime
     // this should only happen for configuration entities as we should not be
     // creating simple configuration objects on the fly.
-    $language_config_name = \Drupal::configFactory()->getLanguageConfigName('de', 'config_test.new');
-    \Drupal::config($language_config_name)->set('language', 'override')->save();
-    \Drupal::config('config_test.new');
+    \Drupal::languageManager()
+      ->getLanguageConfigOverride('de', 'config_test.new')
+      ->set('language', 'override')
+      ->save();
     $config = \Drupal::config('config_test.new');
     $this->assertTrue($config->isNew(), 'The configuration object config_test.new is new');
     $this->assertIdentical($config->get('language'), 'override');
@@ -87,11 +82,6 @@ function testConfigLanguageOverride() {
     $config = \Drupal::config('config_test.new');
     $this->assertIdentical($config->get('language'), NULL);
     \Drupal::configFactory()->setOverrideState($old_state);
-
-    // Ensure that language configuration overrides can not be overridden.
-    global $conf;
-    $conf[$language_config_name]['language'] = 'conf cannot override';
-    $this->assertIdentical(\Drupal::config($language_config_name)->get('language'), 'override');
   }
 }
 
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigLanguageOverrideWebTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigLanguageOverrideWebTest.php
index 3ffb54f..34f5172 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigLanguageOverrideWebTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigLanguageOverrideWebTest.php
@@ -45,10 +45,10 @@ function testSiteNameTranslation() {
       'direction' => '0',
     );
     $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language'));
-
-    // Save an override for the XX language.
-    $config_name = \Drupal::configFactory()->getLanguageConfigName('xx', 'system.site');
-    \Drupal::config($config_name)->set('name', 'XX site name')->save();
+    \Drupal::languageManager()
+      ->getLanguageConfigOverride($langcode, 'system.site')
+      ->set('name', 'XX site name')
+      ->save();
 
     $this->drupalLogout();
 
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigOverridesPriorityTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigOverridesPriorityTest.php
index 49482f9..6deab96 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigOverridesPriorityTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigOverridesPriorityTest.php
@@ -15,7 +15,7 @@
  */
 class ConfigOverridesPriorityTest extends DrupalUnitTestBase {
 
-  public static $modules = array('system', 'config', 'config_override');
+  public static $modules = array('system', 'config', 'config_override', 'language');
 
   public static function getInfo() {
     return array(
@@ -59,10 +59,9 @@ public function testOverridePriorities() {
       'name' => 'French',
       'id' => 'fr',
     ));
-    $config_factory->setLanguage($language);
-    $language_config_name = $config_factory->getLanguageConfigName($language->id, 'system.site');
-    $config_factory
-      ->get($language_config_name)
+    \Drupal::languageManager()->setConfigOverrideLanguage($language);
+    \Drupal::languageManager()
+      ->getLanguageConfigOverride($language->id, 'system.site')
       ->set('name', $language_overridden_name)
       ->set('mail', $language_overridden_mail)
       ->save();
diff --git a/core/modules/config/lib/Drupal/config/Tests/ConfigSchemaTest.php b/core/modules/config/lib/Drupal/config/Tests/ConfigSchemaTest.php
index e9d7562..79c8fc5 100644
--- a/core/modules/config/lib/Drupal/config/Tests/ConfigSchemaTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/ConfigSchemaTest.php
@@ -21,7 +21,7 @@ class ConfigSchemaTest extends DrupalUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('system', 'locale', 'field', 'image', 'config_test');
+  public static $modules = array('system', 'language', ',locale', 'field', 'image', 'config_test');
 
   public static function getInfo() {
     return array(
diff --git a/core/modules/config/lib/Drupal/config/Tests/DefaultConfigTest.php b/core/modules/config/lib/Drupal/config/Tests/DefaultConfigTest.php
index b7424f3..8c0f9cd 100644
--- a/core/modules/config/lib/Drupal/config/Tests/DefaultConfigTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/DefaultConfigTest.php
@@ -79,9 +79,8 @@ public function testDefaultConfig() {
     $default_config_storage = new TestInstallStorage();
 
     foreach ($default_config_storage->listAll() as $config_name) {
-      // @todo: remove once migration (https://drupal.org/node/2183957) and
-      // translation (https://drupal.org/node/2168609) schemas are in.
-      if (strpos($config_name, 'migrate.migration') === 0 || strpos($config_name, 'language.config') === 0) {
+      // @todo: remove once migration (https://drupal.org/node/2183957).
+      if (strpos($config_name, 'migrate.migration') === 0) {
         continue;
       }
 
diff --git a/core/modules/config/tests/config_override/config_override.services.yml b/core/modules/config/tests/config_override/config_override.services.yml
index 586ce49..5272a84 100644
--- a/core/modules/config/tests/config_override/config_override.services.yml
+++ b/core/modules/config/tests/config_override/config_override.services.yml
@@ -1,9 +1,9 @@
 services:
-  config_override_config_subscriber:
-    class: Drupal\config_override\EventSubscriber\ConfigModuleOverrideSubscriber
+  config_override.overrider:
+    class: Drupal\config_override\ConfigOverrider
     tags:
-      - { name: event_subscriber }
-  config_override_low_priority_config_subscriber:
-    class: Drupal\config_override\EventSubscriber\ConfigModuleLowPriorityOverrideSubscriber
+      - { name: config.factory.override}
+  config_override.low_priority_overrider:
+    class: Drupal\config_override\ConfigOverriderLowPriority
     tags:
-      - { name: event_subscriber }
+      - { name: config.factory.override, priority: -100 }
diff --git a/core/modules/config/tests/config_override/lib/Drupal/config_override/ConfigOverrider.php b/core/modules/config/tests/config_override/lib/Drupal/config_override/ConfigOverrider.php
new file mode 100644
index 0000000..d82ed2e
--- /dev/null
+++ b/core/modules/config/tests/config_override/lib/Drupal/config_override/ConfigOverrider.php
@@ -0,0 +1,56 @@
+<?Php
+
+/**
+ * @file
+ * Contains \Drupal\config_override\EventSubscriber\ConfigModuleOverrideSubscriber.
+ */
+
+namespace Drupal\config_override;
+
+use Drupal\Core\Config\ConfigEvents;
+use Drupal\Core\Config\ConfigFactoryOverrideInterface;
+use Drupal\Core\Config\ConfigModuleOverridesEvent;
+use Symfony\Component\EventDispatcher\EventSubscriberInterface;
+
+/**
+ * Tests module overrides for configuration.
+ */
+class ConfigOverrider implements ConfigFactoryOverrideInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function loadOverrides($names) {
+    $overrides = array();
+    if (!empty($GLOBALS['config_test_run_module_overrides'])) {
+      if (in_array('system.site', $names)) {
+        $overrides = $overrides + array('system.site' => array('name' => 'ZOMG overridden site name'));
+      }
+      if (in_array('config_override.new', $names)) {
+        $overrides = $overrides + array('config_override.new' => array('module' => 'override'));
+      }
+    }
+    return $overrides;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheSuffix() {
+    return 'ConfigOverrider';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function install($type, $name) {
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function uninstall($type, $name) {
+  }
+
+}
+
diff --git a/core/modules/config/tests/config_override/lib/Drupal/config_override/ConfigOverriderLowPriority.php b/core/modules/config/tests/config_override/lib/Drupal/config_override/ConfigOverriderLowPriority.php
new file mode 100644
index 0000000..53a30e7
--- /dev/null
+++ b/core/modules/config/tests/config_override/lib/Drupal/config_override/ConfigOverriderLowPriority.php
@@ -0,0 +1,57 @@
+<?Php
+
+/**
+ * @file
+ * Contains \Drupal\config_override\EventSubscriber\ConfigModuleLowPriorityOverrideSubscriber.
+ */
+
+namespace Drupal\config_override;
+
+use Drupal\Core\Config\ConfigFactoryOverrideInterface;
+
+/**
+ * Tests module overrides for configuration.
+ */
+class ConfigOverriderLowPriority implements ConfigFactoryOverrideInterface {
+
+  /**
+   * {@inheritdoc}
+   */
+  public function loadOverrides($names) {
+    $overrides = array();
+    if (!empty($GLOBALS['config_test_run_module_overrides'])) {
+      if (in_array('system.site', $names)) {
+        $overrides = array('system.site' =>
+          array(
+            // This override should apply because it is not overridden by the
+            // higher priority listener.
+            'name' => 'Should not apply because of higher priority listener',
+            'slogan' => 'Yay for overrides!',
+          )
+        );
+      }
+    }
+    return $overrides;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheSuffix() {
+    return 'ConfigOverriderLowPriority';
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function install($type, $name) {
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function uninstall($type, $name) {
+  }
+
+}
+
diff --git a/core/modules/config/tests/config_override/lib/Drupal/config_override/EventSubscriber/ConfigModuleLowPriorityOverrideSubscriber.php b/core/modules/config/tests/config_override/lib/Drupal/config_override/EventSubscriber/ConfigModuleLowPriorityOverrideSubscriber.php
deleted file mode 100644
index fd7e26c..0000000
--- a/core/modules/config/tests/config_override/lib/Drupal/config_override/EventSubscriber/ConfigModuleLowPriorityOverrideSubscriber.php
+++ /dev/null
@@ -1,44 +0,0 @@
-<?Php
-
-/**
- * @file
- * Contains \Drupal\config_override\EventSubscriber\ConfigModuleLowPriorityOverrideSubscriber.
- */
-
-namespace Drupal\config_override\EventSubscriber;
-
-use Drupal\Core\Config\ConfigEvents;
-use Drupal\Core\Config\ConfigModuleOverridesEvent;
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-
-/**
- * Tests module overrides for configuration.
- */
-class ConfigModuleLowPriorityOverrideSubscriber implements EventSubscriberInterface {
-
-  public function onConfigModuleOverride(ConfigModuleOverridesEvent $event) {
-    if (!empty($GLOBALS['config_test_run_module_overrides'])) {
-      $names = $event->getNames();
-      if (in_array('system.site', $names)) {
-        $event->setOverride('system.site', array(
-          'name' => 'Should not apply because of higher priority listener',
-          // This override should apply because it is not overridden by the
-          // higher priority listener.
-          'slogan' => 'Yay for overrides!',
-        ));
-      }
-    }
-  }
-
-  /**
-   * Registers the methods in this class that should be listeners.
-   *
-   * @return array
-   *   An array of event listener definitions.
-   */
-  static function getSubscribedEvents() {
-    $events[ConfigEvents::MODULE_OVERRIDES][] = array('onConfigModuleOverride', 35);
-    return $events;
-  }
-}
-
diff --git a/core/modules/config/tests/config_override/lib/Drupal/config_override/EventSubscriber/ConfigModuleOverrideSubscriber.php b/core/modules/config/tests/config_override/lib/Drupal/config_override/EventSubscriber/ConfigModuleOverrideSubscriber.php
deleted file mode 100644
index e168111..0000000
--- a/core/modules/config/tests/config_override/lib/Drupal/config_override/EventSubscriber/ConfigModuleOverrideSubscriber.php
+++ /dev/null
@@ -1,42 +0,0 @@
-<?Php
-
-/**
- * @file
- * Contains \Drupal\config_override\EventSubscriber\ConfigModuleOverrideSubscriber.
- */
-
-namespace Drupal\config_override\EventSubscriber;
-
-use Drupal\Core\Config\ConfigEvents;
-use Drupal\Core\Config\ConfigModuleOverridesEvent;
-use Symfony\Component\EventDispatcher\EventSubscriberInterface;
-
-/**
- * Tests module overrides for configuration.
- */
-class ConfigModuleOverrideSubscriber implements EventSubscriberInterface {
-
-  public function onConfigModuleOverride(ConfigModuleOverridesEvent $event) {
-    if (!empty($GLOBALS['config_test_run_module_overrides'])) {
-      $names = $event->getNames();
-      if (in_array('system.site', $names)) {
-        $event->setOverride('system.site', array('name' => 'ZOMG overridden site name'));
-      }
-      if (in_array('config_override.new', $names)) {
-        $event->setOverride('config_override.new', array('module' => 'override'));
-      }
-    }
-  }
-
-  /**
-   * Registers the methods in this class that should be listeners.
-   *
-   * @return array
-   *   An array of event listener definitions.
-   */
-  static function getSubscribedEvents() {
-    $events[ConfigEvents::MODULE_OVERRIDES][] = array('onConfigModuleOverride', 40);
-    return $events;
-  }
-}
-
diff --git a/core/modules/config/tests/config_test/config/language.config.de.config_test.system.yml b/core/modules/config/tests/config_test/config/language/de/config_test.system.yml
similarity index 100%
rename from core/modules/config/tests/config_test/config/language.config.de.config_test.system.yml
rename to core/modules/config/tests/config_test/config/language/de/config_test.system.yml
diff --git a/core/modules/config/tests/config_test/config/language.config.en.config_test.system.yml b/core/modules/config/tests/config_test/config/language/en/config_test.system.yml
similarity index 100%
rename from core/modules/config/tests/config_test/config/language.config.en.config_test.system.yml
rename to core/modules/config/tests/config_test/config/language/en/config_test.system.yml
diff --git a/core/modules/config/tests/config_test/config/language.config.fr.config_test.system.yml b/core/modules/config/tests/config_test/config/language/fr/config_test.system.yml
similarity index 100%
rename from core/modules/config/tests/config_test/config/language.config.fr.config_test.system.yml
rename to core/modules/config/tests/config_test/config/language/fr/config_test.system.yml
diff --git a/core/modules/config_translation/lib/Drupal/config_translation/Form/ConfigTranslationDeleteForm.php b/core/modules/config_translation/lib/Drupal/config_translation/Form/ConfigTranslationDeleteForm.php
index 319e168..fe79059 100644
--- a/core/modules/config_translation/lib/Drupal/config_translation/Form/ConfigTranslationDeleteForm.php
+++ b/core/modules/config_translation/lib/Drupal/config_translation/Form/ConfigTranslationDeleteForm.php
@@ -9,10 +9,9 @@
 
 use Drupal\config_translation\ConfigMapperManagerInterface;
 use Drupal\Core\Cache\Cache;
-use Drupal\Core\Config\StorageInterface;
-use Drupal\Core\Config\ConfigFactoryInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Form\ConfirmFormBase;
+use Drupal\language\ConfigurableLanguageManagerInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;
@@ -23,11 +22,11 @@
 class ConfigTranslationDeleteForm extends ConfirmFormBase {
 
   /**
-   * The configuration storage.
+   * The language manager.
    *
-   * @var \Drupal\Core\Config\StorageInterface $config_storage
+   * @var \Drupal\language\ConfigurableLanguageManagerInterface
    */
-  protected $configStorage;
+  protected $languageManager;
 
   /**
    * The configuration mapper manager.
@@ -60,20 +59,17 @@ class ConfigTranslationDeleteForm extends ConfirmFormBase {
   /**
    * Constructs a ConfigTranslationDeleteForm.
    *
-   * @param \Drupal\Core\Config\StorageInterface $config_storage
-   *   The configuration storage.
+   * @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager
+   *   The language override configuration storage.
    * @param \Drupal\config_translation\ConfigMapperManagerInterface $config_mapper_manager
    *   The configuration mapper manager.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    *   The module handler.
-   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
-   *   The configuration factory.
    */
-  public function __construct(StorageInterface $config_storage, ConfigMapperManagerInterface $config_mapper_manager, ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory) {
-    $this->configStorage = $config_storage;
+  public function __construct(ConfigurableLanguageManagerInterface $language_manager, ConfigMapperManagerInterface $config_mapper_manager, ModuleHandlerInterface $module_handler) {
+    $this->languageManager = $language_manager;
     $this->configMapperManager = $config_mapper_manager;
     $this->moduleHandler = $module_handler;
-    $this->configFactory = $config_factory;
   }
 
   /**
@@ -81,10 +77,9 @@ public function __construct(StorageInterface $config_storage, ConfigMapperManage
    */
   public static function create(ContainerInterface $container) {
     return new static(
-      $container->get('config.storage'),
+      $container->get('language_manager'),
       $container->get('plugin.manager.config_translation.mapper'),
-      $container->get('module_handler'),
-      $container->get('config.factory')
+      $container->get('module_handler')
     );
   }
 
@@ -142,8 +137,7 @@ public function buildForm(array $form, array &$form_state, Request $request = NU
    */
   public function submitForm(array &$form, array &$form_state) {
     foreach ($this->mapper->getConfigNames() as $name) {
-      $config_name = $this->configFactory->getLanguageConfigName($this->language->id, $name);
-      $this->configStorage->delete($config_name);
+      $this->languageManager->getLanguageConfigOverride($this->language->id, $name)->delete();
     }
 
     // Flush all persistent caches.
diff --git a/core/modules/config_translation/lib/Drupal/config_translation/Form/ConfigTranslationFormBase.php b/core/modules/config_translation/lib/Drupal/config_translation/Form/ConfigTranslationFormBase.php
index 2dc0726..ebbf17f 100644
--- a/core/modules/config_translation/lib/Drupal/config_translation/Form/ConfigTranslationFormBase.php
+++ b/core/modules/config_translation/lib/Drupal/config_translation/Form/ConfigTranslationFormBase.php
@@ -16,6 +16,8 @@
 use Drupal\Core\Form\BaseFormIdInterface;
 use Drupal\Core\Form\FormBase;
 use Drupal\Core\Language\Language;
+use Drupal\language\Config\LanguageConfigOverride;
+use Drupal\language\ConfigurableLanguageManagerInterface;
 use Drupal\locale\StringStorageInterface;
 use Symfony\Component\DependencyInjection\ContainerInterface;
 use Symfony\Component\HttpFoundation\Request;
@@ -62,6 +64,13 @@
   protected $mapper;
 
   /**
+   * The lanugage manager.
+   *
+   * @var \Drupal\language\ConfigurableLanguageManagerInterface
+   */
+  protected $languageManager;
+
+  /**
    * The language of the configuration translation.
    *
    * @var \Drupal\Core\Language\Language
@@ -96,12 +105,13 @@
    * @param \Drupal\Core\Config\ConfigFactoryInterface
    *   The config factory.
    */
-  public function __construct(TypedConfigManager $typed_config_manager, ConfigMapperManagerInterface $config_mapper_manager, StringStorageInterface $locale_storage, ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory) {
+  public function __construct(TypedConfigManager $typed_config_manager, ConfigMapperManagerInterface $config_mapper_manager, StringStorageInterface $locale_storage, ModuleHandlerInterface $module_handler, ConfigFactoryInterface $config_factory, ConfigurableLanguageManagerInterface $language_manager) {
     $this->typedConfigManager = $typed_config_manager;
     $this->configMapperManager = $config_mapper_manager;
     $this->localeStorage = $locale_storage;
     $this->moduleHandler = $module_handler;
     $this->configFactory = $config_factory;
+    $this->languageManager = $language_manager;
   }
 
   /**
@@ -113,7 +123,8 @@ public static function create(ContainerInterface $container) {
       $container->get('plugin.manager.config_translation.mapper'),
       $container->get('locale.storage'),
       $container->get('module_handler'),
-      $container->get('config.factory')
+      $container->get('config.factory'),
+      $container->get('language_manager')
     );
   }
 
@@ -172,8 +183,8 @@ public function buildForm(array $form, array &$form_state, Request $request = NU
     $this->configFactory->setOverrideState($old_state);
 
     // Set the translation target language on the configuration factory.
-    $original_language = $this->configFactory->getLanguage();
-    $this->configFactory->setLanguage($this->language);
+    $original_language = $this->languageManager->getConfigOverrideLanguage();
+    $this->languageManager->setConfigOverrideLanguage($this->language);
 
     // Add some information to the form state for easier form altering.
     $form_state['config_translation_mapper'] = $this->mapper;
@@ -199,7 +210,7 @@ public function buildForm(array $form, array &$form_state, Request $request = NU
     );
 
     // Set the configuration language back.
-    $this->configFactory->setLanguage($original_language);
+    $this->languageManager->setConfigOverrideLanguage($original_language);
 
     return $form;
   }
@@ -217,19 +228,18 @@ public function submitForm(array &$form, array &$form_state) {
     foreach ($this->mapper->getConfigNames() as $name) {
       // Set configuration values based on form submission and source values.
       $base_config = $this->config($name);
-      $translation_config_name = $this->configFactory->getLanguageConfigName($this->language->id, $name);
-      $translation_config = $this->config($translation_config_name);
+      $config_translation = $this->languageManager->getLanguageConfigOverride($this->language->id, $name);
       $locations = $this->localeStorage->getLocations(array('type' => 'configuration', 'name' => $name));
 
-      $this->setConfig($this->language, $base_config, $translation_config, $form_values[$name], !empty($locations));
+      $this->setConfig($this->language, $base_config, $config_translation, $form_values[$name], !empty($locations));
 
       // If no overrides, delete language specific configuration file.
-      $saved_config = $translation_config->get();
+      $saved_config = $config_translation->get();
       if (empty($saved_config)) {
-        $translation_config->delete();
+        $config_translation->delete();
       }
       else {
-        $translation_config->save();
+        $config_translation->save();
       }
     }
     $this->configFactory->setOverrideState($old_state);
@@ -345,9 +355,8 @@ protected function buildConfigForm(Element $schema, $config_data, $base_config_d
    *   Set the configuration in this language.
    * @param \Drupal\Core\Config\Config $base_config
    *   Base configuration values, in the source language.
-   * @param \Drupal\Core\Config\Config $translation_config
-   *   Translation configuration instance. Values from $config_values will be
-   *   set in this instance.
+   * @param \Drupal\language\Config\LanguageConfigOverride $config_translation
+   *   Translation configuration override data.
    * @param array $config_values
    *   A simple one dimensional or recursive array:
    *     - simple:
@@ -362,12 +371,15 @@ protected function buildConfigForm(Element $schema, $config_data, $base_config_d
    * @param bool $shipped_config
    *   (optional) Flag to specify whether the configuration had a shipped
    *   version and therefore should also be stored in the locale database.
+   *
+   * @return array
+   *   Translation configuration override data.
    */
-  protected function setConfig(Language $language, Config $base_config, Config $translation_config, array $config_values, $shipped_config = FALSE) {
+  protected function setConfig(Language $language, Config $base_config, LanguageConfigOverride $config_translation, array $config_values, $shipped_config = FALSE) {
     foreach ($config_values as $key => $value) {
       if (is_array($value) && !isset($value['translation'])) {
         // Traverse into this level in the configuration.
-        $this->setConfig($language, $base_config, $translation_config, $value, $shipped_config);
+        $this->setConfig($language, $base_config, $config_translation, $value, $shipped_config);
       }
       else {
 
@@ -397,10 +409,10 @@ protected function setConfig(Language $language, Config $base_config, Config $tr
         // Save value, if different from the source value in the base
         // configuration. If same as original configuration, remove override.
         if ($base_config->get($key) !== $value['translation']) {
-          $translation_config->set($key, $value['translation']);
+          $config_translation->set($key, $value['translation']);
         }
         else {
-          $translation_config->clear($key);
+          $config_translation->clear($key);
         }
       }
     }
diff --git a/core/modules/config_translation/lib/Drupal/config_translation/Tests/ConfigTranslationUiTest.php b/core/modules/config_translation/lib/Drupal/config_translation/Tests/ConfigTranslationUiTest.php
index ed4b1e2..405aaf5 100644
--- a/core/modules/config_translation/lib/Drupal/config_translation/Tests/ConfigTranslationUiTest.php
+++ b/core/modules/config_translation/lib/Drupal/config_translation/Tests/ConfigTranslationUiTest.php
@@ -177,16 +177,14 @@ public function testSourceValueDuplicateSave() {
     $this->drupalPostForm("$translation_base_url/fr/add", $edit, t('Save translation'));
 
     // Read overridden file from active config.
-    $file_storage = new FileStorage($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]);
-    $language_config_name = \Drupal::configFactory()->getLanguageConfigName('fr', 'system.site');
-    $config_parsed = $file_storage->read($language_config_name);
+    $override = \Drupal::languageManager()->getLanguageConfigOverride('fr', 'system.site');
 
     // Expect both name and slogan in language specific file.
     $expected = array(
       'name' => 'FR ' . $site_name,
       'slogan' => 'FR ' . $site_slogan,
     );
-    $this->assertEqual($expected, $config_parsed);
+    $this->assertEqual($expected, $override->get());
 
     // Case 2: Update new value for site slogan and default value for site name.
     $this->drupalGet("$translation_base_url/fr/edit");
@@ -200,11 +198,11 @@ public function testSourceValueDuplicateSave() {
     );
     $this->drupalPostForm(NULL, $edit, t('Save translation'));
     $this->assertRaw(t('Successfully updated @language translation.', array('@language' => 'French')));
-    $config_parsed = $file_storage->read($language_config_name);
+    $override = \Drupal::languageManager()->getLanguageConfigOverride('fr', 'system.site');
 
     // Expect only slogan in language specific file.
-    $expected = array('slogan' => 'FR ' . $site_slogan);
-    $this->assertEqual($expected, $config_parsed);
+    $expected = 'FR ' . $site_slogan;
+    $this->assertEqual($expected, $override->get('slogan'));
 
     // Case 3: Keep default value for site name and slogan.
     $this->drupalGet("$translation_base_url/fr/edit");
@@ -214,10 +212,10 @@ public function testSourceValueDuplicateSave() {
       'config_names[system.site][slogan][translation]' => $site_slogan,
     );
     $this->drupalPostForm(NULL, $edit, t('Save translation'));
-    $config_parsed = $file_storage->read($language_config_name);
+    $override = \Drupal::languageManager()->getLanguageConfigOverride('fr', 'system.site');
 
     // Expect no language specific file.
-    $this->assertFalse($config_parsed);
+    $this->assertTrue($override->isNew());
 
     // Check configuration page with translator user. Should have no access.
     $this->drupalLogout();
@@ -240,8 +238,6 @@ public function testSourceValueDuplicateSave() {
   public function testContactConfigEntityTranslation() {
     $this->drupalLogin($this->admin_user);
 
-    $file_storage = new FileStorage($this->configDirectories[CONFIG_ACTIVE_DIRECTORY]);
-
     $this->drupalGet('admin/structure/contact');
 
     // Check for default contact form configuration entity from Contact module.
@@ -285,13 +281,12 @@ public function testContactConfigEntityTranslation() {
       $this->drupalPostForm($translation_page_url, $edit, t('Save translation'));
 
       // Expect translated values in language specific file.
-      $language_config_name = \Drupal::configFactory()->getLanguageConfigName($langcode, 'contact.category.feedback');
-      $config_parsed = $file_storage->read($language_config_name);
+      $override = \Drupal::languageManager()->getLanguageConfigOverride($langcode, 'contact.category.feedback');
       $expected = array(
         'label' => 'Website feedback - ' . $langcode,
         'reply' => 'Thank you for your mail - ' . $langcode,
       );
-      $this->assertEqual($expected, $config_parsed);
+      $this->assertEqual($expected, $override->get());
 
       // Check for edit, delete links (and no 'add' link) for $langcode.
       $this->assertNoLinkByHref("$translation_base_url/$langcode/add");
@@ -349,9 +344,8 @@ public function testContactConfigEntityTranslation() {
       $this->assertNoLinkByHref("$translation_base_url/$langcode/delete");
 
       // Expect no language specific file present anymore.
-      $language_config_name = \Drupal::configFactory()->getLanguageConfigName($langcode, 'contact.category.feedback');
-      $config_parsed = $file_storage->read($language_config_name);
-      $this->assertFalse($config_parsed);
+      $override = \Drupal::languageManager()->getLanguageConfigOverride($langcode, 'contact.category.feedback');
+      $this->assertTrue($override->isNew());
     }
 
     // Check configuration page with translator user. Should have no access.
@@ -417,13 +411,12 @@ public function testDateFormatTranslation() {
       $this->drupalPostForm($translation_page_url, $edit, t('Save translation'));
 
       // Get translation and check we've got the right value.
-      $language_config_name = \Drupal::configFactory()->getLanguageConfigName('fr', 'system.date_format.' . $id);
-      $config_parsed = $file_storage->read($language_config_name);
+      $override = \Drupal::languageManager()->getLanguageConfigOverride('fr', 'system.date_format.' . $id);
       $expected = array(
         'label' => $id . ' - FR',
         'pattern' => array('php' => 'D'),
       );
-      $this->assertEqual($expected, $config_parsed);
+      $this->assertEqual($expected, $override->get());
 
       // Formatting the date 8 / 27 / 1985 @ 13:37 EST with pattern D should
       // display "Tue".
diff --git a/core/modules/language/language.install b/core/modules/language/language.install
new file mode 100644
index 0000000..5f1e6a7
--- /dev/null
+++ b/core/modules/language/language.install
@@ -0,0 +1,27 @@
+<?php
+
+/**
+ * @file
+ * Install, update and uninstall functions for the language module.
+ */
+
+/**
+ * Implements hook_install().
+ */
+function language_install() {
+  // Get all the existing modules that are enabled and install any language
+  // configuration.
+  $language_config_override = \Drupal::service('language.config_factory_override');
+  foreach (array_keys(\Drupal::moduleHandler()->getModuleList()) as $module) {
+    $language_config_override->install('module', $module);
+  }
+  /** @var \Drupal\Core\Extension\ThemeHandler $theme_handler */
+  $theme_handler = \Drupal::service('theme_handler');
+  foreach ($theme_handler->listInfo() as $theme) {
+    if ($theme->status) {
+      $language_config_override->install('theme', $theme->name);
+    }
+  }
+  \Drupal::configFactory()->reset();
+}
+
diff --git a/core/modules/language/language.services.yml b/core/modules/language/language.services.yml
index 422644a..13141a6 100644
--- a/core/modules/language/language.services.yml
+++ b/core/modules/language/language.services.yml
@@ -11,3 +11,22 @@ services:
     class: Drupal\language\EventSubscriber\ConfigSubscriber
     tags:
       - { name: event_subscriber }
+  language.cache_config:
+    class: Drupal\Core\Cache\CacheBackendInterface
+    tags:
+      - { name: cache.bin }
+      - { name: persist }
+    factory_method: get
+    factory_service: cache_factory
+    arguments: [language_config]
+  language.config_storage:
+    class: Drupal\language\Config\LanguageOverrideStorage
+    arguments: ['@language.cache_config']
+    tags:
+      - { name: persist }
+  language.config_factory_override:
+    class: Drupal\language\Config\LanguageConfigFactoryOverride
+    arguments: ['@language.default', '@language.config_storage', '@config.typed']
+    tags:
+      - { name: config.factory.override, priority: -254 }
+      - { name: persist }
diff --git a/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverride.php b/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverride.php
new file mode 100644
index 0000000..7eaf42a
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverride.php
@@ -0,0 +1,131 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\Config\LanguageConfigFactoryOverride.
+ */
+
+namespace Drupal\language\Config;
+
+use Drupal\Core\Config\FileStorage;
+use Drupal\Core\Config\Config;
+use Drupal\Core\Config\TypedConfigManagerInterface;
+use Drupal\Core\Language\Language;
+use Drupal\Core\Language\LanguageDefault;
+
+class LanguageConfigFactoryOverride implements LanguageConfigFactoryOverrideInterface {
+
+  /**
+   * The language configuration storage.
+   *
+   * @var \Drupal\language\Config\LanguageOverrideStorage
+   */
+  protected $storage;
+
+  /**
+   * The language object used to override configuration data.
+   *
+   * @var \Drupal\Core\Language\Language
+   */
+  protected $language;
+
+  /**
+   * Constructs the LanguageConfigFactoryOverride object.
+   *
+   * @param \Drupal\Core\Language\LanguageDefault $language_default
+   *   The default language service. This sets the initial language on the
+   *   config factory to the site's default. The language can be used to
+   *   override configuration data if language overrides are available.
+   * @param \Drupal\language\Config\LanguageOverrideStorageInterface $storage
+   *   The language configuration storage.
+   */
+  public function __construct(LanguageDefault $language_default, LanguageOverrideStorageInterface $storage, TypedConfigManagerInterface $typed_config) {
+    // Ensure that configuration can be localised if the site is monolingual
+    // but the Language module is enabled. This is the case for monolingual
+    // sites not in English.
+    $this->language = $language_default->get();
+    $this->storage = $storage->setLangcode($this->language->id);
+    $this->typedConfig = $typed_config;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function loadOverrides($names) {
+    return $this->storage->readMultiple($names);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getCacheSuffix() {
+    return $this->language->id;
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * @todo maybe this should be done somewhere else?
+   */
+  public function install($type, $name) {
+    // Work out if this extension provides default language overrides.
+    $config_dir = drupal_get_path($type, $name) . '/config/language';
+    if (is_dir($config_dir)) {
+      // List all the directories.
+      // \DirectoryIterator on Windows requires an absolute path.
+      $it  = new \DirectoryIterator(realpath($config_dir));
+      foreach ($it as $dir) {
+        if (!$dir->isDot() && $dir->isDir() ) {
+          $default_language_config = new FileStorage($dir->getPathname());
+          $this->storage->setLangcode($dir->getFilename());
+          foreach ($default_language_config->listAll() as $config_name) {
+            $data = $default_language_config->read($config_name);
+            $config = new LanguageConfigOverride($config_name, $this->storage, $this->typedConfig);
+            $config->setData($data)->save();
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   *
+   * @todo maybe this should be done somewhere else?
+   */
+  public function uninstall($type, $name) {
+    // Can not use ConfigurableLanguageManager::getLanguages() since that would
+    // create a circular dependency.
+    $language_directory = config_get_config_directory() .'/language';
+    if (is_dir(($language_directory))) {
+      $it  = new \DirectoryIterator(realpath($language_directory));
+      foreach ($it as $dir) {
+        if (!$dir->isDot() && $dir->isDir() ) {
+          $this->storage->setLangcode($dir->getFilename());
+          $config_names = $this->storage->listAll($name . '.');
+          foreach ($config_names as $config_name) {
+            $config = new LanguageConfigOverride($config_name, $this->storage, $this->typedConfig);
+            $config->delete();
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLanguage() {
+    return $this->language;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setLanguage(Language $language = NULL) {
+    $this->language = $language;
+    $this->storage->setLangcode($this->language->id);
+    return $this;
+  }
+
+}
diff --git a/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverrideInterface.php b/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverrideInterface.php
new file mode 100644
index 0000000..5eadad3
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverrideInterface.php
@@ -0,0 +1,34 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\Config\LanguageConfigFactoryOverrideInterface.
+ */
+
+namespace Drupal\language\Config;
+
+use Drupal\Core\Config\ConfigFactoryOverrideInterface;
+use Drupal\Core\Language\Language;
+
+interface LanguageConfigFactoryOverrideInterface extends ConfigFactoryOverrideInterface {
+
+  /**
+   * Gets the language object used to override configuration data.
+   *
+   * @return \Drupal\Core\Language\Language
+   *   The language object used to override configuration data.
+   */
+  public function getLanguage();
+
+  /**
+   * Sets the language to be used in configuration overrides.
+   *
+   * @param \Drupal\Core\Language\Language $language
+   *   The language object to be set on the config factory. Used to override
+   *   configuration by language.
+   *
+   * @return $this
+   */
+  public function setLanguage(Language $language = NULL);
+
+}
diff --git a/core/modules/language/lib/Drupal/language/Config/LanguageConfigOverride.php b/core/modules/language/lib/Drupal/language/Config/LanguageConfigOverride.php
new file mode 100644
index 0000000..bc2f2d2
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/Config/LanguageConfigOverride.php
@@ -0,0 +1,72 @@
+<?php
+
+/**
+ * @file
+ * Definition of Drupal\language\Config\LanguageConfigOverride.
+ */
+
+namespace Drupal\language\Config;
+
+use Drupal\Core\Config\StorableConfigBase;
+use Drupal\Core\Config\StorageInterface;
+use Drupal\Core\Config\TypedConfigManagerInterface;
+
+/**
+ * Defines the default configuration object.
+ */
+class LanguageConfigOverride extends StorableConfigBase {
+
+  /**
+   * Constructs a configuration object.
+   *
+   * @param string $name
+   *   The name of the configuration object being constructed.
+   * @param \Drupal\Core\Config\StorageInterface $storage
+   *   A storage controller object to use for reading and writing the
+   *   configuration data.
+   * @param \Drupal\Core\Config\TypedConfigManager $typed_config
+   *   The typed configuration manager service.
+   */
+  public function __construct($name, StorageInterface $storage, TypedConfigManagerInterface $typed_config) {
+    $this->name = $name;
+    $this->storage = $storage;
+    $this->typedConfigManager = $typed_config;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function save() {
+    // Validate the configuration object name before saving.
+    static::validateName($this->name);
+
+    // If there is a schema for this configuration object, cast all values to
+    // conform to the schema.
+    if ($this->typedConfigManager->hasConfigSchema($this->name)) {
+      // Ensure that the schema wrapper has the latest data.
+      $this->schemaWrapper = NULL;
+      foreach ($this->data as $key => $value) {
+        $this->data[$key] = $this->castValue($key, $value);
+      }
+    }
+
+    $this->storage->write($this->name, $this->data);
+    $this->isNew = FALSE;
+    $this->originalData = $this->data;
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function delete() {
+    // @todo Consider to remove the pruning of data for Config::delete().
+    $this->data = array();
+    $this->storage->delete($this->name);
+    $this->isNew = TRUE;
+    $this->originalData = $this->data;
+    return $this;
+  }
+
+}
+
diff --git a/core/modules/language/lib/Drupal/language/Config/LanguageOverrideStorage.php b/core/modules/language/lib/Drupal/language/Config/LanguageOverrideStorage.php
new file mode 100644
index 0000000..256191e
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/Config/LanguageOverrideStorage.php
@@ -0,0 +1,158 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\Config\LanguageOverrideStorage.
+ */
+
+namespace Drupal\language\Config;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Config\CachedStorage;
+use Drupal\Core\Config\FileStorage;
+
+class LanguageOverrideStorage extends CachedStorage implements LanguageOverrideStorageInterface {
+
+  /**
+   * The langcode that determines the configuration override storage directory.
+   *
+   * @var string
+   */
+  protected $langcode;
+
+  /**
+   * Tracks whether the directory exists to prevent excessive file system reads.
+   *
+   * @var bool
+   */
+  protected $directoryExists;
+
+  /**
+   * Constructs a new CachedStorage controller.
+   *
+   * @param \Drupal\Core\Config\StorageInterface $storage
+   *   A configuration storage controller to be cached.
+   * @param \Drupal\Core\Cache\CacheBackendInterface $cache
+   *   A cache backend instance to use for caching.
+   */
+  public function __construct(CacheBackendInterface $cache) {
+    $this->cache = $cache;
+  }
+
+  /**
+   * Gets the override configuration directory for a langcode.
+   *
+   * @param string $langcode
+   *   The language langcode to get the directory for.
+   *
+   * @return string
+   *   The directory where the language override configuration is stored.
+   */
+  protected function getDirectory($langcode) {
+    return config_get_config_directory(CONFIG_ACTIVE_DIRECTORY) .'/language/' . $langcode;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function write($name, array $data) {
+    $this->ensureDirectory();
+    return parent::write($name, $data);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function delete($name) {
+    if ($this->hasDirectory()) {
+      return parent::delete($name);
+    }
+    return FALSE;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function listAll($prefix = '') {
+    if ($this->hasDirectory()) {
+      return parent::listAll($prefix);
+    }
+    return array();
+  }
+
+  /**
+   * Creates a directory if necessary.
+   */
+  protected function ensureDirectory() {
+    if (!$this->hasDirectory()) {
+      drupal_mkdir($this->directory, NULL, TRUE);
+      $this->directoryExists = TRUE;
+    }
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setLangcode($langcode) {
+    // Reset directory check.
+    $this->directoryExists = NULL;
+    $this->langcode = $langcode;
+    $this->directory = $this->getDirectory($langcode);
+    if (empty($this->storage)) {
+      $this->storage = new FileStorage($this->directory);
+    }
+    else {
+      $this->storage->setDirectory($this->directory);
+    }
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getCacheKey($name) {
+    return $this->langcode . ':' . $name;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getCacheKeys(array $names) {
+    foreach ($names as $name) {
+      $names[$name] = $this->getCacheKey($name);
+    }
+    return $names;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getNameFromCacheKey($key) {
+    // Remove everything before the semicolon.
+    return substr($key, strpos($key, ':') + 1);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function getNamesFromCacheKeys($keys) {
+    foreach ($keys as $key) {
+      $keys[$key] = $this->getNameFromCacheKey($key);
+    }
+    return $keys;
+  }
+
+  /**
+   * Discovers is the directory for the langcode exists.
+   *
+   * @return bool
+   *   TRUE if the directory exists, FALSE if not.
+   */
+  protected function hasDirectory() {
+    if (!isset($this->directoryExists)) {
+      $this->directoryExists = is_dir($this->directory);
+    }
+    return $this->directoryExists;
+  }
+
+}
diff --git a/core/modules/language/lib/Drupal/language/Config/LanguageOverrideStorageInterface.php b/core/modules/language/lib/Drupal/language/Config/LanguageOverrideStorageInterface.php
new file mode 100644
index 0000000..5e31cc8
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/Config/LanguageOverrideStorageInterface.php
@@ -0,0 +1,26 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\Config\LanguageOverrideStorageInterface.
+ */
+
+namespace Drupal\language\Config;
+
+use Drupal\Core\Config\StorageCacheInterface;
+use Drupal\Core\Config\StorageInterface;
+
+interface LanguageOverrideStorageInterface extends StorageInterface, StorageCacheInterface {
+
+  /**
+   * Sets the langcode to determine the override configuration directory to use.
+   *
+   * @param string $langcode
+   *   The language langcode to get the directory for.
+   *
+   * @return $this
+   *   The language configuration storage.
+   */
+  public function setLangcode($langcode);
+
+}
diff --git a/core/modules/language/lib/Drupal/language/ConfigurableLanguageManager.php b/core/modules/language/lib/Drupal/language/ConfigurableLanguageManager.php
index a7fb8f6..2cf8459 100644
--- a/core/modules/language/lib/Drupal/language/ConfigurableLanguageManager.php
+++ b/core/modules/language/lib/Drupal/language/ConfigurableLanguageManager.php
@@ -9,10 +9,14 @@
 
 use Drupal\Component\PhpStorage\PhpStorageFactory;
 use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\Core\Config\TypedConfigManagerInterface;
 use Drupal\Core\Extension\ModuleHandlerInterface;
 use Drupal\Core\Language\Language;
 use Drupal\Core\Language\LanguageDefault;
 use Drupal\Core\Language\LanguageManager;
+use Drupal\language\Config\LanguageConfigFactoryOverrideInterface;
+use Drupal\language\Config\LanguageConfigOverride;
+use Drupal\language\Config\LanguageOverrideStorageInterface;
 use Symfony\Component\HttpFoundation\Request;
 
 /**
@@ -35,6 +39,27 @@ class ConfigurableLanguageManager extends LanguageManager implements Configurabl
   protected $moduleHandler;
 
   /**
+   * The language configuration override service.
+   *
+   * @var \Drupal\language\Config\LanguageConfigFactoryOverrideInterface
+   */
+  protected $configFactoryOverride;
+
+  /**
+   * The language configuration override storage service.
+   *
+   * @var \Drupal\language\Config\LanguageOverrideStorageInterface
+   */
+  protected $configOverrideStorage;
+
+  /**
+   * The typed config manager.
+   *
+   * @var \Drupal\Core\Config\TypedConfigManagerInterface
+   */
+  protected $typedConfig;
+
+  /**
    * The request object.
    *
    * @var \Symfony\Component\HttpFoundation\Request
@@ -93,15 +118,26 @@ public static function rebuildServices() {
   /**
    * Constructs a new ConfigurableLanguageManager object.
    *
+   * @param \Drupal\Core\Language\LanguageDefault $default_language
+   *   The default language service.
    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
-   *   The configuration storage service.
+   *   The configuration factory service.
    * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler
    *   The module handler service.
+   * @param \Drupal\language\Config\LanguageConfigFactoryOverrideInterface $config_override
+   *   The language configuration override service.
+   * @param \Drupal\language\Config\LanguageOverrideStorageInterface $config_storage
+   *   The language configuration storage service.
+   * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
+   *   The typed configuration manager.
    */
-  public function __construct(LanguageDefault $default_language, ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler) {
+  public function __construct(LanguageDefault $default_language, ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, LanguageConfigFactoryOverrideInterface $config_override, LanguageOverrideStorageInterface $config_storage, TypedConfigManagerInterface $typed_config) {
     $this->defaultLanguage = $default_language;
     $this->configFactory = $config_factory;
     $this->moduleHandler = $module_handler;
+    $this->configFactoryOverride = $config_override;
+    $this->configOverrideStorage = $config_storage;
+    $this->typedConfig = $typed_config;
   }
 
   /**
@@ -371,4 +407,40 @@ public function getLanguageSwitchLinks($type, $path) {
     return $links;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function setConfigOverrideLanguage(Language $language) {
+    $this->configFactoryOverride->setLanguage($language);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getConfigOverrideLanguage() {
+    return $this->configFactoryOverride->getLanguage();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLanguageConfigOverride($langcode, $name) {
+    $storage = $this->getLanguageOverrideStorage($langcode);
+    $override = new LanguageConfigOverride($name, $storage, $this->typedConfig);
+    if ($data = $storage->read($name)) {
+      $override->initWithData($data);
+    }
+    return $override;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getLanguageOverrideStorage($langcode) {
+    // Clone the language override storage so a process could compare language
+    // overrides if it wanted to.
+    return clone $this->configOverrideStorage->setLangcode($langcode);
+  }
+
 }
diff --git a/core/modules/language/lib/Drupal/language/ConfigurableLanguageManagerInterface.php b/core/modules/language/lib/Drupal/language/ConfigurableLanguageManagerInterface.php
index 02c8205..5e08205 100644
--- a/core/modules/language/lib/Drupal/language/ConfigurableLanguageManagerInterface.php
+++ b/core/modules/language/lib/Drupal/language/ConfigurableLanguageManagerInterface.php
@@ -78,4 +78,28 @@ public function saveLanguageTypesConfiguration(array $config);
    */
   public function updateLockedLanguageWeights();
 
+  /**
+   * Gets a language config override object.
+   *
+   * @param string $langcode
+   *   The langcode.
+   * @param string $name
+   *   The language configuration override object name.
+   *
+   * @return \Drupal\language\Config\LanguageConfigOverride
+   *   The language config override object.
+   */
+  public function getLanguageConfigOverride($langcode, $name);
+
+  /**
+   * Gets the language override configuration override storage for a langcode.
+   *
+   * @param string $langcode
+   *   The langcode.
+   *
+   * @return \Drupal\language\Config\LanguageOverrideStorage
+   *   The language override storage.
+   */
+  public function getLanguageOverrideStorage($langcode);
+
 }
diff --git a/core/modules/language/lib/Drupal/language/EventSubscriber/LanguageRequestSubscriber.php b/core/modules/language/lib/Drupal/language/EventSubscriber/LanguageRequestSubscriber.php
index 2d81206..7700611 100644
--- a/core/modules/language/lib/Drupal/language/EventSubscriber/LanguageRequestSubscriber.php
+++ b/core/modules/language/lib/Drupal/language/EventSubscriber/LanguageRequestSubscriber.php
@@ -7,8 +7,6 @@
 
 namespace Drupal\language\EventSubscriber;
 
-use Drupal\Core\Config\ConfigFactoryInterface;
-use Drupal\Core\Language\Language;
 use Drupal\Core\Session\AccountInterface;
 use Drupal\Core\StringTranslation\Translator\TranslatorInterface;
 use Drupal\language\ConfigurableLanguageManagerInterface;
@@ -52,13 +50,6 @@ class LanguageRequestSubscriber implements EventSubscriberInterface {
   protected $currentUser;
 
   /**
-   * The configuration factory.
-   *
-   * @var \Drupal\Core\Config\ConfigFactoryInterface
-   */
-  protected $configFactory;
-
-  /**
    * Constructs a LanguageRequestSubscriber object.
    *
    * @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager
@@ -69,15 +60,12 @@ class LanguageRequestSubscriber implements EventSubscriberInterface {
    *   The translation service.
    * @param \Drupal\Core\Session\AccountInterface $current_user
    *   The current active user.
-   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
-   *   The configuration factory.
    */
-  public function __construct(ConfigurableLanguageManagerInterface $language_manager, LanguageNegotiatorInterface $negotiator, TranslatorInterface $translation, AccountInterface $current_user, ConfigFactoryInterface $config_factory) {
+  public function __construct(ConfigurableLanguageManagerInterface $language_manager, LanguageNegotiatorInterface $negotiator, TranslatorInterface $translation, AccountInterface $current_user) {
     $this->languageManager = $language_manager;
     $this->negotiator = $negotiator;
     $this->translation = $translation;
     $this->currentUser = $current_user;
-    $this->configFactory = $config_factory;
   }
 
   /**
@@ -94,7 +82,7 @@ public function onKernelRequestLanguage(GetResponseEvent $event) {
       if ($this->languageManager instanceof ConfigurableLanguageManagerInterface) {
         $this->languageManager->setNegotiator($this->negotiator);
         $this->languageManager->setRequest($request);
-        $this->configFactory->setLanguage($this->languageManager->getCurrentLanguage());
+        $this->languageManager->setConfigOverrideLanguage($this->languageManager->getCurrentLanguage());
       }
       // After the language manager has initialized, set the default langcode
       // for the string translations.
diff --git a/core/modules/language/lib/Drupal/language/LanguageServiceProvider.php b/core/modules/language/lib/Drupal/language/LanguageServiceProvider.php
index 8c11eca..4b7aac4 100644
--- a/core/modules/language/lib/Drupal/language/LanguageServiceProvider.php
+++ b/core/modules/language/lib/Drupal/language/LanguageServiceProvider.php
@@ -31,8 +31,7 @@ public function register(ContainerBuilder $container) {
         ->addArgument(new Reference('language_manager'))
         ->addArgument(new Reference('language_negotiator'))
         ->addArgument(new Reference('string_translation'))
-        ->addArgument(new Reference('current_user'))
-        ->addArgument(new Reference('config.factory'));
+        ->addArgument(new Reference('current_user'));
 
       $container->register('path_processor_language', 'Drupal\language\HttpKernel\PathProcessorLanguage')
         ->addTag('path_processor_inbound', array('priority' => 300))
@@ -52,14 +51,12 @@ public function alter(ContainerBuilder $container) {
     $definition = $container->getDefinition('language_manager');
     $definition->setClass('Drupal\language\ConfigurableLanguageManager')
       ->addArgument(new Reference('config.factory'))
-      ->addArgument(new Reference('module_handler'));
+      ->addArgument(new Reference('module_handler'))
+      ->addArgument(new Reference('language.config_factory_override'))
+      ->addArgument(new Reference('language.config_storage'))
+      ->addArgument(new Reference('config.typed'));
     if ($default_language_values = $this->getDefaultLanguageValues()) {
       $container->setParameter('language.default_values', $default_language_values);
-      // Ensure that configuration can be localised if the site is monolingual
-      // but the Language module is enabled. This is the case for monolingual
-      // sites not in English.
-      $definition = $container->getDefinition('config.factory');
-      $definition->addMethodCall('setLanguageFromDefault', array(new Reference('language.default')));
     }
   }
 
diff --git a/core/modules/locale/lib/Drupal/locale/LocaleConfigManager.php b/core/modules/locale/lib/Drupal/locale/LocaleConfigManager.php
index 353fcc6..94bd547 100644
--- a/core/modules/locale/lib/Drupal/locale/LocaleConfigManager.php
+++ b/core/modules/locale/lib/Drupal/locale/LocaleConfigManager.php
@@ -12,6 +12,8 @@
 use Drupal\Core\Config\TypedConfigManager;
 use Drupal\Core\Config\StorageInterface;
 use Drupal\Core\Config\ConfigFactoryInterface;
+use Drupal\language\Config\LanguageOverrideStorageInterface;
+use Drupal\language\ConfigurableLanguageManagerInterface;
 
 /**
  * Manages localized configuration type plugins.
@@ -45,6 +47,13 @@ class LocaleConfigManager extends TypedConfigManager {
   protected $configFactory;
 
   /**
+   * The language manager.
+   *
+   * @var \Drupal\language\ConfigurableLanguageManagerInterface
+   */
+  protected $languageManager;
+
+  /**
    * Creates a new typed configuration manager.
    *
    * @param \Drupal\Core\Config\StorageInterface $configStorage
@@ -60,13 +69,16 @@ class LocaleConfigManager extends TypedConfigManager {
    *   The cache backend to use for caching the definitions.
    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
    *   The configuration factory
+   * @param \Drupal\language\ConfigurableLanguageManagerInterface
+   *   The language manager.
    */
-  public function __construct(StorageInterface $configStorage, StorageInterface $schemaStorage, StorageInterface $installStorage, StringStorageInterface $localeStorage, CacheBackendInterface $cache, ConfigFactoryInterface $config_factory) {
+  public function __construct(StorageInterface $configStorage, StorageInterface $schemaStorage, StorageInterface $installStorage, StringStorageInterface $localeStorage, CacheBackendInterface $cache, ConfigFactoryInterface $config_factory, ConfigurableLanguageManagerInterface $language_manager) {
     // Note we use the install storage for the parent constructor.
     parent::__construct($configStorage, $schemaStorage, $cache);
     $this->installStorage = $installStorage;
     $this->localeStorage = $localeStorage;
     $this->configFactory = $config_factory;
+    $this->languageManager = $language_manager;
   }
 
   /**
@@ -133,8 +145,7 @@ protected function compareConfigData(array $default, $updated) {
    *   Configuration data to be saved, that will be only the translated values.
    */
   public function saveTranslationData($name, $langcode, array $data) {
-    $locale_name = $this->configFactory->getLanguageConfigName($langcode, $name);
-    $this->configStorage->write($locale_name, $data);
+    $this->languageManager->getLanguageConfigOverride($langcode, $name)->setData($data)->save();
   }
 
   /**
@@ -146,8 +157,7 @@ public function saveTranslationData($name, $langcode, array $data) {
    *   Language code.
    */
   public function deleteTranslationData($name, $langcode) {
-    $locale_name = $this->configFactory->getLanguageConfigName($langcode, $name);
-    $this->configStorage->delete($locale_name);
+    $this->languageManager->getLanguageConfigOverride($langcode, $name)->delete();
   }
 
   /**
@@ -220,9 +230,9 @@ public function getStringNames(array $lids) {
    *   Language code to delete.
    */
   public function deleteLanguageTranslations($langcode) {
-    $locale_name = ConfigFactoryInterface::LANGUAGE_CONFIG_PREFIX . '.' . $langcode . '.';
-    foreach ($this->configStorage->listAll($locale_name) as $name) {
-      $this->configStorage->delete($name);
+    $storage = $this->languageManager->getLanguageOverrideStorage($langcode);
+    foreach ($storage->listAll() as $name) {
+      $this->languageManager->getLanguageConfigOverride($langcode, $name)->delete();
     }
   }
 
@@ -305,9 +315,8 @@ public function translateString($name, $langcode, $source, $context) {
    *   A boolean indicating if a language has configuration translations.
    */
   public function hasTranslation($name, Language $language) {
-    $locale_name = $this->configFactory->getLanguageConfigName($language->id, $name);
-    $translation = $this->configStorage->read($locale_name);
-    return !empty($translation);
+    $translation = $this->languageManager->getLanguageConfigOverride($language->id, $name);
+    return !$translation->isNew();
   }
 
 }
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigManagerTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigManagerTest.php
index 16fc30b..453743c 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigManagerTest.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigManagerTest.php
@@ -22,7 +22,7 @@ class LocaleConfigManagerTest extends DrupalUnitTestBase {
    *
    * @var array
    */
-  public static $modules = array('locale', 'locale_test');
+  public static $modules = array('language', 'locale', 'locale_test');
 
   /**
    * {@inheritdoc}
@@ -39,30 +39,19 @@ public static function getInfo() {
    * Tests hasTranslation().
    */
   public function testHasTranslation() {
+    $this->installConfig(array('locale_test'));
     $locale_config_manager = new LocaleConfigManager(
-      // In contrast to the actual configuration we use the installer storage
-      // as the config storage. That way, we do not actually have to install
-      // the module and can extend DrupalUnitTestBase.
-      $this->container->get('config.storage.installer'),
+      $this->container->get('config.storage'),
       $this->container->get('config.storage.schema'),
       $this->container->get('config.storage.installer'),
       $this->container->get('locale.storage'),
       $this->container->get('cache.config'),
-      $this->container->get('config.factory')
+      $this->container->get('config.factory'),
+      $this->container->get('language_manager')
     );
 
     $language = new Language(array('id' => 'de'));
-    // The installer storage throws an expcetion when requesting a non-existing
-    // file.
-    try {
-      $locale_config_manager->hasTranslation('locale_test.no_translation', $language);
-    }
-    catch (StorageException $exception) {
-      $result = FALSE;
-    }
-    $this->assertIdentical(FALSE, $result);
-
-    $result = $locale_config_manager->hasTranslation('locale_test.translation', $language);
-    $this->assertIdentical(TRUE, $result);
+    $this->assertFalse($locale_config_manager->hasTranslation('locale_test.no_translation', $language), 'There is no translation for locale_test.no_translation configuration.');
+    $this->assertTrue($locale_config_manager->hasTranslation('locale_test.translation', $language), 'There is a translation for locale_test.translation configuration.');
   }
 }
diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php
index f7c64bf..48a0132 100644
--- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php
+++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php
@@ -171,15 +171,16 @@ function testConfigTranslation() {
     $this->assertEqual($property->getValue(), $image_style_label, 'Got the right translation for image style name after translation');
 
     // Quick test to ensure translation file exists.
-    $language_config_name = \Drupal::configFactory()->getLanguageConfigName('xx', 'image.style.medium');
-    $this->assertEqual(\Drupal::config($language_config_name)->get('label'), $image_style_label);
+    $override = \Drupal::languageManager()->getLanguageConfigOverride('xx', 'image.style.medium');
+    $this->assertEqual($override->get('label'), $image_style_label);
 
     // Uninstall the module.
     $this->drupalPostForm('admin/modules/uninstall', array('uninstall[image]' => "image"), t('Uninstall'));
     $this->drupalPostForm(NULL, array(), t('Uninstall'));
 
     // Ensure that the translated configuration has been removed.
-    $this->assertFalse(\Drupal::config($language_config_name)->get('label'), 'Translated configuration for image module removed.');
+    $override = \Drupal::languageManager()->getLanguageConfigOverride('xx', 'image.style.medium');
+    $this->assertTrue($override->isNew(), 'Translated configuration for image module removed.');
 
     // Translate default category using the UI so configuration is refreshed.
     $category_label = $this->randomName(20);
diff --git a/core/modules/locale/locale.services.yml b/core/modules/locale/locale.services.yml
index b5f4cf4..7a0f0d7 100644
--- a/core/modules/locale/locale.services.yml
+++ b/core/modules/locale/locale.services.yml
@@ -6,7 +6,7 @@ services:
     arguments: ['@entity.manager', '@config.factory']
   locale.config.typed:
     class: Drupal\locale\LocaleConfigManager
-    arguments: ['@config.storage', '@config.storage.schema', '@config.storage.installer', '@locale.storage', '@cache.config', '@config.factory']
+    arguments: ['@config.storage', '@config.storage.schema', '@config.storage.installer', '@locale.storage', '@cache.config', '@config.factory', '@language_manager']
   locale.storage:
     class: Drupal\locale\StringDatabaseStorage
     arguments: ['@database']
diff --git a/core/modules/locale/tests/modules/locale_test/config/language.config.de.locale_test.translation.yml b/core/modules/locale/tests/modules/locale_test/config/language/de/locale_test.translation.yml
similarity index 100%
rename from core/modules/locale/tests/modules/locale_test/config/language.config.de.locale_test.translation.yml
rename to core/modules/locale/tests/modules/locale_test/config/language/de/locale_test.translation.yml
diff --git a/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterRebuildTest.php b/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterRebuildTest.php
index 93bb446..f3d43b2 100644
--- a/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterRebuildTest.php
+++ b/core/modules/system/lib/Drupal/system/Tests/Menu/MenuRouterRebuildTest.php
@@ -20,7 +20,7 @@ class MenuRouterRebuildTest extends WebTestBase {
    *
    * @var array
    */
-  public static $modules = array('locale', 'menu_test');
+  public static $modules = array('language', 'menu_test');
 
   /**
    * {@inheritdoc}
@@ -48,7 +48,7 @@ function setUp() {
    */
   public function testMenuRouterRebuildContext() {
     // Enter a language context before rebuilding the menu router tables.
-    \Drupal::configFactory()->setLanguage(language_load('nl'));
+    \Drupal::languageManager()->setConfigOverrideLanguage(language_load('nl'));
     \Drupal::service('router.builder')->rebuild();
 
     // Check that the language context was not used for building the menu item.
diff --git a/core/modules/system/tests/modules/menu_test/config/language.config.nl.menu_test.menu_item.yml b/core/modules/system/tests/modules/menu_test/config/language/nl/menu_test.menu_item.yml
similarity index 100%
rename from core/modules/system/tests/modules/menu_test/config/language.config.nl.menu_test.menu_item.yml
rename to core/modules/system/tests/modules/menu_test/config/language/nl/menu_test.menu_item.yml
diff --git a/core/modules/tour/tests/tour_test/config/language.config.it.tour.tour.tour-test.yml b/core/modules/tour/tests/tour_test/config/language/it/tour.tour.tour-test.yml
similarity index 100%
rename from core/modules/tour/tests/tour_test/config/language.config.it.tour.tour.tour-test.yml
rename to core/modules/tour/tests/tour_test/config/language/it/tour.tour.tour-test.yml
diff --git a/core/modules/user/user.module b/core/modules/user/user.module
index ab9a163..4e8bbe4 100644
--- a/core/modules/user/user.module
+++ b/core/modules/user/user.module
@@ -1183,13 +1183,13 @@ function user_view_multiple($accounts, $view_mode = 'full', $langcode = NULL) {
  */
 function user_mail($key, &$message, $params) {
   $token_service = \Drupal::token();
+  $language_manager = \Drupal::languageManager();
   $langcode = $message['langcode'];
   $variables = array('user' => $params['account']);
 
-  $original_language = \Drupal::configFactory()->getLanguage();
-
   $language = language_load($params['account']->getPreferredLangcode());
-  \Drupal::configFactory()->setLanguage($language);
+  $original_language = $language_manager->getConfigOverrideLanguage();
+  $language_manager->setConfigOverrideLanguage($language);
   $mail_config = \Drupal::config('user.mail');
 
    // We do not sanitize the token replacement, since the output of this
@@ -1198,7 +1198,8 @@ function user_mail($key, &$message, $params) {
   $message['subject'] .= $token_service->replace($mail_config->get($key . '.subject'), $variables, $token_options);
   $message['body'][] = $token_service->replace($mail_config->get($key . '.body'), $variables, $token_options);
 
-  \Drupal::configFactory()->setLanguage($original_language);
+  $language_manager->setConfigOverrideLanguage($original_language);
+
 }
 
 /**
