annotate core/modules/locale/src/LocaleConfigManager.php @ 19:fa3358dc1485 tip

Add ndrum files
author Chris Cannam
date Wed, 28 Aug 2019 13:14:47 +0100
parents 129ea1e6d783
children
rev   line source
Chris@0 1 <?php
Chris@0 2
Chris@0 3 namespace Drupal\locale;
Chris@0 4
Chris@0 5 use Drupal\Component\Utility\NestedArray;
Chris@0 6 use Drupal\Core\Config\ConfigFactoryInterface;
Chris@0 7 use Drupal\Core\Config\ConfigManagerInterface;
Chris@0 8 use Drupal\Core\Config\StorageInterface;
Chris@0 9 use Drupal\Core\Config\TypedConfigManagerInterface;
Chris@0 10 use Drupal\Core\StringTranslation\TranslatableMarkup;
Chris@0 11 use Drupal\Core\TypedData\TraversableTypedDataInterface;
Chris@0 12 use Drupal\Core\TypedData\TypedDataInterface;
Chris@0 13 use Drupal\language\ConfigurableLanguageManagerInterface;
Chris@0 14
Chris@0 15 /**
Chris@0 16 * Manages configuration supported in part by interface translation.
Chris@0 17 *
Chris@0 18 * This manager is responsible to update configuration overrides and active
Chris@0 19 * translations when interface translation data changes. This allows Drupal to
Chris@0 20 * translate user roles, views, blocks, etc. after Drupal has been installed
Chris@0 21 * using the locale module's storage. When translations change in locale,
Chris@0 22 * LocaleConfigManager::updateConfigTranslations() is invoked to update the
Chris@0 23 * corresponding storage of the translation in the original config object or an
Chris@0 24 * override.
Chris@0 25 *
Chris@0 26 * In turn when translated configuration or configuration language overrides are
Chris@0 27 * changed, it is the responsibility of LocaleConfigSubscriber to update locale
Chris@0 28 * storage.
Chris@0 29 *
Chris@0 30 * By design locale module only deals with sources in English.
Chris@0 31 *
Chris@0 32 * @see \Drupal\locale\LocaleConfigSubscriber
Chris@0 33 */
Chris@0 34 class LocaleConfigManager {
Chris@0 35
Chris@0 36 /**
Chris@0 37 * The storage instance for reading configuration data.
Chris@0 38 *
Chris@0 39 * @var \Drupal\Core\Config\StorageInterface
Chris@0 40 */
Chris@0 41 protected $configStorage;
Chris@0 42
Chris@0 43 /**
Chris@0 44 * The string storage for reading and writing translations.
Chris@0 45 *
Chris@17 46 * @var \Drupal\locale\StringStorageInterface
Chris@0 47 */
Chris@0 48 protected $localeStorage;
Chris@0 49
Chris@0 50 /**
Chris@0 51 * Array with preloaded string translations.
Chris@0 52 *
Chris@0 53 * @var array
Chris@0 54 */
Chris@0 55 protected $translations;
Chris@0 56
Chris@0 57 /**
Chris@0 58 * The configuration factory.
Chris@0 59 *
Chris@0 60 * @var \Drupal\Core\Config\ConfigFactoryInterface
Chris@0 61 */
Chris@0 62 protected $configFactory;
Chris@0 63
Chris@0 64 /**
Chris@0 65 * The language manager.
Chris@0 66 *
Chris@0 67 * @var \Drupal\language\ConfigurableLanguageManagerInterface
Chris@0 68 */
Chris@0 69 protected $languageManager;
Chris@0 70
Chris@0 71 /**
Chris@0 72 * The typed config manager.
Chris@0 73 *
Chris@0 74 * @var \Drupal\Core\Config\TypedConfigManagerInterface
Chris@0 75 */
Chris@0 76 protected $typedConfigManager;
Chris@0 77
Chris@0 78 /**
Chris@0 79 * Whether or not configuration translations are being updated from locale.
Chris@0 80 *
Chris@0 81 * @see self::isUpdatingFromLocale()
Chris@0 82 *
Chris@0 83 * @var bool
Chris@0 84 */
Chris@0 85 protected $isUpdatingFromLocale = FALSE;
Chris@0 86
Chris@0 87 /**
Chris@0 88 * The locale default config storage instance.
Chris@0 89 *
Chris@0 90 * @var \Drupal\locale\LocaleDefaultConfigStorage
Chris@0 91 */
Chris@0 92 protected $defaultConfigStorage;
Chris@0 93
Chris@0 94 /**
Chris@0 95 * The configuration manager.
Chris@0 96 *
Chris@0 97 * @var \Drupal\Core\Config\ConfigManagerInterface
Chris@0 98 */
Chris@0 99 protected $configManager;
Chris@0 100
Chris@0 101 /**
Chris@0 102 * Creates a new typed configuration manager.
Chris@0 103 *
Chris@0 104 * @param \Drupal\Core\Config\StorageInterface $config_storage
Chris@0 105 * The storage object to use for reading configuration data.
Chris@0 106 * @param \Drupal\locale\StringStorageInterface $locale_storage
Chris@0 107 * The locale storage to use for reading string translations.
Chris@0 108 * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
Chris@0 109 * The configuration factory
Chris@0 110 * @param \Drupal\Core\Config\TypedConfigManagerInterface $typed_config
Chris@0 111 * The typed configuration manager.
Chris@0 112 * @param \Drupal\language\ConfigurableLanguageManagerInterface $language_manager
Chris@0 113 * The language manager.
Chris@0 114 * @param \Drupal\locale\LocaleDefaultConfigStorage $default_config_storage
Chris@0 115 * The locale default configuration storage.
Chris@0 116 * @param \Drupal\Core\Config\ConfigManagerInterface $config_manager
Chris@0 117 * The configuration manager.
Chris@0 118 */
Chris@0 119 public function __construct(StorageInterface $config_storage, StringStorageInterface $locale_storage, ConfigFactoryInterface $config_factory, TypedConfigManagerInterface $typed_config, ConfigurableLanguageManagerInterface $language_manager, LocaleDefaultConfigStorage $default_config_storage, ConfigManagerInterface $config_manager) {
Chris@0 120 $this->configStorage = $config_storage;
Chris@0 121 $this->localeStorage = $locale_storage;
Chris@0 122 $this->configFactory = $config_factory;
Chris@0 123 $this->typedConfigManager = $typed_config;
Chris@0 124 $this->languageManager = $language_manager;
Chris@0 125 $this->defaultConfigStorage = $default_config_storage;
Chris@0 126 $this->configManager = $config_manager;
Chris@0 127 }
Chris@0 128
Chris@0 129 /**
Chris@0 130 * Gets array of translated strings for Locale translatable configuration.
Chris@0 131 *
Chris@0 132 * @param string $name
Chris@0 133 * Configuration object name.
Chris@0 134 *
Chris@0 135 * @return array
Chris@0 136 * Array of Locale translatable elements of the default configuration in
Chris@0 137 * $name.
Chris@0 138 */
Chris@0 139 public function getTranslatableDefaultConfig($name) {
Chris@0 140 if ($this->isSupported($name)) {
Chris@0 141 // Create typed configuration wrapper based on install storage data.
Chris@0 142 $data = $this->defaultConfigStorage->read($name);
Chris@0 143 $typed_config = $this->typedConfigManager->createFromNameAndData($name, $data);
Chris@0 144 if ($typed_config instanceof TraversableTypedDataInterface) {
Chris@0 145 return $this->getTranslatableData($typed_config);
Chris@0 146 }
Chris@0 147 }
Chris@0 148 return [];
Chris@0 149 }
Chris@0 150
Chris@0 151 /**
Chris@0 152 * Gets translatable configuration data for a typed configuration element.
Chris@0 153 *
Chris@0 154 * @param \Drupal\Core\TypedData\TypedDataInterface $element
Chris@0 155 * Typed configuration element.
Chris@0 156 *
Chris@0 157 * @return array|\Drupal\Core\StringTranslation\TranslatableMarkup
Chris@0 158 * A nested array matching the exact structure under $element with only the
Chris@0 159 * elements that are translatable wrapped into a TranslatableMarkup. If the
Chris@0 160 * provided $element is not traversable, the return value is a single
Chris@0 161 * TranslatableMarkup.
Chris@0 162 */
Chris@0 163 protected function getTranslatableData(TypedDataInterface $element) {
Chris@0 164 $translatable = [];
Chris@0 165 if ($element instanceof TraversableTypedDataInterface) {
Chris@0 166 foreach ($element as $key => $property) {
Chris@0 167 $value = $this->getTranslatableData($property);
Chris@0 168 if (!empty($value)) {
Chris@0 169 $translatable[$key] = $value;
Chris@0 170 }
Chris@0 171 }
Chris@0 172 }
Chris@0 173 else {
Chris@0 174 // Something is only translatable by Locale if there is a string in the
Chris@0 175 // first place.
Chris@0 176 $value = $element->getValue();
Chris@0 177 $definition = $element->getDataDefinition();
Chris@0 178 if (!empty($definition['translatable']) && $value !== '' && $value !== NULL) {
Chris@0 179 $options = [];
Chris@0 180 if (isset($definition['translation context'])) {
Chris@0 181 $options['context'] = $definition['translation context'];
Chris@0 182 }
Chris@0 183 return new TranslatableMarkup($value, [], $options);
Chris@0 184 }
Chris@0 185 }
Chris@0 186 return $translatable;
Chris@0 187 }
Chris@0 188
Chris@0 189 /**
Chris@0 190 * Process the translatable data array with a given language.
Chris@0 191 *
Chris@0 192 * If the given language is translatable, will return the translated copy
Chris@0 193 * which will only contain strings that had translations. If the given
Chris@0 194 * language is English and is not translatable, will return a simplified
Chris@0 195 * array of the English source strings only.
Chris@0 196 *
Chris@0 197 * @param string $name
Chris@0 198 * The configuration name.
Chris@0 199 * @param array $active
Chris@0 200 * The active configuration data.
Chris@0 201 * @param array|\Drupal\Core\StringTranslation\TranslatableMarkup[] $translatable
Chris@0 202 * The translatable array structure. A nested array matching the exact
Chris@0 203 * structure under of the default configuration for $name with only the
Chris@0 204 * elements that are translatable wrapped into a TranslatableMarkup.
Chris@0 205 * @param string $langcode
Chris@0 206 * The language code to process the array with.
Chris@0 207 *
Chris@0 208 * @return array
Chris@0 209 * Processed translatable data array. Will only contain translations
Chris@0 210 * different from source strings or in case of untranslatable English, the
Chris@0 211 * source strings themselves.
Chris@0 212 *
Chris@0 213 * @see self::getTranslatableData()
Chris@0 214 */
Chris@0 215 protected function processTranslatableData($name, array $active, array $translatable, $langcode) {
Chris@0 216 $translated = [];
Chris@0 217 foreach ($translatable as $key => $item) {
Chris@0 218 if (!isset($active[$key])) {
Chris@0 219 continue;
Chris@0 220 }
Chris@0 221 if (is_array($item)) {
Chris@0 222 // Only add this key if there was a translated value underneath.
Chris@0 223 $value = $this->processTranslatableData($name, $active[$key], $item, $langcode);
Chris@0 224 if (!empty($value)) {
Chris@0 225 $translated[$key] = $value;
Chris@0 226 }
Chris@0 227 }
Chris@0 228 else {
Chris@0 229 if (locale_is_translatable($langcode)) {
Chris@0 230 $value = $this->translateString($name, $langcode, $item->getUntranslatedString(), $item->getOption('context'));
Chris@0 231 }
Chris@0 232 else {
Chris@0 233 $value = $item->getUntranslatedString();
Chris@0 234 }
Chris@0 235 if (!empty($value)) {
Chris@0 236 $translated[$key] = $value;
Chris@0 237 }
Chris@0 238 }
Chris@0 239 }
Chris@0 240 return $translated;
Chris@0 241 }
Chris@0 242
Chris@0 243 /**
Chris@0 244 * Saves translated configuration override.
Chris@0 245 *
Chris@0 246 * @param string $name
Chris@0 247 * Configuration object name.
Chris@0 248 * @param string $langcode
Chris@0 249 * Language code.
Chris@0 250 * @param array $data
Chris@0 251 * Configuration data to be saved, that will be only the translated values.
Chris@0 252 */
Chris@0 253 protected function saveTranslationOverride($name, $langcode, array $data) {
Chris@0 254 $this->isUpdatingFromLocale = TRUE;
Chris@0 255 $this->languageManager->getLanguageConfigOverride($langcode, $name)->setData($data)->save();
Chris@0 256 $this->isUpdatingFromLocale = FALSE;
Chris@0 257 }
Chris@0 258
Chris@0 259 /**
Chris@0 260 * Saves translated configuration data.
Chris@0 261 *
Chris@0 262 * @param string $name
Chris@0 263 * Configuration object name.
Chris@0 264 * @param array $data
Chris@0 265 * Configuration data to be saved with translations merged in.
Chris@0 266 */
Chris@0 267 protected function saveTranslationActive($name, array $data) {
Chris@0 268 $this->isUpdatingFromLocale = TRUE;
Chris@0 269 $this->configFactory->getEditable($name)->setData($data)->save();
Chris@0 270 $this->isUpdatingFromLocale = FALSE;
Chris@0 271 }
Chris@0 272
Chris@0 273 /**
Chris@0 274 * Deletes translated configuration data.
Chris@0 275 *
Chris@0 276 * @param string $name
Chris@0 277 * Configuration object name.
Chris@0 278 * @param string $langcode
Chris@0 279 * Language code.
Chris@0 280 */
Chris@0 281 protected function deleteTranslationOverride($name, $langcode) {
Chris@0 282 $this->isUpdatingFromLocale = TRUE;
Chris@0 283 $this->languageManager->getLanguageConfigOverride($langcode, $name)->delete();
Chris@0 284 $this->isUpdatingFromLocale = FALSE;
Chris@0 285 }
Chris@0 286
Chris@0 287 /**
Chris@0 288 * Gets configuration names associated with components.
Chris@0 289 *
Chris@0 290 * @param array $components
Chris@0 291 * (optional) Array of component lists indexed by type. If not present or it
Chris@0 292 * is an empty array, it will update all components.
Chris@0 293 *
Chris@0 294 * @return array
Chris@0 295 * Array of configuration object names.
Chris@0 296 */
Chris@0 297 public function getComponentNames(array $components = []) {
Chris@0 298 $components = array_filter($components);
Chris@0 299 if ($components) {
Chris@0 300 $names = [];
Chris@0 301 foreach ($components as $type => $list) {
Chris@0 302 // InstallStorage::getComponentNames returns a list of folders keyed by
Chris@0 303 // config name.
Chris@0 304 $names = array_merge($names, $this->defaultConfigStorage->getComponentNames($type, $list));
Chris@0 305 }
Chris@0 306 return $names;
Chris@0 307 }
Chris@0 308 else {
Chris@0 309 return $this->defaultConfigStorage->listAll();
Chris@0 310 }
Chris@0 311 }
Chris@0 312
Chris@0 313 /**
Chris@0 314 * Gets configuration names associated with strings.
Chris@0 315 *
Chris@0 316 * @param array $lids
Chris@0 317 * Array with string identifiers.
Chris@0 318 *
Chris@0 319 * @return array
Chris@0 320 * Array of configuration object names.
Chris@0 321 */
Chris@0 322 public function getStringNames(array $lids) {
Chris@0 323 $names = [];
Chris@0 324 $locations = $this->localeStorage->getLocations(['sid' => $lids, 'type' => 'configuration']);
Chris@0 325 foreach ($locations as $location) {
Chris@0 326 $names[$location->name] = $location->name;
Chris@0 327 }
Chris@0 328 return $names;
Chris@0 329 }
Chris@0 330
Chris@0 331 /**
Chris@0 332 * Deletes configuration for language.
Chris@0 333 *
Chris@0 334 * @param string $langcode
Chris@0 335 * Language code to delete.
Chris@0 336 */
Chris@0 337 public function deleteLanguageTranslations($langcode) {
Chris@0 338 $this->isUpdatingFromLocale = TRUE;
Chris@0 339 $storage = $this->languageManager->getLanguageConfigOverrideStorage($langcode);
Chris@0 340 foreach ($storage->listAll() as $name) {
Chris@0 341 $this->languageManager->getLanguageConfigOverride($langcode, $name)->delete();
Chris@0 342 }
Chris@0 343 $this->isUpdatingFromLocale = FALSE;
Chris@0 344 }
Chris@0 345
Chris@0 346 /**
Chris@0 347 * Translates string using the localization system.
Chris@0 348 *
Chris@0 349 * So far we only know how to translate strings from English so the source
Chris@0 350 * string should be in English.
Chris@0 351 * Unlike regular t() translations, strings will be added to the source
Chris@0 352 * tables only if this is marked as default data.
Chris@0 353 *
Chris@0 354 * @param string $name
Chris@0 355 * Name of the configuration location.
Chris@0 356 * @param string $langcode
Chris@0 357 * Language code to translate to.
Chris@0 358 * @param string $source
Chris@0 359 * The source string, should be English.
Chris@0 360 * @param string $context
Chris@0 361 * The string context.
Chris@0 362 *
Chris@0 363 * @return string|false
Chris@0 364 * Translated string if there is a translation, FALSE if not.
Chris@0 365 */
Chris@0 366 public function translateString($name, $langcode, $source, $context) {
Chris@0 367 if ($source) {
Chris@0 368 // If translations for a language have not been loaded yet.
Chris@0 369 if (!isset($this->translations[$name][$langcode])) {
Chris@0 370 // Preload all translations for this configuration name and language.
Chris@0 371 $this->translations[$name][$langcode] = [];
Chris@0 372 foreach ($this->localeStorage->getTranslations(['language' => $langcode, 'type' => 'configuration', 'name' => $name]) as $string) {
Chris@0 373 $this->translations[$name][$langcode][$string->context][$string->source] = $string;
Chris@0 374 }
Chris@0 375 }
Chris@0 376 if (!isset($this->translations[$name][$langcode][$context][$source])) {
Chris@0 377 // There is no translation of the source string in this config location
Chris@0 378 // to this language for this context.
Chris@0 379 if ($translation = $this->localeStorage->findTranslation(['source' => $source, 'context' => $context, 'language' => $langcode])) {
Chris@0 380 // Look for a translation of the string. It might have one, but not
Chris@0 381 // be saved in this configuration location yet.
Chris@0 382 // If the string has a translation for this context to this language,
Chris@0 383 // save it in the configuration location so it can be looked up faster
Chris@0 384 // next time.
Chris@0 385 $this->localeStorage->createString((array) $translation)
Chris@0 386 ->addLocation('configuration', $name)
Chris@0 387 ->save();
Chris@0 388 }
Chris@0 389 else {
Chris@0 390 // No translation was found. Add the source to the configuration
Chris@0 391 // location so it can be translated, and the string is faster to look
Chris@0 392 // for next time.
Chris@0 393 $translation = $this->localeStorage
Chris@0 394 ->createString(['source' => $source, 'context' => $context])
Chris@0 395 ->addLocation('configuration', $name)
Chris@0 396 ->save();
Chris@0 397 }
Chris@0 398
Chris@0 399 // Add an entry, either the translation found, or a blank string object
Chris@0 400 // to track the source string, to this configuration location, language,
Chris@0 401 // and context.
Chris@0 402 $this->translations[$name][$langcode][$context][$source] = $translation;
Chris@0 403 }
Chris@0 404
Chris@0 405 // Return the string only when the string object had a translation.
Chris@0 406 if ($this->translations[$name][$langcode][$context][$source]->isTranslation()) {
Chris@0 407 return $this->translations[$name][$langcode][$context][$source]->getString();
Chris@0 408 }
Chris@0 409 }
Chris@0 410 return FALSE;
Chris@0 411 }
Chris@0 412
Chris@0 413 /**
Chris@0 414 * Reset static cache of configuration string translations.
Chris@0 415 *
Chris@0 416 * @return $this
Chris@0 417 */
Chris@0 418 public function reset() {
Chris@0 419 $this->translations = [];
Chris@0 420 return $this;
Chris@0 421 }
Chris@0 422
Chris@0 423 /**
Chris@0 424 * Get the translation object for the given source/context and language.
Chris@0 425 *
Chris@0 426 * @param string $name
Chris@0 427 * Name of the configuration location.
Chris@0 428 * @param string $langcode
Chris@0 429 * Language code to translate to.
Chris@0 430 * @param string $source
Chris@0 431 * The source string, should be English.
Chris@0 432 * @param string $context
Chris@0 433 * The string context.
Chris@0 434 *
Chris@0 435 * @return \Drupal\locale\TranslationString|false
Chris@0 436 * The translation object if the string was not empty or FALSE otherwise.
Chris@0 437 */
Chris@0 438 public function getStringTranslation($name, $langcode, $source, $context) {
Chris@0 439 if ($source) {
Chris@0 440 $this->translateString($name, $langcode, $source, $context);
Chris@0 441 if ($string = $this->translations[$name][$langcode][$context][$source]) {
Chris@0 442 if (!$string->isTranslation()) {
Chris@0 443 $conditions = ['lid' => $string->lid, 'language' => $langcode];
Chris@0 444 $translation = $this->localeStorage->createTranslation($conditions);
Chris@0 445 $this->translations[$name][$langcode][$context][$source] = $translation;
Chris@0 446 return $translation;
Chris@0 447 }
Chris@0 448 else {
Chris@0 449 return $string;
Chris@0 450 }
Chris@0 451 }
Chris@0 452 }
Chris@0 453 return FALSE;
Chris@0 454 }
Chris@0 455
Chris@0 456 /**
Chris@0 457 * Checks whether a language has configuration translation.
Chris@0 458 *
Chris@0 459 * @param string $name
Chris@0 460 * Configuration name.
Chris@0 461 * @param string $langcode
Chris@0 462 * A language code.
Chris@0 463 *
Chris@0 464 * @return bool
Chris@0 465 * A boolean indicating if a language has configuration translations.
Chris@0 466 */
Chris@0 467 public function hasTranslation($name, $langcode) {
Chris@0 468 $translation = $this->languageManager->getLanguageConfigOverride($langcode, $name);
Chris@0 469 return !$translation->isNew();
Chris@0 470 }
Chris@0 471
Chris@0 472 /**
Chris@0 473 * Returns the original language code for this shipped configuration.
Chris@0 474 *
Chris@0 475 * @param string $name
Chris@0 476 * The configuration name.
Chris@0 477 *
Chris@0 478 * @return null|string
Chris@0 479 * Language code of the default configuration for $name. If the default
Chris@0 480 * configuration data for $name did not contain a language code, it is
Chris@0 481 * assumed to be English. The return value is NULL if no such default
Chris@0 482 * configuration exists.
Chris@0 483 */
Chris@0 484 public function getDefaultConfigLangcode($name) {
Chris@0 485 // Config entities that do not have the 'default_config_hash' cannot be
Chris@0 486 // shipped configuration regardless of whether there is a name match.
Chris@0 487 // configurable_language entities are a special case since they can be
Chris@0 488 // translated regardless of whether they are shipped if they in the standard
Chris@0 489 // language list.
Chris@0 490 $config_entity_type = $this->configManager->getEntityTypeIdByName($name);
Chris@0 491 if (!$config_entity_type || $config_entity_type === 'configurable_language'
Chris@0 492 || !empty($this->configFactory->get($name)->get('_core.default_config_hash'))
Chris@0 493 ) {
Chris@0 494 $shipped = $this->defaultConfigStorage->read($name);
Chris@0 495 if (!empty($shipped)) {
Chris@0 496 return !empty($shipped['langcode']) ? $shipped['langcode'] : 'en';
Chris@0 497 }
Chris@0 498 }
Chris@0 499 return NULL;
Chris@0 500 }
Chris@0 501
Chris@0 502 /**
Chris@0 503 * Returns the current language code for this active configuration.
Chris@0 504 *
Chris@0 505 * @param string $name
Chris@0 506 * The configuration name.
Chris@0 507 *
Chris@0 508 * @return null|string
Chris@0 509 * Language code of the current active configuration for $name. If the
Chris@0 510 * configuration data for $name did not contain a language code, it is
Chris@0 511 * assumed to be English. The return value is NULL if no such active
Chris@0 512 * configuration exists.
Chris@0 513 */
Chris@0 514 public function getActiveConfigLangcode($name) {
Chris@0 515 $active = $this->configStorage->read($name);
Chris@0 516 if (!empty($active)) {
Chris@0 517 return !empty($active['langcode']) ? $active['langcode'] : 'en';
Chris@0 518 }
Chris@0 519 }
Chris@0 520
Chris@0 521 /**
Chris@0 522 * Whether the given configuration is supported for interface translation.
Chris@0 523 *
Chris@0 524 * @param string $name
Chris@0 525 * The configuration name.
Chris@0 526 *
Chris@0 527 * @return bool
Chris@0 528 * TRUE if interface translation is supported.
Chris@0 529 */
Chris@0 530 public function isSupported($name) {
Chris@0 531 return $this->getDefaultConfigLangcode($name) == 'en' && $this->configStorage->read($name);
Chris@0 532 }
Chris@0 533
Chris@0 534 /**
Chris@0 535 * Indicates whether configuration translations are being updated from locale.
Chris@0 536 *
Chris@0 537 * @return bool
Chris@0 538 * Whether or not configuration translations are currently being updated.
Chris@0 539 * If TRUE, LocaleConfigManager is in control of the process and the
Chris@0 540 * reference data is locale's storage. Changes made to active configuration
Chris@0 541 * and overrides in this case should not feed back to locale storage.
Chris@0 542 * On the other hand, when not updating from locale and configuration
Chris@0 543 * translations change, we need to feed back to the locale storage.
Chris@0 544 */
Chris@0 545 public function isUpdatingTranslationsFromLocale() {
Chris@0 546 return $this->isUpdatingFromLocale;
Chris@0 547 }
Chris@0 548
Chris@0 549 /**
Chris@0 550 * Updates all configuration translations for the names / languages provided.
Chris@0 551 *
Chris@0 552 * To be used when interface translation changes result in the need to update
Chris@0 553 * configuration translations to keep them in sync.
Chris@0 554 *
Chris@0 555 * @param array $names
Chris@0 556 * Array of names of configuration objects to update.
Chris@0 557 * @param array $langcodes
Chris@0 558 * (optional) Array of language codes to update. Defaults to all
Chris@0 559 * configurable languages.
Chris@0 560 *
Chris@0 561 * @return int
Chris@0 562 * Total number of configuration override and active configuration objects
Chris@0 563 * updated (saved or removed).
Chris@0 564 */
Chris@0 565 public function updateConfigTranslations(array $names, array $langcodes = []) {
Chris@0 566 $langcodes = $langcodes ? $langcodes : array_keys($this->languageManager->getLanguages());
Chris@0 567 $count = 0;
Chris@0 568 foreach ($names as $name) {
Chris@0 569 $translatable = $this->getTranslatableDefaultConfig($name);
Chris@0 570 if (empty($translatable)) {
Chris@0 571 // If there is nothing translatable in this configuration or not
Chris@0 572 // supported, skip it.
Chris@0 573 continue;
Chris@0 574 }
Chris@0 575
Chris@0 576 $active_langcode = $this->getActiveConfigLangcode($name);
Chris@0 577 $active = $this->configStorage->read($name);
Chris@0 578
Chris@0 579 foreach ($langcodes as $langcode) {
Chris@0 580 $processed = $this->processTranslatableData($name, $active, $translatable, $langcode);
Chris@0 581 // If the language code is not the same as the active storage
Chris@0 582 // language, we should update the configuration override.
Chris@0 583 if ($langcode != $active_langcode) {
Chris@0 584 $override = $this->languageManager->getLanguageConfigOverride($langcode, $name);
Chris@0 585 // Filter out locale managed configuration keys so that translations
Chris@0 586 // removed from Locale will be reflected in the config override.
Chris@0 587 $data = $this->filterOverride($override->get(), $translatable);
Chris@0 588 if (!empty($processed)) {
Chris@0 589 // Merge in the Locale managed translations with existing data.
Chris@0 590 $data = NestedArray::mergeDeepArray([$data, $processed], TRUE);
Chris@0 591 }
Chris@0 592 if (empty($data) && !$override->isNew()) {
Chris@0 593 // The configuration override contains Locale overrides that no
Chris@0 594 // longer exist.
Chris@0 595 $this->deleteTranslationOverride($name, $langcode);
Chris@0 596 $count++;
Chris@0 597 }
Chris@0 598 elseif (!empty($data)) {
Chris@0 599 // Update translation data in configuration override.
Chris@0 600 $this->saveTranslationOverride($name, $langcode, $data);
Chris@0 601 $count++;
Chris@0 602 }
Chris@0 603 }
Chris@0 604 elseif (locale_is_translatable($langcode)) {
Chris@0 605 // If the language code is the active storage language, we should
Chris@0 606 // update. If it is English, we should only update if English is also
Chris@0 607 // translatable.
Chris@0 608 $active = NestedArray::mergeDeepArray([$active, $processed], TRUE);
Chris@0 609 $this->saveTranslationActive($name, $active);
Chris@0 610 $count++;
Chris@0 611 }
Chris@0 612 }
Chris@0 613 }
Chris@0 614 return $count;
Chris@0 615 }
Chris@0 616
Chris@0 617 /**
Chris@0 618 * Filters override data based on default translatable items.
Chris@0 619 *
Chris@0 620 * @param array $override_data
Chris@0 621 * Configuration override data.
Chris@0 622 * @param array $translatable
Chris@0 623 * Translatable data array. @see self::getTranslatableData()
Chris@0 624 * @return array
Chris@0 625 * Nested array of any items of $override_data which did not have keys in
Chris@0 626 * $translatable. May be empty if $override_data only had items which were
Chris@0 627 * also in $translatable.
Chris@0 628 */
Chris@0 629 protected function filterOverride(array $override_data, array $translatable) {
Chris@0 630 $filtered_data = [];
Chris@0 631 foreach ($override_data as $key => $value) {
Chris@0 632 if (isset($translatable[$key])) {
Chris@0 633 // If the translatable default configuration has this key, look further
Chris@0 634 // for subkeys or ignore this element for scalar values.
Chris@0 635 if (is_array($value)) {
Chris@0 636 $value = $this->filterOverride($value, $translatable[$key]);
Chris@0 637 if (!empty($value)) {
Chris@0 638 $filtered_data[$key] = $value;
Chris@0 639 }
Chris@0 640 }
Chris@0 641 }
Chris@0 642 else {
Chris@0 643 // If this key was not in the translatable default configuration,
Chris@0 644 // keep it.
Chris@0 645 $filtered_data[$key] = $value;
Chris@0 646 }
Chris@0 647 }
Chris@0 648 return $filtered_data;
Chris@0 649 }
Chris@0 650
Chris@0 651 }