Mercurial > hg > isophonics-drupal-site
comparison core/modules/language/src/ConfigurableLanguageManager.php @ 0:4c8ae668cc8c
Initial import (non-working)
author | Chris Cannam |
---|---|
date | Wed, 29 Nov 2017 16:09:58 +0000 |
parents | |
children | 1fec387a4317 |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:4c8ae668cc8c |
---|---|
1 <?php | |
2 | |
3 namespace Drupal\language; | |
4 | |
5 use Drupal\Core\Language\LanguageInterface; | |
6 use Drupal\Core\Config\ConfigFactoryInterface; | |
7 use Drupal\Core\Extension\ModuleHandlerInterface; | |
8 use Drupal\Core\Language\Language; | |
9 use Drupal\Core\Language\LanguageDefault; | |
10 use Drupal\Core\Language\LanguageManager; | |
11 use Drupal\Core\StringTranslation\TranslatableMarkup; | |
12 use Drupal\Core\Url; | |
13 use Drupal\language\Config\LanguageConfigFactoryOverrideInterface; | |
14 use Drupal\language\Entity\ConfigurableLanguage; | |
15 use Symfony\Component\HttpFoundation\RequestStack; | |
16 | |
17 /** | |
18 * Overrides default LanguageManager to provide configured languages. | |
19 */ | |
20 class ConfigurableLanguageManager extends LanguageManager implements ConfigurableLanguageManagerInterface { | |
21 | |
22 /** | |
23 * The configuration storage service. | |
24 * | |
25 * @var \Drupal\Core\Config\ConfigFactoryInterface | |
26 */ | |
27 protected $configFactory; | |
28 | |
29 /** | |
30 * The module handler service. | |
31 * | |
32 * @var \Drupal\Core\Extension\ModuleHandlerInterface | |
33 */ | |
34 protected $moduleHandler; | |
35 | |
36 /** | |
37 * The language configuration override service. | |
38 * | |
39 * @var \Drupal\language\Config\LanguageConfigFactoryOverrideInterface | |
40 */ | |
41 protected $configFactoryOverride; | |
42 | |
43 /** | |
44 * The request object. | |
45 * | |
46 * @var \Symfony\Component\HttpFoundation\RequestStack | |
47 */ | |
48 protected $requestStack; | |
49 | |
50 /** | |
51 * The language negotiator. | |
52 * | |
53 * @var \Drupal\language\LanguageNegotiatorInterface | |
54 */ | |
55 protected $negotiator; | |
56 | |
57 /** | |
58 * Local cache for language type configuration data. | |
59 * | |
60 * @var array | |
61 */ | |
62 protected $languageTypes; | |
63 | |
64 /** | |
65 * Local cache for language type information. | |
66 * | |
67 * @var array | |
68 */ | |
69 protected $languageTypesInfo; | |
70 | |
71 /** | |
72 * An array of language objects keyed by language type. | |
73 * | |
74 * @var \Drupal\Core\Language\LanguageInterface[] | |
75 */ | |
76 protected $negotiatedLanguages; | |
77 | |
78 /** | |
79 * An array of language negotiation method IDs keyed by language type. | |
80 * | |
81 * @var array | |
82 */ | |
83 protected $negotiatedMethods; | |
84 | |
85 /** | |
86 * Whether or not the language manager has been initialized. | |
87 * | |
88 * @var bool | |
89 */ | |
90 protected $initialized = FALSE; | |
91 | |
92 /** | |
93 * Whether already in the process of language initialization. | |
94 * | |
95 * @var bool | |
96 */ | |
97 protected $initializing = FALSE; | |
98 | |
99 /** | |
100 * {@inheritdoc} | |
101 */ | |
102 public static function rebuildServices() { | |
103 \Drupal::service('kernel')->invalidateContainer(); | |
104 } | |
105 | |
106 /** | |
107 * Constructs a new ConfigurableLanguageManager object. | |
108 * | |
109 * @param \Drupal\Core\Language\LanguageDefault $default_language | |
110 * The default language service. | |
111 * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory | |
112 * The configuration factory service. | |
113 * @param \Drupal\Core\Extension\ModuleHandlerInterface $module_handler | |
114 * The module handler service. | |
115 * @param \Drupal\language\Config\LanguageConfigFactoryOverrideInterface $config_override | |
116 * The language configuration override service. | |
117 * @param \Symfony\Component\HttpFoundation\RequestStack $request_stack | |
118 * The request stack object. | |
119 */ | |
120 public function __construct(LanguageDefault $default_language, ConfigFactoryInterface $config_factory, ModuleHandlerInterface $module_handler, LanguageConfigFactoryOverrideInterface $config_override, RequestStack $request_stack) { | |
121 $this->defaultLanguage = $default_language; | |
122 $this->configFactory = $config_factory; | |
123 $this->moduleHandler = $module_handler; | |
124 $this->configFactoryOverride = $config_override; | |
125 $this->requestStack = $request_stack; | |
126 } | |
127 | |
128 /** | |
129 * {@inheritdoc} | |
130 */ | |
131 public function init() { | |
132 if (!$this->initialized) { | |
133 foreach ($this->getDefinedLanguageTypes() as $type) { | |
134 $this->getCurrentLanguage($type); | |
135 } | |
136 $this->initialized = TRUE; | |
137 } | |
138 } | |
139 | |
140 /** | |
141 * {@inheritdoc} | |
142 */ | |
143 public function isMultilingual() { | |
144 return count($this->getLanguages(LanguageInterface::STATE_CONFIGURABLE)) > 1; | |
145 } | |
146 | |
147 /** | |
148 * {@inheritdoc} | |
149 */ | |
150 public function getLanguageTypes() { | |
151 $this->loadLanguageTypesConfiguration(); | |
152 return $this->languageTypes['configurable']; | |
153 } | |
154 | |
155 /** | |
156 * {@inheritdoc} | |
157 */ | |
158 public function getDefinedLanguageTypes() { | |
159 $this->loadLanguageTypesConfiguration(); | |
160 return $this->languageTypes['all']; | |
161 } | |
162 | |
163 /** | |
164 * Retrieves language types from the configuration storage. | |
165 * | |
166 * @return array | |
167 * An array of language type names. | |
168 */ | |
169 protected function loadLanguageTypesConfiguration() { | |
170 if (!$this->languageTypes) { | |
171 $this->languageTypes = $this->configFactory->get('language.types')->get() ?: ['configurable' => [], 'all' => parent::getLanguageTypes()]; | |
172 } | |
173 return $this->languageTypes; | |
174 } | |
175 | |
176 /** | |
177 * {@inheritdoc} | |
178 */ | |
179 public function getDefinedLanguageTypesInfo() { | |
180 if (!isset($this->languageTypesInfo)) { | |
181 $defaults = parent::getDefinedLanguageTypesInfo(); | |
182 | |
183 $info = $this->moduleHandler->invokeAll('language_types_info'); | |
184 $language_info = $info + $defaults; | |
185 | |
186 // Let other modules alter the list of language types. | |
187 $this->moduleHandler->alter('language_types_info', $language_info); | |
188 $this->languageTypesInfo = $language_info; | |
189 } | |
190 return $this->languageTypesInfo; | |
191 } | |
192 | |
193 /** | |
194 * {@inheritdoc} | |
195 */ | |
196 public function saveLanguageTypesConfiguration(array $values) { | |
197 $config = $this->configFactory->getEditable('language.types'); | |
198 if (isset($values['configurable'])) { | |
199 $config->set('configurable', $values['configurable']); | |
200 } | |
201 if (isset($values['all'])) { | |
202 $config->set('all', $values['all']); | |
203 } | |
204 $config->save(); | |
205 } | |
206 | |
207 /** | |
208 * {@inheritdoc} | |
209 */ | |
210 public function getCurrentLanguage($type = LanguageInterface::TYPE_INTERFACE) { | |
211 if (!isset($this->negotiatedLanguages[$type])) { | |
212 // Ensure we have a valid value for this language type. | |
213 $this->negotiatedLanguages[$type] = $this->getDefaultLanguage(); | |
214 | |
215 if ($this->negotiator && $this->isMultilingual()) { | |
216 if (!$this->initializing) { | |
217 $this->initializing = TRUE; | |
218 $negotiation = $this->negotiator->initializeType($type); | |
219 $this->negotiatedLanguages[$type] = reset($negotiation); | |
220 $this->negotiatedMethods[$type] = key($negotiation); | |
221 $this->initializing = FALSE; | |
222 } | |
223 // If the current interface language needs to be retrieved during | |
224 // initialization we return the system language. This way string | |
225 // translation calls happening during initialization will return the | |
226 // original strings which can be translated by calling them again | |
227 // afterwards. This can happen for instance while parsing negotiation | |
228 // method definitions. | |
229 elseif ($type == LanguageInterface::TYPE_INTERFACE) { | |
230 return new Language(['id' => LanguageInterface::LANGCODE_SYSTEM]); | |
231 } | |
232 } | |
233 } | |
234 | |
235 return $this->negotiatedLanguages[$type]; | |
236 } | |
237 | |
238 /** | |
239 * {@inheritdoc} | |
240 */ | |
241 public function reset($type = NULL) { | |
242 if (!isset($type)) { | |
243 $this->initialized = FALSE; | |
244 $this->negotiatedLanguages = []; | |
245 $this->negotiatedMethods = []; | |
246 $this->languageTypes = NULL; | |
247 $this->languageTypesInfo = NULL; | |
248 $this->languages = []; | |
249 if ($this->negotiator) { | |
250 $this->negotiator->reset(); | |
251 } | |
252 } | |
253 elseif (isset($this->negotiatedLanguages[$type])) { | |
254 unset($this->negotiatedLanguages[$type]); | |
255 unset($this->negotiatedMethods[$type]); | |
256 } | |
257 return $this; | |
258 } | |
259 | |
260 /** | |
261 * {@inheritdoc} | |
262 */ | |
263 public function getNegotiator() { | |
264 return $this->negotiator; | |
265 } | |
266 | |
267 /** | |
268 * {@inheritdoc} | |
269 */ | |
270 public function setNegotiator(LanguageNegotiatorInterface $negotiator) { | |
271 $this->negotiator = $negotiator; | |
272 $this->initialized = FALSE; | |
273 $this->negotiatedLanguages = []; | |
274 } | |
275 | |
276 /** | |
277 * {@inheritdoc} | |
278 */ | |
279 public function getLanguages($flags = LanguageInterface::STATE_CONFIGURABLE) { | |
280 // If a config override is set, cache using that language's ID. | |
281 if ($override_language = $this->getConfigOverrideLanguage()) { | |
282 $static_cache_id = $override_language->getId(); | |
283 } | |
284 else { | |
285 $static_cache_id = $this->getCurrentLanguage()->getId(); | |
286 } | |
287 | |
288 if (!isset($this->languages[$static_cache_id][$flags])) { | |
289 // Initialize the language list with the default language and default | |
290 // locked languages. These cannot be removed. This serves as a fallback | |
291 // list if this method is invoked while the language module is installed | |
292 // and the configuration entities for languages are not yet fully | |
293 // imported. | |
294 $default = $this->getDefaultLanguage(); | |
295 $languages = [$default->getId() => $default]; | |
296 $languages += $this->getDefaultLockedLanguages($default->getWeight()); | |
297 | |
298 // Load configurable languages on top of the defaults. Ideally this could | |
299 // use the entity API to load and instantiate ConfigurableLanguage | |
300 // objects. However the entity API depends on the language system, so that | |
301 // would result in infinite loops. We use the configuration system | |
302 // directly and instantiate runtime Language objects. When language | |
303 // entities are imported those cover the default and locked languages, so | |
304 // site-specific configuration will prevail over the fallback values. | |
305 // Having them in the array already ensures if this is invoked in the | |
306 // middle of importing language configuration entities, the defaults are | |
307 // always present. | |
308 $config_ids = $this->configFactory->listAll('language.entity.'); | |
309 foreach ($this->configFactory->loadMultiple($config_ids) as $config) { | |
310 $data = $config->get(); | |
311 $data['name'] = $data['label']; | |
312 $languages[$data['id']] = new Language($data); | |
313 } | |
314 Language::sort($languages); | |
315 | |
316 // Filter the full list of languages based on the value of $flags. | |
317 $this->languages[$static_cache_id][$flags] = $this->filterLanguages($languages, $flags); | |
318 } | |
319 | |
320 return $this->languages[$static_cache_id][$flags]; | |
321 } | |
322 | |
323 /** | |
324 * {@inheritdoc} | |
325 */ | |
326 public function getNativeLanguages() { | |
327 $languages = $this->getLanguages(LanguageInterface::STATE_CONFIGURABLE); | |
328 $natives = []; | |
329 | |
330 $original_language = $this->getConfigOverrideLanguage(); | |
331 | |
332 foreach ($languages as $langcode => $language) { | |
333 $this->setConfigOverrideLanguage($language); | |
334 $natives[$langcode] = ConfigurableLanguage::load($langcode); | |
335 } | |
336 $this->setConfigOverrideLanguage($original_language); | |
337 Language::sort($natives); | |
338 return $natives; | |
339 } | |
340 | |
341 /** | |
342 * {@inheritdoc} | |
343 */ | |
344 public function updateLockedLanguageWeights() { | |
345 // Get the weight of the last configurable language. | |
346 $configurable_languages = $this->getLanguages(LanguageInterface::STATE_CONFIGURABLE); | |
347 $max_weight = end($configurable_languages)->getWeight(); | |
348 | |
349 $locked_languages = $this->getLanguages(LanguageInterface::STATE_LOCKED); | |
350 // Update locked language weights to maintain the existing order, if | |
351 // necessary. | |
352 if (reset($locked_languages)->getWeight() <= $max_weight) { | |
353 foreach ($locked_languages as $language) { | |
354 // Update system languages weight. | |
355 $max_weight++; | |
356 ConfigurableLanguage::load($language->getId()) | |
357 ->setWeight($max_weight) | |
358 ->save(); | |
359 } | |
360 } | |
361 } | |
362 | |
363 /** | |
364 * {@inheritdoc} | |
365 */ | |
366 public function getFallbackCandidates(array $context = []) { | |
367 if ($this->isMultilingual()) { | |
368 $candidates = []; | |
369 if (empty($context['operation']) || $context['operation'] != 'locale_lookup') { | |
370 // If the fallback context is not locale_lookup, initialize the | |
371 // candidates with languages ordered by weight and add | |
372 // LanguageInterface::LANGCODE_NOT_SPECIFIED at the end. Interface | |
373 // translation fallback should only be based on explicit configuration | |
374 // gathered via the alter hooks below. | |
375 $candidates = array_keys($this->getLanguages()); | |
376 $candidates[] = LanguageInterface::LANGCODE_NOT_SPECIFIED; | |
377 $candidates = array_combine($candidates, $candidates); | |
378 | |
379 // The first candidate should always be the desired language if | |
380 // specified. | |
381 if (!empty($context['langcode'])) { | |
382 $candidates = [$context['langcode'] => $context['langcode']] + $candidates; | |
383 } | |
384 } | |
385 | |
386 // Let other modules hook in and add/change candidates. | |
387 $type = 'language_fallback_candidates'; | |
388 $types = []; | |
389 if (!empty($context['operation'])) { | |
390 $types[] = $type . '_' . $context['operation']; | |
391 } | |
392 $types[] = $type; | |
393 $this->moduleHandler->alter($types, $candidates, $context); | |
394 } | |
395 else { | |
396 $candidates = parent::getFallbackCandidates($context); | |
397 } | |
398 | |
399 return $candidates; | |
400 } | |
401 | |
402 /** | |
403 * {@inheritdoc} | |
404 */ | |
405 public function getLanguageSwitchLinks($type, Url $url) { | |
406 $links = FALSE; | |
407 | |
408 if ($this->negotiator) { | |
409 foreach ($this->negotiator->getNegotiationMethods($type) as $method_id => $method) { | |
410 $reflector = new \ReflectionClass($method['class']); | |
411 | |
412 if ($reflector->implementsInterface('\Drupal\language\LanguageSwitcherInterface')) { | |
413 $result = $this->negotiator->getNegotiationMethodInstance($method_id)->getLanguageSwitchLinks($this->requestStack->getCurrentRequest(), $type, $url); | |
414 | |
415 if (!empty($result)) { | |
416 // Allow modules to provide translations for specific links. | |
417 $this->moduleHandler->alter('language_switch_links', $result, $type, $url); | |
418 $links = (object) ['links' => $result, 'method_id' => $method_id]; | |
419 break; | |
420 } | |
421 } | |
422 } | |
423 } | |
424 | |
425 return $links; | |
426 } | |
427 | |
428 /** | |
429 * {@inheritdoc} | |
430 */ | |
431 public function setConfigOverrideLanguage(LanguageInterface $language = NULL) { | |
432 $this->configFactoryOverride->setLanguage($language); | |
433 return $this; | |
434 } | |
435 | |
436 /** | |
437 * {@inheritdoc} | |
438 */ | |
439 public function getConfigOverrideLanguage() { | |
440 return $this->configFactoryOverride->getLanguage(); | |
441 } | |
442 | |
443 /** | |
444 * {@inheritdoc} | |
445 */ | |
446 public function getLanguageConfigOverride($langcode, $name) { | |
447 return $this->configFactoryOverride->getOverride($langcode, $name); | |
448 } | |
449 | |
450 /** | |
451 * {@inheritdoc} | |
452 */ | |
453 public function getLanguageConfigOverrideStorage($langcode) { | |
454 return $this->configFactoryOverride->getStorage($langcode); | |
455 } | |
456 | |
457 /** | |
458 * {@inheritdoc} | |
459 */ | |
460 public function getStandardLanguageListWithoutConfigured() { | |
461 $languages = $this->getLanguages(); | |
462 $predefined = $this->getStandardLanguageList(); | |
463 foreach ($predefined as $key => $value) { | |
464 if (isset($languages[$key])) { | |
465 unset($predefined[$key]); | |
466 continue; | |
467 } | |
468 $predefined[$key] = new TranslatableMarkup($value[0]); | |
469 } | |
470 natcasesort($predefined); | |
471 return $predefined; | |
472 } | |
473 | |
474 /** | |
475 * {@inheritdoc} | |
476 */ | |
477 public function getNegotiatedLanguageMethod($type = LanguageInterface::TYPE_INTERFACE) { | |
478 if (isset($this->negotiatedLanguages[$type]) && isset($this->negotiatedMethods[$type])) { | |
479 return $this->negotiatedMethods[$type]; | |
480 } | |
481 } | |
482 | |
483 } |