‪TYPO3CMS  ‪main
LocalizationUtility.php
Go to the documentation of this file.
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 
19 
20 use Psr\Http\Message\ServerRequestInterface;
30 
35 {
36  protected static string ‪$locallangPath = 'Resources/Private/Language/';
37 
47  public static function ‪translate(string $key, ?string $extensionName = null, array $arguments = null, ‪Locale|string $languageKey = null): ?string
48  {
49  if ($key === '') {
50  // Early return guard: returns null if the key was empty, because the key may be a dynamic value
51  // (from for example Fluid). Returning null allows null coalescing to a default value when that happens.
52  return null;
53  }
54  if (str_starts_with($key, 'LLL:')) {
55  $keyParts = explode(':', $key);
56  unset($keyParts[0]);
57  $key = array_pop($keyParts);
58  $languageFilePath = implode(':', $keyParts);
59  } else {
60  if (empty($extensionName)) {
61  throw new \InvalidArgumentException(
62  'Parameter $extensionName cannot be empty if a fully-qualified key is not specified.',
63  1498144052
64  );
65  }
66  $languageFilePath = static::getLanguageFilePath($extensionName);
67  }
68  $locale = ‪self::getLocale($languageKey);
69  $languageService = static::initializeLocalization($languageFilePath, $locale, $extensionName);
70  $resolvedLabel = $languageService->sL('LLL:' . $languageFilePath . ':' . $key);
71  $value = $resolvedLabel !== '' ? $resolvedLabel : null;
72 
73  // Check if a value was explicitly set to "" via TypoScript, if so, we need to ensure that this is "" and not null
74  if ($extensionName) {
75  $overrideLabels = static::loadTypoScriptLabels($extensionName);
76  $languageKey = $locale->getName();
77  // @todo: probably cannot handle "de-DE" and "de" fallbacks
78  if ($value === null && isset($overrideLabels[$languageKey])) {
79  $value = '';
80  }
81  }
82 
83  if (is_array($arguments) && $arguments !== [] && $value !== null) {
84  // This unrolls arguments from $arguments - instead of calling vsprintf which receives arguments as an array.
85  // The reason is that only sprintf() will return an error message if the number of arguments does not match
86  // the number of placeholders in the format string. Whereas, vsprintf would silently return nothing.
87  return sprintf($value, ...array_values($arguments)) ?: sprintf('Error: could not translate key "%s" with value "%s" and %d argument(s)!', $key, $value, count($arguments));
88  }
89  return $value;
90  }
91 
96  protected static function ‪initializeLocalization(string $languageFilePath, ‪Locale $locale, ?string $extensionName): ‪LanguageService
97  {
98  $languageService = ‪self::buildLanguageService($locale, $languageFilePath);
99  if (!empty($extensionName)) {
100  $overrideLabels = static::loadTypoScriptLabels($extensionName);
101  if ($overrideLabels !== []) {
102  $languageService->overrideLabels($languageFilePath, $overrideLabels);
103  }
104  }
105  return $languageService;
106  }
107 
108  protected static function ‪buildLanguageService(‪Locale $locale, string $languageFilePath): ‪LanguageService
109  {
110  $languageKeyHash = sha1(json_encode(array_merge([(string)$locale], $locale->getDependencies(), [$languageFilePath])));
111  $cache = ‪self::getRuntimeCache();
112  if (!$cache->get($languageKeyHash)) {
113  $languageService = GeneralUtility::makeInstance(LanguageServiceFactory::class)->create($locale);
114  $languageService->includeLLFile($languageFilePath);
115  $cache->set($languageKeyHash, $languageService);
116  }
117  return $cache->get($languageKeyHash);
118  }
119 
123  protected static function ‪getLanguageFilePath(string $extensionName): string
124  {
125  return 'EXT:' . ‪GeneralUtility::camelCaseToLowerCaseUnderscored($extensionName) . '/' . self::$locallangPath . 'locallang.xlf';
126  }
127 
132  protected static function ‪getLocale(‪Locale|string|null $localeOrLanguageKey): ‪Locale
133  {
134  if ($localeOrLanguageKey instanceof ‪Locale) {
135  return $localeOrLanguageKey;
136  }
137  $localeFactory = GeneralUtility::makeInstance(Locales::class);
138  if (is_string($localeOrLanguageKey)) {
139  return $localeFactory->createLocale($localeOrLanguageKey);
140  }
141  return $localeFactory->createLocaleFromRequest(‪$GLOBALS['TYPO3_REQUEST'] ?? null);
142  }
143 
149  protected static function ‪loadTypoScriptLabels(string $extensionName): array
150  {
151  // Only allow overrides in Frontend Context
152  $request = ‪$GLOBALS['TYPO3_REQUEST'] ?? null;
153  if (!$request instanceof ServerRequestInterface || !‪ApplicationType::fromRequest($request)->isFrontend()) {
154  return [];
155  }
156  $configurationManager = GeneralUtility::makeInstance(ConfigurationManagerInterface::class);
157  $frameworkConfiguration = $configurationManager->getConfiguration(‪ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK, $extensionName);
158  if (!is_array($frameworkConfiguration['_LOCAL_LANG'] ?? false)) {
159  return [];
160  }
161  $finalLabels = [];
162  foreach ($frameworkConfiguration['_LOCAL_LANG'] as $languageKey => $labels) {
163  if (!is_array($labels)) {
164  continue;
165  }
166  foreach ($labels as $labelKey => $labelValue) {
167  if (is_string($labelValue)) {
168  $finalLabels[$languageKey][$labelKey] = $labelValue;
169  } elseif (is_array($labelValue)) {
170  $labelValue = ‪self::flattenTypoScriptLabelArray($labelValue, $labelKey);
171  foreach ($labelValue as $key => $value) {
172  $finalLabels[$languageKey][$key] = $value;
173  }
174  }
175  }
176  }
177  return $finalLabels;
178  }
179 
191  protected static function ‪flattenTypoScriptLabelArray(array $labelValues, string $parentKey = ''): array
192  {
193  $result = [];
194  foreach ($labelValues as $key => $labelValue) {
195  if (!empty($parentKey)) {
196  if ($key === '_typoScriptNodeValue') {
197  $key = $parentKey;
198  } else {
199  $key = $parentKey . '.' . $key;
200  }
201  }
202  if (is_array($labelValue)) {
203  $labelValue = ‪self::flattenTypoScriptLabelArray($labelValue, $key);
204  $result = array_merge($result, $labelValue);
205  } else {
206  $result[$key] = $labelValue;
207  }
208  }
209  return $result;
210  }
211 
212  protected static function ‪getRuntimeCache(): ‪FrontendInterface
213  {
214  return GeneralUtility::makeInstance(CacheManager::class)->getCache('runtime');
215  }
216 }
‪TYPO3\CMS\Core\Localization\LanguageServiceFactory
Definition: LanguageServiceFactory.php:25
‪TYPO3\CMS\Extbase\Utility\LocalizationUtility\getRuntimeCache
‪static getRuntimeCache()
Definition: LocalizationUtility.php:212
‪TYPO3\CMS\Extbase\Utility\LocalizationUtility
Definition: LocalizationUtility.php:35
‪TYPO3\CMS\Extbase\Utility\LocalizationUtility\getLanguageFilePath
‪static getLanguageFilePath(string $extensionName)
Definition: LocalizationUtility.php:123
‪TYPO3\CMS\Extbase\Utility\LocalizationUtility\$locallangPath
‪static string $locallangPath
Definition: LocalizationUtility.php:36
‪TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
Definition: ConfigurationManagerInterface.php:28
‪TYPO3\CMS\Extbase\Utility\LocalizationUtility\flattenTypoScriptLabelArray
‪static array flattenTypoScriptLabelArray(array $labelValues, string $parentKey='')
Definition: LocalizationUtility.php:191
‪TYPO3\CMS\Core\Localization\Locales
Definition: Locales.php:36
‪TYPO3\CMS\Core\Utility\GeneralUtility\camelCaseToLowerCaseUnderscored
‪static string camelCaseToLowerCaseUnderscored($string)
Definition: GeneralUtility.php:683
‪TYPO3\CMS\Extbase\Utility\LocalizationUtility\loadTypoScriptLabels
‪static loadTypoScriptLabels(string $extensionName)
Definition: LocalizationUtility.php:149
‪TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface\CONFIGURATION_TYPE_FRAMEWORK
‪const CONFIGURATION_TYPE_FRAMEWORK
Definition: ConfigurationManagerInterface.php:29
‪TYPO3\CMS\Extbase\Utility\LocalizationUtility\getLocale
‪static getLocale(Locale|string|null $localeOrLanguageKey)
Definition: LocalizationUtility.php:132
‪TYPO3\CMS\Extbase\Utility\LocalizationUtility\initializeLocalization
‪static initializeLocalization(string $languageFilePath, Locale $locale, ?string $extensionName)
Definition: LocalizationUtility.php:96
‪TYPO3\CMS\Extbase\Utility\LocalizationUtility\translate
‪static string null translate(string $key, ?string $extensionName=null, array $arguments=null, Locale|string $languageKey=null)
Definition: LocalizationUtility.php:47
‪TYPO3\CMS\Core\Cache\CacheManager
Definition: CacheManager.php:36
‪TYPO3\CMS\Core\Cache\Frontend\FrontendInterface
Definition: FrontendInterface.php:22
‪$GLOBALS
‪$GLOBALS['TYPO3_CONF_VARS']['EXTCONF']['adminpanel']['modules']
Definition: ext_localconf.php:25
‪TYPO3\CMS\Core\Localization\Locale
Definition: Locale.php:30
‪TYPO3\CMS\Extbase\Utility
Definition: DebuggerUtility.php:18
‪TYPO3\CMS\Core\Http\fromRequest
‪@ fromRequest
Definition: ApplicationType.php:66
‪TYPO3\CMS\Core\Localization\LanguageService
Definition: LanguageService.php:46
‪TYPO3\CMS\Core\Utility\GeneralUtility
Definition: GeneralUtility.php:52
‪TYPO3\CMS\Extbase\Utility\LocalizationUtility\buildLanguageService
‪static buildLanguageService(Locale $locale, string $languageFilePath)
Definition: LocalizationUtility.php:108
‪TYPO3\CMS\Core\Http\ApplicationType
‪ApplicationType
Definition: ApplicationType.php:55