silverstripe-framework/i18n/i18n.php
Sean Harvey 2b316e79e5 NEW Provide a consistent way of triggering flush
Provides an interface for classes to implement their own flush()
functionality. This function gets called early in a request on
all implementations of Flushable when flush=1|all is requested in the
URL.

This fix came out of an issue where Requirements combined files were not
being cleaned up after dev/build?flush=1, due to the fact that flush
would only occur when you called it while on a page that used those
combined files, but not in any other contexts. This will now call flush
on any implementors of Flushable regardless of the context of where
flush was called.
2014-08-22 09:24:27 +12:00

2646 lines
72 KiB
PHP
Raw Blame History

<?php
require_once 'Zend/Translate.php';
require_once 'i18nRailsYamlAdapter.php';
require_once 'i18nSSLegacyAdapter.php';
/**
* Base-class for storage and retrieval of translated entities.
*
* Please see the 'translatable' module for managing translations of database-content.
*
* <b>Usage</b>
*
* PHP:
* <code>
* _t('MyNamespace.MYENTITY', 'My default natural language value');
* _t('MyNamespace.MYENTITY', 'My default natural language value', 'My explanatory context');
* sprintf(_t('MyNamespace.MYENTITY', 'Counting %s things'), 42);
* </code>
*
* Templates:
* <code>
* <% _t('MyNamespace.MYENTITY', 'My default natural language value') %>
* <% sprintf(_t('MyNamespace.MYENTITY','Counting %s things'),$ThingsCount) %>
* </code>
*
* Javascript (see framework/javascript/i18n.js):
* <code>
* ss.i18n._t('MyEntity.MyNamespace','My default natural language value');
* </code>
*
* File-based i18n-translations always have a "locale" (e.g. 'en_US').
* Common language names (e.g. 'en') are mainly used in the 'translatable' module
* database-entities.
*
* <b>Text Collection</b>
*
* Features a "textcollector-mode" that parses all files with a certain extension
* (currently *.php and *.ss) for new translatable strings. Textcollector will write
* updated string-tables to their respective folders inside the module, and automatically
* namespace entities to the classes/templates they are found in (e.g. $lang['en_US']['AssetAdmin']['UPLOADFILES']).
*
* Caution: Does not apply any character-set conversion, it is assumed that all content
* is stored and represented in UTF-8 (Unicode). Please make sure your files are created with the correct
* character-set, and your HTML-templates render UTF-8.
*
* Caution: The language file has to be stored in the same module path as the "filename namespaces"
* on the entities. So an entity stored in $lang['en_US']['AssetAdmin']['DETAILSTAB'] has to
* in the language file cms/lang/en_US.php, as the referenced file (AssetAdmin.php) is stored
* in the "cms" module.
*
* <b>Locales</b>
*
* For the i18n class, a "locale" consists of a language code plus a region code separated by an underscore,
* for example "de_AT" for German language ("de") in the region Austria ("AT").
* See http://www.w3.org/International/articles/language-tags/ for a detailed description.
*
* @see http://doc.silverstripe.org/i18n
* @see http://www.w3.org/TR/i18n-html-tech-lang
*
* @author Bernat Foj Capell <bernat@silverstripe.com>
* @package framework
* @subpackage misc
*/
class i18n extends Object implements TemplateGlobalProvider, Flushable {
/**
* This static variable is used to store the current defined locale.
*/
protected static $current_locale = '';
/**
* @config
* @var string
*/
private static $default_locale = 'en_US';
/**
* @config
* @var boolean
*/
private static $js_i18n = true;
/**
* @config
* @var string
*/
private static $date_format;
/**
* @config
* @var string
*/
private static $time_format;
/**
* @var array Array of priority keys to instances of Zend_Translate, mapped by name.
*/
protected static $translators;
/**
* Triggered early in the request when someone requests a flush.
*/
public static function flush() {
self::get_cache()->clean(Zend_Cache::CLEANING_MODE_ALL);
}
/**
* Return an instance of the cache used for i18n data.
* @return Zend_Cache
*/
public static function get_cache() {
return SS_Cache::factory('i18n', 'Output', array('lifetime' => null, 'automatic_serialization' => true));
}
/**
* Use javascript i18n through the ss.i18n class (enabled by default).
* If set to TRUE, includes javascript requirements for the base library
* (framework/javascript/i18n.js) and all necessary lang files (e.g. framework/lang/de_DE.js)
* plus fallbacks to the default locale (e.g. framework/lang/en_US.js).
* If set to FALSE, only includes a stub implementation
* which is necessary. Mainly disabled to save bandwidth
* in a frontend context when website is in single language.
*
* Caution: This flag gets overwritten in {@link LeftAndMain::init()} to enforce javascript
* i18n for the CMS interfaces.
*
* @see Requirements::process_i18n_javascript()
*
* @deprecated 3.2 Use the "i18n.js_i18n" config setting instead
* @param bool $bool
*/
public static function set_js_i18n($bool) {
Deprecation::notice('3.2', 'Use the "i18n.js_i18n" config setting instead');
Config::inst()->update('i18n', 'js_i18n', $bool);
}
/**
* @deprecated 3.2 Use the "i18n.js_i18n" config setting instead
* @return bool
*/
public static function get_js_i18n() {
Deprecation::notice('3.2', 'Use the "i18n.js_i18n" config setting instead');
return Config::inst()->get('i18n', 'js_i18n');
}
/**
* @deprecated 3.2 Use the "i18n.date_format" config setting instead
* @param string ISO date format
*/
public static function set_date_format($format) {
Deprecation::notice('3.2', 'Use the "i18n.date_format" config setting instead');
Config::inst()->update('i18n', 'date_format', $format);
}
/**
* @return string ISO date format
*/
public static function get_date_format() {
require_once 'Zend/Date.php';
$dateFormat = Config::inst()->get('i18n', 'date_format');
return ($dateFormat) ? $dateFormat : Zend_Locale_Format::getDateFormat(self::get_locale());
}
/**
* @deprecated 3.2 Use the "i18n.time_format" config setting instead
* @param string ISO time format
*/
public static function set_time_format($format) {
Deprecation::notice('3.2', 'Use the "i18n.time_format" config setting instead');
Config::inst()->update('i18n', 'time_format', $format);
}
/**
* @return string ISO time format
*/
public static function get_time_format() {
require_once 'Zend/Date.php';
$timeFormat = Config::inst()->get('i18n', 'time_format');
return ($timeFormat) ? $timeFormat : Zend_Locale_Format::getTimeFormat(self::get_locale());
}
/**
* An exhaustive list of possible locales (code => language and country)
*
* @config
* @var array
*/
private static $all_locales = array (
'aa_DJ' => 'Afar (Djibouti)',
'ab_GE' => 'Abkhazian (Georgia)',
'abr_GH' => 'Abron (Ghana)',
'ace_ID' => 'Achinese (Indonesia)',
'ady_RU' => 'Adyghe (Russia)',
'af_ZA' => 'Afrikaans (South Africa)',
'ak_GH' => 'Akan (Ghana)',
'am_ET' => 'Amharic (Ethiopia)',
'ar_AE' => 'Arabic (United Arab Emirates)',
'ar_BH' => 'Arabic (Bahrain)',
'ar_DZ' => 'Arabic (Algeria)',
'ar_EG' => 'Arabic (Egypt)',
'ar_EH' => 'Arabic (Western Sahara)',
'ar_IQ' => 'Arabic (Iraq)',
'ar_JO' => 'Arabic (Jordan)',
'ar_KW' => 'Arabic (Kuwait)',
'ar_LB' => 'Arabic (Lebanon)',
'ar_LY' => 'Arabic (Libya)',
'ar_MA' => 'Arabic (Morocco)',
'ar_MR' => 'Arabic (Mauritania)',
'ar_OM' => 'Arabic (Oman)',
'ar_PS' => 'Arabic (Palestinian Territory)',
'ar_QA' => 'Arabic (Qatar)',
'ar_SA' => 'Arabic (Saudi Arabia)',
'ar_SD' => 'Arabic (Sudan)',
'ar_SY' => 'Arabic (Syria)',
'ar_TD' => 'Arabic (Chad)',
'ar_TN' => 'Arabic (Tunisia)',
'ar_YE' => 'Arabic (Yemen)',
'as_IN' => 'Assamese (India)',
'ast_ES' => 'Asturian (Spain)',
'auv_FR' => 'Auvergnat (France)',
'av_RU' => 'Avaric (Russia)',
'awa_IN' => 'Awadhi (India)',
'ay_BO' => 'Aymara (Bolivia)',
'ay_PE' => 'Aymara (Peru)',
'az_AZ' => 'Azerbaijani (Azerbaijan)',
'az_IR' => 'Azerbaijani (Iran)',
'ba_RU' => 'Bashkir (Russia)',
'ban_ID' => 'Balinese (Indonesia)',
'bcc_PK' => 'Balochi, Southern (Pakistan)',
'bcl_PH' => 'Bicolano, Central (Philippines)',
'be_BY' => 'Belarusian (Belarus)',
'bew_ID' => 'Betawi (Indonesia)',
'bg_BG' => 'Bulgarian (Bulgaria)',
'bgc_IN' => 'Haryanvi (India)',
'bgn_PK' => 'Balochi, Western (Pakistan)',
'bgp_PK' => 'Balochi, Easter (Pakistan)',
'bhb_IN' => 'Bhili (India)',
'bhi_IN' => 'Bhilali (India)',
'bhk_PH' => 'Bicolano, Albay (Philippines)',
'bho_IN' => 'Bhojpuri (India)',
'bho_MU' => 'Bhojpuri (Mauritius)',
'bho_NP' => 'Bhojpuri (Nepal)',
'bi_VU' => 'Bislama (Vanuatu)',
'bjj_IN' => 'Kanauji (India)',
'bjn_ID' => 'Banjar (Indonesia)',
'bm_ML' => 'Bambara (Mali)',
'bn_BD' => 'Bengali (Bangladesh)',
'bn_IN' => 'Bengali (India)',
'bo_CN' => 'Tibetan (China)',
'bqi_IR' => 'Bakhtiari (Iran)',
'brh_PK' => 'Brahui (Pakistan)',
'bs_BA' => 'Bosnian (Bosnia and Herzegovina)',
'btk_ID' => 'Batak (Indonesia)',
'buc_YT' => 'Bushi (Mayotte)',
'bug_ID' => 'Buginese (Indonesia)',
'ca_AD' => 'Catalan (Andorra)',
'ca_ES' => 'Catalan (Spain)',
'ce_RU' => 'Chechen (Russia)',
'ceb_PH' => 'Cebuano (Philippines)',
'cgg_UG' => 'Chiga (Uganda)',
'ch_GU' => 'Chamorro (Guam)',
'chk_FM' => 'Chuukese (Micronesia)',
'crk_CA' => 'Cree, Plains (Canada)',
'cs_CZ' => 'Czech (Czech Republic)',
'cwd_CA' => 'Cree, Woods (Canada)',
'cy_GB' => 'Welsh (United Kingdom)',
'da_DK' => 'Danish (Denmark)',
'da_GL' => 'Danish (Greenland)',
'dcc_IN' => 'Deccan (India)',
'de_AT' => 'German (Austria)',
'de_BE' => 'German (Belgium)',
'de_CH' => 'German (Switzerland)',
'de_DE' => 'German (Germany)',
'de_LI' => 'German (Liechtenstein)',
'de_LU' => 'German (Luxembourg)',
'dgo_IN' => 'Dogri (India)',
'dhd_IN' => 'Dhundari (India)',
'diq_TR' => 'Dimli (Turkey)',
'dje_NE' => 'Zarma (Niger)',
'dv_MV' => 'Divehi (Maldives)',
'dz_BT' => 'Dzongkha (Bhutan)',
'ee_GH' => 'Ewe (Ghana)',
'el_CY' => 'Greek (Cyprus)',
'el_GR' => 'Greek (Greece)',
'en_AS' => 'English (American Samoa)',
'en_AU' => 'English (Australia)',
'en_BM' => 'English (Bermuda)',
'en_BS' => 'English (Bahamas)',
'en_CA' => 'English (Canada)',
'en_DE' => 'English (Germany)',
'en_ES' => 'English (Spain)',
'en_FR' => 'English (France)',
'en_GB' => 'English (United Kingdom)',
'en_HK' => 'English (Hong Kong SAR China)',
'en_IE' => 'English (Ireland)',
'en_IN' => 'English (India)',
'en_IT' => 'English (Italy)',
'en_JM' => 'English (Jamaica)',
'en_KE' => 'English (Kenya)',
'en_LR' => 'English (Liberia)',
'en_MM' => 'English (Myanmar)',
'en_MW' => 'English (Malawi)',
'en_MY' => 'English (Malaysia)',
'en_NL' => 'English (Netherlands)',
'en_NZ' => 'English (New Zealand)',
'en_PH' => 'English (Philippines)',
'en_SG' => 'English (Singapore)',
'en_TT' => 'English (Trinidad and Tobago)',
'en_US' => 'English (United States)',
'en_ZA' => 'English (South Africa)',
'eo_XX' => 'Esperanto',
'es_419' => 'Spanish (Latin America)',
'es_AR' => 'Spanish (Argentina)',
'es_BO' => 'Spanish (Bolivia)',
'es_CL' => 'Spanish (Chile)',
'es_CO' => 'Spanish (Colombia)',
'es_CR' => 'Spanish (Costa Rica)',
'es_CU' => 'Spanish (Cuba)',
'es_DO' => 'Spanish (Dominican Republic)',
'es_EC' => 'Spanish (Ecuador)',
'es_ES' => 'Spanish (Spain)',
'es_GQ' => 'Spanish (Equatorial Guinea)',
'es_GT' => 'Spanish (Guatemala)',
'es_HN' => 'Spanish (Honduras)',
'es_MX' => 'Spanish (Mexico)',
'es_NI' => 'Spanish (Nicaragua)',
'es_PA' => 'Spanish (Panama)',
'es_PE' => 'Spanish (Peru)',
'es_PH' => 'Spanish (Philippines)',
'es_PR' => 'Spanish (Puerto Rico)',
'es_PY' => 'Spanish (Paraguay)',
'es_SV' => 'Spanish (El Salvador)',
'es_US' => 'Spanish (United States)',
'es_UY' => 'Spanish (Uruguay)',
'es_VE' => 'Spanish (Venezuela)',
'et_EE' => 'Estonian (Estonia)',
'eu_ES' => 'Basque (Spain)',
'fa_AF' => 'Persian (Afghanistan)',
'fa_IR' => 'Persian (Iran)',
'fa_PK' => 'Persian (Pakistan)',
'fan_GQ' => 'Fang (Equatorial Guinea)',
'fi_FI' => 'Finnish (Finland)',
'fi_SE' => 'Finnish (Sweden)',
'fil_PH' => 'Filipino (Philippines)',
'fj_FJ' => 'Fijian (Fiji)',
'fo_FO' => 'Faroese (Faroe Islands)',
'fon_BJ' => 'Fon (Benin)',
'fr_002' => 'French (Africa)',
'fr_BE' => 'French (Belgium)',
'fr_CA' => 'French (Canada)',
'fr_CH' => 'French (Switzerland)',
'fr_DZ' => 'French (Algeria)',
'fr_FR' => 'French (France)',
'fr_GF' => 'French (French Guiana)',
'fr_GP' => 'French (Guadeloupe)',
'fr_HT' => 'French (Haiti)',
'fr_KM' => 'French (Comoros)',
'fr_MA' => 'French (Morocco)',
'fr_MQ' => 'French (Martinique)',
'fr_MU' => 'French (Mauritius)',
'fr_NC' => 'French (New Caledonia)',
'fr_PF' => 'French (French Polynesia)',
'fr_PM' => 'French (Saint Pierre and Miquelon)',
'fr_RE' => 'French (Reunion)',
'fr_SC' => 'French (Seychelles)',
'fr_SN' => 'French (Senegal)',
'fr_US' => 'French (United States)',
'fuv_NG' => 'Fulfulde (Nigeria)',
'ga_GB' => 'Irish (United Kingdom)',
'ga_IE' => 'Irish (Ireland)',
'gaa_GH' => 'Ga (Ghana)',
'gbm_IN' => 'Garhwali (India)',
'gcr_GF' => 'Guianese Creole French (French Guiana)',
'gd_GB' => 'Scottish Gaelic (United Kingdom)',
'gil_KI' => 'Gilbertese (Kiribati)',
'gl_ES' => 'Galician (Spain)',
'glk_IR' => 'Gilaki (Iran)',
'gn_PY' => 'Guarani (Paraguay)',
'gno_IN' => 'Gondi, Northern (India)',
'gsw_CH' => 'Swiss German (Switzerland)',
'gsw_LI' => 'Swiss German (Liechtenstein)',
'gu_IN' => 'Gujarati (India)',
'guz_KE' => 'Gusii (Kenya)',
'ha_NE' => 'Hausa (Niger)',
'ha_NG' => 'Hausa (Nigeria)',
'haw_US' => 'Hawaiian (United States)',
'haz_AF' => 'Hazaragi (Afghanistan)',
'he_IL' => 'Hebrew (Israel)',
'hi_IN' => 'Hindi (India)',
'hil_PH' => 'Hiligaynon (Philippines)',
'hne_IN' => 'Chhattisgarhi (India)',
'hno_PK' => 'Hindko, Northern (Pakistan)',
'hoc_IN' => 'Ho (India)',
'hr_AT' => 'Croatian (Austria)',
'hr_BA' => 'Croatian (Bosnia and Herzegovina)',
'hr_HR' => 'Croatian (Croatia)',
'ht_HT' => 'Haitian (Haiti)',
'hu_AT' => 'Hungarian (Austria)',
'hu_HU' => 'Hungarian (Hungary)',
'hu_RO' => 'Hungarian (Romania)',
'hu_RS' => 'Hungarian (Serbia)',
'hy_AM' => 'Armenian (Armenia)',
'id_ID' => 'Indonesian (Indonesia)',
'ig_NG' => 'Igbo (Nigeria)',
'ilo_PH' => 'Iloko (Philippines)',
'inh_RU' => 'Ingush (Russia)',
'is_IS' => 'Icelandic (Iceland)',
'it_CH' => 'Italian (Switzerland)',
'it_FR' => 'Italian (France)',
'it_HR' => 'Italian (Croatia)',
'it_IT' => 'Italian (Italy)',
'it_SM' => 'Italian (San Marino)',
'it_US' => 'Italian (United States)',
'iu_CA' => 'Inuktitut (Canada)',
'ja_JP' => 'Japanese (Japan)',
'jv_ID' => 'Javanese (Indonesia)',
'ka_GE' => 'Georgian (Georgia)',
'kam_KE' => 'Kamba (Kenya)',
'kbd_RU' => 'Kabardian (Russia)',
'kfy_IN' => 'Kumauni (India)',
'kha_IN' => 'Khasi (India)',
'khn_IN' => 'Khandesi (India)',
'ki_KE' => 'Kikuyu (Kenya)',
'kj_NA' => 'Kuanyama (Namibia)',
'kk_CN' => 'Kazakh (China)',
'kk_KZ' => 'Kazakh (Kazakhstan)',
'kl_DK' => 'Kalaallisut (Denmark)',
'kl_GL' => 'Kalaallisut (Greenland)',
'kln_KE' => 'Kalenjin (Kenya)',
'km_KH' => 'Khmer (Cambodia)',
'kn_IN' => 'Kannada (India)',
'ko_KR' => 'Korean (Korea)',
'koi_RU' => 'Komi-Permyak (Russia)',
'kok_IN' => 'Konkani (India)',
'kos_FM' => 'Kosraean (Micronesia)',
'kpv_RU' => 'Komi-Zyrian (Russia)',
'krc_RU' => 'Karachay-Balkar (Russia)',
'kru_IN' => 'Kurukh (India)',
'ks_IN' => 'Kashmiri (India)',
'ku_IQ' => 'Kurdish (Iraq)',
'ku_IR' => 'Kurdish (Iran)',
'ku_SY' => 'Kurdish (Syria)',
'ku_TR' => 'Kurdish (Turkey)',
'kum_RU' => 'Kumyk (Russia)',
'kxm_TH' => 'Khmer, Northern (Thailand)',
'ky_KG' => 'Kirghiz (Kyrgyzstan)',
'la_VA' => 'Latin (Vatican)',
'lah_PK' => 'Lahnda (Pakistan)',
'lb_LU' => 'Luxembourgish (Luxembourg)',
'lbe_RU' => 'Lak (Russia)',
'lc_XX' => 'LOLCAT',
'lez_RU' => 'Lezghian (Russia)',
'lg_UG' => 'Ganda (Uganda)',
'lij_IT' => 'Ligurian (Italy)',
'lij_MC' => 'Ligurian (Monaco)',
'ljp_ID' => 'Lampung (Indonesia)',
'lmn_IN' => 'Lambadi (India)',
'ln_CD' => 'Lingala (Congo - Kinshasa)',
'ln_CG' => 'Lingala (Congo - Brazzaville)',
'lo_LA' => 'Lao (Laos)',
'lrc_IR' => 'Luri, Northern (Iran)',
'lt_LT' => 'Lithuanian (Lithuania)',
'luo_KE' => 'Luo (Kenya)',
'luy_KE' => 'Luyia (Kenya)',
'lv_LV' => 'Latvian (Latvia)',
'mad_ID' => 'Madurese (Indonesia)',
'mai_IN' => 'Maithili (India)',
'mai_NP' => 'Maithili (Nepal)',
'mak_ID' => 'Makasar (Indonesia)',
'mdf_RU' => 'Moksha (Russia)',
'mdh_PH' => 'Maguindanao (Philippines)',
'mer_KE' => 'Meru (Kenya)',
'mfa_TH' => 'Malay, Pattani (Thailand)',
'mfe_MU' => 'Morisyen (Mauritius)',
'mg_MG' => 'Malagasy (Madagascar)',
'mh_MH' => 'Marshallese (Marshall Islands)',
'mi_NZ' => 'te reo Māori (New Zealand)',
'min_ID' => 'Minangkabau (Indonesia)',
'mk_MK' => 'Macedonian (Macedonia)',
'ml_IN' => 'Malayalam (India)',
'mn_CN' => 'Mongolian (China)',
'mn_MN' => 'Mongolian (Mongolia)',
'mni_IN' => 'Manipuri (India)',
'mr_IN' => 'Marathi (India)',
'ms_BN' => 'Malay (Brunei)',
'ms_CC' => 'Malay (Cocos Islands)',
'ms_ID' => 'Malay (Indonesia)',
'ms_MY' => 'Malay (Malaysia)',
'ms_SG' => 'Malay (Singapore)',
'mt_MT' => 'Maltese (Malta)',
'mtr_IN' => 'Mewari (India)',
'mup_IN' => 'Malvi (India)',
'muw_IN' => 'Mundari (India)',
'my_MM' => 'Burmese (Myanmar)',
'myv_RU' => 'Erzya (Russia)',
'na_NR' => 'Nauru (Nauru)',
'nb_NO' => 'Norwegian Bokmal (Norway)',
'nb_SJ' => 'Norwegian Bokmal (Svalbard and Jan Mayen)',
'nd_ZW' => 'North Ndebele (Zimbabwe)',
'ndc_MZ' => 'Ndau (Mozambique)',
'ne_IN' => 'Nepali (India)',
'ne_NP' => 'Nepali (Nepal)',
'ng_NA' => 'Ndonga (Namibia)',
'ngl_MZ' => 'Lomwe (Mozambique)',
'niu_NU' => 'Niuean (Niue)',
'nl_AN' => 'Dutch (Netherlands Antilles)',
'nl_AW' => 'Dutch (Aruba)',
'nl_BE' => 'Dutch (Belgium)',
'nl_NL' => 'Dutch (Netherlands)',
'nl_SR' => 'Dutch (Suriname)',
'nn_NO' => 'Norwegian Nynorsk (Norway)',
'nb_NO' => 'Norwegian',
'nod_TH' => 'Thai, Northern (Thailand)',
'noe_IN' => 'Nimadi (India)',
'nso_ZA' => 'Northern Sotho (South Africa)',
'ny_MW' => 'Nyanja (Malawi)',
'ny_ZM' => 'Nyanja (Zambia)',
'nyn_UG' => 'Nyankole (Uganda)',
'om_ET' => 'Oromo (Ethiopia)',
'or_IN' => 'Oriya (India)',
'pa_IN' => 'Punjabi (India)',
'pag_PH' => 'Pangasinan (Philippines)',
'pap_AN' => 'Papiamento (Netherlands Antilles)',
'pap_AW' => 'Papiamento (Aruba)',
'pau_PW' => 'Palauan (Palau)',
'pl_PL' => 'Polish (Poland)',
'pl_UA' => 'Polish (Ukraine)',
'pon_FM' => 'Pohnpeian (Micronesia)',
'ps_AF' => 'Pashto (Afghanistan)',
'ps_PK' => 'Pashto (Pakistan)',
'pt_AO' => 'Portuguese (Angola)',
'pt_BR' => 'Portuguese (Brazil)',
'pt_CV' => 'Portuguese (Cape Verde)',
'pt_GW' => 'Portuguese (Guinea-Bissau)',
'pt_MZ' => 'Portuguese (Mozambique)',
'pt_PT' => 'Portuguese (Portugal)',
'pt_ST' => 'Portuguese (Sao Tome and Principe)',
'pt_TL' => 'Portuguese (East Timor)',
'qu_BO' => 'Quechua (Bolivia)',
'qu_PE' => 'Quechua (Peru)',
'rcf_RE' => 'R<>union Creole French (Reunion)',
'rej_ID' => 'Rejang (Indonesia)',
'rif_MA' => 'Tarifit (Morocco)',
'rjb_IN' => 'Rajbanshi (India)',
'rm_CH' => 'Rhaeto-Romance (Switzerland)',
'rmt_IR' => 'Domari (Iran)',
'rn_BI' => 'Rundi (Burundi)',
'ro_MD' => 'Romanian (Moldova)',
'ro_RO' => 'Romanian (Romania)',
'ro_RS' => 'Romanian (Serbia)',
'ru_BY' => 'Russian (Belarus)',
'ru_KG' => 'Russian (Kyrgyzstan)',
'ru_KZ' => 'Russian (Kazakhstan)',
'ru_RU' => 'Russian (Russia)',
'ru_SJ' => 'Russian (Svalbard and Jan Mayen)',
'ru_UA' => 'Russian (Ukraine)',
'rw_RW' => 'Kinyarwanda (Rwanda)',
'sa_IN' => 'Sanskrit (India)',
'sah_RU' => 'Yakut (Russia)',
'sas_ID' => 'Sasak (Indonesia)',
'sat_IN' => 'Santali (India)',
'sck_IN' => 'Sadri (India)',
'sco_GB' => 'Scots (United Kingdom)',
'sco_SCO' => 'Scots',
'sd_IN' => 'Sindhi (India)',
'sd_PK' => 'Sindhi (Pakistan)',
'se_NO' => 'Northern Sami (Norway)',
'sg_CF' => 'Sango (Central African Republic)',
'si_LK' => 'Sinhalese (Sri Lanka)',
'sid_ET' => 'Sidamo (Ethiopia)',
'sk_RS' => 'Slovak (Serbia)',
'sk_SK' => 'Slovak (Slovakia)',
'sl_AT' => 'Slovenian (Austria)',
'sl_SI' => 'Slovenian (Slovenia)',
'sm_AS' => 'Samoan (American Samoa)',
'sm_WS' => 'Samoan (Samoa)',
'sn_ZW' => 'Shona (Zimbabwe)',
'so_DJ' => 'Somali (Djibouti)',
'so_ET' => 'Somali (Ethiopia)',
'so_SO' => 'Somali (Somalia)',
'sou_TH' => 'Thai, Southern (Thailand)',
'sq_AL' => 'Albanian (Albania)',
'sr_BA' => 'Serbian (Bosnia and Herzegovina)',
'sr_ME' => 'Serbian (Montenegro)',
'sr_RS' => 'Serbian (Serbia)',
'ss_SZ' => 'Swati (Swaziland)',
'ss_ZA' => 'Swati (South Africa)',
'st_LS' => 'Southern Sotho (Lesotho)',
'st_ZA' => 'Southern Sotho (South Africa)',
'su_ID' => 'Sundanese (Indonesia)',
'sv_AX' => 'Swedish (Aland Islands)',
'sv_FI' => 'Swedish (Finland)',
'sv_SE' => 'Swedish (Sweden)',
'sw_KE' => 'Swahili (Kenya)',
'sw_SO' => 'Swahili (Somalia)',
'sw_TZ' => 'Swahili (Tanzania)',
'sw_UG' => 'Swahili (Uganda)',
'swb_KM' => 'Comorian (Comoros)',
'swb_YT' => 'Comorian (Mayotte)',
'swv_IN' => 'Shekhawati (India)',
'ta_IN' => 'Tamil (India)',
'ta_LK' => 'Tamil (Sri Lanka)',
'ta_MY' => 'Tamil (Malaysia)',
'ta_SG' => 'Tamil (Singapore)',
'tcy_IN' => 'Tulu (India)',
'te_IN' => 'Telugu (India)',
'tet_TL' => 'Tetum (East Timor)',
'tg_TJ' => 'Tajik (Tajikistan)',
'th_TH' => 'Thai (Thailand)',
'ti_ER' => 'Tigrinya (Eritrea)',
'ti_ET' => 'Tigrinya (Ethiopia)',
'tk_IR' => 'Turkmen (Iran)',
'tk_TM' => 'Turkmen (Turkmenistan)',
'tkl_TK' => 'Tokelau (Tokelau)',
'tl_PH' => 'Tagalog (Philippines)',
'tl_US' => 'Tagalog (United States)',
'tn_BW' => 'Tswana (Botswana)',
'tn_ZA' => 'Tswana (South Africa)',
'to_TO' => 'Tonga (Tonga)',
'tr_CY' => 'Turkish (Cyprus)',
'tr_DE' => 'Turkish (Germany)',
'tr_MK' => 'Turkish (Macedonia)',
'tr_TR' => 'Turkish (Turkey)',
'ts_MZ' => 'Tsonga (Mozambique)',
'ts_ZA' => 'Tsonga (South Africa)',
'tsg_PH' => 'Tausug (Philippines)',
'tt_RU' => 'Tatar (Russia)',
'tts_TH' => 'Thai, Northeastern (Thailand)',
'tvl_TV' => 'Tuvalu (Tuvalu)',
'tw_GH' => 'Twi (Ghana)',
'ty_PF' => 'Tahitian (French Polynesia)',
'tyv_RU' => 'Tuvinian (Russia)',
'tzm_MA' => 'Tamazight, Central Atlas (Morocco)',
'udm_RU' => 'Udmurt (Russia)',
'ug_CN' => 'Uighur (China)',
'uk_UA' => 'Ukrainian (Ukraine)',
'uli_FM' => 'Ulithian (Micronesia)',
'ur_IN' => 'Urdu (India)',
'ur_PK' => 'Urdu (Pakistan)',
'uz_AF' => 'Uzbek (Afghanistan)',
'uz_UZ' => 'Uzbek (Uzbekistan)',
've_ZA' => 'Venda (South Africa)',
'vi_US' => 'Vietnamese (United States)',
'vi_VN' => 'Vietnamese (Vietnam)',
'vmw_MZ' => 'Waddar (Mozambique)',
'wal_ET' => 'Walamo (Ethiopia)',
'war_PH' => 'Waray (Philippines)',
'wbq_IN' => 'Waddar (India)',
'wbr_IN' => 'Wagdi (India)',
'wo_MR' => 'Wolof (Mauritania)',
'wo_SN' => 'Wolof (Senegal)',
'wtm_IN' => 'Mewati (India)',
'xh_ZA' => 'Xhosa (South Africa)',
'xnr_IN' => 'Kangri (India)',
'xog_UG' => 'Soga (Uganda)',
'yap_FM' => 'Yapese (Micronesia)',
'yo_NG' => 'Yoruba (Nigeria)',
'za_CN' => 'Zhuang (China)',
'zh_CN' => 'Chinese (China)',
'zh_HK' => 'Chinese (Hong Kong SAR China)',
'zh_MO' => 'Chinese (Macao SAR China)',
'zh_SG' => 'Chinese (Singapore)',
'zh_TW' => 'Chinese (Taiwan)',
'zh_US' => 'Chinese (United States)',
'zh_cmn' => 'Chinese (Mandarin)',
'zh_yue' => 'Chinese (Cantonese)',
'zu_ZA' => 'Zulu (South Africa)'
);
/**
* @config
* @var array $common_languages A list of commonly used languages, in the form
* langcode => array( EnglishName, NativeName)
*/
private static $common_languages = array(
'af' => array(
'name' => 'Afrikaans',
'native' => 'Afrikaans'
),
'sq' => array(
'name' => 'Albanian',
'native' => 'shqip'
),
'ar' => array(
'name' => 'Arabic',
'native' => '&#1575;&#1604;&#1593;&#1585;&#1576;&#1610;&#1577;'
),
'eu' => array(
'name' => 'Basque',
'native' => 'euskera'
),
'be' => array(
'name' => 'Belarusian',
'native' =>
'&#1041;&#1077;&#1083;&#1072;&#1088;&#1091;&#1089;&#1082;&#1072;&#1103; &#1084;&#1086;&#1074;&#1072;'
),
'bn' => array(
'name' => 'Bengali',
'native' => '&#2476;&#2494;&#2434;&#2482;&#2494;'
),
'bg' => array(
'name' => 'Bulgarian',
'native' => '&#1073;&#1098;&#1083;&#1075;&#1072;&#1088;&#1089;&#1082;&#1080;'
),
'ca' => array(
'name' => 'Catalan',
'native' => 'catal&agrave;'
),
'zh_yue' => array(
'name' => 'Chinese (Cantonese)',
'native' => '&#24291;&#26481;&#35441; [&#24191;&#19996;&#35805;]'
),
'zh_cmn' => array(
'name' => 'Chinese (Mandarin)',
'native' => '&#26222;&#36890;&#35441; [&#26222;&#36890;&#35805;]'
),
'hr' => array(
'name' => 'Croatian',
'native' => 'Hrvatski'
),
'zh' => array(
'name' => 'Chinese',
'native' => '&#20013;&#22269;&#30340;'
),
'cs' => array(
'name' => 'Czech',
'native' => '&#x010D;e&#353;tina'
),
'cy' => array(
'name' => 'Welsh',
'native' => 'Welsh/Cymraeg'
),
'da' => array(
'name' => 'Danish',
'native' => 'dansk'
),
'nl' => array(
'name' => 'Dutch',
'native' => 'Nederlands'
),
'en' => array(
'name' => 'English',
'native' => 'English'
),
'eo' => array(
'name' => 'Esperanto',
'native' => 'Esperanto'
),
'et' => array(
'name' => 'Estonian',
'native' => 'eesti keel'
),
'fo' => array(
'name' => 'Faroese',
'native' => 'F&oslash;royska'
),
'fi' => array(
'name' => 'Finnish',
'native' => 'suomi'
),
'fr' => array(
'name' => 'French',
'native' => 'fran&ccedil;ais'
),
'gd' => array(
'name' => 'Gaelic',
'native' => 'Gaeilge'
),
'gl' => array(
'name' => 'Galician',
'native' => 'Galego'
),
'de' => array(
'name' => 'German',
'native' => 'Deutsch'
),
'el' => array(
'name' => 'Greek',
'native' => '&#949;&#955;&#955;&#951;&#957;&#953;&#954;&#940;'
),
'gu' => array(
'name' => 'Gujarati',
'native' => '&#2711;&#2753;&#2716;&#2736;&#2750;&#2724;&#2752;'
),
'ha' => array(
'name' => 'Hausa',
'native' => '&#1581;&#1614;&#1608;&#1618;&#1587;&#1614;'
),
'he' => array(
'name' => 'Hebrew',
'native' => '&#1506;&#1489;&#1512;&#1497;&#1514;'
),
'hi' => array(
'name' => 'Hindi',
'native' => '&#2361;&#2367;&#2344;&#2381;&#2342;&#2368;'
),
'hu' => array(
'name' => 'Hungarian',
'native' => 'magyar'
),
'is' => array(
'name' => 'Icelandic',
'native' => '&Iacute;slenska'
),
'io' => array(
'name' => 'Ido',
'native' => 'Ido'
),
'id' => array(
'name' => 'Indonesian',
'native' => 'Bahasa Indonesia'
),
'ga' => array(
'name' => 'Irish',
'native' => 'Irish'
),
'it' => array(
'name' => 'Italian',
'native' => 'italiano'
),
'ja' => array(
'name' => 'Japanese',
'native' => '&#26085;&#26412;&#35486;'
),
'jv' => array(
'name' => 'Javanese',
'native' => 'basa Jawa'
),
'ko' => array(
'name' => 'Korean',
'native' => '&#54620;&#44397;&#50612; [&#38867;&#22283;&#35486;]'
),
'ku' => array(
'name' => 'Kurdish',
'native' => 'Kurd&iacute;'
),
'lv' => array(
'name' => 'Latvian',
'native' => 'latvie&#353;u'
),
'lt' => array(
'name' => 'Lithuanian',
'native' => 'lietuvi&#353;kai'
),
'lmo' => array(
'name' => 'Lombard',
'native' => 'Lombardo'
),
'mk' => array(
'name' => 'Macedonian',
'native' => '&#1084;&#1072;&#1082;&#1077;&#1076;&#1086;&#1085;&#1089;&#1082;&#1080;'
),
'mi' => array(
'name' => 'te reo Māori',
'native' => 'te reo Māori'
),
'ms' => array(
'name' => 'Malay',
'native' => 'Bahasa melayu'
),
'mt' => array(
'name' => 'Maltese',
'native' => 'Malti'
),
'mr' => array(
'name' => 'Marathi',
'native' => '&#2350;&#2352;&#2366;&#2336;&#2368;'
),
'ne' => array(
'name' => 'Nepali',
'native' => '&#2344;&#2375;&#2346;&#2366;&#2354;&#2368;'
),
'nb' => array(
'name' => 'Norwegian',
'native' => 'Norsk'
),
'om' => array(
'name' => 'Oromo',
'native' => 'Afaan Oromo'
),
'fa' => array(
'name' => 'Persian',
'native' => '&#1601;&#1575;&#1585;&#1587;&#1609;'
),
'pl' => array(
'name' => 'Polish',
'native' => 'polski'
),
'pt_PT' => array(
'name' => 'Portuguese (Portugal)',
'native' => 'portugu&ecirc;s (Portugal)'
),
'pt_BR' => array(
'name' => 'Portuguese (Brazil)',
'native' => 'portugu&ecirc;s (Brazil)'
),
'pa' => array(
'name' => 'Punjabi',
'native' => '&#2602;&#2672;&#2588;&#2622;&#2604;&#2624;'
),
'qu' => array(
'name' => 'Quechua',
'native' => 'Quechua'
),
'rm' => array(
'name' => 'Romansh',
'native' => 'rumantsch'
),
'ro' => array(
'name' => 'Romanian',
'native' => 'rom&acirc;n'
),
'ru' => array(
'name' => 'Russian',
'native' => '&#1056;&#1091;&#1089;&#1089;&#1082;&#1080;&#1081;'
),
'sco' => array(
'name' => 'Scots',
'native' => 'Scoats leid, Lallans'
),
'sr' => array(
'name' => 'Serbian',
'native' => '&#1089;&#1088;&#1087;&#1089;&#1082;&#1080;'
),
'sk' => array(
'name' => 'Slovak',
'native' => 'sloven&#269;ina'
),
'sl' => array(
'name' => 'Slovenian',
'native' => 'sloven&#353;&#269;ina'
),
'es' => array(
'name' => 'Spanish',
'native' => 'espa&ntilde;ol'
),
'sv' => array(
'name' => 'Swedish',
'native' => 'Svenska'
),
'tl' => array(
'name' => 'Tagalog',
'native' => 'Tagalog'
),
'ta' => array(
'name' => 'Tamil',
'native' => '&#2980;&#2990;&#3007;&#2996;&#3021;'
),
'te' => array(
'name' => 'Telugu',
'native' => '&#3108;&#3142;&#3122;&#3137;&#3095;&#3137;'
),
'to' => array(
'name' => 'Tonga',
'native' => 'chiTonga'
),
'ts' => array(
'name' => 'Tsonga',
'native' => 'xiTshonga'
),
'tn' => array(
'name' => 'Tswana',
'native' => 'seTswana'
),
'tr' => array(
'name' => 'Turkish',
'native' => 'T&uuml;rk&ccedil;e'
),
'tk' => array(
'name' => 'Turkmen',
'native' => '&#1090;&#1199;&#1088;&#1082;m&#1077;&#1085;&#1095;&#1077;'
),
'tw' => array(
'name' => 'Twi',
'native' => 'twi'
),
'uk' => array(
'name' => 'Ukrainian',
'native' => '&#1059;&#1082;&#1088;&#1072;&#1111;&#1085;&#1089;&#1100;&#1082;&#1072;'
),
'ur' => array(
'name' => 'Urdu',
'native' => '&#1575;&#1585;&#1583;&#1608;'
),
'uz' => array(
'name' => 'Uzbek',
'native' => '&#1118;&#1079;&#1073;&#1077;&#1082;'
),
've' => array(
'name' => 'Venda',
'native' => 'tshiVen&#x1E13;a'
),
'vi' => array(
'name' => 'Vietnamese',
'native' => 'ti&#7871;ng vi&#7879;t'
),
'wa' => array(
'name' => 'Walloon',
'native' => 'walon'
),
'wo' => array(
'name' => 'Wolof',
'native' => 'Wollof'
),
'xh' => array(
'name' => 'Xhosa',
'native' => 'isiXhosa'
),
'yi' => array(
'name' => 'Yiddish',
'native' => '&#1522;&#1460;&#1491;&#1497;&#1513;'
),
'zu' => array(
'name' => 'Zulu',
'native' => 'isiZulu'
)
);
/**
* @config
* @var array $common_locales
* Sorted alphabtically by the common language name,
* not the locale key.
*/
private static $common_locales = array(
'af_ZA' => array(
'name' => 'Afrikaans',
'native' => 'Afrikaans'
),
'sq_AL' => array(
'name' => 'Albanian',
'native' => 'shqip'
),
'ar_EG' => array(
'name' => 'Arabic',
'native' => '&#1575;&#1604;&#1593;&#1585;&#1576;&#1610;&#1577;'
),
'eu_ES' => array(
'name' => 'Basque',
'native' => 'euskera'
),
'be_BY' => array(
'name' => 'Belarusian',
'native' =>
'&#1041;&#1077;&#1083;&#1072;&#1088;&#1091;&#1089;&#1082;&#1072;&#1103; &#1084;&#1086;&#1074;&#1072;'
),
'bn_BD' => array(
'name' => 'Bengali',
'native' => '&#2476;&#2494;&#2434;&#2482;&#2494;'
),
'bg_BG' => array(
'name' => 'Bulgarian',
'native' => '&#1073;&#1098;&#1083;&#1075;&#1072;&#1088;&#1089;&#1082;&#1080;'
),
'ca_ES' => array(
'name' => 'Catalan',
'native' => 'catal&agrave;'
),
'zh_CN' => array(
'name' => 'Chinese',
'native' => '中国的'
),
'zh_yue' => array(
'name' => 'Chinese (Cantonese)',
'native' => '&#24291;&#26481;&#35441; [&#24191;&#19996;&#35805;]'
),
'zh_cmn' => array(
'name' => 'Chinese (Mandarin)',
'native' => '&#26222;&#36890;&#35441; [&#26222;&#36890;&#35805;]'
),
'hr_HR' => array(
'name' => 'Croatian',
'native' => 'Hrvatski'
),
'cs_CZ' => array(
'name' => 'Czech',
'native' => '&#x010D;e&#353;tina'
),
'cy_GB' => array(
'name' => 'Welsh',
'native' => 'Welsh/Cymraeg'
),
'da_DK' => array(
'name' => 'Danish',
'native' => 'dansk'
),
'nl_NL' => array(
'name' => 'Dutch',
'native' => 'Nederlands'
),
'en_NZ' => array(
'name' => 'English (NZ)',
'native' => 'English (NZ)'
),
'en_US' => array(
'name' => 'English (US)',
'native' => 'English (US)'
),
'en_GB' => array(
'name' => 'English (UK)',
'native' => 'English (UK)'
),
'eo_XX' => array(
'name' => 'Esperanto',
'native' => 'Esperanto'
),
'et_EE' => array(
'name' => 'Estonian',
'native' => 'eesti keel'
),
'fo_FO' => array(
'name' => 'Faroese',
'native' => 'F&oslash;royska'
),
'fi_FI' => array(
'name' => 'Finnish',
'native' => 'suomi'
),
'fr_FR' => array(
'name' => 'French',
'native' => 'fran&ccedil;ais'
),
'gd_GB' => array(
'name' => 'Gaelic',
'native' => 'Gaeilge'
),
'gl_ES' => array(
'name' => 'Galician',
'native' => 'Galego'
),
'de_DE' => array(
'name' => 'German',
'native' => 'Deutsch'
),
'el_GR' => array(
'name' => 'Greek',
'native' => '&#949;&#955;&#955;&#951;&#957;&#953;&#954;&#940;'
),
'gu_IN' => array(
'name' => 'Gujarati',
'native' => '&#2711;&#2753;&#2716;&#2736;&#2750;&#2724;&#2752;'
),
'ha_NG' => array(
'name' => 'Hausa',
'native' => '&#1581;&#1614;&#1608;&#1618;&#1587;&#1614;'
),
'he_IL' => array(
'name' => 'Hebrew',
'native' => '&#1506;&#1489;&#1512;&#1497;&#1514;'
),
'hi_IN' => array(
'name' => 'Hindi',
'native' => '&#2361;&#2367;&#2344;&#2381;&#2342;&#2368;'
),
'hu_HU' => array(
'name' => 'Hungarian',
'native' => 'magyar'
),
'is_IS' => array(
'name' => 'Icelandic',
'native' => '&Iacute;slenska'
),
'id_ID' => array(
'name' => 'Indonesian',
'native' => 'Bahasa Indonesia'
),
'ga_IE' => array(
'name' => 'Irish',
'native' => 'Irish'
),
'it_IT' => array(
'name' => 'Italian',
'native' => 'italiano'
),
'ja_JP' => array(
'name' => 'Japanese',
'native' => '&#26085;&#26412;&#35486;'
),
'jv_ID' => array(
'name' => 'Javanese',
'native' => 'basa Jawa'
),
'ko_KR' => array(
'name' => 'Korean',
'native' => '&#54620;&#44397;&#50612; [&#38867;&#22283;&#35486;]'
),
'ku_IQ' => array(
'name' => 'Kurdish',
'native' => 'Kurd&iacute;'
),
'lv_LV' => array(
'name' => 'Latvian',
'native' => 'latvie&#353;u'
),
'lt_LT' => array(
'name' => 'Lithuanian',
'native' => 'lietuvi&#353;kai'
),
'mk_MK' => array(
'name' => 'Macedonian',
'native' => '&#1084;&#1072;&#1082;&#1077;&#1076;&#1086;&#1085;&#1089;&#1082;&#1080;'
),
'mi_NZ' => array(
'name' => 'te reo Māori',
'native' => 'te reo Māori'
),
'ms_MY' => array(
'name' => 'Malay',
'native' => 'Bahasa melayu'
),
'mt_MT' => array(
'name' => 'Maltese',
'native' => 'Malti'
),
'mr_IN' => array(
'name' => 'Marathi',
'native' => '&#2350;&#2352;&#2366;&#2336;&#2368;'
),
'ne_NP' => array(
'name' => 'Nepali',
'native' => '&#2344;&#2375;&#2346;&#2366;&#2354;&#2368;'
),
'nb_NO' => array(
'name' => 'Norwegian',
'native' => 'Norsk'
),
'om_ET' => array(
'name' => 'Oromo',
'native' => 'Afaan Oromo'
),
'fa_IR' => array(
'name' => 'Persian',
'native' => '&#1601;&#1575;&#1585;&#1587;&#1609;'
),
'pl_PL' => array(
'name' => 'Polish',
'native' => 'polski'
),
'pt_PT' => array(
'name' => 'Portuguese (Portugal)',
'native' => 'portugu&ecirc;s (Portugal)'
),
'pt_BR' => array(
'name' => 'Portuguese (Brazil)',
'native' => 'portugu&ecirc;s (Brazil)'
),
'pa_IN' => array(
'name' => 'Punjabi',
'native' => '&#2602;&#2672;&#2588;&#2622;&#2604;&#2624;'
),
'qu_PE' => array(
'name' => 'Quechua',
'native' => 'Quechua'
),
'rm_CH' => array(
'name' => 'Romansh',
'native' => 'rumantsch'
),
'ro_RO' => array(
'name' => 'Romanian',
'native' => 'rom&acirc;n'
),
'ru_RU' => array(
'name' => 'Russian',
'native' => '&#1056;&#1091;&#1089;&#1089;&#1082;&#1080;&#1081;'
),
'sco_SCO' => array(
'name' => 'Scots',
'native' => 'Scoats leid, Lallans'
),
'sr_RS' => array(
'name' => 'Serbian',
'native' => '&#1089;&#1088;&#1087;&#1089;&#1082;&#1080;'
),
'sk_SK' => array(
'name' => 'Slovak',
'native' => 'sloven&#269;ina'
),
'sl_SI' => array(
'name' => 'Slovenian',
'native' => 'sloven&#353;&#269;ina'
),
'es_ES' => array(
'name' => 'Spanish',
'native' => 'espa&ntilde;ol'
),
'sv_SE' => array(
'name' => 'Swedish',
'native' => 'Svenska'
),
'tl_PH' => array(
'name' => 'Tagalog',
'native' => 'Tagalog'
),
'ta_IN' => array(
'name' => 'Tamil',
'native' => '&#2980;&#2990;&#3007;&#2996;&#3021;'
),
'te_IN' => array(
'name' => 'Telugu',
'native' => '&#3108;&#3142;&#3122;&#3137;&#3095;&#3137;'
),
'to_TO' => array(
'name' => 'Tonga',
'native' => 'chiTonga'
),
'ts_ZA' => array(
'name' => 'Tsonga',
'native' => 'xiTshonga'
),
'tn_ZA' => array(
'name' => 'Tswana',
'native' => 'seTswana'
),
'tr_TR' => array(
'name' => 'Turkish',
'native' => 'T&uuml;rk&ccedil;e'
),
'tk_TM' => array(
'name' => 'Turkmen',
'native' => '&#1090;&#1199;&#1088;&#1082;m&#1077;&#1085;&#1095;&#1077;'
),
'tw_GH' => array(
'name' => 'Twi',
'native' => 'twi'
),
'uk_UA' => array(
'name' => 'Ukrainian',
'native' => '&#1059;&#1082;&#1088;&#1072;&#1111;&#1085;&#1089;&#1100;&#1082;&#1072;'
),
'ur_PK' => array(
'name' => 'Urdu',
'native' => '&#1575;&#1585;&#1583;&#1608;'
),
'uz_UZ' => array(
'name' => 'Uzbek',
'native' => '&#1118;&#1079;&#1073;&#1077;&#1082;'
),
've_ZA' => array(
'name' => 'Venda',
'native' => 'tshiVen&#x1E13;a'
),
'vi_VN' => array(
'name' => 'Vietnamese',
'native' => 'ti&#7871;ng vi&#7879;t'
),
'wo_SN' => array(
'name' => 'Wolof',
'native' => 'Wollof'
),
'xh_ZA' => array(
'name' => 'Xhosa',
'native' => 'isiXhosa'
),
'zu_ZA' => array(
'name' => 'Zulu',
'native' => 'isiZulu'
),
);
/**
* @config
* @var array
*/
private static $tinymce_lang = array(
'ar_EG' => 'ar',
'ca_AD' => 'ca',
'ca_ES' => 'ca',
'cs_CZ' => 'cs',
'cy_GB' => 'cy',
'da_DK' => 'da',
'da_GL' => 'da',
'de_AT' => 'de',
'de_BE' => 'de',
'de_CH' => 'de',
'de_DE' => 'de',
'de_LI' => 'de',
'de_LU' => 'de',
'de_BR' => 'de',
'de_US' => 'de',
'el_CY' => 'el',
'el_GR' => 'el',
'es_AR' => 'es',
'es_BO' => 'es',
'es_CL' => 'es',
'es_CO' => 'es',
'es_CR' => 'es',
'es_CU' => 'es',
'es_DO' => 'es',
'es_EC' => 'es',
'es_ES' => 'es',
'es_GQ' => 'es',
'es_GT' => 'es',
'es_HN' => 'es',
'es_MX' => 'es',
'es_NI' => 'es',
'es_PA' => 'es',
'es_PE' => 'es',
'es_PH' => 'es',
'es_PR' => 'es',
'es_PY' => 'es',
'es_SV' => 'es',
'es_UY' => 'es',
'es_VE' => 'es',
'es_AD' => 'es',
'es_BZ' => 'es',
'es_US' => 'es',
'fa_AF' => 'fa',
'fa_IR' => 'fa',
'fa_PK' => 'fa',
'fi_FI' => 'fi',
'fi_SE' => 'fi',
'fr_BE' => 'fr',
'fr_BF' => 'fr',
'fr_BI' => 'fr',
'fr_BJ' => 'fr',
'fr_CA' => 'fr_ca',
'fr_CF' => 'fr',
'fr_CG' => 'fr',
'fr_CH' => 'fr',
'fr_CI' => 'fr',
'fr_CM' => 'fr',
'fr_DJ' => 'fr',
'fr_DZ' => 'fr',
'fr_FR' => 'fr',
'fr_GA' => 'fr',
'fr_GF' => 'fr',
'fr_GN' => 'fr',
'fr_GP' => 'fr',
'fr_HT' => 'fr',
'fr_KM' => 'fr',
'fr_LU' => 'fr',
'fr_MA' => 'fr',
'fr_MC' => 'fr',
'fr_MG' => 'fr',
'fr_ML' => 'fr',
'fr_MQ' => 'fr',
'fr_MU' => 'fr',
'fr_NC' => 'fr',
'fr_NE' => 'fr',
'fr_PF' => 'fr',
'fr_PM' => 'fr',
'fr_RE' => 'fr',
'fr_RW' => 'fr',
'fr_SC' => 'fr',
'fr_SN' => 'fr',
'fr_SY' => 'fr',
'fr_TD' => 'fr',
'fr_TG' => 'fr',
'fr_TN' => 'fr',
'fr_VU' => 'fr',
'fr_WF' => 'fr',
'fr_YT' => 'fr',
'fr_GB' => 'fr',
'fr_US' => 'fr',
'he_IL' => 'he',
'hu_HU' => 'hu',
'hu_AT' => 'hu',
'hu_RO' => 'hu',
'hu_RS' => 'hu',
'is_IS' => 'is',
'it_CH' => 'it',
'it_IT' => 'it',
'it_SM' => 'it',
'it_FR' => 'it',
'it_HR' => 'it',
'it_US' => 'it',
'it_VA' => 'it',
'ja_JP' => 'ja',
'ko_KP' => 'ko',
'ko_KR' => 'ko',
'ko_CN' => 'ko',
'mi_NZ' => 'mi_NZ',
'nb_NO' => 'nb',
'nb_SJ' => 'nb',
'nl_AN' => 'nl',
'nl_AW' => 'nl',
'nl_BE' => 'nl',
'nl_NL' => 'nl',
'nl_SR' => 'nl',
'nn_NO' => 'nn',
'pl_PL' => 'pl',
'pl_UA' => 'pl',
'pt_AO' => 'pt',
'pt_BR' => 'pt',
'pt_CV' => 'pt',
'pt_GW' => 'pt',
'pt_MZ' => 'pt',
'pt_PT' => 'pt',
'pt_ST' => 'pt',
'pt_TL' => 'pt',
'ro_MD' => 'ro',
'ro_RO' => 'ro',
'ro_RS' => 'ro',
'ru_BY' => 'ru',
'ru_KG' => 'ru',
'ru_KZ' => 'ru',
'ru_RU' => 'ru',
'ru_SJ' => 'ru',
'ru_UA' => 'ru',
'si_LK' => 'si',
'sk_SK' => 'sk',
'sk_RS' => 'sk',
'sq_AL' => 'sq',
'sr_BA' => 'sr',
'sr_ME' => 'sr',
'sr_RS' => 'sr',
'sv_FI' => 'sv',
'sv_SE' => 'sv',
'tr_CY' => 'tr',
'tr_TR' => 'tr',
'tr_DE' => 'tr',
'tr_MK' => 'tr',
'uk_UA' => 'uk',
'vi_VN' => 'vi',
'vi_US' => 'vi',
'zh_CN' => 'zh-cn',
'zh_HK' => 'zh-cn',
'zh_MO' => 'zh-cn',
'zh_SG' => 'zh-cn',
'zh_TW' => 'zh-tw',
'zh_ID' => 'zh-cn',
'zh_MY' => 'zh-cn',
'zh_TH' => 'zh-cn',
'zh_US' => 'zn-cn',
);
/**
* @config
* @var array $likely_subtags Provides you "likely locales"
* for a given "short" language code. This is a guess,
* as we can't disambiguate from e.g. "en" to "en_US" - it
* could also mean "en_UK".
* @see http://www.unicode.org/cldr/data/charts/supplemental/likely_subtags.html
*/
private static $likely_subtags = array(
'aa' => 'aa_ET',
'ab' => 'ab_GE',
'ady' => 'ady_RU',
'af' => 'af_ZA',
'ak' => 'ak_GH',
'am' => 'am_ET',
'ar' => 'ar_EG',
'as' => 'as_IN',
'ast' => 'ast_ES',
'av' => 'av_RU',
'ay' => 'ay_BO',
'az' => 'az_AZ',
'az_Cyrl' => 'az_AZ',
'az_Arab' => 'az_IR',
'az_IR' => 'az_IR',
'ba' => 'ba_RU',
'be' => 'be_BY',
'bg' => 'bg_BG',
'bi' => 'bi_VU',
'bn' => 'bn_BD',
'bo' => 'bo_CN',
'bs' => 'bs_BA',
'ca' => 'ca_ES',
'ce' => 'ce_RU',
'ceb' => 'ceb_PH',
'ch' => 'ch_GU',
'chk' => 'chk_FM',
'crk' => 'crk_CA',
'cs' => 'cs_CZ',
'cwd' => 'cwd_CA',
'cy' => 'cy_GB',
'da' => 'da_DK',
'de' => 'de_DE',
'dv' => 'dv_MV',
'dz' => 'dz_BT',
'ee' => 'ee_GH',
'efi' => 'efi_NG',
'el' => 'el_GR',
'en' => 'en_US',
'es' => 'es_ES',
'et' => 'et_EE',
'eu' => 'eu_ES',
'eo' => 'eo_XX',
'fa' => 'fa_IR',
'fi' => 'fi_FI',
'fil' => 'fil_PH',
'fj' => 'fj_FJ',
'fo' => 'fo_FO',
'fr' => 'fr_FR',
'fur' => 'fur_IT',
'fy' => 'fy_NL',
'ga' => 'ga_IE',
'gaa' => 'gaa_GH',
'gd' => 'gd_GB',
'gil' => 'gil_KI',
'gl' => 'gl_ES',
'gn' => 'gn_PY',
'gu' => 'gu_IN',
'ha' => 'ha_NG',
'ha_Arab' => 'ha_SD',
'ha_SD' => 'ha_SD',
'haw' => 'haw_US',
'he' => 'he_IL',
'hi' => 'hi_IN',
'hil' => 'hil_PH',
'ho' => 'ho_PG',
'hr' => 'hr_HR',
'ht' => 'ht_HT',
'hu' => 'hu_HU',
'hy' => 'hy_AM',
'id' => 'id_ID',
'ig' => 'ig_NG',
'ii' => 'ii_CN',
'ilo' => 'ilo_PH',
'inh' => 'inh_RU',
'is' => 'is_IS',
'it' => 'it_IT',
'iu' => 'iu_CA',
'ja' => 'ja_JP',
'jv' => 'jv_ID',
'ka' => 'ka_GE',
'kaj' => 'kaj_NG',
'kam' => 'kam_KE',
'kbd' => 'kbd_RU',
'kha' => 'kha_IN',
'kk' => 'kk_KZ',
'kl' => 'kl_GL',
'km' => 'km_KH',
'kn' => 'kn_IN',
'ko' => 'ko_KR',
'koi' => 'koi_RU',
'kok' => 'kok_IN',
'kos' => 'kos_FM',
'kpe' => 'kpe_LR',
'kpv' => 'kpv_RU',
'krc' => 'krc_RU',
'ks' => 'ks_IN',
'ku' => 'ku_IQ',
'ku_Latn' => 'ku_TR',
'ku_TR' => 'ku_TR',
'kum' => 'kum_RU',
'kxm' => 'kxm_TH',
'ky' => 'ky_KG',
'la' => 'la_VA',
'lah' => 'lah_PK',
'lb' => 'lb_LU',
'lbe' => 'lbe_RU',
'lez' => 'lez_RU',
'ln' => 'ln_CD',
'lo' => 'lo_LA',
'lt' => 'lt_LT',
'lv' => 'lv_LV',
'mai' => 'mai_IN',
'mdf' => 'mdf_RU',
'mdh' => 'mdh_PH',
'mg' => 'mg_MG',
'mh' => 'mh_MH',
'mi' => 'mi_NZ',
'mk' => 'mk_MK',
'ml' => 'ml_IN',
'mn' => 'mn_MN',
'mn_CN' => 'mn_CN',
'mn_Mong' => 'mn_CN',
'mr' => 'mr_IN',
'ms' => 'ms_MY',
'mt' => 'mt_MT',
'my' => 'my_MM',
'myv' => 'myv_RU',
'na' => 'na_NR',
'nb' => 'nb_NO',
'ne' => 'ne_NP',
'niu' => 'niu_NU',
'nl' => 'nl_NL',
'nn' => 'nn_NO',
'nr' => 'nr_ZA',
'nso' => 'nso_ZA',
'ny' => 'ny_MW',
'om' => 'om_ET',
'or' => 'or_IN',
'os' => 'os_GE',
'pa' => 'pa_IN',
'pa_Arab' => 'pa_PK',
'pa_PK' => 'pa_PK',
'pag' => 'pag_PH',
'pap' => 'pap_AN',
'pau' => 'pau_PW',
'pl' => 'pl_PL',
'pon' => 'pon_FM',
'ps' => 'ps_AF',
'pt' => 'pt_BR',
'qu' => 'qu_PE',
'rm' => 'rm_CH',
'rn' => 'rn_BI',
'ro' => 'ro_RO',
'ru' => 'ru_RU',
'rw' => 'rw_RW',
'sa' => 'sa_IN',
'sah' => 'sah_RU',
'sat' => 'sat_IN',
'sd' => 'sd_IN',
'se' => 'se_NO',
'sg' => 'sg_CF',
'si' => 'si_LK',
'sid' => 'sid_ET',
'sk' => 'sk_SK',
'sl' => 'sl_SI',
'sm' => 'sm_WS',
'sn' => 'sn_ZW',
'so' => 'so_SO',
'sq' => 'sq_AL',
'sr' => 'sr_RS',
'ss' => 'ss_ZA',
'st' => 'st_ZA',
'su' => 'su_ID',
'sv' => 'sv_SE',
'sw' => 'sw_TZ',
'swb' => 'swb_KM',
'ta' => 'ta_IN',
'te' => 'te_IN',
'tet' => 'tet_TL',
'tg' => 'tg_TJ',
'th' => 'th_TH',
'ti' => 'ti_ET',
'tig' => 'tig_ER',
'tk' => 'tk_TM',
'tkl' => 'tkl_TK',
'tl' => 'tl_PH',
'tn' => 'tn_ZA',
'to' => 'to_TO',
'tpi' => 'tpi_PG',
'tr' => 'tr_TR',
'trv' => 'trv_TW',
'ts' => 'ts_ZA',
'tsg' => 'tsg_PH',
'tt' => 'tt_RU',
'tts' => 'tts_TH',
'tvl' => 'tvl_TV',
'tw' => 'tw_GH',
'ty' => 'ty_PF',
'tyv' => 'tyv_RU',
'udm' => 'udm_RU',
'ug' => 'ug_CN',
'uk' => 'uk_UA',
'uli' => 'uli_FM',
'und' => 'en_US',
'und_AD' => 'ca_AD',
'und_AE' => 'ar_AE',
'und_AF' => 'fa_AF',
'und_AL' => 'sq_AL',
'und_AM' => 'hy_AM',
'und_AN' => 'pap_AN',
'und_AO' => 'pt_AO',
'und_AR' => 'es_AR',
'und_AS' => 'sm_AS',
'und_AT' => 'de_AT',
'und_AW' => 'nl_AW',
'und_AX' => 'sv_AX',
'und_AZ' => 'az_AZ',
'und_Arab' => 'ar_EG',
'und_Arab_CN' => 'ug_CN',
'und_Arab_DJ' => 'ar_DJ',
'und_Arab_ER' => 'ar_ER',
'und_Arab_IL' => 'ar_IL',
'und_Arab_IN' => 'ur_IN',
'und_Arab_PK' => 'ur_PK',
'und_Armn' => 'hy_AM',
'und_BA' => 'bs_BA',
'und_BD' => 'bn_BD',
'und_BE' => 'nl_BE',
'und_BF' => 'fr_BF',
'und_BG' => 'bg_BG',
'und_BH' => 'ar_BH',
'und_BI' => 'rn_BI',
'und_BJ' => 'fr_BJ',
'und_BL' => 'fr_BL',
'und_BN' => 'ms_BN',
'und_BO' => 'es_BO',
'und_BR' => 'pt_BR',
'und_BT' => 'dz_BT',
'und_BY' => 'be_BY',
'und_Beng' => 'bn_BD',
'und_CD' => 'fr_CD',
'und_CF' => 'sg_CF',
'und_CG' => 'ln_CG',
'und_CH' => 'de_CH',
'und_CI' => 'fr_CI',
'und_CL' => 'es_CL',
'und_CM' => 'fr_CM',
'und_CN' => 'zh_CN',
'und_CO' => 'es_CO',
'und_CR' => 'es_CR',
'und_CU' => 'es_CU',
'und_CV' => 'pt_CV',
'und_CY' => 'el_CY',
'und_CZ' => 'cs_CZ',
'und_Cans' => 'cwd_CA',
'und_Cyrl' => 'ru_RU',
'und_Cyrl_BA' => 'sr_BA',
'und_Cyrl_GE' => 'ab_GE',
'und_DE' => 'de_DE',
'und_DJ' => 'aa_DJ',
'und_DK' => 'da_DK',
'und_DO' => 'es_DO',
'und_DZ' => 'ar_DZ',
'und_Deva' => 'hi_IN',
'und_EC' => 'es_EC',
'und_EE' => 'et_EE',
'und_EG' => 'ar_EG',
'und_EH' => 'ar_EH',
'und_ER' => 'ti_ER',
'und_ES' => 'es_ES',
'und_ET' => 'am_ET',
'und_Ethi' => 'am_ET',
'und_FI' => 'fi_FI',
'und_FJ' => 'fj_FJ',
'und_FM' => 'chk_FM',
'und_FO' => 'fo_FO',
'und_FR' => 'fr_FR',
'und_GA' => 'fr_GA',
'und_GE' => 'ka_GE',
'und_GF' => 'fr_GF',
'und_GH' => 'ak_GH',
'und_GL' => 'kl_GL',
'und_GN' => 'fr_GN',
'und_GP' => 'fr_GP',
'und_GQ' => 'fr_GQ',
'und_GR' => 'el_GR',
'und_GT' => 'es_GT',
'und_GU' => 'ch_GU',
'und_GW' => 'pt_GW',
'und_Geor' => 'ka_GE',
'und_Grek' => 'el_GR',
'und_Gujr' => 'gu_IN',
'und_Guru' => 'pa_IN',
'und_HK' => 'zh_HK',
'und_HN' => 'es_HN',
'und_HR' => 'hr_HR',
'und_HT' => 'ht_HT',
'und_HU' => 'hu_HU',
'und_Hani' => 'zh_CN',
'und_Hans' => 'zh_CN',
'und_Hant' => 'zh_TW',
'und_Hebr' => 'he_IL',
'und_ID' => 'id_ID',
'und_IL' => 'he_IL',
'und_IN' => 'hi_IN',
'und_IQ' => 'ar_IQ',
'und_IR' => 'fa_IR',
'und_IS' => 'is_IS',
'und_IT' => 'it_IT',
'und_JO' => 'ar_JO',
'und_JP' => 'ja_JP',
'und_Jpan' => 'ja_JP',
'und_KG' => 'ky_KG',
'und_KH' => 'km_KH',
'und_KM' => 'ar_KM',
'und_KP' => 'ko_KP',
'und_KR' => 'ko_KR',
'und_KW' => 'ar_KW',
'und_KZ' => 'ru_KZ',
'und_Khmr' => 'km_KH',
'und_Knda' => 'kn_IN',
'und_Kore' => 'ko_KR',
'und_LA' => 'lo_LA',
'und_LB' => 'ar_LB',
'und_LI' => 'de_LI',
'und_LK' => 'si_LK',
'und_LS' => 'st_LS',
'und_LT' => 'lt_LT',
'und_LU' => 'fr_LU',
'und_LV' => 'lv_LV',
'und_LY' => 'ar_LY',
'und_Laoo' => 'lo_LA',
'und_Latn_CN' => 'ii_CN',
'und_Latn_CY' => 'tr_CY',
'und_Latn_DZ' => 'fr_DZ',
'und_Latn_ET' => 'om_ET',
'und_Latn_KM' => 'fr_KM',
'und_Latn_MA' => 'fr_MA',
'und_Latn_MK' => 'sq_MK',
'und_Latn_SY' => 'fr_SY',
'und_Latn_TD' => 'fr_TD',
'und_Latn_TN' => 'fr_TN',
'und_MA' => 'ar_MA',
'und_MC' => 'fr_MC',
'und_MD' => 'ro_MD',
'und_ME' => 'sr_ME',
'und_MF' => 'fr_MF',
'und_MG' => 'mg_MG',
'und_MH' => 'mh_MH',
'und_MK' => 'mk_MK',
'und_ML' => 'fr_ML',
'und_MM' => 'my_MM',
'und_MN' => 'mn_MN',
'und_MO' => 'zh_MO',
'und_MQ' => 'fr_MQ',
'und_MR' => 'ar_MR',
'und_MT' => 'mt_MT',
'und_MV' => 'dv_MV',
'und_MW' => 'ny_MW',
'und_MX' => 'es_MX',
'und_MY' => 'ms_MY',
'und_MZ' => 'pt_MZ',
'und_Mlym' => 'ml_IN',
'und_Mong' => 'mn_CN',
'und_Mymr' => 'my_MM',
'und_NC' => 'fr_NC',
'und_NE' => 'ha_NE',
'und_NG' => 'ha_NG',
'und_NI' => 'es_NI',
'und_NL' => 'nl_NL',
'und_NO' => 'nb_NO',
'und_NP' => 'ne_NP',
'und_NR' => 'na_NR',
'und_NU' => 'niu_NU',
'und_OM' => 'ar_OM',
'und_Orya' => 'or_IN',
'und_PA' => 'es_PA',
'und_PE' => 'es_PE',
'und_PF' => 'ty_PF',
'und_PG' => 'tpi_PG',
'und_PH' => 'fil_PH',
'und_PK' => 'ur_PK',
'und_PL' => 'pl_PL',
'und_PM' => 'fr_PM',
'und_PR' => 'es_PR',
'und_PS' => 'ar_PS',
'und_PT' => 'pt_PT',
'und_PW' => 'pau_PW',
'und_PY' => 'gn_PY',
'und_QA' => 'ar_QA',
'und_RE' => 'fr_RE',
'und_RO' => 'ro_RO',
'und_RS' => 'sr_RS',
'und_RU' => 'ru_RU',
'und_RW' => 'rw_RW',
'und_SA' => 'ar_SA',
'und_SD' => 'ar_SD',
'und_SE' => 'sv_SE',
'und_SI' => 'sl_SI',
'und_SJ' => 'nb_SJ',
'und_SK' => 'sk_SK',
'und_SM' => 'it_SM',
'und_SN' => 'fr_SN',
'und_SO' => 'so_SO',
'und_SR' => 'nl_SR',
'und_ST' => 'pt_ST',
'und_SV' => 'es_SV',
'und_SY' => 'ar_SY',
'und_Sinh' => 'si_LK',
'und_TD' => 'ar_TD',
'und_TG' => 'ee_TG',
'und_TH' => 'th_TH',
'und_TJ' => 'tg_TJ',
'und_TK' => 'tkl_TK',
'und_TL' => 'tet_TL',
'und_TM' => 'tk_TM',
'und_TN' => 'ar_TN',
'und_TO' => 'to_TO',
'und_TR' => 'tr_TR',
'und_TV' => 'tvl_TV',
'und_TW' => 'zh_TW',
'und_Taml' => 'ta_IN',
'und_Telu' => 'te_IN',
'und_Thaa' => 'dv_MV',
'und_Thai' => 'th_TH',
'und_Tibt' => 'bo_CN',
'und_UA' => 'uk_UA',
'und_UY' => 'es_UY',
'und_UZ' => 'uz_UZ',
'und_VA' => 'la_VA',
'und_VE' => 'es_VE',
'und_VN' => 'vi_VN',
'und_VU' => 'fr_VU',
'und_WF' => 'fr_WF',
'und_WS' => 'sm_WS',
'und_YE' => 'ar_YE',
'und_YT' => 'fr_YT',
'und_ZW' => 'sn_ZW',
'ur' => 'ur_PK',
'uz' => 'uz_UZ',
'uz_AF' => 'uz_AF',
'uz_Arab' => 'uz_AF',
've' => 've_ZA',
'vi' => 'vi_VN',
'wal' => 'wal_ET',
'war' => 'war_PH',
'wo' => 'wo_SN',
'xh' => 'xh_ZA',
'yap' => 'yap_FM',
'yo' => 'yo_NG',
'za' => 'za_CN',
'zh' => 'zh_CN',
'zh_HK' => 'zh_HK',
'zh_Hani' => 'zh_CN',
'zh_Hant' => 'zh_TW',
'zh_MO' => 'zh_MO',
'zh_TW' => 'zh_TW',
'zu' => 'zu_ZA',
);
/**
* This is the main translator function. Returns the string defined by $class and $entity according to the
* currently set locale.
*
* @param string $entity Entity that identifies the string. It must be in the form "Namespace.Entity" where
* Namespace will be usually the class name where this string is used and Entity identifies
* the string inside the namespace.
* @param string $string The original string itself. In a usual call this is a mandatory parameter, but if you are
* reusing a string which has already been "declared" (using another call to this function,
* with the same class and entity), you can omit it.
* @param string $context (optional) If the string can be difficult to translate by any reason, you can help
* translators with some more info using this param
* @param string injectionArray (optional) array of key value pairs that are used to replace corresponding
* expressions in {curly brackets} in the $string. The injection array can also be
* used as the their argument to the _t() function
* @return string The translated string, according to the currently set locale {@link i18n::set_locale()}
*/
public static function _t($entity, $string = "", $context = "", $injection = "") {
if(is_numeric($context) && in_array($context, array(PR_LOW, PR_MEDIUM, PR_HIGH))) {
Deprecation::notice(
'3.0',
'The $priority argument to _t() is deprecated, please use module inclusion priorities instead',
Deprecation::SCOPE_GLOBAL
);
}
//fetch the injection array out of the parameters (if it is present)
$argList = func_get_args();
$argNum = func_num_args();
//_t($entity, $string = "", $context (optional), $injectionArray (optional))
$injectionArray = null;
for($i = 0; $i < $argNum; $i++) {
if (is_array($argList[$i])) { //we have reached the injectionArray
$injectionArray = $argList[$i]; //any array in the args will be the injection array
}
}
// get current locale (either default or user preference)
$locale = i18n::get_locale();
$lang = i18n::get_lang_from_locale($locale);
// Only call getter if static isn't already defined (for performance reasons)
$translatorsByPrio = self::$translators;
if(!$translatorsByPrio) $translatorsByPrio = self::get_translators();
$returnValue = (is_string($string)) ? $string : ''; // Fall back to default string argument
foreach($translatorsByPrio as $priority => $translators) {
foreach($translators as $name => $translator) {
$adapter = $translator->getAdapter();
// at this point, we need to ensure the language and locale are loaded
// as include_by_locale() doesn't load a fallback.
// TODO Remove reliance on global state, by refactoring into an i18nTranslatorManager
// which is instanciated by core with a $clean instance variable.
if(!$adapter->isAvailable($lang)) {
i18n::include_by_locale($lang);
}
if(!$adapter->isAvailable($locale)) {
i18n::include_by_locale($locale);
}
$translation = $adapter->translate($entity, $locale);
// Return translation only if we found a match thats not the entity itself (Zend fallback)
if($translation && $translation != $entity) {
$returnValue = $translation;
break 2;
}
}
}
// inject the variables from injectionArray (if present)
if($injectionArray) {
$regex = '/\{[\w\d]*\}/i';
if(!preg_match($regex, $returnValue)) {
// Legacy mode: If no injection placeholders are found,
// replace sprintf placeholders in fixed order.
// Fail silently in case the translation is outdated
preg_match_all('/%[s,d]/', $returnValue, $returnValueArgs);
if($returnValueArgs) foreach($returnValueArgs[0] as $i => $returnValueArg) {
if($i >= count($injectionArray)) {
$injectionArray[] = '';
}
}
$replaced = vsprintf($returnValue, array_values($injectionArray));
if($replaced) $returnValue = $replaced;
} else if(!ArrayLib::is_associative($injectionArray)) {
// Legacy mode: If injection placeholders are found,
// but parameters are passed without names, replace them in fixed order.
$returnValue = preg_replace_callback(
$regex,
function($matches) use(&$injectionArray) {
return $injectionArray ? array_shift($injectionArray) : '';
},
$returnValue
);
} else {
// Standard placeholder replacement with named injections and variable order.
foreach($injectionArray as $variable => $injection) {
$placeholder = '{'.$variable.'}';
$returnValue = str_replace($placeholder, $injection, $returnValue, $count);
if(!$count) {
SS_Log::log(sprintf(
"Couldn't find placeholder '%s' in translation string '%s' (id: '%s')",
$placeholder,
$returnValue,
$entity
), SS_Log::NOTICE);
}
}
}
}
return $returnValue;
}
/**
* @return array Array of priority keys to instances of Zend_Translate, mapped by name.
*/
public static function get_translators() {
if(!Zend_Translate::getCache()) {
Zend_Translate::setCache(self::get_cache());
}
if(!self::$translators) {
$defaultPriority = 10;
self::$translators[$defaultPriority] = array(
'core' => new Zend_Translate(array(
'adapter' => 'i18nRailsYamlAdapter',
'locale' => self::$default_locale,
'disableNotices' => true,
))
);
i18n::include_by_locale('en');
i18n::include_by_locale('en_US');
}
return self::$translators;
}
/**
* @param String
* @return Zend_Translate
*/
public static function get_translator($name) {
foreach(self::get_translators() as $priority => $translators) {
if(isset($translators[$name])) return $translators[$name];
}
return false;
}
/**
* @param Zend_Translate Needs to implement {@link i18nTranslateAdapterInterface}
* @param String If left blank will override the default translator.
* @param Int
*/
public static function register_translator($translator, $name, $priority = 10) {
if (!is_int($priority)) throw new InvalidArgumentException("register_translator expects an int priority");
// Ensure it's not there. If it is, we're replacing it. It may exist in a different priority.
self::unregister_translator($name);
// Add our new translator
if(!isset(self::$translators[$priority])) self::$translators[$priority] = array();
self::$translators[$priority][$name] = $translator;
// Resort array, ensuring highest priority comes first
krsort(self::$translators);
i18n::include_by_locale('en_US');
i18n::include_by_locale('en');
}
/**
* @param String
*/
public static function unregister_translator($name) {
foreach (self::get_translators() as $priority => $translators) {
if (isset($translators[$name])) unset(self::$translators[$priority][$name]);
}
}
/**
* Get a list of commonly used languages
*
* @param boolean $native Use native names for languages instead of English ones
* @return list of languages in the form 'code' => 'name'
*/
public static function get_common_languages($native = false) {
$languages = array();
foreach (Config::inst()->get('i18n', 'common_languages') as $code => $name) {
$languages[$code] = ($native ? $name['native'] : $name['name']);
}
return $languages;
}
/**
* Get a list of commonly used locales
*
* @param boolean $native Use native names for locale instead of English ones
* @return list of languages in the form 'code' => 'name'
*/
public static function get_common_locales($native = false) {
$languages = array();
foreach (Config::inst()->get('i18n', 'common_locales') as $code => $name) {
$languages[$code] = ($native ? $name['native'] : $name['name']);
}
return $languages;
}
/**
* Get a list of locales (code => language and country)
*
* @return list of languages in the form 'code' => 'name'
*/
public static function get_locale_list() {
Deprecation::notice('3.2', 'Use the "i18n.all_locales" config setting instead');
return (array)Config::inst()->get('i18n', 'all_locales');
}
/**
* Matches a given locale with the closest translation available in the system
*
* @param string $locale locale code
* @return string Locale of closest available translation, if available
*/
public static function get_closest_translation($locale) {
// Check if exact match
$pool = self::get_existing_translations();
if(isset($pool[$locale])) return $locale;
// Fallback to best locale for common language
$lang = self::get_lang_from_locale($locale);
$candidate = self::get_locale_from_lang($lang);
if(isset($pool[$candidate])) return $candidate;
}
/**
* Searches the root-directory for module-directories
* (identified by having a _config.php on their first directory-level).
* Finds locales by filename convention ("<locale>.<extension>", e.g. "de_AT.yml").
*
* @return array
*/
public static function get_existing_translations() {
$locales = array();
// TODO Inspect themes
$modules = SS_ClassLoader::instance()->getManifest()->getModules();
foreach($modules as $module) {
if(!file_exists("{$module}/lang/")) continue;
$allLocales = Config::inst()->get('i18n', 'all_locales');
$moduleLocales = scandir("{$module}/lang/");
foreach($moduleLocales as $moduleLocale) {
$locale = pathinfo($moduleLocale, PATHINFO_FILENAME);
$ext = pathinfo($moduleLocale, PATHINFO_EXTENSION);
if($locale && in_array($ext, array('php','yml'))) {
// Normalize locale to include likely region tag, avoid repetition in locale labels
// TODO Replace with CLDR list of actually available languages/regions
// Only allow explicitly registered locales, otherwise we'll get into trouble
// if the locale doesn't exist in Zend's CLDR data
$fullLocale = self::get_locale_from_lang($locale);
if(isset($allLocales[$fullLocale])) {
$locales[$fullLocale] = $allLocales[$fullLocale];
}
}
}
}
// sort by title (not locale)
asort($locales);
return $locales;
}
/**
* Get a name from a language code (two characters, e.g. "en").
*
* @see get_locale_name()
*
* @param mixed $code Language code
* @param boolean $native If true, the native name will be returned
* @return Name of the language
*/
public static function get_language_name($code, $native = false) {
$langs = Config::inst()->get('i18n', 'common_languages');
if($native) {
return (isset($langs[$code]['native'])) ? $langs[$code]['native'] : false;
} else {
return (isset($langs[$code]['name'])) ? $langs[$code]['name'] : false;
}
}
/**
* Get a name from a locale code (xx_YY).
*
* @see get_language_name()
*
* @param mixed $code locale code
* @return Name of the locale
*/
public static function get_locale_name($code) {
$langs = self::config()->all_locales;
return isset($langs[$code]) ? $langs[$code] : false;
}
/**
* Get a code from an English language name
*
* @param mixed $name Name of the language
* @return Language code (if the name is not found, it'll return the passed name)
*/
public static function get_language_code($name) {
$code = array_search($name,self::get_common_languages());
return ($code ? $code : $name);
}
/**
* Get the current tinyMCE language
*
* @return Language
*/
public static function get_tinymce_lang() {
$lang = Config::inst()->get('i18n', 'tinymce_lang');
if(isset($lang[self::get_locale()])) {
return $lang[self::get_locale()];
}
return 'en';
}
/**
* Searches the root-directory for module-directories
* (identified by having a _config.php on their first directory-level
* and a language-file with the default locale in the /lang-subdirectory).
*
* @return array
*/
public static function get_translatable_modules() {
$translatableModules = array();
$baseDir = Director::baseFolder();
$modules = scandir($baseDir);
foreach($modules as $module) {
$moduleDir = $baseDir . DIRECTORY_SEPARATOR . $module;
if(
is_dir($moduleDir)
&& is_file($moduleDir . DIRECTORY_SEPARATOR . "_config.php")
&& is_file($moduleDir . DIRECTORY_SEPARATOR . "lang" . DIRECTORY_SEPARATOR
. self::$default_locale . ".php")
) {
$translatableModules[] = $module;
}
}
return $translatableModules;
}
/**
* Returns the "short" language name from a locale,
* e.g. "en_US" would return "en".
*
* @param string $locale E.g. "en_US"
* @return string Short language code, e.g. "en"
*/
public static function get_lang_from_locale($locale) {
return preg_replace('/(_|-).*/', '', $locale);
}
/**
* Provides you "likely locales"
* for a given "short" language code. This is a guess,
* as we can't disambiguate from e.g. "en" to "en_US" - it
* could also mean "en_UK". Based on the Unicode CLDR
* project.
* @see http://www.unicode.org/cldr/data/charts/supplemental/likely_subtags.html
*
* @param string $lang Short language code, e.g. "en"
* @return string Long locale, e.g. "en_US"
*/
public static function get_locale_from_lang($lang) {
$subtags = Config::inst()->get('i18n', 'likely_subtags');
if(preg_match('/\-|_/', $lang)) {
return str_replace('-', '_', $lang);
} else if(isset($subtags[$lang])) {
return $subtags[$lang];
} else {
return $lang . '_' . strtoupper($lang);
}
}
/**
* Gets a RFC 1766 compatible language code,
* e.g. "en-US".
*
* @see http://www.ietf.org/rfc/rfc1766.txt
* @see http://tools.ietf.org/html/rfc2616#section-3.10
*
* @param string $locale
* @return string
*/
public static function convert_rfc1766($locale) {
return str_replace('_','-', $locale);
}
/**
* Given a PHP class name, finds the module where it's located.
*
* @param string $name
* @return string
*/
public static function get_owner_module($name) {
$manifest = SS_ClassLoader::instance()->getManifest();
$path = $manifest->getItemPath($name);
if (!$path) {
return false;
}
$path = Director::makeRelative($path);
$path = str_replace('\\', '/', $path);
$parts = explode('/', trim($path, '/'));
return array_shift($parts);
}
/**
* Validates a "long" locale format (e.g. "en_US")
* by checking it against {@link $all_locales}.
*
* To add a locale to {@link $all_locales}, use the following example
* in your mysite/_config.php:
* <code>
* i18n::$allowed_locales['xx_XX'] = '<Language name>';
* </code>
*
* Note: Does not check for {@link $allowed_locales}.
*
* @return boolean
*/
public static function validate_locale($locale) {
// Convert en-US to en_US
$locale = str_replace('-', '_', $locale);
return (array_key_exists($locale, Config::inst()->get('i18n', 'all_locales')));
}
/**
* Set the current locale, used as the default for
* any localized classes, such as {@link FormField} or {@link DBField}
* instances. Locales can also be persisted in {@link Member->Locale},
* for example in the {@link CMSMain} interface the Member locale
* overrules the global locale value set here.
*
* @param string $locale Locale to be set. See
* http://unicode.org/cldr/data/diff/supplemental/languages_and_territories.html for a list
* of possible locales.
*/
public static function set_locale($locale) {
if ($locale) self::$current_locale = $locale;
}
/**
* Get the current locale.
* Used by {@link Member::populateDefaults()}
*
* @return string Current locale in the system
*/
public static function get_locale() {
return (!empty(self::$current_locale)) ? self::$current_locale : self::$default_locale;
}
/**
* This is the "fallback locale", in case resources with the "current locale"
* (set through {@link set_locale()}) can't be found.
*
* If you just want to globally read/write a different locale (e.g. in a CMS interface),
* please use {@link get_locale()} and {@link set_locale()} instead.
*
* For example, {@link Requirements::add_i18n_javascript()} and {@link i18n::include_by_class()}
* use this "fallback locale" value to include fallback language files.
*
* @return String
*/
public static function default_locale() {
return self::$default_locale;
}
/**
* See {@link default_locale()} for usage.
*
*
* @param String $locale
*/
public static function set_default_locale($locale) {
self::$default_locale = $locale;
}
/**
* Returns the script direction in format compatible with the HTML "dir" attribute.
*
* @see http://www.w3.org/International/tutorials/bidi-xhtml/
* @param String $locale Optional locale incl. region (underscored)
* @return String "rtl" or "ltr"
*/
public static function get_script_direction($locale = null) {
require_once 'Zend/Locale/Data.php';
if(!$locale) $locale = i18n::get_locale();
try {
$dir = Zend_Locale_Data::getList($locale, 'layout');
} catch(Zend_Locale_Exception $e) {
$dir = Zend_Locale_Data::getList(i18n::get_lang_from_locale($locale), 'layout');
}
return ($dir && $dir['characters'] == 'right-to-left') ? 'rtl' : 'ltr';
}
/**
* Includes all available language files for a certain defined locale.
*
* @param string $locale All resources from any module in locale $locale will be loaded
* @param Boolean $clean Clean old caches?
*/
public static function include_by_locale($locale, $clean = false) {
if($clean) {
self::flush();
}
// Get list of module => path pairs, and then just the names
$modules = SS_ClassLoader::instance()->getManifest()->getModules();
$moduleNames = array_keys($modules);
// Remove the "project" module from the list - we'll add it back specially later if needed
global $project;
if (($idx = array_search($project, $moduleNames)) !== false) array_splice($moduleNames, $idx, 1);
// Get the order from the config syste,
$order = Config::inst()->get('i18n', 'module_priority');
// Find all modules that don't have their order specified by the config system
$unspecified = array_diff($moduleNames, $order);
// If the placeholder "other_modules" exists in the order array, replace it by the unspecified modules
if (($idx = array_search('other_modules', $order)) !== false) array_splice($order, $idx, 1, $unspecified);
// Otherwise just jam them on the front
else array_splice($order, 0, 0, $unspecified);
// Put the project module back in at the begining if it wasn't specified by the config system
if (!in_array($project, $order)) array_unshift($order, $project);
$sortedModules = array();
foreach ($order as $module) {
if (isset($modules[$module])) $sortedModules[$module] = $modules[$module];
}
$sortedModules = array_reverse($sortedModules, true);
// Loop in reverse order, meaning the translator with the highest priority goes first
$translators = array_reverse(self::get_translators(), true);
foreach($translators as $priority => $translators) {
foreach($translators as $name => $translator) {
$adapter = $translator->getAdapter();
// Load translations from modules
foreach($sortedModules as $module) {
$filename = $adapter->getFilenameForLocale($locale);
$filepath = "{$module}/lang/" . $filename;
if($filename && !file_exists($filepath)) continue;
$adapter->addTranslation(
array('content' => $filepath, 'locale' => $locale)
);
}
// Load translations from themes
// TODO Replace with theme listing once implemented in TemplateManifest
$themesBase = Director::baseFolder() . '/themes';
if(is_dir($themesBase)) {
foreach(scandir($themesBase) as $theme) {
if(
strpos($theme, Config::inst()->get('SSViewer', 'theme')) === 0
&& file_exists("{$themesBase}/{$theme}/lang/")
) {
$filename = $adapter->getFilenameForLocale($locale);
$filepath = "{$themesBase}/{$theme}/lang/" . $filename;
if($filename && !file_exists($filepath)) continue;
$adapter->addTranslation(
array('content' => $filepath, 'locale' => $locale)
);
}
}
}
// Add empty translations to ensure the locales are "registered" with isAvailable(),
// and the next invocation of include_by_locale() doesn't cause a new reparse.
$adapter->addTranslation(
array(
// Cached by content hash, so needs to be locale dependent
'content' => array($locale => $locale),
'locale' => $locale,
'usetranslateadapter' => true
)
);
}
}
}
/**
* Given a class name (a "locale namespace"), will search for its module and, if available,
* will load the resources for the currently defined locale.
* If not available, the original English resource will be loaded instead (to avoid blanks)
*
* @param string $class Resources for this class will be included, according to the set locale.
*/
public static function include_by_class($class) {
$module = self::get_owner_module($class);
$translators = array_reverse(self::get_translators(), true);
foreach($translators as $priority => $translators) {
foreach($translators as $name => $translator) {
$adapter = $translator->getAdapter();
$filename = $adapter->getFilenameForLocale(self::get_locale());
$filepath = "{$module}/lang/" . $filename;
if($filename && !file_exists($filepath)) continue;
$adapter->addTranslation(array(
'content' => $filepath,
'locale' => self::get_locale()
));
}
}
}
public static function get_template_global_variables() {
return array(
'i18nLocale' => 'get_locale',
'get_locale',
'i18nScriptDirection' => 'get_script_direction',
);
}
}