TranslationService::getAllTypo3BackendLanguages()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 4
dl 0
loc 8
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
1
<?php
2
3
declare(strict_types=1);
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
namespace TYPO3\CMS\Form\Service;
19
20
use Psr\Http\Message\ServerRequestInterface;
21
use TYPO3\CMS\Core\Http\ApplicationType;
22
use TYPO3\CMS\Core\Localization\LanguageService;
23
use TYPO3\CMS\Core\Localization\Locales;
24
use TYPO3\CMS\Core\Localization\LocalizationFactory;
25
use TYPO3\CMS\Core\SingletonInterface;
26
use TYPO3\CMS\Core\Site\Entity\SiteLanguage;
27
use TYPO3\CMS\Core\Utility\ArrayUtility;
28
use TYPO3\CMS\Core\Utility\Exception\MissingArrayPathException;
29
use TYPO3\CMS\Core\Utility\GeneralUtility;
30
use TYPO3\CMS\Core\Utility\PathUtility;
31
use TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface;
32
use TYPO3\CMS\Form\Domain\Model\FormElements\FormElementInterface;
33
use TYPO3\CMS\Form\Domain\Model\Renderable\RootRenderableInterface;
34
use TYPO3\CMS\Form\Domain\Runtime\FormRuntime;
35
36
/**
37
 * Advanced translations
38
 * This class is subjected to change.
39
 * **Do NOT subclass**
40
 *
41
 * Scope: frontend / backend
42
 * @internal
43
 */
