Bitrix-D7 22.6
 
Загрузка...
Поиск...
Не найдено
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 $val = Encoding::convertEncoding($val, $sourceEncoding, $targetEncoding);
265 $mess[$key] = $val;
266 }
267
268 self::$messages[$language][$key] = $val;
269 }
270 }
271 }
272
273 return $mess;
274 }
275
282 public static function loadCustomMessages($file, $language = null)
283 {
284 if($language === null)
285 {
286 $language = self::getCurrentLang();
287 }
288
289 if(!isset(self::$customMessages[$language]))
290 {
291 self::$customMessages[$language] = array();
292 }
293
294 //let's find language folder and include lang files
295 $mess = self::includeLangFiles(Path::normalize($file), $language, $langFile);
296
297 if (!empty($mess))
298 {
299 [$convertEncoding, $targetEncoding, $sourceEncoding] = Translation::getEncodings($language, $langFile);
300
301 foreach ($mess as $key => $val)
302 {
303 if ($convertEncoding)
304 {
305 $val = $mess[$key] = Encoding::convertEncoding($val, $sourceEncoding, $targetEncoding);
306 }
307
308 self::$customMessages[$language][$key] = $val;
309 }
310 }
311 }
312
313 private static function loadLazy($code, $language)
314 {
315 if($code == '')
316 {
317 return;
318 }
319
320 //control of duplicates
321 if(!isset(self::$triedFiles[$language]))
322 {
323 self::$triedFiles[$language] = [];
324 }
325
326 $trace = Main\Diag\Helper::getBackTrace(4, DEBUG_BACKTRACE_IGNORE_ARGS);
327
328 $currentFile = null;
329 for($i = 3; $i >= 1; $i--)
330 {
331 if (isset($trace[$i]) && stripos($trace[$i]["function"], "GetMessage") === 0)
332 {
333 $currentFile = Path::normalize($trace[$i]["file"]);
334
335 //we suppose there is a language file even if it wasn't registered via loadMessages()
336 self::$lazyLoadFiles[$currentFile] = $currentFile;
337 break;
338 }
339 }
340
341 if($currentFile !== null && isset(self::$lazyLoadFiles[$currentFile]))
342 {
343 //in most cases we know the file containing the "code" - load it directly
344 if(!isset(self::$triedFiles[$language][$currentFile]))
345 {
346 self::loadLanguageFile($currentFile, $language, false);
347 self::$triedFiles[$language][$currentFile] = true;
348 }
349 unset(self::$lazyLoadFiles[$currentFile]);
350 }
351
352 if(!isset(self::$messages[$language][$code]))
353 {
354 //we still don't know which file contains the "code" - go through the files in the reverse order
355 $unset = array();
356 if(($file = end(self::$lazyLoadFiles)) !== false)
357 {
358 do
359 {
360 if(!isset(self::$triedFiles[$language][$file]))
361 {
362 self::loadLanguageFile($file, $language, false);
363 self::$triedFiles[$language][$file] = true;
364 }
365
366 $unset[] = $file;
367 if(isset(self::$messages[$language][$code]))
368 {
369 if(defined("BX_MESS_LOG") && $currentFile !== null)
370 {
371 file_put_contents(BX_MESS_LOG, 'CTranslateUtils::CopyMessage("'.$code.'", "'.$file.'", "'.$currentFile.'");'."\n", FILE_APPEND);
372 }
373 break;
374 }
375 }
376 while(($file = prev(self::$lazyLoadFiles)) !== false);
377 }
378 foreach($unset as $file)
379 {
380 unset(self::$lazyLoadFiles[$file]);
381 }
382 }
383
384 if(!isset(self::$messages[$language][$code]) && defined("BX_MESS_LOG"))
385 {
386 file_put_contents(BX_MESS_LOG, $code.": not found for ".$currentFile."\n", FILE_APPEND);
387 }
388 }
389
396 private static function loadUserMessages($lang)
397 {
398 $userMess = array();
399 $documentRoot = Main\Application::getDocumentRoot();
400 if(($fname = Main\Loader::getLocal("php_interface/user_lang/".$lang."/lang.php", $documentRoot)) !== false)
401 {
402 $mess = self::includeFile($fname);
403
404 // typical call is Loc::loadMessages(__FILE__)
405 // __FILE__ can differ from path used in the user file
406 foreach($mess as $key => $val)
407 $userMess[str_replace("\\", "/", realpath($documentRoot.$key))] = $val;
408 }
409 return $userMess;
410 }
411
418 private static function includeFile($path)
419 {
420 self::$includedFiles[$path] = $path;
421
422 //the name $MESS is predefined in language files
423 $MESS = array();
424 include($path);
425
426 //redefine messages from user lang file
427 if(!empty(self::$userMessages))
428 {
429 $path = str_replace("\\", "/", realpath($path));
430
431 //cycle through languages
432 foreach(self::$userMessages as $messages)
433 {
434 if(isset($messages[$path]) && is_array($messages[$path]))
435 {
436 foreach($messages[$path] as $key => $val)
437 {
438 $MESS[$key] = $val;
439 }
440 }
441 }
442 }
443
444 return $MESS;
445 }
446
453 public static function getDefaultLang($lang)
454 {
455 static $subst = ['ua' => 'en', 'kz' => 'ru', 'by' => 'ru', 'ru' => 'ru', 'en' => 'en', 'de' => 'en'];
456 if(isset($subst[$lang]))
457 {
458 return $subst[$lang];
459 }
460
461 $options = Configuration::getValue("default_language");
462 if(isset($options[$lang]))
463 {
464 return $options[$lang];
465 }
466
467 return 'en';
468 }
469
474 public static function getIncludedFiles()
475 {
476 return self::$includedFiles;
477 }
478
497 public static function getMessagePlural(string $code, int $value, array $replace = null, string $language = null): ?string
498 {
499 $language = (string)$language;
500 if ($language === '')
501 {
502 $language = LANGUAGE_ID;
503 }
504
505 $result = self::getMessage($code . '_PLURAL_' . self::getPluralForm($value, $language), $replace);
506 if ($result === null)
507 {
508 $result = self::getMessage($code . '_PLURAL_1', $replace);
509 }
510
511 return $result;
512 }
513
521 public static function getPluralForm($value, $language = ''): int
522 {
523 $value = (int)$value;
524 $language = (string)$language;
525 if ($language === '')
526 {
527 $language = LANGUAGE_ID;
528 }
529
530 if ($value < 0)
531 {
532 $value = (-1) * $value;
533 }
534
535 switch ($language)
536 {
537 case 'ar':
538 $pluralForm = (($value !== 1) ? 1 : 0);
539/*
540 if ($value === 0)
541 {
542 $pluralForm = 0;
543 }
544 else if ($value === 1)
545 {
546 $pluralForm = 1;
547 }
548 else if ($value === 2)
549 {
550 $pluralForm = 2;
551 }
552 else if (
553 $value % 100 >= 3
554 && $value % 100 <= 10
555 )
556 {
557 $pluralForm = 3;
558 }
559 else if ($value % 100 >= 11)
560 {
561 $pluralForm = 4;
562 }
563 else
564 {
565 $pluralForm = 5;
566 }
567*/
568 break;
569
570 case 'br':
571 case 'fr':
572 case 'tr':
573 $pluralForm = (($value > 1) ? 1 : 0);
574 break;
575
576 case 'de':
577 case 'en':
578 case 'hi':
579 case 'it':
580 case 'la':
581 $pluralForm = (($value !== 1) ? 1 : 0);
582 break;
583
584 case 'ru':
585 case 'ua':
586 if (
587 ($value % 10 === 1)
588 && ($value % 100 !== 11)
589 )
590 {
591 $pluralForm = 0;
592 }
593 else if (
594 ($value % 10 >= 2)
595 && ($value % 10 <= 4)
596 && (
597 ($value % 100 < 10)
598 || ($value % 100 >= 20)
599 )
600 )
601 {
602 $pluralForm = 1;
603 }
604 else
605 {
606 $pluralForm = 2;
607 }
608 break;
609
610 case 'pl':
611 if ($value === 1)
612 {
613 $pluralForm = 0;
614 }
615 else if (
616 $value % 10 >= 2
617 && $value % 10 <= 4
618 && (
619 $value % 100 < 10
620 || $value % 100 >= 20
621 )
622 )
623 {
624 $pluralForm = 1;
625 }
626 else
627 {
628 $pluralForm = 2;
629 }
630 break;
631
632 case 'id':
633 case 'ja':
634 case 'ms':
635 case 'sc':
636 case 'tc':
637 case 'th':
638 case 'vn':
639 $pluralForm = 0;
640 break;
641
642 default:
643 $pluralForm = 1;
644 break;
645 }
646
647 return $pluralForm;
648
649 }
650
651}
static getCurrent()
Definition: context.php:241
static getLocal($path, $root=null)
Definition: loader.php:523
static loadLanguageFile($file, $language=null, $normalize=true)
Definition: loc.php:224
static getPluralForm($value, $language='')
Definition: loc.php:521
static loadCustomMessages($file, $language=null)
Definition: loc.php:282
static getMessagePlural(string $code, int $value, array $replace=null, string $language=null)
Definition: loc.php:497
static loadMessages($file)
Definition: loc.php:64
static setCurrentLang($language)
Definition: loc.php:95
static getDefaultLang($lang)
Definition: loc.php:453
static getMessage($code, $replace=null, $language=null)
Definition: loc.php:29
static convertLangPath($langFile, $language)
static getEncodings($language, $langFile)