[CLEANUP] Replace strlen() with === for zero length check
[Packages/TYPO3.CMS.git] / typo3 / sysext / extbase / Classes / Utility / LocalizationUtility.php
1 <?php
2 namespace TYPO3\CMS\Extbase\Utility;
3
4 /*
5 * This file is part of the TYPO3 CMS project.
6 *
7 * It is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU General Public License, either version 2
9 * of the License, or any later version.
10 *
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
13 *
14 * The TYPO3 project - inspiring people to share!
15 */
16
17 /**
18 * Localization helper which should be used to fetch localized labels.
19 *
20 * @api
21 */
22 class LocalizationUtility {
23
24 /**
25 * @var string
26 */
27 static protected $locallangPath = 'Resources/Private/Language/';
28
29 /**
30 * Local Language content
31 *
32 * @var array
33 */
34 static protected $LOCAL_LANG = array();
35
36 /**
37 * Contains those LL keys, which have been set to (empty) in TypoScript.
38 * This is necessary, as we cannot distinguish between a nonexisting
39 * translation and a label that has been cleared by TS.
40 * In both cases ['key'][0]['target'] is "".
41 *
42 * @var array
43 */
44 static protected $LOCAL_LANG_UNSET = array();
45
46 /**
47 * Local Language content charset for individual labels (overriding)
48 *
49 * @var array
50 */
51 static protected $LOCAL_LANG_charset = array();
52
53 /**
54 * Key of the language to use
55 *
56 * @var string
57 */
58 static protected $languageKey = 'default';
59
60 /**
61 * Pointer to alternative fall-back language to use
62 *
63 * @var array
64 */
65 static protected $alternativeLanguageKeys = array();
66
67 /**
68 * @var \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
69 */
70 static protected $configurationManager = NULL;
71
72 /**
73 * Returns the localized label of the LOCAL_LANG key, $key.
74 *
75 * @param string $key The key from the LOCAL_LANG array for which to return the value.
76 * @param string $extensionName The name of the extension
77 * @param array $arguments the arguments of the extension, being passed over to vsprintf
78 * @return string|NULL The value from LOCAL_LANG or NULL if no translation was found.
79 * @api
80 * @todo : If vsprintf gets a malformed string, it returns FALSE! Should we throw an exception there?
81 */
82 static public function translate($key, $extensionName, $arguments = NULL) {
83 $value = NULL;
84 if (\TYPO3\CMS\Core\Utility\GeneralUtility::isFirstPartOfStr($key, 'LLL:')) {
85 $value = self::translateFileReference($key);
86 } else {
87 self::initializeLocalization($extensionName);
88 // The "from" charset of csConv() is only set for strings from TypoScript via _LOCAL_LANG
89 if (!empty(self::$LOCAL_LANG[$extensionName][self::$languageKey][$key][0]['target'])
90 || isset(self::$LOCAL_LANG_UNSET[$extensionName][self::$languageKey][$key])
91 ) {
92 // Local language translation for key exists
93 $value = self::$LOCAL_LANG[$extensionName][self::$languageKey][$key][0]['target'];
94 if (!empty(self::$LOCAL_LANG_charset[$extensionName][self::$languageKey][$key])) {
95 $value = self::convertCharset($value, self::$LOCAL_LANG_charset[$extensionName][self::$languageKey][$key]);
96 }
97 } elseif (count(self::$alternativeLanguageKeys)) {
98 $languages = array_reverse(self::$alternativeLanguageKeys);
99 foreach ($languages as $language) {
100 if (!empty(self::$LOCAL_LANG[$extensionName][$language][$key][0]['target'])
101 || isset(self::$LOCAL_LANG_UNSET[$extensionName][$language][$key])
102 ) {
103 // Alternative language translation for key exists
104 $value = self::$LOCAL_LANG[$extensionName][$language][$key][0]['target'];
105 if (!empty(self::$LOCAL_LANG_charset[$extensionName][$language][$key])) {
106 $value = self::convertCharset($value, self::$LOCAL_LANG_charset[$extensionName][$language][$key]);
107 }
108 break;
109 }
110 }
111 }
112 if ($value === NULL && (!empty(self::$LOCAL_LANG[$extensionName]['default'][$key][0]['target'])
113 || isset(self::$LOCAL_LANG_UNSET[$extensionName]['default'][$key]))
114 ) {
115 // Default language translation for key exists
116 // No charset conversion because default is English and thereby ASCII
117 $value = self::$LOCAL_LANG[$extensionName]['default'][$key][0]['target'];
118 }
119 }
120 if (is_array($arguments) && $value !== NULL) {
121 return vsprintf($value, $arguments);
122 } else {
123 return $value;
124 }
125 }
126
127 /**
128 * Returns the localized label of the LOCAL_LANG key, $key.
129 *
130 * @param string $key The language key including the path to a custom locallang file ("LLL:path:key").
131 * @return string The value from LOCAL_LANG or NULL if no translation was found.
132 * @see language::sL()
133 * @see \TYPO3\CMS\Frontend\Controller\TypoScriptFrontendController::sL()
134 */
135 static protected function translateFileReference($key) {
136 if (TYPO3_MODE === 'FE') {
137 $value = $GLOBALS['TSFE']->sL($key);
138 return $value !== FALSE ? $value : NULL;
139 } elseif (is_object($GLOBALS['LANG'])) {
140 $value = $GLOBALS['LANG']->sL($key);
141 return $value !== '' ? $value : NULL;
142 } else {
143 return $key;
144 }
145 }
146
147 /**
148 * Loads local-language values by looking for a "locallang.xlf" (or "locallang.xml") file in the plugin resources directory and if found includes it.
149 * Also locallang values set in the TypoScript property "_LOCAL_LANG" are merged onto the values found in the "locallang.xlf" file.
150 *
151 * @param string $extensionName
152 * @return void
153 */
154 static protected function initializeLocalization($extensionName) {
155 if (isset(self::$LOCAL_LANG[$extensionName])) {
156 return;
157 }
158 $locallangPathAndFilename = 'EXT:' . \TYPO3\CMS\Core\Utility\GeneralUtility::camelCaseToLowerCaseUnderscored($extensionName) . '/' . self::$locallangPath . 'locallang.xlf';
159 self::setLanguageKeys();
160 $renderCharset = TYPO3_MODE === 'FE' ? $GLOBALS['TSFE']->renderCharset : $GLOBALS['LANG']->charSet;
161 self::$LOCAL_LANG[$extensionName] = \TYPO3\CMS\Core\Utility\GeneralUtility::readLLfile($locallangPathAndFilename, self::$languageKey, $renderCharset);
162 foreach (self::$alternativeLanguageKeys as $language) {
163 $tempLL = \TYPO3\CMS\Core\Utility\GeneralUtility::readLLfile($locallangPathAndFilename, $language, $renderCharset);
164 if (self::$languageKey !== 'default' && isset($tempLL[$language])) {
165 self::$LOCAL_LANG[$extensionName][$language] = $tempLL[$language];
166 }
167 }
168 self::loadTypoScriptLabels($extensionName);
169 }
170
171 /**
172 * Sets the currently active language/language_alt keys.
173 * Default values are "default" for language key and "" for language_alt key.
174 *
175 * @return void
176 */
177 static protected function setLanguageKeys() {
178 self::$languageKey = 'default';
179 self::$alternativeLanguageKeys = array();
180 if (TYPO3_MODE === 'FE') {
181 if (isset($GLOBALS['TSFE']->config['config']['language'])) {
182 self::$languageKey = $GLOBALS['TSFE']->config['config']['language'];
183 if (isset($GLOBALS['TSFE']->config['config']['language_alt'])) {
184 self::$alternativeLanguageKeys[] = $GLOBALS['TSFE']->config['config']['language_alt'];
185 } else {
186 /** @var $locales \TYPO3\CMS\Core\Localization\Locales */
187 $locales = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Core\Localization\Locales::class);
188 if (in_array(self::$languageKey, $locales->getLocales())) {
189 foreach ($locales->getLocaleDependencies(self::$languageKey) as $language) {
190 self::$alternativeLanguageKeys[] = $language;
191 }
192 }
193 }
194 }
195 } elseif ($GLOBALS['BE_USER']->uc['lang'] !== '') {
196 self::$languageKey = $GLOBALS['BE_USER']->uc['lang'];
197 }
198 }
199
200 /**
201 * Overwrites labels that are set via TypoScript.
202 * TS locallang labels have to be configured like:
203 * plugin.tx_myextension._LOCAL_LANG.languageKey.key = value
204 *
205 * @param string $extensionName
206 * @return void
207 */
208 static protected function loadTypoScriptLabels($extensionName) {
209 $configurationManager = static::getConfigurationManager();
210 $frameworkConfiguration = $configurationManager->getConfiguration(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::CONFIGURATION_TYPE_FRAMEWORK, $extensionName);
211 if (!is_array($frameworkConfiguration['_LOCAL_LANG'])) {
212 return;
213 }
214 self::$LOCAL_LANG_UNSET[$extensionName] = array();
215 foreach ($frameworkConfiguration['_LOCAL_LANG'] as $languageKey => $labels) {
216 if (!(is_array($labels) && isset(self::$LOCAL_LANG[$extensionName][$languageKey]))) {
217 continue;
218 }
219 foreach ($labels as $labelKey => $labelValue) {
220 if (is_string($labelValue)) {
221 self::$LOCAL_LANG[$extensionName][$languageKey][$labelKey][0]['target'] = $labelValue;
222 if ($labelValue === '') {
223 self::$LOCAL_LANG_UNSET[$extensionName][$languageKey][$labelKey] = '';
224 }
225 if (is_object($GLOBALS['LANG'])) {
226 self::$LOCAL_LANG_charset[$extensionName][$languageKey][$labelKey] = $GLOBALS['LANG']->csConvObj->charSetArray[$languageKey];
227 } else {
228 self::$LOCAL_LANG_charset[$extensionName][$languageKey][$labelKey] = $GLOBALS['TSFE']->csConvObj->charSetArray[$languageKey];
229 }
230 } elseif (is_array($labelValue)) {
231 $labelValue = self::flattenTypoScriptLabelArray($labelValue, $labelKey);
232 foreach ($labelValue as $key => $value) {
233 self::$LOCAL_LANG[$extensionName][$languageKey][$key][0]['target'] = $value;
234 if ($value === '') {
235 self::$LOCAL_LANG_UNSET[$extensionName][$languageKey][$key] = '';
236 }
237 }
238 }
239 }
240 }
241 }
242
243 /**
244 * Flatten TypoScript label array; converting a hierarchical array into a flat
245 * array with the keys separated by dots.
246 *
247 * Example Input: array('k1' => array('subkey1' => 'val1'))
248 * Example Output: array('k1.subkey1' => 'val1')
249 *
250 * @param array $labelValues Hierarchical array of labels
251 * @param string $parentKey the name of the parent key in the recursion; is only needed for recursion.
252 * @return array flattened array of labels.
253 */
254 static protected function flattenTypoScriptLabelArray(array $labelValues, $parentKey = '') {
255 $result = array();
256 foreach ($labelValues as $key => $labelValue) {
257 if (!empty($parentKey)) {
258 $key = $parentKey . '.' . $key;
259 }
260 if (is_array($labelValue)) {
261 $labelValue = self::flattenTypoScriptLabelArray($labelValue, $key);
262 $result = array_merge($result, $labelValue);
263 } else {
264 $result[$key] = $labelValue;
265 }
266 }
267 return $result;
268 }
269
270 /**
271 * Converts a string from the specified character set to the current.
272 * The current charset is defined by the TYPO3 mode.
273 *
274 * @param string $value string to be converted
275 * @param string $charset The source charset
276 * @return string converted string
277 */
278 static protected function convertCharset($value, $charset) {
279 if (TYPO3_MODE === 'FE') {
280 return $GLOBALS['TSFE']->csConv($value, $charset);
281 } else {
282 $convertedValue = $GLOBALS['LANG']->csConvObj->conv($value, $GLOBALS['LANG']->csConvObj->parse_charset($charset), $GLOBALS['LANG']->charSet, 1);
283 return $convertedValue !== NULL ? $convertedValue : $value;
284 }
285 }
286
287 /**
288 * Returns instance of the configuration manager
289 *
290 * @return \TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface
291 */
292 static protected function getConfigurationManager() {
293 if (!is_null(static::$configurationManager)) {
294 return static::$configurationManager;
295 }
296 $objectManager = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Object\ObjectManager::class);
297 $configurationManager = $objectManager->get(\TYPO3\CMS\Extbase\Configuration\ConfigurationManagerInterface::class);
298 static::$configurationManager = $configurationManager;
299 return $configurationManager;
300 }
301
302 }