Textpattern | PHP Cross Reference | Content Management Systems |
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
Body
title
Description
Body
title
Description
Body
title
Body
title