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.
Auf den Verzeichnisnamen klicken, dies zeigt nur das Verzeichnis mit Inhalt an

(Beispiel Datei-Icons)

Auf das Icon klicken um den Quellcode anzuzeigen

language.php

Zuletzt modifiziert: 09.10.2024, 12:52 - Dateigröße: 17.41 KiB


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