Passed
Push — master ( ee8df7...208bf9 )
by
unknown
13:35
created

TranslationService::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

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

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