Bitrix-D7  20.0.0
loc.php
См. документацию.
1 <?php
3 
4 use Bitrix\Main;
7 use Bitrix\Main\Config\Configuration;
9 
10 final 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 
20  /**
21  * Returns translation by message code.
22  * Loc::loadMessages(__FILE__) should be called first once per php file
23  *
24  * @param string $code
25  * @param array $replace e.g. array("#NUM#"=>5)
26  * @param string $language
27  * @return string
28  */
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];
50 
51  if($replace !== null && is_array($replace))
52  {
53  foreach($replace as $search => $repl)
54  {
55  $s = str_replace($search, $repl, $s);
56  }
57  }
58 
59  return $s;
60  }
61 
62  /**
63  * Loads language messages for specified file in a lazy way
64  *
65  * @param string $file
66  */
67  public static function loadMessages($file)
68  {
69  if(($realPath = realpath($file)) !== false)
70  {
71  $file = $realPath;
72  }
73  $file = Path::normalize($file);
74 
75  self::$lazyLoadFiles[$file] = $file;
76  }
77 
78  /**
79  * @return string
80  */
81  public static function getCurrentLang()
82  {
83  if(self::$currentLang === null)
84  {
85  $context = Context::getCurrent();
86  if($context !== null)
87  {
88  $language = $context->getLanguage();
89  if($language !== null)
90  {
91  self::$currentLang = $language;
92  }
93  }
94  }
95  return (self::$currentLang !== null? self::$currentLang : 'en');
96  }
97 
98  public static function setCurrentLang($language)
99  {
100  self::$currentLang = $language;
101  }
102 
103  /**
104  * Loads language messages for specified file and language.
105  *
106  * @param string $file File path to look for language translation for its.
107  * @param string $language Language code.
108  * @param string $loadedLangFile Certain loaded language file.
109  *
110  * @return array
111  */
112  private static function includeLangFiles($file, $language, &$loadedLangFile)
113  {
114  static $langDirCache = array();
115 
116  $path = Path::getDirectory($file);
117 
118  if(isset($langDirCache[$path]))
119  {
120  $langDir = $langDirCache[$path];
121  $fileName = substr($file, (strlen($langDir)-5));
122  }
123  else
124  {
125  //let's find language folder
126  $langDir = $fileName = '';
127  $filePath = $file;
128  while(($slashPos = strrpos($filePath, '/')) !== false)
129  {
130  $filePath = substr($filePath, 0, $slashPos);
131  $langPath = $filePath.'/lang';
132  if(is_dir($langPath))
133  {
134  $langDir = $langPath;
135  $fileName = substr($file, $slashPos);
136  $langDirCache[$path] = $langDir;
137  break;
138  }
139  }
140  }
141 
142  $mess = array();
143  if($langDir <> '')
144  {
145  //load messages for default lang first
146  $defaultLang = self::getDefaultLang($language);
147  if($defaultLang <> $language)
148  {
149  $langFile = $langDir. '/'. $defaultLang. $fileName;
150 
151  $langFile = Translation::convertLangPath($langFile, $defaultLang);
152 
153  if(file_exists($langFile))
154  {
155  $mess = self::includeFile($langFile);
156  $loadedLangFile = $langFile;
157  }
158  }
159 
160  //then load messages for specified lang
161  $langFile = $langDir. '/'. $language. $fileName;
162 
163  $langFile = Translation::convertLangPath($langFile, $language);
164 
165  if(file_exists($langFile))
166  {
167  $mess = array_merge($mess, self::includeFile($langFile));
168  $loadedLangFile = $langFile;
169  }
170  }
171 
172  return $mess;
173  }
174 
175  /**
176  * Loads language messages for specified file
177  *
178  * @param string $file
179  * @param string $language
180  * @param bool $normalize
181  * @return array
182  */
183  public static function loadLanguageFile($file, $language = null, $normalize = true)
184  {
185  if($language === null)
186  {
187  $language = self::getCurrentLang();
188  }
189 
190  if($normalize)
191  {
192  $file = Path::normalize($file);
193  }
194 
195  if(!isset(self::$messages[$language]))
196  {
197  self::$messages[$language] = array();
198  }
199 
200  //first time call only for lang
201  if(!isset(self::$userMessages[$language]))
202  {
203  self::$userMessages[$language] = self::loadUserMessages($language);
204  }
205 
206  //let's find language folder and include lang files
207  $mess = self::includeLangFiles($file, $language, $langFile);
208 
209  if (!empty($mess))
210  {
211  list($convertEncoding, $targetEncoding, $sourceEncoding) = Translation::getEncodings($language, $langFile);
212 
213  foreach ($mess as $key => $val)
214  {
215  if (isset(self::$customMessages[$language][$key]))
216  {
217  self::$messages[$language][$key] = $mess[$key] = self::$customMessages[$language][$key];
218  }
219  else
220  {
221  if ($convertEncoding)
222  {
223  $val = Encoding::convertEncoding($val, $sourceEncoding, $targetEncoding);
224  $mess[$key] = $val;
225  }
226 
227  self::$messages[$language][$key] = $val;
228  }
229  }
230  }
231 
232  return $mess;
233  }
234 
235  /**
236  * Loads custom messages from the file to overwrite messages by their IDs.
237  *
238  * @param string $file
239  * @param string|null $language
240  */
241  public static function loadCustomMessages($file, $language = null)
242  {
243  if($language === null)
244  {
245  $language = self::getCurrentLang();
246  }
247 
248  if(!isset(self::$customMessages[$language]))
249  {
250  self::$customMessages[$language] = array();
251  }
252 
253  //let's find language folder and include lang files
254  $mess = self::includeLangFiles(Path::normalize($file), $language, $langFile);
255 
256  if (!empty($mess))
257  {
258  list($convertEncoding, $targetEncoding, $sourceEncoding) = Translation::getEncodings($language, $langFile);
259 
260  foreach ($mess as $key => $val)
261  {
262  if ($convertEncoding)
263  {
264  $val = $mess[$key] = Encoding::convertEncoding($val, $sourceEncoding, $targetEncoding);
265  }
266 
267  self::$customMessages[$language][$key] = $val;
268  }
269  }
270  }
271 
272  private static function loadLazy($code, $language)
273  {
274  if($code == '')
275  {
276  return;
277  }
278 
279  //control of duplicates
280  if(!isset(self::$triedFiles[$language]))
281  {
282  self::$triedFiles[$language] = [];
283  }
284 
285  $trace = Main\Diag\Helper::getBackTrace(4, DEBUG_BACKTRACE_IGNORE_ARGS);
286 
287  $currentFile = null;
288  for($i = 3; $i >= 1; $i--)
289  {
290  if(stripos($trace[$i]["function"], "GetMessage") === 0)
291  {
292  $currentFile = Path::normalize($trace[$i]["file"]);
293 
294  //we suppose there is a language file even if it wasn't registered via loadMessages()
295  self::$lazyLoadFiles[$currentFile] = $currentFile;
296  break;
297  }
298  }
299 
300  if($currentFile !== null && isset(self::$lazyLoadFiles[$currentFile]))
301  {
302  //in most cases we know the file containing the "code" - load it directly
303  if(!isset(self::$triedFiles[$language][$currentFile]))
304  {
305  self::loadLanguageFile($currentFile, $language, false);
306  self::$triedFiles[$language][$currentFile] = true;
307  }
308  unset(self::$lazyLoadFiles[$currentFile]);
309  }
310 
311  if(!isset(self::$messages[$language][$code]))
312  {
313  //we still don't know which file contains the "code" - go through the files in the reverse order
314  $unset = array();
315  if(($file = end(self::$lazyLoadFiles)) !== false)
316  {
317  do
318  {
319  if(!isset(self::$triedFiles[$language][$file]))
320  {
321  self::loadLanguageFile($file, $language, false);
322  self::$triedFiles[$language][$file] = true;
323  }
324 
325  $unset[] = $file;
326  if(isset(self::$messages[$language][$code]))
327  {
328  if(defined("BX_MESS_LOG") && $currentFile !== null)
329  {
330  file_put_contents(BX_MESS_LOG, 'CTranslateUtils::CopyMessage("'.$code.'", "'.$file.'", "'.$currentFile.'");'."\n", FILE_APPEND);
331  }
332  break;
333  }
334  }
335  while(($file = prev(self::$lazyLoadFiles)) !== false);
336  }
337  foreach($unset as $file)
338  {
339  unset(self::$lazyLoadFiles[$file]);
340  }
341  }
342 
343  if(!isset(self::$messages[$language][$code]) && defined("BX_MESS_LOG"))
344  {
345  file_put_contents(BX_MESS_LOG, $code.": not found for ".$currentFile."\n", FILE_APPEND);
346  }
347  }
348 
349  /**
350  * Reads messages from user defined lang file
351  *
352  * @param string $lang
353  * @return array
354  */
355  private static function loadUserMessages($lang)
356  {
357  $userMess = array();
358  $documentRoot = Main\Application::getDocumentRoot();
359  if(($fname = Main\Loader::getLocal("php_interface/user_lang/".$lang."/lang.php", $documentRoot)) !== false)
360  {
361  $mess = self::includeFile($fname);
362 
363  // typical call is Loc::loadMessages(__FILE__)
364  // __FILE__ can differ from path used in the user file
365  foreach($mess as $key => $val)
366  $userMess[str_replace("\\", "/", realpath($documentRoot.$key))] = $val;
367  }
368  return $userMess;
369  }
370 
371  /**
372  * Reads messages from lang file.
373  *
374  * @param string $path
375  * @return array
376  */
377  private static function includeFile($path)
378  {
379  self::$includedFiles[$path] = $path;
380 
381  //the name $MESS is predefined in language files
382  $MESS = array();
383  include($path);
384 
385  //redefine messages from user lang file
386  if(!empty(self::$userMessages))
387  {
388  $path = str_replace("\\", "/", realpath($path));
389 
390  //cycle through languages
391  foreach(self::$userMessages as $messages)
392  {
393  if(is_array($messages[$path]))
394  {
395  foreach($messages[$path] as $key => $val)
396  {
397  $MESS[$key] = $val;
398  }
399  }
400  }
401  }
402 
403  return $MESS;
404  }
405 
406  /**
407  * Returns default language for specified language. Default language is used when translation is not found.
408  *
409  * @param string $lang
410  * @return string
411  */
412  public static function getDefaultLang($lang)
413  {
414  static $subst = array('ua'=>'ru', 'kz'=>'ru', 'by'=>'ru', 'ru'=>'ru', 'en'=>'en', 'de'=>'en');
415  if(isset($subst[$lang]))
416  {
417  return $subst[$lang];
418  }
419 
420  $options = Configuration::getValue("default_language");
421  if(isset($options[$lang]))
422  {
423  return $options[$lang];
424  }
425 
426  return 'en';
427  }
428 
429  /**
430  * Returns previously included lang files.
431  * @return array
432  */
433  public static function getIncludedFiles()
434  {
435  return self::$includedFiles;
436  }
437 }
Bitrix\Main\Localization\Loc\getMessage
static getMessage($code, $replace=null, $language=null)
Returns translation by message code.
Definition: loc.php:29
Bitrix\Main\Localization\Translation\convertLangPath
static convertLangPath($langFile, $language)
Converts lang file to translation repository path.
Definition: main/lib/localization/translation.php:331
Bitrix\Main\Localization\Loc\loadCustomMessages
static loadCustomMessages($file, $language=null)
Loads custom messages from the file to overwrite messages by their IDs.
Definition: loc.php:241
Bitrix\Main\Localization
Definition: localization/culture.php:8
Bitrix\Main
Bitrix\Main\IO\Path\normalize
static normalize($path)
Definition: main/lib/io/path.php:26
Bitrix\Main\Localization\Loc\getCurrentLang
static getCurrentLang()
Definition: loc.php:81
Bitrix\Main\Localization\Loc\loadLanguageFile
static loadLanguageFile($file, $language=null, $normalize=true)
Loads language messages for specified file.
Definition: loc.php:183
Bitrix\Main\Localization\Translation\getEncodings
static getEncodings($language, $langFile)
Definition: main/lib/localization/translation.php:595
Bitrix\Main\Localization\Loc\setCurrentLang
static setCurrentLang($language)
Definition: loc.php:98
Bitrix\Main\IO\Path\getDirectory
static getDirectory($path)
Definition: main/lib/io/path.php:109
Bitrix\Main\IO\Path
Definition: main/lib/io/path.php:10
Bitrix\Main\Localization\Loc\getIncludedFiles
static getIncludedFiles()
Returns previously included lang files.
Definition: loc.php:433
Bitrix\Main\Diag\Helper\getBackTrace
static getBackTrace($limit=0, $options=null, $skip=1)
Returns array backtrace.
Definition: main/lib/diag/helper.php:26
Bitrix\Main\Localization\Loc
Definition: loc.php:10
Bitrix\Main\Text\Encoding
Definition: encoding.php:10
Bitrix\Main\Text\Encoding\convertEncoding
static convertEncoding($data, $charsetFrom, $charsetTo, &$errorMessage="")
Converts data from a source encoding to a target encoding.
Definition: encoding.php:53
Bitrix\Main\Application\getDocumentRoot
static getDocumentRoot()
Returns server document root.
Definition: main/lib/application.php:460
Bitrix\Main\Loader\getLocal
static getLocal($path, $root=null)
Checks if file exists in /local or /bitrix directories.
Definition: main/lib/loader.php:439
Bitrix\Main\Localization\Loc\loadMessages
static loadMessages($file)
Loads language messages for specified file in a lazy way.
Definition: loc.php:67
Bitrix\Main\Context
Definition: context/culture.php:9
Bitrix\Main\Localization\Loc\getDefaultLang
static getDefaultLang($lang)
Returns default language for specified language.
Definition: loc.php:412
Bitrix\Main\Context\getCurrent
static getCurrent()
Static method returns current instance of context.
Definition: main/lib/context.php:194