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