Verzeichnisstruktur phpBB-3.2.0
- Veröffentlicht
- 06.01.2017
So funktioniert es
|
Auf das letzte Element klicken. Dies geht jeweils ein Schritt zurück |
Auf das Icon klicken, dies öffnet das Verzeichnis. Nochmal klicken schließt das Verzeichnis. |
|
(Beispiel Datei-Icons)
|
Auf das Icon klicken um den Quellcode anzuzeigen |
language.php
001 <?php
002 /**
003 *
004 * This file is part of the phpBB Forum Software package.
005 *
006 * @copyright (c) phpBB Limited <https://www.phpbb.com>
007 * @license GNU General Public License, version 2 (GPL-2.0)
008 *
009 * For full copyright and license information, please see
010 * the docs/CREDITS.txt file.
011 *
012 */
013
014 namespace phpbb\language;
015
016 use phpbb\language\exception\invalid_plural_rule_exception;
017
018 /**
019 * Wrapper class for loading translations
020 */
021 class language
022 {
023 /**
024 * Global fallback language
025 *
026 * ISO code of the language to fallback to when the specified language entries
027 * cannot be found.
028 *
029 * @var string
030 */
031 const FALLBACK_LANGUAGE = 'en';
032
033 /**
034 * @var array List of common language files
035 */
036 protected $common_language_files;
037
038 /**
039 * @var bool
040 */
041 protected $common_language_files_loaded;
042
043 /**
044 * @var string ISO code of the default board language
045 */
046 protected $default_language;
047
048 /**
049 * @var string ISO code of the User's language
050 */
051 protected $user_language;
052
053 /**
054 * @var array Language fallback array (the order is important)
055 */
056 protected $language_fallback;
057
058 /**
059 * @var array Array of language variables
060 */
061 protected $lang;
062
063 /**
064 * @var array Loaded language sets
065 */
066 protected $loaded_language_sets;
067
068 /**
069 * @var \phpbb\language\language_file_loader Language file loader
070 */
071 protected $loader;
072
073 /**
074 * Constructor
075 *
076 * @param \phpbb\language\language_file_loader $loader Language file loader
077 * @param array|null $common_modules Array of common language modules to load (optional)
078 */
079 public function __construct(language_file_loader $loader, $common_modules = null)
080 {
081 $this->loader = $loader;
082
083 // Set up default information
084 $this->user_language = false;
085 $this->default_language = false;
086 $this->lang = array();
087 $this->loaded_language_sets = array(
088 'core' => array(),
089 'ext' => array(),
090 );
091
092 // Common language files
093 if (is_array($common_modules))
094 {
095 $this->common_language_files = $common_modules;
096 }
097 else
098 {
099 $this->common_language_files = array(
100 'common',
101 );
102 }
103
104 $this->common_language_files_loaded = false;
105
106 $this->language_fallback = array(self::FALLBACK_LANGUAGE);
107 }
108
109 /**
110 * Function to set user's language to display.
111 *
112 * @param string $user_lang_iso ISO code of the User's language
113 * @param bool $reload Whether or not to reload language files
114 */
115 public function set_user_language($user_lang_iso, $reload = false)
116 {
117 $this->user_language = $user_lang_iso;
118
119 $this->set_fallback_array($reload);
120 }
121
122 /**
123 * Function to set the board's default language to display.
124 *
125 * @param string $default_lang_iso ISO code of the board's default language
126 * @param bool $reload Whether or not to reload language files
127 */
128 public function set_default_language($default_lang_iso, $reload = false)
129 {
130 $this->default_language = $default_lang_iso;
131
132 $this->set_fallback_array($reload);
133 }
134
135 /**
136 * Returns language array
137 *
138 * Note: This function is needed for the BC purposes, until \phpbb\user::lang[] is
139 * not removed.
140 *
141 * @return array Array of loaded language strings
142 */
143 public function get_lang_array()
144 {
145 // Load common language files if they not loaded yet
146 if (!$this->common_language_files_loaded)
147 {
148 $this->load_common_language_files();
149 }
150
151 return $this->lang;
152 }
153
154 /**
155 * Add Language Items
156 *
157 * Examples:
158 * <code>
159 * $component = array('posting');
160 * $component = array('posting', 'viewtopic')
161 * $component = 'posting'
162 * </code>
163 *
164 * @param string|array $component The name of the language component to load
165 * @param string|null $extension_name Name of the extension to load component from, or null for core file
166 */
167 public function add_lang($component, $extension_name = null)
168 {
169 // Load common language files if they not loaded yet
170 // This needs to be here to correctly merge language arrays
171 if (!$this->common_language_files_loaded)
172 {
173 $this->load_common_language_files();
174 }
175
176 if (!is_array($component))
177 {
178 if (!is_null($extension_name))
179 {
180 $this->load_extension($extension_name, $component);
181 }
182 else
183 {
184 $this->load_core_file($component);
185 }
186 }
187 else
188 {
189 foreach ($component as $lang_file)
190 {
191 $this->add_lang($lang_file, $extension_name);
192 }
193 }
194 }
195
196 /**
197 * @param $key array|string The language key we want to know more about. Can be string or array.
198 *
199 * @return bool Returns whether the language key is set.
200 */
201 public function is_set($key)
202 {
203 // Load common language files if they not loaded yet
204 if (!$this->common_language_files_loaded)
205 {
206 $this->load_common_language_files();
207 }
208
209 if (is_array($key))
210 {
211 $lang = &$this->lang[array_shift($key)];
212
213 foreach ($key as $_key)
214 {
215 $lang = &$lang[$_key];
216 }
217 }
218 else
219 {
220 $lang = &$this->lang[$key];
221 }
222
223 return isset($lang);
224 }
225
226 /**
227 * Advanced language substitution
228 *
229 * Function to mimic sprintf() with the possibility of using phpBB's language system to substitute nullar/singular/plural forms.
230 * Params are the language key and the parameters to be substituted.
231 * This function/functionality is inspired by SHS` and Ashe.
232 *
233 * Example call: <samp>$user->lang('NUM_POSTS_IN_QUEUE', 1);</samp>
234 *
235 * If the first parameter is an array, the elements are used as keys and subkeys to get the language entry:
236 * Example: <samp>$user->lang(array('datetime', 'AGO'), 1)</samp> uses $user->lang['datetime']['AGO'] as language entry.
237 *
238 * @return string Return localized string or the language key if the translation is not available
239 */
240 public function lang()
241 {
242 $args = func_get_args();
243 $key = array_shift($args);
244
245 return $this->lang_array($key, $args);
246 }
247
248 /**
249 * Returns the raw value associated to a language key or the language key no translation is available.
250 * No parameter substitution is performed, can be a string or an array.
251 *
252 * @param string|array $key Language key
253 *
254 * @return array|string
255 */
256 public function lang_raw($key)
257 {
258 // Load common language files if they not loaded yet
259 if (!$this->common_language_files_loaded)
260 {
261 $this->load_common_language_files();
262 }
263
264 if (is_array($key))
265 {
266 $lang = &$this->lang[array_shift($key)];
267
268 foreach ($key as $_key)
269 {
270 $lang = &$lang[$_key];
271 }
272 }
273 else
274 {
275 $lang = &$this->lang[$key];
276 }
277
278 // Return if language string does not exist
279 if (!isset($lang) || (!is_string($lang) && !is_array($lang)))
280 {
281 return $key;
282 }
283
284 return $lang;
285 }
286
287 /**
288 * Act like lang() but takes a key and an array of parameters instead of using variadic
289 *
290 * @param string|array $key Language key
291 * @param array $args Parameters
292 *
293 * @return string
294 */
295 public function lang_array($key, $args = array())
296 {
297 $lang = $this->lang_raw($key);
298
299 if ($lang === $key)
300 {
301 return $key;
302 }
303
304 // If the language entry is a string, we simply mimic sprintf() behaviour
305 if (is_string($lang))
306 {
307 if (count($args) === 0)
308 {
309 return $lang;
310 }
311
312 // Replace key with language entry and simply pass along...
313 return vsprintf($lang, $args);
314 }
315 else if (sizeof($lang) == 0)
316 {
317 // If the language entry is an empty array, we just return the language key
318 return $key;
319 }
320
321 // It is an array... now handle different nullar/singular/plural forms
322 $key_found = false;
323
324 // We now get the first number passed and will select the key based upon this number
325 for ($i = 0, $num_args = sizeof($args); $i < $num_args; $i++)
326 {
327 if (is_int($args[$i]) || is_float($args[$i]))
328 {
329 if ($args[$i] == 0 && isset($lang[0]))
330 {
331 // We allow each translation using plural forms to specify a version for the case of 0 things,
332 // so that "0 users" may be displayed as "No users".
333 $key_found = 0;
334 break;
335 }
336 else
337 {
338 $use_plural_form = $this->get_plural_form($args[$i]);
339 if (isset($lang[$use_plural_form]))
340 {
341 // The key we should use exists, so we use it.
342 $key_found = $use_plural_form;
343 }
344 else
345 {
346 // If the key we need to use does not exist, we fall back to the previous one.
347 $numbers = array_keys($lang);
348
349 foreach ($numbers as $num)
350 {
351 if ($num > $use_plural_form)
352 {
353 break;
354 }
355
356 $key_found = $num;
357 }
358 }
359 break;
360 }
361 }
362 }
363
364 // Ok, let's check if the key was found, else use the last entry (because it is mostly the plural form)
365 if ($key_found === false)
366 {
367 $numbers = array_keys($lang);
368 $key_found = end($numbers);
369 }
370
371 // Use the language string we determined and pass it to sprintf()
372 return vsprintf($lang[$key_found], $args);
373 }
374
375 /**
376 * Loads common language files
377 */
378 protected function load_common_language_files()
379 {
380 if (!$this->common_language_files_loaded)
381 {
382 foreach ($this->common_language_files as $lang_file)
383 {
384 $this->load_core_file($lang_file);
385 }
386
387 $this->common_language_files_loaded = true;
388 }
389 }
390
391 /**
392 * Determine which plural form we should use.
393 *
394 * For some languages this is not as simple as for English.
395 *
396 * @param int|float $number The number we want to get the plural case for. Float numbers are floored.
397 * @param int|bool $force_rule False to use the plural rule of the language package
398 * or an integer to force a certain plural rule
399 *
400 * @return int The plural-case we need to use for the number plural-rule combination
401 *
402 * @throws \phpbb\language\exception\invalid_plural_rule_exception When $force_rule has an invalid value
403 */
404 public function get_plural_form($number, $force_rule = false)
405 {
406 $number = (int) $number;
407 $plural_rule = ($force_rule !== false) ? $force_rule : ((isset($this->lang['PLURAL_RULE'])) ? $this->lang['PLURAL_RULE'] : 1);
408
409 if ($plural_rule > 15 || $plural_rule < 0)
410 {
411 throw new invalid_plural_rule_exception('INVALID_PLURAL_RULE', array(
412 'plural_rule' => $plural_rule,
413 ));
414 }
415
416 /**
417 * The following plural rules are based on a list published by the Mozilla Developer Network
418 * https://developer.mozilla.org/en/Localization_and_Plurals
419 */
420 switch ($plural_rule)
421 {
422 case 0:
423 /**
424 * Families: Asian (Chinese, Japanese, Korean, Vietnamese), Persian, Turkic/Altaic (Turkish), Thai, Lao
425 * 1 - everything: 0, 1, 2, ...
426 */
427 return 1;
428
429 case 1:
430 /**
431 * Families: Germanic (Danish, Dutch, English, Faroese, Frisian, German, Norwegian, Swedish), Finno-Ugric (Estonian, Finnish, Hungarian), Language isolate (Basque), Latin/Greek (Greek), Semitic (Hebrew), Romanic (Italian, Portuguese, Spanish, Catalan)
432 * 1 - 1
433 * 2 - everything else: 0, 2, 3, ...
434 */
435 return ($number === 1) ? 1 : 2;
436
437 case 2:
438 /**
439 * Families: Romanic (French, Brazilian Portuguese)
440 * 1 - 0, 1
441 * 2 - everything else: 2, 3, ...
442 */
443 return (($number === 0) || ($number === 1)) ? 1 : 2;
444
445 case 3:
446 /**
447 * Families: Baltic (Latvian)
448 * 1 - 0
449 * 2 - ends in 1, not 11: 1, 21, ... 101, 121, ...
450 * 3 - everything else: 2, 3, ... 10, 11, 12, ... 20, 22, ...
451 */
452 return ($number === 0) ? 1 : ((($number % 10 === 1) && ($number % 100 != 11)) ? 2 : 3);
453
454 case 4:
455 /**
456 * Families: Celtic (Scottish Gaelic)
457 * 1 - is 1 or 11: 1, 11
458 * 2 - is 2 or 12: 2, 12
459 * 3 - others between 3 and 19: 3, 4, ... 10, 13, ... 18, 19
460 * 4 - everything else: 0, 20, 21, ...
461 */
462 return ($number === 1 || $number === 11) ? 1 : (($number === 2 || $number === 12) ? 2 : (($number >= 3 && $number <= 19) ? 3 : 4));
463
464 case 5:
465 /**
466 * Families: Romanic (Romanian)
467 * 1 - 1
468 * 2 - is 0 or ends in 01-19: 0, 2, 3, ... 19, 101, 102, ... 119, 201, ...
469 * 3 - everything else: 20, 21, ...
470 */
471 return ($number === 1) ? 1 : ((($number === 0) || (($number % 100 > 0) && ($number % 100 < 20))) ? 2 : 3);
472
473 case 6:
474 /**
475 * Families: Baltic (Lithuanian)
476 * 1 - ends in 1, not 11: 1, 21, 31, ... 101, 121, ...
477 * 2 - ends in 0 or ends in 10-20: 0, 10, 11, 12, ... 19, 20, 30, 40, ...
478 * 3 - everything else: 2, 3, ... 8, 9, 22, 23, ... 29, 32, 33, ...
479 */
480 return (($number % 10 === 1) && ($number % 100 != 11)) ? 1 : ((($number % 10 < 2) || (($number % 100 >= 10) && ($number % 100 < 20))) ? 2 : 3);
481
482 case 7:
483 /**
484 * Families: Slavic (Croatian, Serbian, Russian, Ukrainian)
485 * 1 - ends in 1, not 11: 1, 21, 31, ... 101, 121, ...
486 * 2 - ends in 2-4, not 12-14: 2, 3, 4, 22, 23, 24, 32, ...
487 * 3 - everything else: 0, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 25, 26, ...
488 */
489 return (($number % 10 === 1) && ($number % 100 != 11)) ? 1 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 10) || ($number % 100 >= 20))) ? 2 : 3);
490
491 case 8:
492 /**
493 * Families: Slavic (Slovak, Czech)
494 * 1 - 1
495 * 2 - 2, 3, 4
496 * 3 - everything else: 0, 5, 6, 7, ...
497 */
498 return ($number === 1) ? 1 : ((($number >= 2) && ($number <= 4)) ? 2 : 3);
499
500 case 9:
501 /**
502 * Families: Slavic (Polish)
503 * 1 - 1
504 * 2 - ends in 2-4, not 12-14: 2, 3, 4, 22, 23, 24, 32, ... 104, 122, ...
505 * 3 - everything else: 0, 5, 6, ... 11, 12, 13, 14, 15, ... 20, 21, 25, ...
506 */
507 return ($number === 1) ? 1 : ((($number % 10 >= 2) && ($number % 10 <= 4) && (($number % 100 < 12) || ($number % 100 > 14))) ? 2 : 3);
508
509 case 10:
510 /**
511 * Families: Slavic (Slovenian, Sorbian)
512 * 1 - ends in 01: 1, 101, 201, ...
513 * 2 - ends in 02: 2, 102, 202, ...
514 * 3 - ends in 03-04: 3, 4, 103, 104, 203, 204, ...
515 * 4 - everything else: 0, 5, 6, 7, 8, 9, 10, 11, ...
516 */
517 return ($number % 100 === 1) ? 1 : (($number % 100 === 2) ? 2 : ((($number % 100 === 3) || ($number % 100 === 4)) ? 3 : 4));
518
519 case 11:
520 /**
521 * Families: Celtic (Irish Gaeilge)
522 * 1 - 1
523 * 2 - 2
524 * 3 - is 3-6: 3, 4, 5, 6
525 * 4 - is 7-10: 7, 8, 9, 10
526 * 5 - everything else: 0, 11, 12, ...
527 */
528 return ($number === 1) ? 1 : (($number === 2) ? 2 : (($number >= 3 && $number <= 6) ? 3 : (($number >= 7 && $number <= 10) ? 4 : 5)));
529
530 case 12:
531 /**
532 * Families: Semitic (Arabic)
533 * 1 - 1
534 * 2 - 2
535 * 3 - ends in 03-10: 3, 4, ... 10, 103, 104, ... 110, 203, 204, ...
536 * 4 - ends in 11-99: 11, ... 99, 111, 112, ...
537 * 5 - everything else: 100, 101, 102, 200, 201, 202, ...
538 * 6 - 0
539 */
540 return ($number === 1) ? 1 : (($number === 2) ? 2 : ((($number % 100 >= 3) && ($number % 100 <= 10)) ? 3 : ((($number % 100 >= 11) && ($number % 100 <= 99)) ? 4 : (($number != 0) ? 5 : 6))));
541
542 case 13:
543 /**
544 * Families: Semitic (Maltese)
545 * 1 - 1
546 * 2 - is 0 or ends in 01-10: 0, 2, 3, ... 9, 10, 101, 102, ...
547 * 3 - ends in 11-19: 11, 12, ... 18, 19, 111, 112, ...
548 * 4 - everything else: 20, 21, ...
549 */
550 return ($number === 1) ? 1 : ((($number === 0) || (($number % 100 > 1) && ($number % 100 < 11))) ? 2 : ((($number % 100 > 10) && ($number % 100 < 20)) ? 3 : 4));
551
552 case 14:
553 /**
554 * Families: Slavic (Macedonian)
555 * 1 - ends in 1: 1, 11, 21, ...
556 * 2 - ends in 2: 2, 12, 22, ...
557 * 3 - everything else: 0, 3, 4, ... 10, 13, 14, ... 20, 23, ...
558 */
559 return ($number % 10 === 1) ? 1 : (($number % 10 === 2) ? 2 : 3);
560
561 case 15:
562 /**
563 * Families: Icelandic
564 * 1 - ends in 1, not 11: 1, 21, 31, ... 101, 121, 131, ...
565 * 2 - everything else: 0, 2, 3, ... 10, 11, 12, ... 20, 22, ...
566 */
567 return (($number % 10 === 1) && ($number % 100 != 11)) ? 1 : 2;
568 }
569 }
570
571 /**
572 * Returns the ISO code of the used language
573 *
574 * @return string The ISO code of the currently used language
575 */
576 public function get_used_language()
577 {
578 return $this->language_fallback[0];
579 }
580
581 /**
582 * Returns language fallback data
583 *
584 * @param bool $reload Whether or not to reload language files
585 *
586 * @return array
587 */
588 protected function set_fallback_array($reload = false)
589 {
590 $fallback_array = array();
591
592 if ($this->user_language)
593 {
594 $fallback_array[] = $this->user_language;
595 }
596
597 if ($this->default_language)
598 {
599 $fallback_array[] = $this->default_language;
600 }
601
602 $fallback_array[] = self::FALLBACK_LANGUAGE;
603
604 $this->language_fallback = $fallback_array;
605
606 if ($reload)
607 {
608 $this->reload_language_files();
609 }
610 }
611
612 /**
613 * Load core language file
614 *
615 * @param string $component Name of the component to load
616 */
617 protected function load_core_file($component)
618 {
619 // Check if the component is already loaded
620 if (isset($this->loaded_language_sets['core'][$component]))
621 {
622 return;
623 }
624
625 $this->loader->load($component, $this->language_fallback, $this->lang);
626 $this->loaded_language_sets['core'][$component] = true;
627 }
628
629 /**
630 * Load extension language file
631 *
632 * @param string $extension_name Name of the extension to load language from
633 * @param string $component Name of the component to load
634 */
635 protected function load_extension($extension_name, $component)
636 {
637 // Check if the component is already loaded
638 if (isset($this->loaded_language_sets['ext'][$extension_name][$component]))
639 {
640 return;
641 }
642
643 $this->loader->load_extension($extension_name, $component, $this->language_fallback, $this->lang);
644 $this->loaded_language_sets['ext'][$extension_name][$component] = true;
645 }
646
647 /**
648 * Reload language files
649 */
650 protected function reload_language_files()
651 {
652 $loaded_files = $this->loaded_language_sets;
653 $this->loaded_language_sets = array(
654 'core' => array(),
655 'ext' => array(),
656 );
657
658 // Reload core files
659 foreach ($loaded_files['core'] as $component => $value)
660 {
661 $this->load_core_file($component);
662 }
663
664 // Reload extension files
665 foreach ($loaded_files['ext'] as $ext_name => $ext_info)
666 {
667 foreach ($ext_info as $ext_component => $value)
668 {
669 $this->load_extension($ext_name, $ext_component);
670 }
671 }
672 }
673 }
674