Docker image / push (push) Canceled after 0s
包含:登录失败锁定、90天密码有效期、30分钟会话超时、 强制改密、登录审计日志、屏幕水印、企业背景图、 备案信息固定底部、favicon、登录页JS修复等全部改动
1277 lines
45 KiB
PHP
1277 lines
45 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
namespace MRBS;
|
|
|
|
use MRBS\Intl\Locale;
|
|
use ResourceBundle;
|
|
|
|
class System
|
|
{
|
|
public const BIG_ENDIAN = 0;
|
|
public const LITTLE_ENDIAN = 1;
|
|
|
|
|
|
/**
|
|
* A set of special cases where the default region is different from the language.
|
|
* (Normally the region is the same as the language, eg 'fr' => 'FR'.)
|
|
* @var array<string, string> $default_regions
|
|
*/
|
|
private static $default_regions = array(
|
|
'ca' => 'ES',
|
|
'cs' => 'CZ',
|
|
'da' => 'DK',
|
|
'el' => 'GR',
|
|
'en' => 'GB',
|
|
'et' => 'EE',
|
|
'eu' => 'ES',
|
|
'ja' => 'JP',
|
|
'ko' => 'KR',
|
|
'nb' => 'NO',
|
|
'nn' => 'NO',
|
|
'sh' => 'RS',
|
|
'sl' => 'SI',
|
|
'sr' => 'RS',
|
|
'sv' => 'SE',
|
|
'zh' => 'CN',
|
|
);
|
|
|
|
|
|
// A map is needed to convert from the HTTP language specifier to a
|
|
// locale specifier for Windows
|
|
//
|
|
// The ordering of this array is important as it is also used to map in the
|
|
// reverse direction, ie to convert a Windows style locale into an xx-yy style
|
|
// locale by finding the first occurrence of a value and then using the
|
|
// corresponding key.
|
|
//
|
|
// See https://www.iana.org/assignments/language-subtag-registry/language-subtag-registry
|
|
// for language tags.
|
|
//
|
|
// Windows locales can be found by running the following PHP code on a Windows system
|
|
/*
|
|
foreach (range('a', 'z') as $a)
|
|
{
|
|
foreach (range('a', 'z') as $b)
|
|
{
|
|
foreach (range('a', 'z') as $c)
|
|
{
|
|
$locale = "$a$b$c";
|
|
setlocale(LC_ALL, $locale);
|
|
$result = setlocale(LC_ALL, '0');
|
|
if ($locale != $result)
|
|
{
|
|
echo "$locale: $result\n";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
*/
|
|
private static $lang_map_windows = array(
|
|
'aa-et' => 'zzz', // Afar_Ethiopia.utf8
|
|
'af-za' => 'afk', // Afrikaans_South Africa.1252
|
|
'am' => 'amh', // Amharic.utf8
|
|
'am-et' => 'amh', // Amharic_Ethiopia.utf8
|
|
'ar-ae' => 'aru', // Arabic_United Arab Emirates.1256
|
|
'ar-bh' => 'arh', // Arabic_Bahrain.1256
|
|
'ar-dz' => 'arg', // Arabic_Algeria.1256
|
|
'ar-eg' => 'are', // Arabic_Egypt.1256
|
|
'ar-iq' => 'ari', // Arabic_Iraq.1256
|
|
'ar-jo' => 'arj', // Arabic_Jordan.1256
|
|
'ar-kw' => 'ark', // Arabic_Kuwait.1256
|
|
'ar-lb' => 'arb', // Arabic_Lebanon.1256
|
|
'ar-ly' => 'arl', // Arabic_Libya.1256
|
|
'ar-ma' => 'arm', // Arabic_Morocco.1256
|
|
'ar-om' => 'aro', // Arabic_Oman.1256
|
|
'ar-qa' => 'arq', // Arabic_Qatar.1256
|
|
'ar-sa' => 'ara', // Arabic_Saudi Arabia.1256
|
|
'ar-sy' => 'ars', // Arabic_Syria.1256
|
|
'ar-tn' => 'art', // Arabic_Tunisia.1256
|
|
'ar-ye' => 'ary', // Arabic_Yemen.1256
|
|
'arn' => 'mpd', // Mapudungun.1252
|
|
'arn-cl' => 'mpd', // Mapudungun_Chile.1252
|
|
'as-in' => 'asm', // Assamese_India.utf8
|
|
'az' => 'aze', // Azerbaijani.1254
|
|
'az-cyrl' => 'azc', // Azerbaijani (Cyrillic).1251
|
|
'az-cyrl-az' => 'azc', // Azerbaijani (Cyrillic)_Azerbaijan.1251
|
|
'az-latn' => 'aze', // Azerbaijani.1254
|
|
'az-latn-az' => 'aze', // Azerbaijani_Azerbaijan.1254
|
|
'ba-ru' => 'bas', // Bashkir_Russia.1251
|
|
'bg' => 'bgr', // Bulgarian.1251
|
|
'bg-bg' => 'bgr', // Bulgarian_Bulgaria.1251
|
|
'be' => 'bel', // Belarusian.1251
|
|
'be-by' => 'bel', // Belarusian_Belarus.1251
|
|
'bn-bd' => 'bnb', // Bangla_Bangladesh.utf8
|
|
'bn-in' => 'bng', // Bengali_India.utf8
|
|
'bo-cn' => 'bob', // Tibetan_China.utf8
|
|
'br-fr' => 'bre', // Breton_France.1252
|
|
'bs' => 'bsb', // Bosnian.1250'
|
|
'bs-cyrl' => 'bsc', // Bosnian (Cyrillic).1251
|
|
'bs-cyrl-ba' => 'bsc', // Bosnian (Cyrillic)_Bosnia and Herzegovina.1251
|
|
'bs-latn' => 'bsb', // Bosnian_Bosnia and Herzegovina.1250'
|
|
'bs-latn-ba' => 'bsb', // Bosnian_Bosnia and Herzegovina.1250
|
|
'ca' => 'cat', // Catalan_Spain.1252
|
|
'ca-es' => 'cat', // Catalan_Spain.1252
|
|
'chr-us' => 'cre', // Cherokee_United States.utf8
|
|
'ckb-iq' => 'kur', // Central Kurdish_Iraq.1256
|
|
'co' => 'cos', // Corsican.1252
|
|
'co-fr' => 'cos', // Corsican_France.1252
|
|
'cs' => 'csy', // Czech.1250
|
|
'cs-cz' => 'csy', // Czech_Czechia.1250
|
|
'cy' => 'cym', // Welsh.1252'
|
|
'cy-gb' => 'cym', // Welsh_United Kingdom.1252'
|
|
'da' => 'dan', // Danish.1252
|
|
'da-dk' => 'dan', // Danish_Denmark.1252
|
|
'de' => 'deu', // German.1252
|
|
'de-at' => 'dea', // German_Austria.1252
|
|
'de-ch' => 'des', // German_Switzerland.1252
|
|
'de-de' => 'deu', // German_Germany.1252
|
|
'de-li' => 'dec', // German_Liechtenstein.1252
|
|
'de-lu' => 'del', // German_Luxembourg.1252
|
|
'dsb-de' => 'dsb', // Lower Sorbian_Germany.1252
|
|
'dv-mv' => 'div', // Divehi_Maldives.utf8'
|
|
'el' => 'ell', // Greek.1253
|
|
'el-gr' => 'ell', // Greek_Greece.1253
|
|
'en' => 'enu', // English.1252
|
|
'en-au' => 'ena', // English_Australia.1252
|
|
'en-bz' => 'enl', // English_Belize.1252
|
|
'en-ca' => 'enc', // English_Canada.1252
|
|
'en-cb' => 'enb', // English_Caribbean.1252
|
|
'en-gb' => 'eng', // English_United Kingdom.1252
|
|
'en-hk' => 'enh', // English_Hong Kong SAR.1252
|
|
'en-ie' => 'eni', // English_Ireland.1252
|
|
'en-in' => 'enn', // English_India.1252
|
|
'en-jm' => 'enj', // English_Jamaica.1252
|
|
'en-my' => 'enm', // English_Malaysia.1252
|
|
'en-nz' => 'enz', // English_New Zealand.1252
|
|
'en-ph' => 'enp', // English_Philippines.1252
|
|
'en-sg' => 'ene', // English_Singapore.1252
|
|
'en-tt' => 'ent', // English_Trinidad and Tobago.1252
|
|
'en-us' => 'enu', // English_United States.1252
|
|
'en-za' => 'ens', // English_South Africa.1252
|
|
'en-zw' => 'enw', // English_Zimbabwe.1252
|
|
'es' => 'esp', // Spanish_Spain.1252
|
|
'es-419' => 'esj', // Spanish_Latin America.1252
|
|
'es-ar' => 'ess', // Spanish_Argentina.1252
|
|
'es-bo' => 'esb', // Spanish_Bolivia.1252
|
|
'es-cl' => 'esl', // Spanish_Chile.1252
|
|
'es-co' => 'eso', // Spanish_Colombia.1252
|
|
'es-cr' => 'esc', // Spanish_Costa Rica.1252
|
|
'es-cu' => 'esk', // Spanish_Cuba.1252
|
|
'es-do' => 'esd', // Spanish_Dominican Republic.1252
|
|
'es-ec' => 'esf', // Spanish_Ecuador.1252
|
|
'es-es' => 'esn', // Spanish_Spain.1252
|
|
'es-gt' => 'esg', // Spanish_Guatemala.1252
|
|
'es-hn' => 'esh', // Spanish_Honduras.1252
|
|
'es-mx' => 'esm', // Spanish_Mexico.1252
|
|
'es-ni' => 'esi', // Spanish_Nicaragua.1252
|
|
'es-pa' => 'esa', // Spanish_Panama.1252
|
|
'es-pe' => 'esr', // Spanish_Peru.1252
|
|
'es-pr' => 'esu', // Spanish_Puerto Rico.1252
|
|
'es-py' => 'esz', // Spanish_Paraguay.1252
|
|
'es-sv' => 'ese', // Spanish_El Salvador.1252
|
|
'es-us' => 'est', // Spanish_United States.1252
|
|
'es-uy' => 'esy', // Spanish_Uruguay.1252
|
|
'es-ve' => 'esv', // Spanish_Venezuela.1252
|
|
'et' => 'eti', // Estonian.1257
|
|
'et-ee' => 'eti', // Estonian_Estonia.1257
|
|
'eu' => 'euq', // Basque.1252
|
|
'eu-es' => 'euq', // Basque_Spain.1252
|
|
'fa' => 'far', // Persian.1256
|
|
'fa-ir' => 'far', // Persian_Iran.1256
|
|
'ff-latn-sn' => 'ful', // Fulah (Latin)_Senegal.1252
|
|
'fi' => 'fin', // Finnish.1252
|
|
'fi-fi' => 'fin', // Finnish_Finland.1252
|
|
'fil' => 'fpo', // Filipino.1252
|
|
'fil-ph' => 'fpo', // Filipino_Philippines.1252
|
|
'fo' => 'fos', // Faroese.1252
|
|
'fo-fo' => 'fos', // Faroese_Faroe Islands.1252
|
|
'fr' => 'fra', // French.1252
|
|
'fr-be' => 'frb', // French_Belgium.1252
|
|
'fr-ca' => 'frc', // French_Canada.1252
|
|
'fr-cg' => 'frd', // French_Congo (DRC).1252
|
|
'fr-ch' => 'frs', // French_Switzerland.1252
|
|
'fr-ci' => 'fri', // French_Côte d'Ivoire.1252
|
|
'fr-cm' => 'fre', // French_Cameroon.1252
|
|
'fr-fr' => 'fra', // French_France.1252
|
|
'fr-ht' => 'frh', // French_Haiti.1252
|
|
'fr-lu' => 'frl', // French_Luxembourg.1252
|
|
'fr-ma' => 'fro', // French_Morocco.1252
|
|
'fr-mc' => 'frm', // French_Monaco.1252
|
|
'fr-ml' => 'frf', // French_Mali.1252
|
|
'fr-re' => 'frr', // French_Réunion.1252'
|
|
'fr-sn' => 'frn', // French_Senegal.1252
|
|
'fy' => 'fyn', // Western Frisian.1252
|
|
'fy-nl' => 'fyn', // Western Frisian_Netherlands.1252
|
|
'ga' => 'ire', // Irish.1252
|
|
'ga-ie' => 'ire', // Irish_Ireland.1252
|
|
'gaz-et' => 'orm', // Oromo_Ethiopia.utf8
|
|
'gd' => 'gla', // Scottish Gaelic.1252
|
|
'gd-gb' => 'gla', // Scottish Gaelic_United Kingdom.1252
|
|
'gl' => 'glc', // Galician.1252
|
|
'gl-es' => 'glc', // Galician_Spain.1252
|
|
'gn-py' => 'grn', // Guarani_Paraguay.1252
|
|
'gsw-fr' => 'gsw', // Alsatian_France.1252
|
|
'gu' => 'guj', // Gujarati.utf8
|
|
'gu-in' => 'guj', // Gujarati_India.utf8
|
|
'ha' => 'hau', // Hausa.1252
|
|
'ha-latn' => 'hau', // Hausa.1252'
|
|
'ha-latn-ng' => 'hau', // Hausa_Nigeria.1252
|
|
'haw-us' => 'haw', // Hawaiian_United States.1252
|
|
'he' => 'heb', // Hebrew_Israel.1255
|
|
'he-il' => 'heb', // Hebrew_Israel.1255
|
|
'hi' => 'hin', // Hindi.utf8
|
|
'hi-in' => 'hin', // Hindi_India.utf8
|
|
'hr' => 'hrv', // Croatian.1250
|
|
'hr-ba' => 'hrb', // Croatian_Bosnia and Herzegovina.1250
|
|
'hr-hr' => 'hrv', // Croatian_Croatia.1250
|
|
'hsb' => 'hsb', // Upper Sorbian.1252
|
|
'hsb-de' => 'hsb', // Upper Sorbian_Germany.1252
|
|
'hu' => 'hun', // Hungarian.1250
|
|
'hu-hu' => 'hun', // Hungarian_Hungary.1250
|
|
'hy' => 'hye', // Armenian.utf8
|
|
'hy-am' => 'hye', // Armenian_Armenia.utf8
|
|
'id' => 'ind', // Indonesian.1252
|
|
'id-id' => 'ind', // Indonesian_Indonesia.1252
|
|
'ig' => 'ibo', // Igbo.1252
|
|
'ig-ng' => 'ibo', // Igbo_Nigeria.1252
|
|
'ii' => 'iii', // Yi.utf8
|
|
'ii-cn' => 'iii', // Yi_China.utf8
|
|
'is' => 'isl', // Icelandic.1252
|
|
'is-is' => 'isl', // Icelandic_Iceland.1252
|
|
'it' => 'ita', // Italian.1252
|
|
'it-ch' => 'its', // Italian_Switzerland.1252
|
|
'it-it' => 'ita', // Italian_Italy.1252
|
|
'iu' => 'iuk', // Inuktitut.1252
|
|
'iu-cans-ca' => 'ius', // Inuktitut (Syllabics)_Canada.utf8
|
|
'iu-latn' => 'iuk', // Inuktitut.1252
|
|
'iu-latn-ca' => 'iuk', // Inuktitut_Canada.1252
|
|
'ja' => 'jpn', // Japanese.932
|
|
'ja-jp' => 'jpn', // Japanese_Japan.932
|
|
'jv-id' => 'jav', // Javanese_Indonesia.1252
|
|
'ka' => 'kat', // Georgian.utf8
|
|
'ka-ge' => 'kat', // Georgian_Georgia.utf8
|
|
'kk' => 'kkz', // Kazakh.utf8
|
|
'kk-kz' => 'kkz', // Kazakh_Kazakhstan.utf8
|
|
'kl-gl' => 'kal', // Greenlandic_Greenland.1252
|
|
'km' => 'khm', // Khmer.utf8
|
|
'km-kh' => 'khm', // Khmer_Cambodia.utf8
|
|
'kn' => 'kdi', // Kannada.utf8
|
|
'kn-in' => 'kdi', // Kannada_India.utf8
|
|
'ko' => 'kor', // Korean.949
|
|
'ko-kr' => 'kor', // Korean_Korea.949
|
|
'kok' => 'knk', // Konkani.utf8
|
|
'kok-in' => 'knk', // Konkani_India.utf8
|
|
'ky-kg' => 'kyr', // Kyrgyz_Kyrgyzstan.1251
|
|
'lb' => 'lbx', // Luxembourgish.1252
|
|
'lb-lu' => 'lbx', // Luxembourgish_Luxembourg.1252
|
|
'lo' => 'lao', // Lao.utf8
|
|
'lo-la' => 'lao', // Lao_Laos.utf8
|
|
'lt' => 'lth', // Lithuanian.1257
|
|
'lt-lt' => 'lth', // Lithuanian_Lithuania.1257
|
|
'lv' => 'lvi', // Latvian.1257
|
|
'lv-lv' => 'lvi', // Latvian_Latvia.1257
|
|
'mk' => 'mki', // Macedonian.1251
|
|
'mk-mk' => 'mki', // Macedonian_North Macedonia.1251
|
|
'mi' => 'mri', // Maori.utf8
|
|
'mi-nz' => 'mri', // Maori_New Zealand.utf8
|
|
'ml' => 'mym', // Malayalam.utf8
|
|
'ml-in' => 'mym', // Malayalam_India.utf8
|
|
'mn' => 'mnn', // Mongolian_Mongolia.1251
|
|
'mn-mn' => 'mnn', // Mongolian_Mongolia.1251
|
|
'mn-mong-cn' => 'mng', // Mongolian (Traditional Mongolian)_China.utf8
|
|
'mn-mong-mn' => 'mnm', // Mongolian (Traditional Mongolian)_Mongolia.utf8
|
|
'moh' => 'mwk', // Mohawk.1252
|
|
'moh-ca' => 'mwk', // Mohawk_Canada.1252
|
|
'mr' => 'mar', // Marathi.utf8
|
|
'mr-in' => 'mar', // Marathi_India.utf8
|
|
'ms' => 'msl', // Malay.1252
|
|
'ms-bn' => 'msb', // Malay_Brunei.1252
|
|
'ms-my' => 'msl', // Malay_Malaysia.1252
|
|
'mt' => 'mlt', // Maltese.utf8
|
|
'mt-mt' => 'mlt', // Maltese_Malta.utf8
|
|
'my' => 'mya', // Burmese.utf8
|
|
'my-mm' => 'mya', // Burmese_Myanmar.utf8
|
|
'nb' => 'nor', // Norwegian Bokmål.1252
|
|
'nb-no' => 'nor', // Norwegian Bokmål_Norway.1252
|
|
'ne-in' => 'nei', // Nepali_India.utf8
|
|
'ne-np' => 'nep', // Nepali_Nepal.utf8
|
|
'ngo-gn' => 'nqo', // N'ko_Guinea.utf8
|
|
'nl' => 'nld', // Dutch.1252
|
|
'nl-be' => 'nlb', // Dutch_Belgium.1252
|
|
'nl-nl' => 'nld', // Dutch_Netherlands.1252
|
|
'nn' => 'non', // Norwegian Nynorsk.1252
|
|
'nn-no' => 'non', // Norwegian Nynorsk_Norway.1252
|
|
'no' => 'nor', // Norwegian.1252
|
|
'no-no' => 'nor', // Norwegian_Norway.1252
|
|
'nso-za' => 'nso', // Sesotho sa Leboa_South Africa.1252
|
|
'oc' => 'oci', // Occitan.1252
|
|
'oc-fr' => 'oci', // Occitan_France.1252
|
|
'ory-in' => 'ori', // Odia_India.utf8
|
|
'pa-in' => 'pan', // Punjabi_India.utf8
|
|
'pa-pk' => 'pap', // Punjabi_Pakistan.1256
|
|
'pl' => 'plk', // Polish.1250
|
|
'pl-pl' => 'plk', // Polish_Poland.1250
|
|
'plt-mg' => 'mlg', // Malagasy_Madagascar.utf8
|
|
'prs' => 'prs', // Dari.1256
|
|
'prs-af' => 'prs', // Dari_Afghanistan.1256
|
|
'ps-af' => 'pas', // Pashto_Afghanistan.utf8
|
|
'pt' => 'ptb', // Portuguese.1252
|
|
'pt-ao' => 'pta', // Portuguese_Angola.1252
|
|
'pt-br' => 'ptb', // Portuguese_Brazil.1252
|
|
'pt-pt' => 'ptg', // Portuguese_Portugal.1252
|
|
'quc-gt' => 'qut', // K'iche'_Guatemala.1252
|
|
'quz-bo' => 'qub', // Quechua_Bolivia.1252
|
|
'quz-ec' => 'que', // Quechua_Ecuador.1252
|
|
'quz-pe' => 'qup', // Quechua_Peru.1252
|
|
'rm-ch' => 'rmc', // Romansh_Switzerland.1252
|
|
'ro' => 'rom', // Romanian.1250
|
|
'ro-md' => 'rod', // Romanian_Moldova.1250
|
|
'ro-ro' => 'rom', // Romanian_Romania.1250
|
|
'ru' => 'rus', // Russian.1251
|
|
'ru-md' => 'rum', // Russian_Moldova.1251
|
|
'ru-ru' => 'rus', // Russian_Russia.1251
|
|
'rw' => 'kin', // Kinyarwanda.1252
|
|
'rw-rw' => 'kin', // Kinyarwanda_Rwanda.1252
|
|
'sa' => 'san', // Sanskrit.utf8
|
|
'sa-in' => 'san', // Sanskrit_India.utf8
|
|
'sah-ru' => 'sah', // Sakha_Russia.1251
|
|
'sd-pk' => 'sip', // Sindhi_Pakistan.1256
|
|
'se-fi' => 'smg', // Sami (Northern)_Finland.1252
|
|
'se-no' => 'sme', // Northern Sami_Norway.1252
|
|
'se-se' => 'smf', // Sami (Northern)_Sweden.1252
|
|
'si' => 'sin', // Sinhala.utf8
|
|
'si-lk' => 'sin', // Sinhala_Sri Lanka.utf8
|
|
'sk' => 'sky', // Slovak.1250
|
|
'sk-sk' => 'sky', // Slovak_Slovakia.1250
|
|
'sl' => 'slv', // Slovenian.1250
|
|
'sl-si' => 'slv', // Slovenian_Slovenia.1250
|
|
'sma-no' => 'sma', // Sami (Southern)_Norway.1252
|
|
'sma-se' => 'smb', // Sami (Southern)_Sweden.1252
|
|
'smj-no' => 'smj', // Sami (Lule)_Norway.1252
|
|
'smj-se' => 'smk', // Sami (Lule)_Sweden.1252
|
|
'smn-fi' => 'smn', // Sami (Inari)_Finland.1252
|
|
'sms-fi' => 'sms', // Sami (Skolt)_Finland.1252
|
|
'sn-zw' => 'sna', // Shona_Zimbabwe.utf8
|
|
'so-so' => 'som', // Somali_Somalia.utf8
|
|
'sq' => 'sqi', // Albanian.1250
|
|
'sq-al' => 'sqi', // Albanian_Albania.1250
|
|
'sr' => 'srl', // Serbian.1250
|
|
'sr-cyrl-ba' => 'srn', // Serbian (Cyrillic)_Bosnia and Herzegovina.1251
|
|
'sr-cyrl-me' => 'srq', // Serbian (Cyrillic)_Montenegro.1251
|
|
'sr-cyrl-rs' => 'sro', // Serbian (Cyrillic)_Serbia.1251
|
|
'sr-latn-ba' => 'srs', // Serbian (Latin)_Bosnia and Herzegovina.1250
|
|
'sr-latn-me' => 'srp', // Serbian (Latin)_Montenegro.1250
|
|
'sr-latn-rs' => 'srm', // Serbian (Latin)_Serbia.1250
|
|
'sr-hr' => 'srb', // Serbian_Croatia.1250
|
|
'sr-rs' => 'srb', // Serbian_Serbia.1250
|
|
'st-za' => 'sot', // Sesotho_South Africa.utf8
|
|
'sv' => 'sve', // Swedish.1252
|
|
'sv-fi' => 'svf', // Swedish_Finland.1252
|
|
'sv-se' => 'sve', // Swedish_Sweden.1252
|
|
'sw' => 'swk', // Kiswahili.1252
|
|
'sw-ke' => 'swk', // Kiswahili_Kenya.1252
|
|
'syr' => 'syr', // Syriac.utf8
|
|
'syr-sy' => 'syr', // Syriac_Syria.utf8
|
|
'ta-in' => 'tai', // Tamil_India.utf8
|
|
'ta-lk' => 'tam', // Tamil_Sri Lanka.utf8
|
|
'te-in' => 'tel', // Telugu_India.utf8
|
|
'tg-cyrl-tj' => 'taj', // Tajik_Tajikistan.1251
|
|
'th' => 'tha', // Thai.874
|
|
'th-th' => 'tha', // Thai_Thailand.874
|
|
'ti-er' => 'tir', // Tigrinya_Eritrea.utf8
|
|
'ti-et' => 'tie', // Tigrinya_Ethiopia.utf8
|
|
'tk' => 'tuk', // Turkmen_Turkmenistan.1250
|
|
'tk-tm' => 'tuk', // Turkmen_Turkmenistan.1250
|
|
'tn-bw' => 'tsb', // Setswana_Botswana.1252
|
|
'tn-za' => 'tsn', // Setswana_South Africa.1252
|
|
'tr' => 'trk', // Turkish.1254
|
|
'tr-tr' => 'trk', // Turkish_Turkey.1254
|
|
'ts-za' => 'tso', // Tsonga_South Africa.utf8
|
|
'tt' => 'ttt', // Tatar.1251
|
|
'tt-ru' => 'ttt', // Tatar_Russia.1251
|
|
'tzm-latn-dz' => 'tza', // Central Atlas Tamazight_Algeria.1252
|
|
'tzm-latn-ma' => 'tzm', // Central Atlas Tamazight (Tifinagh)_Morocco.utf8
|
|
'ug' => 'uig', // Uyghur.1256
|
|
'ug-cn' => 'uig', // Uyghur_China.1256
|
|
'uk' => 'ukr', // Ukrainian.1251
|
|
'uk-ua' => 'ukr', // Ukrainian_Ukraine.1251
|
|
'ur-in' => 'uri', // Urdu_India.1256
|
|
'ur-pk' => 'urd', // Urdu_Pakistan.1256
|
|
'uz-cyrl' => 'uzc', // Uzbek (Cyrillic).1251
|
|
'uz-cyrl-uz' => 'uzc', // Uzbek (Cyrillic)_Uzbekistan.1251
|
|
'uz-latn' => 'uzb', // Uzbek.1254
|
|
'uz-latn-uz' => 'uzb', // Uzbek_Uzbekistan.1254
|
|
'vi' => 'vit', // Vietnamese.1258
|
|
'vi-vn' => 'vit', // Vietnamese_Vietnam.1258
|
|
'wo' => 'wol', // Wolof.1252
|
|
'wo-sn' => 'wol', // Wolof_Senegal.1252
|
|
'xh' => 'xho', // isiXhosa.1252
|
|
'xh-za' => 'xho', // isiXhosa_South Africa.1252
|
|
'yo' => 'yor', // Yoruba.1252
|
|
'yo-ng' => 'yor', // Yoruba_Nigeria.1252
|
|
'zgh-ma' => 'zhg', // Standard Moroccan Tamazight_Morocco.utf8
|
|
'zh' => 'chs', // Chinese.936
|
|
'zh-cn' => 'chs', // Chinese_China.936
|
|
'zh-hans-sg' => 'zhi', // Chinese (Simplified)_Singapore.936
|
|
'zh-hant-hk' => 'zhh', // Chinese (Traditional)_Hong Kong SAR.950
|
|
'zh-hant-mo' => 'zhm', // Chinese (Traditional)_Macao SAR.950
|
|
'zh-hk' => 'zhh', // Chinese_Hong Kong SAR.950
|
|
'zh-mo' => 'zhm', // Chinese_Macao SAR.950
|
|
'zh-sg' => 'zhi', // Chinese_Singapore.936
|
|
'zh-tw' => 'cht', // Chinese_Taiwan.950
|
|
'zu' => 'zul', // isiZulu.1252
|
|
'zu-za' => 'zul' // isiZulu_South Africa.1252
|
|
);
|
|
|
|
|
|
// This maps a Windows locale to the charset it uses, which are
|
|
// all Windows code pages
|
|
//
|
|
// The code pages used by these locales found at:
|
|
// http://msdn.microsoft.com/en-us/goglobal/bb896001.aspx
|
|
private static $winlocale_codepage_map = array(
|
|
'afk' => 'CP1252',
|
|
'amh' => 'utf-8',
|
|
'ara' => 'CP1256',
|
|
'arb' => 'CP1256',
|
|
'are' => 'CP1256',
|
|
'arg' => 'CP1256',
|
|
'arh' => 'CP1256',
|
|
'ari' => 'CP1256',
|
|
'arj' => 'CP1256',
|
|
'ark' => 'CP1256',
|
|
'arl' => 'CP1256',
|
|
'arm' => 'CP1256',
|
|
'aro' => 'CP1256',
|
|
'arq' => 'CP1256',
|
|
'ars' => 'CP1256',
|
|
'art' => 'CP1256',
|
|
'aru' => 'CP1256',
|
|
'ary' => 'CP1256',
|
|
'asm' => 'utf-8',
|
|
'azc' => 'CP1251',
|
|
'aze' => 'CP1254',
|
|
'bas' => 'CP1251',
|
|
'bel' => 'CP1251',
|
|
'bgr' => 'CP1251',
|
|
'bnb' => 'utf-8',
|
|
'bng' => 'utf-8',
|
|
'bob' => 'utf-8',
|
|
'bre' => 'CP1252',
|
|
'bsb' => 'CP1250',
|
|
'bsc' => 'CP1251',
|
|
'cat' => 'CP1252',
|
|
'chh' => 'CP950',
|
|
'chi' => 'CP936',
|
|
'chs' => 'CP936',
|
|
'cht' => 'CP950',
|
|
'cos' => 'CP1252',
|
|
'cre' => 'utf-8',
|
|
'csy' => 'CP1250',
|
|
'cym' => 'CP1252',
|
|
'dan' => 'CP1252',
|
|
'dea' => 'CP1252',
|
|
'dec' => 'CP1252',
|
|
'del' => 'CP1252',
|
|
'des' => 'CP1252',
|
|
'deu' => 'CP1252',
|
|
'div' => 'utf-8',
|
|
'dsb' => 'CP1252',
|
|
'ell' => 'CP1253',
|
|
'ena' => 'CP1252',
|
|
'enb' => 'CP1252',
|
|
'enc' => 'CP1252',
|
|
'ene' => 'CP1252',
|
|
'eng' => 'CP1252',
|
|
'enh' => 'CP1252',
|
|
'eni' => 'CP1252',
|
|
'enj' => 'CP1252',
|
|
'enl' => 'CP1252',
|
|
'enm' => 'CP1252',
|
|
'enn' => 'CP1252',
|
|
'enp' => 'CP1252',
|
|
'ens' => 'CP1252',
|
|
'ent' => 'CP1252',
|
|
'enu' => 'CP1252',
|
|
'enw' => 'CP1252',
|
|
'enz' => 'CP1252',
|
|
'esa' => 'CP1252',
|
|
'esb' => 'CP1252',
|
|
'esc' => 'CP1252',
|
|
'esd' => 'CP1252',
|
|
'ese' => 'CP1252',
|
|
'esf' => 'CP1252',
|
|
'esg' => 'CP1252',
|
|
'esh' => 'CP1252',
|
|
'esi' => 'CP1252',
|
|
'esj' => 'CP1252',
|
|
'esk' => 'CP1252',
|
|
'esl' => 'CP1252',
|
|
'esm' => 'CP1252',
|
|
'esn' => 'CP1252',
|
|
'eso' => 'CP1252',
|
|
'esp' => 'CP1252',
|
|
'esr' => 'CP1252',
|
|
'ess' => 'CP1252',
|
|
'est' => 'CP1252',
|
|
'esu' => 'CP1252',
|
|
'esv' => 'CP1252',
|
|
'esy' => 'CP1252',
|
|
'esz' => 'CP1252',
|
|
'euq' => 'CP1252',
|
|
'eti' => 'CP1257',
|
|
'far' => 'CP1256',
|
|
'fin' => 'CP1252',
|
|
'fos' => 'CP1252',
|
|
'fpo' => 'CP1252',
|
|
'fra' => 'CP1252',
|
|
'frb' => 'CP1252',
|
|
'frc' => 'CP1252',
|
|
'frd' => 'CP1252',
|
|
'fre' => 'CP1252',
|
|
'frf' => 'CP1252',
|
|
'frh' => 'CP1252',
|
|
'fri' => 'CP1252',
|
|
'frl' => 'CP1252',
|
|
'frm' => 'CP1252',
|
|
'frn' => 'CP1252',
|
|
'fro' => 'CP1252',
|
|
'frr' => 'CP1252',
|
|
'frs' => 'CP1252',
|
|
'ful' => 'CP1252',
|
|
'fyn' => 'CP1252',
|
|
'gla' => 'CP1252',
|
|
'glc' => 'CP1252',
|
|
'grn' => 'CP1252',
|
|
'gsw' => 'CP1252',
|
|
'guj' => 'utf-8',
|
|
'hau' => 'CP1252',
|
|
'haw' => 'CP1252',
|
|
'heb' => 'CP1255',
|
|
'hin' => 'utf-8',
|
|
'hrb' => 'CP1250',
|
|
'hrv' => 'CP1250',
|
|
'hsb' => 'CP1252',
|
|
'hun' => 'CP1250',
|
|
'hye' => 'utf-8',
|
|
'ibo' => 'CP1252',
|
|
'iii' => 'utf-8',
|
|
'ind' => 'CP1252',
|
|
'ire' => 'CP1252',
|
|
'isl' => 'CP1252',
|
|
'ita' => 'CP1252',
|
|
'its' => 'CP1252',
|
|
'ius' => 'utf-8',
|
|
'iuk' => 'CP1252',
|
|
'ivl' => 'CP1252', // Invariant Language_Invariant Country.1252
|
|
'jav' => 'CP1252',
|
|
'jpn' => 'CP932',
|
|
'kal' => 'CP1252',
|
|
'kat' => 'utf-8',
|
|
'kdi' => 'utf-8',
|
|
'khm' => 'utf-8',
|
|
'kin' => 'CP1252',
|
|
'kkz' => 'utf-8',
|
|
'knk' => 'utf-8',
|
|
'kor' => 'CP949',
|
|
'kur' => 'CP1256',
|
|
'kyr' => 'CP1251',
|
|
'lao' => 'utf-8',
|
|
'lbx' => 'CP1252',
|
|
'lth' => 'CP1257',
|
|
'lvi' => 'CP1257',
|
|
'mar' => 'utf-8',
|
|
'mki' => 'CP1251',
|
|
'mlg' => 'utf-8',
|
|
'mlt' => 'utf-8',
|
|
'mng' => 'utf-8',
|
|
'mnm' => 'utf-8',
|
|
'mnn' => 'CP1251',
|
|
'mon' => 'CP1251',
|
|
'mpd' => 'CP1252',
|
|
'mri' => 'utf-8',
|
|
'msb' => 'CP1252',
|
|
'msl' => 'CP1252',
|
|
'mwk' => 'CP1252',
|
|
'mya' => 'utf-8',
|
|
'mym' => 'utf-8',
|
|
'nei' => 'utf-8',
|
|
'nep' => 'utf-8',
|
|
'ngo' => 'utf-8',
|
|
'nlb' => 'CP1252',
|
|
'nld' => 'CP1252',
|
|
'non' => 'CP1252',
|
|
'nor' => 'CP1252',
|
|
'nso' => 'CP1252',
|
|
'oci' => 'CP1252',
|
|
'ori' => 'utf-8',
|
|
'orm' => 'utf-8',
|
|
'pan' => 'utf-8',
|
|
'pap' => 'CP1256',
|
|
'pas' => 'utf-8',
|
|
'plk' => 'CP1250',
|
|
'prs' => 'CP1256',
|
|
'pta' => 'CP1252',
|
|
'ptb' => 'CP1252',
|
|
'ptg' => 'CP1252',
|
|
'qub' => 'CP1252',
|
|
'que' => 'CP1252',
|
|
'qup' => 'CP1252',
|
|
'qut' => 'CP1252',
|
|
'rmc' => 'CP1252',
|
|
'rod' => 'CP1250',
|
|
'rom' => 'CP1250',
|
|
'rum' => 'CP1251',
|
|
'rus' => 'CP1251',
|
|
'sah' => 'CP1251',
|
|
'san' => 'utf-8',
|
|
'sin' => 'utf-8',
|
|
'sip' => 'CP1256',
|
|
'sky' => 'CP1250',
|
|
'slv' => 'CP1250',
|
|
'sma' => 'CP1252',
|
|
'smb' => 'CP1252',
|
|
'sme' => 'CP1252',
|
|
'smf' => 'CP1252',
|
|
'smg' => 'CP1252',
|
|
'smj' => 'CP1252',
|
|
'smk' => 'CP1252',
|
|
'smn' => 'CP1252',
|
|
'sms' => 'CP1252',
|
|
'sna' => 'utf-8',
|
|
'som' => 'utf-8',
|
|
'sot' => 'utf-8',
|
|
'sqi' => 'CP1250',
|
|
'srb' => 'CP1250',
|
|
'srl' => 'CP1250',
|
|
'srm' => 'CP1250',
|
|
'srn' => 'CP1251',
|
|
'sro' => 'CP1251',
|
|
'srp' => 'CP1250',
|
|
'srq' => 'CP1251',
|
|
'srs' => 'CP1250',
|
|
'sve' => 'CP1252',
|
|
'svf' => 'CP1252',
|
|
'swk' => 'CP1252',
|
|
'syr' => 'utf-8',
|
|
'tai' => 'utf-8',
|
|
'tam' => 'utf-8',
|
|
'taj' => 'CP1251',
|
|
'tel' => 'utf-8',
|
|
'tha' => 'CP874',
|
|
'tie' => 'utf-8',
|
|
'tir' => 'utf-8',
|
|
'trk' => 'CP1254',
|
|
'tsb' => 'CP1252',
|
|
'tsn' => 'CP1252',
|
|
'tso' => 'utf-8',
|
|
'ttt' => 'CP1251',
|
|
'tuk' => 'CP1250',
|
|
'tza' => 'CP1252',
|
|
'tzm' => 'utf-8',
|
|
'uig' => 'CP1256',
|
|
'ukr' => 'CP1251',
|
|
'urb' => 'CP1256',
|
|
'urd' => 'CP1256',
|
|
'uri' => 'CP1256',
|
|
'usa' => 'CP1252',
|
|
'uzb' => 'CP1254',
|
|
'uzc' => 'CP1251',
|
|
'val' => 'CP1252',
|
|
'vit' => 'CP1258',
|
|
'wol' => 'CP1252',
|
|
'xho' => 'CP1252',
|
|
'yor' => 'CP1252',
|
|
'zhg' => 'utf-8',
|
|
'zhh' => 'CP950',
|
|
'zhi' => 'CP936',
|
|
'zhm' => 'CP950',
|
|
'zul' => 'CP1252',
|
|
'zzz' => 'utf-8'
|
|
);
|
|
|
|
|
|
// IBM AIX locale to code set table
|
|
// See http://publibn.boulder.ibm.com/doc_link/en_US/a_doc_lib/libs/basetrf2/setlocale.htm
|
|
private static $aixlocale_codepage_map = array(
|
|
'Ar_AA' => 'IBM-1046',
|
|
'ar_AA' => 'ISO8859-6',
|
|
'bg_BG' => 'ISO8856-5',
|
|
'cs_CZ' => 'ISO8859-2',
|
|
'Da_DK' => 'IBM-850',
|
|
'da_DK' => 'ISO8859-1',
|
|
'De_CH' => 'IBM-850',
|
|
'de_CH' => 'ISO8859-1',
|
|
'De_DE' => 'IBM-850',
|
|
'de_DE' => 'ISO8859-1',
|
|
'el_GR' => 'ISO8859-7',
|
|
'En_GB' => 'IBM-850',
|
|
'en_GB' => 'ISO8859-1',
|
|
'En_US' => 'IBM-850',
|
|
'en_US' => 'ISO8859-1',
|
|
'Es_ES' => 'IBM-850',
|
|
'es_ES' => 'ISO8859-1',
|
|
'Fi_FI' => 'IBM-850',
|
|
'fi_FI' => 'ISO8859-1',
|
|
'Fr_BE' => 'IBM-850',
|
|
'fr_BE' => 'ISO8859-1',
|
|
'Fr_CA' => 'IBM-850',
|
|
'fr_CA' => 'ISO8859-1',
|
|
'Fr_FR' => 'IBM-850',
|
|
'fr_FR' => 'ISO8859-1 ',
|
|
'Fr_CH' => 'IBM-850',
|
|
'fr_CH' => 'ISO8859-1',
|
|
'hr_HR' => 'ISO8859-2',
|
|
'hu_HU' => 'ISO8859-2',
|
|
'Is_IS' => 'IBM-850',
|
|
'is_IS' => 'ISO8859-1',
|
|
'It_IT' => 'IBM-850',
|
|
'it_IT' => 'ISO8859-1',
|
|
'Iw_IL' => 'IBM-856',
|
|
'iw_IL' => 'ISO8859-8',
|
|
'Ja_JP' => 'IBM-943',
|
|
'ko_KR' => 'IBM-eucKR',
|
|
'mk_MK' => 'ISO8859-5',
|
|
'Nl_BE' => 'IBM-850',
|
|
'nl_BE' => 'ISO8859-1',
|
|
'Nl_NL' => 'IBM-850',
|
|
'nl_NL' => 'ISO8859-1',
|
|
'No_NO' => 'IBM-850',
|
|
'no_NO' => 'ISO8859-1',
|
|
'pl_PL' => 'ISO8859-2',
|
|
'Pt_PT' => 'IBM-850',
|
|
'pt_PT' => 'ISO8859-1',
|
|
'ro_RO' => 'ISO8859-2',
|
|
'ru_RU' => 'ISO8859-5',
|
|
'sh_SP' => 'ISO8859-2',
|
|
'sl_SI' => 'ISO8859-2',
|
|
'sk_SK' => 'ISO8859-2',
|
|
'sr_SP' => 'ISO8859-5',
|
|
'Zh_CN' => 'GBK',
|
|
'Sv_SE' => 'IBM-850',
|
|
'sv_SE' => 'ISO8859-1',
|
|
'tr_TR' => 'ISO8859-9',
|
|
'zh_TW' => 'IBM-eucTW'
|
|
);
|
|
|
|
// IBM AIX libiconv UTF-8 converters
|
|
// See http://publibn.boulder.ibm.com/doc_link/en_US/a_doc_lib/aixprggd/genprogc/convert_prg.htm#HDRDNNRI49HOWA
|
|
private static $aix_utf8_converters = array(
|
|
'ISO8859-1',
|
|
'ISO8859-2',
|
|
'ISO8859-3',
|
|
'ISO8859-4',
|
|
'ISO8859-5',
|
|
'ISO8859-6',
|
|
'ISO8859-7',
|
|
'ISO8859-8',
|
|
'ISO8859-9',
|
|
'JISX0201.1976-0',
|
|
'JISX0208.1983-0',
|
|
'CNS11643.1986-1',
|
|
'CNS11643.1986-2',
|
|
'KSC5601.1987-0',
|
|
'IBM-eucCN',
|
|
'IBM-eucJP',
|
|
'IBM-eucKR',
|
|
'IBM-eucTW',
|
|
'IBM-udcJP',
|
|
'IBM-udcTW',
|
|
'IBM-sbdTW',
|
|
'UCS-2',
|
|
'IBM-437',
|
|
'IBM-850',
|
|
'IBM-852',
|
|
'IBM-857',
|
|
'IBM-860',
|
|
'IBM-861',
|
|
'IBM-863',
|
|
'IBM-865',
|
|
'IBM-869',
|
|
'IBM-921',
|
|
'IBM-922',
|
|
'IBM-932',
|
|
'IBM-943',
|
|
'IBM-934',
|
|
'IBM-935',
|
|
'IBM-936',
|
|
'IBM-938',
|
|
'IBM-942',
|
|
'IBM-944',
|
|
'IBM-946',
|
|
'IBM-948',
|
|
'IBM-1124',
|
|
'IBM-1129',
|
|
'TIS-620',
|
|
'IBM-037',
|
|
'IBM-273',
|
|
'IBM-277',
|
|
'IBM-278',
|
|
'IBM-280',
|
|
'IBM-284',
|
|
'IBM-285',
|
|
'IBM-297',
|
|
'IBM-500',
|
|
'IBM-875',
|
|
'IBM-930',
|
|
'IBM-933',
|
|
'IBM-937',
|
|
'IBM-939',
|
|
'IBM-1026',
|
|
'IBM-1112',
|
|
'IBM-1122',
|
|
'IBM-1124',
|
|
'IBM-1129',
|
|
'IBM-1381',
|
|
'GBK',
|
|
'TIS-620'
|
|
);
|
|
|
|
// GNU iconv code set to IBM AIX libiconv code set table
|
|
// Keys of this table should be in lowercase, and searches should be performed using lowercase!
|
|
private static $gnu_iconv_to_aix_iconv_codepage_map = array(
|
|
// "iso-8859-[1-9]" --> "ISO8859-[1-9]" according to http://publibn.boulder.ibm.com/doc_link/en_US/a_doc_lib/libs/basetrf2/setlocale.htm
|
|
'iso-8859-1' => 'ISO8859-1',
|
|
'iso-8859-2' => 'ISO8859-2',
|
|
'iso-8859-3' => 'ISO8859-3',
|
|
'iso-8859-4' => 'ISO8859-4',
|
|
'iso-8859-5' => 'ISO8859-5',
|
|
'iso-8859-6' => 'ISO8859-6',
|
|
'iso-8859-7' => 'ISO8859-7',
|
|
'iso-8859-8' => 'ISO8859-8',
|
|
'iso-8859-9' => 'ISO8859-9',
|
|
|
|
// "big5" --> "IBM-eucTW" according to http://kadesh.cepba.upc.es/mancpp/classref/ref/ITranscoder_DSC.htm
|
|
'big5' => 'IBM-eucTW',
|
|
|
|
// "big-5" --> "IBM-eucTW" (see above)
|
|
'big-5' => 'IBM-eucTW'
|
|
);
|
|
|
|
|
|
// Works out whether the machine architecture is little-endian or big-endian
|
|
public static function getEndianness() : int
|
|
{
|
|
static $result;
|
|
|
|
if (!isset($result))
|
|
{
|
|
$testint = 0x00FF;
|
|
$p = pack('S', $testint);
|
|
$result = ($testint===current(unpack('v', $p))) ? self::LITTLE_ENDIAN : self::BIG_ENDIAN;
|
|
}
|
|
|
|
return $result;
|
|
}
|
|
|
|
public static function getServerOS() : string
|
|
{
|
|
static $server_os = null;
|
|
|
|
if (!isset($server_os))
|
|
{
|
|
if (stristr(PHP_OS,'Darwin'))
|
|
{
|
|
$server_os = 'macosx';
|
|
}
|
|
elseif (stristr(PHP_OS, 'WIN'))
|
|
{
|
|
$server_os = 'windows';
|
|
}
|
|
elseif (stristr(PHP_OS, 'Linux'))
|
|
{
|
|
$server_os = 'linux';
|
|
}
|
|
elseif (stristr(PHP_OS, 'BSD'))
|
|
{
|
|
$server_os = 'bsd';
|
|
}
|
|
elseif (stristr(PHP_OS, 'SunOS'))
|
|
{
|
|
$server_os = 'sunos';
|
|
}
|
|
elseif (stristr(PHP_OS, 'AIX'))
|
|
{
|
|
$server_os = 'aix';
|
|
}
|
|
else
|
|
{
|
|
$server_os = 'unsupported';
|
|
}
|
|
}
|
|
|
|
return $server_os;
|
|
}
|
|
|
|
|
|
// Returns information about the operating system PHP is running on
|
|
public static function info() : string
|
|
{
|
|
// On some systems php_uname() is disabled for security reasons
|
|
if (in_array('php_uname', explode(',', ini_get('disable_functions'))))
|
|
{
|
|
return PHP_OS;
|
|
}
|
|
return php_uname();
|
|
}
|
|
|
|
|
|
/**
|
|
* Checks whether a language tag is advertised as being available on this system.
|
|
*
|
|
* Note that this method just tells us which locales are supported by the 'intl' extension. It
|
|
* doesn't tell us which locales are supported by setlocale(), which uses a different library.
|
|
*
|
|
* @return bool|null NULL if the advertised locales aren't known, otherwise TRUE/FALSE.
|
|
*/
|
|
private static function isAdvertisedLocale(string $langtag) : ?bool
|
|
{
|
|
if (!class_exists('\\ResourceBundle'))
|
|
{
|
|
// Could try using locale -a but that requires exec() which is not
|
|
// available on most systems.
|
|
// Could also use IntlCalendar::getAvailableLocales(), but that needs
|
|
// the intl extension, just like ResourceBundle::getLocales().
|
|
return null;
|
|
}
|
|
|
|
// Get the available locales
|
|
$locales = ResourceBundle::getLocales('');
|
|
// Put our locale into PHP's format
|
|
$locale = Locale::composeLocale(Locale::parseLocale($langtag));
|
|
// See whether our locale exists.
|
|
return in_array($locale, $locales);
|
|
}
|
|
|
|
|
|
/**
|
|
* Checks whether a language tag is available on this system.
|
|
*
|
|
* WARNING! This method is not reliable on Windows. It may return TRUE even if the locale is not
|
|
* available on the system.
|
|
*
|
|
* @param string $langtag A language tag in BCP 47 format.
|
|
*/
|
|
public static function isAvailableLocale(string $langtag) : bool
|
|
{
|
|
// If the OS is able to tell us whether the locale is supported, then
|
|
// use that answer.
|
|
if (null !== ($result = self::isAdvertisedLocale($langtag)))
|
|
{
|
|
return $result;
|
|
}
|
|
|
|
// Otherwise try setting the locale
|
|
return self::testLocale($langtag);
|
|
}
|
|
|
|
|
|
// Add a codeset suffix to $locale
|
|
private static function addCodeset(string $locale) : string
|
|
{
|
|
$server_os = self::getServerOS();
|
|
|
|
switch ($server_os)
|
|
{
|
|
case 'sunos':
|
|
case 'linux':
|
|
case 'bsd':
|
|
$codeset = '.UTF-8';
|
|
break;
|
|
|
|
case 'macosx':
|
|
case 'windows':
|
|
$codeset = '.utf-8';
|
|
break;
|
|
|
|
default:
|
|
$codeset = '';
|
|
break;
|
|
}
|
|
|
|
return $locale . $codeset;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns an array of locales in the correct format for the server OS given a BCP 47 language tag.
|
|
*
|
|
* There is an array of locales to try because some operating systems and versions accept locales
|
|
* with underscores, some with hyphens and some as special codes.
|
|
*
|
|
* @param string $langtag A BCP 47 language tag.
|
|
* @return string[]
|
|
*/
|
|
public static function getOSlocale(string $langtag) : array
|
|
{
|
|
static $locales = [];
|
|
|
|
if (!isset($locales[$langtag]))
|
|
{
|
|
$locales[$langtag] = self::getLocaleAlternatives($langtag);
|
|
|
|
// Add on a codeset [is this still necessary??]
|
|
$locales[$langtag] = array_map(self::class . '::addCodeset', $locales[$langtag]);;
|
|
}
|
|
|
|
return $locales[$langtag];
|
|
}
|
|
|
|
|
|
/**
|
|
* The inverse of getOSlocale(). Turns an OS-specific locale into a BCP 47 style language tag.
|
|
*
|
|
* This is provided for backwards compatibility with old versions of MRBS where the $override_locale
|
|
* config setting was required to be operating-system-specific (eg 'en_GB.utf-8' on Unix systems or
|
|
* 'eng' on Windows). Now $override_locale should be in BCP 47 format, but we accept old-style settings.
|
|
*/
|
|
public static function getBCPlocale(string $locale) : string
|
|
{
|
|
$result = $locale;
|
|
|
|
// Strip anything like '.utf-8' off the end
|
|
if (mb_strpos($result, '.') !== false)
|
|
{
|
|
$result = strstr($locale, '.', true);
|
|
}
|
|
|
|
// Convert an old-style Windows locale, eg 'eng' to a BCP 47 one, eg 'en-gb'
|
|
if ((self::getServerOS() == 'windows') && in_array($result, self::$lang_map_windows))
|
|
{
|
|
$result = array_search($result, self::$lang_map_windows);
|
|
}
|
|
|
|
// Parse it and then recompose it. This will get the capitalisation correct, eg
|
|
// "sr-Latn-RS". Note, though, that BCP 47 language tags are case-insensitive and
|
|
// the capitalisation is just a convention.
|
|
$result = Locale::composeLocale(Locale::parseLocale($result));
|
|
|
|
// Convert underscores to hyphens. Locale::composeLocale() returns underscores.
|
|
$result = str_replace('_', '-', $result);
|
|
|
|
return $result;
|
|
}
|
|
|
|
|
|
public static function utf8ConvertFromLocale(string $string, ?string $locale=null) : string
|
|
{
|
|
$server_os = self::getServerOS();
|
|
|
|
if ($server_os == 'windows')
|
|
{
|
|
if (!isset($locale))
|
|
{
|
|
$locale = Language::getInstance()->getWebLocale();
|
|
}
|
|
|
|
$locale = mb_strtolower($locale);
|
|
|
|
if (isset(self::$lang_map_windows[$locale]) &&
|
|
array_key_exists(self::$lang_map_windows[$locale], self::$winlocale_codepage_map))
|
|
{
|
|
$from_encoding = self::$winlocale_codepage_map[self::$lang_map_windows[$locale]];
|
|
$to_encoding = 'utf-8';
|
|
$string = iconv($from_encoding, $to_encoding, $string);
|
|
if ($string === false)
|
|
{
|
|
$message = "iconv() failed converting from '$from_encoding' to '$to_encoding'";
|
|
throw new Exception($message);
|
|
}
|
|
}
|
|
}
|
|
else if ($server_os == 'aix')
|
|
{
|
|
$string = self::utf8ConvertAix($string, $locale);
|
|
}
|
|
return $string;
|
|
}
|
|
|
|
|
|
private static function getLocaleAlternatives(string $langtag) : array
|
|
{
|
|
$locales = array();
|
|
|
|
// Put the $langtag into standard PHP format
|
|
$subtags = Locale::parseLocale($langtag);
|
|
|
|
if (!empty($subtags))
|
|
{
|
|
$locale = Locale::composeLocale($subtags);
|
|
|
|
// The first locale to try is one with hyphens instead of underscores. These work on newer
|
|
// Windows systems, whereas underscores do not. Also, on Windows systems, although
|
|
// setlocale will succeed with, for example, both 'en_GB' and 'en-GB', only 'en-GB' (and
|
|
// indeed 'eng') will give the date in the correct format when using strftime('%x').
|
|
$locales[] = str_replace('_', '-', $locale);
|
|
|
|
// The next locale to try is a PHP style locale, ie with underscores.
|
|
// Make sure we haven't already got it
|
|
if (!in_array($locale, $locales))
|
|
{
|
|
$locales[] = $locale;
|
|
}
|
|
|
|
if (self::getServerOS() == 'windows')
|
|
{
|
|
// Add in the three-letter code if any as a last resort
|
|
if (isset(self::$lang_map_windows[mb_strtolower($langtag)]))
|
|
{
|
|
$locales[] = self::$lang_map_windows[mb_strtolower($langtag)];
|
|
}
|
|
}
|
|
|
|
// If there isn't a region specified then add one, because on some systems
|
|
// setlocale(LC_ALL, 'en'), for example, doesn't work, even though 'en' seems
|
|
// to be an available locale.
|
|
if (!isset($subtags['region']))
|
|
{
|
|
$subtags['region'] = self::getDefaultRegion($subtags['language']);
|
|
if (isset($subtags['region'])) // avoid an infinite recursion
|
|
{
|
|
$locales = array_merge($locales, self::getLocaleAlternatives(Locale::composeLocale($subtags)));
|
|
}
|
|
}
|
|
|
|
// Finally, if there's a script subtag, then add a locale with the script removed. For example,
|
|
// setlocale() will usually fail with 'zh-Hant-TW' but succeed with 'zh-TW'.
|
|
if (isset($subtags['script']))
|
|
{
|
|
unset($subtags['script']);
|
|
$locales = array_merge($locales, self::getLocaleAlternatives(Locale::composeLocale($subtags)));
|
|
}
|
|
}
|
|
|
|
return $locales;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the default region for a language.
|
|
*/
|
|
private static function getDefaultRegion(string $language) : string
|
|
{
|
|
if (isset(self::$default_regions[$language]))
|
|
{
|
|
return self::$default_regions[$language];
|
|
}
|
|
|
|
return mb_strtoupper($language);
|
|
}
|
|
|
|
|
|
// AIX version of utf8_convert(); needed as locales won't give us UTF-8
|
|
// NOTE: Should ONLY be called with input encoded in the default code set of the current locale!
|
|
// NOTE: Uses the LC_TIME category for determining the current locale setting, so should preferably be used on date/time input only!
|
|
private static function utf8ConvertAix(string $string, ?string $aix_locale=null) : string
|
|
{
|
|
if (!isset($aix_locale))
|
|
{
|
|
// Retrieve current locale setting
|
|
$aix_locale = setlocale(LC_TIME, '0');
|
|
}
|
|
|
|
if ($aix_locale === false)
|
|
{
|
|
// Locale setting could not be retrieved; return string unchanged
|
|
return $string;
|
|
}
|
|
|
|
if (!array_key_exists($aix_locale, self::$aixlocale_codepage_map))
|
|
{
|
|
// The default code page of locale could not be found; return string unchanged
|
|
return $string;
|
|
}
|
|
|
|
$aix_codepage = self::$aixlocale_codepage_map[$aix_locale];
|
|
|
|
if (!in_array($aix_codepage, self::$aix_utf8_converters, true))
|
|
{
|
|
// No suitable UTF-8 converter was found for this code page; return string unchanged
|
|
return $string;
|
|
}
|
|
|
|
// Convert string to UTF-8
|
|
$from_encoding = $aix_codepage;
|
|
$to_encoding = 'UTF-8';
|
|
$string = iconv($from_encoding, $to_encoding, $string);
|
|
if ($string === false)
|
|
{
|
|
$message = "iconv() failed converting from '$from_encoding' to '$to_encoding'";
|
|
throw new Exception($message);
|
|
}
|
|
|
|
return $string;
|
|
}
|
|
|
|
|
|
// Translates a GNU libiconv character encoding name to its corresponding IBM AIX libiconv character
|
|
// encoding name. Returns FALSE if character encoding name is unknown.
|
|
private static function getAixCharacterEncoding(?string $character_encoding)
|
|
{
|
|
// Check arguments
|
|
if ($character_encoding == null ||
|
|
!is_string($character_encoding) ||
|
|
empty($character_encoding))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Convert character encoding name to lowercase
|
|
$character_encoding = mb_strtolower($character_encoding);
|
|
|
|
// Check that we know of an IBM AIX libiconv character encoding name equivalent for this character encoding name
|
|
if (!array_key_exists($character_encoding, self::$gnu_iconv_to_aix_iconv_codepage_map))
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return self::$gnu_iconv_to_aix_iconv_codepage_map[$character_encoding];
|
|
}
|
|
|
|
|
|
/**
|
|
* Tests whether a language tag can be set on this system. Preserves the current locale.
|
|
*
|
|
* WARNING! This method is unreliable on Windows systems where setlocale(LC_ALL, 'xyz.utf-8')
|
|
* will return 'xyz.utf-8', even though the locale doesn't exist.
|
|
*/
|
|
private static function testLocale(string $langtag) : bool
|
|
{
|
|
// TODO: Do something better for Windows systems? Could check whether the locale exists in
|
|
// TODO: a known list of locales. However, it's probably not worth it because (a) we will
|
|
// TODO: only ever use this method with locales that are supported by other MRBS components,
|
|
// TODO: and (b) we will eventually move to using the intl extension anyway.
|
|
|
|
// Save the original locales so that we can restore them later. Note that
|
|
// there could be different locales for different categories
|
|
$original_locales = explode(';', setlocale(LC_ALL, '0'));
|
|
|
|
// Try the test locale
|
|
$result = Language::setLocale(LC_ALL, $langtag);
|
|
|
|
// Restore the original settings
|
|
foreach ($original_locales as $locale_setting)
|
|
{
|
|
if (mb_strpos($locale_setting, '=') !== false)
|
|
{
|
|
list($category, $locale) = explode('=', $locale_setting);
|
|
// Need to turn the string back into a constant (sometimes PHP doesn't recognise a LC_ constant,
|
|
// so check that it has been defined first before turning it into a constant).
|
|
$category = (defined($category)) ? constant($category) : null;
|
|
}
|
|
else
|
|
{
|
|
$category = LC_ALL;
|
|
$locale = $locale_setting;
|
|
}
|
|
if (isset($category))
|
|
{
|
|
setlocale($category, $locale);
|
|
}
|
|
}
|
|
|
|
return ($result !== false);
|
|
}
|
|
|
|
}
|