TYPO3 CMS  TYPO3_8-7
TranslationService.php
Go to the documentation of this file.
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 
30 
40 {
41 
47  protected $LOCAL_LANG = [];
48 
57  protected $LOCAL_LANG_UNSET = [];
58 
64  protected $languageKey = null;
65 
71  protected $alternativeLanguageKeys = [];
72 
76  protected $configurationManager = null;
77 
84  public static function getInstance()
85  {
86  return GeneralUtility::makeInstance(ObjectManager::class)->get(self::class);
87  }
88 
100  public function translate(
101  $key,
102  array $arguments = null,
103  string $locallangPathAndFilename = null,
104  string $language = null,
105  $defaultValue = ''
106  ) {
107  $value = null;
108  $key = (string)$key;
109 
110  if ($locallangPathAndFilename) {
111  $key = $locallangPathAndFilename . ':' . $key;
112  }
113 
114  $keyParts = explode(':', $key);
115  if (GeneralUtility::isFirstPartOfStr($key, 'LLL:')) {
116  $locallangPathAndFilename = $keyParts[1] . ':' . $keyParts[2];
117  $key = $keyParts[3];
118  } elseif (GeneralUtility::isFirstPartOfStr($key, 'EXT:')) {
119  $locallangPathAndFilename = $keyParts[0] . ':' . $keyParts[1];
120  $key = $keyParts[2];
121  } else {
122  if (count($keyParts) === 2) {
123  $locallangPathAndFilename = $keyParts[0];
124  $key = $keyParts[1];
125  }
126  }
127 
128  if ($language) {
129  $this->languageKey = $language;
130  }
131 
132  $this->initializeLocalization($locallangPathAndFilename);
133 
134  // The "from" charset of csConv() is only set for strings from TypoScript via _LOCAL_LANG
135  if (!empty($this->LOCAL_LANG[$this->languageKey][$key][0]['target'])
136  || isset($this->LOCAL_LANG_UNSET[$this->languageKey][$key])
137  ) {
138  // Local language translation for key exists
139  $value = $this->LOCAL_LANG[$this->languageKey][$key][0]['target'];
140  } elseif (!empty($this->alternativeLanguageKeys)) {
141  $languages = array_reverse($this->alternativeLanguageKeys);
142  foreach ($languages as $language) {
143  if (!empty($this->LOCAL_LANG[$language][$key][0]['target'])
144  || isset($this->LOCAL_LANG_UNSET[$language][$key])
145  ) {
146  // Alternative language translation for key exists
147  $value = $this->LOCAL_LANG[$language][$key][0]['target'];
148  break;
149  }
150  }
151  }
152 
153  if ($value === null && (!empty($this->LOCAL_LANG['default'][$key][0]['target'])
154  || isset($this->LOCAL_LANG_UNSET['default'][$key]))
155  ) {
156  // Default language translation for key exists
157  // No charset conversion because default is English and thereby ASCII
158  $value = $this->LOCAL_LANG['default'][$key][0]['target'];
159  }
160 
161  if (is_array($arguments) && !empty($arguments) && $value !== null) {
162  $value = vsprintf($value, $arguments);
163  } else {
164  if (empty($value)) {
165  $value = $defaultValue;
166  }
167  }
168 
169  return $value;
170  }
171 
180  public function translateValuesRecursive(array $array, $translationFile = null): array
181  {
182  $result = $array;
183  foreach ($result as $key => $value) {
184  if (is_array($value)) {
185  $result[$key] = $this->translateValuesRecursive($value, $translationFile);
186  } else {
187  $translationFiles = null;
188  if (is_string($translationFile)) {
189  $translationFiles = [$translationFile];
190  } elseif (is_array($translationFile)) {
191  $translationFiles = $this->sortArrayWithIntegerKeysDescending($translationFile);
192  }
193 
194  if ($translationFiles) {
195  foreach ($translationFiles as $_translationFile) {
196  $translatedValue = $this->translate($value, null, $_translationFile, null);
197  if (!empty($translatedValue)) {
198  $result[$key] = $translatedValue;
199  break;
200  }
201  }
202  } else {
203  $result[$key] = $this->translate($value, null, $translationFile, null, $value);
204  }
205  }
206  }
207  return $result;
208  }
209 
220  public function translateFinisherOption(
221  FormRuntime $formRuntime,
222  string $finisherIdentifier,
223  string $optionKey,
224  string $optionValue,
225  array $renderingOptions = []
226  ): string {
227  if (empty($finisherIdentifier)) {
228  throw new \InvalidArgumentException('The argument "finisherIdentifier" is empty', 1476216059);
229  }
230  if (empty($optionKey)) {
231  throw new \InvalidArgumentException('The argument "optionKey" is empty', 1476216060);
232  }
233 
234  $finisherIdentifier = preg_replace('/Finisher$/', '', $finisherIdentifier);
235  $translationFile = $renderingOptions['translationFile'];
236  if (empty($translationFile)) {
237  $translationFile = $formRuntime->getRenderingOptions()['translation']['translationFile'];
238  }
239 
240  if (is_string($translationFile)) {
241  $translationFiles = [$translationFile];
242  } else {
243  $translationFiles = $this->sortArrayWithIntegerKeysDescending($translationFile);
244  }
245 
246  if (isset($renderingOptions['translatePropertyValueIfEmpty'])) {
247  $translatePropertyValueIfEmpty = (bool)$renderingOptions['translatePropertyValueIfEmpty'];
248  } else {
249  $translatePropertyValueIfEmpty = true;
250  }
251 
252  if (empty($optionValue) && !$translatePropertyValueIfEmpty) {
253  return $optionValue;
254  }
255 
256  $language = null;
257  if (isset($renderingOptions['language'])) {
258  $language = $renderingOptions['language'];
259  }
260 
261  $translationKeyChain = [];
262  foreach ($translationFiles as $translationFile) {
263  $translationKeyChain[] = sprintf('%s:%s.finisher.%s.%s', $translationFile, $formRuntime->getIdentifier(), $finisherIdentifier, $optionKey);
264  $translationKeyChain[] = sprintf('%s:finisher.%s.%s', $translationFile, $finisherIdentifier, $optionKey);
265  }
266 
267  $translatedValue = $this->processTranslationChain($translationKeyChain, $language);
268  $translatedValue = (empty($translatedValue)) ? $optionValue : $translatedValue;
269 
270  return $translatedValue;
271  }
272 
281  public function translateFormElementValue(
282  RootRenderableInterface $element,
283  array $propertyParts,
284  FormRuntime $formRuntime
285  ) {
286  if (empty($propertyParts)) {
287  throw new \InvalidArgumentException('The argument "propertyParts" is empty', 1476216007);
288  }
289 
290  $propertyType = 'properties';
291  $property = implode('.', $propertyParts);
292  $renderingOptions = $element->getRenderingOptions();
293 
294  if ($property === 'label') {
295  $defaultValue = $element->getLabel();
296  } else {
297  if ($element instanceof FormElementInterface) {
298  try {
299  $defaultValue = ArrayUtility::getValueByPath($element->getProperties(), $propertyParts, '.');
300  } catch (\RuntimeException $exception) {
301  $defaultValue = null;
302  }
303  } else {
304  $propertyType = 'renderingOptions';
305  try {
306  $defaultValue = ArrayUtility::getValueByPath($renderingOptions, $propertyParts, '.');
307  } catch (\RuntimeException $exception) {
308  $defaultValue = null;
309  }
310  }
311  }
312 
313  if (isset($renderingOptions['translation']['translatePropertyValueIfEmpty'])) {
314  $translatePropertyValueIfEmpty = $renderingOptions['translation']['translatePropertyValueIfEmpty'];
315  } else {
316  $translatePropertyValueIfEmpty = true;
317  }
318 
319  if (empty($defaultValue) && !$translatePropertyValueIfEmpty) {
320  return $defaultValue;
321  }
322 
323  $defaultValue = empty($defaultValue) ? '' : $defaultValue;
324  $translationFile = $renderingOptions['translation']['translationFile'];
325  if (empty($translationFile)) {
326  $translationFile = $formRuntime->getRenderingOptions()['translation']['translationFile'];
327  }
328 
329  if (is_string($translationFile)) {
330  $translationFiles = [$translationFile];
331  } else {
332  $translationFiles = $this->sortArrayWithIntegerKeysDescending($translationFile);
333  }
334 
335  $language = null;
336  if (isset($renderingOptions['translation']['language'])) {
337  $language = $renderingOptions['translation']['language'];
338  }
339 
340  if ($property === 'options' && is_array($defaultValue)) {
341  foreach ($defaultValue as $optionValue => &$optionLabel) {
342  $translationKeyChain = [];
343  foreach ($translationFiles as $translationFile) {
344  $translationKeyChain[] = sprintf('%s:%s.element.%s.%s.%s.%s', $translationFile, $formRuntime->getIdentifier(), $element->getIdentifier(), $propertyType, $property, $optionValue);
345  $translationKeyChain[] = sprintf('%s:element.%s.%s.%s.%s', $translationFile, $element->getIdentifier(), $propertyType, $property, $optionValue);
346  $translationKeyChain[] = sprintf('%s:element.%s.%s.%s.%s', $translationFile, $element->getType(), $propertyType, $property, $optionValue);
347  }
348 
349  $translatedValue = $this->processTranslationChain($translationKeyChain, $language);
350  $optionLabel = (empty($translatedValue)) ? $optionLabel : $translatedValue;
351  }
352  $translatedValue = $defaultValue;
353  } elseif ($property === 'fluidAdditionalAttributes' && is_array($defaultValue)) {
354  foreach ($defaultValue as $propertyName => &$propertyValue) {
355  $translationKeyChain = [];
356  foreach ($translationFiles as $translationFile) {
357  $translationKeyChain[] = sprintf('%s:%s.element.%s.%s.%s', $translationFile, $formRuntime->getIdentifier(), $element->getIdentifier(), $propertyType, $propertyName);
358  $translationKeyChain[] = sprintf('%s:element.%s.%s.%s', $translationFile, $element->getIdentifier(), $propertyType, $propertyName);
359  $translationKeyChain[] = sprintf('%s:element.%s.%s.%s', $translationFile, $element->getType(), $propertyType, $propertyName);
360  }
361 
362  $translatedValue = $this->processTranslationChain($translationKeyChain, $language);
363  $propertyValue = (empty($translatedValue)) ? $propertyValue : $translatedValue;
364  }
365  $translatedValue = $defaultValue;
366  } else {
367  $translationKeyChain = [];
368  foreach ($translationFiles as $translationFile) {
369  $translationKeyChain[] = sprintf('%s:%s.element.%s.%s.%s', $translationFile, $formRuntime->getIdentifier(), $element->getIdentifier(), $propertyType, $property);
370  $translationKeyChain[] = sprintf('%s:element.%s.%s.%s', $translationFile, $element->getIdentifier(), $propertyType, $property);
371  $translationKeyChain[] = sprintf('%s:element.%s.%s.%s', $translationFile, $element->getType(), $propertyType, $property);
372  }
373 
374  $translatedValue = $this->processTranslationChain($translationKeyChain, $language);
375  $translatedValue = (empty($translatedValue)) ? $defaultValue : $translatedValue;
376  }
377 
378  return $translatedValue;
379  }
380 
391  public function translateFormElementError(
392  RootRenderableInterface $element,
393  int $code,
394  array $arguments,
395  string $defaultValue = '',
396  FormRuntime $formRuntime
397  ): string {
398  if (empty($code)) {
399  throw new \InvalidArgumentException('The argument "code" is empty', 1489272978);
400  }
401 
402  $validationErrors = $element->getProperties()['validationErrorMessages'];
403  if (is_array($validationErrors)) {
404  foreach ($validationErrors as $validationError) {
405  if ((int)$validationError['code'] === $code) {
406  return sprintf($validationError['message'], $arguments);
407  }
408  }
409  }
410 
411  $renderingOptions = $element->getRenderingOptions();
412  $translationFile = $renderingOptions['translation']['translationFile'];
413  if (empty($translationFile)) {
414  $translationFile = $formRuntime->getRenderingOptions()['translation']['translationFile'];
415  }
416 
417  if (is_string($translationFile)) {
418  $translationFiles = [$translationFile];
419  } else {
420  $translationFiles = $this->sortArrayWithIntegerKeysDescending($translationFile);
421  }
422 
423  $language = null;
424  if (isset($renderingOptions['language'])) {
425  $language = $renderingOptions['language'];
426  }
427 
428  $translationKeyChain = [];
429  foreach ($translationFiles as $translationFile) {
430  $translationKeyChain[] = sprintf('%s:%s.validation.error.%s.%s', $translationFile, $formRuntime->getIdentifier(), $element->getIdentifier(), $code);
431  $translationKeyChain[] = sprintf('%s:%s.validation.error.%s', $translationFile, $formRuntime->getIdentifier(), $code);
432  $translationKeyChain[] = sprintf('%s:validation.error.%s.%s', $translationFile, $element->getIdentifier(), $code);
433  $translationKeyChain[] = sprintf('%s:validation.error.%s', $translationFile, $code);
434  }
435 
436  $translatedValue = $this->processTranslationChain($translationKeyChain, $language, $arguments);
437  $translatedValue = (empty($translatedValue)) ? $defaultValue : $translatedValue;
438  return $translatedValue;
439  }
440 
445  public function setLanguage(string $languageKey)
446  {
447  $this->languageKey = $languageKey;
448  }
449 
454  public function getLanguage(): string
455  {
456  return $this->languageKey;
457  }
458 
465  protected function processTranslationChain(
466  array $translationKeyChain,
467  string $language = null,
468  array $arguments = null
469  ) {
470  $translatedValue = null;
471  foreach ($translationKeyChain as $translationKey) {
472  $translatedValue = $this->translate($translationKey, $arguments, null, $language);
473  if (!empty($translatedValue)) {
474  break;
475  }
476  }
477  return $translatedValue;
478  }
479 
483  protected function initializeLocalization(string $locallangPathAndFilename)
484  {
485  if (empty($this->languageKey)) {
486  $this->setLanguageKeys();
487  }
488 
489  if (!empty($locallangPathAndFilename)) {
491  $languageFactory = GeneralUtility::makeInstance(LocalizationFactory::class);
492  $this->LOCAL_LANG = $languageFactory->getParsedData($locallangPathAndFilename, $this->languageKey, 'utf-8');
493 
494  foreach ($this->alternativeLanguageKeys as $language) {
495  $tempLL = $languageFactory->getParsedData($locallangPathAndFilename, $language, 'utf-8');
496  if ($this->languageKey !== 'default' && isset($tempLL[$language])) {
497  $this->LOCAL_LANG[$language] = $tempLL[$language];
498  }
499  }
500  }
501  $this->loadTypoScriptLabels();
502  }
503 
508  protected function setLanguageKeys()
509  {
510  $this->languageKey = 'default';
511 
512  $this->alternativeLanguageKeys = [];
513  if (TYPO3_MODE === 'FE') {
514  if (isset($this->getTypoScriptFrontendController()->config['config']['language'])) {
515  $this->languageKey = $this->getTypoScriptFrontendController()->config['config']['language'];
516  if (isset($this->getTypoScriptFrontendController()->config['config']['language_alt'])) {
517  $this->alternativeLanguageKeys[] = $this->getTypoScriptFrontendController()->config['config']['language_alt'];
518  } else {
520  $locales = GeneralUtility::makeInstance(Locales::class);
521  if (in_array($this->languageKey, $locales->getLocales(), true)) {
522  foreach ($locales->getLocaleDependencies($this->languageKey) as $language) {
523  $this->alternativeLanguageKeys[] = $language;
524  }
525  }
526  }
527  }
528  } elseif (!empty($GLOBALS['BE_USER']->uc['lang'])) {
529  $this->languageKey = $GLOBALS['BE_USER']->uc['lang'];
530  } elseif (!empty($this->getLanguageService()->lang)) {
531  $this->languageKey = $this->getLanguageService()->lang;
532  }
533  }
534 
540  protected function loadTypoScriptLabels()
541  {
542  $frameworkConfiguration = $this->getConfigurationManager()
544 
545  if (!is_array($frameworkConfiguration['_LOCAL_LANG'])) {
546  return;
547  }
548  $this->LOCAL_LANG_UNSET = [];
549  foreach ($frameworkConfiguration['_LOCAL_LANG'] as $languageKey => $labels) {
550  if (!(is_array($labels) && isset($this->LOCAL_LANG[$languageKey]))) {
551  continue;
552  }
553  foreach ($labels as $labelKey => $labelValue) {
554  if (is_string($labelValue)) {
555  $this->LOCAL_LANG[$languageKey][$labelKey][0]['target'] = $labelValue;
556  if ($labelValue === '') {
557  $this->LOCAL_LANG_UNSET[$languageKey][$labelKey] = '';
558  }
559  } elseif (is_array($labelValue)) {
560  $labelValue = $this->flattenTypoScriptLabelArray($labelValue, $labelKey);
561  foreach ($labelValue as $key => $value) {
562  $this->LOCAL_LANG[$languageKey][$key][0]['target'] = $value;
563  if ($value === '') {
564  $this->LOCAL_LANG_UNSET[$languageKey][$key] = '';
565  }
566  }
567  }
568  }
569  }
570  }
571 
583  protected function flattenTypoScriptLabelArray(array $labelValues, string $parentKey = ''): array
584  {
585  $result = [];
586  foreach ($labelValues as $key => $labelValue) {
587  if (!empty($parentKey)) {
588  $key = $parentKey . '.' . $key;
589  }
590  if (is_array($labelValue)) {
591  $labelValue = $this->flattenTypoScriptLabelArray($labelValue, $key);
592  $result = array_merge($result, $labelValue);
593  } else {
594  $result[$key] = $labelValue;
595  }
596  }
597  return $result;
598  }
599 
606  protected function sortArrayWithIntegerKeysDescending(array $array)
607  {
608  if (count(array_filter(array_keys($array), 'is_string')) === 0) {
609  krsort($array);
610  }
611  return $array;
612  }
613 
620  {
621  if ($this->configurationManager !== null) {
623  }
624 
625  $this->configurationManager = GeneralUtility::makeInstance(ObjectManager::class)
626  ->get(ConfigurationManagerInterface::class);
628  }
629 
633  protected function getLanguageService(): LanguageService
634  {
635  return $GLOBALS['LANG'];
636  }
637 
642  {
643  return $GLOBALS['TSFE'];
644  }
645 }
translateValuesRecursive(array $array, $translationFile=null)
static getValueByPath(array $array, $path, $delimiter='/')
static isFirstPartOfStr($str, $partStr)
flattenTypoScriptLabelArray(array $labelValues, string $parentKey='')
static makeInstance($className,... $constructorArguments)
translateFormElementValue(RootRenderableInterface $element, array $propertyParts, FormRuntime $formRuntime)
processTranslationChain(array $translationKeyChain, string $language=null, array $arguments=null)
if(TYPO3_MODE==='BE') $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_tsfebeuserauth.php']['frontendEditingController']['default']
translateFinisherOption(FormRuntime $formRuntime, string $finisherIdentifier, string $optionKey, string $optionValue, array $renderingOptions=[])
translate( $key, array $arguments=null, string $locallangPathAndFilename=null, string $language=null, $defaultValue='')
$locales
Definition: be_users.php:6
translateFormElementError(RootRenderableInterface $element, int $code, array $arguments, string $defaultValue='', FormRuntime $formRuntime)