44
class TranslationService implements SingletonInterface
45
{
46
47
    /**
48
     * Local Language content
49
     *
50
     * @var array
51
     */
52
    protected $LOCAL_LANG = [];
53
54
    /**
55
     * Contains those LL keys, which have been set to (empty) in TypoScript.
56
     * This is necessary, as we cannot distinguish between a nonexisting
57
     * translation and a label that has been cleared by TS.
58
     * In both cases ['key'][0]['target'] is "".
59
     *
60
     * @var array
61
     */
62
    protected $LOCAL_LANG_UNSET = [];
63
64
    /**
65
     * Key of the language to use
66
     *
67
     * @var string
68
     */
69
    protected $languageKey;
70
71
    /**
72
     * Pointer to alternative fall-back language to use
73
     *
74
     * @var array
75
     */
76
    protected $alternativeLanguageKeys = [];
77
78
    protected ConfigurationManagerInterface $configurationManager;
79
80
    public function __construct(ConfigurationManagerInterface $configurationManager)
81
    {
82
        $this->configurationManager = $configurationManager;
83
    }
84
85
    /**
86
     * Returns the localized label of the LOCAL_LANG key, $key.
87
     *
88
     * @param mixed $key The key from the LOCAL_LANG array for which to return the value.
89
     * @param array $arguments the arguments of the extension, being passed over to vsprintf
90
     * @param string $locallangPathAndFilename
91
     * @param string $language
92
     * @param mixed $defaultValue
93
     * @return mixed The value from LOCAL_LANG or $defaultValue if no translation was found.
94
     * @internal
95
     */
96
    public function translate(
97
        $key,
98
        array $arguments = null,
99
        string $locallangPathAndFilename = null,
100
        string $language = null,
101
        $defaultValue = ''
102
    ) {
103
        $value = null;
104
        $key = (string)$key;
105
106
        if ($locallangPathAndFilename) {
107
            $key = $locallangPathAndFilename . ':' . $key;
108
        }
109
110
        $keyParts = explode(':', $key);
111
        if (str_starts_with($key, 'LLL:')) {
112
            $locallangPathAndFilename = $keyParts[1] . ':' . $keyParts[2];
113
            $key = $keyParts[3];
114
        } elseif (PathUtility::isExtensionPath($key)) {
115
            $locallangPathAndFilename = $keyParts[0] . ':' . $keyParts[1];
116
            $key = $keyParts[2];
117
        } else {
118
            if (count($keyParts) === 2) {
119
                $locallangPathAndFilename = $keyParts[0];
120
                $key = $keyParts[1];
121
            }
122
        }
123
124
        if ($language) {
125
            $this->languageKey = $language;
126
        }
127
128
        $this->initializeLocalization($locallangPathAndFilename ?? '');
129
130
        // The "from" charset of csConv() is only set for strings from TypoScript via _LOCAL_LANG
131
        if (!empty($this->LOCAL_LANG[$this->languageKey][$key][0]['target'])
132
            || isset($this->LOCAL_LANG_UNSET[$this->languageKey][$key])
133
        ) {
134
            // Local language translation for key exists
135
            $value = $this->LOCAL_LANG[$this->languageKey][$key][0]['target'];
136
        } elseif (!empty($this->alternativeLanguageKeys)) {
137
            $languages = array_reverse($this->alternativeLanguageKeys);
138
            foreach ($languages as $language) {
139
                if (!empty($this->LOCAL_LANG[$language][$key][0]['target'])
140
                    || isset($this->LOCAL_LANG_UNSET[$language][$key])
141
                ) {
142
                    // Alternative language translation for key exists
143
                    $value = $this->LOCAL_LANG[$language][$key][0]['target'];
144
                    break;
145
                }
146
            }
147
        }
148
149
        if ($value === null && (!empty($this->LOCAL_LANG['default'][$key][0]['target'])
150
            || isset($this->LOCAL_LANG_UNSET['default'][$key]))
151
        ) {
152
            // Default language translation for key exists
153
            // No charset conversion because default is English and thereby ASCII
154
            $value = $this->LOCAL_LANG['default'][$key][0]['target'];
155
        }
156
157
        if (is_array($arguments) && !empty($arguments) && $value !== null) {
158
            $value = vsprintf($value, $arguments);
159
        } else {
160
            if (empty($value)) {
161
                $value = $defaultValue;
162
            }
163
        }
164
165
        return $value;
166
    }
167
168
    /**
169
     * Recursively translate values.
170
     *
171
     * @param array $array
172
     * @param array $translationFiles
173
     * @return array the modified array
174
     * @internal
175
     */
176
    public function translateValuesRecursive(array $array, array $translationFiles = []): array
177
    {
178
        $result = $array;
179
        foreach ($result as $key => $value) {
180
            if (is_array($value)) {
181
                $result[$key] = $this->translateValuesRecursive($value, $translationFiles);
182
            } else {
183
                $this->sortArrayWithIntegerKeysDescending($translationFiles);
184
185
                if (!empty($translationFiles)) {
186
                    foreach ($translationFiles as $translationFile) {
187
                        $translatedValue = $this->translate($value, null, $translationFile, null);
188
                        if (!empty($translatedValue)) {
189
                            $result[$key] = $translatedValue;
190
                            break;
191
                        }
192
                    }
193
                } else {
194
                    $result[$key] = $this->translate($value, null, null, null, $value);
195
                }
196
            }
197
        }
198
        return $result;
199
    }
200
201
    /**
202
     * @param string $key
203
     * @param array $arguments
204
     * @param array $translationFiles
205
     * @return array the modified array
206
     * @internal
207
     */
208
    public function translateToAllBackendLanguages(
209
        string $key,
210
        array $arguments = null,
211
        array $translationFiles = []
212
    ): array {
213
        $result = [];
214
        $translationFiles = $this->sortArrayWithIntegerKeysDescending($translationFiles);
215
216
        foreach ($this->getAllTypo3BackendLanguages() as $language) {
217
            $result[$language] = $key;
218
            foreach ($translationFiles as $translationFile) {
219
                $translatedValue = $this->translate($key, $arguments, $translationFile, $language, $key);
220
                if ($translatedValue !== $key) {
221
                    $result[$language] = $translatedValue;
222
                    break;
223
                }
224
            }
225
        }
226
227
        return $result;
228
    }
229
230
    /**
231
     * @param FormRuntime $formRuntime
232
     * @param string $finisherIdentifier
233
     * @param string $optionKey
234
     * @param string $optionValue
235
     * @param array $renderingOptions
236
     * @return string
237
     * @throws \InvalidArgumentException
238
     */
239
    public function translateFinisherOption(
240
        FormRuntime $formRuntime,
241
        string $finisherIdentifier,
242
        string $optionKey,
243
        string $optionValue,
244
        array $renderingOptions = []
245
    ): string {
246
        if (empty($finisherIdentifier)) {
247
            throw new \InvalidArgumentException('The argument "finisherIdentifier" is empty', 1476216059);
248
        }
249
        if (empty($optionKey)) {
250
            throw new \InvalidArgumentException('The argument "optionKey" is empty', 1476216060);
251
        }
252
253
        $finisherIdentifier = preg_replace('/Finisher$/', '', $finisherIdentifier);
254
        $translationFiles = $renderingOptions['translationFiles'] ?? [];
255
        if (empty($translationFiles)) {
256
            $translationFiles = $formRuntime->getRenderingOptions()['translation']['translationFiles'];
257
        }
258
259
        $translationFiles = $this->sortArrayWithIntegerKeysDescending($translationFiles);
260
261
        if (isset($renderingOptions['translatePropertyValueIfEmpty'])) {
262
            $translatePropertyValueIfEmpty = (bool)$renderingOptions['translatePropertyValueIfEmpty'];
263
        } else {
264
            $translatePropertyValueIfEmpty = true;
265
        }
266
267
        if (empty($optionValue) && !$translatePropertyValueIfEmpty) {
268
            return $optionValue;
269
        }
270
271
        $language = null;
272
        if (isset($renderingOptions['language'])) {
273
            $language = $renderingOptions['language'];
274
        }
275
276
        try {
277
            $arguments = ArrayUtility::getValueByPath($renderingOptions['arguments'] ?? [], $optionKey, '.');
278
        } catch (MissingArrayPathException $e) {
279
            $arguments = [];
280
        }
281
282
        $originalFormIdentifier = null;
283
        if (isset($formRuntime->getRenderingOptions()['_originalIdentifier'])) {
284
            $originalFormIdentifier = $formRuntime->getRenderingOptions()['_originalIdentifier'];
285
        }
286
287
        $translationKeyChain = [];
288
        foreach ($translationFiles as $translationFile) {
289
            if (!empty($originalFormIdentifier)) {
290
                $translationKeyChain[] = sprintf('%s:%s.finisher.%s.%s', $translationFile, $originalFormIdentifier, $finisherIdentifier, $optionKey);
291
            }
292
            $translationKeyChain[] = sprintf('%s:%s.finisher.%s.%s', $translationFile, $formRuntime->getIdentifier(), $finisherIdentifier, $optionKey);
293
            $translationKeyChain[] = sprintf('%s:finisher.%s.%s', $translationFile, $finisherIdentifier, $optionKey);
294
        }
295
296
        $translatedValue = $this->processTranslationChain($translationKeyChain, $language, $arguments);
297
        $translatedValue = empty($translatedValue) ? $optionValue : $translatedValue;
298
299
        return $translatedValue;
300
    }
301
302
    /**
303
     * @param RootRenderableInterface $element
304
     * @param array $propertyParts
305
     * @param FormRuntime $formRuntime
306
     * @return string|array
307
     * @throws \InvalidArgumentException
308
     * @internal
309
     */
310
    public function translateFormElementValue(
311
        RootRenderableInterface $element,
312
        array $propertyParts,
313
        FormRuntime $formRuntime
314
    ) {
315
        if (empty($propertyParts)) {
316
            throw new \InvalidArgumentException('The argument "propertyParts" is empty', 1476216007);
317
        }
318
319
        $propertyType = 'properties';
320
        $property = implode('.', $propertyParts);
321
        $renderingOptions = $element->getRenderingOptions();
322
323
        if ($property === 'label') {
324
            $defaultValue = $element->getLabel();
325
        } else {
326
            if ($element instanceof FormElementInterface) {
327
                try {
328
                    $defaultValue = ArrayUtility::getValueByPath($element->getProperties(), $propertyParts, '.');
329
                } catch (MissingArrayPathException $exception) {
330
                    $defaultValue = null;
331
                }
332
            } else {
333
                $propertyType = 'renderingOptions';
334
                try {
335
                    $defaultValue = ArrayUtility::getValueByPath($renderingOptions, $propertyParts, '.');
336
                } catch (MissingArrayPathException $exception) {
337
                    $defaultValue = null;
338
                }
339
            }
340
        }
341
342
        if (isset($renderingOptions['translation']['translatePropertyValueIfEmpty'])) {
343
            $translatePropertyValueIfEmpty = $renderingOptions['translation']['translatePropertyValueIfEmpty'];
344
        } else {
345
            $translatePropertyValueIfEmpty = true;
346
        }
347
348
        if (empty($defaultValue) && !$translatePropertyValueIfEmpty) {
349
            return $defaultValue;
350
        }
351
352
        $defaultValue = empty($defaultValue) ? '' : $defaultValue;
353
        $translationFiles = $renderingOptions['translation']['translationFiles'] ?? [];
354
        if (empty($translationFiles)) {
355
            $translationFiles = $formRuntime->getRenderingOptions()['translation']['translationFiles'];
356
        }
357
358
        $translationFiles = $this->sortArrayWithIntegerKeysDescending($translationFiles);
359
360
        $language = null;
361
        if (isset($renderingOptions['translation']['language'])) {
362
            $language = $renderingOptions['translation']['language'];
363
        }
364
365
        try {
366
            $arguments = ArrayUtility::getValueByPath($renderingOptions['translation']['arguments'] ?? [], $propertyParts, '.');
367
        } catch (MissingArrayPathException $e) {
368
            $arguments = [];
369
        }
370
371
        $originalFormIdentifier = null;
372
        if (isset($formRuntime->getRenderingOptions()['_originalIdentifier'])) {
373
            $originalFormIdentifier = $formRuntime->getRenderingOptions()['_originalIdentifier'];
374
        }
375
376
        if ($property === 'options' && is_array($defaultValue)) {
377
            foreach ($defaultValue as $optionValue => &$optionLabel) {
378
                $translationKeyChain = [];
379
                foreach ($translationFiles as $translationFile) {
380
                    if (!empty($originalFormIdentifier)) {
381
                        if ($element instanceof FormRuntime) {
382
                            $translationKeyChain[] = sprintf('%s:%s.element.%s.%s.%s.%s', $translationFile, $originalFormIdentifier, $originalFormIdentifier, $propertyType, $property, $optionValue);
383
                            $translationKeyChain[] = sprintf('%s:element.%s.%s.%s.%s', $translationFile, $originalFormIdentifier, $propertyType, $property, $optionValue);
384
                        } else {
385
                            $translationKeyChain[] = sprintf('%s:%s.element.%s.%s.%s.%s', $translationFile, $originalFormIdentifier, $element->getIdentifier(), $propertyType, $property, $optionValue);
386
                        }
387
                    }
388
                    $translationKeyChain[] = sprintf('%s:%s.element.%s.%s.%s.%s', $translationFile, $formRuntime->getIdentifier(), $element->getIdentifier(), $propertyType, $property, $optionValue);
389
                    $translationKeyChain[] = sprintf('%s:element.%s.%s.%s.%s', $translationFile, $element->getIdentifier(), $propertyType, $property, $optionValue);
390
                    $translationKeyChain[] = sprintf('%s:element.%s.%s.%s.%s', $translationFile, $element->getType(), $propertyType, $property, $optionValue);
391
                }
392
393
                $translatedValue = $this->processTranslationChain($translationKeyChain, $language, $arguments);
394
                $optionLabel = empty($translatedValue) ? $optionLabel : $translatedValue;
395
            }
396
            $translatedValue = $defaultValue;
397
        } elseif ($property === 'fluidAdditionalAttributes' && is_array($defaultValue)) {
398
            foreach ($defaultValue as $propertyName => &$propertyValue) {
399
                $translationKeyChain = [];
400
                foreach ($translationFiles as $translationFile) {
401
                    if (!empty($originalFormIdentifier)) {
402
                        if ($element instanceof FormRuntime) {
403
                            $translationKeyChain[] = sprintf('%s:%s.element.%s.%s.%s', $translationFile, $originalFormIdentifier, $originalFormIdentifier, $propertyType, $propertyName);
404
                            $translationKeyChain[] = sprintf('%s:element.%s.%s.%s', $translationFile, $originalFormIdentifier, $propertyType, $propertyName);
405
                        } else {
406
                            $translationKeyChain[] = sprintf('%s:%s.element.%s.%s.%s', $translationFile, $originalFormIdentifier, $element->getIdentifier(), $propertyType, $propertyName);
407
                        }
408
                    }
409
                    $translationKeyChain[] = sprintf('%s:%s.element.%s.%s.%s', $translationFile, $formRuntime->getIdentifier(), $element->getIdentifier(), $propertyType, $propertyName);
410
                    $translationKeyChain[] = sprintf('%s:element.%s.%s.%s', $translationFile, $element->getIdentifier(), $propertyType, $propertyName);
411
                    $translationKeyChain[] = sprintf('%s:element.%s.%s.%s', $translationFile, $element->getType(), $propertyType, $propertyName);
412
                }
413
414
                $translatedValue = $this->processTranslationChain($translationKeyChain, $language, $arguments);
415
                $propertyValue = empty($translatedValue) ? $propertyValue : $translatedValue;
416
            }
417
            $translatedValue = $defaultValue;
418
        } else {
419
            $translationKeyChain = [];
420
            foreach ($translationFiles as $translationFile) {
421
                if (!empty($originalFormIdentifier)) {
422
                    if ($element instanceof FormRuntime) {
423
                        $translationKeyChain[] = sprintf('%s:%s.element.%s.%s.%s', $translationFile, $originalFormIdentifier, $originalFormIdentifier, $propertyType, $property);
424
                        $translationKeyChain[] = sprintf('%s:element.%s.%s.%s', $translationFile, $originalFormIdentifier, $propertyType, $property);
425
                    } else {
426
                        $translationKeyChain[] = sprintf('%s:%s.element.%s.%s.%s', $translationFile, $originalFormIdentifier, $element->getIdentifier(), $propertyType, $property);
427
                    }
428
                }
429
                $translationKeyChain[] = sprintf('%s:%s.element.%s.%s.%s', $translationFile, $formRuntime->getIdentifier(), $element->getIdentifier(), $propertyType, $property);
430
                $translationKeyChain[] = sprintf('%s:element.%s.%s.%s', $translationFile, $element->getIdentifier(), $propertyType, $property);
431
                $translationKeyChain[] = sprintf('%s:element.%s.%s.%s', $translationFile, $element->getType(), $propertyType, $property);
432
            }
433
434
            $translatedValue = $this->processTranslationChain($translationKeyChain, $language, $arguments);
435
            $translatedValue = empty($translatedValue) ? $defaultValue : $translatedValue;
436
        }
437
438
        return $translatedValue;
439
    }
440
441
    /**
442
     * @param RootRenderableInterface $element
443
     * @param int $code
444
     * @param string $defaultValue
445
     * @param array $arguments
446
     * @param FormRuntime $formRuntime
447
     * @return string
448
     * @throws \InvalidArgumentException
449
     * @internal
450
     */
451
    public function translateFormElementError(
452
        RootRenderableInterface $element,
453
        int $code,
454
        array $arguments,
455
        string $defaultValue,
456
        FormRuntime $formRuntime
457
    ): string {
458
        if (empty($code)) {
459
            throw new \InvalidArgumentException('The argument "code" is empty', 1489272978);
460
        }
461
462
        $validationErrors = $element->getProperties()['validationErrorMessages'] ?? null;
0 ignored issues
show
Bug introduced by
The method getProperties() does not exist on TYPO3\CMS\Form\Domain\Mo...RootRenderableInterface. It seems like you code against a sub-type of TYPO3\CMS\Form\Domain\Mo...RootRenderableInterface such as TYPO3\CMS\Form\Domain\Mo...ts\FormElementInterface or TYPO3\CMS\Form\Domain\Model\FormElements\Section or TYPO3\CMS\Form\Domain\Mo...ents\UnknownFormElement or TYPO3\CMS\Form\Domain\Mo...nts\AbstractFormElement or TYPO3\CMS\Form\Domain\Model\FormElements\Section. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

462
        $validationErrors = $element->/** @scrutinizer ignore-call */ getProperties()['validationErrorMessages'] ?? null;
Loading history...
463
        if (is_array($validationErrors)) {
464
            foreach ($validationErrors as $validationError) {
465
                if ((int)$validationError['code'] === $code) {
466
                    return sprintf($validationError['message'], ...$arguments);
467
                }
468
            }
469
        }
470
471
        $renderingOptions = $element->getRenderingOptions();
472
        $translationFiles = $renderingOptions['translation']['translationFiles'] ?? [];
473
        if (empty($translationFiles)) {
474
            $translationFiles = $formRuntime->getRenderingOptions()['translation']['translationFiles'];
475
        }
476
477
        $translationFiles = $this->sortArrayWithIntegerKeysDescending($translationFiles);
478
479
        $language = null;
480
        if (isset($renderingOptions['language'])) {
481
            $language = $renderingOptions['language'];
482
        }
483
484
        $originalFormIdentifier = null;
485
        if (isset($formRuntime->getRenderingOptions()['_originalIdentifier'])) {
486
            $originalFormIdentifier = $formRuntime->getRenderingOptions()['_originalIdentifier'];
487
        }
488
489
        $translationKeyChain = [];
490
        foreach ($translationFiles as $translationFile) {
491
            if (!empty($originalFormIdentifier)) {
492
                if ($element instanceof FormRuntime) {
493
                    $translationKeyChain[] = sprintf('%s:%s.validation.error.%s.%s', $translationFile, $originalFormIdentifier, $originalFormIdentifier, $code);
494
                    $translationKeyChain[] = sprintf('%s:validation.error.%s.%s', $translationFile, $originalFormIdentifier, $code);
495
                } else {
496
                    $translationKeyChain[] = sprintf('%s:%s.validation.error.%s.%s', $translationFile, $originalFormIdentifier, $element->getIdentifier(), $code);
497
                }
498
                $translationKeyChain[] = sprintf('%s:%s.validation.error.%s', $translationFile, $originalFormIdentifier, $code);
499
            }
500
            $translationKeyChain[] = sprintf('%s:%s.validation.error.%s.%s', $translationFile, $formRuntime->getIdentifier(), $element->getIdentifier(), $code);
501
            $translationKeyChain[] = sprintf('%s:%s.validation.error.%s', $translationFile, $formRuntime->getIdentifier(), $code);
502
            $translationKeyChain[] = sprintf('%s:validation.error.%s.%s', $translationFile, $element->getIdentifier(), $code);
503
            $translationKeyChain[] = sprintf('%s:validation.error.%s', $translationFile, $code);
504
        }
505
506
        $translatedValue = $this->processTranslationChain($translationKeyChain, $language, $arguments);
507
        $translatedValue = empty($translatedValue) ? $defaultValue : $translatedValue;
508
        return $translatedValue;
509
    }
510
511
    /**
512
     * @param string $languageKey
513
     * @internal
514
     */
515
    public function setLanguage(string $languageKey)
516
    {
517
        $this->languageKey = $languageKey;
518
    }
519
520
    /**
521
     * @return string
522
     * @internal
523
     */
524
    public function getLanguage(): string
525
    {
526
        return $this->languageKey;
527
    }
528
529
    /**
530
     * @param array $translationKeyChain
531
     * @param string $language
532
     * @param array $arguments
533
     * @return string|null
534
     */
535
    protected function processTranslationChain(
536
        array $translationKeyChain,
537
        string $language = null,
538
        array $arguments = null
539
    ) {
540
        $translatedValue = null;
541
        foreach ($translationKeyChain as $translationKey) {
542
            $translatedValue = $this->translate($translationKey, $arguments, null, $language);
543
            if (!empty($translatedValue)) {
544
                break;
545
            }
546
        }
547
        return $translatedValue;
548
    }
549
550
    /**
551
     * @param string $locallangPathAndFilename
552
     */
553
    protected function initializeLocalization(string $locallangPathAndFilename)
554
    {
555
        if (empty($this->languageKey)) {
556
            $this->setLanguageKeys();
557
        }
558
559
        if (!empty($locallangPathAndFilename)) {
560
            /** @var LocalizationFactory $languageFactory */
561
            $languageFactory = GeneralUtility::makeInstance(LocalizationFactory::class);
562
            $this->LOCAL_LANG = $languageFactory->getParsedData($locallangPathAndFilename, $this->languageKey);
563
564
            foreach ($this->alternativeLanguageKeys as $language) {
565
                $tempLL = $languageFactory->getParsedData($locallangPathAndFilename, $language);
566
                if ($this->languageKey !== 'default' && isset($tempLL[$language])) {
567
                    $this->LOCAL_LANG[$language] = $tempLL[$language];
568
                }
569
            }
570
        }
571
        $this->loadTypoScriptLabels();
572
    }
573
574
    /**
575
     * Sets the currently active language keys.
576
     */
577
    protected function setLanguageKeys()
578
    {
579
        $this->languageKey = 'default';
580
581
        $this->alternativeLanguageKeys = [];
582
        if (($GLOBALS['TYPO3_REQUEST'] ?? null) instanceof ServerRequestInterface
583
            && ApplicationType::fromRequest($GLOBALS['TYPO3_REQUEST'])->isFrontend()
584
        ) {
585
            $this->languageKey = $this->getCurrentSiteLanguage()->getTypo3Language();
586
            if ($this->languageKey !== 'default') {
587
                /** @var \TYPO3\CMS\Core\Localization\Locales $locales */
588
                $locales = GeneralUtility::makeInstance(Locales::class);
589
                if (in_array($this->languageKey, $locales->getLocales(), true)) {
590
                    foreach ($locales->getLocaleDependencies($this->languageKey) as $language) {
591
                        $this->alternativeLanguageKeys[] = $language;
592
                    }
593
                }
594
            }
595
        } elseif (!empty($GLOBALS['BE_USER']->user['lang'])) {
596
            $this->languageKey = $GLOBALS['BE_USER']->user['lang'];
597
        } elseif (!empty($this->getLanguageService()->lang)) {
598
            $this->languageKey = $this->getLanguageService()->lang;
599
        }
600
    }
601
602
    /**
603
     * Overwrites labels that are set via TypoScript.
604
     * TS locallang labels have to be configured like:
605
     * plugin.tx_form._LOCAL_LANG.languageKey.key = value
606
     */
607
    protected function loadTypoScriptLabels()
608
    {
609
        $frameworkConfiguration = $this->configurationManager
610
            ->getConfiguration(ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK, 'form');
611
612
        if (!is_array($frameworkConfiguration['_LOCAL_LANG'] ?? null)) {
613
            return;
614
        }
615
        $this->LOCAL_LANG_UNSET = [];
616
        foreach ($frameworkConfiguration['_LOCAL_LANG'] as $languageKey => $labels) {
617
            if (!(is_array($labels) && isset($this->LOCAL_LANG[$languageKey]))) {
618
                continue;
619
            }
620
            foreach ($labels as $labelKey => $labelValue) {
621
                if (is_string($labelValue)) {
622
                    $this->LOCAL_LANG[$languageKey][$labelKey][0]['target'] = $labelValue;
623
                    if ($labelValue === '') {
624
                        $this->LOCAL_LANG_UNSET[$languageKey][$labelKey] = '';
625
                    }
626
                } elseif (is_array($labelValue)) {
627
                    $labelValue = $this->flattenTypoScriptLabelArray($labelValue, $labelKey);
628
                    foreach ($labelValue as $key => $value) {
629
                        $this->LOCAL_LANG[$languageKey][$key][0]['target'] = $value;
630
                        if ($value === '') {
631
                            $this->LOCAL_LANG_UNSET[$languageKey][$key] = '';
632
                        }
633
                    }
634
                }
635
            }
636
        }
637
    }
638
639
    /**
640
     * Flatten TypoScript label array; converting a hierarchical array into a flat
641
     * array with the keys separated by dots.
642
     *
643
     * Example Input:  array('k1' => array('subkey1' => 'val1'))
644
     * Example Output: array('k1.subkey1' => 'val1')
645
     *
646
     * @param array $labelValues Hierarchical array of labels
647
     * @param string $parentKey the name of the parent key in the recursion; is only needed for recursion.
648
     * @return array flattened array of labels.
649
     */
650
    protected function flattenTypoScriptLabelArray(array $labelValues, string $parentKey = ''): array
651
    {
652
        $result = [];
653
        foreach ($labelValues as $key => $labelValue) {
654
            if (!empty($parentKey)) {
655
                $key = $parentKey . '.' . $key;
656
            }
657
            if (is_array($labelValue)) {
658
                $labelValue = $this->flattenTypoScriptLabelArray($labelValue, $key);
659
                $result = array_merge($result, $labelValue);
660
            } else {
661
                $result[$key] = $labelValue;
662
            }
663
        }
664
        return $result;
665
    }
666
667
    /**
668
     * If the array contains numerical keys only, sort it in descending order
669
     *
670
     * @param array $array
671
     * @return array
672
     */
673
    protected function sortArrayWithIntegerKeysDescending(array $array)
674
    {
675
        if (count(array_filter(array_keys($array), 'is_string')) === 0) {
676
            krsort($array);
677
        }
678
        return $array;
679
    }
680
681
    /**
682
     * Returns the currently configured "site language" if a site is configured (= resolved) in the current request.
683
     *
684
     * @return SiteLanguage|null
685
     */
686
    protected function getCurrentSiteLanguage(): ?SiteLanguage
687
    {
688
        if ($GLOBALS['TYPO3_REQUEST'] instanceof ServerRequestInterface) {
689
            return $GLOBALS['TYPO3_REQUEST']->getAttribute('language', null);
690
        }
691
        return null;
692
    }
693
694
    /**
695
     * @return array
696
     */
697
    protected function getAllTypo3BackendLanguages(): array
698
    {
699
        $languages = array_merge(
700
            ['default'],
701
            array_values($GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['lang']['availableLanguages'] ?? [])
702
        );
703
704
        return $languages;
705
    }
706
707
    /**
708
     * @return LanguageService
709
     */
710
    protected function getLanguageService(): LanguageService
711
    {
712
        return $GLOBALS['LANG'];
713
    }
714
}
715