Chris@0
|
1 <?php
|
Chris@0
|
2
|
Chris@0
|
3 namespace Drupal\locale;
|
Chris@0
|
4
|
Chris@0
|
5 use Drupal\Core\Config\ConfigCrudEvent;
|
Chris@0
|
6 use Drupal\Core\Config\ConfigEvents;
|
Chris@0
|
7 use Drupal\Core\Config\ConfigFactoryInterface;
|
Chris@0
|
8 use Drupal\Core\Config\StorableConfigBase;
|
Chris@0
|
9 use Drupal\language\Config\LanguageConfigOverrideCrudEvent;
|
Chris@0
|
10 use Drupal\language\Config\LanguageConfigOverrideEvents;
|
Chris@0
|
11 use Symfony\Component\EventDispatcher\EventSubscriberInterface;
|
Chris@0
|
12
|
Chris@0
|
13 /**
|
Chris@0
|
14 * Updates strings translation when configuration translations change.
|
Chris@0
|
15 *
|
Chris@0
|
16 * This reacts to the updates of translated active configuration and
|
Chris@0
|
17 * configuration language overrides. When those updates involve configuration
|
Chris@0
|
18 * which was available as default configuration, we need to feed back changes
|
Chris@0
|
19 * to any item which was originally part of that configuration to the interface
|
Chris@0
|
20 * translation storage. Those updated translations are saved as customized, so
|
Chris@0
|
21 * further community translation updates will not undo user changes.
|
Chris@0
|
22 *
|
Chris@0
|
23 * This subscriber does not respond to deleting active configuration or deleting
|
Chris@0
|
24 * configuration translations. The locale storage is additive and we cannot be
|
Chris@0
|
25 * sure that only a given configuration translation used a source string. So
|
Chris@0
|
26 * we should not remove the translations from locale storage in these cases. The
|
Chris@0
|
27 * configuration or override would itself be deleted either way.
|
Chris@0
|
28 *
|
Chris@0
|
29 * By design locale module only deals with sources in English.
|
Chris@0
|
30 *
|
Chris@0
|
31 * @see \Drupal\locale\LocaleConfigManager
|
Chris@0
|
32 */
|
Chris@0
|
33 class LocaleConfigSubscriber implements EventSubscriberInterface {
|
Chris@0
|
34
|
Chris@0
|
35 /**
|
Chris@0
|
36 * The configuration factory.
|
Chris@0
|
37 *
|
Chris@0
|
38 * @var \Drupal\Core\Config\ConfigFactoryInterface
|
Chris@0
|
39 */
|
Chris@0
|
40 protected $configFactory;
|
Chris@0
|
41
|
Chris@0
|
42 /**
|
Chris@0
|
43 * The typed configuration manager.
|
Chris@0
|
44 *
|
Chris@0
|
45 * @var \Drupal\locale\LocaleConfigManager
|
Chris@0
|
46 */
|
Chris@0
|
47 protected $localeConfigManager;
|
Chris@0
|
48
|
Chris@0
|
49 /**
|
Chris@0
|
50 * Constructs a LocaleConfigSubscriber.
|
Chris@0
|
51 *
|
Chris@0
|
52 * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
|
Chris@0
|
53 * The configuration factory.
|
Chris@0
|
54 * @param \Drupal\locale\LocaleConfigManager $locale_config_manager
|
Chris@0
|
55 * The typed configuration manager.
|
Chris@0
|
56 */
|
Chris@0
|
57 public function __construct(ConfigFactoryInterface $config_factory, LocaleConfigManager $locale_config_manager) {
|
Chris@0
|
58 $this->configFactory = $config_factory;
|
Chris@0
|
59 $this->localeConfigManager = $locale_config_manager;
|
Chris@0
|
60 }
|
Chris@0
|
61
|
Chris@0
|
62 /**
|
Chris@0
|
63 * {@inheritdoc}
|
Chris@0
|
64 */
|
Chris@0
|
65 public static function getSubscribedEvents() {
|
Chris@0
|
66 $events[LanguageConfigOverrideEvents::SAVE_OVERRIDE] = 'onOverrideChange';
|
Chris@0
|
67 $events[LanguageConfigOverrideEvents::DELETE_OVERRIDE] = 'onOverrideChange';
|
Chris@0
|
68 $events[ConfigEvents::SAVE] = 'onConfigSave';
|
Chris@0
|
69 return $events;
|
Chris@0
|
70 }
|
Chris@0
|
71
|
Chris@0
|
72 /**
|
Chris@0
|
73 * Updates the locale strings when a translated active configuration is saved.
|
Chris@0
|
74 *
|
Chris@0
|
75 * @param \Drupal\Core\Config\ConfigCrudEvent $event
|
Chris@0
|
76 * The configuration event.
|
Chris@0
|
77 */
|
Chris@0
|
78 public function onConfigSave(ConfigCrudEvent $event) {
|
Chris@0
|
79 // Only attempt to feed back configuration translation changes to locale if
|
Chris@0
|
80 // the update itself was not initiated by locale data changes.
|
Chris@0
|
81 if (!drupal_installation_attempted() && !$this->localeConfigManager->isUpdatingTranslationsFromLocale()) {
|
Chris@0
|
82 $config = $event->getConfig();
|
Chris@0
|
83 $langcode = $config->get('langcode') ?: 'en';
|
Chris@0
|
84 $this->updateLocaleStorage($config, $langcode);
|
Chris@0
|
85 }
|
Chris@0
|
86 }
|
Chris@0
|
87
|
Chris@0
|
88 /**
|
Chris@0
|
89 * Updates the locale strings when a configuration override is saved/deleted.
|
Chris@0
|
90 *
|
Chris@0
|
91 * @param \Drupal\language\Config\LanguageConfigOverrideCrudEvent $event
|
Chris@0
|
92 * The language configuration event.
|
Chris@0
|
93 */
|
Chris@0
|
94 public function onOverrideChange(LanguageConfigOverrideCrudEvent $event) {
|
Chris@0
|
95 // Only attempt to feed back configuration override changes to locale if
|
Chris@0
|
96 // the update itself was not initiated by locale data changes.
|
Chris@0
|
97 if (!drupal_installation_attempted() && !$this->localeConfigManager->isUpdatingTranslationsFromLocale()) {
|
Chris@0
|
98 $translation_config = $event->getLanguageConfigOverride();
|
Chris@0
|
99 $langcode = $translation_config->getLangcode();
|
Chris@0
|
100 $reference_config = $this->configFactory->getEditable($translation_config->getName())->get();
|
Chris@0
|
101 $this->updateLocaleStorage($translation_config, $langcode, $reference_config);
|
Chris@0
|
102 }
|
Chris@0
|
103 }
|
Chris@0
|
104
|
Chris@0
|
105 /**
|
Chris@0
|
106 * Update locale storage based on configuration translations.
|
Chris@0
|
107 *
|
Chris@0
|
108 * @param \Drupal\Core\Config\StorableConfigBase $config
|
Chris@0
|
109 * Active configuration or configuration translation override.
|
Chris@0
|
110 * @param string $langcode
|
Chris@0
|
111 * The language code of $config.
|
Chris@0
|
112 * @param array $reference_config
|
Chris@0
|
113 * (Optional) Reference configuration to check against if $config was an
|
Chris@0
|
114 * override. This allows us to update locale keys for data not in the
|
Chris@0
|
115 * override but still in the active configuration.
|
Chris@0
|
116 */
|
Chris@0
|
117 protected function updateLocaleStorage(StorableConfigBase $config, $langcode, array $reference_config = []) {
|
Chris@0
|
118 $name = $config->getName();
|
Chris@0
|
119 if ($this->localeConfigManager->isSupported($name) && locale_is_translatable($langcode)) {
|
Chris@0
|
120 $translatables = $this->localeConfigManager->getTranslatableDefaultConfig($name);
|
Chris@0
|
121 $this->processTranslatableData($name, $config->get(), $translatables, $langcode, $reference_config);
|
Chris@0
|
122 }
|
Chris@0
|
123 }
|
Chris@0
|
124
|
Chris@0
|
125 /**
|
Chris@0
|
126 * Process the translatable data array with a given language.
|
Chris@0
|
127 *
|
Chris@0
|
128 * @param string $name
|
Chris@0
|
129 * The configuration name.
|
Chris@0
|
130 * @param array $config
|
Chris@0
|
131 * The active configuration data or override data.
|
Chris@0
|
132 * @param array|\Drupal\Core\StringTranslation\TranslatableMarkup[] $translatable
|
Chris@0
|
133 * The translatable array structure.
|
Chris@0
|
134 * @see \Drupal\locale\LocaleConfigManager::getTranslatableData()
|
Chris@0
|
135 * @param string $langcode
|
Chris@0
|
136 * The language code to process the array with.
|
Chris@0
|
137 * @param array $reference_config
|
Chris@0
|
138 * (Optional) Reference configuration to check against if $config was an
|
Chris@0
|
139 * override. This allows us to update locale keys for data not in the
|
Chris@0
|
140 * override but still in the active configuration.
|
Chris@0
|
141 */
|
Chris@0
|
142 protected function processTranslatableData($name, array $config, array $translatable, $langcode, array $reference_config = []) {
|
Chris@0
|
143 foreach ($translatable as $key => $item) {
|
Chris@0
|
144 if (!isset($config[$key])) {
|
Chris@0
|
145 if (isset($reference_config[$key])) {
|
Chris@0
|
146 $this->resetExistingTranslations($name, $translatable[$key], $reference_config[$key], $langcode);
|
Chris@0
|
147 }
|
Chris@0
|
148 continue;
|
Chris@0
|
149 }
|
Chris@0
|
150 if (is_array($item)) {
|
Chris@0
|
151 $reference_config = isset($reference_config[$key]) ? $reference_config[$key] : [];
|
Chris@0
|
152 $this->processTranslatableData($name, $config[$key], $item, $langcode, $reference_config);
|
Chris@0
|
153 }
|
Chris@0
|
154 else {
|
Chris@0
|
155 $this->saveCustomizedTranslation($name, $item->getUntranslatedString(), $item->getOption('context'), $config[$key], $langcode);
|
Chris@0
|
156 }
|
Chris@0
|
157 }
|
Chris@0
|
158 }
|
Chris@0
|
159
|
Chris@0
|
160 /**
|
Chris@0
|
161 * Reset existing locale translations to their source values.
|
Chris@0
|
162 *
|
Chris@0
|
163 * Goes through $translatable to reset any existing translations to the source
|
Chris@0
|
164 * string, so prior translations would not reappear in the configuration.
|
Chris@0
|
165 *
|
Chris@0
|
166 * @param string $name
|
Chris@0
|
167 * The configuration name.
|
Chris@0
|
168 * @param array|\Drupal\Core\StringTranslation\TranslatableMarkup $translatable
|
Chris@0
|
169 * Either a possibly nested array with TranslatableMarkup objects at the
|
Chris@0
|
170 * leaf items or a TranslatableMarkup object directly.
|
Chris@0
|
171 * @param array|string $reference_config
|
Chris@0
|
172 * Either a possibly nested array with strings at the leaf items or a string
|
Chris@0
|
173 * directly. Only those $translatable items that are also present in
|
Chris@0
|
174 * $reference_config will get translations reset.
|
Chris@0
|
175 * @param string $langcode
|
Chris@0
|
176 * The language code of the translation being processed.
|
Chris@0
|
177 */
|
Chris@0
|
178 protected function resetExistingTranslations($name, $translatable, $reference_config, $langcode) {
|
Chris@0
|
179 if (is_array($translatable)) {
|
Chris@0
|
180 foreach ($translatable as $key => $item) {
|
Chris@0
|
181 if (isset($reference_config[$key])) {
|
Chris@0
|
182 // Process further if the key still exists in the reference active
|
Chris@0
|
183 // configuration and the default translation but not the current
|
Chris@0
|
184 // configuration override.
|
Chris@0
|
185 $this->resetExistingTranslations($name, $item, $reference_config[$key], $langcode);
|
Chris@0
|
186 }
|
Chris@0
|
187 }
|
Chris@0
|
188 }
|
Chris@0
|
189 elseif (!is_array($reference_config)) {
|
Chris@0
|
190 $this->saveCustomizedTranslation($name, $translatable->getUntranslatedString(), $translatable->getOption('context'), $reference_config, $langcode);
|
Chris@0
|
191 }
|
Chris@0
|
192 }
|
Chris@0
|
193
|
Chris@0
|
194 /**
|
Chris@0
|
195 * Saves a translation string and marks it as customized.
|
Chris@0
|
196 *
|
Chris@0
|
197 * @param string $name
|
Chris@0
|
198 * The configuration name.
|
Chris@0
|
199 * @param string $source
|
Chris@0
|
200 * The source string value.
|
Chris@0
|
201 * @param string $context
|
Chris@0
|
202 * The source string context.
|
Chris@0
|
203 * @param string $new_translation
|
Chris@0
|
204 * The translation string.
|
Chris@0
|
205 * @param string $langcode
|
Chris@0
|
206 * The language code of the translation.
|
Chris@0
|
207 */
|
Chris@0
|
208 protected function saveCustomizedTranslation($name, $source, $context, $new_translation, $langcode) {
|
Chris@0
|
209 $locale_translation = $this->localeConfigManager->getStringTranslation($name, $langcode, $source, $context);
|
Chris@0
|
210 if (!empty($locale_translation)) {
|
Chris@0
|
211 // Save this translation as custom if it was a new translation and not the
|
Chris@0
|
212 // same as the source. (The interface prefills translation values with the
|
Chris@0
|
213 // source). Or if there was an existing (non-empty) translation and the
|
Chris@0
|
214 // user changed it (even if it was changed back to the original value).
|
Chris@0
|
215 // Otherwise the translation file would be overwritten with the locale
|
Chris@0
|
216 // copy again later.
|
Chris@0
|
217 $existing_translation = $locale_translation->getString();
|
Chris@0
|
218 if (($locale_translation->isNew() && $source != $new_translation) ||
|
Chris@0
|
219 (!$locale_translation->isNew() && ((empty($existing_translation) && $source != $new_translation) || ((!empty($existing_translation) && $new_translation != $existing_translation))))) {
|
Chris@0
|
220 $locale_translation
|
Chris@0
|
221 ->setString($new_translation)
|
Chris@0
|
222 ->setCustomized(TRUE)
|
Chris@0
|
223 ->save();
|
Chris@0
|
224 }
|
Chris@0
|
225 }
|
Chris@0
|
226 }
|
Chris@0
|
227
|
Chris@0
|
228 }
|