Textpattern PHP Cross Reference Content Management Systems

Source: /textpattern/vendors/Textpattern/L10n/Locale.php - 407 lines - 16648 bytes - Summary - Text - Print

Description: Handles locales.

   1  <?php
   2  
   3  /*
   4   * Textpattern Content Management System
   5   * https://textpattern.com/
   6   *
   7   * Copyright (C) 2020 The Textpattern Development Team
   8   *
   9   * This file is part of Textpattern.
  10   *
  11   * Textpattern is free software; you can redistribute it and/or
  12   * modify it under the terms of the GNU General Public License
  13   * as published by the Free Software Foundation, version 2.
  14   *
  15   * Textpattern is distributed in the hope that it will be useful,
  16   * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18   * GNU General Public License for more details.
  19   *
  20   * You should have received a copy of the GNU General Public License
  21   * along with Textpattern. If not, see <https://www.gnu.org/licenses/>.
  22   */
  23  
  24  /**
  25   * Handles locales.
  26   *
  27   * <code>
  28   * echo Txp::get('\Textpattern\L10n\Locale')->setLocale(LC_ALL, 'da-dk')->getLocale();
  29   * </code>
  30   *
  31   * @since   4.6.0
  32   * @package L10n
  33   */
  34  
  35  namespace Textpattern\L10n;
  36  
  37  class Locale
  38  {
  39      /**
  40       * An array of locale identifiers.
  41       *
  42       * @var array
  43       */
  44  
  45      protected $locales = array(
  46          'ar'    => array('ar_SA.UTF-8', 'ar_SA.ISO_8859-6', 'Arabic_Saudi Arabia.1256', 'ar_SA', 'ara', 'ar', 'arabic'),
  47          'bg'    => array('bg_BG.UTF-8', 'bg_BG.ISO_8859-5', 'Bulgarian_Bulgaria.1251', 'bg_BG', 'bg', 'bul', 'bulgarian'),
  48          'bn'    => array('bn_BD.UTF-8', 'bn_BD', 'bn', 'ben', 'bengali', 'bangla'),
  49          'bs'    => array('bs_BA.UTF-8', 'bs_BA.ISO_8859-2', 'Bosnian_Bosnia and Herzegovina.1250', 'bs_BA', 'bs', 'bos', 'bosnian'),
  50          'ca'    => array('ca_ES.UTF-8', 'ca_ES.ISO_8859-1', 'Catalan_Spain.1252', 'ca_ES', 'cat', 'ca', 'catalan'),
  51          'ceb'   => array('ceb.UTF-8', 'ceb', 'cebuano'),
  52          'cs'    => array('cs_CZ.UTF-8', 'cs_CZ.ISO_8859-2', 'Czech_Czech Republic.1250', 'cs_CZ', 'ces', 'cze', 'cs', 'csy', 'czech'),
  53          'cy'    => array('cy_GB.UTF-8', 'cy_GB.ISO_8859-14', 'Welsh_United Kingdom.1252', 'cy_GB', 'cy', 'cym', 'wel', 'welsh'),
  54          'da'    => array('da_DK.UTF-8', 'da_DK.ISO_8859-1', 'Danish_Denmark.1252', 'da_DK', 'da', 'dan', 'danish'),
  55          'de'    => array('de_DE.UTF-8', 'de_DE.ISO_8859-1', 'de_DE.ISO_8859-16', 'German_Germany.1252', 'de_DE', 'de', 'deu', 'german'),
  56          'el'    => array('el_GR.UTF-8', 'el_GR.ISO_8859-7', 'Greek_Greece.1253', 'el_GR', 'el', 'gre', 'greek'),
  57          'en-gb' => array('en_GB.UTF-8', 'en_GB.ISO_8859-1', 'English_UK.1252', 'en_GB', 'en_UK', 'eng', 'en', 'english-uk', 'english', 'C'),
  58          'en-us' => array('en_US.UTF-8', 'en_US.ISO_8859-1', 'English_USA.1252', 'en_US', 'english-us', 'eng', 'en', 'english'),
  59          'es'    => array('es_ES.UTF-8', 'es_ES.ISO_8859-1', 'Spanish_Spain.1252', 'es_ES', 'esp', 'spanish'),
  60          'es-ve' => array('es_VE.UTF-8', 'es_VE.ISO_8859-1', 'Spanish_Venezuela.1252', 'es_VE', 'esv', 'spanish-venezuela'),
  61          'et'    => array('et_EE.UTF-8', 'et_EE.ISO_8859-1', 'et_EE.ISO_8859-15', 'Estonian_Estonia.1257', 'et_EE', 'et', 'est', 'estonian'),
  62          'fa'    => array('fa_IR.UTF-8', 'Farsi_Iran.1256', 'fa_IR', 'fa', 'persian', 'per', 'fas', 'farsi'),
  63          'fi'    => array('fi_FI.UTF-8', 'fi_FI.ISO_8859-1', 'fi_FI.ISO-8859-15', 'fi_FI.ISO_8859-16', 'Finnish_Finland.1252', 'fi_FI', 'fin', 'fi', 'finnish'),
  64          'fil'   => array('ph_PH.UTF-8', 'ph_PH.ISO_8859-1', 'Filipino_Philippines.1252', 'ph_PH', 'fil', 'filipino'),
  65          'fr'    => array('fr_FR.UTF-8', 'fr_FR.ISO_8859-1', 'fr_FR.ISO-8859-15', 'fr_FR.ISO_8859-16', 'French_France.1252', 'fr_FR', 'fra', 'fre', 'fr', 'french'),
  66          'gl'    => array('gl_ES.UTF-8', 'gl_ES.ISO_8859-1', 'Galician_Spain.1252', 'gl_ES', 'gle', 'gl', 'galician', 'galleco'),
  67          'he'    => array('he_IL.UTF-8', 'he_IL.ISO_8859-8', 'Hebrew_Israel.1255', 'he_IL', 'heb', 'he', 'hebrew'),
  68          'hi'    => array('hi_IN.UTF-8', 'Hindi.65001', 'hi_IN', 'hi', 'hin', 'hindi-india'),
  69          'hr'    => array('hr_HR.UTF-8', 'hr_HR.ISO_8859-2', 'hr_HR.ISO_8859-16', 'Croatian_Croatia.1250', 'hr_HR', 'hr', 'hrv', 'croatian'),
  70          'hu'    => array('hu_HU.UTF-8', 'hu_HU.ISO_8859-2', 'hu_HU.ISO_8859-16', 'Hungarian_Hungary.1250', 'hu_HU', 'hun', 'hu', 'hungarian'),
  71          'id'    => array('id_ID.UTF-8', 'id_ID.ISO_8859-1', 'Indonesian_indonesia.1252', 'id_ID', 'id', 'ind', 'indonesian'),
  72          'is'    => array('is_IS.UTF-8', 'is_IS.ISO_8859-1', 'Icelandic_Iceland.1252', 'is_IS', 'is', 'ice', 'isl', 'icelandic'),
  73          'it'    => array('it_IT.UTF-8', 'it_IT.ISO_8859-1', 'it_IT.ISO_8859-16', 'Italian_Italy.1252', 'it_IT', 'it', 'ita', 'italian'),
  74          'ja'    => array('ja_JP.UTF-8', 'Japanese_Japan.932', 'ja_JP', 'ja', 'jpn', 'japanese'),
  75          'km'    => array('km_KH.UTF-8', 'Khmer.65001', 'km_KH', 'km', 'khm', 'kxm', 'khmer'),
  76          'ko'    => array('ko_KR.UTF-8', 'Korean_Korea.949', 'ko_KR', 'ko', 'kor', 'korean'),
  77          'ky'    => array('ky.UTF-8', 'ky-KG.ISO_8859-5', 'Kyrgyz_Kyrgyzstan.1251', 'ky-KG', 'ky', 'kir', 'kyrgyz', 'kirghiz'),
  78          'lt'    => array('lt_LT.UTF-8', 'lt_LT.ISO_8859-4', 'Lithuanian_Lithuania.1257', 'lt_LT', 'lt', 'lit', 'lithuanian'),
  79          'lv'    => array('lv_LV.UTF-8', 'lv_LV.ISO_8859-4', 'Latvian_Latvia.1257', 'lv_LV', 'lv', 'lav', 'latvian'),
  80          'nb'    => array('nb_NO.UTF-8', 'nb_NO.ISO_8859-1', 'Norwegian_Norway.1252', 'nb_NO', 'no', 'nb', 'nob', 'norwegian'),
  81          'nl'    => array('nl_NL.UTF-8', 'nl_NL.ISO_8859-1', 'Dutch_Netherlands.1252', 'nl_NL', 'dut', 'nla', 'nl', 'nld', 'dutch'),
  82          'nn'    => array('nn_NO.UTF-8', 'nn_NO.ISO_8859-1', 'Norwegian-Nynorsk_Norway.1252', 'nn_NO', 'nno', 'nn', 'nynorsk'),
  83          'pa'    => array('pa_IN.UTF-8', 'pa_IN', 'pa', 'pan', 'punjabi'),
  84          'pl'    => array('pl_PL.UTF-8', 'pl_PL.ISO_8859-2', 'Polish_Poland.1250', 'pl_PL', 'pl', 'pol', 'polish'),
  85          'pt-br' => array('pt_BR.UTF-8', 'Portuguese_Brazil.1252', 'pt_BR', 'pt', 'ptb', 'portuguese-brazil'),
  86          'pt'    => array('pt_PT.UTF-8', 'pt_PT.ISO_8859-1', 'Portuguese_Portugal.1252', 'pt_PT', 'por', 'portuguese'),
  87          'ro'    => array('ro_RO.UTF-8', 'ro_RO.ISO_8859-2', 'ro_RO.ISO_8859-16', 'Romanian_Romania.1250', 'ro_RO', 'ron', 'rum', 'ro', 'romanian'),
  88          'ru'    => array('ru_RU.UTF-8', 'ru_RU.ISO_8859-5', 'Russian_Russia.1251', 'ru_RU', 'ru', 'rus', 'russian'),
  89          'sk'    => array('sk_SK.UTF-8', 'sk_SK.ISO_8859-1', 'Slovak_Slovakia.1250', 'sk_SK', 'sk', 'slo', 'slk', 'slovak'),
  90          'sl'    => array('sl_SI.UTF-8', 'sl_SI.ISO-8859-2', 'Slovenian_Slovenia.1250', 'sl_SI', 'sl', 'slv', 'slovenian'),
  91          'sr-rs' => array('sr_RS.UTF-8', 'sr_RS.ISO_8859-5', 'Serbian (Cyrillic)_Serbia and Montenegro (Former).1251', 'sr_RS', 'sr', 'rs', 'srb', 'serbian'),
  92          'sr'    => array('sr_SP.UTF-8', 'sr_SP.ISO_8859-2', 'Serbian (Latin)_Serbia and Montenegro (Former).1250', 'sr_SP', 'sr', 'sp', 'srb', 'serbian'),
  93          'sv'    => array('sv_SE.UTF-8', 'sv_SE.ISO_8859-1', 'Swedish_Sweden.1252', 'sv_SE', 'sv', 'swe', 'sve', 'swedish'),
  94          'th'    => array('th_TH.UTF-8', 'th_TH.ISO_8859-11', 'Thai_Thailand.874', 'th_TH', 'th', 'tha', 'thai'),
  95          'tl'    => array('tl_PH.UTF-8', 'tl_PH.ISO-8859-1', 'tl_PH', 'tl', 'tgl', 'tagalog'),
  96          'tr'    => array('tr_TR.UTF-8', 'tr_TR.ISO_8859-3', 'tr_TR.ISO_8859-9', 'Turkish_Turkey.1254', 'tr_TR', 'tr', 'tur', 'turkish'),
  97          'uk'    => array('uk_UA.UTF-8', 'uk_UA.ISO_8859-5', 'Ukrainian_Ukraine.1251', 'uk_UA', 'uk', 'ukr', 'ukrainian'),
  98          'ur-pk' => array('ur_PK.UTF-8', 'Urdu_Islamic Republic of Pakistan.1256', 'ur_PK', 'ur', 'urd', 'urdu-pakistan'),
  99          'ur'    => array('ur_IN.UTF-8', 'ur_IN', 'ur', 'urd', 'urdu'),
 100          'vi'    => array('vi_VN.UTF-8', 'Vietnamese_Viet Nam.1258', 'vi_VN', 'vi', 'vie', 'vietnamese'),
 101          'zh-cn' => array('zh_CN.UTF-8', 'Chinese_China.936', 'zh_CN', 'chinese-simplified'),
 102          'zh-tw' => array('zh_TW.UTF-8', 'Chinese_Taiwan.950', 'zh_TW', 'chinese-traditional'),
 103      );
 104  
 105      /**
 106       * Used in function validLocale()
 107       *
 108       * @var array
 109       */
 110  
 111      protected $recodeLocale = array(
 112          'ar-dz' => 'ar',
 113          'bg-bg' => 'bg',
 114          'bs-ba' => 'bs',
 115          'ca-es' => 'ca',
 116          'cs-cz' => 'cs',
 117          'da-dk' => 'da',
 118          'de-de' => 'de',
 119          'el-gr' => 'el',
 120          'es-es' => 'es',
 121          'et-ee' => 'et',
 122          'fa-ir' => 'fa',
 123          'fi-fi' => 'fi',
 124          'fr-fr' => 'fr',
 125          'gl-gz' => 'gl',
 126          'gl-es' => 'gl',
 127          'he-il' => 'he',
 128          'hr-hr' => 'hr',
 129          'hu-hu' => 'hu',
 130          'id-id' => 'id',
 131          'is-is' => 'is',
 132          'it-it' => 'it',
 133          'ja-jp' => 'ja',
 134          'ko-kr' => 'ko',
 135          'lt-lt' => 'lt',
 136          'lv-lv' => 'lv',
 137          'nl-nl' => 'nl',
 138          'nn-no' => 'nn',
 139          'no-no' => 'nb',
 140          'pl-pl' => 'pl',
 141          'pt-pt' => 'pt',
 142          'ro-ro' => 'ro',
 143          'ru-ru' => 'ru',
 144          'sk-sk' => 'sk',
 145          'sr-sp' => 'sr',
 146          'sv-se' => 'sv',
 147          'th-th' => 'th',
 148          'tr-tr' => 'tr',
 149          'uk-ua' => 'uk',
 150          'ur-in' => 'ur',
 151          'vi-vn' => 'vi',
 152      );
 153  
 154  
 155      /**
 156       * Sets the locale.
 157       *
 158       * This method wraps around system setlocale. It takes an IETF language code
 159       * and sets the locale accordingly.
 160       *
 161       * The following would set the locale to English:
 162       *
 163       * <code>
 164       * Txp::get('\Textpattern\L10n\Locale')->setLocale(LC_ALL, 'en-GB');
 165       * </code>
 166       *
 167       * This would format currencies according to the French localisation:
 168       *
 169       * <code>
 170       * Txp::get('\Textpattern\L10n\Locale')->setLocale(LC_MONETARY, 'fr-FR');
 171       * echo money_format('%i', 51.99);
 172       * </code>
 173       *
 174       * The '51.99' would be returned as '51,99 EUR' if you have up to date
 175       * French locale installed on your system.
 176       *
 177       * If an array of locales is provided, the first one that works is used.
 178       *
 179       * @param  int          $category The localisation category to change
 180       * @param  string|array $locale   The language code
 181       * @return Locale
 182       * @throws \Exception
 183       * @see    setlocale()
 184       */
 185  
 186      public function setLocale($category, $locale)
 187      {
 188          foreach ((array)$locale as $name) {
 189              $code = strtolower($name);
 190              $code = isset($this->locales[$code]) ? $this->locales[$code] : $name;
 191  
 192              if (@setlocale($category, $code)) {
 193                  return $this;
 194              }
 195          }
 196  
 197          @setlocale($category, null);
 198  
 199          return $this;
 200      }
 201  
 202      /**
 203       * Gets the current locale.
 204       *
 205       * <code>
 206       * echo Txp::get('\Textpattern\L10n\Locale')->getLocale(LC_ALL);
 207       * </code>
 208       *
 209       * @param  int $category The localisation category
 210       * @return mixed
 211       */
 212  
 213      public function getLocale($category = LC_ALL)
 214      {
 215          return @setlocale($category, 0);
 216      }
 217  
 218      /**
 219       * Gets a locale identifier for the given language code.
 220       *
 221       * This method takes an IETF language code and returns a locale for it that
 222       * works on the current system.
 223       *
 224       * The following returns 'en_GB.UTF-8':
 225       *
 226       * <code>
 227       * echo Txp::get('\Textpattern\L10n\Locale')->getLanguageLocale('en-GB');
 228       * </code>
 229       *
 230       * Returns the default locale name if the system doesn't have anything
 231       * more appropriate.
 232       *
 233       * @param  string $language The language
 234       * @return string|bool Locale code, or FALSE on error
 235       */
 236  
 237      public function getLanguageLocale($language)
 238      {
 239          $locale = false;
 240  
 241          if ($original = $this->getLocale(LC_TIME)) {
 242              $locale = $this->setLocale(LC_TIME, $language)->getLocale(LC_TIME);
 243              $this->setLocale(LC_TIME, $original);
 244          }
 245  
 246          return $locale;
 247      }
 248  
 249      /**
 250       * Gets a language code for the given locale identifier.
 251       *
 252       * This method supports various different formats used by different host
 253       * platform. These formats include IETF language tag, POSIX locale name and
 254       * language name in English.
 255       *
 256       * All these will return 'en-gb':
 257       *
 258       * <code>
 259       * echo Txp::get('\Textpattern\L10n\Locale')->getLocaleLanguage('en_GB.UTF-8');
 260       * echo Txp::get('\Textpattern\L10n\Locale')->getLocaleLanguage('en-gb');
 261       * echo Txp::get('\Textpattern\L10n\Locale')->getLocaleLanguage('english');
 262       * echo Txp::get('\Textpattern\L10n\Locale')->getLocaleLanguage('c');
 263       * echo Txp::get('\Textpattern\L10n\Locale')->getLocaleLanguage('English_UK.1252');
 264       * </code>
 265       *
 266       * If the specified locale isn't supported, FALSE will be returned.
 267       *
 268       * @param  string $locale The locale identifier
 269       * @return string|bool The language code, or FALSE on failure
 270       */
 271  
 272      public function getLocaleLanguage($locale)
 273      {
 274          $code = strtolower($locale);
 275  
 276          foreach ($this->locales as $lang => $data) {
 277              if ($lang === $code || in_array($code, array_map('strtolower', $data), true)) {
 278                  return $lang;
 279              }
 280          }
 281  
 282          if (strpos($locale, '.')) {
 283              return strtok($locale, '.');
 284          }
 285  
 286          return false;
 287      }
 288  
 289      /**
 290       * Gets the character set from the current locale.
 291       *
 292       * This method exports the character set from the current locale string as
 293       * returned by the OS.
 294       *
 295       * <code>
 296       * echo Txp::get('\Textpattern\L10n\Locale')->getCharset();
 297       * </code>
 298       *
 299       * @param  int $category The localisation category
 300       * @return string|bool The character set, or FALSE on failure
 301       */
 302  
 303      public function getCharset($category = LC_ALL, $default = false)
 304      {
 305          if (!($locale = $this->getLocale($category))) {
 306              $charset = false;
 307          } elseif (is_callable('nl_langinfo')) {
 308              $oldLocale = $this->getLocale(LC_CTYPE);
 309              $this->setLocale(LC_CTYPE, $locale);
 310              $charset = nl_langinfo(CODESET);
 311              $this->setLocale(LC_CTYPE, $oldLocale);
 312          } elseif (strpos($locale, '.')) {
 313              list($language, $charset) = explode('.', $locale);
 314  
 315              if (IS_WIN && is_numeric($charset)) {
 316                  $charset = 'Windows-'.$charset;
 317              }
 318          }
 319  
 320          return isset($charset) ? $charset : $default;
 321      }
 322  
 323      /**
 324       * Gets the language from the current locale.
 325       *
 326       * <code>
 327       * echo Txp::get('\Textpattern\L10n\Locale')->getLanguage();
 328       * </code>
 329       *
 330       * @param  int $category The localisation category
 331       * @return string|bool The language code, or FALSE on failure
 332       */
 333  
 334      public function getLanguage($category = LC_ALL)
 335      {
 336          if ($locale = $this->getLocale($category)) {
 337              if (($lang = $this->getLocaleLanguage($locale)) !== false) {
 338                  return $lang;
 339              }
 340          }
 341  
 342          return false;
 343      }
 344  
 345      /**
 346       * Gets locale identifiers mapped to the given language.
 347       *
 348       * Returns all locale identifiers that match the given language or locale
 349       * code. For instance providing 'en', will return both en-US and en-GB
 350       * locale identifiers.
 351       *
 352       * <code>
 353       * print_r(Txp::get('\Textpattern\L10n\Locale')->getLocaleIdentifiers('english'));
 354       * print_r(Txp::get('\Textpattern\L10n\Locale')->getLocaleIdentifiers('en'));
 355       * print_r(Txp::get('\Textpattern\L10n\Locale')->getLocaleIdentifiers('en-gb'));
 356       * </code>
 357       *
 358       * @param  string $locale The locale or language code
 359       * @return array|bool An array of identifiers, or FALSE if not supported
 360       */
 361  
 362      public function getLocaleIdentifiers($locale)
 363      {
 364          if (isset($this->locales[strtolower($locale)])) {
 365              return array_merge($this->locales[$locale], array($locale));
 366          }
 367  
 368          $code = strtolower($locale);
 369          $matches = array();
 370  
 371          foreach ($this->locales as $lang => $data) {
 372              if ($lang === $code || in_array($code, array_map('strtolower', $data), true)) {
 373                  $matches = array_merge($matches, $data, array($lang, $locale));
 374              }
 375          }
 376  
 377          if ($matches) {
 378              return array_unique($matches);
 379          }
 380  
 381          return false;
 382      }
 383  
 384      /**
 385       * Return valid locale, if possible.
 386       */
 387  
 388      public function validLocale($code)
 389      {
 390          $code = strtolower($code);
 391  
 392          if (empty($this->locales[$code])) {
 393              if (!empty($this->recodeLocale[$code])) {
 394                  $code = $this->recodeLocale[$code];
 395              } else {
 396                  // Fall back on trying partial match.
 397                  $codePart = explode('-', $code);
 398  
 399                  if (array_search($codePart[0], $this->recodeLocale)) {
 400                      $code = $codePart[0];
 401                  }
 402              }
 403          }
 404  
 405          return $code;
 406      }
 407  }

title

Description

title

Description

title

Description

title

title

Body