configFactory = $config_factory; $this->localeConfigManager = $locale_config_manager; } /** * {@inheritdoc} */ public static function getSubscribedEvents() { $events[LanguageConfigOverrideEvents::SAVE_OVERRIDE] = 'onOverrideChange'; $events[LanguageConfigOverrideEvents::DELETE_OVERRIDE] = 'onOverrideChange'; $events[ConfigEvents::SAVE] = 'onConfigSave'; return $events; } /** * Updates the locale strings when a translated active configuration is saved. * * @param \Drupal\Core\Config\ConfigCrudEvent $event * The configuration event. */ public function onConfigSave(ConfigCrudEvent $event) { // Only attempt to feed back configuration translation changes to locale if // the update itself was not initiated by locale data changes. if (!drupal_installation_attempted() && !$this->localeConfigManager->isUpdatingTranslationsFromLocale()) { $config = $event->getConfig(); $langcode = $config->get('langcode') ?: 'en'; $this->updateLocaleStorage($config, $langcode); } } /** * Updates the locale strings when a configuration override is saved/deleted. * * @param \Drupal\language\Config\LanguageConfigOverrideCrudEvent $event * The language configuration event. */ public function onOverrideChange(LanguageConfigOverrideCrudEvent $event) { // Only attempt to feed back configuration override changes to locale if // the update itself was not initiated by locale data changes. if (!drupal_installation_attempted() && !$this->localeConfigManager->isUpdatingTranslationsFromLocale()) { $translation_config = $event->getLanguageConfigOverride(); $langcode = $translation_config->getLangcode(); $reference_config = $this->configFactory->getEditable($translation_config->getName())->get(); $this->updateLocaleStorage($translation_config, $langcode, $reference_config); } } /** * Update locale storage based on configuration translations. * * @param \Drupal\Core\Config\StorableConfigBase $config * Active configuration or configuration translation override. * @param string $langcode * The language code of $config. * @param array $reference_config * (Optional) Reference configuration to check against if $config was an * override. This allows us to update locale keys for data not in the * override but still in the active configuration. */ protected function updateLocaleStorage(StorableConfigBase $config, $langcode, array $reference_config = []) { $name = $config->getName(); if ($this->localeConfigManager->isSupported($name) && locale_is_translatable($langcode)) { $translatables = $this->localeConfigManager->getTranslatableDefaultConfig($name); $this->processTranslatableData($name, $config->get(), $translatables, $langcode, $reference_config); } } /** * Process the translatable data array with a given language. * * @param string $name * The configuration name. * @param array $config * The active configuration data or override data. * @param array|\Drupal\Core\StringTranslation\TranslatableMarkup[] $translatable * The translatable array structure. * @see \Drupal\locale\LocaleConfigManager::getTranslatableData() * @param string $langcode * The language code to process the array with. * @param array $reference_config * (Optional) Reference configuration to check against if $config was an * override. This allows us to update locale keys for data not in the * override but still in the active configuration. */ protected function processTranslatableData($name, array $config, array $translatable, $langcode, array $reference_config = []) { foreach ($translatable as $key => $item) { if (!isset($config[$key])) { if (isset($reference_config[$key])) { $this->resetExistingTranslations($name, $translatable[$key], $reference_config[$key], $langcode); } continue; } if (is_array($item)) { $reference_config = isset($reference_config[$key]) ? $reference_config[$key] : []; $this->processTranslatableData($name, $config[$key], $item, $langcode, $reference_config); } else { $this->saveCustomizedTranslation($name, $item->getUntranslatedString(), $item->getOption('context'), $config[$key], $langcode); } } } /** * Reset existing locale translations to their source values. * * Goes through $translatable to reset any existing translations to the source * string, so prior translations would not reappear in the configuration. * * @param string $name * The configuration name. * @param array|\Drupal\Core\StringTranslation\TranslatableMarkup $translatable * Either a possibly nested array with TranslatableMarkup objects at the * leaf items or a TranslatableMarkup object directly. * @param array|string $reference_config * Either a possibly nested array with strings at the leaf items or a string * directly. Only those $translatable items that are also present in * $reference_config will get translations reset. * @param string $langcode * The language code of the translation being processed. */ protected function resetExistingTranslations($name, $translatable, $reference_config, $langcode) { if (is_array($translatable)) { foreach ($translatable as $key => $item) { if (isset($reference_config[$key])) { // Process further if the key still exists in the reference active // configuration and the default translation but not the current // configuration override. $this->resetExistingTranslations($name, $item, $reference_config[$key], $langcode); } } } elseif (!is_array($reference_config)) { $this->saveCustomizedTranslation($name, $translatable->getUntranslatedString(), $translatable->getOption('context'), $reference_config, $langcode); } } /** * Saves a translation string and marks it as customized. * * @param string $name * The configuration name. * @param string $source * The source string value. * @param string $context * The source string context. * @param string $new_translation * The translation string. * @param string $langcode * The language code of the translation. */ protected function saveCustomizedTranslation($name, $source, $context, $new_translation, $langcode) { $locale_translation = $this->localeConfigManager->getStringTranslation($name, $langcode, $source, $context); if (!empty($locale_translation)) { // Save this translation as custom if it was a new translation and not the // same as the source. (The interface prefills translation values with the // source). Or if there was an existing (non-empty) translation and the // user changed it (even if it was changed back to the original value). // Otherwise the translation file would be overwritten with the locale // copy again later. $existing_translation = $locale_translation->getString(); if (($locale_translation->isNew() && $source != $new_translation) || (!$locale_translation->isNew() && ((empty($existing_translation) && $source != $new_translation) || ((!empty($existing_translation) && $new_translation != $existing_translation))))) { $locale_translation ->setString($new_translation) ->setCustomized(TRUE) ->save(); } } } }