[BUGFIX] Perform the translation key look-up as it was before #81775
[Packages/TYPO3.CMS.git] / typo3 / sysext / form / Classes / Service / TranslationService.php
1 <?php
2 declare(strict_types = 1);
3 namespace TYPO3\CMS\Form\Service;
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18 use TYPO3\CMS\Core\Localization\LanguageService;
19 use TYPO3\CMS\Core\Localization\Locales;
20 use TYPO3\CMS\Core\Localization\LocalizationFactory;
21 use TYPO3\CMS\Core\SingletonInterface;
22 use TYPO3\CMS\Core\Utility\ArrayUtility;
23 use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
24 use TYPO3\CMS\Core\Utility\GeneralUtility;
25 use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
26 use TYPO3\CMS\Extbase\Object\ObjectManager;
27 use TYPO3\CMS\Form\Domain\Model\FormElements\FormElementInterface;
28 use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface;
29 use TYPO3\CMS\Form\Domain\Runtime\FormRuntime;
30 use TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController;
31
32 /**
33 * Advanced translations
34 * This class is subjected to change.
35 * **Do NOT subclass**
36 *
37 * Scope: frontend / backend
38 * @internal
39 */
40 class TranslationService implements SingletonInterface
41 {
42
43 /**
44 * Local Language content
45 *
46 * @var array
47 */
48 protected $LOCAL_LANG = [];
49
50 /**
51 * Contains those LL keys, which have been set to (empty) in TypoScript.
52 * This is necessary, as we cannot distinguish between a nonexisting
53 * translation and a label that has been cleared by TS.
54 * In both cases ['key'][0]['target'] is "".
55 *
56 * @var array
57 */
58 protected $LOCAL_LANG_UNSET = [];
59
60 /**
61 * Key of the language to use
62 *
63 * @var string
64 */
65 protected $languageKey = null;
66
67 /**
68 * Pointer to alternative fall-back language to use
69 *
70 * @var array
71 */
72 protected $alternativeLanguageKeys = [];
73
74 /**
75 * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
76 */
77 protected $configurationManager = null;
78
79 /**
80 * Return TranslationService as singleton
81 *
82 * @return TranslationService
83 * @internal
84 */
85 public static function getInstance()
86 {
87 return GeneralUtility::makeInstance(ObjectManager::class)->get(self::class);
88 }
89
90 /**
91 * Returns the localized label of the LOCAL_LANG key, $key.
92 *
93 * @param mixed $key The key from the LOCAL_LANG array for which to return the value.
94 * @param array $arguments the arguments of the extension, being passed over to vsprintf
95 * @param string $locallangPathAndFilename
96 * @param string $language
97 * @param mixed $defaultValue
98 * @return mixed The value from LOCAL_LANG or $defaultValue if no translation was found.
99 * @internal
100 */
101 public function translate(
102 $key,
103 array $arguments = null,
104 string $locallangPathAndFilename = null,
105 string $language = null,
106 $defaultValue = ''
107 ) {
108 $value = null;
109 $key = (string)$key;
110
111 if ($locallangPathAndFilename) {
112 $key = $locallangPathAndFilename . ':' . $key;
113 }
114
115 $keyParts = explode(':', $key);
116 if (GeneralUtility::isFirstPartOfStr($key, 'LLL:')) {
117 $locallangPathAndFilename = $keyParts[1] . ':' . $keyParts[2];
118 $key = $keyParts[3];
119 } elseif (GeneralUtility::isFirstPartOfStr($key, 'EXT:')) {
120 $locallangPathAndFilename = $keyParts[0] . ':' . $keyParts[1];
121 $key = $keyParts[2];
122 } else {
123 if (count($keyParts) === 2) {
124 $locallangPathAndFilename = $keyParts[0];
125 $key = $keyParts[1];
126 }
127 }
128
129 if ($language) {
130 $this->languageKey = $language;
131 }
132
133 $this->initializeLocalization($locallangPathAndFilename);
134
135 // The "from" charset of csConv() is only set for strings from TypoScript via _LOCAL_LANG
136 if (!empty($this->LOCAL_LANG[$this->languageKey][$key][0]['target'])
137 || isset($this->LOCAL_LANG_UNSET[$this->languageKey][$key])
138 ) {
139 // Local language translation for key exists
140 $value = $this->LOCAL_LANG[$this->languageKey][$key][0]['target'];
141 } elseif (!empty($this->alternativeLanguageKeys)) {
142 $languages = array_reverse($this->alternativeLanguageKeys);
143 foreach ($languages as $language) {
144 if (!empty($this->LOCAL_LANG[$language][$key][0]['target'])
145 || isset($this->LOCAL_LANG_UNSET[$language][$key])
146 ) {
147 // Alternative language translation for key exists
148 $value = $this->LOCAL_LANG[$language][$key][0]['target'];
149 break;
150 }
151 }
152 }
153
154 if ($value === null && (!empty($this->LOCAL_LANG['default'][$key][0]['target'])
155 || isset($this->LOCAL_LANG_UNSET['default'][$key]))
156 ) {
157 // Default language translation for key exists
158 // No charset conversion because default is English and thereby ASCII
159 $value = $this->LOCAL_LANG['default'][$key][0]['target'];
160 }
161
162 if (is_array($arguments) && !empty($arguments) && $value !== null) {
163 $value = vsprintf($value, $arguments);
164 } else {
165 if (empty($value)) {
166 $value = $defaultValue;
167 }
168 }
169
170 return $value;
171 }
172
173 /**
174 * Recursively translate values.
175 *
176 * @param array $array
177 * @param array|string|null $translationFile
178 * @return array the modified array
179 * @internal
180 */
181 public function translateValuesRecursive(array $array, $translationFile = null): array
182 {
183 $result = $array;
184 foreach ($result as $key => $value) {
185 if (is_array($value)) {
186 $result[$key] = $this->translateValuesRecursive($value, $translationFile);
187 } else {
188 $translationFiles = null;
189 if (is_string($translationFile)) {
190 $translationFiles = [$translationFile];
191 } elseif (is_array($translationFile)) {
192 $translationFiles = $this->sortArrayWithIntegerKeysDescending($translationFile);
193 }
194
195 if ($translationFiles) {
196 foreach ($translationFiles as $_translationFile) {
197 $translatedValue = $this->translate($value, null, $_translationFile, null);
198 if (!empty($translatedValue)) {
199 $result[$key] = $translatedValue;
200 break;
201 }
202 }
203 } else {
204 $result[$key] = $this->translate($value, null, $translationFile, null, $value);
205 }
206 }
207 }
208 return $result;
209 }
210
211 /**
212 * @param FormRuntime $formRuntime
213 * @param string $finisherIdentifier
214 * @param string $optionKey
215 * @param string $optionValue
216 * @param array $renderingOptions
217 * @return string
218 * @throws \InvalidArgumentException
219 * @api
220 */
221 public function translateFinisherOption(
222 FormRuntime $formRuntime,
223 string $finisherIdentifier,
224 string $optionKey,
225 string $optionValue,
226 array $renderingOptions = []
227 ): string {
228 if (empty($finisherIdentifier)) {
229 throw new \InvalidArgumentException('The argument "finisherIdentifier" is empty', 1476216059);
230 }
231 if (empty($optionKey)) {
232 throw new \InvalidArgumentException('The argument "optionKey" is empty', 1476216060);
233 }
234
235 $finisherIdentifier = preg_replace('/Finisher$/', '', $finisherIdentifier);
236 $translationFile = $renderingOptions['translationFile'] ?? null;
237 if (empty($translationFile)) {
238 $translationFile = $formRuntime->getRenderingOptions()['translation']['translationFile'];
239 }
240
241 if (is_string($translationFile)) {
242 $translationFiles = [$translationFile];
243 } else {
244 $translationFiles = $this->sortArrayWithIntegerKeysDescending($translationFile);
245 }
246
247 if (isset($renderingOptions['translatePropertyValueIfEmpty'])) {
248 $translatePropertyValueIfEmpty = (bool)$renderingOptions['translatePropertyValueIfEmpty'];
249 } else {
250 $translatePropertyValueIfEmpty = true;
251 }
252
253 if (empty($optionValue) && !$translatePropertyValueIfEmpty) {
254 return $optionValue;
255 }
256
257 $language = null;
258 if (isset($renderingOptions['language'])) {
259 $language = $renderingOptions['language'];
260 }
261
262 try {
263 $arguments = ArrayUtility::getValueByPath($renderingOptions['arguments'] ?? [], $optionKey, '.');
264 } catch (MissingArrayPathException $e) {
265 $arguments = [];
266 }
267
268 $originalFormIdentifier = null;
269 if (isset($formRuntime->getRenderingOptions()['_originalIdentifier'])) {
270 $originalFormIdentifier = $formRuntime->getRenderingOptions()['_originalIdentifier'];
271 }
272
273 $translationKeyChain = [];
274 foreach ($translationFiles as $translationFile) {
275 if (!empty($originalFormIdentifier)) {
276 $translationKeyChain[] = sprintf('%s:%s.finisher.%s.%s', $translationFile, $originalFormIdentifier, $finisherIdentifier, $optionKey);
277 }
278 $translationKeyChain[] = sprintf('%s:%s.finisher.%s.%s', $translationFile, $formRuntime->getIdentifier(), $finisherIdentifier, $optionKey);
279 $translationKeyChain[] = sprintf('%s:finisher.%s.%s', $translationFile, $finisherIdentifier, $optionKey);
280 }
281
282 $translatedValue = $this->processTranslationChain($translationKeyChain, $language, $arguments);
283 $translatedValue = empty($translatedValue) ? $optionValue : $translatedValue;
284
285 return $translatedValue;
286 }
287
288 /**
289 * @param RootRenderableInterface $element
290 * @param array $propertyParts
291 * @param FormRuntime $formRuntime
292 * @return string|array
293 * @throws \InvalidArgumentException
294 * @internal
295 */
296 public function translateFormElementValue(
297 RootRenderableInterface $element,
298 array $propertyParts,
299 FormRuntime $formRuntime
300 ) {
301 if (empty($propertyParts)) {
302 throw new \InvalidArgumentException('The argument "propertyParts" is empty', 1476216007);
303 }
304
305 $propertyType = 'properties';
306 $property = implode('.', $propertyParts);
307 $renderingOptions = $element->getRenderingOptions();
308
309 if ($property === 'label') {
310 $defaultValue = $element->getLabel();
311 } else {
312 if ($element instanceof FormElementInterface) {
313 try {
314 $defaultValue = ArrayUtility::getValueByPath($element->getProperties(), $propertyParts, '.');
315 } catch (MissingArrayPathException $exception) {
316 $defaultValue = null;
317 }
318 } else {
319 $propertyType = 'renderingOptions';
320 try {
321 $defaultValue = ArrayUtility::getValueByPath($renderingOptions, $propertyParts, '.');
322 } catch (MissingArrayPathException $exception) {
323 $defaultValue = null;
324 }
325 }
326 }
327
328 if (isset($renderingOptions['translation']['translatePropertyValueIfEmpty'])) {
329 $translatePropertyValueIfEmpty = $renderingOptions['translation']['translatePropertyValueIfEmpty'];
330 } else {
331 $translatePropertyValueIfEmpty = true;
332 }
333
334 if (empty($defaultValue) && !$translatePropertyValueIfEmpty) {
335 return $defaultValue;
336 }
337
338 $defaultValue = empty($defaultValue) ? '' : $defaultValue;
339 $translationFile = $renderingOptions['translation']['translationFile'] ?? null;
340 if (empty($translationFile)) {
341 $translationFile = $formRuntime->getRenderingOptions()['translation']['translationFile'];
342 }
343
344 if (is_string($translationFile)) {
345 $translationFiles = [$translationFile];
346 } else {
347 $translationFiles = $this->sortArrayWithIntegerKeysDescending($translationFile);
348 }
349
350 $language = null;
351 if (isset($renderingOptions['translation']['language'])) {
352 $language = $renderingOptions['translation']['language'];
353 }
354
355 try {
356 $arguments = ArrayUtility::getValueByPath($renderingOptions['translation']['arguments'] ?? [], $propertyParts, '.');
357 } catch (MissingArrayPathException $e) {
358 $arguments = [];
359 }
360
361 $originalFormIdentifier = null;
362 if (isset($formRuntime->getRenderingOptions()['_originalIdentifier'])) {
363 $originalFormIdentifier = $formRuntime->getRenderingOptions()['_originalIdentifier'];
364 }
365
366 if ($property === 'options' && is_array($defaultValue)) {
367 foreach ($defaultValue as $optionValue => &$optionLabel) {
368 $translationKeyChain = [];
369 foreach ($translationFiles as $translationFile) {
370 if (!empty($originalFormIdentifier)) {
371 if ($element instanceof FormRuntime) {
372 $translationKeyChain[] = sprintf('%s:%s.element.%s.%s.%s.%s', $translationFile, $originalFormIdentifier, $originalFormIdentifier, $propertyType, $property, $optionValue);
373 $translationKeyChain[] = sprintf('%s:element.%s.%s.%s.%s', $translationFile, $originalFormIdentifier, $propertyType, $property, $optionValue);
374 } else {
375 $translationKeyChain[] = sprintf('%s:%s.element.%s.%s.%s.%s', $translationFile, $originalFormIdentifier, $element->getIdentifier(), $propertyType, $property, $optionValue);
376 }
377 }
378 $translationKeyChain[] = sprintf('%s:%s.element.%s.%s.%s.%s', $translationFile, $formRuntime->getIdentifier(), $element->getIdentifier(), $propertyType, $property, $optionValue);
379 $translationKeyChain[] = sprintf('%s:element.%s.%s.%s.%s', $translationFile, $element->getIdentifier(), $propertyType, $property, $optionValue);
380 $translationKeyChain[] = sprintf('%s:element.%s.%s.%s.%s', $translationFile, $element->getType(), $propertyType, $property, $optionValue);
381 }
382
383 $translatedValue = $this->processTranslationChain($translationKeyChain, $language, $arguments);
384 $optionLabel = empty($translatedValue) ? $optionLabel : $translatedValue;
385 }
386 $translatedValue = $defaultValue;
387 } elseif ($property === 'fluidAdditionalAttributes' && is_array($defaultValue)) {
388 foreach ($defaultValue as $propertyName => &$propertyValue) {
389 $translationKeyChain = [];
390 foreach ($translationFiles as $translationFile) {
391 if (!empty($originalFormIdentifier)) {
392 if ($element instanceof FormRuntime) {
393 $translationKeyChain[] = sprintf('%s:%s.element.%s.%s.%s', $translationFile, $originalFormIdentifier, $originalFormIdentifier, $propertyType, $propertyName);
394 $translationKeyChain[] = sprintf('%s:element.%s.%s.%s', $translationFile, $originalFormIdentifier, $propertyType, $propertyName);
395 } else {
396 $translationKeyChain[] = sprintf('%s:%s.element.%s.%s.%s', $translationFile, $originalFormIdentifier, $element->getIdentifier(), $propertyType, $propertyName);
397 }
398 }
399 $translationKeyChain[] = sprintf('%s:%s.element.%s.%s.%s', $translationFile, $formRuntime->getIdentifier(), $element->getIdentifier(), $propertyType, $propertyName);
400 $translationKeyChain[] = sprintf('%s:element.%s.%s.%s', $translationFile, $element->getIdentifier(), $propertyType, $propertyName);
401 $translationKeyChain[] = sprintf('%s:element.%s.%s.%s', $translationFile, $element->getType(), $propertyType, $propertyName);
402 }
403
404 $translatedValue = $this->processTranslationChain($translationKeyChain, $language, $arguments);
405 $propertyValue = empty($translatedValue) ? $propertyValue : $translatedValue;
406 }
407 $translatedValue = $defaultValue;
408 } else {
409 $translationKeyChain = [];
410 foreach ($translationFiles as $translationFile) {
411 if (!empty($originalFormIdentifier)) {
412 if ($element instanceof FormRuntime) {
413 $translationKeyChain[] = sprintf('%s:%s.element.%s.%s.%s', $translationFile, $originalFormIdentifier, $originalFormIdentifier, $propertyType, $property);
414 $translationKeyChain[] = sprintf('%s:element.%s.%s.%s', $translationFile, $originalFormIdentifier, $propertyType, $property);
415 } else {
416 $translationKeyChain[] = sprintf('%s:%s.element.%s.%s.%s', $translationFile, $originalFormIdentifier, $element->getIdentifier(), $propertyType, $property);
417 }
418 }
419 $translationKeyChain[] = sprintf('%s:%s.element.%s.%s.%s', $translationFile, $formRuntime->getIdentifier(), $element->getIdentifier(), $propertyType, $property);
420 $translationKeyChain[] = sprintf('%s:element.%s.%s.%s', $translationFile, $element->getIdentifier(), $propertyType, $property);
421 $translationKeyChain[] = sprintf('%s:element.%s.%s.%s', $translationFile, $element->getType(), $propertyType, $property);
422 }
423
424 $translatedValue = $this->processTranslationChain($translationKeyChain, $language, $arguments);
425 $translatedValue = empty($translatedValue) ? $defaultValue : $translatedValue;
426 }
427
428 return $translatedValue;
429 }
430
431 /**
432 * @param RootRenderableInterface $element
433 * @param int $code
434 * @param string $defaultValue
435 * @param array $arguments
436 * @param FormRuntime $formRuntime
437 * @return string
438 * @throws \InvalidArgumentException
439 * @internal
440 */
441 public function translateFormElementError(
442 RootRenderableInterface $element,
443 int $code,
444 array $arguments,
445 string $defaultValue = '',
446 FormRuntime $formRuntime
447 ): string {
448 if (empty($code)) {
449 throw new \InvalidArgumentException('The argument "code" is empty', 1489272978);
450 }
451
452 $validationErrors = $element->getProperties()['validationErrorMessages'] ?? null;
453 if (is_array($validationErrors)) {
454 foreach ($validationErrors as $validationError) {
455 if ((int)$validationError['code'] === $code) {
456 return sprintf($validationError['message'], $arguments);
457 }
458 }
459 }
460
461 $renderingOptions = $element->getRenderingOptions();
462 $translationFile = $renderingOptions['translation']['translationFile'] ?? null;
463 if (empty($translationFile)) {
464 $translationFile = $formRuntime->getRenderingOptions()['translation']['translationFile'];
465 }
466
467 if (is_string($translationFile)) {
468 $translationFiles = [$translationFile];
469 } else {
470 $translationFiles = $this->sortArrayWithIntegerKeysDescending($translationFile);
471 }
472
473 $language = null;
474 if (isset($renderingOptions['language'])) {
475 $language = $renderingOptions['language'];
476 }
477
478 $originalFormIdentifier = null;
479 if (isset($formRuntime->getRenderingOptions()['_originalIdentifier'])) {
480 $originalFormIdentifier = $formRuntime->getRenderingOptions()['_originalIdentifier'];
481 }
482
483 $translationKeyChain = [];
484 foreach ($translationFiles as $translationFile) {
485 if (!empty($originalFormIdentifier)) {
486 if ($element instanceof FormRuntime) {
487 $translationKeyChain[] = sprintf('%s:%s.validation.error.%s.%s', $translationFile, $originalFormIdentifier, $originalFormIdentifier, $code);
488 $translationKeyChain[] = sprintf('%s:validation.error.%s.%s', $translationFile, $originalFormIdentifier, $code);
489 } else {
490 $translationKeyChain[] = sprintf('%s:%s.validation.error.%s.%s', $translationFile, $originalFormIdentifier, $element->getIdentifier(), $code);
491 }
492 $translationKeyChain[] = sprintf('%s:%s.validation.error.%s', $translationFile, $originalFormIdentifier, $code);
493 }
494 $translationKeyChain[] = sprintf('%s:%s.validation.error.%s.%s', $translationFile, $formRuntime->getIdentifier(), $element->getIdentifier(), $code);
495 $translationKeyChain[] = sprintf('%s:%s.validation.error.%s', $translationFile, $formRuntime->getIdentifier(), $code);
496 $translationKeyChain[] = sprintf('%s:validation.error.%s.%s', $translationFile, $element->getIdentifier(), $code);
497 $translationKeyChain[] = sprintf('%s:validation.error.%s', $translationFile, $code);
498 }
499
500 $translatedValue = $this->processTranslationChain($translationKeyChain, $language, $arguments);
501 $translatedValue = empty($translatedValue) ? $defaultValue : $translatedValue;
502 return $translatedValue;
503 }
504
505 /**
506 * @param string $languageKey
507 * @internal
508 */
509 public function setLanguage(string $languageKey)
510 {
511 $this->languageKey = $languageKey;
512 }
513
514 /**
515 * @return string
516 * @internal
517 */
518 public function getLanguage(): string
519 {
520 return $this->languageKey;
521 }
522
523 /**
524 * @param array $translationKeyChain
525 * @param string $language
526 * @param array $arguments
527 * @return string|null
528 */
529 protected function processTranslationChain(
530 array $translationKeyChain,
531 string $language = null,
532 array $arguments = null
533 ) {
534 $translatedValue = null;
535 foreach ($translationKeyChain as $translationKey) {
536 $translatedValue = $this->translate($translationKey, $arguments, null, $language);
537 if (!empty($translatedValue)) {
538 break;
539 }
540 }
541 return $translatedValue;
542 }
543
544 /**
545 * @param string $locallangPathAndFilename
546 */
547 protected function initializeLocalization(string $locallangPathAndFilename)
548 {
549 if (empty($this->languageKey)) {
550 $this->setLanguageKeys();
551 }
552
553 if (!empty($locallangPathAndFilename)) {
554 /** @var $languageFactory LocalizationFactory */
555 $languageFactory = GeneralUtility::makeInstance(LocalizationFactory::class);
556 $this->LOCAL_LANG = $languageFactory->getParsedData($locallangPathAndFilename, $this->languageKey);
557
558 foreach ($this->alternativeLanguageKeys as $language) {
559 $tempLL = $languageFactory->getParsedData($locallangPathAndFilename, $language);
560 if ($this->languageKey !== 'default' && isset($tempLL[$language])) {
561 $this->LOCAL_LANG[$language] = $tempLL[$language];
562 }
563 }
564 }
565 $this->loadTypoScriptLabels();
566 }
567
568 /**
569 * Sets the currently active language/language_alt keys.
570 * Default values are "default" for language key and "" for language_alt key.
571 */
572 protected function setLanguageKeys()
573 {
574 $this->languageKey = 'default';
575
576 $this->alternativeLanguageKeys = [];
577 if (TYPO3_MODE === 'FE') {
578 if (isset($this->getTypoScriptFrontendController()->config['config']['language'])) {
579 $this->languageKey = $this->getTypoScriptFrontendController()->config['config']['language'];
580 if (isset($this->getTypoScriptFrontendController()->config['config']['language_alt'])) {
581 $this->alternativeLanguageKeys[] = $this->getTypoScriptFrontendController()->config['config']['language_alt'];
582 } else {
583 /** @var $locales \TYPO3\CMS\Core\Localization\Locales */
584 $locales = GeneralUtility::makeInstance(Locales::class);
585 if (in_array($this->languageKey, $locales->getLocales(), true)) {
586 foreach ($locales->getLocaleDependencies($this->languageKey) as $language) {
587 $this->alternativeLanguageKeys[] = $language;
588 }
589 }
590 }
591 }
592 } elseif (!empty($GLOBALS['BE_USER']->uc['lang'])) {
593 $this->languageKey = $GLOBALS['BE_USER']->uc['lang'];
594 } elseif (!empty($this->getLanguageService()->lang)) {
595 $this->languageKey = $this->getLanguageService()->lang;
596 }
597 }
598
599 /**
600 * Overwrites labels that are set via TypoScript.
601 * TS locallang labels have to be configured like:
602 * plugin.tx_form._LOCAL_LANG.languageKey.key = value
603 */
604 protected function loadTypoScriptLabels()
605 {
606 $frameworkConfiguration = $this->getConfigurationManager()
607 ->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK, 'form');
608
609 if (!is_array($frameworkConfiguration['_LOCAL_LANG'])) {
610 return;
611 }
612 $this->LOCAL_LANG_UNSET = [];
613 foreach ($frameworkConfiguration['_LOCAL_LANG'] as $languageKey => $labels) {
614 if (!(is_array($labels) && isset($this->LOCAL_LANG[$languageKey]))) {
615 continue;
616 }
617 foreach ($labels as $labelKey => $labelValue) {
618 if (is_string($labelValue)) {
619 $this->LOCAL_LANG[$languageKey][$labelKey][0]['target'] = $labelValue;
620 if ($labelValue === '') {
621 $this->LOCAL_LANG_UNSET[$languageKey][$labelKey] = '';
622 }
623 } elseif (is_array($labelValue)) {
624 $labelValue = $this->flattenTypoScriptLabelArray($labelValue, $labelKey);
625 foreach ($labelValue as $key => $value) {
626 $this->LOCAL_LANG[$languageKey][$key][0]['target'] = $value;
627 if ($value === '') {
628 $this->LOCAL_LANG_UNSET[$languageKey][$key] = '';
629 }
630 }
631 }
632 }
633 }
634 }
635
636 /**
637 * Flatten TypoScript label array; converting a hierarchical array into a flat
638 * array with the keys separated by dots.
639 *
640 * Example Input: array('k1' => array('subkey1' => 'val1'))
641 * Example Output: array('k1.subkey1' => 'val1')
642 *
643 * @param array $labelValues Hierarchical array of labels
644 * @param string $parentKey the name of the parent key in the recursion; is only needed for recursion.
645 * @return array flattened array of labels.
646 */
647 protected function flattenTypoScriptLabelArray(array $labelValues, string $parentKey = ''): array
648 {
649 $result = [];
650 foreach ($labelValues as $key => $labelValue) {
651 if (!empty($parentKey)) {
652 $key = $parentKey . '.' . $key;
653 }
654 if (is_array($labelValue)) {
655 $labelValue = $this->flattenTypoScriptLabelArray($labelValue, $key);
656 $result = array_merge($result, $labelValue);
657 } else {
658 $result[$key] = $labelValue;
659 }
660 }
661 return $result;
662 }
663
664 /**
665 * If the array contains numerical keys only, sort it in descending order
666 *
667 * @param array $array
668 * @return array
669 */
670 protected function sortArrayWithIntegerKeysDescending(array $array)
671 {
672 if (count(array_filter(array_keys($array), 'is_string')) === 0) {
673 krsort($array);
674 }
675 return $array;
676 }
677
678 /**
679 * Returns instance of the configuration manager
680 *
681 * @return ConfigurationManagerInterface
682 */
683 protected function getConfigurationManager(): ConfigurationManagerInterface
684 {
685 if ($this->configurationManager !== null) {
686 return $this->configurationManager;
687 }
688
689 $this->configurationManager = GeneralUtility::makeInstance(ObjectManager::class)
690 ->get(ConfigurationManagerInterface::class);
691 return $this->configurationManager;
692 }
693
694 /**
695 * @return LanguageService
696 */
697 protected function getLanguageService(): LanguageService
698 {
699 return $GLOBALS['LANG'];
700 }
701
702 /**
703 * @return TypoScriptFrontendController
704 */
705 protected function getTypoScriptFrontendController(): TypoScriptFrontendController
706 {
707 return $GLOBALS['TSFE'];
708 }
709 }