2 namespace TYPO3\CMS\Lang
;
5 * This file is part of the TYPO3 CMS project.
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.
11 * For the full copyright and license information, please read the
12 * LICENSE.txt file that was distributed with this source code.
14 * The TYPO3 project - inspiring people to share!
17 use TYPO3\CMS\Core\Charset\CharsetConverter
;
18 use TYPO3\CMS\Core\Localization\Locales
;
19 use TYPO3\CMS\Core\Localization\LocalizationFactory
;
20 use TYPO3\CMS\Core\Utility\ArrayUtility
;
21 use TYPO3\CMS\Core\Utility\GeneralUtility
;
24 * Contains the TYPO3 Backend Language class
25 * For detailed information about how localization is handled,
26 * please refer to the 'Inside TYPO3' document which describes this.
27 * This class is normally instantiated as the global variable $GLOBALS['LANG']
28 * It's only available in the backend and under certain circumstances in the frontend
29 * @see \TYPO3\CMS\Backend\Template\DocumentTemplate
34 * This is set to the language that is currently running for the user
38 public $lang = 'default';
41 * Default charset in backend
45 public $charSet = 'utf-8';
48 * If TRUE, will show the key/location of labels in the backend.
52 public $debugKey = false;
55 * Can contain labels and image references from the backend modules.
56 * Relies on \TYPO3\CMS\Backend\Module\ModuleLoader to initialize modules after a global instance of $LANG has been created.
60 public $moduleLabels = array();
63 * Internal cache for read LL-files
67 public $LL_files_cache = array();
70 * Internal cache for ll-labels (filled as labels are requested)
74 public $LL_labels_cache = array();
77 * instance of the "\TYPO3\CMS\Core\Charset\CharsetConverter" class. May be used by any application.
79 * @var \TYPO3\CMS\Core\Charset\CharsetConverter
84 * instance of the parser factory
86 * @var LocalizationFactory
88 public $parserFactory;
91 * List of language dependencies for actual language. This is used for local variants of a language
92 * that depend on their "main" language, like Brazilian Portuguese or Canadian French.
96 protected $languageDependencies = array();
99 * Initializes the backend language.
100 * This is for example done in \TYPO3\CMS\Backend\Template\DocumentTemplate with lines like these:
101 * $LANG = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Lang\LanguageService::class);
102 * $LANG->init($GLOBALS['BE_USER']->uc['lang']);
104 * @throws \RuntimeException
105 * @param string $lang The language key (two character string from backend users profile)
108 public function init($lang)
110 // Initialize the conversion object:
111 $this->csConvObj
= GeneralUtility
::makeInstance(CharsetConverter
::class);
112 // Initialize the parser factory object
113 $this->parserFactory
= GeneralUtility
::makeInstance(LocalizationFactory
::class);
114 // Find the requested language in this list based
115 // on the $lang key being inputted to this function.
116 /** @var $locales \TYPO3\CMS\Core\Localization\Locales */
117 $locales = GeneralUtility
::makeInstance(Locales
::class);
118 // Language is found. Configure it:
119 if (in_array($lang, $locales->getLocales())) {
120 // The current language key
122 $this->languageDependencies
[] = $this->lang
;
123 foreach ($locales->getLocaleDependencies($this->lang
) as $language) {
124 $this->languageDependencies
[] = $language;
127 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['lang']['debug']) {
128 $this->debugKey
= true;
133 * Gets the parser factory.
135 * @return LocalizationFactory
137 public function getParserFactory()
139 return $this->parserFactory
;
143 * Adds labels and image references from the backend modules to the internal moduleLabels array
145 * @param array $arr Array with references to module labels, keys: ['labels']['table'],
146 * @param string $prefix Module name prefix
148 * @see \TYPO3\CMS\Backend\Module\ModuleLoader
150 public function addModuleLabels($arr, $prefix)
152 if (is_array($arr)) {
153 foreach ($arr as $k => $larr) {
154 if (!isset($this->moduleLabels
[$k])) {
155 $this->moduleLabels
[$k] = array();
157 if (is_array($larr)) {
158 foreach ($larr as $l => $v) {
159 $this->moduleLabels
[$k][$prefix . $l] = $v;
167 * Will convert the input strings special chars (all above 127) to entities.
168 * The string is expected to be encoded in UTF-8
169 * This function is used to create strings that can be used in the Click Menu
170 * (Context Sensitive Menus). The reason is that the values that are dynamically
171 * written into the <div> layer is decoded as iso-8859-1 no matter what charset
172 * is used in the document otherwise (only MSIE, Mozilla is OK).
173 * So by converting we by-pass this problem.
175 * @param string $str Input string
176 * @return string Output string
177 * @deprecated since TYPO3 v8, will be removed in TYPO3 v9
179 public function makeEntities($str)
181 GeneralUtility
::logDeprecatedFunction();
182 // Convert string back again, but using the full entity conversion:
183 return $this->csConvObj
->utf8_to_entities($str);
187 * Debugs localization key.
189 * @param string $value value to debug
192 public function debugLL($value)
194 return $this->debugKey ?
'[' . $value . ']' : '';
198 * Returns the label with key $index from the globally loaded $LOCAL_LANG array.
199 * Mostly used from modules with only one LOCAL_LANG file loaded into the global space.
201 * @param string $index Label key
202 * @param bool $hsc If set, the return value is htmlspecialchar'ed
205 public function getLL($index, $hsc = false)
207 return $this->getLLL($index, $GLOBALS['LOCAL_LANG'], $hsc);
211 * Returns the label with key $index from the $LOCAL_LANG array used as the second argument
213 * @param string $index Label key
214 * @param array $localLanguage $LOCAL_LANG array to get label key from
215 * @param bool $hsc If set, the return value is htmlspecialchar'ed
218 public function getLLL($index, $localLanguage, $hsc = false)
220 // Get Local Language. Special handling for all extensions that
221 // read PHP LL files and pass arrays here directly.
222 if (isset($localLanguage[$this->lang
][$index])) {
223 $value = is_string($localLanguage[$this->lang
][$index])
224 ?
$localLanguage[$this->lang
][$index]
225 : $localLanguage[$this->lang
][$index][0]['target'];
226 } elseif (isset($localLanguage['default'][$index])) {
227 $value = is_string($localLanguage['default'][$index])
228 ?
$localLanguage['default'][$index]
229 : $localLanguage['default'][$index][0]['target'];
234 $value = htmlspecialchars($value);
236 return $value . $this->debugLL($index);
240 * splitLabel function
242 * All translations are based on $LOCAL_LANG variables.
243 * 'language-splitted' labels can therefore refer to a local-lang file + index.
244 * Refer to 'Inside TYPO3' for more details
246 * @param string $input Label key/reference
247 * @param bool $hsc If set, the return value is htmlspecialchar'ed
250 public function sL($input, $hsc = false)
252 $identifier = $input . '_' . (int)$hsc . '_' . (int)$this->debugKey
;
253 if (isset($this->LL_labels_cache
[$this->lang
][$identifier])) {
254 return $this->LL_labels_cache
[$this->lang
][$identifier];
256 if (strpos($input, 'LLL:') === 0) {
257 $restStr = trim(substr($input, 4));
259 // ll-file referred to is found in an extension.
260 if (strpos($restStr, 'EXT:') === 0) {
261 $restStr = trim(substr($restStr, 4));
264 $parts = explode(':', $restStr);
265 $parts[0] = $extPrfx . $parts[0];
266 // Getting data if not cached
267 if (!isset($this->LL_files_cache
[$parts[0]])) {
268 $this->LL_files_cache
[$parts[0]] = $this->readLLfile($parts[0]);
270 $output = $this->getLLL($parts[1], $this->LL_files_cache
[$parts[0]]);
272 // Use a constant non-localizable label
276 $output = htmlspecialchars($output, ENT_COMPAT
, 'UTF-8', false);
278 $output .= $this->debugLL($input);
279 $this->LL_labels_cache
[$this->lang
][$identifier] = $output;
284 * Loading $TCA_DESCR[$table]['columns'] with content from locallang files
285 * as defined in $TCA_DESCR[$table]['refs']
286 * $TCA_DESCR is a global var
288 * @param string $table Table name found as key in global array $TCA_DESCR
291 public function loadSingleTableDescription($table)
293 // First the 'table' cannot already be loaded in [columns]
294 // and secondly there must be a references to locallang files available in [refs]
295 if (is_array($GLOBALS['TCA_DESCR'][$table]) && !isset($GLOBALS['TCA_DESCR'][$table]['columns']) && is_array($GLOBALS['TCA_DESCR'][$table]['refs'])) {
296 // Init $TCA_DESCR for $table-key
297 $GLOBALS['TCA_DESCR'][$table]['columns'] = array();
298 // Get local-lang for each file in $TCA_DESCR[$table]['refs'] as they are ordered.
299 foreach ($GLOBALS['TCA_DESCR'][$table]['refs'] as $llfile) {
300 $localLanguage = $this->includeLLFile($llfile, 0, 1);
302 if (is_array($localLanguage['default'])) {
303 foreach ($localLanguage['default'] as $lkey => $lVal) {
306 // n+1 => type from (alttitle, description, details, syntax, image_descr,image,seeAlso),
307 // n+2 => special instruction, if any
308 $keyParts = explode('.', $lkey);
309 $keyPartsCount = count($keyParts);
310 // Check if last part is special instruction
311 // Only "+" is currently supported
312 $specialInstruction = $keyParts[$keyPartsCount - 1] === '+';
313 if ($specialInstruction) {
314 array_pop($keyParts);
316 // If there are more than 2 parts, get the type from the last part
317 // and merge back the other parts with a dot (.)
318 // Otherwise just get type and field name straightaway
319 if ($keyPartsCount > 2) {
320 $type = array_pop($keyParts);
321 $fieldName = implode('.', $keyParts);
323 $fieldName = $keyParts[0];
324 $type = $keyParts[1];
326 // Detecting 'hidden' labels, converting to normal fieldname
327 if ($fieldName === '_') {
330 if ($fieldName !== '' && $fieldName[0] === '_') {
331 $fieldName = substr($fieldName, 1);
334 $label = $lVal[0]['target'] ?
:
336 if ($specialInstruction) {
337 $GLOBALS['TCA_DESCR'][$table]['columns'][$fieldName][$type] .= LF
. $label;
340 $GLOBALS['TCA_DESCR'][$table]['columns'][$fieldName][$type] = $label;
349 * Includes locallang file (and possibly additional localized version if configured for)
350 * Read language labels will be merged with $LOCAL_LANG (if $setGlobal = TRUE).
352 * @param string $fileRef $fileRef is a file-reference
353 * @param bool $setGlobal Setting in global variable $LOCAL_LANG (or returning the variable)
354 * @param bool $mergeLocalOntoDefault
355 * @return mixed if $setGlobal===TRUE, LL-files set $LOCAL_LANG in global scope, or array is returned from function
357 public function includeLLFile($fileRef, $setGlobal = true, $mergeLocalOntoDefault = false)
359 $globalLanguage = array();
361 $localLanguage = $this->readLLfile($fileRef);
362 if (is_array($localLanguage) && !empty($localLanguage)) {
363 // it depends on, whether we should return the result or set it in the global $LOCAL_LANG array
365 $globalLanguage = (array)$GLOBALS['LOCAL_LANG'];
366 ArrayUtility
::mergeRecursiveWithOverrule($globalLanguage, $localLanguage);
368 $globalLanguage = $localLanguage;
370 // Merge local onto default
371 if ($mergeLocalOntoDefault && $this->lang
!== 'default' && is_array($globalLanguage[$this->lang
]) && is_array($globalLanguage['default'])) {
372 // array_merge can be used so far the keys are not
373 // numeric - which we assume they are not...
374 $globalLanguage['default'] = array_merge($globalLanguage['default'], $globalLanguage[$this->lang
]);
375 unset($globalLanguage[$this->lang
]);
378 // Return value if not global is set.
380 return $globalLanguage;
382 $GLOBALS['LOCAL_LANG'] = $globalLanguage;
388 * Includes a locallang file and returns the $LOCAL_LANG array found inside.
390 * @param string $fileRef Input is a file-reference to be a 'local_lang' file containing a $LOCAL_LANG array
391 * @return array value of $LOCAL_LANG found in the included file, empty if non found
393 protected function readLLfile($fileRef)
395 // @todo: Usually, an instance of the LocalizationFactory is found in $this->parserFactory.
396 // @todo: This is not the case if $GLOBALS['LANG'] is not used to get hold of this object,
397 // @todo: but the objectManager instead. If then init() is not called, this will fatal ...
398 // @todo: To be sure, we always create an instance here for now.
399 /** @var $languageFactory LocalizationFactory */
400 $languageFactory = GeneralUtility
::makeInstance(LocalizationFactory
::class);
402 if ($this->lang
!== 'default') {
403 $languages = array_reverse($this->languageDependencies
);
405 $languages = array('default');
407 $localLanguage = array();
408 foreach ($languages as $language) {
409 $tempLL = $languageFactory->getParsedData($fileRef, $language, $this->charSet
);
410 $localLanguage['default'] = $tempLL['default'];
411 if (!isset($localLanguage[$this->lang
])) {
412 $localLanguage[$this->lang
] = $localLanguage['default'];
414 if ($this->lang
!== 'default' && isset($tempLL[$language])) {
415 // Merge current language labels onto labels from previous language
416 // This way we have a labels with fall back applied
417 ArrayUtility
::mergeRecursiveWithOverrule($localLanguage[$this->lang
], $tempLL[$language], true, false);
420 return $localLanguage;
426 * @param string $index
427 * @param string $value
428 * @param bool $overrideDefault Overrides default language
430 * @deprecated since TYPO3 v8, will be removed in TYPO3 v9
432 public function overrideLL($index, $value, $overrideDefault = true)
434 GeneralUtility
::logDeprecatedFunction();
435 if (!isset($GLOBALS['LOCAL_LANG'])) {
436 $GLOBALS['LOCAL_LANG'] = array();
438 $GLOBALS['LOCAL_LANG'][$this->lang
][$index][0]['target'] = $value;
439 if ($overrideDefault) {
440 $GLOBALS['LOCAL_LANG']['default'][$index][0]['target'] = $value;
445 * Gets labels with a specific fetched from the current locallang file.
446 * This is useful for e.g gathering javascript labels.
448 * @param string $prefix Prefix to select the correct labels
449 * @param string $strip Sub-prefix to be removed from label names in the result
450 * @return array Processed labels
452 public function getLabelsWithPrefix($prefix, $strip = '')
454 $extraction = array();
455 $labels = array_merge((array)$GLOBALS['LOCAL_LANG']['default'], (array)$GLOBALS['LOCAL_LANG'][$this->lang
]);
456 // Regular expression to strip the selection prefix and possibly something from the label name:
457 $labelPattern = '#^' . preg_quote($prefix, '#') . '(' . preg_quote($strip, '#') . ')?#';
458 // Iterate through all locallang labels:
459 foreach ($labels as $label => $value) {
460 if (strpos($label, $prefix) === 0) {
461 $key = preg_replace($labelPattern, '', $label);
462 $extraction[$key] = $value;