diff --git a/core/modules/locale/lib/Drupal/locale/LocaleConfigManager.php b/core/modules/locale/lib/Drupal/locale/LocaleConfigManager.php index 66e9ecc..252778d 100644 --- a/core/modules/locale/lib/Drupal/locale/LocaleConfigManager.php +++ b/core/modules/locale/lib/Drupal/locale/LocaleConfigManager.php @@ -11,7 +11,7 @@ use Drupal\Core\Config\StorageInterface; /** - * Manages config type plugins. + * Manages localized configuration type plugins. */ class LocaleConfigManager extends TypedConfigManager { @@ -44,7 +44,8 @@ class LocaleConfigManager extends TypedConfigManager { * @param \Drupal\Core\Config\StorageInterface $installStorage * The storage controller object to use for reading default configuration data. * @param \Drupal\locale\StringStorageInterface $localeStorage - * The locale storage to use for reading string translations. + * (Optional) The locale storage to use for reading string translations. + * Defaults to locale_storage(). */ public function __construct(StorageInterface $configStorage, StorageInterface $schemaStorage, StorageInterface $installStorage, StringStorageInterface $localeStorage = NULL) { // Note we use the install storage for the parent constructor. @@ -59,24 +60,25 @@ public function __construct(StorageInterface $configStorage, StorageInterface $s * @param string $name * Configuration object name. * - * @return Drupal\locale\LocaleTypedConfig + * @return \Drupal\locale\LocaleTypedConfig * Locale-wrapped configuration element. */ public function get($name) { - // Note we get only the data that didn't change from default. + // Read default and current configuration data. $default = $this->installStorage->read($name); - // Unless the configuration has a explicit language code we assume English. - $langcode = isset($default['langcode']) ? $default['langcode'] : 'en'; $updated = $this->configStorage->read($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; } /** - * Compare default configuration with updated data. + * Compares default configuration with updated data. * * @param array $default * Default configuration data. @@ -106,13 +108,16 @@ protected function compareConfigData($default, $updated) { } /** - * Save translated configuration data. + * 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 saveConfigData($name, $langcode, $data) { + public function saveTranslationData($name, $langcode, $data) { $locale_name = 'locale.config.' . $langcode . '.' . $name; $this->configStorage->write($locale_name, $data); } @@ -121,10 +126,11 @@ public function saveConfigData($name, $langcode, $data) { * Save translated configuration data. * * @param string $name + * Configuration object name. * @param string $langcode - * @param array $data + * Language code. */ - public function deleteConfigData($name, $langcode) { + public function deleteTranslationData($name, $langcode) { $locale_name = 'locale.config.' . $langcode . '.' . $name; $this->configStorage->delete($locale_name); } @@ -133,7 +139,8 @@ public function deleteConfigData($name, $langcode) { * Gets configuration names associated with components. * * @param array $components - * Array with string identifiers. + * (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. @@ -155,19 +162,19 @@ public function getComponentNames($components) { } /** - * Deletes configuration for uninstalled components. + * Deletes configuration translations for uninstalled components. * * @param array $components * Array with string identifiers. * @param array $langcodes - * Array of language codes + * Array of language codes. */ - public function deleteComponents($components, $langcodes) { + public function deleteComponentTranslations($components, $langcodes) { $names = $this->getComponentNames($components); if ($names && $langcodes) { foreach ($names as $name) { foreach ($langcodes as $langcode) { - $this->deleteConfigData($name, $langcode); + $this->deleteTranslationData($name, $langcode); } } } @@ -197,14 +204,13 @@ public function getStringNames($lids) { * @param $langcode * Language code to delete. */ - public function deleteLanguage($langcode) { + public function deleteLanguageTranslations($langcode) { $locale_name = 'locale.config.' . $langcode; foreach ($this->configStorage->listAll($locale_name) as $name) { $this->configStorage->delete($name); } } - /** * Translates string using the localization system. * @@ -252,6 +258,7 @@ public function translateString($name, $langcode, $source, $context) { $this->translations[$name][$langcode][$context][$source] = $translation; } $translation = $this->translations[$name][$langcode][$context][$source]; + // Return the string only when the object is an actual translation. return $translation->isTranslation() ? $translation->getString() : FALSE; } return FALSE; diff --git a/core/modules/locale/lib/Drupal/locale/LocaleTypedConfig.php b/core/modules/locale/lib/Drupal/locale/LocaleTypedConfig.php index 69462b1..5f184a8 100644 --- a/core/modules/locale/lib/Drupal/locale/LocaleTypedConfig.php +++ b/core/modules/locale/lib/Drupal/locale/LocaleTypedConfig.php @@ -19,13 +19,6 @@ class LocaleTypedConfig extends Element implements TranslatableInterface { /** - * The configuration name. - * - * @var string - */ - //protected $name; - - /** * The typed configuration data. * * @var \Drupal\Core\Config\Schema\Element @@ -49,14 +42,14 @@ class LocaleTypedConfig extends Element implements TranslatableInterface { /** * Constructs a configuration wrapper object. * + * @param array $definition + * The data definition. * @param string $name * The configuration object name. - * @param \Drupal\Core\Config\Schema\Element $typedConfig - * Typed configuration element. * @param string $langcode * Language code for the source configuration data. * @param \Drupal\locale\LocaleConfigManager $localeConfig; - * Locale configuration manager that produced this wrapper. + * The locale configuration manager object. */ public function __construct(array $definition, $name, $langcode, \Drupal\locale\LocaleConfigManager $localeConfig) { parent::__construct($definition, $name); @@ -125,17 +118,18 @@ protected function canTranslate($from_langcode, $to_langcode) { } /** - * Get translated configuration data. + * Gets translated configuration data for a typed configuration element. * * @param \Drupal\Core\Config\Schema\Element $element * Typed configuration element. * @param array $options - * Array with options that will depend on the translator used. + * Array with translation options that must contain the keys defined in + * LocaleTypedConfig::translateElement() * * @return array * Configuration data translated to the requested language. */ - protected function getElementTranslation($element, $options) { + protected function getElementTranslation($element, array $options) { $translation = NULL; if ($element instanceof ArrayElement) { $translation = $this->getArrayTranslation($element, $options); @@ -152,17 +146,18 @@ protected function getElementTranslation($element, $options) { } /** - * Get translated configuration data. + * Gets translated configuration data for an element of type ArrayElement. * * @param \Traversable $element * Typed configuration element. * @param array $options - * Array with options that will depend on the translator used. + * Array with translation options that must contain the keys defined in + * LocaleTypedConfig::translateElement() * * @return array * Configuration data translated to the requested language. */ - protected function getArrayTranslation($element, $options) { + protected function getArrayTranslation(\Traversable $element, array $options) { $translation = array(); foreach ($element as $key => $property) { $value = $this->getElementTranslation($property, $options); @@ -186,12 +181,15 @@ protected function getArrayTranslation($element, $options) { * @param \Drupal\Core\TypedData\TypedDataInterface $element * Configuration element. * @param array $options - * Array with translation options that are dependent on the translator. + * Array with translation options that must contain the following keys: + * - 'source', Source language code. + * - 'target', Target language code. + * - 'strict', True to return only elements that actually have translation. * * @return bool * Whether the element fits the translation criteria. */ - protected function translateElement($element, $options) { + protected function translateElement(\Drupal\Core\TypedData\TypedDataInterface $element, array $options) { if ($this->canTranslate($options['source'], $options['target'])) { $definition = $element->getDefinition(); $value = $element->getValue(); diff --git a/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php b/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php index c42844f..eabdef0 100644 --- a/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php +++ b/core/modules/locale/lib/Drupal/locale/Tests/LocaleConfigTranslationTest.php @@ -78,8 +78,6 @@ function testConfigTranslation() { ); $this->drupalPost('admin/config/regional/translate/translate', $edit, t('Save translations')); - //$typed_config = config_typed()->get('system.site'); - //$wrapper = new LocaleTypedConfig('system.site', 'en', $typed_config, $this->storage); $wrapper = locale_config()->get('system.site'); // Get strict translation and check we've got only the site name. @@ -102,7 +100,7 @@ function testConfigTranslation() { $string = $this->storage->findString(array('source' => 'Medium (220x220)', 'context' => '', 'type' => 'configuration')); $this->assertFalse($string, 'Configuration strings have been created upon installation.'); - // Enable the image module + // Enable the image module. $this->drupalPost('admin/modules', array('modules[Core][image][enable]' => "1"), t('Save configuration')); $this->resetAll(); @@ -110,6 +108,7 @@ function testConfigTranslation() { $this->assertTrue($string, 'Configuration strings have been created upon installation.'); $locations = $string->getLocations(); $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')); $translation = reset($translations); @@ -129,14 +128,13 @@ function testConfigTranslation() { $lid => $image_style_label, ); $this->drupalPost('admin/config/regional/translate/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')); $translation = reset($translations); $this->assertTrue(count($translations) == 1 && $translation->source == $string->source && $translation->translation == $image_style_label, 'Got only one translation for image configuration.'); - // This really should be testing entity_load_multiple. - //$typed_config = config_typed()->get('image.style.medium'); - //$wrapper = new LocaleTypedConfig('image.style.medium', 'en', $typed_config, $this->storage); + // Try more complex configuration data. $wrapper = locale_config()->get('image.style.medium'); $translation = $wrapper->getTranslation($langcode, TRUE); $property = $translation->get('label'); diff --git a/core/modules/locale/locale.bulk.inc b/core/modules/locale/locale.bulk.inc index 0547aa3..0b6f1b2 100644 --- a/core/modules/locale/locale.bulk.inc +++ b/core/modules/locale/locale.bulk.inc @@ -285,8 +285,6 @@ function locale_translate_export_form_submit($form, &$form_state) { * after the import. Optional, defaults to FALSE. * - 'finish_feedback': Whether or not to give feedback to the user when the * batch is finished. Optional, defaults to TRUE. - * - 'components': Array of arrays of components to refresh the configuration - * indexed by type ('module' or 'theme'). Optional, defaults to none. * * @param $force * (optional) Import all available files, even if they were imported before. @@ -408,6 +406,7 @@ function locale_translate_batch_build($files, $options) { } // Save the translation status of all files. $operations[] = array('locale_translate_batch_import_save', array()); + // Add a final step to refresh JavaScript and configuration strings. $operations[] = array('locale_translate_batch_refresh', array($options)); @@ -569,32 +568,40 @@ function locale_translate_batch_refresh($options, &$context) { } } if ($strings) { + // Initialize multi-step string refresh. $context['message'] = t('Updating translations for JavaScript and Configuration strings.'); - $strings = array_unique($strings); - // Clear cache and force refresh of JavaScript translations. - _locale_refresh_translations($langcodes, $strings); - // Check whether we need to refresh configuration objects. - if ($options['refresh_configuration'] && $names = locale_config()->getStringNames($strings)) { - $context['sandbox']['refresh']['names'] = $names; - $context['sandbox']['refresh']['languages'] = $langcodes; - $context['sandbox']['refresh']['count'] = count($names); + $context['sandbox']['refresh']['strings'] = array_unique($strings); + $context['sandbox']['refresh']['languages'] = $langcodes; + if (!empty($options['refresh_configuration'])) { + $context['sandbox']['refresh']['names'] = array(); $context['results']['stats']['config'] = 0; } - } - if (isset($context['sandbox']['refresh'])) { - // We will update configuration on next steps. - $context['finished'] = 1 / $context['sandbox']['refresh']['count']; + $context['sandbox']['refresh']['count'] = count($strings); + + // We will update strings on later steps. + $context['finished'] = 1 - 1 / $context['sandbox']['refresh']['count']; } else { $context['finished'] = 1; } } - elseif ($name = array_shift($context['sandbox']['refresh']['names'])) { + elseif (!empty($options['refresh_configuration']) && $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']); $context['results']['stats']['config'] += $count; - // Not perfect but will give some idea of progress. - $context['finished'] = 1 - count($context['sandbox']['refresh']['names']) / $context['sandbox']['refresh']['count']; + } + elseif (!empty($context['sandbox']['refresh']['strings'])) { + // Not perfect but will give some indication of progress. + $context['finished'] = 1 - count($context['sandbox']['refresh']['strings']) / $context['sandbox']['refresh']['count']; + // Pending strings, refresh 100 at a time, get next pack. + $next = array_slice($context['sandbox']['refresh']['strings'], 0, 100); + array_splice($context['sandbox']['refresh']['strings'], 0, count($next)); + // Clear cache and force refresh of JavaScript translations. + _locale_refresh_translations($context['sandbox']['refresh']['languages'], $next); + // Check whether we need to refresh configuration objects. + if (!empty($options['refresh_configuration']) && $names = locale_config()->getStringNames($next)) { + $context['sandbox']['refresh']['names'] = $names; + } } else { $context['finished'] = 1; @@ -607,7 +614,6 @@ function locale_translate_batch_refresh($options, &$context) { function locale_translate_batch_finished($success, $results) { if ($success) { $additions = $updates = $deletes = $skips = $config = 0; - $strings = $langcodes = array(); if (isset($results['failed_files'])) { if (module_exists('dblog')) { $message = format_plural(count($results['failed_files']), 'One translation file could not be imported. See the log for details.', '@count translation files could not be imported. See the log for details.', array('@url' => url('admin/reports/dblog'))); @@ -630,11 +636,7 @@ function locale_translate_batch_finished($success, $results) { if ($report['skips'] > 0) { $skipped_files[] = $filepath; } - $strings = array_merge($strings, $report['strings']); } - // Get list of unique string identifiers and language codes updated. - $strings = array_unique($strings); - $langcodes = array_unique(array_values($results['languages'])); } drupal_set_message(format_plural(count($results['files']), 'One translation file imported. %number translations were added, %update translations were updated and %delete translations were removed.', @@ -653,17 +655,12 @@ function locale_translate_batch_finished($success, $results) { drupal_set_message($message, 'warning'); watchdog('locale', '@count disallowed HTML string(s) in files: @files.', array('@count' => $skips, '@files' => implode(',', $skipped_files)), WATCHDOG_WARNING); } - - if ($strings) { - // Clear cache and force refresh of JavaScript translations. - _locale_refresh_translations($langcodes); - // Merge feedback about configuration updates too. - if (isset($results['stats']['config'])) { - locale_config_batch_finished($success, $results); - } - } } } + // Add messages for configuration too. + if (isset($results['stats']['config'])) { + locale_config_batch_finished($success, $results); + } } /** @@ -684,7 +681,7 @@ function locale_translate_file_create($filepath) { } /** - * Generate file properties from filename and options. + * Generates file properties from filename and options. * * An attempt is made to determine the translation language, project name and * project version from the file name. Supported file name patterns are: @@ -806,7 +803,7 @@ function locale_config_batch_build($names, $langcodes, $options = array()) { $batch = array( 'operations' => $operations, 'title' => $t('Updating configuration translations'), - 'init_message' => $t('Starting update'), + 'init_message' => $t('Starting configuration update'), 'error_message' => $t('Error updating configuration translations'), 'file' => drupal_get_path('module', 'locale') . '/locale.bulk.inc', ); @@ -859,7 +856,7 @@ function locale_config_batch_finished($success, $results) { } /** - * Update all configuration for names / languages. + * Updates all configuration for names / languages. * * @param array $names * Array of names of configuration objects to update. @@ -876,11 +873,11 @@ function locale_config_update_multiple($names, $langcodes = array()) { foreach ($langcodes as $langcode) { $translation = $wrapper->getValue() ? $wrapper->getTranslation($langcode, TRUE)->getValue() : NULL; if ($translation) { - locale_config()->saveConfigData($name, $langcode, $translation); + locale_config()->saveTranslationData($name, $langcode, $translation); $count++; } else { - locale_config()->deleteConfigData($name, $langcode); + locale_config()->deleteTranslationData($name, $langcode); } } } diff --git a/core/modules/locale/locale.module b/core/modules/locale/locale.module index 76cd7dc..59ad5cd 100644 --- a/core/modules/locale/locale.module +++ b/core/modules/locale/locale.module @@ -320,7 +320,7 @@ function locale_language_delete($language) { locale_translate_delete_translation_files(array(), array($language->langcode)); // Remove translated configuration objects. - locale_config()->deleteLanguage($language->langcode); + locale_config()->deleteLanguageTranslations($language->langcode); // Changing the language settings impacts the interface: _locale_invalidate_js($language->langcode); @@ -564,7 +564,7 @@ function locale_system_remove($components) { module_load_include('compare.inc', 'locale'); module_load_include('bulk.inc', 'locale'); // Delete configuration translations. - locale_config()->deleteComponents($components, array_keys($language_list)); + 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 modules. @@ -1356,14 +1356,14 @@ function _locale_rebuild_js($langcode = NULL) { } /** - * Returns the locale config manager service. + * Returns the locale configuration manager service. * * Use the locale config manager service for creating locale-wrapped typed * configuration objects. * * @see Drupal\Core\TypedData\TypedDataManager::create() * - * @return Drupal\locale\LocaleConfigManager + * @return \Drupal\locale\LocaleConfigManager */ function locale_config() { return drupal_container()->get('locale.config.typed'); diff --git a/core/modules/locale/locale.pages.inc b/core/modules/locale/locale.pages.inc index f571ce5..a109f5f 100644 --- a/core/modules/locale/locale.pages.inc +++ b/core/modules/locale/locale.pages.inc @@ -453,6 +453,7 @@ function locale_translate_edit_form_submit($form, &$form_state) { if ($updated) { // Clear cache and refresh configuration and JavaScript translations. + _locale_refresh_translations(array($langcode), $updated); _locale_refresh_configuration(array($langcode), $updated); }