[ Index ]

PHP Cross Reference of phpBB-3.2.8-deutsch

title

Body

[close]

/phpbb/language/ -> language.php (source)

   1  <?php
   2  /**
   3   *
   4   * This file is part of the phpBB Forum Software package.
   5   *
   6   * @copyright (c) phpBB Limited <https://www.phpbb.com>
   7   * @license GNU General Public License, version 2 (GPL-2.0)
   8   *
   9   * For full copyright and license information, please see
  10   * the docs/CREDITS.txt file.
  11   *
  12   */
  13  
  14  namespace phpbb\language;
  15  
  16  use phpbb\language\exception\invalid_plural_rule_exception;
  17  
  18  /**
  19   * Wrapper class for loading translations
  20   */
  21  class language
  22  {
  23      /**
  24       * Global fallback language
  25       *
  26       * ISO code of the language to fallback to when the specified language entries
  27       * cannot be found.
  28       *
  29       * @var string
  30       */
  31      const FALLBACK_LANGUAGE = 'en';
  32  
  33      /**
  34       * @var array    List of common language files
  35       */
  36      protected $common_language_files;
  37  
  38      /**
  39       * @var bool
  40       */
  41      protected $common_language_files_loaded;
  42  
  43      /**
  44       * @var string    ISO code of the default board language
  45       */
  46      protected $default_language;
  47  
  48      /**
  49       * @var string    ISO code of the User's language
  50       */
  51      protected $user_language;
  52  
  53      /**
  54       * @var array    Language fallback array (the order is important)
  55       */
  56      protected $language_fallback;
  57  
  58      /**
  59       * @var array    Array of language variables
  60       */
  61      protected $lang;
  62  
  63      /**
  64       * @var array    Loaded language sets
  65       */
  66      protected $loaded_language_sets;
  67  
  68      /**
  69       * @var \phpbb\language\language_file_loader Language file loader
  70       */
  71      protected $loader;
  72  
  73      /**
  74       * Constructor
  75       *
  76       * @param \phpbb\language\language_file_loader    $loader            Language file loader
  77       * @param array|null                            $common_modules    Array of common language modules to load (optional)
  78       */
  79  	public function __construct(language_file_loader $loader, $common_modules = null)
  80      {
  81          $this->loader = $loader;
  82  
  83          // Set up default information
  84          $this->user_language        = false;
  85          $this->default_language        = false;
  86          $this->lang                    = array();
  87          $this->loaded_language_sets    = array(
  88              'core'    => array(),
  89              'ext'    => array(),
  90          );
  91  
  92          // Common language files
  93          if (is_array($common_modules))
  94          {
  95              $this->common_language_files = $common_modules;
  96          }
  97          else
  98          {
  99              $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 (count($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 = count($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  }


Generated: Tue Apr 7 19:42:26 2020 Cross-referenced by PHPXref 0.7.1