1C-Bitrix 25.700.0
Загрузка...
Поиск...
Не найдено
loc.php
См. документацию.
1<?php
2
3namespace Bitrix\Main\Localization;
4
5use Bitrix\Main;
6use Bitrix\Main\IO\Path;
7use Bitrix\Main\Context;
8use Bitrix\Main\Config\Configuration;
9use Bitrix\Main\Text\Encoding;
10
11final class Loc
12{
13 private static $currentLang = null;
14 private static $messages = [];
15 private static $customMessages = [];
16 private static $userMessages = [];
17 private static $includedFiles = [];
18 private static $lazyLoadFiles = [];
19 private static $triedFiles = [];
20
30 public static function getMessage($code, $replace = null, $language = null)
31 {
32 if ($language === null)
33 {
34 //function call optimization
35 if (self::$currentLang === null)
36 {
37 $language = self::getCurrentLang();
38 }
39 else
40 {
41 $language = self::$currentLang;
42 }
43 }
44
45 if (!isset(self::$messages[$language][$code]))
46 {
47 self::loadLazy($code, $language);
48 }
49
50 $s = self::$messages[$language][$code] ?? null;
51
52 if (is_array($replace) && $s !== null)
53 {
54 $s = strtr($s, $replace);
55 }
56
57 return $s;
58 }
59
65 public static function loadMessages($file)
66 {
67 if (($realPath = realpath($file)) !== false)
68 {
69 $file = $realPath;
70 }
71 $file = Path::normalize($file);
72
73 self::$lazyLoadFiles[$file] = $file;
74 }
75
79 public static function getCurrentLang()
80 {
81 if (self::$currentLang === null)
82 {
83 $context = Context::getCurrent();
84 if ($context !== null)
85 {
86 $language = $context->getLanguage();
87 if ($language !== null)
88 {
89 self::$currentLang = $language;
90 }
91 }
92 }
93 return (self::$currentLang !== null ? self::$currentLang : 'en');
94 }
95
96 public static function setCurrentLang($language)
97 {
98 self::$currentLang = $language;
99 }
100
110 private static function includeLangFiles($file, $language, &$loadedLangFile)
111 {
112 static $langDirCache = [];
113
114 // open_basedir restriction
115 static $openBasedir = [], $openBasedirRestriction;
116 if ($openBasedirRestriction === null)
117 {
118 $openBasedirTmp = ini_get('open_basedir');
119 if (!empty($openBasedirTmp))
120 {
121 // multiple paths split by colon ":" - "/home/bitrix:/var/www/html"
122 // under non windows by semicolon ";" - "c:/www/;c:/www/html"
123 $openBasedirTmp = explode(
124 (strncasecmp(PHP_OS, 'WIN', 3) == 0 ? ';' : ':'),
125 $openBasedirTmp
126 );
127 foreach ($openBasedirTmp as $testDir)
128 {
129 if (!empty($testDir))
130 {
131 $testDir = Path::normalize($testDir);
132 if (is_dir($testDir))
133 {
134 $openBasedir[] = $testDir;
135 }
136 }
137 }
138 }
139 $openBasedirRestriction = !empty($openBasedir);
140 }
141
142 $path = Path::getDirectory($file);
143
144 if (isset($langDirCache[$path]))
145 {
146 $langDir = $langDirCache[$path];
147 $fileName = mb_substr($file, (mb_strlen($langDir) - 5));
148 }
149 else
150 {
151 //let's find language folder
152 $langDir = $fileName = '';
153 $filePath = $file;
154 while (($slashPos = mb_strrpos($filePath, '/')) !== false)
155 {
156 $filePath = mb_substr($filePath, 0, $slashPos);
157 if ($openBasedirRestriction === true)
158 {
159 $withinOpenBasedir = false;
160 foreach ($openBasedir as $testDir)
161 {
162 if (stripos($filePath, $testDir) === 0)
163 {
164 $withinOpenBasedir = true;
165 break;
166 }
167 }
168 if (!$withinOpenBasedir)
169 {
170 break;
171 }
172 }
173 $langPath = $filePath . '/lang';
174 if (is_dir($langPath))
175 {
176 $langDir = $langPath;
177 $fileName = mb_substr($file, $slashPos);
178 $langDirCache[$path] = $langDir;
179 break;
180 }
181 }
182 }
183
184 $mess = [];
185 if ($langDir <> '')
186 {
187 //load messages for default lang first
188 $defaultLang = self::getDefaultLang($language);
189 if ($defaultLang <> $language)
190 {
191 $langFile = $langDir . '/' . $defaultLang . $fileName;
192
194
195 if (file_exists($langFile))
196 {
197 $mess = self::includeFile($langFile);
198 $loadedLangFile = $langFile;
199 }
200 }
201
202 //then load messages for specified lang
203 $langFile = $langDir . '/' . $language . $fileName;
204
206
207 if (file_exists($langFile))
208 {
209 $mess = array_merge($mess, self::includeFile($langFile));
210 $loadedLangFile = $langFile;
211 }
212 }
213
214 return $mess;
215 }
216
225 public static function loadLanguageFile($file, $language = null, $normalize = true)
226 {
227 if ($language === null)
228 {
229 $language = self::getCurrentLang();
230 }
231
232 if ($normalize)
233 {
234 $file = Path::normalize($file);
235 }
236
237 if (!isset(self::$messages[$language]))
238 {
239 self::$messages[$language] = [];
240 }
241
242 //first time call only for lang
243 if (!isset(self::$userMessages[$language]))
244 {
245 self::$userMessages[$language] = self::loadUserMessages($language);
246 }
247
248 //let's find language folder and include lang files
249 $mess = self::includeLangFiles($file, $language, $langFile);
250
251 if (!empty($mess))
252 {
253 [$convertEncoding, $targetEncoding, $sourceEncoding] = Translation::getEncodings($language, $langFile);
254
255 foreach ($mess as $key => $val)
256 {
257 if (isset(self::$customMessages[$language][$key]))
258 {
259 self::$messages[$language][$key] = $mess[$key] = self::$customMessages[$language][$key];
260 }
261 else
262 {
263 if ($convertEncoding)
264 {
265 if ($targetEncoding !== 'utf-8' || !preg_match('//u', $val))
266 {
267 $val = Encoding::convertEncoding($val, $sourceEncoding, $targetEncoding);
268 }
269 $mess[$key] = $val;
270 }
271
272 self::$messages[$language][$key] = $val;
273 }
274 }
275 }
276
277 return $mess;
278 }
279
286 public static function loadCustomMessages($file, $language = null)
287 {
288 if ($language === null)
289 {
290 $language = self::getCurrentLang();
291 }
292
293 if (!isset(self::$customMessages[$language]))
294 {
295 self::$customMessages[$language] = [];
296 }
297
298 //let's find language folder and include lang files
299 $mess = self::includeLangFiles(Path::normalize($file), $language, $langFile);
300
301 if (!empty($mess))
302 {
303 [$convertEncoding, $targetEncoding, $sourceEncoding] = Translation::getEncodings($language, $langFile);
304
305 foreach ($mess as $key => $val)
306 {
307 if ($convertEncoding)
308 {
309 if ($targetEncoding !== 'utf-8' || !preg_match('//u', $val))
310 {
311 $val = Encoding::convertEncoding($val, $sourceEncoding, $targetEncoding);
312 }
313 $mess[$key] = $val;
314 }
315
316 self::$customMessages[$language][$key] = $val;
317 }
318 }
319 }
320
321 private static function loadLazy($code, $language)
322 {
323 if ($code == '')
324 {
325 return;
326 }
327
328 //control of duplicates
329 if (!isset(self::$triedFiles[$language]))
330 {
331 self::$triedFiles[$language] = [];
332 }
333
334 $trace = Main\Diag\Helper::getBackTrace(4, DEBUG_BACKTRACE_IGNORE_ARGS);
335
336 $currentFile = null;
337 for ($i = 3; $i >= 1; $i--)
338 {
339 if (isset($trace[$i]) && stripos($trace[$i]["function"], "GetMessage") === 0)
340 {
341 $currentFile = Path::normalize($trace[$i]["file"]);
342
343 //we suppose there is a language file even if it wasn't registered via loadMessages()
344 self::$lazyLoadFiles[$currentFile] = $currentFile;
345 break;
346 }
347 }
348
349 if ($currentFile !== null && isset(self::$lazyLoadFiles[$currentFile]))
350 {
351 //in most cases we know the file containing the "code" - load it directly
352 if (!isset(self::$triedFiles[$language][$currentFile]))
353 {
354 self::loadLanguageFile($currentFile, $language, false);
355 self::$triedFiles[$language][$currentFile] = true;
356 }
357 unset(self::$lazyLoadFiles[$currentFile]);
358 }
359
360 if (!isset(self::$messages[$language][$code]))
361 {
362 //we still don't know which file contains the "code" - go through the files in the reverse order
363 $unset = [];
364 if (($file = end(self::$lazyLoadFiles)) !== false)
365 {
366 do
367 {
368 if (!isset(self::$triedFiles[$language][$file]))
369 {
370 self::loadLanguageFile($file, $language, false);
371 self::$triedFiles[$language][$file] = true;
372 }
373
374 $unset[] = $file;
375 if (isset(self::$messages[$language][$code]))
376 {
377 if (defined("BX_MESS_LOG") && $currentFile !== null)
378 {
379 file_put_contents(BX_MESS_LOG, 'CTranslateUtils::CopyMessage("' . $code . '", "' . $file . '", "' . $currentFile . '");' . "\n", FILE_APPEND);
380 }
381 break;
382 }
383 }
384 while (($file = prev(self::$lazyLoadFiles)) !== false);
385 }
386 foreach ($unset as $file)
387 {
388 unset(self::$lazyLoadFiles[$file]);
389 }
390 }
391
392 if (!isset(self::$messages[$language][$code]) && defined("BX_MESS_LOG"))
393 {
394 file_put_contents(BX_MESS_LOG, $code . ": not found for " . $currentFile . "\n", FILE_APPEND);
395 }
396 }
397
404 private static function loadUserMessages($lang)
405 {
406 $userMess = [];
407 $documentRoot = Main\Application::getDocumentRoot();
408 if (($fname = Main\Loader::getLocal("php_interface/user_lang/" . $lang . "/lang.php", $documentRoot)) !== false)
409 {
410 $mess = self::includeFile($fname);
411
412 // typical call is Loc::loadMessages(__FILE__)
413 // __FILE__ can differ from path used in the user file
414 foreach ($mess as $key => $val)
415 {
416 $userMess[str_replace("\\", "/", realpath($documentRoot . $key))] = $val;
417 }
418 }
419 return $userMess;
420 }
421
428 private static function includeFile($path)
429 {
430 self::$includedFiles[$path] = $path;
431
432 //the name $MESS is predefined in language files
433 $MESS = [];
434 include($path);
435
436 //redefine messages from user lang file
437 if (!empty(self::$userMessages))
438 {
439 $path = str_replace("\\", "/", realpath($path));
440
441 //cycle through languages
442 foreach (self::$userMessages as $messages)
443 {
444 if (isset($messages[$path]) && is_array($messages[$path]))
445 {
446 foreach ($messages[$path] as $key => $val)
447 {
448 $MESS[$key] = $val;
449 }
450 }
451 }
452 }
453
454 return $MESS;
455 }
456
463 public static function getDefaultLang($lang)
464 {
465 static $subst = ['ua' => 'en', 'kz' => 'ru', 'by' => 'ru', 'ru' => 'ru', 'en' => 'en', 'de' => 'en'];
466 if (isset($subst[$lang]))
467 {
468 return $subst[$lang];
469 }
470
471 $options = Configuration::getValue("default_language");
472 if (isset($options[$lang]))
473 {
474 return $options[$lang];
475 }
476
477 return 'en';
478 }
479
484 public static function getIncludedFiles()
485 {
486 return self::$includedFiles;
487 }
488
498 public static function getMessagePlural(string $code, int $value, ?array $replace = null, ?string $language = null): ?string
499 {
500 $result = self::getMessage($code . '_PLURAL_' . self::getPluralForm($value, $language), $replace);
501 if ($result === null)
502 {
503 $result = self::getMessage($code . '_PLURAL_1', $replace);
504 }
505
506 return $result;
507 }
508
516 public static function getPluralForm($value, $language = null): int
517 {
518 $value = (int)$value;
519 if ($language === null)
520 {
521 $language = self::getCurrentLang();
522 }
523
524 if ($value < 0)
525 {
526 $value = (-1) * $value;
527 }
528
529 switch ($language)
530 {
531 case 'ar':
532 if ($value == 0)
533 {
534 $pluralForm = 0;
535 }
536 elseif ($value == 1 || $value % 100 == 0)
537 {
538 $pluralForm = 1;
539 }
540 elseif ($value == 2)
541 {
542 $pluralForm = 2;
543 }
544 elseif (($value % 100) >= 3 && ($value % 100) <= 10)
545 {
546 $pluralForm = 3;
547 }
548 else
549 {
550 $pluralForm = 4;
551 }
552 break;
553
554 case 'br':
555 case 'fr':
556 case 'tr':
557 $pluralForm = (($value > 1) ? 1 : 0);
558 break;
559
560 case 'de':
561 case 'en':
562 case 'hi':
563 case 'it':
564 case 'la':
565 $pluralForm = (($value !== 1) ? 1 : 0);
566 break;
567
568 case 'ru':
569 case 'ua':
570 if (
571 ($value % 10 === 1)
572 && ($value % 100 !== 11)
573 )
574 {
575 $pluralForm = 0;
576 }
577 elseif (
578 ($value % 10 >= 2)
579 && ($value % 10 <= 4)
580 && (
581 ($value % 100 < 10)
582 || ($value % 100 >= 20)
583 )
584 )
585 {
586 $pluralForm = 1;
587 }
588 else
589 {
590 $pluralForm = 2;
591 }
592 break;
593
594 case 'pl':
595 if ($value === 1)
596 {
597 $pluralForm = 0;
598 }
599 elseif (
600 $value % 10 >= 2
601 && $value % 10 <= 4
602 && (
603 $value % 100 < 10
604 || $value % 100 >= 20
605 )
606 )
607 {
608 $pluralForm = 1;
609 }
610 else
611 {
612 $pluralForm = 2;
613 }
614 break;
615
616 case 'id':
617 case 'ja':
618 case 'ms':
619 case 'sc':
620 case 'tc':
621 case 'th':
622 case 'vn':
623 $pluralForm = 0;
624 break;
625
626 default:
627 $pluralForm = 1;
628 break;
629 }
630
631 return $pluralForm;
632 }
633}
$path
Определения access_edit.php:21
static getBackTrace($limit=0, $options=null, $skip=1)
Определения helper.php:26
static getLocal($path, $root=null)
Определения loader.php:572
static loadLanguageFile($file, $language=null, $normalize=true)
Определения loc.php:225
static getCurrentLang()
Определения loc.php:79
static getMessagePlural(string $code, int $value, ?array $replace=null, ?string $language=null)
Определения loc.php:498
static loadCustomMessages($file, $language=null)
Определения loc.php:286
static getIncludedFiles()
Определения loc.php:484
static getPluralForm($value, $language=null)
Определения loc.php:516
static loadMessages($file)
Определения loc.php:65
static setCurrentLang($language)
Определения loc.php:96
static getDefaultLang($lang)
Определения loc.php:463
static getMessage($code, $replace=null, $language=null)
Определения loc.php:30
static convertLangPath($langFile, $language)
Определения translation.php:267
static getEncodings($language, $langFile)
Определения translation.php:554
$options
Определения commerceml2.php:49
$langFile
Определения .description.php:2
</td ></tr ></table ></td ></tr >< tr >< td class="bx-popup-label bx-width30"><?=GetMessage("PAGE_NEW_TAGS")?> array( $site)
Определения file_new.php:804
$result
Определения get_property_values.php:14
global $MESS
Определения bill.php:2
$context
Определения csv_new_setup.php:223
if(!is_null($config))($config as $configItem)(! $configItem->isVisible()) $code
Определения options.php:195
foreach(['Bitrix\\Main'=> '/lib', 'Psr\\Container'=> '/vendor/psr/container/src', 'Psr\\Log'=> '/vendor/psr/log/src', 'Psr\\Http\\Message'=> '/vendor/psr/http-message/src', 'Psr\\Http\\Client'=> '/vendor/psr/http-client/src', 'Http\\Promise'=> '/vendor/php-http/promise/src', 'PHPMailer\\PHPMailer'=> '/vendor/phpmailer/phpmailer/src', 'GeoIp2'=> '/vendor/geoip2/geoip2/src', 'MaxMind\\Db'=> '/vendor/maxmind-db/reader/src/MaxMind/Db', 'PhpParser'=> '/vendor/nikic/php-parser/lib/PhpParser', 'Recurr'=> '/vendor/simshaun/recurr/src/Recurr',] as $namespace=> $namespacePath) $documentRoot
Определения autoload.php:27
if(!defined('SITE_ID')) $lang
Определения include.php:91
if( $daysToExpire >=0 &&$daysToExpire< 60 elseif)( $daysToExpire< 0)
Определения prolog_main_admin.php:393
$fileName
Определения quickway.php:305
if(empty($signedUserToken)) $key
Определения quickway.php:257
$i
Определения factura.php:643
$val
Определения options.php:1793