diff --git a/core/lib/Drupal/Core/Config/CachedStorage.php b/core/lib/Drupal/Core/Config/CachedStorage.php
index 08dc582..b6c28ad 100644
--- a/core/lib/Drupal/Core/Config/CachedStorage.php
+++ b/core/lib/Drupal/Core/Config/CachedStorage.php
@@ -41,6 +41,13 @@ class CachedStorage implements StorageInterface, StorageCacheInterface {
   protected $findByPrefixCache = array();
 
   /**
+   * The cache prefix to use for all cache entries.
+   *
+   * @var string
+   */
+  protected $cachePrefix = NULL;
+
+  /**
    * Constructs a new CachedStorage.
    *
    * @param \Drupal\Core\Config\StorageInterface $storage
@@ -67,7 +74,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 +82,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 +94,21 @@ 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)) {
-      $list = $this->storage->readMultiple($names);
+    if (!empty($cids)) {
+      $list = $this->storage->readMultiple(array_keys($cids));
       // 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 +123,8 @@ 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);
+      // @todo: How to deal with the prefix for the tag?
       Cache::deleteTags(array($this::FIND_BY_PREFIX_CACHE_TAG => TRUE));
       $this->findByPrefixCache = array();
       return TRUE;
@@ -130,7 +139,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 +154,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 +208,13 @@ 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)) {
+      if ($cache = $this->cache->get($this->getCacheKey('find:' . $prefix))) {
         $this->findByPrefixCache[$prefix] = $cache->data;
       }
       else {
         $this->findByPrefixCache[$prefix] = $this->storage->listAll($prefix);
         $this->cache->set(
-          'find:' . $prefix,
+          $this->getCacheKey('find:' . $prefix),
           $this->findByPrefixCache[$prefix],
           Cache::PERMANENT,
           array($this::FIND_BY_PREFIX_CACHE_TAG => TRUE)
@@ -221,9 +230,9 @@ 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);
+    $names = $this->storage->listAll($prefix);
     if ($this->storage->deleteAll($prefix)) {
-      $this->cache->deleteMultiple($cids);
+      $this->cache->deleteMultiple($this->getCacheKeys($names));
       return TRUE;
     }
     return FALSE;
@@ -235,4 +244,58 @@ public function deleteAll($prefix = '') {
   public function resetListCache() {
     $this->findByPrefixCache = array();
   }
+
+  /**
+   * Builds the cache key for a config name.
+   *
+   * @param string $name
+   *   Name of the config file.
+   *
+   * @return string
+   *   The cache key.
+   */
+  protected function getCacheKey($name) {
+    return $this->cachePrefix ? $this->cachePrefix . ':' . $name : $name;
+  }
+
+  /**
+   * Builds the cache key for a list of config names.
+   *
+   * @param array $names
+   *   List of config file names.
+   *
+   * @return array
+   *   List of cache keys, keyed by config name.
+   */
+  protected function getCacheKeys(array $names) {
+    $cids = array();
+    foreach ($names as $name) {
+      $cids[$name] = $this->getCacheKey($name);
+    }
+    return $cids;
+  }
+
+  /**
+   * Returns the config name from a cache key.
+   *
+   * @param string $cid
+   *   Cache key
+   *
+   * @return string
+   *   Name of the config file.
+   */
+  protected function getNameFromCacheKey($cid) {
+    return $this->cachePrefix ? substr($cid, strlen($this->cachePrefix) + 1) : $cid;
+  }
+
+  /**
+   * Set the cache prefix used by this storage.
+   *
+   * @param string $cachePrefix
+   *   Name of the prefix that should be used for all cache IDs.
+   */
+  public function setCachePrefix($cachePrefix) {
+    $this->cachePrefix = $cachePrefix;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Config/ConfigFactory.php b/core/lib/Drupal/Core/Config/ConfigFactory.php
index 5d3756e..f4e44d1 100644
--- a/core/lib/Drupal/Core/Config/ConfigFactory.php
+++ b/core/lib/Drupal/Core/Config/ConfigFactory.php
@@ -312,4 +312,11 @@ public function addOverride(ConfigFactoryOverrideInterface $config_factory_overr
     $this->configFactoryOverrides[] = $config_factory_override;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getOverrides() {
+    return $this->configFactoryOverrides;
+  }
+
 }
diff --git a/core/lib/Drupal/Core/Config/ConfigFactoryInterface.php b/core/lib/Drupal/Core/Config/ConfigFactoryInterface.php
index 55eb6f6..cafd3fd 100644
--- a/core/lib/Drupal/Core/Config/ConfigFactoryInterface.php
+++ b/core/lib/Drupal/Core/Config/ConfigFactoryInterface.php
@@ -134,4 +134,11 @@ public function listAll($prefix = '');
    */
   public function addOverride(ConfigFactoryOverrideInterface $config_factory_override);
 
+  /**
+   * Get all added configuration factory override instances.
+   *
+   * @return ConfigFactoryOverrideInterface[]
+   *   The configuration factory override instances.
+   */
+  public function getOverrides();
 }
diff --git a/core/lib/Drupal/Core/Config/ConfigFactoryOverrideInterface.php b/core/lib/Drupal/Core/Config/ConfigFactoryOverrideInterface.php
index 06e1f3f..96c8d48 100644
--- a/core/lib/Drupal/Core/Config/ConfigFactoryOverrideInterface.php
+++ b/core/lib/Drupal/Core/Config/ConfigFactoryOverrideInterface.php
@@ -32,4 +32,24 @@ public function loadOverrides($names);
    */
   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/ConfigInstaller.php b/core/lib/Drupal/Core/Config/ConfigInstaller.php
index 7ee1b56..d1197d7 100644
--- a/core/lib/Drupal/Core/Config/ConfigInstaller.php
+++ b/core/lib/Drupal/Core/Config/ConfigInstaller.php
@@ -138,12 +138,12 @@ public function installDefaultConfig($type, $name) {
       // Remove configuration that already exists in the active storage.
       $sorted_config = array_diff($sorted_config, $this->activeStorage->listAll());
 
-      foreach ($sorted_config as $name) {
-        $new_config = new Config($name, $this->activeStorage, $this->eventDispatcher, $this->typedConfig);
-        if ($data[$name] !== FALSE) {
-          $new_config->setData($data[$name]);
+      foreach ($sorted_config as $config_name) {
+        $new_config = new Config($config_name, $this->activeStorage, $this->eventDispatcher, $this->typedConfig);
+        if ($data[$config_name] !== FALSE) {
+          $new_config->setData($data[$config_name]);
         }
-        if ($entity_type = $this->configManager->getEntityTypeIdByName($name)) {
+        if ($entity_type = $this->configManager->getEntityTypeIdByName($config_name)) {
 
           // If we are syncing do not create configuration entities. Pluggable
           // configuration entities can have dependencies on modules that are
@@ -159,8 +159,8 @@ public function installDefaultConfig($type, $name) {
             ->getStorage($entity_type);
           // It is possible that secondary writes can occur during configuration
           // creation. Updates of such configuration are allowed.
-          if ($this->activeStorage->exists($name)) {
-            $id = $entity_storage->getIDFromConfigName($name, $entity_storage->getEntityType()->getConfigPrefix());
+          if ($this->activeStorage->exists($config_name)) {
+            $id = $entity_storage->getIDFromConfigName($config_name, $entity_storage->getEntityType()->getConfigPrefix());
             $entity = $entity_storage->load($id);
             foreach ($new_config->get() as $property => $value) {
               $entity->set($property, $value);
@@ -179,6 +179,12 @@ public function installDefaultConfig($type, $name) {
       }
       $this->configFactory->setOverrideState($old_state);
     }
+
+    // Allow configuration factory overrides to respond to installation.
+    foreach ($this->configFactory->getOverrides() as $config_override) {
+      $config_override->install($type, $name);
+    }
+
     // Reset all the static caches and list caches.
     $this->configFactory->reset();
   }
diff --git a/core/lib/Drupal/Core/Config/ConfigManager.php b/core/lib/Drupal/Core/Config/ConfigManager.php
index 6fd55c9..487bd6e 100644
--- a/core/lib/Drupal/Core/Config/ConfigManager.php
+++ b/core/lib/Drupal/Core/Config/ConfigManager.php
@@ -156,6 +156,11 @@ public function uninstall($type, $name) {
     foreach ($config_names as $config_name) {
       $this->configFactory->get($config_name)->delete();
     }
+    // Allow configuration factory overrides to respond to uninstallation.
+    foreach ($this->configFactory->getOverrides() 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 fbb9970..3db69a2 100644
--- a/core/lib/Drupal/Core/Config/FileStorage.php
+++ b/core/lib/Drupal/Core/Config/FileStorage.php
@@ -241,4 +241,14 @@ 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/modules/config/lib/Drupal/config/Tests/DefaultConfigTest.php b/core/modules/config/lib/Drupal/config/Tests/DefaultConfigTest.php
index aa5b950..52496e6 100644
--- a/core/modules/config/lib/Drupal/config/Tests/DefaultConfigTest.php
+++ b/core/modules/config/lib/Drupal/config/Tests/DefaultConfigTest.php
@@ -78,9 +78,9 @@ 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) schemas
+      // are in.
+      if (strpos($config_name, 'migrate.migration') === 0) {
         continue;
       }
 
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
index 106df3d..355869f 100644
--- 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
@@ -40,5 +40,17 @@ 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
index a9a435e..19ea21e 100644
--- 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
@@ -41,5 +41,17 @@ 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_test/config/language.config.de.config_test.system.yml b/core/modules/config/tests/config_test/config/language.config.de.config_test.system.yml
deleted file mode 100644
index 301e5a5..0000000
--- a/core/modules/config/tests/config_test/config/language.config.de.config_test.system.yml
+++ /dev/null
@@ -1 +0,0 @@
-foo: de bar
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.config.en.config_test.system.yml
deleted file mode 100644
index 05f8d1a..0000000
--- a/core/modules/config/tests/config_test/config/language.config.en.config_test.system.yml
+++ /dev/null
@@ -1 +0,0 @@
-foo: en bar
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.config.fr.config_test.system.yml
deleted file mode 100644
index c36dbf7..0000000
--- a/core/modules/config/tests/config_test/config/language.config.fr.config_test.system.yml
+++ /dev/null
@@ -1 +0,0 @@
-foo: fr bar
diff --git a/core/modules/config/tests/config_test/config/language/de/config_test.system.yml b/core/modules/config/tests/config_test/config/language/de/config_test.system.yml
new file mode 100644
index 0000000..301e5a5
--- /dev/null
+++ b/core/modules/config/tests/config_test/config/language/de/config_test.system.yml
@@ -0,0 +1 @@
+foo: de bar
diff --git a/core/modules/config/tests/config_test/config/language/en/config_test.system.yml b/core/modules/config/tests/config_test/config/language/en/config_test.system.yml
new file mode 100644
index 0000000..05f8d1a
--- /dev/null
+++ b/core/modules/config/tests/config_test/config/language/en/config_test.system.yml
@@ -0,0 +1 @@
+foo: en bar
diff --git a/core/modules/config/tests/config_test/config/language/fr/config_test.system.yml b/core/modules/config/tests/config_test/config/language/fr/config_test.system.yml
new file mode 100644
index 0000000..c36dbf7
--- /dev/null
+++ b/core/modules/config/tests/config_test/config/language/fr/config_test.system.yml
@@ -0,0 +1 @@
+foo: fr bar
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 4d2bd68..851a9b2 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,7 @@
 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;
@@ -355,7 +356,7 @@ 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 $config_translation
+   * @param \Drupal\language\Config\LanguageConfigOverride $config_translation
    *   Translation configuration override data.
    * @param array $config_values
    *   A simple one dimensional or recursive array:
@@ -375,7 +376,7 @@ protected function buildConfigForm(Element $schema, $config_data, $base_config_d
    * @return array
    *   Translation configuration override data.
    */
-  protected function setConfig(Language $language, Config $base_config, Config $config_translation, 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.
diff --git a/core/modules/language/language.install b/core/modules/language/language.install
new file mode 100644
index 0000000..8825db3
--- /dev/null
+++ b/core/modules/language/language.install
@@ -0,0 +1,26 @@
+<?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 7396d57..48c7c82 100644
--- a/core/modules/language/language.services.yml
+++ b/core/modules/language/language.services.yml
@@ -11,8 +11,20 @@ services:
     class: Drupal\language\EventSubscriber\ConfigSubscriber
     tags:
       - { name: event_subscriber }
+  language.cache_config:
+    class: Drupal\Core\Cache\CacheBackendInterface
+    tags:
+      - { name: cache.bin }
+    factory_method: get
+    factory_service: cache_factory
+    arguments: [language_config]
+  language.config_storage.file:
+    class: Drupal\language\Config\LanguageOverrideFileStorage
+  language.config_storage:
+    class: Drupal\language\Config\LanguageOverrideCachedStorage
+    arguments: ['@language.config_storage.file', '@language.cache_config']
   language.config_factory_override:
     class: Drupal\language\Config\LanguageConfigFactoryOverride
-    arguments: ['@config.storage', '@event_dispatcher', '@config.typed']
+    arguments: ['@language.config_storage', '@event_dispatcher', '@config.typed']
     tags:
       - { name: config.factory.override, priority: -254 }
diff --git a/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverride.php b/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverride.php
index bfadec0..37bdf9e 100644
--- a/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverride.php
+++ b/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverride.php
@@ -8,6 +8,7 @@
 namespace Drupal\language\Config;
 
 use Drupal\Core\Config\Config;
+use Drupal\Core\Config\FileStorage;
 use Drupal\Core\Config\StorageInterface;
 use Drupal\Core\Config\TypedConfigManagerInterface;
 use Drupal\Core\Language\Language;
@@ -22,7 +23,7 @@ class LanguageConfigFactoryOverride implements LanguageConfigFactoryOverrideInte
   /**
    * The configuration storage.
    *
-   * @var \Drupal\Core\Config\StorageInterface
+   * @var \Drupal\language\Config\LanguageOverrideStorageInterface
    */
   protected $storage;
 
@@ -50,14 +51,14 @@ class LanguageConfigFactoryOverride implements LanguageConfigFactoryOverrideInte
   /**
    * Constructs the LanguageConfigFactoryOverride object.
    *
-   * @param \Drupal\Core\Config\StorageInterface $storage
+   * @param \Drupal\language\Config\LanguageOverrideStorageInterface $storage
    *   The configuration storage engine.
    * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
    *   An event dispatcher instance to use for configuration events.
    * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
    *   The typed configuration manager.
    */
-  public function __construct(StorageInterface $storage, EventDispatcherInterface $event_dispatcher, TypedConfigManagerInterface $typed_config) {
+  public function __construct(LanguageOverrideStorageInterface $storage, EventDispatcherInterface $event_dispatcher, TypedConfigManagerInterface $typed_config) {
     $this->storage = $storage;
     $this->eventDispatcher = $event_dispatcher;
     $this->typedConfigManager = $typed_config;
@@ -67,72 +68,30 @@ public function __construct(StorageInterface $storage, EventDispatcherInterface
    * {@inheritdoc}
    */
   public function loadOverrides($names) {
-    $data = array();
-    $language_names = $this->getLanguageConfigNames($names);
-    if ($language_names) {
-      $data = $this->storage->readMultiple(array_values($language_names));
-      // Re-key the data array to use configuration names rather than override
-      // names.
-      $prefix_length = strlen(static::LANGUAGE_CONFIG_PREFIX . '.' . $this->language->id) + 1;
-      foreach ($data as $key => $value) {
-        unset($data[$key]);
-        $key = substr($key, $prefix_length);
-        $data[$key] = $value;
-      }
+    if ($this->language) {
+      return $this->storage->readMultiple($names);
     }
-    return $data;
+    return array();
   }
 
   /**
    * {@inheritdoc}
    */
   public function getOverride($langcode, $name) {
-    $override_name = $this->getLanguageConfigName($langcode, $name);
-    $overrides = $this->storage->read($override_name);
-    $config = new Config($override_name, $this->storage, $this->eventDispatcher, $this->typedConfigManager);
-    if (!empty($overrides)) {
-      $config->initWithData($overrides);
-    }
-    return $config;
-  }
-
-  /**
-   * Generate a list of configuration names based on base names.
-   *
-   * @param array $names
-   *   List of configuration names.
-   *
-   * @return array
-   *   List of configuration names for language override files if applicable.
-   */
-  protected 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;
-        }
-      }
+    $storage = clone $this->storage;
+    $data = $storage->setLangcode($langcode)->read($name);
+    $override = new LanguageConfigOverride($name, $storage, $this->typedConfigManager);
+    if (!empty($data)) {
+      $override->initWithData($data);
     }
-    return $language_names;
+    return $override;
   }
 
   /**
-   * Get language override name for given language and configuration name.
-   *
-   * @param string $langcode
-   *   Language code.
-   * @param string $name
-   *   Configuration name.
-   *
-   * @return bool|string
-   *   Configuration name or FALSE if not applicable.
+   * {@inheritdoc}
    */
-  protected function getLanguageConfigName($langcode, $name) {
-     if (strpos($name, static::LANGUAGE_CONFIG_PREFIX) === 0) {
-      return FALSE;
-    }
-    return static::LANGUAGE_CONFIG_PREFIX . '.' . $langcode . '.' . $name;
+  public function getStorage() {
+    return $this->storage;
   }
 
   /**
@@ -154,6 +113,7 @@ public function getLanguage() {
    */
   public function setLanguage(Language $language = NULL) {
     $this->language = $language;
+    $this->storage->setLangcode($this->language ? $this->language->id : NULL);
     return $this;
   }
 
@@ -162,7 +122,58 @@ public function setLanguage(Language $language = NULL) {
    */
   public function setLanguageFromDefault(LanguageDefault $language_default = NULL) {
     $this->language = $language_default ? $language_default->get() : NULL;
+    $this->storage->setLangcode($this->language->id);
     return $this;
   }
 
+  /**
+   * {@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->typedConfigManager);
+            $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->typedConfigManager);
+            $config->delete();
+          }
+        }
+      }
+    }
+  }
+
 }
diff --git a/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverrideInterface.php b/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverrideInterface.php
index 1c3de55..43fcd41 100644
--- a/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverrideInterface.php
+++ b/core/modules/language/lib/Drupal/language/Config/LanguageConfigFactoryOverrideInterface.php
@@ -62,4 +62,9 @@ public function setLanguageFromDefault(LanguageDefault $language_default = NULL)
    */
   public function getOverride($langcode, $name);
 
+  /**
+   * Returns the storage instance.
+   */
+  public function getStorage();
+
 }
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..6ed2b11
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/Config/LanguageConfigOverride.php
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\Config\LanguageConfigOverride.
+ */
+
+namespace Drupal\language\Config;
+
+use Drupal\Core\Config\StorableConfigBase;
+use Drupal\Core\Config\StorageInterface;
+use Drupal\Core\Config\TypedConfigManagerInterface;
+
+/**
+ * Defines language configuration overrides.
+ */
+class LanguageConfigOverride extends StorableConfigBase {
+
+  /**
+   * Constructs a language override object.
+   *
+   * @param string $name
+   *   The name of the configuration object being overridden.
+   * @param \Drupal\Core\Config\StorageInterface $storage
+   *   A storage controller object to use for reading and writing the
+   *   configuration override.
+   * @param \Drupal\Core\Config\TypedConfigManagerInterface $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/LanguageOverrideCachedStorage.php b/core/modules/language/lib/Drupal/language/Config/LanguageOverrideCachedStorage.php
new file mode 100644
index 0000000..4026102
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/Config/LanguageOverrideCachedStorage.php
@@ -0,0 +1,55 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\Config\LanguageOverrideFileStorage.
+ */
+
+namespace Drupal\language\Config;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Config\CachedStorage;
+
+/**
+ * Defines cached storage for language configuration overrides.
+ */
+class LanguageOverrideCachedStorage extends CachedStorage implements LanguageOverrideStorageInterface {
+
+  /**
+   * The language configuration storage to be cached.
+   *
+   * @var \Drupal\language\Config\LanguageOverrideStorageInterface
+   */
+  protected $storage;
+
+  /**
+   * Constructs a new LanguageOverrideCachedStorage controller.
+   *
+   * @param \Drupal\language\Config\LanguageOverrideStorageInterface $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(LanguageOverrideStorageInterface $storage, CacheBackendInterface $cache) {
+    parent::__construct($storage, $cache);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function setLangcode($langcode) {
+    $this->cachePrefix = $langcode;
+    $this->storage->setLangcode($langcode);
+    return $this;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  function __clone() {
+    // Also clone the inner storage, so that language code changes work as
+    // expected.
+    $this->storage = clone $this->storage;
+  }
+
+}
diff --git a/core/modules/language/lib/Drupal/language/Config/LanguageOverrideFileStorage.php b/core/modules/language/lib/Drupal/language/Config/LanguageOverrideFileStorage.php
new file mode 100644
index 0000000..fa31003
--- /dev/null
+++ b/core/modules/language/lib/Drupal/language/Config/LanguageOverrideFileStorage.php
@@ -0,0 +1,128 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\language\Config\LanguageOverrideFileStorage.
+ */
+
+namespace Drupal\language\Config;
+
+use Drupal\Core\Cache\CacheBackendInterface;
+use Drupal\Core\Config\CachedStorage;
+use Drupal\Core\Config\FileStorage;
+
+/**
+ * Defines file storage for language configuration overrides.
+ */
+class LanguageOverrideFileStorage extends FileStorage implements LanguageOverrideStorageInterface {
+
+  /**
+   * The directory for the current language.
+   *
+   * @var string
+   */
+  protected $langcode;
+
+  /**
+   * The directory for the current language.
+   *
+   * @var string
+   */
+  protected $directory;
+
+  /**
+   * Tracks whether the directory exists to prevent excessive file system reads.
+   *
+   * @var bool
+   */
+  protected $directoryExists;
+
+  /**
+   * Constructs a new LanguageOverrideFileStorage.
+   */
+  public function __construct() {
+    // Do not call the parent as we do not yet have a directory.
+  }
+
+  /**
+   * Gets the override configuration directory for a language code.
+   *
+   * @param string $langcode
+   *   The language code 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;
+  }
+
+  /**
+   * Discovers is the directory for the language 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..ba62497
--- /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 {
+
+  /**
+   * 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 b164cd5..6ea8999 100644
--- a/core/modules/language/lib/Drupal/language/ConfigurableLanguageManager.php
+++ b/core/modules/language/lib/Drupal/language/ConfigurableLanguageManager.php
@@ -407,4 +407,13 @@ public function getLanguageConfigOverride($langcode, $name) {
     return $this->configFactoryOverride->getOverride($langcode, $name);
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getLanguageOverrideStorage($langcode) {
+    // Clone the language override storage so a process could compare language
+    // overrides if it wanted to.
+    $storage = clone $this->configFactoryOverride->getStorage();
+    return $storage->setLangcode($langcode);
+  }
 }
diff --git a/core/modules/language/lib/Drupal/language/ConfigurableLanguageManagerInterface.php b/core/modules/language/lib/Drupal/language/ConfigurableLanguageManagerInterface.php
index d2f4f36..4f7fd24 100644
--- a/core/modules/language/lib/Drupal/language/ConfigurableLanguageManagerInterface.php
+++ b/core/modules/language/lib/Drupal/language/ConfigurableLanguageManagerInterface.php
@@ -86,7 +86,7 @@ public function updateLockedLanguageWeights();
    * @param string $name
    *   The language configuration object name.
    *
-   * @return \Drupal\Core\Config\Config
+   * @return \Drupal\language\Config\LanguageConfigOverride
    *   The language config override object.
    */
   public function getLanguageConfigOverride($langcode, $name);
diff --git a/core/modules/locale/lib/Drupal/locale/LocaleConfigManager.php b/core/modules/locale/lib/Drupal/locale/LocaleConfigManager.php
index 3c0dc57..50e2f40 100644
--- a/core/modules/locale/lib/Drupal/locale/LocaleConfigManager.php
+++ b/core/modules/locale/lib/Drupal/locale/LocaleConfigManager.php
@@ -230,9 +230,9 @@ public function getStringNames(array $lids) {
    *   Language code to delete.
    */
   public function deleteLanguageTranslations($langcode) {
-    $locale_name = LanguageConfigFactoryOverrideInterface::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();
     }
   }
 
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.config.de.locale_test.translation.yml
deleted file mode 100644
index cd23f6b..0000000
--- a/core/modules/locale/tests/modules/locale_test/config/language.config.de.locale_test.translation.yml
+++ /dev/null
@@ -1 +0,0 @@
-test: German test
diff --git a/core/modules/locale/tests/modules/locale_test/config/language/de/locale_test.translation.yml b/core/modules/locale/tests/modules/locale_test/config/language/de/locale_test.translation.yml
new file mode 100644
index 0000000..cd23f6b
--- /dev/null
+++ b/core/modules/locale/tests/modules/locale_test/config/language/de/locale_test.translation.yml
@@ -0,0 +1 @@
+test: German test
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.config.nl.menu_test.menu_item.yml
deleted file mode 100644
index a845667..0000000
--- a/core/modules/system/tests/modules/menu_test/config/language.config.nl.menu_test.menu_item.yml
+++ /dev/null
@@ -1 +0,0 @@
-title: Dutch
diff --git a/core/modules/system/tests/modules/menu_test/config/language/nl/menu_test.menu_item.yml b/core/modules/system/tests/modules/menu_test/config/language/nl/menu_test.menu_item.yml
new file mode 100644
index 0000000..a845667
--- /dev/null
+++ b/core/modules/system/tests/modules/menu_test/config/language/nl/menu_test.menu_item.yml
@@ -0,0 +1 @@
+title: Dutch
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.config.it.tour.tour.tour-test.yml
deleted file mode 100644
index e36e435..0000000
--- a/core/modules/tour/tests/tour_test/config/language.config.it.tour.tour.tour-test.yml
+++ /dev/null
@@ -1,5 +0,0 @@
-label: Tour test italian
-tips:
-  tour-test-1:
-    label: La pioggia cade in spagna
-    body: Per lo più in pianura.
diff --git a/core/modules/tour/tests/tour_test/config/language/it/tour.tour.tour-test.yml b/core/modules/tour/tests/tour_test/config/language/it/tour.tour.tour-test.yml
new file mode 100644
index 0000000..e36e435
--- /dev/null
+++ b/core/modules/tour/tests/tour_test/config/language/it/tour.tour.tour-test.yml
@@ -0,0 +1,5 @@
+label: Tour test italian
+tips:
+  tour-test-1:
+    label: La pioggia cade in spagna
+    body: Per lo più in pianura.
diff --git a/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php b/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php
index 6c60af4..f25a66f 100644
--- a/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php
+++ b/core/tests/Drupal/Tests/Core/Config/CachedStorageTest.php
@@ -84,6 +84,41 @@ public function testGetMultipleOnPrimedCache() {
   }
 
   /**
+   * Test that the cache prefix is used correctly.
+   */
+  public function testGetMultipleCachePrefix() {
+    $configNames = array(
+      'foo.bar',
+      'baz.back',
+    );
+    $configCacheValues = array(
+      'prefix:foo.bar' => array(
+        'foo' => 'bar',
+      ),
+      'prefix:baz.back' => array(
+        'foo' => 'bar',
+      ),
+    );
+    $configValues = array(
+      'foo.bar' => array(
+        'foo' => 'bar',
+      ),
+      'baz.back' => array(
+        'foo' => 'bar',
+      ),
+    );
+    $storage = $this->getMock('Drupal\Core\Config\StorageInterface');
+    $storage->expects($this->never())->method('readMultiple');
+    $cache = new MemoryBackend(__FUNCTION__);
+    foreach ($configCacheValues as $key => $value) {
+      $cache->set($key, $value);
+    }
+    $cachedStorage = new CachedStorage($storage, $cache);
+    $cachedStorage->setCachePrefix('prefix');
+    $this->assertEquals($configValues, $cachedStorage->readMultiple($configNames));
+  }
+
+  /**
    * Test fall through to file storage in CachedStorage::readMulitple().
    */
   public function testGetMultipleOnPartiallyPrimedCache() {
@@ -116,7 +151,7 @@ public function testGetMultipleOnPartiallyPrimedCache() {
     $storage = $this->getMock('Drupal\Core\Config\StorageInterface');
     $storage->expects($this->once())
       ->method('readMultiple')
-      ->with(array(2 => $configNames[2], 4 => $configNames[4]))
+      ->with(array($configNames[2], $configNames[4]))
       ->will($this->returnValue($response));
 
     $cachedStorage = new CachedStorage($storage, $cache);
