diff --git a/core/lib/Drupal/Core/Config/ConfigFactoryOverrideBase.php b/core/lib/Drupal/Core/Config/ConfigFactoryOverrideBase.php
index 18d2ee8..d7bc941 100644
--- a/core/lib/Drupal/Core/Config/ConfigFactoryOverrideBase.php
+++ b/core/lib/Drupal/Core/Config/ConfigFactoryOverrideBase.php
@@ -67,12 +67,12 @@ static function getSubscribedEvents() {
    */
   protected function filterOverride(Config $config, StorableConfigBase $override) {
     $override_data = $override->get();
-    $this->filterNestedArray($config->get(), $override_data);
+    $changed = $this->filterNestedArray($config->get(), $override_data);
     if (empty($override_data)) {
       // If no override values are left that would apply, remove the override.
       $override->delete();
     }
-    else {
+    elseif ($changed) {
       // Otherwise set the filtered override values back.
       $override->setData($override_data)->save();
     }
@@ -85,29 +85,37 @@ protected function filterOverride(Config $config, StorableConfigBase $override)
    *   Original data array to filter against.
    * @param array $override_data
    *   Override data to filter.
+   *
+   * @return bool
+   *   TRUE if $override_data was changed, FALSE otherwise.
    */
   protected function filterNestedArray(array $original_data, array &$override_data) {
+    $changed = FALSE;
     foreach ($override_data as $key => $value) {
       if (!isset($original_data[$key])) {
         // The original data is not there anymore, remove the override.
         unset($override_data[$key]);
+        $changed = TRUE;
       }
       elseif (is_array($override_data[$key])) {
         if (is_array($original_data[$key])) {
           // Do the filtering one level deeper.
-          $this->filterNestedArray($original_data[$key], $override_data[$key]);
+          $changed = $this->filterNestedArray($original_data[$key], $override_data[$key]);
           // If no overrides are left under this level, remove the level.
           if (empty($override_data[$key])) {
             unset($override_data[$key]);
+            $changed = TRUE;
           }
         }
         else {
           // The override is an array but the value is not, this will not go
           // well, remove the override.
           unset($override_data[$key]);
+          $changed = TRUE;
         }
       }
     }
+    return $changed;
   }
 
 }
diff --git a/core/lib/Drupal/Core/Config/Schema/SchemaCheckTrait.php b/core/lib/Drupal/Core/Config/Schema/SchemaCheckTrait.php
index 5127adc..6915bbd 100644
--- a/core/lib/Drupal/Core/Config/Schema/SchemaCheckTrait.php
+++ b/core/lib/Drupal/Core/Config/Schema/SchemaCheckTrait.php
@@ -53,7 +53,7 @@ public function checkConfigSchema(TypedConfigManagerInterface $typed_config, $co
     if (!$typed_config->hasConfigSchema($config_name)) {
       return FALSE;
     }
-    $definition = $typed_config->getDefinition($config_name);
+    $definition = $typed_config->getDefinitionForConfigName($config_name);
     $data_definition = $typed_config->buildDataDefinition($definition, $config_data);
     $this->schema = $typed_config->create($data_definition, $config_data);
     $errors = array();
diff --git a/core/lib/Drupal/Core/Config/StorableConfigBase.php b/core/lib/Drupal/Core/Config/StorableConfigBase.php
index 4af470e..8507a40 100644
--- a/core/lib/Drupal/Core/Config/StorableConfigBase.php
+++ b/core/lib/Drupal/Core/Config/StorableConfigBase.php
@@ -128,7 +128,7 @@ public function getStorage() {
    */
   protected function getSchemaWrapper() {
     if (!isset($this->schemaWrapper)) {
-      $definition = $this->typedConfigManager->getDefinition($this->name);
+      $definition = $this->typedConfigManager->getDefinitionForConfigName($this->name);
       $data_definition = $this->typedConfigManager->buildDataDefinition($definition, $this->data);
       $this->schemaWrapper = $this->typedConfigManager->create($data_definition, $this->data);
     }
diff --git a/core/lib/Drupal/Core/Config/TypedConfigManager.php b/core/lib/Drupal/Core/Config/TypedConfigManager.php
index 5f71e6b..99c77cd 100644
--- a/core/lib/Drupal/Core/Config/TypedConfigManager.php
+++ b/core/lib/Drupal/Core/Config/TypedConfigManager.php
@@ -72,8 +72,8 @@ public function __construct(StorageInterface $configStorage, StorageInterface $s
    */
   public function get($name) {
     $data = $this->configStorage->read($name);
-    $type_definition = $this->getDefinition($name);
-    $data_definition =  $this->buildDataDefinition($type_definition, $data);
+    $type_definition = $this->getDefinitionForConfigName($name);
+    $data_definition = $this->buildDataDefinition($type_definition, $data);
     return $this->create($data_definition, $data);
   }
 
@@ -148,6 +148,22 @@ public function getDefinition($base_plugin_id, $exception_on_invalid = TRUE) {
   }
 
   /**
+   * @inheritdoc
+   */
+  public function getDefinitionForConfigName($name) {
+    $definition = $this->getDefinition($name);
+    // If this definition is defined as a mapping, add a langcode element if
+    // not already present.
+    if (isset($definition['mapping']) && !isset($definition['mapping']['langcode'])) {
+      $definition['mapping']['langcode'] = array(
+        'type' => 'string',
+        'label' => 'Language code',
+      );
+    }
+    return $definition;
+  }
+
+  /**
    * {@inheritdoc}
    */
   public function clearCachedDefinitions() {
diff --git a/core/lib/Drupal/Core/Config/TypedConfigManagerInterface.php b/core/lib/Drupal/Core/Config/TypedConfigManagerInterface.php
index 84d347b..1d1db79 100644
--- a/core/lib/Drupal/Core/Config/TypedConfigManagerInterface.php
+++ b/core/lib/Drupal/Core/Config/TypedConfigManagerInterface.php
@@ -102,4 +102,41 @@ public function buildDataDefinition(array $definition, $value, $name = NULL, $pa
    */
   public function hasConfigSchema($name);
 
+  /**
+   * Gets a specific plugin definition for a configuration name.
+   *
+   * To request definition of internal configuration types, use
+   * self::getDefinition(). This method is to be used only for types for
+   * configuration names.
+   *
+   * @param string $name
+   *   The configuration name.
+   *
+   * @return array
+   *   A plugin definition array. If the given configuration name does not have
+   *   typed configuration definition assigned, the definition of an undefined
+   *   element type is returned.
+   */
+  public function getDefinitionForConfigName($name);
+
+  /**
+   * Gets a specific plugin definition.
+   *
+   * To request definition for a configuration name,
+   * self::getDefinitionForConfigName() should be used. Use this method
+   * to get definition of internal configuration types.
+   *
+   * @param string $plugin_id
+   *   A plugin id.
+   * @param bool $exception_on_invalid
+   *   Ignored with TypedConfigManagerInterface. Kept for compatibility with
+   *   DiscoveryInterface.
+   *
+   * @return array
+   *   A plugin definition array. If the given configuration name does not have
+   *   typed configuration definition assigned, the definition of an undefined
+   *   element type is returned.
+   */
+  public function getDefinition($plugin_id, $exception_on_invalid = TRUE);
+
 }
diff --git a/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php b/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php
index 1285cb7..e2c1a98 100644
--- a/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php
+++ b/core/lib/Drupal/Core/StringTranslation/TranslationWrapper.php
@@ -60,6 +60,29 @@ public function __construct($string, array $arguments = array(), array $options
   }
 
   /**
+   * Gets the untranslated string value stored in this translation wrapper.
+   *
+   * @return string
+   *   The string stored in this wrapper.
+   */
+  public function getUntranslatedString() {
+    return $this->string;
+  }
+
+  /**
+   * Gets a specific option from this translation wrapper.
+   *
+   * @param $name
+   *   Option name.
+   *
+   * @return mixed
+   *   The value of this option or empty string of option is not set.
+   */
+  public function getOption($name) {
+    return isset($this->options[$name]) ? $this->options[$name] : '';
+  }
+
+  /**
    * Implements the magic __toString() method.
    */
   public function __toString() {
diff --git a/core/modules/config/src/Tests/ConfigSchemaTest.php b/core/modules/config/src/Tests/ConfigSchemaTest.php
index ca53a53..ede034a 100644
--- a/core/modules/config/src/Tests/ConfigSchemaTest.php
+++ b/core/modules/config/src/Tests/ConfigSchemaTest.php
@@ -26,7 +26,7 @@ class ConfigSchemaTest extends KernelTestBase {
    *
    * @var array
    */
-  public static $modules = array('system', 'language', 'locale', 'field', 'image', 'config_test', 'config_schema_test');
+  public static $modules = array('system', 'language', 'field', 'image', 'config_test', 'config_schema_test');
 
   /**
    * {@inheritdoc}
diff --git a/core/modules/config_translation/src/ConfigEntityMapper.php b/core/modules/config_translation/src/ConfigEntityMapper.php
index 08ce8a6..fe3da3c 100644
--- a/core/modules/config_translation/src/ConfigEntityMapper.php
+++ b/core/modules/config_translation/src/ConfigEntityMapper.php
@@ -98,7 +98,7 @@ public static function create(ContainerInterface $container, array $configuratio
       $plugin_definition,
       $container->get('config.factory'),
       $container->get('config.typed'),
-      $container->get('locale.config.typed'),
+      $container->get('locale.config_manager'),
       $container->get('plugin.manager.config_translation.mapper'),
       $container->get('router.route_provider'),
       $container->get('string_translation'),
diff --git a/core/modules/config_translation/src/ConfigNamesMapper.php b/core/modules/config_translation/src/ConfigNamesMapper.php
index b681b33..ee1d0ac 100644
--- a/core/modules/config_translation/src/ConfigNamesMapper.php
+++ b/core/modules/config_translation/src/ConfigNamesMapper.php
@@ -150,7 +150,7 @@ public static function create(ContainerInterface $container, array $configuratio
       $plugin_definition,
       $container->get('config.factory'),
       $container->get('config.typed'),
-      $container->get('locale.config.typed'),
+      $container->get('locale.config_manager'),
       $container->get('plugin.manager.config_translation.mapper'),
       $container->get('router.route_provider'),
       $container->get('string_translation'),
@@ -465,7 +465,7 @@ public function hasTranslatable() {
    */
   public function hasTranslation(LanguageInterface $language) {
     foreach ($this->getConfigNames() as $name) {
-      if ($this->localeConfigManager->hasTranslation($name, $language)) {
+      if ($this->localeConfigManager->hasTranslation($name, $language->getId())) {
         return TRUE;
       }
     }
diff --git a/core/modules/config_translation/tests/src/Unit/ConfigNamesMapperTest.php b/core/modules/config_translation/tests/src/Unit/ConfigNamesMapperTest.php
index 0b3ead9..796012f 100644
--- a/core/modules/config_translation/tests/src/Unit/ConfigNamesMapperTest.php
+++ b/core/modules/config_translation/tests/src/Unit/ConfigNamesMapperTest.php
@@ -589,7 +589,7 @@ public function testHasTranslation(array $mock_return_values, $expected) {
 
     $map = array();
     foreach ($config_names as $i => $config_name) {
-      $map[] = array($config_name, $language, $mock_return_values[$i]);
+      $map[] = array($config_name, $language->getId(), $mock_return_values[$i]);
     }
     $this->localeConfigManager
       ->expects($this->any())
diff --git a/core/modules/language/src/Entity/ConfigurableLanguage.php b/core/modules/language/src/Entity/ConfigurableLanguage.php
index 8fef8d2..89cc167 100644
--- a/core/modules/language/src/Entity/ConfigurableLanguage.php
+++ b/core/modules/language/src/Entity/ConfigurableLanguage.php
@@ -117,10 +117,6 @@ public function preSave(EntityStorageInterface $storage) {
     // rebuild services if necessary during
     // \Drupal\language\Entity\ConfigurableLanguage::postSave().
     $this->preSaveMultilingual = \Drupal::languageManager()->isMultilingual();
-    // Languages are picked from a predefined list which is given in English.
-    // For the uncommon case of custom languages the label should be given in
-    // English.
-    $this->langcode = 'en';
   }
 
   /**
diff --git a/core/modules/language/src/Form/LanguageFormBase.php b/core/modules/language/src/Form/LanguageFormBase.php
index a272166..14f219a 100644
--- a/core/modules/language/src/Form/LanguageFormBase.php
+++ b/core/modules/language/src/Form/LanguageFormBase.php
@@ -75,7 +75,7 @@ public function commonForm(array &$form) {
     }
     $form['label'] = array(
       '#type' => 'textfield',
-      '#title' => $this->t('Language name in English'),
+      '#title' => $this->t('Language name'),
       '#maxlength' => 64,
       '#default_value' => $language->label(),
       '#required' => TRUE,
diff --git a/core/modules/language/src/Tests/LanguageConfigurationTest.php b/core/modules/language/src/Tests/LanguageConfigurationTest.php
index dc522bc..fdaf217 100644
--- a/core/modules/language/src/Tests/LanguageConfigurationTest.php
+++ b/core/modules/language/src/Tests/LanguageConfigurationTest.php
@@ -132,7 +132,7 @@ function testLanguageConfiguration() {
     );
     $this->drupalPostForm('admin/config/regional/language/add', $edit, 'Add language');
     $language = $this->config('language.entity.de')->get();
-    $this->assertEqual($language['langcode'], 'en');
+    $this->assertEqual($language['langcode'], 'fr');
 
     // Ensure that German language has a weight of 5 after being created through
     // the UI.
diff --git a/core/modules/language/src/Tests/LanguageCustomLanguageConfigurationTest.php b/core/modules/language/src/Tests/LanguageCustomLanguageConfigurationTest.php
index fb74878..da6ddf5 100644
--- a/core/modules/language/src/Tests/LanguageCustomLanguageConfigurationTest.php
+++ b/core/modules/language/src/Tests/LanguageCustomLanguageConfigurationTest.php
@@ -41,7 +41,7 @@ public function testLanguageConfiguration() {
     $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language'));
     // Test validation on missing values.
     $this->assertText(t('!name field is required.', array('!name' => t('Language code'))));
-    $this->assertText(t('!name field is required.', array('!name' => t('Language name in English'))));
+    $this->assertText(t('!name field is required.', array('!name' => t('Language name'))));
     $empty_language = new Language();
     $this->assertFieldChecked('edit-direction-' . $empty_language->getDirection(), 'Consistent usage of language direction.');
     $this->assertUrl(\Drupal::url('language.add', array(), array('absolute' => TRUE)), [], 'Correct page redirection.');
@@ -60,7 +60,7 @@ public function testLanguageConfiguration() {
       '@url' => 'http://www.w3.org/International/articles/language-tags/',
     )));
 
-    $this->assertRaw(t('%field cannot contain any markup.', array('%field' => t('Language name in English'))));
+    $this->assertRaw(t('%field cannot contain any markup.', array('%field' => t('Language name'))));
     $this->assertUrl(\Drupal::url('language.add', array(), array('absolute' => TRUE)), [], 'Correct page redirection.');
 
     // Test adding a custom language with a numeric region code.
diff --git a/core/modules/locale/locale.bulk.inc b/core/modules/locale/locale.bulk.inc
index 357aaf8..a4677ca 100644
--- a/core/modules/locale/locale.bulk.inc
+++ b/core/modules/locale/locale.bulk.inc
@@ -324,7 +324,7 @@ function locale_translate_batch_refresh(array &$context) {
   }
   elseif ($name = array_shift($context['sandbox']['refresh']['names'])) {
     // Refresh all languages for one object at a time.
-    $count = locale_config_update_multiple(array($name), $context['sandbox']['refresh']['languages']);
+    $count = Locale::config()->updateConfigTranslations(array($name), $context['sandbox']['refresh']['languages']);
     $context['results']['stats']['config'] += $count;
     // Inherit finished information from the "parent" string lookup step so
     // visual display of status will make sense.
@@ -532,7 +532,7 @@ function locale_translate_delete_translation_files(array $projects = array(), ar
  *   The batch definition.
  */
 function locale_config_batch_update_components(array $options, array $langcodes = array(), array $components = array()) {
-  $langcodes = $langcodes ? $langcodes : array_keys(locale_translatable_language_list());
+  $langcodes = $langcodes ? $langcodes : array_keys(\Drupal::languageManager()->getLanguages());
   if ($langcodes && $names = \Drupal\locale\Locale::config()->getComponentNames($components)) {
     return locale_config_batch_build($names, $langcodes, $options);
   }
@@ -606,7 +606,7 @@ function locale_config_batch_refresh_name(array $names, array $langcodes, array
   if (!isset($context['result']['stats']['config'])) {
     $context['result']['stats']['config'] = 0;
   }
-  $context['result']['stats']['config'] += locale_config_update_multiple($names, $langcodes);
+  $context['result']['stats']['config'] += Locale::config()->updateConfigTranslations($names, $langcodes);
   foreach ($names as $name) {
     $context['result']['names'][] = $name;
   }
@@ -639,41 +639,3 @@ function locale_config_batch_finished($success, array $results) {
     }
   }
 }
-
-/**
- * Updates all configuration for names / languages.
- *
- * @param array $names
- *   Array of names of configuration objects to update.
- * @param array $langcodes
- *   (optional) Array of language codes to update. Defaults to all languages.
- *
- * @return int
- *   Number of configuration objects retranslated.
- */
-function locale_config_update_multiple(array $names, array $langcodes = array()) {
-  /** @var \Drupal\language\ConfigurableLanguageManagerInterface $language_manager */
-  $language_manager = \Drupal::languageManager();
-  $locale_config_manager = Locale::config();
-
-  $langcodes = $langcodes ? $langcodes : array_keys(locale_translatable_language_list());
-  $count = 0;
-  foreach ($names as $name) {
-    $wrapper = $locale_config_manager->get($name);
-    foreach ($langcodes as $langcode) {
-      $translation = $wrapper->getValue() ? $wrapper->getTranslation($langcode)->getValue() : NULL;
-      if ($translation) {
-        $locale_config_manager->saveTranslationData($name, $langcode, $translation);
-        $count++;
-      }
-      else {
-        // Do not bother deleting language overrides which do not exist in the
-        // first place.
-        if (!$language_manager->getLanguageConfigOverride($langcode, $name)->isNew()) {
-          $locale_config_manager->deleteTranslationData($name, $langcode);
-        }
-      }
-    }
-  }
-  return $count;
-}
diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module
index 971b47d..8100654 100644
--- a/core/modules/locale/locale.module
+++ b/core/modules/locale/locale.module
@@ -24,6 +24,7 @@
 use Drupal\Core\Language\LanguageInterface;
 use Drupal\language\ConfigurableLanguageInterface;
 use Drupal\Component\Utility\Crypt;
+use Drupal\locale\Locale;
 
 /**
  * Regular expression pattern used to localize JavaScript strings.
@@ -226,7 +227,7 @@ function locale_configurable_language_delete(ConfigurableLanguageInterface $lang
   locale_translate_delete_translation_files(array(), array($language->id()));
 
   // Remove translated configuration objects.
-  \Drupal\locale\Locale::config()->deleteLanguageTranslations($language->id());
+  Locale::config()->deleteLanguageTranslations($language->id());
 
   // Changing the language settings impacts the interface:
   _locale_invalidate_js($language->id());
@@ -246,7 +247,7 @@ function locale_configurable_language_delete(ConfigurableLanguageInterface $lang
  */
 function locale_translatable_language_list() {
   $languages = \Drupal::languageManager()->getLanguages();
-  if (!locale_translate_english()) {
+  if (!locale_is_translatable('en')) {
     unset($languages['en']);
   }
   return $languages;
@@ -312,6 +313,8 @@ function locale_get_plural($count, $langcode = NULL) {
  * Implements hook_modules_installed().
  */
 function locale_modules_installed($modules) {
+  locale_system_set_config_langcodes();
+
   $components['module'] = $modules;
   locale_system_update($components);
 }
@@ -328,6 +331,8 @@ function locale_module_preuninstall($module) {
  * Implements hook_themes_installed().
  */
 function locale_themes_installed($themes) {
+  locale_system_set_config_langcodes();
+
   $components['theme'] = $themes;
   locale_system_update($components);
 }
@@ -356,6 +361,34 @@ function locale_cron() {
 }
 
 /**
+ * Update default configuration when new modules or themes are installed.
+ */
+function locale_system_set_config_langcodes() {
+  // Need to rewrite some default configuration language codes if the default
+  // site language is not English.
+  $default_langcode = \Drupal::languageManager()->getDefaultLanguage()->getId();
+  if ($default_langcode != 'en') {
+    // Update active configuration copies of all prior shipped configuration if
+    // they are still English. It is not enough to change configuration shipped
+    // with the components just installed, because installing a component such
+    // as views or tour module may bring in default configuration from prior
+    // components.
+    $names = Locale::config()->getComponentNames();
+    foreach ($names as $name) {
+      $config = \Drupal::configFactory()->reset($name)->getEditable($name);
+      // Should only update if still exists in active configuration. If locale
+      // module is enabled later, then some configuration may not exist anymore.
+      if (!$config->isNew()) {
+        $langcode = $config->get('langcode');
+        if (empty($langcode) || $langcode == 'en') {
+          $config->set('langcode', $default_langcode)->save();
+        }
+      }
+    }
+  }
+}
+
+/**
  * Imports translations when new modules or themes are installed.
  *
  * This function will start a batch to import translations for the added
@@ -411,8 +444,6 @@ function locale_system_remove($components) {
   if ($language_list = locale_translatable_language_list()) {
     module_load_include('compare.inc', 'locale');
     \Drupal::moduleHandler()->loadInclude('locale', 'bulk.inc');
-    // Delete configuration translations.
-    \Drupal\locale\Locale::config()->deleteComponentTranslations($components, array_keys($language_list));
 
     // Only when projects are removed, the translation files and records will be
     // deleted. Not each disabled module will remove a project, e.g., sub
@@ -606,7 +637,7 @@ function locale_form_language_admin_overview_form_alter(&$form, FormStateInterfa
       'translated' => 0,
       'ratio' => 0,
     );
-    if (!$language->isLocked() && ($langcode != 'en' || locale_translate_english())) {
+    if (!$language->isLocked() && locale_is_translatable($langcode)) {
       $form['languages'][$langcode]['locale_statistics'] = array(
         '#markup' => \Drupal::l(
           t('@translated/@total (@ratio%)', array(
@@ -689,13 +720,16 @@ function locale_form_language_admin_edit_form_alter_submit($form, FormStateInter
 }
 
 /**
- * Checks whether locale translates to English.
+ * Checks whether $langcode is a language supported as a locale target.
+ *
+ * @param string $langcode
+ *   The language code.
  *
  * @return bool
- *   Returns TRUE if content should be translated to English, FALSE otherwise.
+ *   Whether $langcode can be translated to in locale.
  */
-function locale_translate_english() {
-  return \Drupal::config('locale.settings')->get('translate_english');
+function locale_is_translatable($langcode) {
+  return $langcode != 'en' || \Drupal::config('locale.settings')->get('translate_english');
 }
 
 /**
@@ -1020,19 +1054,14 @@ function _locale_refresh_translations($langcodes, $lids = array()) {
 /**
  * Refreshes configuration after string translations have been updated.
  *
- * The information that will be refreshed includes:
- * - JavaScript translations.
- * - Locale cache.
- *
  * @param array $langcodes
  *   Language codes for updated translations.
  * @param array $lids
  *   List of string identifiers that have been updated / created.
  */
 function _locale_refresh_configuration(array $langcodes, array $lids) {
-  if ($lids && $langcodes && $names = \Drupal\locale\Locale::config()->getStringNames($lids)) {
-    \Drupal::moduleHandler()->loadInclude('locale', 'bulk.inc');
-    locale_config_update_multiple($names, $langcodes);
+  if ($lids && $langcodes && $names = Locale::config()->getStringNames($lids)) {
+    Locale::config()->updateConfigTranslations($names, $langcodes);
   }
 }
 
diff --git a/core/modules/locale/locale.services.yml b/core/modules/locale/locale.services.yml
index 3df6f93..284878b 100644
--- a/core/modules/locale/locale.services.yml
+++ b/core/modules/locale/locale.services.yml
@@ -1,5 +1,5 @@
 services:
-  locale.config.typed:
+  locale.config_manager:
     class: Drupal\locale\LocaleConfigManager
     arguments: ['@config.storage', '@config.storage.installer', '@locale.storage', '@config.factory', '@config.typed', '@language_manager']
   locale.storage:
@@ -22,6 +22,6 @@ services:
       - { name: stream_wrapper, scheme: translations }
   locale.config_subscriber:
     class: Drupal\locale\LocaleConfigSubscriber
-    arguments: ['@locale.storage', '@config.factory', '@locale.config.typed']
+    arguments: ['@config.factory', '@locale.config_manager']
     tags:
       - { name: event_subscriber }
diff --git a/core/modules/locale/src/Form/ExportForm.php b/core/modules/locale/src/Form/ExportForm.php
index f9b6361..035b2e8 100644
--- a/core/modules/locale/src/Form/ExportForm.php
+++ b/core/modules/locale/src/Form/ExportForm.php
@@ -61,7 +61,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
     $languages = $this->languageManager->getLanguages();
     $language_options = array();
     foreach ($languages as $langcode => $language) {
-      if ($langcode != 'en' || locale_translate_english()) {
+      if (locale_is_translatable($langcode)) {
         $language_options[$langcode] = $language->getName();
       }
     }
diff --git a/core/modules/locale/src/Form/ImportForm.php b/core/modules/locale/src/Form/ImportForm.php
index e3448b3..ca706ea 100644
--- a/core/modules/locale/src/Form/ImportForm.php
+++ b/core/modules/locale/src/Form/ImportForm.php
@@ -79,7 +79,7 @@ public function buildForm(array $form, FormStateInterface $form_state) {
     // are to translate Drupal to English as well.
     $existing_languages = array();
     foreach ($languages as $langcode => $language) {
-      if ($langcode != 'en' || locale_translate_english()) {
+      if (locale_is_translatable($langcode)) {
         $existing_languages[$langcode] = $language->getName();
       }
     }
diff --git a/core/modules/locale/src/Form/TranslateFormBase.php b/core/modules/locale/src/Form/TranslateFormBase.php
index b894f17..59af72b 100644
--- a/core/modules/locale/src/Form/TranslateFormBase.php
+++ b/core/modules/locale/src/Form/TranslateFormBase.php
@@ -164,7 +164,7 @@ protected function translateFilters() {
     $languages = $this->languageManager->getLanguages();
     $language_options = array();
     foreach ($languages as $langcode => $language) {
-      if ($langcode != 'en' || locale_translate_english()) {
+      if (locale_is_translatable($langcode)) {
         $language_options[$langcode] = $language->getName();
       }
     }
diff --git a/core/modules/locale/src/Locale.php b/core/modules/locale/src/Locale.php
index f4afaed..9235a9c 100644
--- a/core/modules/locale/src/Locale.php
+++ b/core/modules/locale/src/Locale.php
@@ -23,6 +23,6 @@ class Locale {
    * @return \Drupal\locale\LocaleConfigManager
    */
   public static function config() {
-    return \Drupal::service('locale.config.typed');
+    return \Drupal::service('locale.config_manager');
   }
 }
diff --git a/core/modules/locale/src/LocaleConfigManager.php b/core/modules/locale/src/LocaleConfigManager.php
index aee2d59..ed1941c 100644
--- a/core/modules/locale/src/LocaleConfigManager.php
+++ b/core/modules/locale/src/LocaleConfigManager.php
@@ -7,33 +7,53 @@
 
 namespace Drupal\locale;
 
-use Drupal\Core\Config\TypedConfigManagerInterface;
-use Drupal\Core\Config\StorageInterface;
+use Drupal\Component\Utility\NestedArray;
 use Drupal\Core\Config\ConfigFactoryInterface;
-use Drupal\Core\Language\LanguageInterface;
+use Drupal\Core\Config\StorageInterface;
+use Drupal\Core\Config\TypedConfigManagerInterface;
+use Drupal\Core\StringTranslation\TranslationWrapper;
+use Drupal\Core\TypedData\TraversableTypedDataInterface;
+use Drupal\Core\TypedData\TypedDataInterface;
 use Drupal\language\ConfigurableLanguageManagerInterface;
 
 /**
- * Manages localized configuration type plugins.
+ * Manages configuration supported in part by interface translation.
+ *
+ * This manager is responsible to update configuration overrides and active
+ * translations when interface translation data changes. This allows Drupal to
+ * translate user roles, views, blocks, etc. after Drupal has been installed
+ * using the locale module's storage. When translations change in locale,
+ * LocaleConfigManager::updateConfigTranslations() is invoked to update the
+ * corresponding storage of the translation (in active storage or overrides).
+ *
+ * In turn when translated configuration or configuration language overrides are
+ * changed, it is the responsibility of LocaleConfigSubscriber to update locale
+ * storage.
+ *
+ * By design locale module only deals with sources in English.
+ *
+ * @see \Drupal\locale\LocaleConfigSubscriber
  */
 class LocaleConfigManager {
 
   /**
-   * A storage instance for reading configuration data.
+   * The storage instance for reading configuration data.
    *
    * @var \Drupal\Core\Config\StorageInterface
    */
   protected $configStorage;
 
   /**
-   * A storage instance for reading default configuration data.
+   * The storage instance for reading default configuration data.
    *
    * @var \Drupal\Core\Config\StorageInterface
    */
   protected $installStorage;
 
   /**
-   * A string storage for reading and writing translations.
+   * The string storage for reading and writing translations.
+   *
+   * @var \Drupal\locale\StringStorageInterface;
    */
   protected $localeStorage;
 
@@ -66,11 +86,13 @@ class LocaleConfigManager {
   protected $typedConfigManager;
 
   /**
-   * Whether or not configuration translations are currently being updated.
+   * Whether or not configuration translations are being updated from locale.
+   *
+   * @see self::isUpdatingFromLocale()
    *
    * @var bool
    */
-  protected $isUpdating = FALSE;
+  protected $isUpdatingFromLocale = FALSE;
 
   /**
    * Creates a new typed configuration manager.
@@ -99,61 +121,119 @@ public function __construct(StorageInterface $config_storage, StorageInterface $
   }
 
   /**
-   * Gets locale wrapper with typed configuration data.
+   * Gets array of translation wrappers for translatable configuration.
    *
    * @param string $name
    *   Configuration object name.
    *
-   * @return \Drupal\locale\LocaleTypedConfig
-   *   Locale-wrapped configuration element.
-   */
-  public function get($name) {
-    // Read default and current configuration data.
-    $default = $this->installStorageRead($name);
-    $updated = $this->configStorage->read($name);
-    // We get only the data that didn't change from default.
-    $data = $this->compareConfigData($default, $updated);
-    $definition = $this->typedConfigManager->getDefinition($name);
-    $data_definition = $this->typedConfigManager->buildDataDefinition($definition, $data);
-    // Unless the configuration has a explicit language code we assume English.
-    $langcode = isset($default['langcode']) ? $default['langcode'] : 'en';
-    $wrapper = new LocaleTypedConfig($data_definition, $name, $langcode, $this, $this->typedConfigManager, $this->languageManager);
-    $wrapper->setValue($data);
-    return $wrapper;
+   * @return array
+   *   Array of translatable elements of the default configuration in $name.
+   */
+  public function getTranslatableDefaultConfig($name) {
+    if ($this->isSupported($name)) {
+      // Create typed configuration wrapper based on install storage data.
+      $data = $this->installStorageRead($name);
+      $type_definition = $this->typedConfigManager->getDefinitionForConfigName($name);
+      $data_definition = $this->typedConfigManager->buildDataDefinition($type_definition, $data);
+      $typed_config = $this->typedConfigManager->create($data_definition, $data);
+      if ($typed_config instanceof TraversableTypedDataInterface) {
+        return $this->getTranslatableData($typed_config);
+      }
+    }
+    return array();
   }
 
   /**
-   * Compares default configuration with updated data.
+   * Gets translatable configuration data for a typed configuration element.
    *
-   * @param array $default
-   *   Default configuration data.
-   * @param array|false $updated
-   *   Current configuration data, or FALSE if no configuration data existed.
+   * @param \Drupal\Core\TypedData\TypedDataInterface $element
+   *   Typed configuration element.
    *
-   * @return array
-   *   The elements of default configuration that haven't changed.
+   * @return array|\Drupal\Core\StringTranslation\TranslationWrapper
+   *   A nested array matching the exact structure under $element with only the
+   *   elements that are translatable wrapped into a TranslationWrapper. If the
+   *   provided $element is not traversable, the return value is a single
+   *   TranslationWrapper.
    */
-  protected function compareConfigData(array $default, $updated) {
-    // Speed up comparison, specially for install operations.
-    if ($default === $updated) {
-      return $default;
+  protected function getTranslatableData(TypedDataInterface $element) {
+    $translatable = array();
+    if ($element instanceof TraversableTypedDataInterface) {
+      foreach ($element as $key => $property) {
+        $value = $this->getTranslatableData($property);
+        if (!empty($value)) {
+          $translatable[$key] = $value;
+        }
+      }
     }
-    $result = array();
-    foreach ($default as $key => $value) {
-      if (isset($updated[$key])) {
-        if (is_array($value)) {
-          $result[$key] = $this->compareConfigData($value, $updated[$key]);
+    else {
+      $definition = $element->getDataDefinition();
+      if (!empty($definition['translatable'])) {
+        $options = array();
+        if (isset($definition['translation context'])) {
+          $options['context'] = $definition['translation context'];
+        }
+        return new TranslationWrapper($element->getValue(), array(), $options);
+      }
+    }
+    return $translatable;
+  }
+
+  /**
+   * Process the translatable data array with a given language.
+   *
+   * If the given language is translatable, will return the translated copy
+   * which will only contain strings that had translations. If the given
+   * language is English and is not translatable, will return a simplified
+   * array of the English source strings only.
+   *
+   * @param string $name
+   *   The configuration name.
+   * @param array $active
+   *   The active configuration data.
+   * @param array $translatable
+   *   The translatable array structure. A nested array matching the exact
+   *   structure under of the default configuration for $name with only the
+   *   elements that are translatable wrapped into a TranslationWrapper.
+   *   @see self::getTranslatableData().
+   * @param string $langcode
+   *   The language code to process the array with
+   *
+   * @return array
+   *   Processed translatable data array. Will only contain translations
+   *   different from source strings or in case of untranslatable English, the
+   *   source strings themselves.
+   */
+  protected function processTranslatableData($name, array $active, array $translatable, $langcode) {
+    $translated = array();
+    foreach ($translatable as $key => $item) {
+      if (!isset($active[$key])) {
+        continue;
+      }
+      if (is_array($item)) {
+        // Only add this key if there was a translated value underneath.
+        $value = $this->processTranslatableData($name, $active[$key], $item, $langcode);
+        if (!empty($value)) {
+          $translated[$key] = $value;
+        }
+      }
+      else {
+        /** @var \Drupal\Core\StringTranslation\TranslationWrapper $item */
+        if (locale_is_translatable($langcode)) {
+          $value = $this->translateString($name, $langcode, $item->getUntranslatedString(), $item->getOption('context'));
+        }
+        else {
+          $value = $item->getUntranslatedString();
         }
-        elseif ($value === $updated[$key]) {
-          $result[$key] = $value;
+        if (!empty($value)) {
+          $translated[$key] = $value;
         }
       }
     }
-    return $result;
+    return $translated;
   }
 
   /**
-   * Saves translated configuration data.
+   * Saves translated configuration override.
    *
    * @param string $name
    *   Configuration object name.
@@ -162,10 +242,24 @@ protected function compareConfigData(array $default, $updated) {
    * @param array $data
    *   Configuration data to be saved, that will be only the translated values.
    */
-  public function saveTranslationData($name, $langcode, array $data) {
-    $this->isUpdating = TRUE;
+  protected function saveTranslationOverride($name, $langcode, array $data) {
+    $this->isUpdatingFromLocale = TRUE;
     $this->languageManager->getLanguageConfigOverride($langcode, $name)->setData($data)->save();
-    $this->isUpdating = FALSE;
+    $this->isUpdatingFromLocale = FALSE;
+  }
+
+  /**
+   * Saves translated configuration data.
+   *
+   * @param string $name
+   *   Configuration object name.
+   * @param array $data
+   *   Configuration data to be saved with translations merged in.
+   */
+  protected function saveTranslationActive($name, array $data) {
+    $this->isUpdatingFromLocale = TRUE;
+    $this->configFactory->getEditable($name)->setData($data)->save();
+    $this->isUpdatingFromLocale = FALSE;
   }
 
   /**
@@ -176,10 +270,10 @@ public function saveTranslationData($name, $langcode, array $data) {
    * @param string $langcode
    *   Language code.
    */
-  public function deleteTranslationData($name, $langcode) {
-    $this->isUpdating = TRUE;
+  protected function deleteTranslationOverride($name, $langcode) {
+    $this->isUpdatingFromLocale = TRUE;
     $this->languageManager->getLanguageConfigOverride($langcode, $name)->delete();
-    $this->isUpdating = FALSE;
+    $this->isUpdatingFromLocale = FALSE;
   }
 
   /**
@@ -192,7 +286,7 @@ public function deleteTranslationData($name, $langcode) {
    * @return array
    *   Array of configuration object names.
    */
-  public function getComponentNames(array $components) {
+  public function getComponentNames(array $components = array()) {
     $components = array_filter($components);
     if ($components) {
       $names = array();
@@ -209,27 +303,6 @@ public function getComponentNames(array $components) {
   }
 
   /**
-   * Deletes configuration translations for uninstalled components.
-   *
-   * @param array $components
-   *   Array with string identifiers.
-   * @param array $langcodes
-   *   Array of language codes.
-   */
-  public function deleteComponentTranslations(array $components, array $langcodes) {
-    $this->isUpdating = TRUE;
-    $names = $this->getComponentNames($components);
-    if ($names && $langcodes) {
-      foreach ($names as $name) {
-        foreach ($langcodes as $langcode) {
-          $this->deleteTranslationData($name, $langcode);
-        }
-      }
-    }
-    $this->isUpdating = FALSE;
-  }
-
-  /**
    * Gets configuration names associated with strings.
    *
    * @param array $lids
@@ -254,12 +327,12 @@ public function getStringNames(array $lids) {
    *   Language code to delete.
    */
   public function deleteLanguageTranslations($langcode) {
-    $this->isUpdating = TRUE;
+    $this->isUpdatingFromLocale = TRUE;
     $storage = $this->languageManager->getLanguageConfigOverrideStorage($langcode);
     foreach ($storage->listAll() as $name) {
       $this->languageManager->getLanguageConfigOverride($langcode, $name)->delete();
     }
-    $this->isUpdating = FALSE;
+    $this->isUpdatingFromLocale = FALSE;
   }
 
   /**
@@ -330,29 +403,234 @@ public function translateString($name, $langcode, $source, $context) {
   }
 
   /**
+   * Reset static cache of configuration string translations.
+   *
+   * @return $this
+   */
+  public function reset() {
+    $this->translations = array();
+    return $this;
+  }
+
+  /**
+   * Get the translation object for the given source/context and language.
+   *
+   * @param string $name
+   *   Name of the configuration location.
+   * @param string $langcode
+   *   Language code to translate to.
+   * @param string $source
+   *   The source string, should be English.
+   * @param string $context
+   *   The string context.
+   *
+   * @return \Drupal\locale\TranslationString|FALSE
+   *   The translation object if the string was not empty or FALSE otherwise.
+   */
+  public function getStringTranslation($name, $langcode, $source, $context) {
+    if ($source) {
+      $this->translateString($name, $langcode, $source, $context);
+      if ($string = $this->translations[$name][$langcode][$context][$source]) {
+        if (!$string->isTranslation()) {
+          $conditions = array('lid' => $string->lid, 'language' => $langcode);
+          return $this->localeStorage->createTranslation($conditions);
+        }
+        else {
+          return $string;
+        }
+      }
+    }
+    return FALSE;
+  }
+
+  /**
    * Checks whether a language has configuration translation.
    *
    * @param string $name
    *   Configuration name.
-   * @param \Drupal\Core\Language\LanguageInterface $language
-   *   A language object.
+   * @param string $langcode
+   *   A language code.
    *
    * @return bool
    *   A boolean indicating if a language has configuration translations.
    */
-  public function hasTranslation($name, LanguageInterface $language) {
-    $translation = $this->languageManager->getLanguageConfigOverride($language->getId(), $name);
+  public function hasTranslation($name, $langcode) {
+    $translation = $this->languageManager->getLanguageConfigOverride($langcode, $name);
     return !$translation->isNew();
   }
 
   /**
-   * Indicates whether configuration translations are currently being updated.
+   * Returns the original language code for this shipped configuration.
+   *
+   * @param $name
+   *   The configuration name.
+   *
+   * @return null|string
+   *   Language code of the default configuration for $name. If the default
+   *   configuration data for $name did not contain a language code, it is
+   *   assumed to be English. The return value is NULL if no such default
+   *   configuration exists.
+   */
+  public function getDefaultConfigLangcode($name) {
+    $shipped = $this->installStorageRead($name);
+    if (!empty($shipped)) {
+      return !empty($shipped['langcode']) ? $shipped['langcode'] : 'en';
+    }
+  }
+
+  /**
+   * Returns the current language code for this active configuration.
+   *
+   * @param $name
+   *   The configuration name.
+   *
+   * @return null|string
+   *   Language code of the current active configuration for $name. If the
+   *   configuration data for $name did not contain a language code, it is
+   *   assumed to be English. The return value is NULL if no such active
+   *   configuration exists.
+   */
+  public function getActiveConfigLangcode($name) {
+    $active = $this->configStorage->read($name);
+    if (!empty($active)) {
+      return !empty($active['langcode']) ? $active['langcode'] : 'en';
+    }
+  }
+
+  /**
+   * Whether the given configuration is supported for interface translation.
+   *
+   * @param $name
+   *   The configuration name.
+   *
+   * @return bool
+   *   TRUE if interface translation is supported.
+   */
+  public function isSupported($name) {
+    return $this->getDefaultConfigLangcode($name) == 'en' && $this->configStorage->read($name);
+  }
+
+  /**
+   * Indicates whether configuration translations are being updated from locale.
    *
    * @return bool
    *   Whether or not configuration translations are currently being updated.
+   *   If TRUE, LocaleConfigManager is in control of the process and the
+   *   reference data is locale's storage. Changes made to active configuration
+   *   and overrides in this case should not feed back to locale storage.
+   *   On the other hand, when not updating from locale and configuration
+   *   translations change, we need to feed back to the locale storage.
+   */
+  public function isUpdatingTranslationsFromLocale() {
+    return $this->isUpdatingFromLocale;
+  }
+
+  /**
+   * Updates all configuration translations for the names / languages provided.
+   *
+   * To be used when interface translation changes result in the need to update
+   * configuration translations to keep them in sync.
+   *
+   * @param array $names
+   *   Array of names of configuration objects to update.
+   * @param array $langcodes
+   *   (optional) Array of language codes to update. Defaults to all
+   *   configurable languages.
+   *
+   * @return int
+   *   Total number of configuration override and active configuration objects
+   *   updated (saved or removed).
    */
-  public function isUpdatingConfigTranslations() {
-    return $this->isUpdating;
+  public function updateConfigTranslations(array $names, array $langcodes = array()) {
+    $langcodes = $langcodes ? $langcodes : array_keys($this->languageManager->getLanguages());
+    $count = 0;
+    foreach ($names as $name) {
+      $translatable = $this->getTranslatableDefaultConfig($name);
+      if (empty($translatable)) {
+        // If there is nothing translatable in this configuration or not
+        // supported, skip it.
+        continue;
+      }
+
+      $active_langcode = $this->getActiveConfigLangcode($name);
+      $active = $this->configStorage->read($name);
+
+      foreach ($langcodes as $langcode) {
+        $processed = $this->processTranslatableData($name, $active, $translatable, $langcode);
+        if ($langcode != $active_langcode) {
+          // If the language code is not the same as the active storage
+          // language, we should update a configuration override.
+          if (!empty($processed)) {
+            // Update translation data in configuration override.
+            $this->saveTranslationOverride($name, $langcode, $processed);
+            $count++;
+          }
+          else {
+            $override = $this->languageManager->getLanguageConfigOverride($langcode, $name);
+            if (!$override->isNew()) {
+              $data = $this->filterOverride($override->get(), $translatable);
+              if (empty($data)) {
+                // Delete language override if there is no data left at all.
+                // This means all prior translations in the override were locale
+                // managed.
+                $this->deleteTranslationOverride($name, $langcode);
+                $count++;
+              }
+              else {
+                // If there were translatable elements besides locale managed
+                // items, save with only those, and remove the ones managed
+                // by locale only.
+                $this->saveTranslationOverride($name, $langcode, $data);
+                $count++;
+              }
+            }
+          }
+        }
+        elseif (locale_is_translatable($langcode)) {
+          // If the language code is the active storage language, we should
+          // update. If it is English, we should only update if English is also
+          // translatable.
+          $active = NestedArray::mergeDeepArray(array($active, $processed), TRUE);
+          $this->saveTranslationActive($name, $active);
+          $count++;
+        }
+      }
+    }
+    return $count;
+  }
+
+  /**
+   * Filters override data based on default translatable items.
+   *
+   * @param array $override_data
+   *   Configuration override data.
+   * @param array $translatable
+   *   Translatable data array. @see self::getTranslatableData()
+   * @return array
+   *   Nested array of any items of $override_data which did not have keys in
+   *   $translatable. May be empty if $override_data only had items which were
+   *   also in $translatable.
+   */
+  protected function filterOverride(array $override_data, array $translatable) {
+    $filtered_data = array();
+    foreach ($override_data as $key => $value) {
+      if (isset($translatable[$key])) {
+        // If the translatable default configuration has this key, look further
+        // for subkeys or ignore this element for scalar values.
+        if (is_array($value)) {
+          $value = $this->filterOverride($value, $translatable[$key]);
+          if (!empty($value)) {
+            $filtered_data[$key] = $value;
+          }
+        }
+      }
+      else {
+        // If this key was not in the translatable default configuration,
+        // keep it.
+        $filtered_data[$key] = $value;
+      }
+    }
+    return $filtered_data;
   }
 
   /**
diff --git a/core/modules/locale/src/LocaleConfigSubscriber.php b/core/modules/locale/src/LocaleConfigSubscriber.php
index 6c42d50..d388edb 100644
--- a/core/modules/locale/src/LocaleConfigSubscriber.php
+++ b/core/modules/locale/src/LocaleConfigSubscriber.php
@@ -6,35 +6,37 @@
 
 namespace Drupal\locale;
 
-use Drupal\Core\Config\Config;
+use Drupal\Core\Config\ConfigCrudEvent;
+use Drupal\Core\Config\ConfigEvents;
 use Drupal\Core\Config\ConfigFactoryInterface;
-use Drupal\Core\TypedData\TraversableTypedDataInterface;
-use Drupal\language\Config\LanguageConfigOverride;
+use Drupal\Core\Config\StorableConfigBase;
 use Drupal\language\Config\LanguageConfigOverrideCrudEvent;
 use Drupal\language\Config\LanguageConfigOverrideEvents;
 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
 
 /**
- * Updates corresponding string translation when language overrides change.
+ * Updates strings translation when configuration translations change.
  *
- * This reacts to the updating or deleting of configuration language overrides.
- * It checks whether there are string translations associated with the
- * configuration that is being saved and, if so, updates those string
- * translations with the new configuration values and marks them as customized.
- * That way manual updates to configuration will not be inadvertently reverted
- * when updated translations from https://localize.drupal.org are being
- * imported.
+ * This reacts to the updates of translated active configuration and
+ * configuration language overrides. When those updates involve configuration
+ * which was available as default configuration, we need to feed back changes
+ * to any item which was originally part of that configuration to the interface
+ * translation storage. Those updated translations are saved as customized, so
+ * further community translation updates will not undo user changes.
+ *
+ * This subscriber does not respond to deleting active configuration or deleting
+ * configuration translations. The locale storage is additive and we cannot be
+ * sure that only a given configuration translation used a source string. So
+ * we should not remove the translations from locale storage in these cases. The
+ * configuration or override would itself be deleted either way.
+ *
+ * By design locale module only deals with sources in English.
+ *
+ * @see \Drupal\locale\LocaleConfigManager
  */
 class LocaleConfigSubscriber implements EventSubscriberInterface {
 
   /**
-   * The string storage.
-   *
-   * @var \Drupal\locale\StringStorageInterface;
-   */
-  protected $stringStorage;
-
-  /**
    * The configuration factory.
    *
    * @var \Drupal\Core\Config\ConfigFactoryInterface
@@ -51,15 +53,12 @@ class LocaleConfigSubscriber implements EventSubscriberInterface {
   /**
    * Constructs a LocaleConfigSubscriber.
    *
-   * @param \Drupal\locale\StringStorageInterface $string_storage
-   *   The string storage.
    * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
    *   The configuration factory.
    * @param \Drupal\locale\LocaleConfigManager $locale_config_manager
    *   The typed configuration manager.
    */
-  public function __construct(StringStorageInterface $string_storage, ConfigFactoryInterface $config_factory, LocaleConfigManager $locale_config_manager) {
-    $this->stringStorage = $string_storage;
+  public function __construct(ConfigFactoryInterface $config_factory, LocaleConfigManager $locale_config_manager) {
     $this->configFactory = $config_factory;
     $this->localeConfigManager = $locale_config_manager;
   }
@@ -68,202 +67,165 @@ public function __construct(StringStorageInterface $string_storage, ConfigFactor
    * {@inheritdoc}
    */
   public static function getSubscribedEvents() {
-    $events[LanguageConfigOverrideEvents::SAVE_OVERRIDE] = 'onSave';
-    $events[LanguageConfigOverrideEvents::DELETE_OVERRIDE] = 'onDelete';
+    $events[LanguageConfigOverrideEvents::SAVE_OVERRIDE] = 'onOverrideChange';
+    $events[LanguageConfigOverrideEvents::DELETE_OVERRIDE] = 'onOverrideChange';
+    $events[ConfigEvents::SAVE] = 'onConfigSave';
     return $events;
   }
 
-
   /**
-   * Updates the translation strings when shipped configuration is saved.
+   * Updates the locale strings when a translated active configuration is saved.
    *
-   * @param \Drupal\language\Config\LanguageConfigOverrideCrudEvent $event
-   *   The language configuration event.
+   * @param \Drupal\Core\Config\ConfigCrudEvent $event
+   *   The configuration event.
    */
-  public function onSave(LanguageConfigOverrideCrudEvent $event) {
-    // Do not mark strings as customized when community translations are being
-    // imported.
-    if ($this->localeConfigManager->isUpdatingConfigTranslations()) {
-      $callable = [$this, 'saveTranslation'];
-    }
-    else {
-      $callable = [$this, 'saveCustomizedTranslation'];
+  public function onConfigSave(ConfigCrudEvent $event) {
+    // Only attempt to feed back configuration translation changes to locale if
+    // the update itself was not initiated by locale data changes.
+    if (!$this->localeConfigManager->isUpdatingTranslationsFromLocale()) {
+      $config = $event->getConfig();
+      $langcode = $config->get('langcode') ?: 'en';
+      $this->updateLocaleStorage($config, $langcode);
     }
-
-    $this->updateTranslationStrings($event, $callable);
   }
 
   /**
-   * Updates the translation strings when shipped configuration is deleted.
+   * Updates the locale strings when a configuration override is saved/deleted.
    *
    * @param \Drupal\language\Config\LanguageConfigOverrideCrudEvent $event
    *   The language configuration event.
    */
-  public function onDelete(LanguageConfigOverrideCrudEvent $event) {
-    if ($this->localeConfigManager->isUpdatingConfigTranslations()) {
-      $callable = [$this, 'deleteTranslation'];
-    }
-    else {
-      // Do not delete the string, but save a customized translation with the
-      // source value so that the deletion will not be reverted by importing
-      // community translations.
-      // @see \Drupal\locale\LocaleConfigSubscriber::saveCustomizedTranslation()
-      $callable = [$this, 'saveCustomizedTranslation'];
+  public function onOverrideChange(LanguageConfigOverrideCrudEvent $event) {
+    // Only attempt to feed back configuration override changes to locale if
+    // the update itself was not initiated by locale data changes.
+    if (!$this->localeConfigManager->isUpdatingTranslationsFromLocale()) {
+      $translation_config = $event->getLanguageConfigOverride();
+      $langcode = $translation_config->getLangcode();
+      $reference_config = $this->configFactory->getEditable($translation_config->getName())->get();
+      $this->updateLocaleStorage($translation_config, $langcode, $reference_config);
     }
-
-    $this->updateTranslationStrings($event, $callable);
   }
 
   /**
-   * Updates the translation strings of shipped configuration.
+   * Update locale storage based on configuration translations.
    *
-   * @param \Drupal\language\Config\LanguageConfigOverrideCrudEvent $event
-   *   The language configuration event.
-   * @param $callable
-   *   A callable to apply to each translatable string of the configuration.
+   * @param \Drupal\Core\Config\StorableConfigBase $config
+   *   Active configuration or configuration translation override.
+   * @param string $langcode
+   *   The language code of $config.
+   * @param array $reference_config
+   *   (Optional) Reference configuration to check against if $config was an
+   *   override. This allows us to update locale keys for data not in the
+   *   override but still in the active configuration.
    */
-  protected function updateTranslationStrings(LanguageConfigOverrideCrudEvent $event, $callable) {
-    $translation_config = $event->getLanguageConfigOverride();
-    $name = $translation_config->getName();
-
-    // Only do anything if the configuration was shipped.
-    if ($this->stringStorage->getLocations(['type' => 'configuration', 'name' => $name])) {
-      $source_config = $this->configFactory->getEditable($name);
-      $schema = $this->localeConfigManager->get($name)->getTypedConfig();
-      $this->traverseSchema($schema, $source_config, $translation_config, $callable);
+  protected function updateLocaleStorage(StorableConfigBase $config, $langcode, array $reference_config = array()) {
+    $name = $config->getName();
+    if ($this->localeConfigManager->isSupported($name) && locale_is_translatable($langcode)) {
+      $translatables = $this->localeConfigManager->getTranslatableDefaultConfig($name);
+      $this->processTranslatableData($name, $config->get(), $translatables, $langcode, $reference_config);
     }
   }
 
   /**
-   * Traverses configuration schema and applies a callback to each leaf element.
-   *
-   * It skips leaf elements that are not translatable.
-   *
-   * @param \Drupal\Core\TypedData\TraversableTypedDataInterface $schema
-   *   The respective configuration schema.
-   * @param callable $callable
-   *   The callable to apply to each leaf element. The callable will be called
-   *   with the leaf element and the element key as arguments.
-   * @param string|null $base_key
-   *   (optional) The base key that the schema belongs to. This should be NULL
-   *   for the top-level schema and be populated consecutively when recursing
-   *   into the schema structure.
+   * Process the translatable data array with a given language.
+   *
+   * @param string $name
+   *   The configuration name.
+   * @param array $config
+   *   The active configuration data or override data.
+   * @param array $translatable
+   *   The translatable array structure, see this::getTranslatableData().
+   * @param string $langcode
+   *   The language code to process the array with.
+   * @param array $reference_config
+   *   (Optional) Reference configuration to check against if $config was an
+   *   override. This allows us to update locale keys for data not in the
+   *   override but still in the active configuration.
    */
-  protected function traverseSchema(TraversableTypedDataInterface $schema, Config $source_config, LanguageConfigOverride $translation_config, $callable, $base_key = NULL) {
-    foreach ($schema as $key => $element) {
-      $element_key = implode('.', array_filter([$base_key, $key]));
-
-      // We only care for strings here, so traverse the schema further in the
-      // case of traversable elements.
-      if ($element instanceof TraversableTypedDataInterface) {
-        $this->traverseSchema($element, $source_config, $translation_config, $callable, $element_key);
+  protected function processTranslatableData($name, array $config, array $translatable, $langcode, array $reference_config = array()) {
+    foreach ($translatable as $key => $item) {
+      if (!isset($config[$key])) {
+        if (isset($reference_config[$key])) {
+          $this->resetExistingTranslations($name, $translatable[$key], $reference_config[$key], $langcode);
+        }
+        continue;
+      }
+      if (is_array($item)) {
+        $reference_config = isset($reference_config[$key]) ? $reference_config[$key] : array();
+        $this->processTranslatableData($name, $config[$key], $item, $langcode, $reference_config);
       }
-      // Skip elements which are not translatable.
-      elseif (!empty($element->getDataDefinition()['translatable'])) {
-        $callable(
-          $source_config->get($element_key),
-          $translation_config->getLangcode(),
-          $translation_config->get($element_key)
-        );
+      else {
+        /** @var \Drupal\Core\StringTranslation\TranslationWrapper $item */
+        $this->saveCustomizedTranslation($name, $item->getUntranslatedString(), $item->getOption('context'), $config[$key], $langcode);
       }
     }
   }
 
   /**
-   * Saves a translation string.
-   *
-   * @param string $source_value
-   *   The source string value.
-   * @param string $langcode
-   *   The language code of the translation.
-   * @param string|null $translation_value
-   *   (optional) The translation string value. If omitted, no translation will
-   *   be saved.
+   * Reset existing locale translations to their source values.
+   *
+   * Goes through $translatable to reset any existing translations to the source
+   * string, so prior translations would not reappear in the configuration.
+   *
+   * @param string $name
+   *   The configuration name.
+   * @param array|\Drupal\Core\StringTranslation\TranslationWrapper $translatable
+   *   Either a possibly nested array with TranslationWrapper objects at the
+   *   leaf items or a TranslationWrapper object directly.
+   * @param array|string $reference_config
+   *   Either a possibly nested array with strings at the leaf items or a string
+   *   directly. Only those $translatable items that are also present in
+   *   $reference_config will get translations reset.
+   * @param $langcode
+   *   The language code of the translation being processed.
    */
-  protected function saveTranslation($source_value, $langcode, $translation_value = NULL) {
-    if ($translation_value && ($translation = $this->getTranslation($source_value, $langcode, TRUE))) {
-      if ($translation->isNew() || $translation->getString() != $translation_value) {
-        $translation
-          ->setString($translation_value)
-          ->save();
+  protected function resetExistingTranslations($name, $translatable, $reference_config, $langcode) {
+    if (is_array($translatable)) {
+      foreach ($translatable as $key => $item) {
+        if (isset($reference_config[$key])) {
+          // Process further if the key still exists in the reference active
+          // configuration and the default translation but not the current
+          // configuration override.
+          $this->resetExistingTranslations($name, $item, $reference_config[$key], $langcode);
+        }
       }
     }
+    elseif (!is_array($reference_config)) {
+      /** @var \Drupal\Core\StringTranslation\TranslationWrapper $translatable */
+      $this->saveCustomizedTranslation($name, $translatable->getUntranslatedString(), $translatable->getOption('context'), $reference_config, $langcode);
+    }
   }
 
   /**
    * Saves a translation string and marks it as customized.
    *
-   * @param string $source_value
+   * @param string $name
+   *   The configuration name.
+   * @param string $source
    *   The source string value.
+   * @param string $context
+   *   The source string context.
+   * @param string $translation
+   *   The translation string.
    * @param string $langcode
    *   The language code of the translation.
-   * @param string|null $translation_value
-   *   (optional) The translation string value. If omitted, a customized string
-   *   with the source value will be saved.
-   *
-   * @see \Drupal\locale\LocaleConfigSubscriber::onDelete()
    */
-  protected function saveCustomizedTranslation($source_value, $langcode, $translation_value = NULL) {
-    if ($translation = $this->getTranslation($source_value, $langcode, TRUE)) {
-      if (!isset($translation_value)) {
-        $translation_value = $source_value;
-      }
-      if ($translation->isNew() || $translation->getString() != $translation_value) {
-        $translation
-          ->setString($translation_value)
+  protected function saveCustomizedTranslation($name, $source, $context, $translation, $langcode) {
+    $locale_translation = $this->localeConfigManager->getStringTranslation($name, $langcode, $source, $context);
+    if (!empty($locale_translation)) {
+      // Save this translation as custom if it was a new translation and not the
+      // same as the source. (The interface prefills translation values with the
+      // source). Or if there was an existing translation and the user changed
+      // it (even if it was changed back to the original value). Otherwise the
+      // translation file would be overwritten with the locale copy again later.
+      if (($locale_translation->isNew() && $source != $translation) ||
+          (!$locale_translation->isNew() && $translation != $locale_translation->getString())) {
+        $locale_translation
+          ->setString($translation)
           ->setCustomized(TRUE)
           ->save();
       }
     }
   }
 
-  /**
-   * Deletes a translation string, if it exists.
-   *
-   * @param string $source_value
-   *   The source string value.
-   * @param string $langcode
-   *   The language code of the translation.
-   *
-   * @see \Drupal\locale\LocaleConfigSubscriber::onDelete()
-   */
-  protected function deleteTranslation($source_value, $langcode) {
-    if ($translation = $this->getTranslation($source_value, $langcode, FALSE)) {
-      $translation->delete();
-    }
-  }
-
-  /**
-   * Gets a translation string.
-   *
-   * @param string $source_value
-   *   The source string value.
-   * @param string $langcode
-   *   The language code of the translation.
-   * @param bool $create_fallback
-   *   (optional) By default if a source string could be found and no
-   *   translation in the given language exists yet, a translation object is
-   *   created. This can be circumvented by passing FALSE.
-   *
-   * @return \Drupal\locale\TranslationString|null
-   *   The translation string if one was found or created.
-   */
-  protected function getTranslation($source_value, $langcode, $create_fallback = TRUE) {
-    // There is no point in creating a translation without a source.
-    if ($source_string = $this->stringStorage->findString(['source' => $source_value])) {
-      // Get the translation for this original source string from locale.
-      $conditions = [
-        'lid' => $source_string->lid,
-        'language' => $langcode,
-      ];
-      $translations = $this->stringStorage->getTranslations($conditions + ['translated' => TRUE]);
-      if ($translations) {
-        return reset($translations);
-      }
-      elseif ($create_fallback) {
-        return $this->stringStorage->createTranslation($conditions);
-      }
-    }
-  }
-
 }
diff --git a/core/modules/locale/src/LocaleTypedConfig.php b/core/modules/locale/src/LocaleTypedConfig.php
deleted file mode 100644
index ca31a38..0000000
--- a/core/modules/locale/src/LocaleTypedConfig.php
+++ /dev/null
@@ -1,209 +0,0 @@
-<?php
-
-/**
- * @file
- * Contains \Drupal\locale\LocaleTypedConfig.
- */
-
-namespace Drupal\locale;
-
-use Drupal\Core\Language\LanguageManagerInterface;
-use Drupal\Core\TypedData\ContextAwareInterface;
-use Drupal\Core\TypedData\DataDefinitionInterface;
-use Drupal\Core\Config\Schema\Element;
-use Drupal\Core\Config\TypedConfigManagerInterface;
-use Drupal\Core\TypedData\TraversableTypedDataInterface;
-use Drupal\Core\TypedData\TypedDataInterface;
-
-/**
- * Defines the locale configuration wrapper object.
- */
-class LocaleTypedConfig extends Element {
-
-  /**
-   * The typed configuration data.
-   *
-   * @var \Drupal\Core\Config\Schema\Element
-   */
-  protected $typedConfig;
-
-  /**
-   * The language code for which this is a translation.
-   *
-   * @var string
-   */
-  protected $langcode;
-
-  /**
-   * The locale configuration manager object.
-   *
-   * @var \Drupal\locale\LocaleConfigManager
-   */
-  protected $localeConfig;
-
-  /**
-   * The typed config manager.
-   *
-   * @var \Drupal\Core\Config\TypedConfigManagerInterface
-   */
-  protected $typedConfigManager;
-
-  /**
-   * The language manager.
-   *
-   * @var \Drupal\Core\Language\LanguageManagerInterface
-   */
-  protected $languageManager;
-
-  /**
-   * Constructs a configuration wrapper object.
-   *
-   * @param \Drupal\Core\TypedData\DataDefinitionInterface $definition
-   *   The data definition.
-   * @param string $name
-   *   The configuration object name.
-   * @param string $langcode
-   *   Language code for the source configuration data.
-   * @param \Drupal\locale\LocaleConfigManager $locale_config
-   *   The locale configuration manager object.
-   * @param \Drupal\locale\TypedConfigManagerInterface $typed_config;
-   *   The typed configuration manager interface.
-   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
-   *   The language manager.
-   */
-  public function __construct(DataDefinitionInterface $definition, $name, $langcode, LocaleConfigManager $locale_config, TypedConfigManagerInterface $typed_config, LanguageManagerInterface $language_manager) {
-    parent::__construct($definition, $name);
-    $this->langcode = $langcode;
-    $this->localeConfig = $locale_config;
-    $this->typedConfigManager = $typed_config;
-    $this->languageManager = $language_manager;
-  }
-
-  /**
-   * Gets wrapped typed config object.
-   */
-  public function getTypedConfig() {
-    return $this->typedConfigManager->create($this->definition, $this->value);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function getTranslation($langcode) {
-    $options = array(
-      'source' => $this->langcode,
-      'target' => $langcode,
-    );
-    $data = $this->getElementTranslation($this->getTypedConfig(), $options);
-    return $this->typedConfigManager->create($this->definition, $data);
-  }
-
-  /**
-   * {@inheritdoc}
-   */
-  public function language() {
-    return $this->languageManager->getLanguage($this->langcode);
-  }
-
-  /**
-   * Checks whether we can translate these languages.
-   *
-   * @param string $from_langcode
-   *   Source language code.
-   * @param string $to_langcode
-   *   Destination language code.
-   *
-   * @return bool
-   *   TRUE if this translator supports translations for these languages.
-   */
-  protected function canTranslate($from_langcode, $to_langcode) {
-    if ($from_langcode == 'en') {
-      return TRUE;
-    }
-    return FALSE;
-  }
-
-  /**
-   * Gets translated configuration data for a typed configuration element.
-   *
-   * @param \Drupal\Core\TypedData\TypedDataInterface $element
-   *   Typed configuration element.
-   * @param array $options
-   *   Array with translation options that must contain the keys defined in
-   *   \Drupal\locale\LocaleTypedConfig::translateElement().
-   *
-   * @return array
-   *   Configuration data translated to the requested language if available,
-   *   an empty array otherwise.
-   */
-  protected function getElementTranslation(TypedDataInterface $element, array $options) {
-    $translation = array();
-    if ($element instanceof TraversableTypedDataInterface) {
-      $translation = $this->getArrayTranslation($element, $options);
-    }
-    elseif ($this->translateElement($element, $options)) {
-      $translation = $element->getValue();
-    }
-    return $translation;
-  }
-
-  /**
-   * Gets translated configuration data for a traversable element.
-   *
-   * @param \Drupal\Core\TypedData\TraversableTypedDataInterface $element
-   *   Typed configuration array element.
-   * @param array $options
-   *   Array with translation options that must contain the keys defined in
-   *   \Drupal\locale\LocaleTypedConfig::translateElement().
-   *
-   * @return array
-   *   Configuration data translated to the requested language.
-   */
-  protected function getArrayTranslation(TraversableTypedDataInterface $element, array $options) {
-    $translation = array();
-    foreach ($element as $key => $property) {
-      $value = $this->getElementTranslation($property, $options);
-      if (!empty($value)) {
-        $translation[$key] = $value;
-      }
-    }
-    return $translation;
-  }
-
-  /**
-   * Translates element's value if it fits our translation criteria.
-   *
-   * For an element to be translatable by locale module it needs to be of base
-   * type 'string' and have 'translatable = TRUE' in the element's definition.
-   * Translatable elements may use these additional keys in their data
-   * definition:
-   * - 'translatable', FALSE to opt out of translation.
-   * - 'translation context', to define the string context.
-   *
-   * @param \Drupal\Core\TypedData\TypedDataInterface $element
-   *   Configuration element.
-   * @param array $options
-   *   Array with translation options that must contain the following keys:
-   *   - 'source', Source language code.
-   *   - 'target', Target language code.
-   *
-   * @return bool
-   *   Whether the element fits the translation criteria.
-   */
-  protected function translateElement(TypedDataInterface $element, array $options) {
-    if ($this->canTranslate($options['source'], $options['target'])) {
-      $definition = $element->getDataDefinition();
-      $value = $element->getValue();
-      if ($value && !empty($definition['translatable'])) {
-        $context = isset($definition['translation context']) ? $definition['translation context'] : '';
-        if ($translation = $this->localeConfig->translateString($this->name, $options['target'], $value, $context)) {
-          $element->setValue($translation);
-          return TRUE;
-        }
-      }
-    }
-    // The element does not have a translation.
-    return FALSE;
-  }
-
-}
diff --git a/core/modules/locale/src/Tests/LocaleConfigManagerTest.php b/core/modules/locale/src/Tests/LocaleConfigManagerTest.php
index 4cc9fb2..58d63cc 100644
--- a/core/modules/locale/src/Tests/LocaleConfigManagerTest.php
+++ b/core/modules/locale/src/Tests/LocaleConfigManagerTest.php
@@ -28,16 +28,16 @@ class LocaleConfigManagerTest extends KernelTestBase {
    * Tests hasTranslation().
    */
   public function testHasTranslation() {
-    $this->installSchema('locale', array('locales_location'));
+    $this->installSchema('locale', array('locales_location', 'locales_source', 'locales_target'));
     $this->installConfig(array('locale_test'));
-    $locale_config_manager = \Drupal::service('locale.config.typed');
+    $locale_config_manager = \Drupal::service('locale.config_manager');
 
     $language = ConfigurableLanguage::createFromLangcode('de');
     $language->save();
-    $result = $locale_config_manager->hasTranslation('locale_test.no_translation', $language);
+    $result = $locale_config_manager->hasTranslation('locale_test.no_translation', $language->getId());
     $this->assertFalse($result, 'There is no translation for locale_test.no_translation configuration.');
 
-    $result = $locale_config_manager->hasTranslation('locale_test.translation', $language);
+    $result = $locale_config_manager->hasTranslation('locale_test.translation', $language->getId());
     $this->assertTrue($result, 'There is a translation for locale_test.translation configuration.');
   }
 }
diff --git a/core/modules/locale/src/Tests/LocaleConfigSubscriberForeignTest.php b/core/modules/locale/src/Tests/LocaleConfigSubscriberForeignTest.php
new file mode 100644
index 0000000..289333e
--- /dev/null
+++ b/core/modules/locale/src/Tests/LocaleConfigSubscriberForeignTest.php
@@ -0,0 +1,170 @@
+<?php
+
+/**
+ * @file
+ * Contains \Drupal\locale\Tests\LocaleConfigSubscriberForeignTest.
+ */
+
+namespace Drupal\locale\Tests;
+
+use Drupal\Core\Language\Language;
+use Drupal\language\Entity\ConfigurableLanguage;
+
+/**
+ * Tests default configuration handling with a foreign default language.
+ *
+ * @group locale
+ */
+class LocaleConfigSubscriberForeignTest extends LocaleConfigSubscriberTest {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function defaultLanguageData() {
+    $data = Language::$defaultValues;
+    $data['id'] = 'hu';
+    $data['name'] = 'Hungarian';
+    return $data;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUpLanguages() {
+    parent::setUpLanguages();
+    ConfigurableLanguage::createFromLangcode('hu')->save();
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function setUpLocale() {
+    parent::setUpLocale();
+    $this->setUpTranslation('locale_test.translation', 'test', 'English test', 'Hungarian test', 'hu', TRUE);
+  }
+
+  /**
+   * Tests that the language of default configuration was updated.
+   */
+  public function testDefaultConfigLanguage() {
+    $this->assertEqual('hu', $this->configFactory->getEditable('locale_test.no_translation')->get('langcode'));
+    $this->assertEqual('hu', $this->configFactory->getEditable('locale_test.translation')->get('langcode'));
+    $this->assertEqual($this->configFactory->getEditable('locale_test.translation')->get('test'), 'Hungarian test');
+  }
+
+  /**
+   * Tests creating translations of shipped configuration.
+   */
+  public function testCreateActiveTranslation() {
+    $config_name = 'locale_test.no_translation';
+    $this->saveLanguageActive($config_name, 'test', 'Test (Hungarian)', 'hu');
+    $this->assertTranslation($config_name, 'Test (Hungarian)', 'hu');
+  }
+
+  /**
+   * Tests importing community translations of shipped configuration.
+   */
+  public function testLocaleCreateActiveTranslation() {
+    $config_name = 'locale_test.no_translation';
+    $this->saveLocaleTranslationData($config_name, 'test', 'Test', 'Test (Hungarian)', 'hu', TRUE);
+    $this->assertTranslation($config_name, 'Test (Hungarian)', 'hu', FALSE);
+  }
+
+  /**
+   * Tests updating translations of shipped configuration.
+   */
+  public function testUpdateActiveTranslation() {
+    $config_name = 'locale_test.translation';
+    $this->saveLanguageActive($config_name, 'test', 'Updated Hungarian test', 'hu');
+    $this->assertTranslation($config_name, 'Updated Hungarian test', 'hu');
+  }
+
+  /**
+   * Tests updating community translations of shipped configuration.
+   */
+  public function testLocaleUpdateActiveTranslation() {
+    $config_name = 'locale_test.translation';
+    $this->saveLocaleTranslationData($config_name, 'test', 'English test', 'Updated Hungarian test', 'hu', TRUE);
+    $this->assertTranslation($config_name, 'Updated Hungarian test', 'hu', FALSE);
+  }
+
+  /**
+   * @inheritdoc
+   */
+  public function testDeleteTranslation() {
+    $config_name = 'locale_test.translation';
+    $this->deleteLanguageOverride($config_name, 'test', 'English test', 'de');
+    // The German translation in this case will be forced to the Hungarian
+    // source so its not overwritten with locale data later.
+    $this->assertTranslation($config_name, 'Hungarian test', 'de');
+  }
+
+  /**
+   * Tests deleting translations of shipped configuration.
+   */
+  public function testDeleteActiveTranslation() {
+    $config_name = 'locale_test.translation';
+    $this->configFactory->getEditable($config_name)->delete();
+    // Deleting active configuration should not change the locale translation.
+    $this->assertTranslation($config_name, 'Hungarian test', 'hu', FALSE);
+  }
+
+  /**
+   * Tests deleting community translations of shipped configuration.
+   */
+  public function testLocaleDeleteActiveTranslation() {
+    $config_name = 'locale_test.translation';
+    $this->deleteLocaleTranslationData($config_name, 'test', 'English test', 'hu');
+    // Deleting the locale translation should not change active config.
+    $this->assertEqual($this->configFactory->getEditable($config_name)->get('test'), 'Hungarian test');
+  }
+
+  /**
+   * Tests that adding English creates a translation override.
+   */
+  public function testEnglish() {
+    $config_name = 'locale_test.translation';
+    ConfigurableLanguage::createFromLangcode('en')->save();
+    // Adding a language on the UI would normally call updateConfigTranslations.
+    $this->localeConfigManager->updateConfigTranslations(array($config_name), array('en'));
+    $this->assertConfigOverride($config_name, 'test', 'English test', 'en');
+
+    $this->configFactory->getEditable('locale.settings')->set('translate_english', TRUE)->save();
+    $this->saveLocaleTranslationData($config_name, 'test', 'English test', 'Updated English test', 'en');
+    $this->assertTranslation($config_name, 'Updated English test', 'en', FALSE);
+
+    $this->saveLanguageOverride($config_name, 'test', 'Updated English', 'en');
+    $this->assertTranslation($config_name, 'Updated English', 'en');
+
+    $this->deleteLocaleTranslationData($config_name, 'test', 'English test', 'en');
+    $this->assertNoConfigOverride($config_name, 'en');
+  }
+
+  /**
+   * Saves a language override.
+   *
+   * This will invoke LocaleConfigSubscriber through the event dispatcher. To
+   * make sure the configuration was persisted correctly, the configuration
+   * value is checked. Because LocaleConfigSubscriber temporarily disables the
+   * override state of the configuration factory we check that the correct value
+   * is restored afterwards.
+   *
+   * @param string $config_name
+   *   The configuration name.
+   * @param string $key
+   *   The configuration key.
+   * @param string $value
+   *   The configuration value to save.
+   * @param string $langcode
+   *   The language code.
+   */
+  protected function saveLanguageActive($config_name, $key, $value, $langcode) {
+    $this
+      ->configFactory
+      ->getEditable($config_name)
+      ->set($key, $value)
+      ->save();
+    $this->assertActiveConfig($config_name, $key, $value, $langcode);
+  }
+
+}
diff --git a/core/modules/locale/src/Tests/LocaleConfigSubscriberTest.php b/core/modules/locale/src/Tests/LocaleConfigSubscriberTest.php
index f3f7415..24017fd 100644
--- a/core/modules/locale/src/Tests/LocaleConfigSubscriberTest.php
+++ b/core/modules/locale/src/Tests/LocaleConfigSubscriberTest.php
@@ -22,7 +22,7 @@ class LocaleConfigSubscriberTest extends KernelTestBase {
   /**
    * {@inheritdoc}
    */
-  public static $modules = ['language', 'locale', 'locale_test'];
+  public static $modules = ['language', 'locale'];
 
   /**
    * The configurable language manager used in this test.
@@ -53,27 +53,52 @@ class LocaleConfigSubscriberTest extends KernelTestBase {
   protected $localeConfigManager;
 
   /**
-   * The language code used in this test.
-   *
-   * @var string
-   */
-  protected $langcode = 'de';
-
-  /**
    * {@inheritdoc}
    */
   protected function setUp() {
     parent::setUp();
 
-    $this->languageManager = $this->container->get('language_manager');
-    $this->configFactory = $this->container->get('config.factory');
-    $this->stringStorage = $this->container->get('locale.storage');
-    $this->localeConfigManager = $this->container->get('locale.config.typed');
+    $this->setUpDefaultLanguage();
 
     $this->installSchema('locale', ['locales_source', 'locales_target', 'locales_location']);
 
+    $this->setupLanguages();
+
+    $this->enableModules(['locale_test']);
     $this->installConfig(['locale_test']);
-    ConfigurableLanguage::createFromLangcode($this->langcode)->save();
+    // Simulate this hook invoked which would happen if in a non-kernel test
+    // or normal environment.
+    locale_modules_installed(array('locale_test'));
+
+    $this->configFactory = $this->container->get('config.factory');
+    $this->stringStorage = $this->container->get('locale.storage');
+    $this->localeConfigManager = $this->container->get('locale.config_manager');
+    $this->languageManager = $this->container->get('language_manager');
+
+    $this->setUpLocale();
+  }
+
+  /**
+   * Sets up default language for this test.
+   */
+  protected function setUpDefaultLanguage() {
+    // Keep the default English.
+  }
+
+  /**
+   * Sets up languages needed for this test.
+   */
+  protected function setUpLanguages() {
+    ConfigurableLanguage::createFromLangcode('de')->save();
+  }
+
+  /**
+   * Sets up the locale storage strings to be in line with configuration.
+   */
+  protected function setUpLocale() {
+    // Set up the locale database the same way we have in the config samples.
+    $this->setUpNoTranslation('locale_test.no_translation', 'test', 'Test', 'de');
+    $this->setUpTranslation('locale_test.translation', 'test', 'English test', 'German test', 'de');
   }
 
   /**
@@ -82,9 +107,8 @@ protected function setUp() {
   public function testCreateTranslation() {
     $config_name = 'locale_test.no_translation';
 
-    $this->setUpNoTranslation($config_name, 'test', 'Test');
-    $this->saveLanguageOverride($config_name, 'test', 'Test (German)');
-    $this->assertTranslation($config_name, 'Test (German)');
+    $this->saveLanguageOverride($config_name, 'test', 'Test (German)', 'de');
+    $this->assertTranslation($config_name, 'Test (German)', 'de');
   }
 
   /**
@@ -93,9 +117,8 @@ public function testCreateTranslation() {
   public function testLocaleCreateTranslation() {
     $config_name = 'locale_test.no_translation';
 
-    $this->setUpNoTranslation($config_name, 'test', 'Test');
-    $this->saveLocaleTranslationData($config_name, 'test', 'Test (German)');
-    $this->assertTranslation($config_name, 'Test (German)', FALSE);
+    $this->saveLocaleTranslationData($config_name, 'test', 'Test', 'Test (German)', 'de');
+    $this->assertTranslation($config_name, 'Test (German)', 'de', FALSE);
   }
 
   /**
@@ -104,9 +127,8 @@ public function testLocaleCreateTranslation() {
   public function testUpdateTranslation() {
     $config_name = 'locale_test.translation';
 
-    $this->setUpTranslation($config_name, 'test', 'English test', 'German test');
-    $this->saveLanguageOverride($config_name, 'test', 'Updated German test');
-    $this->assertTranslation($config_name, 'Updated German test');
+    $this->saveLanguageOverride($config_name, 'test', 'Updated German test', 'de');
+    $this->assertTranslation($config_name, 'Updated German test', 'de');
   }
 
   /**
@@ -115,9 +137,8 @@ public function testUpdateTranslation() {
   public function testLocaleUpdateTranslation() {
     $config_name = 'locale_test.translation';
 
-    $this->setUpTranslation($config_name, 'test', 'English test', 'German test');
-    $this->saveLocaleTranslationData($config_name, 'test', 'Updated German test');
-    $this->assertTranslation($config_name, 'Updated German test', FALSE);
+    $this->saveLocaleTranslationData($config_name, 'test', 'English test', 'Updated German test', 'de');
+    $this->assertTranslation($config_name, 'Updated German test', 'de', FALSE);
   }
 
   /**
@@ -126,12 +147,11 @@ public function testLocaleUpdateTranslation() {
   public function testDeleteTranslation() {
     $config_name = 'locale_test.translation';
 
-    $this->setUpTranslation($config_name, 'test', 'English test', 'German test');
-    $this->deleteLanguageOverride($config_name, 'test', 'English test');
+    $this->deleteLanguageOverride($config_name, 'test', 'English test', 'de');
     // Instead of deleting the translation, we need to keep a translation with
     // the source value and mark it as customized to prevent the deletion being
     // reverted by importing community translations.
-    $this->assertTranslation($config_name, 'English test');
+    $this->assertTranslation($config_name, 'English test', 'de');
   }
 
   /**
@@ -140,9 +160,8 @@ public function testDeleteTranslation() {
   public function testLocaleDeleteTranslation() {
     $config_name = 'locale_test.translation';
 
-    $this->setUpTranslation($config_name, 'test', 'English test', 'German test');
-    $this->deleteLocaleTranslationData($config_name, 'test', 'English test');
-    $this->assertNoTranslation($config_name, 'English test', FALSE);
+    $this->deleteLocaleTranslationData($config_name, 'test', 'English test', 'de');
+    $this->assertNoTranslation($config_name, 'de');
   }
 
   /**
@@ -159,16 +178,13 @@ public function testLocaleDeleteTranslation() {
    *   The configuration key.
    * @param string $source
    *   The source string.
+   * @param string $langcode
+   *   The language code.
    */
-  protected function setUpNoTranslation($config_name, $key, $source) {
-    // Add a source string with the configuration name as a location. This gets
-    // called from locale_config_update_multiple() normally.
-    $this->localeConfigManager->translateString($config_name, $this->langcode, $source, '');
-    $this->languageManager
-      ->setConfigOverrideLanguage(ConfigurableLanguage::load($this->langcode));
-
-    $this->assertConfigValue($config_name, $key, $source);
-    $this->assertNoTranslation($config_name);
+  protected function setUpNoTranslation($config_name, $key, $source, $langcode) {
+    $this->localeConfigManager->updateConfigTranslations(array($config_name), array($langcode));
+    $this->assertNoConfigOverride($config_name, $key, $source, $langcode);
+    $this->assertNoTranslation($config_name, $langcode);
   }
 
 
@@ -188,27 +204,33 @@ protected function setUpNoTranslation($config_name, $key, $source) {
    *   The source string.
    * @param string $translation
    *   The translation string.
+   * @param string $langcode
+   *   The language code.
+   * @param bool $is_active
+   *   Whether the update will affect the active configuration.
    */
-  protected function setUpTranslation($config_name, $key, $source, $translation) {
+  protected function setUpTranslation($config_name, $key, $source, $translation, $langcode, $is_active = FALSE) {
     // Create source and translation strings for the configuration value and add
     // the configuration name as a location. This would be performed by
-    // locale_translate_batch_import() and locale_config_update_multiple()
-    // normally.
-    $source_object = $this->stringStorage->createString([
-      'source' => $source,
-      'context' => '',
-    ])->save();
-    $this->stringStorage->createTranslation([
-      'lid' => $source_object->getId(),
-      'language' => $this->langcode,
-      'translation' => $translation,
-    ])->save();
-    $this->localeConfigManager->translateString($config_name, $this->langcode, $source, '');
-    $this->languageManager
-      ->setConfigOverrideLanguage(ConfigurableLanguage::load($this->langcode));
-
-    $this->assertConfigValue($config_name, $key, $translation);
-    $this->assertTranslation($config_name, $translation, FALSE);
+    // locale_translate_batch_import() invoking
+    // LocaleConfigManager::updateConfigTranslations() normally.
+    $this->localeConfigManager->reset();
+    $this->localeConfigManager
+      ->getStringTranslation($config_name, $langcode, $source, '')
+      ->setString($translation)
+      ->setCustomized(FALSE)
+      ->save();
+    $this->configFactory->reset($config_name);
+    $this->localeConfigManager->reset();
+    $this->localeConfigManager->updateConfigTranslations(array($config_name), array($langcode));
+
+    if ($is_active) {
+      $this->assertActiveConfig($config_name, $key, $translation, $langcode);
+    }
+    else {
+      $this->assertConfigOverride($config_name, $key, $translation, $langcode);
+    }
+    $this->assertTranslation($config_name, $translation, $langcode, FALSE);
   }
 
   /**
@@ -226,16 +248,18 @@ protected function setUpTranslation($config_name, $key, $source, $translation) {
    *   The configuration key.
    * @param string $value
    *   The configuration value to save.
+   * @param string $langcode
+   *   The language code.
    */
-  protected function saveLanguageOverride($config_name, $key, $value) {
+  protected function saveLanguageOverride($config_name, $key, $value, $langcode) {
     $translation_override = $this->languageManager
-      ->getLanguageConfigOverride($this->langcode, $config_name);
+      ->getLanguageConfigOverride($langcode, $config_name);
     $translation_override
       ->set($key, $value)
       ->save();
     $this->configFactory->reset($config_name);
 
-    $this->assertConfigValue($config_name, $key, $value);
+    $this->assertConfigOverride($config_name, $key, $value, $langcode);
   }
 
   /**
@@ -251,15 +275,31 @@ protected function saveLanguageOverride($config_name, $key, $value) {
    *   The configuration name.
    * @param string $key
    *   The configuration key.
-   * @param string $value
-   *   The configuration value to save.
+   * @param string $source
+   *   The source string.
+   * @param string $translation
+   *   The translation string to save.
+   * @param string $langcode
+   *   The language code.
+   * @param bool $is_active
+   *   Whether the update will affect the active configuration.
    */
-  protected function saveLocaleTranslationData($config_name, $key, $value) {
+  protected function saveLocaleTranslationData($config_name, $key, $source, $translation, $langcode, $is_active = FALSE) {
+    $this->localeConfigManager->reset();
     $this->localeConfigManager
-      ->saveTranslationData($config_name, $this->langcode, [$key => $value]);
+      ->getStringTranslation($config_name, $langcode, $source, '')
+      ->setString($translation)
+      ->save();
+    $this->localeConfigManager->reset();
+    $this->localeConfigManager->updateConfigTranslations(array($config_name), array($langcode));
     $this->configFactory->reset($config_name);
 
-    $this->assertConfigValue($config_name, $key, $value);
+    if ($is_active) {
+      $this->assertActiveConfig($config_name, $key, $translation, $langcode);
+    }
+    else {
+      $this->assertConfigOverride($config_name, $key, $translation, $langcode);
+    }
   }
 
   /**
@@ -278,16 +318,18 @@ protected function saveLocaleTranslationData($config_name, $key, $value) {
    * @param string $source_value
    *   The source configuration value to verify the correct value is returned
    *   from the configuration factory after the deletion.
+   * @param string $langcode
+   *   The language code.
    */
-  protected function deleteLanguageOverride($config_name, $key, $source_value) {
+  protected function deleteLanguageOverride($config_name, $key, $source_value, $langcode) {
     $translation_override = $this->languageManager
-      ->getLanguageConfigOverride($this->langcode, $config_name);
+      ->getLanguageConfigOverride($langcode, $config_name);
     $translation_override
       ->clear($key)
       ->save();
     $this->configFactory->reset($config_name);
 
-    $this->assertConfigValue($config_name, $key, $source_value);
+    $this->assertNoConfigOverride($config_name, $key, $source_value, $langcode);
   }
 
   /**
@@ -306,31 +348,78 @@ protected function deleteLanguageOverride($config_name, $key, $source_value) {
    * @param string $source_value
    *   The source configuration value to verify the correct value is returned
    *   from the configuration factory after the deletion.
+   * @param string $langcode
+   *   The language code.
    */
-  protected function deleteLocaleTranslationData($config_name, $key, $source_value) {
-    $this->localeConfigManager->deleteTranslationData($config_name, $this->langcode);
+  protected function deleteLocaleTranslationData($config_name, $key, $source_value, $langcode) {
+    $this->localeConfigManager
+      ->getStringTranslation($config_name, $langcode, $source_value, '')
+      ->delete();
+    $this->localeConfigManager->reset();
+    $this->localeConfigManager->updateConfigTranslations(array($config_name), array($langcode));
     $this->configFactory->reset($config_name);
 
-    $this->assertConfigValue($config_name, $key, $source_value);
+    $this->assertNoConfigOverride($config_name, $key, $source_value, $langcode);
+  }
+
+  /**
+   * Ensures configuration override is not present anymore.
+   *
+   * @param string $config_name
+   *   The configuration name.
+   * @param string $langcode
+   *   The language code.
+   *
+   * @return bool
+   *   TRUE if the assertion succeeded, FALSE otherwise.
+   */
+  protected function assertNoConfigOverride($config_name, $langcode) {
+    $config_langcode = $this->configFactory->getEditable($config_name)->get('langcode');
+    $override = $this->languageManager->getLanguageConfigOverride($langcode, $config_name);
+    return $this->assertNotEqual($config_langcode, $langcode) && $this->assertEqual($override->isNew(), TRUE);
+  }
+
+  /**
+   * Ensures configuration was saved correctly.
+   *
+   * @param string $config_name
+   *   The configuration name.
+   * @param string $key
+   *   The configuration key.
+   * @param string $value
+   *   The configuration value.
+   * @param string $langcode
+   *   The language code.
+   *
+   * @return bool
+   *   TRUE if the assertion succeeded, FALSE otherwise.
+   */
+  protected function assertConfigOverride($config_name, $key, $value, $langcode) {
+    $config_langcode = $this->configFactory->getEditable($config_name)->get('langcode');
+    $override = $this->languageManager->getLanguageConfigOverride($langcode, $config_name);
+    return $this->assertNotEqual($config_langcode, $langcode) && $this->assertEqual($override->get($key), $value);
   }
 
   /**
    * Ensures configuration was saved correctly.
    *
-   * @param $config_name
+   * @param string $config_name
    *   The configuration name.
-   * @param $key
+   * @param string $key
    *   The configuration key.
-   * @param $value
+   * @param string $value
    *   The configuration value.
+   * @param string $langcode
+   *   The language code.
    *
    * @return bool
    *   TRUE if the assertion succeeded, FALSE otherwise.
    */
-  protected function assertConfigValue($config_name, $key, $value) {
-    // Make sure the configuration was translated correctly.
-    $translation_config = $this->configFactory->get($config_name);
-    return $this->assertIdentical($value, $translation_config->get($key));
+  protected function assertActiveConfig($config_name, $key, $value, $langcode) {
+    $config = $this->configFactory->getEditable($config_name);
+    return
+      $this->assertEqual($config->get('langcode'), $langcode) &&
+      $this->assertIdentical($config->get($key), $value);
   }
 
   /**
@@ -338,15 +427,17 @@ protected function assertConfigValue($config_name, $key, $value) {
    *
    * @param string $config_name
    *   The configuration name.
+   * @param string $langcode
+   *   The language code.
    *
    * @return bool
    *   TRUE if the assertion succeeded, FALSE otherwise.
    */
-  protected function assertNoTranslation($config_name) {
+  protected function assertNoTranslation($config_name, $langcode) {
     $strings = $this->stringStorage->getTranslations([
       'type' => 'configuration',
       'name' => $config_name,
-      'language' => $this->langcode,
+      'language' => $langcode,
       'translated' => TRUE,
     ]);
     return $this->assertIdentical([], $strings);
@@ -359,6 +450,8 @@ protected function assertNoTranslation($config_name) {
    *   The configuration name.
    * @param string $translation
    *   The translation.
+   * @param string $langcode
+   *   The language code.
    * @param bool $customized
    *   Whether or not the string should be asserted to be customized or not
    *   customized.
@@ -366,12 +459,12 @@ protected function assertNoTranslation($config_name) {
    * @return bool
    *   TRUE if the assertion succeeded, FALSE otherwise.
    */
-  protected function assertTranslation($config_name, $translation, $customized = TRUE) {
+  protected function assertTranslation($config_name, $translation, $langcode, $customized = TRUE) {
     // Make sure a string exists.
     $strings = $this->stringStorage->getTranslations([
       'type' => 'configuration',
       'name' => $config_name,
-      'language' => $this->langcode,
+      'language' => $langcode,
       'translated' => TRUE,
     ]);
     $pass = $this->assertIdentical(1, count($strings));
diff --git a/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php b/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php
index 27f120d..d5a7c21 100644
--- a/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php
+++ b/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php
@@ -79,12 +79,10 @@ public function testConfigTranslation() {
     );
     $this->drupalPostForm('admin/config/regional/translate', $edit, t('Save translations'));
 
-    $wrapper = $this->container->get('locale.config.typed')->get('system.site');
-
     // Get translation and check we've only got the site name.
-    $translation = $wrapper->getTranslation($langcode);
-    $properties = $translation->getElements();
-    $this->assertEqual(count($properties), 1, 'Got the right number of properties after translation');
+    $translation = \Drupal::languageManager()->getLanguageConfigOverride($langcode, 'system.site')->get();
+    $this->assertEqual(count($translation), 1, 'Got the right number of properties after translation.');
+    $this->assertEqual($translation['name'], $site_name, 'Got the right translation for the site name.');
 
     // Check the translated site name is displayed.
     $this->drupalGet($langcode);
@@ -109,12 +107,8 @@ public function testConfigTranslation() {
     );
     $this->drupalPostForm('admin/config/regional/translate', $edit, t('Save translations'));
 
-    $wrapper = $this->container->get('locale.config.typed')->get('core.date_format.medium');
-
-    // Get translation and check we've only got the site name.
-    $translation = $wrapper->getTranslation($langcode);
-    $format = $translation->get('pattern')->getValue();
-    $this->assertEqual($format, 'D', 'Got the right date format pattern after translation.');
+    $translation = \Drupal::languageManager()->getLanguageConfigOverride($langcode, 'core.date_format.medium')->get();
+    $this->assertEqual($translation['pattern'], 'D', 'Got the right date format pattern after translation.');
 
     // Formatting the date 8 / 27 / 1985 @ 13:37 EST with pattern D should
     // display "Tue".
@@ -162,15 +156,8 @@ public function testConfigTranslation() {
     $this->assertTrue(count($translations) == 1 && $translation->source == $string->source && $translation->translation == $image_style_label, 'Got only one translation for image configuration.');
 
     // Try more complex configuration data.
-    $wrapper = $this->container->get('locale.config.typed')->get('image.style.medium');
-
-    $translation = $wrapper->getTranslation($langcode);
-    $property = $translation->get('label');
-    $this->assertEqual($property->getValue(), $image_style_label, 'Got the right translation for image style name after translation');
-
-    // Quick test to ensure translation file exists.
-    $override = \Drupal::languageManager()->getLanguageConfigOverride('xx', 'image.style.medium');
-    $this->assertEqual($override->get('label'), $image_style_label);
+    $translation = \Drupal::languageManager()->getLanguageConfigOverride($langcode, 'image.style.medium')->get();
+    $this->assertEqual($translation['label'], $image_style_label, 'Got the right translation for image style name after translation');
 
     // Uninstall the module.
     $this->drupalPostForm('admin/modules/uninstall', array('uninstall[image]' => "image"), t('Uninstall'));
diff --git a/core/modules/locale/tests/modules/locale_test/config/schema/locale_test.schema.yml b/core/modules/locale/tests/modules/locale_test/config/schema/locale_test.schema.yml
index 5e7e056..daf3391 100644
--- a/core/modules/locale/tests/modules/locale_test/config/schema/locale_test.schema.yml
+++ b/core/modules/locale/tests/modules/locale_test/config/schema/locale_test.schema.yml
@@ -9,6 +9,8 @@ locale_test.no_translation:
       label: 'Test'
       # See \Drupal\locale\Tests\LocaleConfigSubscriberTest
       translatable: true
+    langcode:
+      type: string
 
 locale_test.translation:
   type: mapping
@@ -19,3 +21,5 @@ locale_test.translation:
       label: 'Test'
       # See \Drupal\locale\Tests\LocaleConfigSubscriberTest
       translatable: true
+    langcode:
+      type: string
diff --git a/core/modules/simpletest/src/KernelTestBase.php b/core/modules/simpletest/src/KernelTestBase.php
index 14b839b..4f20263 100644
--- a/core/modules/simpletest/src/KernelTestBase.php
+++ b/core/modules/simpletest/src/KernelTestBase.php
@@ -290,7 +290,7 @@ public function containerBuild(ContainerBuilder $container) {
     $this->container = $container;
 
     // Set the default language on the minimal container.
-    $this->container->setParameter('language.default_values', Language::$defaultValues);
+    $this->container->setParameter('language.default_values', $this->defaultLanguageData());
 
     $container->register('lock', 'Drupal\Core\Lock\NullLockBackend');
     $container->register('cache_factory', 'Drupal\Core\Cache\MemoryBackendFactory');
@@ -359,6 +359,16 @@ public function containerBuild(ContainerBuilder $container) {
   }
 
   /**
+   * Provides the data for setting the default language on the container.
+   *
+   * @return array
+   *   The data array for the default language.
+   */
+  protected function defaultLanguageData() {
+    return Language::$defaultValues;
+  }
+
+  /**
    * Installs default configuration for a given list of modules.
    *
    * @param array $modules
diff --git a/core/modules/system/src/Tests/Installer/InstallerTranslationMultipleLanguageForeignTest.php b/core/modules/system/src/Tests/Installer/InstallerTranslationMultipleLanguageForeignTest.php
index 248d485..918e85e 100644
--- a/core/modules/system/src/Tests/Installer/InstallerTranslationMultipleLanguageForeignTest.php
+++ b/core/modules/system/src/Tests/Installer/InstallerTranslationMultipleLanguageForeignTest.php
@@ -26,7 +26,7 @@ class InstallerTranslationMultipleLanguageForeignTest extends InstallerTranslati
    */
   protected function setUpLanguage() {
     parent::setUpLanguage();
-    $this->translations['Save and continue'] = 'Save and continue German';
+    $this->translations['Save and continue'] = 'Save and continue de';
   }
 
 }
diff --git a/core/modules/system/src/Tests/Installer/InstallerTranslationMultipleLanguageKeepEnglishTest.php b/core/modules/system/src/Tests/Installer/InstallerTranslationMultipleLanguageKeepEnglishTest.php
index 0c01fe6..79085db 100644
--- a/core/modules/system/src/Tests/Installer/InstallerTranslationMultipleLanguageKeepEnglishTest.php
+++ b/core/modules/system/src/Tests/Installer/InstallerTranslationMultipleLanguageKeepEnglishTest.php
@@ -2,51 +2,23 @@
 
 /**
  * @file
- * Contains \Drupal\system\Tests\Installer\InstallerTranslationMultipleLanguageForeignTest.
+ * Contains \Drupal\system\Tests\Installer\InstallerTranslationMultipleLanguageKeepEnglishTest.
  */
 
 namespace Drupal\system\Tests\Installer;
-use Drupal\simpletest\InstallerTestBase;
 
 /**
- * Tests translation files for multiple languages get imported during install.
+ * Tests that keeping English in a foreign language install works.
  *
  * @group Installer
  */
-class InstallerTranslationMultipleLanguageKeepEnglishTest extends InstallerTestBase {
+class InstallerTranslationMultipleLanguageKeepEnglishTest extends InstallerTranslationMultipleLanguageForeignTest {
 
   /**
-   * Overrides the language code in which to install Drupal.
+   * Switch to the multilingual testing profile with English kept.
    *
    * @var string
    */
-  protected $langcode = 'de';
-
-  /**
-   * Switch to the multilingual testing profile
-   *
-   * @var string
-   */
-  protected $profile = 'testing_multilingual';
-
-  /**
-   * {@inheritdoc}
-   */
-  protected function setUpLanguage() {
-    // Place custom local translations in the translations directory.
-    mkdir(DRUPAL_ROOT . '/' . $this->siteDirectory . '/files/translations', 0777, TRUE);
-    file_put_contents(DRUPAL_ROOT . '/' . $this->siteDirectory . '/files/translations/drupal-8.0.0.de.po', "msgid \"\"\nmsgstr \"\"\nmsgid \"Save and continue\"\nmsgstr \"Save and continue German\"");
-    file_put_contents(DRUPAL_ROOT . '/' . $this->siteDirectory . '/files/translations/drupal-8.0.0.es.po', "msgid \"\"\nmsgstr \"\"\nmsgid \"Save and continue\"\nmsgstr \"Save and continue Spanish\"");
-
-    parent::setUpLanguage();
-    $this->translations['Save and continue'] = 'Save and continue German';
-  }
-
-  /**
-   * Tests that English is still present.
-   */
-  public function testKeepEnglish() {
-    $this->assertTrue((bool) \Drupal::languageManager()->getLanguage('en'), 'English is present.');
-  }
+  protected $profile = 'testing_multilingual_with_english';
 
 }
diff --git a/core/modules/system/src/Tests/Installer/InstallerTranslationMultipleLanguageTest.php b/core/modules/system/src/Tests/Installer/InstallerTranslationMultipleLanguageTest.php
index 94759e4..c1180bf 100644
--- a/core/modules/system/src/Tests/Installer/InstallerTranslationMultipleLanguageTest.php
+++ b/core/modules/system/src/Tests/Installer/InstallerTranslationMultipleLanguageTest.php
@@ -17,7 +17,7 @@
 class InstallerTranslationMultipleLanguageTest extends InstallerTestBase {
 
   /**
-   * Switch to the multilingual testing profile
+   * Switch to the multilingual testing profile.
    *
    * @var string
    */
@@ -29,47 +29,108 @@ class InstallerTranslationMultipleLanguageTest extends InstallerTestBase {
   protected function setUpLanguage() {
     // Place custom local translations in the translations directory.
     mkdir(DRUPAL_ROOT . '/' . $this->siteDirectory . '/files/translations', 0777, TRUE);
-    file_put_contents(DRUPAL_ROOT . '/' . $this->siteDirectory . '/files/translations/drupal-8.0.0.de.po', "msgid \"\"\nmsgstr \"\"\nmsgid \"Save and continue\"\nmsgstr \"Save and continue German\"\nmsgid\"Anonymous\"\nmsgstr\"Anonymous German\"");
-    file_put_contents(DRUPAL_ROOT . '/' . $this->siteDirectory . '/files/translations/drupal-8.0.0.es.po', "msgid \"\"\nmsgstr \"\"\nmsgid \"Save and continue\"\nmsgstr \"Save and continue Spanish\"\nmsgid\"Anonymous\"\nmsgstr\"Anonymous Spanish\"");
+    file_put_contents(DRUPAL_ROOT . '/' . $this->siteDirectory . '/files/translations/drupal-8.0.0.de.po', $this->getPo('de'));
+    file_put_contents(DRUPAL_ROOT . '/' . $this->siteDirectory . '/files/translations/drupal-8.0.0.es.po', $this->getPo('es'));
 
     parent::setUpLanguage();
   }
 
   /**
-   * Tests that translations for each language were loaded.
+   * Returns the string for the test .po file.
+   *
+   * @param string $langcode
+   *   The language code.
+   * @return string
+   *   Contents for the test .po file.
+   */
+  protected function getPo($langcode) {
+    return <<<ENDPO
+msgid ""
+msgstr ""
+
+msgid "Save and continue"
+msgstr "Save and continue $langcode"
+
+msgid "Anonymous"
+msgstr "Anonymous $langcode"
+ENDPO;
+  }
+
+  /**
+   * Tests that translations ended up at the expected places.
    */
   public function testTranslationsLoaded() {
     // Verify German and Spanish were configured.
     $this->drupalGet('admin/config/regional/language');
     $this->assertText('German');
     $this->assertText('Spanish');
-    // If the installer was English, we expect that configured also.
-    if ($this->langcode == 'en') {
+    // If the installer was English or we used a profile that keeps English, we
+    // expect that configured also. Otherwise English should not be configured
+    // on the site.
+    if ($this->langcode == 'en' || $this->profile == 'testing_multilingual_with_english') {
       $this->assertText('English');
     }
+    else {
+      $this->assertNoText('English');
+    }
 
     // Verify the strings from the translation files were imported.
-    $test_samples = array('Save and continue', 'Anonymous');
-    $languages = array(
-      'de' => 'German',
-      'es' => 'Spanish',
-    );
+    $test_samples = ['Save and continue', 'Anonymous'];
+    $langcodes = ['de', 'es'];
 
     foreach($test_samples as $sample) {
-      foreach($languages as $langcode => $name) {
+      foreach($langcodes as $langcode) {
         $edit = array();
         $edit['langcode'] = $langcode;
         $edit['translation'] = 'translated';
         $edit['string'] = $sample;
         $this->drupalPostForm('admin/config/regional/translate', $edit, t('Filter'));
-        $this->assertText($sample . ' ' . $name);
+        $this->assertText($sample . ' ' . $langcode);
       }
     }
 
-    $config = \Drupal::languageManager()->getLanguageConfigOverride('de', 'user.settings');
-    $this->assertEqual($config->get('anonymous'), 'Anonymous German');
-    $config = \Drupal::languageManager()->getLanguageConfigOverride('es', 'user.settings');
-    $this->assertEqual($config->get('anonymous'), 'Anonymous Spanish');
+    /** @var \Drupal\language\ConfigurableLanguageManager $language_manager */
+    $language_manager = \Drupal::languageManager();
+
+    // If the site was installed in a foreign language (only tested with German
+    // in the InstallerTranslationMultipleLanguageForeignTest subclass), then
+    // the active configuration should be updated and no override should exist
+    // in German. Otherwise the German translation should end up in overrides
+    // the same way as Spanish (which is not used as a site installation
+    // language).
+    $config = \Drupal::config('user.settings');
+    $override_de = $language_manager->getLanguageConfigOverride('de', 'user.settings');
+    if ($this->langcode == 'de') {
+      $this->assertEqual($config->get('anonymous'), 'Anonymous de');
+      $this->assertEqual($config->get('langcode'), 'de');
+      $this->assertTrue($override_de->isNew());
+    }
+    else {
+      $this->assertEqual($config->get('anonymous'), 'Anonymous');
+      $this->assertEqual($config->get('langcode'), 'en');
+      $this->assertEqual($override_de->get('anonymous'), 'Anonymous de');
+    }
+
+    // Spanish is always an override (never used as installation language).
+    $override_es = $language_manager->getLanguageConfigOverride('es', 'user.settings');
+    $this->assertEqual($override_es->get('anonymous'), 'Anonymous es');
+
+    // If we used the profile which also keeps English, that should have made
+    // English translation overrides available, otherwise the active
+    // configuration should be English (as asserted above).
+    $override_en = $language_manager->getLanguageConfigOverride('en', 'user.settings');
+    if ($this->langcode == 'de' && $this->profile == 'testing_multilingual_with_english') {
+      $this->assertEqual($override_en->get('anonymous'), 'Anonymous');
+    }
+    else {
+      $this->assertTrue($override_en->isNew());
+
+      // Assert that adding English makes the English override available.
+      $edit = ['predefined_langcode' => 'en'];
+      $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
+      $override_en = $language_manager->getLanguageConfigOverride('en', 'user.settings');
+      $this->assertEqual($override_en->get('anonymous'), 'Anonymous');
+    }
   }
 
 }
diff --git a/core/modules/system/src/Tests/Installer/InstallerTranslationTest.php b/core/modules/system/src/Tests/Installer/InstallerTranslationTest.php
index 8d9305c..f68ccf8 100644
--- a/core/modules/system/src/Tests/Installer/InstallerTranslationTest.php
+++ b/core/modules/system/src/Tests/Installer/InstallerTranslationTest.php
@@ -11,8 +11,7 @@
 use Drupal\user\Entity\User;
 
 /**
- * Selects German as the installation language and verifies the following page
- * is not in English.
+ * Installs Drupal in German and checks resulting site.
  *
  * @group Installer
  */
@@ -31,14 +30,15 @@ class InstallerTranslationTest extends InstallerTestBase {
   protected function setUpLanguage() {
     // Place a custom local translation in the translations directory.
     mkdir(\Drupal::root() . '/' . $this->siteDirectory . '/files/translations', 0777, TRUE);
-    file_put_contents(\Drupal::root() . '/' . $this->siteDirectory . '/files/translations/drupal-8.0.0.de.po', "msgid \"\"\nmsgstr \"\"\nmsgid \"Save and continue\"\nmsgstr \"Save and continue German\"");
+    file_put_contents(\Drupal::root() . '/' . $this->siteDirectory . '/files/translations/drupal-8.0.0.de.po', $this->getPo('de'));
 
     parent::setUpLanguage();
+
     // After selecting a different language than English, all following screens
     // should be translated already.
     $elements = $this->xpath('//input[@type="submit"]/@value');
-    $this->assertEqual((string) current($elements), 'Save and continue German');
-    $this->translations['Save and continue'] = 'Save and continue German';
+    $this->assertEqual((string) current($elements), 'Save and continue de');
+    $this->translations['Save and continue'] = 'Save and continue de';
 
     // Check the language direction.
     $direction = (string) current($this->xpath('/html/@dir'));
@@ -46,12 +46,18 @@ protected function setUpLanguage() {
   }
 
   /**
-   * Verifies that installation succeeded.
+   * Verifies the expected behaviors of the installation result.
    */
   public function testInstaller() {
     $this->assertUrl('user/1');
     $this->assertResponse(200);
 
+    // Verify German was configured but not English.
+    $this->drupalGet('admin/config/regional/language');
+    $this->assertText('German');
+    $this->assertNoText('English');
+
+    /** @var \Drupal\user\Entity\User $account */
     $account = User::load(0);
     $this->assertEqual($account->language()->getId(), 'en', 'Anonymous user is English.');
     $account = User::load(1);
@@ -68,6 +74,57 @@ public function testInstaller() {
     $this->drupalPostForm('admin/config/development/performance', $edit, t('Save configuration'));
     $this->drupalGet('<front>');
     $this->assertRaw('classy/css/layout.css');
+
+    // Verify the strings from the translation files were imported.
+    $test_samples = ['Save and continue', 'Anonymous'];
+    foreach($test_samples as $sample) {
+      $edit = array();
+      $edit['langcode'] = 'de';
+      $edit['translation'] = 'translated';
+      $edit['string'] = $sample;
+      $this->drupalPostForm('admin/config/regional/translate', $edit, t('Filter'));
+      $this->assertText($sample . ' de');
+    }
+
+    /** @var \Drupal\language\ConfigurableLanguageManager $language_manager */
+    $language_manager = \Drupal::languageManager();
+
+    // Installed in German, configuration should be in German. No German or
+    // English overrides should be present.
+    $config = \Drupal::config('user.settings');
+    $override_de = $language_manager->getLanguageConfigOverride('de', 'user.settings');
+    $override_en = $language_manager->getLanguageConfigOverride('en', 'user.settings');
+    $this->assertEqual($config->get('anonymous'), 'Anonymous de');
+    $this->assertEqual($config->get('langcode'), 'de');
+    $this->assertTrue($override_de->isNew());
+    $this->assertTrue($override_en->isNew());
+
+    // Assert that adding English makes the English override available.
+    $edit = ['predefined_langcode' => 'en'];
+    $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add language'));
+    $override_en = $language_manager->getLanguageConfigOverride('en', 'user.settings');
+    $this->assertEqual($override_en->get('anonymous'), 'Anonymous');
+  }
+
+  /**
+   * Returns the string for the test .po file.
+   *
+   * @param string $langcode
+   *   The language code.
+   * @return string
+   *   Contents for the test .po file.
+   */
+  protected function getPo($langcode) {
+    return <<<ENDPO
+msgid ""
+msgstr ""
+
+msgid "Save and continue"
+msgstr "Save and continue $langcode"
+
+msgid "Anonymous"
+msgstr "Anonymous $langcode"
+ENDPO;
   }
 
 }
diff --git a/core/profiles/testing_multilingual/testing_multilingual.info.yml b/core/profiles/testing_multilingual/testing_multilingual.info.yml
index c33045a..3615e0b 100644
--- a/core/profiles/testing_multilingual/testing_multilingual.info.yml
+++ b/core/profiles/testing_multilingual/testing_multilingual.info.yml
@@ -6,4 +6,3 @@ core: 8.x
 hidden: true
 dependencies:
   - locale
-keep_english: true
diff --git a/core/profiles/testing_multilingual_with_english/config/install/language.entity.de.yml b/core/profiles/testing_multilingual_with_english/config/install/language.entity.de.yml
new file mode 100644
index 0000000..22c040a
--- /dev/null
+++ b/core/profiles/testing_multilingual_with_english/config/install/language.entity.de.yml
@@ -0,0 +1,7 @@
+id: de
+label: German
+direction: 'ltr'
+weight: 0
+locked: false
+status: true
+langcode: en
diff --git a/core/profiles/testing_multilingual_with_english/config/install/language.entity.es.yml b/core/profiles/testing_multilingual_with_english/config/install/language.entity.es.yml
new file mode 100644
index 0000000..626b343
--- /dev/null
+++ b/core/profiles/testing_multilingual_with_english/config/install/language.entity.es.yml
@@ -0,0 +1,7 @@
+id: es
+label: Spanish
+direction: 'ltr'
+weight: 0
+locked: false
+status: true
+langcode: en
diff --git a/core/profiles/testing_multilingual_with_english/testing_multilingual_with_english.info.yml b/core/profiles/testing_multilingual_with_english/testing_multilingual_with_english.info.yml
new file mode 100644
index 0000000..70ea627
--- /dev/null
+++ b/core/profiles/testing_multilingual_with_english/testing_multilingual_with_english.info.yml
@@ -0,0 +1,9 @@
+name: 'Testing multilingual with English'
+type: profile
+description: 'Minimal profile for running tests with a multilingual installer.'
+version: VERSION
+core: 8.x
+hidden: true
+dependencies:
+  - locale
+keep_english: true
