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\Localization\LocalizationFactory
;
18 use TYPO3\CMS\Core\Utility\ArrayUtility
;
19 use TYPO3\CMS\Core\Utility\GeneralUtility
;
22 * Contains the TYPO3 Backend Language class
23 * For detailed information about how localization is handled,
24 * please refer to the 'Inside TYPO3' document which describes this.
25 * This class is normally instantiated as the global variable $GLOBALS['LANG']
26 * It's only available in the backend and under certain circumstances in the frontend
27 * @see \TYPO3\CMS\Backend\Template\DocumentTemplate
32 * This is set to the language that is currently running for the user
36 public $lang = 'default';
39 * Default charset in backend
43 public $charSet = 'utf-8';
46 * If TRUE, will show the key/location of labels in the backend.
50 public $debugKey = false;
53 * Can contain labels and image references from the backend modules.
54 * Relies on \TYPO3\CMS\Backend\Module\ModuleLoader to initialize modules after a global instance of $LANG has been created.
58 public $moduleLabels = array();
61 * Internal cache for read LL-files
65 public $LL_files_cache = array();
68 * Internal cache for ll-labels (filled as labels are requested)
72 public $LL_labels_cache = array();
75 * instance of the "\TYPO3\CMS\Core\Charset\CharsetConverter" class. May be used by any application.
77 * @var \TYPO3\CMS\Core\Charset\CharsetConverter
82 * instance of the parser factory
84 * @var LocalizationFactory
86 public $parserFactory;
89 * List of language dependencies for actual language. This is used for local variants of a language
90 * that depend on their "main" language, like Brazilian Portuguese or Canadian French.
94 protected $languageDependencies = array();
97 * Initializes the backend language.
98 * This is for example done in \TYPO3\CMS\Backend\Template\DocumentTemplate with lines like these:
99 * $LANG = \TYPO3\CMS\Core\Utility\GeneralUtility::makeInstance(\TYPO3\CMS\Lang\LanguageService::class);
100 * $LANG->init($GLOBALS['BE_USER']->uc['lang']);
102 * @throws \RuntimeException
103 * @param string $lang The language key (two character string from backend users profile)
106 public function init($lang)
108 // Initialize the conversion object:
109 $this->csConvObj
= GeneralUtility
::makeInstance(\TYPO3\CMS\Core\Charset\CharsetConverter
::class);
110 // Initialize the parser factory object
111 $this->parserFactory
= GeneralUtility
::makeInstance(LocalizationFactory
::class);
112 // Find the requested language in this list based
113 // on the $lang key being inputted to this function.
114 /** @var $locales \TYPO3\CMS\Core\Localization\Locales */
115 $locales = GeneralUtility
::makeInstance(\TYPO3\CMS\Core\Localization\Locales
::class);
116 // Language is found. Configure it:
117 if (in_array($lang, $locales->getLocales())) {
118 // The current language key
120 $this->languageDependencies
[] = $this->lang
;
121 foreach ($locales->getLocaleDependencies($this->lang
) as $language) {
122 $this->languageDependencies
[] = $language;
125 if ($GLOBALS['TYPO3_CONF_VARS']['BE']['lang']['debug']) {
126 $this->debugKey
= true;
131 * Gets the parser factory.
133 * @return LocalizationFactory
135 public function getParserFactory()
137 return $this->parserFactory
;
141 * Adds labels and image references from the backend modules to the internal moduleLabels array
143 * @param array $arr Array with references to module labels, keys: ['labels']['table'],
144 * @param string $prefix Module name prefix
146 * @see \TYPO3\CMS\Backend\Module\ModuleLoader
148 public function addModuleLabels($arr, $prefix)
150 if (is_array($arr)) {
151 foreach ($arr as $k => $larr) {
152 if (!isset($this->moduleLabels
[$k])) {
153 $this->moduleLabels
[$k] = array();
155 if (is_array($larr)) {
156 foreach ($larr as $l => $v) {
157 $this->moduleLabels
[$k][$prefix . $l] = $v;
165 * Will convert the input strings special chars (all above 127) to entities.
166 * The string is expected to be encoded in UTF-8
167 * This function is used to create strings that can be used in the Click Menu
168 * (Context Sensitive Menus). The reason is that the values that are dynamically
169 * written into the <div> layer is decoded as iso-8859-1 no matter what charset
170 * is used in the document otherwise (only MSIE, Mozilla is OK).
171 * So by converting we by-pass this problem.
173 * @param string $str Input string
174 * @return string Output string
175 * @deprecated since TYPO3 CMS 8, will be removed in TYPO3 CMS 9
177 public function makeEntities($str)
179 GeneralUtility
::logDeprecatedFunction();
180 // Convert string back again, but using the full entity conversion:
181 return $this->csConvObj
->utf8_to_entities($str);
185 * Debugs localization key.
187 * @param string $value value to debug
190 public function debugLL($value)
192 return $this->debugKey ?
'[' . $value . ']' : '';
196 * Returns the label with key $index from the globally loaded $LOCAL_LANG array.
197 * Mostly used from modules with only one LOCAL_LANG file loaded into the global space.
199 * @param string $index Label key
200 * @param bool $hsc If set, the return value is htmlspecialchar'ed
203 public function getLL($index, $hsc = false)
205 return $this->getLLL($index, $GLOBALS['LOCAL_LANG'], $hsc);
209 * Returns the label with key $index from the $LOCAL_LANG array used as the second argument
211 * @param string $index Label key
212 * @param array $localLanguage $LOCAL_LANG array to get label key from
213 * @param bool $hsc If set, the return value is htmlspecialchar'ed
216 public function getLLL($index, $localLanguage, $hsc = false)
218 // Get Local Language. Special handling for all extensions that
219 // read PHP LL files and pass arrays here directly.
220 if (isset($localLanguage[$this->lang
][$index])) {
221 $value = is_string($localLanguage[$this->lang
][$index])
222 ?
$localLanguage[$this->lang
][$index]
223 : $localLanguage[$this->lang
][$index][0]['target'];
224 } elseif (isset($localLanguage['default'][$index])) {
225 $value = is_string($localLanguage['default'][$index])
226 ?
$localLanguage['default'][$index]
227 : $localLanguage['default'][$index][0]['target'];
232 $value = htmlspecialchars($value);
234 return $value . $this->debugLL($index);
238 * splitLabel function
240 * All translations are based on $LOCAL_LANG variables.
241 * 'language-splitted' labels can therefore refer to a local-lang file + index.
242 * Refer to 'Inside TYPO3' for more details
244 * @param string $input Label key/reference
245 * @param bool $hsc If set, the return value is htmlspecialchar'ed
248 public function sL($input, $hsc = false)
250 $identifier = $input . '_' . (int)$hsc . '_' . (int)$this->debugKey
;
251 if (isset($this->LL_labels_cache
[$this->lang
][$identifier])) {
252 return $this->LL_labels_cache
[$this->lang
][$identifier];
254 if (strpos($input, 'LLL:') === 0) {
255 $restStr = trim(substr($input, 4));
257 // ll-file referred to is found in an extension.
258 if (strpos($restStr, 'EXT:') === 0) {
259 $restStr = trim(substr($restStr, 4));
262 $parts = explode(':', $restStr);
263 $parts[0] = $extPrfx . $parts[0];
264 // Getting data if not cached
265 if (!isset($this->LL_files_cache
[$parts[0]])) {
266 $this->LL_files_cache
[$parts[0]] = $this->readLLfile($parts[0]);
268 $output = $this->getLLL($parts[1], $this->LL_files_cache
[$parts[0]]);
270 // Use a constant non-localizable label
274 $output = htmlspecialchars($output, ENT_COMPAT
, 'UTF-8', false);
276 $output .= $this->debugLL($input);
277 $this->LL_labels_cache
[$this->lang
][$identifier] = $output;
282 * Loading $TCA_DESCR[$table]['columns'] with content from locallang files
283 * as defined in $TCA_DESCR[$table]['refs']
284 * $TCA_DESCR is a global var
286 * @param string $table Table name found as key in global array $TCA_DESCR
289 public function loadSingleTableDescription($table)
291 // First the 'table' cannot already be loaded in [columns]
292 // and secondly there must be a references to locallang files available in [refs]
293 if (is_array($GLOBALS['TCA_DESCR'][$table]) && !isset($GLOBALS['TCA_DESCR'][$table]['columns']) && is_array($GLOBALS['TCA_DESCR'][$table]['refs'])) {
294 // Init $TCA_DESCR for $table-key
295 $GLOBALS['TCA_DESCR'][$table]['columns'] = array();
296 // Get local-lang for each file in $TCA_DESCR[$table]['refs'] as they are ordered.
297 foreach ($GLOBALS['TCA_DESCR'][$table]['refs'] as $llfile) {
298 $localLanguage = $this->includeLLFile($llfile, 0, 1);
300 if (is_array($localLanguage['default'])) {
301 foreach ($localLanguage['default'] as $lkey => $lVal) {
304 // n+1 => type from (alttitle, description, details, syntax, image_descr,image,seeAlso),
305 // n+2 => special instruction, if any
306 $keyParts = explode('.', $lkey);
307 $keyPartsCount = count($keyParts);
308 // Check if last part is special instruction
309 // Only "+" is currently supported
310 $specialInstruction = $keyParts[$keyPartsCount - 1] === '+';
311 if ($specialInstruction) {
312 array_pop($keyParts);
314 // If there are more than 2 parts, get the type from the last part
315 // and merge back the other parts with a dot (.)
316 // Otherwise just get type and field name straightaway
317 if ($keyPartsCount > 2) {
318 $type = array_pop($keyParts);
319 $fieldName = implode('.', $keyParts);
321 $fieldName = $keyParts[0];
322 $type = $keyParts[1];
324 // Detecting 'hidden' labels, converting to normal fieldname
325 if ($fieldName === '_') {
328 if ($fieldName !== '' && $fieldName[0] === '_') {
329 $fieldName = substr($fieldName, 1);
332 $label = $lVal[0]['target'] ?
:
334 if ($specialInstruction) {
335 $GLOBALS['TCA_DESCR'][$table]['columns'][$fieldName][$type] .= LF
. $label;
338 $GLOBALS['TCA_DESCR'][$table]['columns'][$fieldName][$type] = $label;
347 * Includes locallang file (and possibly additional localized version if configured for)
348 * Read language labels will be merged with $LOCAL_LANG (if $setGlobal = TRUE).
350 * @param string $fileRef $fileRef is a file-reference
351 * @param bool $setGlobal Setting in global variable $LOCAL_LANG (or returning the variable)
352 * @param bool $mergeLocalOntoDefault
353 * @return mixed if $setGlobal===TRUE, LL-files set $LOCAL_LANG in global scope, or array is returned from function
355 public function includeLLFile($fileRef, $setGlobal = true, $mergeLocalOntoDefault = false)
357 $globalLanguage = array();
359 $localLanguage = $this->readLLfile($fileRef);
360 if (is_array($localLanguage) && !empty($localLanguage)) {
361 // it depends on, whether we should return the result or set it in the global $LOCAL_LANG array
363 $globalLanguage = (array)$GLOBALS['LOCAL_LANG'];
364 ArrayUtility
::mergeRecursiveWithOverrule($globalLanguage, $localLanguage);
366 $globalLanguage = $localLanguage;
368 // Merge local onto default
369 if ($mergeLocalOntoDefault && $this->lang
!== 'default' && is_array($globalLanguage[$this->lang
]) && is_array($globalLanguage['default'])) {
370 // array_merge can be used so far the keys are not
371 // numeric - which we assume they are not...
372 $globalLanguage['default'] = array_merge($globalLanguage['default'], $globalLanguage[$this->lang
]);
373 unset($globalLanguage[$this->lang
]);
376 // Return value if not global is set.
378 return $globalLanguage;
380 $GLOBALS['LOCAL_LANG'] = $globalLanguage;
386 * Includes a locallang file and returns the $LOCAL_LANG array found inside.
388 * @param string $fileRef Input is a file-reference to be a 'local_lang' file containing a $LOCAL_LANG array
389 * @return array value of $LOCAL_LANG found in the included file, empty if non found
391 protected function readLLfile($fileRef)
393 // @todo: Usually, an instance of the LocalizationFactory is found in $this->parserFactory.
394 // @todo: This is not the case if $GLOBALS['LANG'] is not used to get hold of this object,
395 // @todo: but the objectManager instead. If then init() is not called, this will fatal ...
396 // @todo: To be sure, we always create an instance here for now.
397 /** @var $languageFactory LocalizationFactory */
398 $languageFactory = GeneralUtility
::makeInstance(LocalizationFactory
::class);
400 if ($this->lang
!== 'default') {
401 $languages = array_reverse($this->languageDependencies
);
403 $languages = array('default');
405 $localLanguage = array();
406 foreach ($languages as $language) {
407 $tempLL = $languageFactory->getParsedData($fileRef, $language, $this->charSet
);
408 $localLanguage['default'] = $tempLL['default'];
409 if (!isset($localLanguage[$this->lang
])) {
410 $localLanguage[$this->lang
] = $localLanguage['default'];
412 if ($this->lang
!== 'default' && isset($tempLL[$language])) {
413 // Merge current language labels onto labels from previous language
414 // This way we have a labels with fall back applied
415 ArrayUtility
::mergeRecursiveWithOverrule($localLanguage[$this->lang
], $tempLL[$language], true, false);
418 return $localLanguage;
424 * @param string $index
425 * @param string $value
426 * @param bool $overrideDefault Overrides default language
429 public function overrideLL($index, $value, $overrideDefault = true)
431 if (!isset($GLOBALS['LOCAL_LANG'])) {
432 $GLOBALS['LOCAL_LANG'] = array();
434 $GLOBALS['LOCAL_LANG'][$this->lang
][$index][0]['target'] = $value;
435 if ($overrideDefault) {
436 $GLOBALS['LOCAL_LANG']['default'][$index][0]['target'] = $value;
441 * Gets labels with a specific fetched from the current locallang file.
442 * This is useful for e.g gathering javascript labels.
444 * @param string $prefix Prefix to select the correct labels
445 * @param string $strip Sub-prefix to be removed from label names in the result
446 * @return array Processed labels
448 public function getLabelsWithPrefix($prefix, $strip = '')
450 $extraction = array();
451 $labels = array_merge((array)$GLOBALS['LOCAL_LANG']['default'], (array)$GLOBALS['LOCAL_LANG'][$this->lang
]);
452 // Regular expression to strip the selection prefix and possibly something from the label name:
453 $labelPattern = '#^' . preg_quote($prefix, '#') . '(' . preg_quote($strip, '#') . ')?#';
454 // Iterate through all locallang labels:
455 foreach ($labels as $label => $value) {
456 if (strpos($label, $prefix) === 0) {
457 $key = preg_replace($labelPattern, '', $label);
458 $extraction[$key] = $value;