Commit 2ca1f2db authored by alexpott's avatar alexpott

Issue #2457551 by Gábor Hojtsy, rteijeiro, keopx, eiriksm, alexpott:...

Issue #2457551 by Gábor Hojtsy, rteijeiro, keopx, eiriksm, alexpott: Regression: optional default configuration is not translatable anymore in locale
parent e1396322
...@@ -264,8 +264,6 @@ services: ...@@ -264,8 +264,6 @@ services:
config.storage.schema: config.storage.schema:
class: Drupal\Core\Config\ExtensionInstallStorage class: Drupal\Core\Config\ExtensionInstallStorage
arguments: ['@config.storage', 'config/schema'] arguments: ['@config.storage', 'config/schema']
config.storage.installer:
class: Drupal\Core\Config\InstallStorage
config.typed: config.typed:
class: Drupal\Core\Config\TypedConfigManager class: Drupal\Core\Config\TypedConfigManager
arguments: ['@config.storage', '@config.storage.schema', '@cache.discovery', '@module_handler'] arguments: ['@config.storage', '@config.storage.schema', '@cache.discovery', '@module_handler']
......
...@@ -233,27 +233,16 @@ public function getLanguageSwitchLinks($type, Url $url) { ...@@ -233,27 +233,16 @@ public function getLanguageSwitchLinks($type, Url $url) {
} }
/** /**
* Some common languages with their English and native names. * @inheritdoc
*
* Language codes are defined by the W3C language tags document for
* interoperability. Language codes typically have a language and, optionally,
* a script or regional variant name. See:
* http://www.w3.org/International/articles/language-tags/ for more
* information.
*
* This list is based on languages available from localize.drupal.org. See
* http://localize.drupal.org/issues for information on how to add languages
* there.
*
* The "Left-to-right marker" comments and the enclosed UTF-8 markers are to
* make otherwise strange looking PHP syntax natural (to not be displayed in
* right to left). See http://drupal.org/node/128866#comment-528929.
*
* @return array
* An array of language code to language name information.
* Language name information itself is an array of English and native names.
*/ */
public static function getStandardLanguageList() { public static function getStandardLanguageList() {
// This list is based on languages available from localize.drupal.org. See
// http://localize.drupal.org/issues for information on how to add languages
// there.
//
// The "Left-to-right marker" comments and the enclosed UTF-8 markers are to
// make otherwise strange looking PHP syntax natural (to not be displayed in
// right to left). See http://drupal.org/node/128866#comment-528929.
return array( return array(
'af' => array('Afrikaans', 'Afrikaans'), 'af' => array('Afrikaans', 'Afrikaans'),
'am' => array('Amharic', 'አማርኛ'), 'am' => array('Amharic', 'አማርኛ'),
......
...@@ -208,4 +208,19 @@ public function setConfigOverrideLanguage(LanguageInterface $language = NULL); ...@@ -208,4 +208,19 @@ public function setConfigOverrideLanguage(LanguageInterface $language = NULL);
*/ */
public function getConfigOverrideLanguage(); public function getConfigOverrideLanguage();
/**
* Some common languages with their English and native names.
*
* Language codes are defined by the W3C language tags document for
* interoperability. Language codes typically have a language and, optionally,
* a script or regional variant name. See:
* http://www.w3.org/International/articles/language-tags/ for more
* information.
*
* @return array
* An array of language code to language name information. Language name
* information itself is an array of English and native names.
*/
public static function getStandardLanguageList();
} }
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
namespace Drupal\config\Tests; namespace Drupal\config\Tests;
use Drupal\Core\Config\InstallStorage;
use Drupal\Core\Config\PreExistingConfigException; use Drupal\Core\Config\PreExistingConfigException;
use Drupal\Core\Config\StorageInterface; use Drupal\Core\Config\StorageInterface;
use Drupal\Core\Config\UnmetDependenciesException; use Drupal\Core\Config\UnmetDependenciesException;
...@@ -223,7 +224,8 @@ public function testDependencyChecking() { ...@@ -223,7 +224,8 @@ public function testDependencyChecking() {
function testLanguage() { function testLanguage() {
$this->installModules(['config_test_language']); $this->installModules(['config_test_language']);
// Test imported configuration with implicit language code. // Test imported configuration with implicit language code.
$data = $this->container->get('config.storage.installer')->read('config_test.dynamic.dotted.english'); $storage = new InstallStorage();
$data = $storage->read('config_test.dynamic.dotted.english');
$this->assertTrue(!isset($data['langcode'])); $this->assertTrue(!isset($data['langcode']));
$this->assertEqual( $this->assertEqual(
$this->config('config_test.dynamic.dotted.english')->get('langcode'), $this->config('config_test.dynamic.dotted.english')->get('langcode'),
...@@ -231,7 +233,7 @@ function testLanguage() { ...@@ -231,7 +233,7 @@ function testLanguage() {
); );
// Test imported configuration with explicit language code. // Test imported configuration with explicit language code.
$data = $this->container->get('config.storage.installer')->read('config_test.dynamic.dotted.french'); $data = $storage->read('config_test.dynamic.dotted.french');
$this->assertEqual($data['langcode'], 'fr'); $this->assertEqual($data['langcode'], 'fr');
$this->assertEqual( $this->assertEqual(
$this->config('config_test.dynamic.dotted.french')->get('langcode'), $this->config('config_test.dynamic.dotted.french')->get('langcode'),
......
...@@ -407,23 +407,30 @@ function locale_system_update(array $components) { ...@@ -407,23 +407,30 @@ function locale_system_update(array $components) {
// Skip running the translation imports if in the installer, // Skip running the translation imports if in the installer,
// because it would break out of the installer flow. We have // because it would break out of the installer flow. We have
// built-in support for translation imports in the installer. // built-in support for translation imports in the installer.
if (!drupal_installation_attempted() && locale_translatable_language_list() && \Drupal::config('locale.settings')->get('translation.import_enabled')) { if (!drupal_installation_attempted() && locale_translatable_language_list()) {
if (\Drupal::config('locale.settings')->get('translation.import_enabled')) {
module_load_include('compare.inc', 'locale'); module_load_include('compare.inc', 'locale');
// Update the list of translatable projects and start the import batch. // Update the list of translatable projects and start the import batch.
// Only when new projects are added the update batch will be triggered. Not // Only when new projects are added the update batch will be triggered.
// each enabled module will introduce a new project. E.g. sub modules. // Not each enabled module will introduce a new project. E.g. sub modules.
$projects = array_keys(locale_translation_build_projects()); $projects = array_keys(locale_translation_build_projects());
if ($list = array_intersect($list, $projects)) { if ($list = array_intersect($list, $projects)) {
module_load_include('fetch.inc', 'locale'); module_load_include('fetch.inc', 'locale');
// Get translation status of the projects, download and update // Get translation status of the projects, download and update
// translations. // translations.
$options = _locale_translation_default_update_options(); $options = _locale_translation_default_update_options();
$batch = locale_translation_batch_update_build($list, array(), $options); $batch = locale_translation_batch_update_build($list, [], $options);
batch_set($batch); batch_set($batch);
} }
}
// Construct a batch to update configuration for all components. Installing
// this component may have installed configuration from any number of other
// components. Do this even if import is not enabled because parsing new
// configuration may expose new source strings.
\Drupal::moduleHandler()->loadInclude('locale', 'bulk.inc'); \Drupal::moduleHandler()->loadInclude('locale', 'bulk.inc');
if ($batch = locale_config_batch_update_components(array(), array(), $components)) { if ($batch = locale_config_batch_update_components([])) {
batch_set($batch); batch_set($batch);
} }
} }
......
services: services:
locale.default.config.storage:
class: Drupal\locale\LocaleDefaultConfigStorage
arguments: ['@config.storage', '@language_manager']
public: false
locale.config_manager: locale.config_manager:
class: Drupal\locale\LocaleConfigManager class: Drupal\locale\LocaleConfigManager
arguments: ['@config.storage', '@config.storage.installer', '@locale.storage', '@config.factory', '@config.typed', '@language_manager'] arguments: ['@config.storage', '@locale.storage', '@config.factory', '@config.typed', '@language_manager', '@locale.default.config.storage']
locale.storage: locale.storage:
class: Drupal\locale\StringDatabaseStorage class: Drupal\locale\StringDatabaseStorage
arguments: ['@database'] arguments: ['@database']
......
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
use Drupal\Component\Utility\NestedArray; use Drupal\Component\Utility\NestedArray;
use Drupal\Core\Config\ConfigFactoryInterface; use Drupal\Core\Config\ConfigFactoryInterface;
use Drupal\Core\Config\InstallStorage;
use Drupal\Core\Config\StorageInterface; use Drupal\Core\Config\StorageInterface;
use Drupal\Core\Config\TypedConfigManagerInterface; use Drupal\Core\Config\TypedConfigManagerInterface;
use Drupal\Core\StringTranslation\TranslationWrapper; use Drupal\Core\StringTranslation\TranslationWrapper;
...@@ -44,13 +45,6 @@ class LocaleConfigManager { ...@@ -44,13 +45,6 @@ class LocaleConfigManager {
*/ */
protected $configStorage; protected $configStorage;
/**
* The storage instance for reading default configuration data.
*
* @var \Drupal\Core\Config\StorageInterface
*/
protected $installStorage;
/** /**
* The string storage for reading and writing translations. * The string storage for reading and writing translations.
* *
...@@ -95,14 +89,18 @@ class LocaleConfigManager { ...@@ -95,14 +89,18 @@ class LocaleConfigManager {
*/ */
protected $isUpdatingFromLocale = FALSE; protected $isUpdatingFromLocale = FALSE;
/**
* The locale default config storage instance.
*
* @var \Drupal\locale\LocaleDefaultConfigStorage
*/
protected $defaultConfigStorage;
/** /**
* Creates a new typed configuration manager. * Creates a new typed configuration manager.
* *
* @param \Drupal\Core\Config\StorageInterface $config_storage * @param \Drupal\Core\Config\StorageInterface $config_storage
* The storage object to use for reading configuration data. * The storage object to use for reading configuration data.
* @param \Drupal\Core\Config\StorageInterface $install_storage
* The storage object to use for reading default configuration
* data.
* @param \Drupal\locale\StringStorageInterface $locale_storage * @param \Drupal\locale\StringStorageInterface $locale_storage
* The locale storage to use for reading string translations. * The locale storage to use for reading string translations.
* @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
...@@ -111,14 +109,16 @@ class LocaleConfigManager { ...@@ -111,14 +109,16 @@ class LocaleConfigManager {
* The typed configuration manager. * The typed configuration manager.
* @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager * @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager
* The language manager. * The language manager.
* @param \Drupal\locale\LocaleDefaultConfigStorage $default_config_storage
* The locale default configuration storage.
*/ */
public function __construct(StorageInterface $config_storage, StorageInterface $install_storage, StringStorageInterface $locale_storage, ConfigFactoryInterface $config_factory, TypedConfigManagerInterface $typed_config, ConfigurableLanguageManagerInterface $language_manager) { public function __construct(StorageInterface $config_storage, StringStorageInterface $locale_storage, ConfigFactoryInterface $config_factory, TypedConfigManagerInterface $typed_config, ConfigurableLanguageManagerInterface $language_manager, LocaleDefaultConfigStorage $default_config_storage) {
$this->configStorage = $config_storage; $this->configStorage = $config_storage;
$this->installStorage = $install_storage;
$this->localeStorage = $locale_storage; $this->localeStorage = $locale_storage;
$this->configFactory = $config_factory; $this->configFactory = $config_factory;
$this->typedConfigManager = $typed_config; $this->typedConfigManager = $typed_config;
$this->languageManager = $language_manager; $this->languageManager = $language_manager;
$this->defaultConfigStorage = $default_config_storage;
} }
/** /**
...@@ -133,7 +133,7 @@ public function __construct(StorageInterface $config_storage, StorageInterface $ ...@@ -133,7 +133,7 @@ public function __construct(StorageInterface $config_storage, StorageInterface $
public function getTranslatableDefaultConfig($name) { public function getTranslatableDefaultConfig($name) {
if ($this->isSupported($name)) { if ($this->isSupported($name)) {
// Create typed configuration wrapper based on install storage data. // Create typed configuration wrapper based on install storage data.
$data = $this->installStorageRead($name); $data = $this->defaultConfigStorage->read($name);
$type_definition = $this->typedConfigManager->getDefinition($name); $type_definition = $this->typedConfigManager->getDefinition($name);
$data_definition = $this->typedConfigManager->buildDataDefinition($type_definition, $data); $data_definition = $this->typedConfigManager->buildDataDefinition($type_definition, $data);
$typed_config = $this->typedConfigManager->create($data_definition, $data); $typed_config = $this->typedConfigManager->create($data_definition, $data);
...@@ -293,12 +293,12 @@ public function getComponentNames(array $components = array()) { ...@@ -293,12 +293,12 @@ public function getComponentNames(array $components = array()) {
foreach ($components as $type => $list) { foreach ($components as $type => $list) {
// InstallStorage::getComponentNames returns a list of folders keyed by // InstallStorage::getComponentNames returns a list of folders keyed by
// config name. // config name.
$names = array_merge($names, $this->installStorageComponents($type, $list)); $names = array_merge($names, $this->defaultConfigStorage->getComponentNames($type, $list));
} }
return $names; return $names;
} }
else { else {
return $this->installStorageAll(); return $this->defaultConfigStorage->listAll();
} }
} }
...@@ -474,7 +474,7 @@ public function hasTranslation($name, $langcode) { ...@@ -474,7 +474,7 @@ public function hasTranslation($name, $langcode) {
* configuration exists. * configuration exists.
*/ */
public function getDefaultConfigLangcode($name) { public function getDefaultConfigLangcode($name) {
$shipped = $this->installStorageRead($name); $shipped = $this->defaultConfigStorage->read($name);
if (!empty($shipped)) { if (!empty($shipped)) {
return !empty($shipped['langcode']) ? $shipped['langcode'] : 'en'; return !empty($shipped['langcode']) ? $shipped['langcode'] : 'en';
} }
...@@ -635,81 +635,4 @@ protected function filterOverride(array $override_data, array $translatable) { ...@@ -635,81 +635,4 @@ protected function filterOverride(array $override_data, array $translatable) {
return $filtered_data; return $filtered_data;
} }
/**
* Read a configuration from install storage or default languages.
*
* @param string $name
* Configuration object name.
*
* @return array
* Configuration data from install storage or default language.
*/
protected function installStorageRead($name) {
if ($this->installStorage->exists($name)) {
return $this->installStorage->read($name);
}
elseif (strpos($name, 'language.entity.') === 0) {
// Simulate default languages as if they were shipped as default
// configuration.
$langcode = str_replace('language.entity.', '', $name);
$predefined_languages = $this->languageManager->getStandardLanguageList();
if (isset($predefined_languages[$langcode])) {
$data = $this->configStorage->read($name);
$data['label'] = $predefined_languages[$langcode][0];
return $data;
}
}
}
/**
* Return the list of configuration in install storage and current languages.
*
* @return array
* List of configuration in install storage and current languages.
*/
protected function installStorageAll() {
$languages = $this->predefinedConfiguredLanguages();
return array_unique(array_merge($this->installStorage->listAll(), $languages));
}
/**
* Get all configuration names and folders for a list of modules or themes.
*
* @param string $type
* Type of components: 'module' | 'theme' | 'profile'
* @param array $list
* Array of theme or module names.
*
* @return array
* Configuration names provided by that component. In case of language
* module this list is extended with configured languages that have
* predefined names as well.
*/
protected function installStorageComponents($type, array $list) {
$names = array_keys($this->installStorage->getComponentNames($type, $list));
if ($type == 'module' && in_array('language', $list)) {
$languages = $this->predefinedConfiguredLanguages();
$names = array_unique(array_merge($names, $languages));
}
return $names;
}
/**
* Compute the list of configuration names that match predefined languages.
*
* @return array
* The list of configuration names that match predefined languages.
*/
protected function predefinedConfiguredLanguages() {
$names = $this->configStorage->listAll('language.entity.');
$predefined_languages = $this->languageManager->getStandardLanguageList();
foreach ($names as $id => $name) {
$langcode = str_replace('language.entity.', '', $name);
if (!isset($predefined_languages[$langcode])) {
unset($names[$id]);
}
}
return array_values($names);
}
} }
<?php
/**
* @file
* Contains \Drupal\locale\LocaleDefaultConfigStorage.
*/
namespace Drupal\locale;
use Drupal\Core\Config\InstallStorage;
use Drupal\Core\Config\StorageInterface;
use Drupal\language\ConfigurableLanguageManagerInterface;
/**
* Provides access to default configuration for locale integration.
*
* Allows unified access to default configuration from one of three sources:
* - Required default configuration (config/install/*)
* - Optional default configuration (config/optional/*)
* - Predefined languages mocked as default configuration (list defined in
* LocaleConfigManagerInterface::getStandardLanguageList())
*
* These sources are considered equal in terms of how locale module interacts
* with them for translation. Their translatable source strings are exposed
* for interface translation and participate in remote translation updates.
*/
class LocaleDefaultConfigStorage {
/**
* The storage instance for reading configuration data.
*
* @var \Drupal\Core\Config\StorageInterface
*/
protected $configStorage;
/**
* The language manager.
*
* @var \Drupal\language\ConfigurableLanguageManagerInterface
*/
protected $languageManager;
/**
* The storage instance for reading required default configuration data.
*
* @var \Drupal\Core\Config\StorageInterface
*/
protected $requiredInstallStorage;
/**
* The storage instance for reading optional default configuration data.
*
* @var \Drupal\Core\Config\StorageInterface
*/
protected $optionalInstallStorage;
/**
* Constructs a LocaleDefaultConfigStorage.
*
* @param \Drupal\Core\Config\StorageInterface $config_storage
* The storage object to use for reading configuration data.
* @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager
* The language manager.
*/
public function __construct(StorageInterface $config_storage, ConfigurableLanguageManagerInterface $language_manager) {
$this->configStorage = $config_storage;
$this->languageManager = $language_manager;
$this->requiredInstallStorage = new InstallStorage();
$this->optionalInstallStorage = new InstallStorage(InstallStorage::CONFIG_OPTIONAL_DIRECTORY);
}
/**
* Read a configuration from install storage or default languages.
*
* @param string $name
* Configuration object name.
*
* @return array
* Configuration data from install storage or default language.
*/
public function read($name) {
if ($this->requiredInstallStorage->exists($name)) {
return $this->requiredInstallStorage->read($name);
}
elseif ($this->optionalInstallStorage->exists($name)) {
return $this->optionalInstallStorage->read($name);
}
elseif (strpos($name, 'language.entity.') === 0) {
// Simulate default languages as if they were shipped as default
// configuration.
$langcode = str_replace('language.entity.', '', $name);
$predefined_languages = $this->languageManager->getStandardLanguageList();
if (isset($predefined_languages[$langcode])) {
$data = $this->configStorage->read($name);
$data['label'] = $predefined_languages[$langcode][0];
return $data;
}
}
}
/**
* Return the list of configuration in install storage and current languages.
*
* @return array
* List of configuration in install storage and current languages.
*/
public function listAll() {
$languages = $this->predefinedConfiguredLanguages();
return array_unique(
array_merge(
$this->requiredInstallStorage->listAll(),
$this->optionalInstallStorage->listAll(),
$languages
)
);
}
/**
* Get all configuration names and folders for a list of modules or themes.
*
* @param string $type
* Type of components: 'module' | 'theme' | 'profile'
* @param array $list
* Array of theme or module names.
*
* @return array
* Configuration names provided by that component. In case of language
* module this list is extended with configured languages that have
* predefined names as well.
*/
public function getComponentNames($type, array $list) {
$names = array_unique(
array_merge(
array_keys($this->requiredInstallStorage->getComponentNames($type, $list)),
array_keys($this->optionalInstallStorage->getComponentNames($type, $list))
)
);
if ($type == 'module' && in_array('language', $list)) {
$languages = $this->predefinedConfiguredLanguages();
$names = array_unique(array_merge($names, $languages));
}
return $names;
}
/**
* Compute the list of configuration names that match predefined languages.
*
* @return array
* The list of configuration names that match predefined languages.
*/
protected function predefinedConfiguredLanguages() {
$names = $this->configStorage->listAll('language.entity.');
$predefined_languages = $this->languageManager->getStandardLanguageList();
foreach ($names as $id => $name) {
$langcode = str_replace('language.entity.', '', $name);
if (!isset($predefined_languages[$langcode])) {
unset($names[$id]);
}
}
return array_values($names);
}
}
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
namespace Drupal\locale\Tests; namespace Drupal\locale\Tests;
use Drupal\language\Entity\ConfigurableLanguage; use Drupal\language\Entity\ConfigurableLanguage;
use Drupal\locale\Locale;
use Drupal\locale\StringInterface; use Drupal\locale\StringInterface;
use Drupal\locale\TranslationString; use Drupal\locale\TranslationString;
use Drupal\simpletest\KernelTestBase; use Drupal\simpletest\KernelTestBase;
...@@ -22,7 +23,7 @@ class LocaleConfigSubscriberTest extends KernelTestBase { ...@@ -22,7 +23,7 @@ class LocaleConfigSubscriberTest extends KernelTestBase {
/** /**
* {@inheritdoc} * {@inheritdoc}
*/ */
public static $modules = ['language', 'locale']; public static $modules = ['language', 'locale', 'system'];
/** /**
* The configurable language manager used in this test. * The configurable language manager used in this test.
...@@ -61,6 +62,7 @@ protected function setUp() { ...@@ -61,6 +62,7 @@ protected function setUp() {