diff --git a/core/core.services.yml b/core/core.services.yml index c749b8e..8a7ecea 100644 --- a/core/core.services.yml +++ b/core/core.services.yml @@ -252,8 +252,6 @@ services: config.storage.schema: class: Drupal\Core\Config\ExtensionInstallStorage arguments: ['@config.storage', 'config/schema'] - config.storage.installer: - class: Drupal\Core\Config\InstallStorage config.typed: class: Drupal\Core\Config\TypedConfigManager arguments: ['@config.storage', '@config.storage.schema', '@cache.discovery', '@module_handler'] diff --git a/core/lib/Drupal/Core/Language/LanguageManager.php b/core/lib/Drupal/Core/Language/LanguageManager.php index 9169031..8803734 100644 --- a/core/lib/Drupal/Core/Language/LanguageManager.php +++ b/core/lib/Drupal/Core/Language/LanguageManager.php @@ -233,27 +233,16 @@ public function getLanguageSwitchLinks($type, Url $url) { } /** - * 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. - * - * 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. + * @inheritdoc */ 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( 'af' => array('Afrikaans', 'Afrikaans'), 'am' => array('Amharic', 'አማርኛ'), diff --git a/core/lib/Drupal/Core/Language/LanguageManagerInterface.php b/core/lib/Drupal/Core/Language/LanguageManagerInterface.php index c29dd33..b536a00 100644 --- a/core/lib/Drupal/Core/Language/LanguageManagerInterface.php +++ b/core/lib/Drupal/Core/Language/LanguageManagerInterface.php @@ -208,4 +208,19 @@ public function setConfigOverrideLanguage(LanguageInterface $language = NULL); */ 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(); + } diff --git a/core/modules/config/src/Tests/ConfigInstallTest.php b/core/modules/config/src/Tests/ConfigInstallTest.php index 9a903a1..c32922a 100644 --- a/core/modules/config/src/Tests/ConfigInstallTest.php +++ b/core/modules/config/src/Tests/ConfigInstallTest.php @@ -7,6 +7,7 @@ namespace Drupal\config\Tests; +use Drupal\Core\Config\InstallStorage; use Drupal\Core\Config\PreExistingConfigException; use Drupal\Core\Config\StorageInterface; use Drupal\Core\Config\UnmetDependenciesException; @@ -223,7 +224,8 @@ public function testDependencyChecking() { function testLanguage() { $this->installModules(['config_test_language']); // 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->assertEqual( $this->config('config_test.dynamic.dotted.english')->get('langcode'), @@ -231,7 +233,7 @@ function testLanguage() { ); // 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( $this->config('config_test.dynamic.dotted.french')->get('langcode'), diff --git a/core/modules/locale/locale.services.yml b/core/modules/locale/locale.services.yml index 284878b..0e0dd47 100644 --- a/core/modules/locale/locale.services.yml +++ b/core/modules/locale/locale.services.yml @@ -1,7 +1,10 @@ services: + locale.default.config.storage: + class: Drupal\locale\LocaleDefaultConfigStorage + arguments: ['@config.storage', '@language_manager'] locale.config_manager: 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: class: Drupal\locale\StringDatabaseStorage arguments: ['@database'] diff --git a/core/modules/locale/src/LocaleConfigManager.php b/core/modules/locale/src/LocaleConfigManager.php index 17d1e8f..5f9a6bf 100644 --- a/core/modules/locale/src/LocaleConfigManager.php +++ b/core/modules/locale/src/LocaleConfigManager.php @@ -9,6 +9,7 @@ use Drupal\Component\Utility\NestedArray; use Drupal\Core\Config\ConfigFactoryInterface; +use Drupal\Core\Config\InstallStorage; use Drupal\Core\Config\StorageInterface; use Drupal\Core\Config\TypedConfigManagerInterface; use Drupal\Core\StringTranslation\TranslationWrapper; @@ -45,13 +46,6 @@ class LocaleConfigManager { 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. * * @var \Drupal\locale\StringStorageInterface; @@ -96,13 +90,17 @@ class LocaleConfigManager { protected $isUpdatingFromLocale = FALSE; /** + * The locale default config storage instance. + * + * @var \Drupal\locale\LocaleDefaultConfigStorage + */ + protected $defaultConfigStorage; + + /** * Creates a new typed configuration manager. * * @param \Drupal\Core\Config\StorageInterface $config_storage * 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 * The locale storage to use for reading string translations. * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory @@ -112,13 +110,13 @@ class LocaleConfigManager { * @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager * The language manager. */ - 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->installStorage = $install_storage; $this->localeStorage = $locale_storage; $this->configFactory = $config_factory; $this->typedConfigManager = $typed_config; $this->languageManager = $language_manager; + $this->defaultConfigStorage = $default_config_storage; } /** @@ -133,7 +131,7 @@ public function __construct(StorageInterface $config_storage, StorageInterface $ public function getTranslatableDefaultConfig($name) { if ($this->isSupported($name)) { // Create typed configuration wrapper based on install storage data. - $data = $this->installStorageRead($name); + $data = $this->defaultConfigStorage->read($name); $type_definition = $this->typedConfigManager->getDefinition($name, TRUE, TRUE); $data_definition = $this->typedConfigManager->buildDataDefinition($type_definition, $data); $typed_config = $this->typedConfigManager->create($data_definition, $data); @@ -293,12 +291,12 @@ public function getComponentNames(array $components = array()) { foreach ($components as $type => $list) { // InstallStorage::getComponentNames returns a list of folders keyed by // config name. - $names = array_merge($names, $this->installStorageComponents($type, $list)); + $names = array_merge($names, $this->defaultConfigStorage->getComponentNames($type, $list)); } return $names; } else { - return $this->installStorageAll(); + return $this->defaultConfigStorage->listAll(); } } @@ -472,7 +470,7 @@ public function hasTranslation($name, $langcode) { * configuration exists. */ public function getDefaultConfigLangcode($name) { - $shipped = $this->installStorageRead($name); + $shipped = $this->defaultConfigStorage->read($name); if (!empty($shipped)) { return !empty($shipped['langcode']) ? $shipped['langcode'] : 'en'; } @@ -633,81 +631,4 @@ protected function filterOverride(array $override_data, array $translatable) { 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); - } - } diff --git a/core/modules/locale/src/LocaleDefaultConfigStorage.php b/core/modules/locale/src/LocaleDefaultConfigStorage.php new file mode 100644 index 0000000..d17ce9a --- /dev/null +++ b/core/modules/locale/src/LocaleDefaultConfigStorage.php @@ -0,0 +1,152 @@ +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); + } + +} + diff --git a/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php b/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php index d5a7c21..8df0b34 100644 --- a/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php +++ b/core/modules/locale/src/Tests/LocaleConfigTranslationTest.php @@ -18,6 +18,13 @@ class LocaleConfigTranslationTest extends WebTestBase { /** + * The language code used. + * + * @var string + */ + protected $langcode; + + /** * Modules to enable. * * @var array @@ -37,28 +44,28 @@ protected function setUp() { $this->config('locale.settings') ->set('translation.import_enabled', TRUE) ->save(); - } - /** - * Tests basic configuration translation. - */ - public function testConfigTranslation() { // Add custom language. - $langcode = 'xx'; + $this->langcode = 'xx'; $admin_user = $this->drupalCreateUser(array('administer languages', 'access administration pages', 'translate interface', 'administer modules', 'access site-wide contact form', 'administer contact forms')); $this->drupalLogin($admin_user); $name = $this->randomMachineName(16); $edit = array( 'predefined_langcode' => 'custom', - 'langcode' => $langcode, + 'langcode' => $this->langcode, 'label' => $name, 'direction' => LanguageInterface::DIRECTION_LTR, ); $this->drupalPostForm('admin/config/regional/language/add', $edit, t('Add custom language')); // Set path prefix. - $edit = array("prefix[$langcode]" => $langcode); + $edit = array("prefix[{$this->langcode}]" => $this->langcode); $this->drupalPostForm('admin/config/regional/language/detection/url', $edit, t('Save configuration')); + } + /** + * Tests basic configuration translation. + */ + public function testConfigTranslation() { // Check site name string exists and create translation for it. $string = $this->storage->findString(array('source' => 'Drupal', 'context' => '', 'type' => 'configuration')); $this->assertTrue($string, 'Configuration strings have been created upon installation.'); @@ -67,7 +74,7 @@ public function testConfigTranslation() { $site_name = $this->randomMachineName(20); $search = array( 'string' => $string->source, - 'langcode' => $langcode, + 'langcode' => $this->langcode, 'translation' => 'all', ); $this->drupalPostForm('admin/config/regional/translate', $search, t('Filter')); @@ -80,12 +87,12 @@ public function testConfigTranslation() { $this->drupalPostForm('admin/config/regional/translate', $edit, t('Save translations')); // Get translation and check we've only got the site name. - $translation = \Drupal::languageManager()->getLanguageConfigOverride($langcode, 'system.site')->get(); + $translation = \Drupal::languageManager()->getLanguageConfigOverride($this->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); + $this->drupalGet($this->langcode); $this->assertText($site_name, 'The translated site name is displayed after translations refreshed.'); // Check default medium date format exists and create a translation for it. @@ -95,7 +102,7 @@ public function testConfigTranslation() { // Translate using the UI so configuration is refreshed. $search = array( 'string' => $string->source, - 'langcode' => $langcode, + 'langcode' => $this->langcode, 'translation' => 'all', ); $this->drupalPostForm('admin/config/regional/translate', $search, t('Filter')); @@ -107,12 +114,12 @@ public function testConfigTranslation() { ); $this->drupalPostForm('admin/config/regional/translate', $edit, t('Save translations')); - $translation = \Drupal::languageManager()->getLanguageConfigOverride($langcode, 'core.date_format.medium')->get(); + $translation = \Drupal::languageManager()->getLanguageConfigOverride($this->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". - $formatted_date = format_date(494015820, $type = 'medium', NULL, NULL, $langcode); + $formatted_date = format_date(494015820, $type = 'medium', NULL, NULL, $this->langcode); $this->assertEqual($formatted_date, 'Tue', 'Got the right formatted date using the date format translation pattern.'); // Assert strings from image module config are not available. @@ -129,7 +136,7 @@ public function testConfigTranslation() { $this->assertTrue(isset($locations['configuration']) && isset($locations['configuration']['image.style.medium']), 'Configuration string has been created with the right location'); // Check the string is unique and has no translation yet. - $translations = $this->storage->getTranslations(array('language' => $langcode, 'type' => 'configuration', 'name' => 'image.style.medium')); + $translations = $this->storage->getTranslations(array('language' => $this->langcode, 'type' => 'configuration', 'name' => 'image.style.medium')); $this->assertEqual(count($translations), 1); $translation = reset($translations); $this->assertEqual($translation->source, $string->source); @@ -139,7 +146,7 @@ public function testConfigTranslation() { $image_style_label = $this->randomMachineName(20); $search = array( 'string' => $string->source, - 'langcode' => $langcode, + 'langcode' => $this->langcode, 'translation' => 'all', ); $this->drupalPostForm('admin/config/regional/translate', $search, t('Filter')); @@ -151,12 +158,12 @@ public function testConfigTranslation() { $this->drupalPostForm('admin/config/regional/translate', $edit, t('Save translations')); // Check the right single translation has been created. - $translations = $this->storage->getTranslations(array('language' => $langcode, 'type' => 'configuration', 'name' => 'image.style.medium')); + $translations = $this->storage->getTranslations(array('language' => $this->langcode, 'type' => 'configuration', 'name' => 'image.style.medium')); $translation = reset($translations); $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. - $translation = \Drupal::languageManager()->getLanguageConfigOverride($langcode, 'image.style.medium')->get(); + $translation = \Drupal::languageManager()->getLanguageConfigOverride($this->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. @@ -171,7 +178,7 @@ public function testConfigTranslation() { $category_label = $this->randomMachineName(20); $search = array( 'string' => 'Website feedback', - 'langcode' => $langcode, + 'langcode' => $this->langcode, 'translation' => 'all', ); $this->drupalPostForm('admin/config/regional/translate', $search, t('Filter')); @@ -185,7 +192,7 @@ public function testConfigTranslation() { // Check if this category displayed in this language will use the // translation. This test ensures the entity loaded from the request // upcasting will already work. - $this->drupalGet($langcode . '/contact/feedback'); + $this->drupalGet($this->langcode . '/contact/feedback'); $this->assertText($category_label); // Check if the UI does not show the translated String. @@ -193,4 +200,49 @@ public function testConfigTranslation() { $this->assertFieldById('edit-label', 'Website feedback', 'Translation is not loaded for Edit Form.'); } + /** + * Test translatability of optional configuration in locale. + */ + public function testOptionalConfiguration() { + $this->checkNodeStrings(FALSE, FALSE); + // Enable the node module. + $this->drupalPostForm('admin/modules', array('modules[Core][node][enable]' => "1"), t('Save configuration')); + $this->rebuildContainer(); + $this->checkNodeStrings(TRUE, FALSE); + // Enable the views module (which node provides some optional config for). + $this->drupalPostForm('admin/modules', array('modules[Core][views][enable]' => "1"), t('Save configuration')); + $this->rebuildContainer(); + $this->checkNodeStrings(TRUE, TRUE); + } + + /** + * Check that node configuration source strings are made available in locale. + * + * @param bool $required + * Whether to assume a sample of the required default configuration is + * present. + * @param bool $optional + * Whether to assume a sample of the optional default configuration is + * present. + */ + protected function checkNodeStrings($required, $optional) { + // Check the required default configuration string in node. + $string = $this->storage->findString(array('source' => 'Make content sticky', 'context' => '', 'type' => 'configuration')); + if ($required) { + $this->assertTrue($string, 'Node action can be found with node module.'); + } + else { + $this->assertFalse($string, 'Node action can not be found without node module.'); + } + + // Check the optional default configuration string in node. + $string = $this->storage->findString(array('source' => 'No front page content has been created yet.', 'context' => '', 'type' => 'configuration')); + if ($optional) { + $this->assertTrue($string, 'Node view can be found with node and views modules.'); + } + else { + $this->assertFalse($string, 'Node view can not be found without node and/or views modules.'); + } + } + }