diff --git a/core/lib/Drupal/Core/Config/Config.php b/core/lib/Drupal/Core/Config/Config.php index 02afbd1..83fbca0 100644 --- a/core/lib/Drupal/Core/Config/Config.php +++ b/core/lib/Drupal/Core/Config/Config.php @@ -453,8 +453,9 @@ public function save() { $this->load(); } $this->storage->write($this->name, $this->data); - $this->isNew = FALSE; + $this->overrides = array(); $this->notify('save'); + $this->isNew = FALSE; return $this; } diff --git a/core/lib/Drupal/Core/Config/InstallStorage.php b/core/lib/Drupal/Core/Config/InstallStorage.php index 53612b4..25b6ede 100644 --- a/core/lib/Drupal/Core/Config/InstallStorage.php +++ b/core/lib/Drupal/Core/Config/InstallStorage.php @@ -110,7 +110,8 @@ public function listAll($prefix = '') { */ protected function getAllFolders() { if (!isset($this->folders)) { - $this->folders = $this->getComponentNames('profile', array(drupal_get_profile())); + //$this->folders = $this->getComponentNames('profile', array(drupal_get_profile())); + $this->folders = $this->getComponentNames('profile', array(variable_get('install_profile', 'standard'))); $this->folders += $this->getComponentNames('module', array_keys(drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.module$/', 'modules', 'name', 0))); $this->folders += $this->getComponentNames('theme', array_keys(drupal_system_listing('/^' . DRUPAL_PHP_FUNCTION_PATTERN . '\.info.yml$/', 'themes'))); } diff --git a/core/lib/Drupal/Core/EventSubscriber/ConfigGlobalOverrideSubscriber.php b/core/lib/Drupal/Core/EventSubscriber/ConfigGlobalOverrideSubscriber.php index 02bf2cb..2127304 100644 --- a/core/lib/Drupal/Core/EventSubscriber/ConfigGlobalOverrideSubscriber.php +++ b/core/lib/Drupal/Core/EventSubscriber/ConfigGlobalOverrideSubscriber.php @@ -32,10 +32,21 @@ public function configInit(ConfigEvent $event) { } /** + * Resets global overrides after configuration save. + * + * @param \Drupal\Core\Config\ConfigEvent $event + * The Event to process. + */ + public function configSave(ConfigEvent $event) { + $this->configInit($event); + } + + /** * Implements EventSubscriberInterface::getSubscribedEvents(). */ static function getSubscribedEvents() { $events['config.init'][] = array('configInit', 30); + $events['config.save'][] = array('configSave', 30); return $events; } diff --git a/core/modules/locale/lib/Drupal/locale/Locale.php b/core/modules/locale/lib/Drupal/locale/Locale.php index f4f41c1..665f415 100644 --- a/core/modules/locale/lib/Drupal/locale/Locale.php +++ b/core/modules/locale/lib/Drupal/locale/Locale.php @@ -25,6 +25,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/lib/Drupal/locale/LocaleConfigManager.php b/core/modules/locale/lib/Drupal/locale/LocaleConfigManager.php index 8cba49a..a6e2baf 100644 --- a/core/modules/locale/lib/Drupal/locale/LocaleConfigManager.php +++ b/core/modules/locale/lib/Drupal/locale/LocaleConfigManager.php @@ -8,20 +8,28 @@ namespace Drupal\locale; use Drupal\Core\Language\Language; +use Drupal\Core\Config\Config; use Drupal\Core\Config\TypedConfigManager; use Drupal\Core\Config\StorageInterface; /** * Manages localized configuration type plugins. */ -class LocaleConfigManager extends TypedConfigManager { +class LocaleConfigManager { /** - * A storage controller instance for reading default configuration data. + * The locale configuration storage controller. * - * @var \Drupal\Core\Config\StorageInterface + * @var \Drupal\locale\LocaleConfigStorage */ - protected $installStorage; + protected $configStorage; + + /** + * The typed configuration manager. + * + * @var \Drupal\Core\Config\TypedConfigManager + */ + protected $typedConfig; /** * A string storage for reading and writing translations. @@ -38,20 +46,17 @@ class LocaleConfigManager extends TypedConfigManager { /** * Creates a new typed configuration manager. * - * @param \Drupal\Core\Config\StorageInterface $configStorage - * The storage controller object to use for reading configuration data. - * @param \Drupal\Core\Config\StorageInterface $schemaStorage - * The storage controller object to use for reading schema data. - * @param \Drupal\Core\Config\StorageInterface $installStorage - * The storage controller object to use for reading default configuration - * data. + * @param \Drupal\locale\LocaleConfigStorage $configStorage + * The storage controller object to use for accessing configuration data. + * @param \Drupal\Core\Config\TypedConfigManager $typedConfig + * The typed configuration manager. + * * @param \Drupal\locale\StringStorageInterface $localeStorage * The locale storage to use for reading string translations. */ - public function __construct(StorageInterface $configStorage, StorageInterface $schemaStorage, StorageInterface $installStorage, StringStorageInterface $localeStorage) { - // Note we use the install storage for the parent constructor. - parent::__construct($configStorage, $schemaStorage); - $this->installStorage = $installStorage; + public function __construct(LocaleConfigStorage $configStorage, TypedConfigManager $typedConfig, StringStorageInterface $localeStorage) { + $this->configStorage = $configStorage; + $this->typedConfig = $typedConfig; $this->localeStorage = $localeStorage; } @@ -61,105 +66,56 @@ public function __construct(StorageInterface $configStorage, StorageInterface $s * @param string $name * Configuration object name. * - * @return \Drupal\locale\LocaleTypedConfig - * Locale-wrapped configuration element. + * @return \Drupal\locale\LocaleTypedConfig|FALSE + * Locale-wrapped configuration element or false if there is no translatable data. */ - public function get($name) { - // Read default and current configuration data. - $default = $this->installStorage->read($name); - $updated = $this->configStorage->read($name); + public function getLocaleWrapper($name) { // We get only the data that didn't change from default. - $data = $this->compareConfigData($default, $updated); - $definition = $this->getDefinition($name); - // Unless the configuration has a explicit language code we assume English. - $langcode = isset($default['langcode']) ? $default['langcode'] : 'en'; - $wrapper = new LocaleTypedConfig($definition, $name, $langcode, $this); - $wrapper->setValue($data); - return $wrapper; - } - - /** - * Compares default configuration with updated data. - * - * @param array $default - * Default configuration data. - * @param array|false $updated - * Current configuration data, or FALSE if no configuration data existed. - * - * @return array - * The elements of default configuration that haven't changed. - */ - protected function compareConfigData(array $default, $updated) { - // Speed up comparison, specially for install operations. - if ($default === $updated) { - return $default; + $data = $this->configStorage->getUnchanged($name); + if ($data && $this->canTranslateData($data)) { + // Unless the configuration has a explicit language code we assume English. + $langcode = isset($data['langcode']) ? $data['langcode'] : 'en'; + $definition = $this->typedConfig->getDefinition($name); + $wrapper = new LocaleTypedConfig($definition, $name, $langcode, $this); + $wrapper->setValue($data); + return $wrapper; } - $result = array(); - foreach ($default as $key => $value) { - if (isset($updated[$key])) { - if (is_array($value)) { - $result[$key] = $this->compareConfigData($value, $updated[$key]); - } - elseif ($value === $updated[$key]) { - $result[$key] = $value; - } - } + else { + return FALSE; } - return $result; } /** - * Saves translated configuration data. + * Create locale wrapper with typed configuration data. * * @param string $name * Configuration object name. - * @param string $langcode - * Language code. * @param array $data - * Configuration data to be saved, that will be only the translated values. + * Array of configuration data. + * + * @return \Drupal\locale\LocaleTypedConfig|FALSE + * Locale-wrapped configuration element or false if there is no translatable data. */ - public function saveTranslationData($name, $langcode, array $data) { - $locale_name = self::localeConfigName($langcode, $name); - $this->configStorage->write($locale_name, $data); + public function createTypedConfig($name, array $data) { + $definition = $this->typedConfig->getDefinition($name); + $element = $this->typedConfig->create($definition, $data, $name); + $element->setValue($data); + return $element; } + /** - * Deletes translated configuration data. + * Gets translated configuration data. * * @param string $name * Configuration object name. * @param string $langcode * Language code. - */ - public function deleteTranslationData($name, $langcode) { - $locale_name = self::localeConfigName($langcode, $name); - $this->configStorage->delete($locale_name); - } - - /** - * Gets configuration names associated with components. * - * @param array $components - * (optional) Array of component lists indexed by type. If not present or it - * is an empty array, it will update all components. - * - * @return array - * Array of configuration object names. + * @return */ - public function getComponentNames(array $components) { - $components = array_filter($components); - if ($components) { - $names = array(); - foreach ($components as $type => $list) { - // InstallStorage::getComponentNames returns a list of folders keyed by - // config name. - $names = array_merge($names, array_keys($this->installStorage->getComponentNames($type, $list))); - } - return $names; - } - else { - return $this->installStorage->listAll(); - } + public function getTranslationData($name, $langcode) { + return $this->configStorage->readTranslation($name, $langcode); } /** @@ -171,7 +127,7 @@ public function getComponentNames(array $components) { * Array of language codes. */ public function deleteComponentTranslations(array $components, array $langcodes) { - $names = $this->getComponentNames($components); + $names = $this->configStorage->getComponentNames($components); if ($names && $langcodes) { foreach ($names as $name) { foreach ($langcodes as $langcode) { @@ -182,6 +138,20 @@ public function deleteComponentTranslations(array $components, array $langcodes) } /** + * Gets configuration names associated with components. + * + * @param array $components + * (optional) Array of component lists indexed by type. If not present or it + * is an empty array, it will update all components. + * + * @return array + * Array of configuration object names. + */ + public function getComponentNames(array $components = array()) { + return $this->configStorage->getComponentNames($components); + } + + /** * Gets configuration names associated with strings. * * @param array $lids @@ -206,10 +176,7 @@ public function getStringNames(array $lids) { * Language code to delete. */ public function deleteLanguageTranslations($langcode) { - $locale_name = self::localeConfigName($langcode); - foreach ($this->configStorage->listAll($locale_name) as $name) { - $this->configStorage->delete($name); - } + $this->configStorage->deleteLanguage($langcode); } /** @@ -234,63 +201,149 @@ public function deleteLanguageTranslations($langcode) { */ public function translateString($name, $langcode, $source, $context) { if ($source) { - // If translations for a language have not been loaded yet. - if (!isset($this->translations[$name][$langcode])) { - // Preload all translations for this configuration name and language. - $this->translations[$name][$langcode] = array(); - foreach ($this->localeStorage->getTranslations(array('language' => $langcode, 'type' => 'configuration', 'name' => $name)) as $string){ - $this->translations[$name][$langcode][$string->context][$string->source] = $string; - } + // If translator for a configuration name is not created yet. + if (!isset($this->translations[$name])) { + $this->translations[$name] = new LocaleConfigTranslation($name, $this->localeStorage); } - if (!isset($this->translations[$name][$langcode][$context][$source])) { - // There is no translation of the source string in this config location - // to this language for this context. - if ($translation = $this->localeStorage->findTranslation(array('source' => $source, 'context' => $context, 'language' => $langcode))) { - // Look for a translation of the string. It might have one, but not - // be saved in this configuration location yet. - // If the string has a translation for this context to this language, - // save it in the configuration location so it can be looked up faster - // next time. - $string = $this->localeStorage->createString((array) $translation) - ->addLocation('configuration', $name) - ->save(); - } - else { - // No translation was found. Add the source to the configuration - // location so it can be translated, and the string is faster to look - // for next time. - $translation = $this->localeStorage - ->createString(array('source' => $source, 'context' => $context)) - ->addLocation('configuration', $name) - ->save(); - } + return $this->translations[$name]->getStringTranslation($langcode, $source, $context); + } + return FALSE; + } - // Add an entry, either the translation found, or a blank string object - // to track the source string, to this configuration location, language, - // and context. - $this->translations[$name][$langcode][$context][$source] = $translation; + /** + * Translate configuration object. + * + * @param Drupal\Core\Config\Config $config + * Configuration object to translate. + * @param string $langcode + * Language code to translate to. + */ + public function translateConfigObject(Config $config, $langcode) { + if ($this->canTranslateConfigObject($config, $langcode)) { + // Check to see if there is any translated data for this configuration. + if ($override = $this->configStorage->readTranslation($config->getName(), $langcode)) { + $config->setOverride($override); } + } + } - // Return the string only when the string object had a translation. - if ($this->translations[$name][$langcode][$context][$source]->isTranslation()) { - return $this->translations[$name][$langcode][$context][$source]->getString(); + /** + * Updates translations for a configuration object. + * + * @param \Drupal\Core\Config\Config $config + * Configuration object that has been updated. + */ + public function updateConfigObject(Config $config) { + if ($this->canTranslateConfigObject($config)) { + $name = $config->getName(); + // Reset locale translation cache for this configuration name. + unset($this->translations[$name]); + $locations = $this->localeStorage->getLocations(array('type' => 'configuration', 'name' => $name)); + if (!empty($locations)) { + // Update all configuration translation files based on current values. + $this->updateConfigTranslation($name); } } - return FALSE; } /** - * Provides configuration data location for given langcode and name. + * Updates configuration translations for name / languages. * - * @param string $langcode - * The language code. - * @param string|NULL $name - * Name of the original configuration. Set to NULL to get the name prefix - * for all $langcode overrides. + * @param array $names + * Array of names of configuration objects to update. + * @param array $langcodes + * (optional) Array of language codes to update. Defaults to all + * translatable language codes. + * + * @return int + * Number of configuration translations created / updated. + */ + public function updateConfigTranslation($name, $langcodes = array()) { + $langcodes = $langcodes ?: array_keys($this->getTranslatableLanguages()); + $wrapper = $this->getLocaleWrapper($name); + $count = 0; + foreach ($langcodes as $langcode) { + $translation = $wrapper ? $wrapper->getTranslation($langcode)->getValue() : NULL; + if ($translation) { + $this->configStorage->writeTranslation($name, $langcode, $translation); + $count++; + } + else { + $this->configStorage->deleteTranslation($name, $langcode); + } + } + return $count; + } + + /** + * Checks whether configuration object is translatable. + * + * @param \Drupal\Core\Config\Config $config + * Configuration object that has been updated. + * @param string $to_langcode + * (optional) Destination language code. * - * @return string + * @return bool + * TRUE if the configuration object can be translated. */ - public static function localeConfigName($langcode, $name = NULL) { - return rtrim('locale.config.' . $langcode . '.' . $name, '.'); + public function canTranslateConfigObject(Config $config, $to_langcode = NULL) { + // @todo Better workaround to get config raw data without causing recursion. + $data = $config->getStorage()->read($config->getName()); + return $data && $this->canTranslateData($data, $to_langcode); } + + /** + * Checks whether configuration data is translatable. + * + * @param array $data + * Array of configuration data. + * @param string $to_langcode + * (optional) Destination language code. + * + * @return bool + * TRUE if the data can be translated. + */ + public function canTranslateData(array $data, $to_langcode = NULL) { + return isset($data['langcode']) && $this->canTranslateLangcode($data['langcode'], $to_langcode); + } + + /** + * Checks whether we can translate these languages. + * + * @param string $from_langcode + * Source language code. + * @param string $to_langcode + * (optional) Destination language code. + * + * @return bool + * TRUE if this translator supports translations for these languages. + */ + public function canTranslateLangcode($from_langcode, $to_langcode = NULL) { + if ($from_langcode == 'en') { + $translatable = $this->getTranslatableLanguages(); + if (!$to_langcode) { + return !empty($translatable); + } + else { + return isset($translatable[$to_langcode]); + } + } + return FALSE; + } + + /** + * Returns list of translatable languages. + * + * @return array + * Array of installed languages keyed by language name. English is omitted + * unless it is marked as translatable. + */ + public static function getTranslatableLanguages() { + $languages = language_list(); + if (!variable_get('locale_translate_english', FALSE)) { + unset($languages['en']); + } + return $languages; + } + } diff --git a/core/modules/locale/lib/Drupal/locale/LocaleConfigStorage.php b/core/modules/locale/lib/Drupal/locale/LocaleConfigStorage.php new file mode 100644 index 0000000..0a6b468 --- /dev/null +++ b/core/modules/locale/lib/Drupal/locale/LocaleConfigStorage.php @@ -0,0 +1,191 @@ +configStorage = $configStorage; + $this->installStorage = $installStorage; + } + + /** + * Gets configuration names associated with components. + * + * @param array $components + * (optional) Array of component lists indexed by type. If not present or it + * is an empty array, it will update all components. + * + * @return array + * Array of configuration object names. + */ + public function getComponentNames(array $components) { + $components = array_filter($components); + if ($components) { + $names = array(); + foreach ($components as $type => $list) { + // InstallStorage::getComponentNames returns a list of folders keyed by + // config name. + $names = array_merge($names, array_keys($this->installStorage->getComponentNames($type, $list))); + } + return $names; + } + else { + return $this->installStorage->listAll(); + } + } + + /** + * Gets translated configuration data. + * + * @param string $name + * Configuration object name. + * @param string $langcode + * Language code. + * + * @return array|bool + * The configuration data stored for the configuration object name. If no + * configuration data exists for the given name, FALSE is returned. + */ + public function readTranslation($name, $langcode) { + $locale_name = self::localeName($langcode, $name); + return $this->configStorage->read($locale_name); + } + + /** + * Saves translated configuration data. + * + * @param string $name + * Configuration object name. + * @param string $langcode + * Language code. + * @param array $data + * Configuration data to be saved, that will be only the translated values. + */ + public function writeTranslation($name, $langcode, array $data) { + $locale_name = self::localeName($langcode, $name); + $this->configStorage->write($locale_name, $data); + } + + /** + * Deletes translated configuration data. + * + * @param string $name + * Configuration object name. + * @param string $langcode + * Language code. + */ + public function deleteTranslation($name, $langcode) { + $locale_name = self::localeName($langcode, $name); + $this->configStorage->delete($locale_name); + } + + /** + * Deletes configuration for language. + * + * @param string $langcode + * Language code to delete. + */ + public function deleteLanguage($langcode) { + $locale_name = self::localeName($langcode); + foreach ($this->configStorage->listAll($locale_name) as $name) { + $this->configStorage->delete($name); + } + } + + /** + * Gets current configuration data that is not changed from default. + * + * @param string $name + * Configuration object name. + * + * @return array|bool + * The configuration data stored for the configuration object name. If no + * configuration data exists for the given name, FALSE is returned. + */ + public function getUnchanged($name) { + // Read default and current configuration data. + if (($default = $this->installStorage->read($name)) && $updated = $this->configStorage->read($name)) { + // We get only the data that didn't change from default. + return $this->compareConfigData($default, $updated); + } + else { + return FALSE; + } + } + + /** + * Compares default configuration with updated data. + * + * @param array $default + * Default configuration data. + * @param array|false $updated + * Current configuration data, or FALSE if no configuration data existed. + * + * @return array + * The elements of default configuration that haven't changed. + */ + protected function compareConfigData(array $default, $updated) { + // Speed up comparison, specially for install operations. + if ($default === $updated) { + return $default; + } + $result = array(); + foreach ($default as $key => $value) { + if (isset($updated[$key])) { + if (is_array($value)) { + $result[$key] = $this->compareConfigData($value, $updated[$key]); + } + elseif ($value === $updated[$key]) { + $result[$key] = $value; + } + } + } + return $result; + } + + /** + * Provides configuration data location for given langcode and name. + * + * @param string $langcode + * The language code. + * @param string|NULL $name + * Name of the original configuration. Set to NULL to get the name prefix + * for all $langcode overrides. + * + * @return string + */ + protected static function localeName($langcode, $name = NULL) { + return rtrim('locale.config.' . $langcode . '.' . $name, '.'); + } +} diff --git a/core/modules/locale/lib/Drupal/locale/LocaleConfigSubscriber.php b/core/modules/locale/lib/Drupal/locale/LocaleConfigSubscriber.php index cb9be73..e771535 100644 --- a/core/modules/locale/lib/Drupal/locale/LocaleConfigSubscriber.php +++ b/core/modules/locale/lib/Drupal/locale/LocaleConfigSubscriber.php @@ -13,12 +13,12 @@ use Drupal\Core\Config\StorageDispatcher; use Drupal\Core\Language\Language; use Drupal\Core\Language\LanguageManager; +use Drupal\locale\LocaleConfigManager; use Symfony\Component\HttpKernel\HttpKernelInterface; use Symfony\Component\HttpKernel\KernelEvents; use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\EventDispatcher\EventSubscriberInterface; - /** * Locale Config helper * @@ -41,6 +41,13 @@ class LocaleConfigSubscriber implements EventSubscriberInterface { protected $defaultConfigContext; /** + * The locale configuration manager. + * + * @var \Drupal\locale\LocaleConfigManager + */ + protected $configManager; + + /** * Constructs a LocaleConfigSubscriber object. * * @param \Drupal\Core\Language\LanguageManager $language_manager @@ -48,9 +55,10 @@ class LocaleConfigSubscriber implements EventSubscriberInterface { * @param \Drupal\Core\Config\Context\ConfigContext $config_context * The configuration context service. */ - public function __construct(LanguageManager $language_manager, ContextInterface $config_context) { + public function __construct(LanguageManager $language_manager, ContextInterface $config_context, LocaleConfigManager $config_manager) { $this->languageManager = $language_manager; $this->defaultConfigContext = $config_context; + $this->configManager = $config_manager; } /** @@ -87,13 +95,31 @@ public function configLoad(ConfigEvent $event) { $context = $event->getContext(); if ($language = $context->get('locale.language')) { $config = $event->getConfig(); - $locale_name = $this->getLocaleConfigName($config->getName(), $language); - // Check to see if the config storage has an appropriately named file - // containing override data. - if ($override = $event->getConfig()->getStorage()->read($locale_name)) { - $config->setOverride($override); - } + // Handle the object to locale configuration manager to set a translation. + $this->configManager->translateConfigObject($config, $language->id); + } + } + + /** + * Update shipped configuration translations. + * + * @param \Drupal\Core\Config\ConfigEvent $event + * The Event to process. + */ + public function configSave(ConfigEvent $event) { + // Check that the configuration name is a location recognized in the + // interface translation system. That is, the configuration is originally + // shipped, not user created. + + // Retranslate configuration only if the object has been updated. + // For newly created objects, we do that upon module installation. + $config = $event->getConfig(); + if (!$config->isNew()) { + $this->configManager->updateConfigObject($config); } + + // Set language config overrides again for the new values. + $this->configLoad($event); } /** @@ -111,29 +137,12 @@ public function onKernelRequestSetDefaultConfigContextLocale(GetResponseEvent $e } /** - * Get configuration name for this language. - * - * It will be the same name with a prefix depending on language code: - * locale.config.LANGCODE.NAME - * - * @param string $name - * The name of the config object. - * @param \Drupal\Core\Language\Language $language - * The language object. - * - * @return string - * The localized config name. - */ - public function getLocaleConfigName($name, Language $language) { - return 'locale.config.' . $language->id . '.' . $name; - } - - /** * Implements EventSubscriberInterface::getSubscribedEvents(). */ static function getSubscribedEvents() { $events['config.context'][] = array('configContext', 20); $events['config.load'][] = array('configLoad', 20); + $events['config.save'][] = array('configSave', 20); $events[KernelEvents::REQUEST][] = array('onKernelRequestSetDefaultConfigContextLocale', 20); return $events; } diff --git a/core/modules/locale/lib/Drupal/locale/LocaleConfigTranslation.php b/core/modules/locale/lib/Drupal/locale/LocaleConfigTranslation.php new file mode 100644 index 0000000..fa83270 --- /dev/null +++ b/core/modules/locale/lib/Drupal/locale/LocaleConfigTranslation.php @@ -0,0 +1,116 @@ +name = $name; + $this->storage = $storage; + } + + /** + * {@inheritdoc} + */ + public function getStringTranslation($langcode, $source, $context) { + // If the language is not suitable for locale module, just return. + if ($langcode == Language::LANGCODE_SYSTEM || ($langcode == 'en' && !variable_get('locale_translate_english', FALSE))) { + return FALSE; + } + // If translations for a language have not been loaded yet. + if (!isset($this->translations[$langcode])) { + // Preload all translations for this configuration name and language. + $this->translations[$langcode] = array(); + foreach ($this->storage->getTranslations(array('language' => $langcode, 'type' => 'configuration', 'name' => $this->name)) as $string){ + $this->translations[$langcode][$string->context][$string->source] = $string; + } + } + if (!isset($this->translations[$langcode][$context][$source])) { + // There is no translation of the source string in this config location + // to this language for this context. + if ($translation = $this->storage->findTranslation(array('source' => $source, 'context' => $context, 'language' => $langcode))) { + // Look for a translation of the string. It might have one, but not + // be saved in this configuration location yet. + // If the string has a translation for this context to this language, + // save it in the configuration location so it can be looked up faster + // next time. + $string = $this->storage->createString((array) $translation) + ->addLocation('configuration', $this->name) + ->save(); + } + else { + // No translation was found. Add the source to the configuration + // location so it can be translated, and the string is faster to look + // for next time. + $translation = $this->storage + ->createString(array('source' => $source, 'context' => $context)) + ->addLocation('configuration', $this->name) + ->save(); + } + + // Add an entry, either the translation found, or a blank string object + // to track the source string, to this configuration location, language, + // and context. + $this->translations[$langcode][$context][$source] = $translation; + } + + // Return the string only when the string object had a translation. + if ($this->translations[$langcode][$context][$source]->isTranslation()) { + return $this->translations[$langcode][$context][$source]->getString(); + } + else { + return FALSE; + } + } + + /** + * {@inheritdoc} + */ + public function reset() { + $this->translations = array(); + } + +} diff --git a/core/modules/locale/lib/Drupal/locale/LocaleTypedConfig.php b/core/modules/locale/lib/Drupal/locale/LocaleTypedConfig.php index 5d6b200..28b66c3 100644 --- a/core/modules/locale/lib/Drupal/locale/LocaleTypedConfig.php +++ b/core/modules/locale/lib/Drupal/locale/LocaleTypedConfig.php @@ -18,13 +18,6 @@ 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 @@ -50,7 +43,7 @@ class LocaleTypedConfig extends Element { * @param \Drupal\locale\LocaleConfigManager $localeConfig; * The locale configuration manager object. */ - public function __construct(array $definition, $name, $langcode, \Drupal\locale\LocaleConfigManager $localeConfig) { + public function __construct(array $definition, $name, $langcode, LocaleConfigManager $localeConfig) { parent::__construct($definition, $name); $this->langcode = $langcode; $this->localeConfig = $localeConfig; @@ -60,19 +53,24 @@ public function __construct(array $definition, $name, $langcode, \Drupal\locale\ * Gets wrapped typed config object. */ public function getTypedConfig() { - return $this->localeConfig->create($this->definition, $this->value); + return $this->localeConfig->createTypedConfig($this->name, $this->value); } /** * {@inheritdoc} */ public function getTranslation($langcode) { - $options = array( - 'source' => $this->langcode, - 'target' => $langcode, - ); - $data = $this->getElementTranslation($this->getTypedConfig(), $options); - return $this->localeConfig->create($this->definition, $data); + if ($this->localeConfig->canTranslateLangcode($this->langcode, $langcode)) { + $options = array( + 'source' => $this->langcode, + 'target' => $langcode, + ); + $data = $this->getElementTranslation($this->getTypedConfig(), $options); + return $this->localeConfig->createTypedConfig($this->name, $data); + } + else { + return NULL; + } } /** @@ -83,24 +81,6 @@ public function language() { } /** - * 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 mixed $element @@ -169,19 +149,15 @@ protected function getArrayTranslation(ArrayElement $element, array $options) { * Whether the element fits the translation criteria. */ protected function translateElement(\Drupal\Core\TypedData\TypedDataInterface $element, array $options) { - if ($this->canTranslate($options['source'], $options['target'])) { - $definition = $element->getDefinition(); - $value = $element->getValue(); - if ($value && !empty($definition['translatable'])) { - $context = isset($definition['locale context']) ? $definition['locale context'] : ''; - if ($translation = $this->localeConfig->translateString($this->name, $options['target'], $value, $context)) { - $element->setValue($translation); - return TRUE; - } + $definition = $element->getDefinition(); + $value = $element->getValue(); + if ($value && !empty($definition['translatable'])) { + $context = isset($definition['locale context']) ? $definition['locale 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/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php index 414cde0..7576c98 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php @@ -78,7 +78,7 @@ function testConfigTranslation() { ); $this->drupalPost('admin/config/regional/translate/translate', $edit, t('Save translations')); - $wrapper = $this->container->get('locale.config.typed')->get('system.site'); + $wrapper = $this->container->get('locale.config.manager')->getLocaleWrapper('system.site'); // Get translation and check we've only got the site name. $translation = $wrapper->getTranslation($langcode); @@ -129,7 +129,7 @@ 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'); + $wrapper = $this->container->get('locale.config.manager')->getLocaleWrapper('image.style.medium'); $translation = $wrapper->getTranslation($langcode); $property = $translation->get('label'); diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleImportFunctionalTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleImportFunctionalTest.php index fcc74d0..3e3a208 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleImportFunctionalTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleImportFunctionalTest.php @@ -291,7 +291,7 @@ function testConfigPoFile() { $this->assertText($config_string[1], format_string('Translation of @string found.', array('@string' => $config_string[0]))); } - $locale_config = $this->container->get('locale.config.typed'); + $locale_config = $this->container->get('locale.config.manager'); // Translations got recorded in the config system. foreach ($config_strings as $config_key => $config_string) { $wrapper = $locale_config->get($config_key); diff --git a/core/modules/locale/locale.bulk.inc b/core/modules/locale/locale.bulk.inc index 8a15656..cbcdac3 100644 --- a/core/modules/locale/locale.bulk.inc +++ b/core/modules/locale/locale.bulk.inc @@ -886,17 +886,7 @@ function locale_config_update_multiple(array $names, $langcodes = array()) { $langcodes = $langcodes ? $langcodes : array_keys(locale_translatable_language_list()); $count = 0; foreach ($names as $name) { - $wrapper = \Drupal\locale\Locale::config()->get($name); - foreach ($langcodes as $langcode) { - $translation = $wrapper->getValue() ? $wrapper->getTranslation($langcode)->getValue() : NULL; - if ($translation) { - \Drupal\locale\Locale::config()->saveTranslationData($name, $langcode, $translation); - $count++; - } - else { - \Drupal\locale\Locale::config()->deleteTranslationData($name, $langcode); - } - } + $count += \Drupal\locale\Locale::config()->updateConfigTranslation($name, $langcodes); } return $count; } diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module index 932b2a1..4bda875 100644 --- a/core/modules/locale/locale.module +++ b/core/modules/locale/locale.module @@ -272,22 +272,42 @@ function locale_stream_wrappers() { * Implements hook_language_insert(). */ function locale_language_insert($language) { - // @todo move these two cache clears out. See http://drupal.org/node/1293252 - // Changing the language settings impacts the interface. - cache('page')->deleteAll(); - // Force JavaScript translation file re-creation for the new language. - _locale_invalidate_js($language->id); + _locale_language_refresh($language); } /** * Implements hook_language_update(). */ function locale_language_update($language) { + _locale_language_refresh($language); +} + +/** + * Refresh translations after a language has been created or updated. + * + * @param string $langcode + * Language code. + */ +function _locale_language_refresh($language) { // @todo move these two cache clears out. See http://drupal.org/node/1293252 // Changing the language settings impacts the interface. cache('page')->deleteAll(); // Force JavaScript translation file re-creation for the modified language. _locale_invalidate_js($language->id); + + // @todo Update configuration translations for this language. + $translatable = locale_translatable_language_list(); + + if (isset($translatable[$language->id])) { + Drupal::moduleHandler()->loadInclude('locale', 'bulk.inc'); + if ($batch = locale_config_batch_update_components(array(), array($language->id))) { + batch_set($batch); + } + } + else { + // Remove translated configuration objects. + \Drupal\locale\Locale::config()->deleteLanguageTranslations($language->id); + } } /** diff --git a/core/modules/locale/locale.services.yml b/core/modules/locale/locale.services.yml index f632704..fcd777f 100644 --- a/core/modules/locale/locale.services.yml +++ b/core/modules/locale/locale.services.yml @@ -3,10 +3,13 @@ services: class: Drupal\locale\LocaleConfigSubscriber tags: - { name: event_subscriber } - arguments: ['@language_manager', '@config.context'] - locale.config.typed: + arguments: ['@language_manager', '@config.context', '@locale.config.manager'] + locale.config.storage: + class: Drupal\locale\LocaleConfigStorage + arguments: ['@config.storage', '@config.storage.installer'] + locale.config.manager: class: Drupal\locale\LocaleConfigManager - arguments: ['@config.storage', '@config.storage.schema', '@config.storage.installer', '@locale.storage'] + arguments: ['@locale.config.storage', '@config.typed', '@locale.storage'] locale.storage: class: Drupal\locale\StringDatabaseStorage arguments: ['@database']