Version: current

       1 <?php
       2 /**
       3  * Base-class for storage and retrieval of translated entities.
       4  *
       5  * Please see the {@link Translatable} extension for managing translations of database-content.
       6  *
       7  * <b>Usage</b>
       8  *
       9  * PHP:
      10  * <code>
      11  * _t('MyNamespace.MYENTITY', 'My default natural language value');
      12  * _t('MyNamespace.MYENTITY', 'My default natural language value', PR_MEDIUM, 'My explanatory context');
      13  * sprintf(_t('MyNamespace.MYENTITY', 'Counting %s things'), 42);
      14  * </code>
      15  *
      16  * Templates:
      17  * <code>
      18  * <% _t('MyNamespace.MYENTITY', 'My default natural language value') %>
      19  * <% sprintf(_t('MyNamespace.MYENTITY','Counting %s things'),$ThingsCount) %>
      20  * </code>
      21  *
      22  * Javascript (see sapphire/javascript/i18n.js):
      23  * <code>
      24  * ss.i18n._t('MyEntity.MyNamespace','My default natural language value');
      25  * </code>
      26  *
      27  * File-based i18n-translations always have a "locale" (e.g. 'en_US').
      28  * Common language names (e.g. 'en') are mainly used in {@link Translatable} for
      29  * database-entities.
      30  *
      31  * <b>Text Collection</b>
      32  *
      33  * Features a "textcollector-mode" that parses all files with a certain extension
      34  * (currently *.php and *.ss) for new translatable strings. Textcollector will write
      35  * updated string-tables to their respective folders inside the module, and automatically
      36  * namespace entities to the classes/templates they are found in (e.g. $lang['en_US']['AssetAdmin']['UPLOADFILES']).
      37  *
      38  * Caution: Does not apply any character-set conversion, it is assumed that all content
      39  * is stored and represented in UTF-8 (Unicode). Please make sure your files are created with the correct
      40  * character-set, and your HTML-templates render UTF-8.
      41  *
      42  * Caution: The language file has to be stored in the same module path as the "filename namespaces"
      43  * on the entities. So an entity stored in $lang['en_US']['AssetAdmin']['DETAILSTAB'] has to
      44  * in the language file cms/lang/en_US.php, as the referenced file (AssetAdmin.php) is stored
      45  * in the "cms" module.
      46  *
      47  * <b>Locales</b>
      48  *
      49  * For the i18n class, a "locale" consists of a language code plus a region code separated by an underscore,
      50  * for example "de_AT" for German language ("de") in the region Austria ("AT").
      51  * See http://www.w3.org/International/articles/language-tags/ for a detailed description.
      52  *
      53  * @see http://doc.silverstripe.org/i18n
      54  * @see http://www.w3.org/TR/i18n-html-tech-lang
      55  *
      56  * @author Bernat Foj Capell <bernat@silverstripe.com>
      57  * @package sapphire
      58  * @subpackage misc
      59  */
      60 class i18n extends Object {
      61
      62     /**
      63      * This static variable is used to store the current defined locale.
      64      */
      65     protected static $current_locale = '';
      66
      67     /**
      68      * @var string
      69      */
      70     protected static $default_locale = 'en_US';
      71
      72     /**
      73      * @var boolean
      74      */
      75     protected static $js_i18n = true;
      76
      77     /**
      78      * @var string
      79      */
      80     protected static $date_format;
      81
      82     /**
      83      * @var string
      84      */
      85     protected static $time_format;
      86
      87     /**
      88      * Use javascript i18n through the ss.i18n class (enabled by default).
      89      * If set to TRUE, includes javascript requirements for the base library
      90      * (sapphire/javascript/i18n.js) and all necessary lang files (e.g. sapphire/lang/de_DE.js)
      91      * plus fallbacks to the default locale (e.g. sapphire/lang/en_US.js).
      92      * If set to FALSE, only includes a stub implementation
      93      * which is necessary. Mainly disabled to save bandwidth
      94      * in a frontend context when website is in single language.
      95      *
      96      * Caution: This flag gets overwritten in {@link LeftAndMain::init()} to enforce javascript
      97      * i18n for the CMS interfaces.
      98      *
      99      * @see Requirements::process_i18n_javascript()
     100      *
     101      * @param bool $bool
     102      */
     103     public static function set_js_i18n($bool) {
     104         self::$js_i18n = $bool;
     105     }
     106
     107     /**
     108      * @return bool
     109      */
     110     public static function get_js_i18n() {
     111         return self::$js_i18n;
     112     }
     113
     114     /**
     115      * @param string ISO date format
     116      */
     117     public static function set_date_format($format) {
     118         self::$date_format = $format;
     119     }
     120
     121     /**
     122      * @return string ISO date format
     123      */
     124     public static function get_date_format() {
     125         require_once 'Zend/Date.php';
     126         return (self::$date_format) ? self::$date_format : Zend_Locale_Format::getDateFormat(self::get_locale());
     127     }
     128
     129
     130     /**
     131      * @return locale specific formated date
     132      * Special for russian months names
     133      * TODO move months names to language file
     134      */
     135     public static function strftime($format = '%e %B %Y', $time = null) {
     136         if (null === $time) $time = time();
     137         $monthsAlone = array('', 'январь', 'февраль', 'март', 'апрель', 'май', 'июнь', 'июль', 'август', 'сентябрь', 'октябрь', 'ноябрь', 'декабрь');
     138         $monthsFull = array('', 'января', 'февраля', 'марта', 'апреля', 'мая', 'июня', 'июля', 'августа', 'сентября', 'октября', 'ноября', 'декабря');
     139         $monthsShort = array('', 'янв', 'фев', 'мар', 'апр', 'май', 'июн', 'июл', 'авг', 'сен', 'окт', 'ноя', 'дек');
     140
     141         if (preg_match('/%(OB|B|b)/', $format)) {
     142             if (i18n::get_locale() == 'ru_RU') {
     143                 $month = date('n', $time);
     144                 $format = preg_replace('/%b/s', $monthsShort[$month], $format);
     145 //              $format = preg_replace('/(%[ed][^%]*)%B/s', "$1$monthsFull[$month]", $format);
     146                 $format = preg_replace('/%B/s', $monthsAlone[$month], $format);
     147                 $format = preg_replace('/%OB/s', $monthsFull[$month], $format);
     148             }
     149             else {
     150                 $format = preg_replace('/%OB/s', '%B', $format);
     151             }
     152         }
     153         return strftime($format, $time);
     154     }
     155
     156
     157     /**
     158      * @param string ISO time format
     159      */
     160     public static function set_time_format($format) {
     161         self::$time_format = $format;
     162     }
     163
     164     /**
     165      * @return string ISO time format
     166      */
     167     public static function get_time_format() {
     168         require_once 'Zend/Date.php';
     169         return (self::$time_format) ? self::$time_format : Zend_Locale_Format::getTimeFormat(self::get_locale());
     170     }
     171
     172     /**
     173      * An exhaustive list of possible locales (code => language and country)
     174      *
     175      * @var array
     176      */
     177     public static $all_locales = array (
     178             'aa_DJ' => 'Afar (Djibouti)',
     179             'ab_GE' => 'Abkhazian (Georgia)',
     180             'abr_GH' => 'Abron (Ghana)',
     181             'ace_ID' => 'Achinese (Indonesia)',
     182             'ady_RU' => 'Adyghe (Russia)',
     183             'af_ZA' => 'Afrikaans (South Africa)',
     184             'ak_GH' => 'Akan (Ghana)',
     185             'am_ET' => 'Amharic (Ethiopia)',
     186             'ar_AE' => 'Arabic (United Arab Emirates)',
     187             'ar_BH' => 'Arabic (Bahrain)',
     188             'ar_DZ' => 'Arabic (Algeria)',
     189             'ar_EG' => 'Arabic (Egypt)',
     190             'ar_EH' => 'Arabic (Western Sahara)',
     191             'ar_IQ' => 'Arabic (Iraq)',
     192             'ar_JO' => 'Arabic (Jordan)',
     193             'ar_KW' => 'Arabic (Kuwait)',
     194             'ar_LB' => 'Arabic (Lebanon)',
     195             'ar_LY' => 'Arabic (Libya)',
     196             'ar_MA' => 'Arabic (Morocco)',
     197             'ar_MR' => 'Arabic (Mauritania)',
     198             'ar_OM' => 'Arabic (Oman)',
     199             'ar_PS' => 'Arabic (Palestinian Territory)',
     200             'ar_QA' => 'Arabic (Qatar)',
     201             'ar_SA' => 'Arabic (Saudi Arabia)',
     202             'ar_SD' => 'Arabic (Sudan)',
     203             'ar_SY' => 'Arabic (Syria)',
     204             'ar_TD' => 'Arabic (Chad)',
     205             'ar_TN' => 'Arabic (Tunisia)',
     206             'ar_YE' => 'Arabic (Yemen)',
     207             'as_IN' => 'Assamese (India)',
     208             'auv_FR' => 'Auvergnat (France)',
     209             'av_RU' => 'Avaric (Russia)',
     210             'awa_IN' => 'Awadhi (India)',
     211             'ay_BO' => 'Aymara (Bolivia)',
     212             'ay_PE' => 'Aymara (Peru)',
     213             'az_AZ' => 'Azerbaijani (Azerbaijan)',
     214             'az_IR' => 'Azerbaijani (Iran)',
     215             'ba_RU' => 'Bashkir (Russia)',
     216             'ban_ID' => 'Balinese (Indonesia)',
     217             'bcc_PK' => 'Balochi, Southern (Pakistan)',
     218             'bcl_PH' => 'Bicolano, Central (Philippines)',
     219             'be_BY' => 'Belarusian (Belarus)',
     220             'bew_ID' => 'Betawi (Indonesia)',
     221             'bg_BG' => 'Bulgarian (Bulgaria)',
     222             'bgc_IN' => 'Haryanvi (India)',
     223             'bgn_PK' => 'Balochi, Western (Pakistan)',
     224             'bgp_PK' => 'Balochi, Easter (Pakistan)',
     225             'bhb_IN' => 'Bhili (India)',
     226             'bhi_IN' => 'Bhilali (India)',
     227             'bhk_PH' => 'Bicolano, Albay (Philippines)',
     228             'bho_IN' => 'Bhojpuri (India)',
     229             'bho_MU' => 'Bhojpuri (Mauritius)',
     230             'bho_NP' => 'Bhojpuri (Nepal)',
     231             'bi_VU' => 'Bislama (Vanuatu)',
     232             'bjj_IN' => 'Kanauji (India)',
     233             'bjn_ID' => 'Banjar (Indonesia)',
     234             'bm_ML' => 'Bambara (Mali)',
     235             'bn_BD' => 'Bengali (Bangladesh)',
     236             'bn_IN' => 'Bengali (India)',
     237             'bo_CN' => 'Tibetan (China)',
     238             'bqi_IR' => 'Bakhtiari (Iran)',
     239             'brh_PK' => 'Brahui (Pakistan)',
     240             'bs_BA' => 'Bosnian (Bosnia and Herzegovina)',
     241             'btk_ID' => 'Batak (Indonesia)',
     242             'buc_YT' => 'Bushi (Mayotte)',
     243             'bug_ID' => 'Buginese (Indonesia)',
     244             'ca_AD' => 'Catalan (Andorra)',
     245             'ca_ES' => 'Catalan (Spain)',
     246             'ce_RU' => 'Chechen (Russia)',
     247             'ceb_PH' => 'Cebuano (Philippines)',
     248             'cgg_UG' => 'Chiga (Uganda)',
     249             'ch_GU' => 'Chamorro (Guam)',
     250             'chk_FM' => 'Chuukese (Micronesia)',
     251             'crk_CA' => 'Cree, Plains (Canada)',
     252             'cs_CZ' => 'Czech (Czech Republic)',
     253             'cwd_CA' => 'Cree, Woods (Canada)',
     254             'cy_GB' => 'Welsh (United Kingdom)',
     255             'da_DK' => 'Danish (Denmark)',
     256             'da_GL' => 'Danish (Greenland)',
     257             'dcc_IN' => 'Deccan (India)',
     258             'de_AT' => 'German (Austria)',
     259             'de_BE' => 'German (Belgium)',
     260             'de_CH' => 'German (Switzerland)',
     261             'de_DE' => 'German (Germany)',
     262             'de_LI' => 'German (Liechtenstein)',
     263             'de_LU' => 'German (Luxembourg)',
     264             'dgo_IN' => 'Dogri (India)',
     265             'dhd_IN' => 'Dhundari (India)',
     266             'diq_TR' => 'Dimli (Turkey)',
     267             'dje_NE' => 'Zarma (Niger)',
     268             'dv_MV' => 'Divehi (Maldives)',
     269             'dz_BT' => 'Dzongkha (Bhutan)',
     270             'ee_GH' => 'Ewe (Ghana)',
     271             'el_CY' => 'Greek (Cyprus)',
     272             'el_GR' => 'Greek (Greece)',
     273             'en_AS' => 'English (American Samoa)',
     274             'en_AU' => 'English (Australia)',
     275             'en_BM' => 'English (Bermuda)',
     276             'en_BS' => 'English (Bahamas)',
     277             'en_CA' => 'English (Canada)',
     278             'en_DE' => 'English (Germany)',
     279             'en_ES' => 'English (Spain)',
     280             'en_FR' => 'English (France)',
     281             'en_GB' => 'English (United Kingdom)',
     282             'en_HK' => 'English (Hong Kong SAR China)',
     283             'en_IE' => 'English (Ireland)',
     284             'en_IN' => 'English (India)',
     285             'en_IT' => 'English (Italy)',
     286             'en_JM' => 'English (Jamaica)',
     287             'en_KE' => 'English (Kenya)',
     288             'en_LR' => 'English (Liberia)',
     289             'en_MM' => 'English (Myanmar)',
     290             'en_MW' => 'English (Malawi)',
     291             'en_MY' => 'English (Malaysia)',
     292             'en_NL' => 'English (Netherlands)',
     293             'en_NZ' => 'English (New Zealand)',
     294             'en_PH' => 'English (Philippines)',
     295             'en_SG' => 'English (Singapore)',
     296             'en_TT' => 'English (Trinidad and Tobago)',
     297             'en_US' => 'English (United States)',
     298             'en_ZA' => 'English (South Africa)',
     299             'eo_XX' => 'Esperanto',
     300             'es_419' => 'Spanish (Latin America)',
     301             'es_AR' => 'Spanish (Argentina)',
     302             'es_BO' => 'Spanish (Bolivia)',
     303             'es_CL' => 'Spanish (Chile)',
     304             'es_CO' => 'Spanish (Colombia)',
     305             'es_CR' => 'Spanish (Costa Rica)',
     306             'es_CU' => 'Spanish (Cuba)',
     307             'es_DO' => 'Spanish (Dominican Republic)',
     308             'es_EC' => 'Spanish (Ecuador)',
     309             'es_ES' => 'Spanish (Spain)',
     310             'es_GQ' => 'Spanish (Equatorial Guinea)',
     311             'es_GT' => 'Spanish (Guatemala)',
     312             'es_HN' => 'Spanish (Honduras)',
     313             'es_MX' => 'Spanish (Mexico)',
     314             'es_NI' => 'Spanish (Nicaragua)',
     315             'es_PA' => 'Spanish (Panama)',
     316             'es_PE' => 'Spanish (Peru)',
     317             'es_PH' => 'Spanish (Philippines)',
     318             'es_PR' => 'Spanish (Puerto Rico)',
     319             'es_PY' => 'Spanish (Paraguay)',
     320             'es_SV' => 'Spanish (El Salvador)',
     321             'es_US' => 'Spanish (United States)',
     322             'es_UY' => 'Spanish (Uruguay)',
     323             'es_VE' => 'Spanish (Venezuela)',
     324             'et_EE' => 'Estonian (Estonia)',
     325             'eu_ES' => 'Basque (Spain)',
     326             'fa_AF' => 'Persian (Afghanistan)',
     327             'fa_IR' => 'Persian (Iran)',
     328             'fa_PK' => 'Persian (Pakistan)',
     329             'fan_GQ' => 'Fang (Equatorial Guinea)',
     330             'fi_FI' => 'Finnish (Finland)',
     331             'fi_SE' => 'Finnish (Sweden)',
     332             'fil_PH' => 'Filipino (Philippines)',
     333             'fj_FJ' => 'Fijian (Fiji)',
     334             'fo_FO' => 'Faroese (Faroe Islands)',
     335             'fon_BJ' => 'Fon (Benin)',
     336             'fr_002' => 'French (Africa)',
     337             'fr_BE' => 'French (Belgium)',
     338             'fr_CA' => 'French (Canada)',
     339             'fr_CH' => 'French (Switzerland)',
     340             'fr_DZ' => 'French (Algeria)',
     341             'fr_FR' => 'French (France)',
     342             'fr_GF' => 'French (French Guiana)',
     343             'fr_GP' => 'French (Guadeloupe)',
     344             'fr_HT' => 'French (Haiti)',
     345             'fr_KM' => 'French (Comoros)',
     346             'fr_MA' => 'French (Morocco)',
     347             'fr_MQ' => 'French (Martinique)',
     348             'fr_MU' => 'French (Mauritius)',
     349             'fr_NC' => 'French (New Caledonia)',
     350             'fr_PF' => 'French (French Polynesia)',
     351             'fr_PM' => 'French (Saint Pierre and Miquelon)',
     352             'fr_RE' => 'French (Reunion)',
     353             'fr_SC' => 'French (Seychelles)',
     354             'fr_SN' => 'French (Senegal)',
     355             'fr_US' => 'French (United States)',
     356             'fuv_NG' => 'Fulfulde (Nigeria)',
     357             'ga_GB' => 'Irish (United Kingdom)',
     358             'ga_IE' => 'Irish (Ireland)',
     359             'gaa_GH' => 'Ga (Ghana)',
     360             'gbm_IN' => 'Garhwali (India)',
     361             'gcr_GF' => 'Guianese Creole French (French Guiana)',
     362             'gd_GB' => 'Scottish Gaelic (United Kingdom)',
     363             'gil_KI' => 'Gilbertese (Kiribati)',
     364             'gl_ES' => 'Galician (Spain)',
     365             'glk_IR' => 'Gilaki (Iran)',
     366             'gn_PY' => 'Guarani (Paraguay)',
     367             'gno_IN' => 'Gondi, Northern (India)',
     368             'gsw_CH' => 'Swiss German (Switzerland)',
     369             'gsw_LI' => 'Swiss German (Liechtenstein)',
     370             'gu_IN' => 'Gujarati (India)',
     371             'guz_KE' => 'Gusii (Kenya)',
     372             'ha_NE' => 'Hausa (Niger)',
     373             'ha_NG' => 'Hausa (Nigeria)',
     374             'haw_US' => 'Hawaiian (United States)',
     375             'haz_AF' => 'Hazaragi (Afghanistan)',
     376             'he_IL' => 'Hebrew (Israel)',
     377             'hi_IN' => 'Hindi (India)',
     378             'hil_PH' => 'Hiligaynon (Philippines)',
     379             'hne_IN' => 'Chhattisgarhi (India)',
     380             'hno_PK' => 'Hindko, Northern (Pakistan)',
     381             'hoc_IN' => 'Ho (India)',
     382             'hr_AT' => 'Croatian (Austria)',
     383             'hr_BA' => 'Croatian (Bosnia and Herzegovina)',
     384             'hr_HR' => 'Croatian (Croatia)',
     385             'ht_HT' => 'Haitian (Haiti)',
     386             'hu_AT' => 'Hungarian (Austria)',
     387             'hu_HU' => 'Hungarian (Hungary)',
     388             'hu_RO' => 'Hungarian (Romania)',
     389             'hu_RS' => 'Hungarian (Serbia)',
     390             'hy_AM' => 'Armenian (Armenia)',
     391             'id_ID' => 'Indonesian (Indonesia)',
     392             'ig_NG' => 'Igbo (Nigeria)',
     393             'ilo_PH' => 'Iloko (Philippines)',
     394             'inh_RU' => 'Ingush (Russia)',
     395             'is_IS' => 'Icelandic (Iceland)',
     396             'it_CH' => 'Italian (Switzerland)',
     397             'it_FR' => 'Italian (France)',
     398             'it_HR' => 'Italian (Croatia)',
     399             'it_IT' => 'Italian (Italy)',
     400             'it_SM' => 'Italian (San Marino)',
     401             'it_US' => 'Italian (United States)',
     402             'iu_CA' => 'Inuktitut (Canada)',
     403             'ja_JP' => 'Japanese (Japan)',
     404             'jv_ID' => 'Javanese (Indonesia)',
     405             'ka_GE' => 'Georgian (Georgia)',
     406             'kam_KE' => 'Kamba (Kenya)',
     407             'kbd_RU' => 'Kabardian (Russia)',
     408             'kfy_IN' => 'Kumauni (India)',
     409             'kha_IN' => 'Khasi (India)',
     410             'khn_IN' => 'Khandesi (India)',
     411             'ki_KE' => 'Kikuyu (Kenya)',
     412             'kj_NA' => 'Kuanyama (Namibia)',
     413             'kk_CN' => 'Kazakh (China)',
     414             'kk_KZ' => 'Kazakh (Kazakhstan)',
     415             'kl_DK' => 'Kalaallisut (Denmark)',
     416             'kl_GL' => 'Kalaallisut (Greenland)',
     417             'kln_KE' => 'Kalenjin (Kenya)',
     418             'km_KH' => 'Khmer (Cambodia)',
     419             'kn_IN' => 'Kannada (India)',
     420             'ko_KR' => 'Korean (Korea)',
     421             'koi_RU' => 'Komi-Permyak (Russia)',
     422             'kok_IN' => 'Konkani (India)',
     423             'kos_FM' => 'Kosraean (Micronesia)',
     424             'kpv_RU' => 'Komi-Zyrian (Russia)',
     425             'krc_RU' => 'Karachay-Balkar (Russia)',
     426             'kru_IN' => 'Kurukh (India)',
     427             'ks_IN' => 'Kashmiri (India)',
     428             'ku_IQ' => 'Kurdish (Iraq)',
     429             'ku_IR' => 'Kurdish (Iran)',
     430             'ku_SY' => 'Kurdish (Syria)',
     431             'ku_TR' => 'Kurdish (Turkey)',
     432             'kum_RU' => 'Kumyk (Russia)',
     433             'kxm_TH' => 'Khmer, Northern (Thailand)',
     434             'ky_KG' => 'Kirghiz (Kyrgyzstan)',
     435             'la_VA' => 'Latin (Vatican)',
     436             'lah_PK' => 'Lahnda (Pakistan)',
     437             'lb_LU' => 'Luxembourgish (Luxembourg)',
     438             'lbe_RU' => 'Lak (Russia)',
     439             'lc_XX' => 'LOLCAT',
     440             'lez_RU' => 'Lezghian (Russia)',
     441             'lg_UG' => 'Ganda (Uganda)',
     442             'lij_IT' => 'Ligurian (Italy)',
     443             'lij_MC' => 'Ligurian (Monaco)',
     444             'ljp_ID' => 'Lampung (Indonesia)',
     445             'lmn_IN' => 'Lambadi (India)',
     446             'ln_CD' => 'Lingala (Congo - Kinshasa)',
     447             'ln_CG' => 'Lingala (Congo - Brazzaville)',
     448             'lo_LA' => 'Lao (Laos)',
     449             'lrc_IR' => 'Luri, Northern (Iran)',
     450             'lt_LT' => 'Lithuanian (Lithuania)',
     451             'luo_KE' => 'Luo (Kenya)',
     452             'luy_KE' => 'Luyia (Kenya)',
     453             'lv_LV' => 'Latvian (Latvia)',
     454             'mad_ID' => 'Madurese (Indonesia)',
     455             'mai_IN' => 'Maithili (India)',
     456             'mai_NP' => 'Maithili (Nepal)',
     457             'mak_ID' => 'Makasar (Indonesia)',
     458             'mdf_RU' => 'Moksha (Russia)',
     459             'mdh_PH' => 'Maguindanao (Philippines)',
     460             'mer_KE' => 'Meru (Kenya)',
     461             'mfa_TH' => 'Malay, Pattani (Thailand)',
     462             'mfe_MU' => 'Morisyen (Mauritius)',
     463             'mg_MG' => 'Malagasy (Madagascar)',
     464             'mh_MH' => 'Marshallese (Marshall Islands)',
     465             'mi_NZ' => 'Maori (New Zealand)',
     466             'min_ID' => 'Minangkabau (Indonesia)',
     467             'mk_MK' => 'Macedonian (Macedonia)',
     468             'ml_IN' => 'Malayalam (India)',
     469             'mn_CN' => 'Mongolian (China)',
     470             'mn_MN' => 'Mongolian (Mongolia)',
     471             'mni_IN' => 'Manipuri (India)',
     472             'mr_IN' => 'Marathi (India)',
     473             'ms_BN' => 'Malay (Brunei)',
     474             'ms_CC' => 'Malay (Cocos Islands)',
     475             'ms_ID' => 'Malay (Indonesia)',
     476             'ms_MY' => 'Malay (Malaysia)',
     477             'ms_SG' => 'Malay (Singapore)',
     478             'mt_MT' => 'Maltese (Malta)',
     479             'mtr_IN' => 'Mewari (India)',
     480             'mup_IN' => 'Malvi (India)',
     481             'muw_IN' => 'Mundari (India)',
     482             'my_MM' => 'Burmese (Myanmar)',
     483             'myv_RU' => 'Erzya (Russia)',
     484             'na_NR' => 'Nauru (Nauru)',
     485             'nb_NO' => 'Norwegian Bokmal (Norway)',
     486             'nb_SJ' => 'Norwegian Bokmal (Svalbard and Jan Mayen)',
     487             'nd_ZW' => 'North Ndebele (Zimbabwe)',
     488             'ndc_MZ' => 'Ndau (Mozambique)',
     489             'ne_IN' => 'Nepali (India)',
     490             'ne_NP' => 'Nepali (Nepal)',
     491             'ng_NA' => 'Ndonga (Namibia)',
     492             'ngl_MZ' => 'Lomwe (Mozambique)',
     493             'niu_NU' => 'Niuean (Niue)',
     494             'nl_AN' => 'Dutch (Netherlands Antilles)',
     495             'nl_AW' => 'Dutch (Aruba)',
     496             'nl_BE' => 'Dutch (Belgium)',
     497             'nl_NL' => 'Dutch (Netherlands)',
     498             'nl_SR' => 'Dutch (Suriname)',
     499             'nn_NO' => 'Norwegian Nynorsk (Norway)',
     500             'nb_NO' => 'Norwegian',
     501             'nod_TH' => 'Thai, Northern (Thailand)',
     502             'noe_IN' => 'Nimadi (India)',
     503             'nso_ZA' => 'Northern Sotho (South Africa)',
     504             'ny_MW' => 'Nyanja (Malawi)',
     505             'ny_ZM' => 'Nyanja (Zambia)',
     506             'nyn_UG' => 'Nyankole (Uganda)',
     507             'om_ET' => 'Oromo (Ethiopia)',
     508             'or_IN' => 'Oriya (India)',
     509             'pa_IN' => 'Punjabi (India)',
     510             'pag_PH' => 'Pangasinan (Philippines)',
     511             'pap_AN' => 'Papiamento (Netherlands Antilles)',
     512             'pap_AW' => 'Papiamento (Aruba)',
     513             'pau_PW' => 'Palauan (Palau)',
     514             'pl_PL' => 'Polish (Poland)',
     515             'pl_UA' => 'Polish (Ukraine)',
     516             'pon_FM' => 'Pohnpeian (Micronesia)',
     517             'ps_AF' => 'Pashto (Afghanistan)',
     518             'ps_PK' => 'Pashto (Pakistan)',
     519             'pt_AO' => 'Portuguese (Angola)',
     520             'pt_BR' => 'Portuguese (Brazil)',
     521             'pt_CV' => 'Portuguese (Cape Verde)',
     522             'pt_GW' => 'Portuguese (Guinea-Bissau)',
     523             'pt_MZ' => 'Portuguese (Mozambique)',
     524             'pt_PT' => 'Portuguese (Portugal)',
     525             'pt_ST' => 'Portuguese (Sao Tome and Principe)',
     526             'pt_TL' => 'Portuguese (East Timor)',
     527             'qu_BO' => 'Quechua (Bolivia)',
     528             'qu_PE' => 'Quechua (Peru)',
     529             'rcf_RE' => 'R�union Creole French (Reunion)',
     530             'rej_ID' => 'Rejang (Indonesia)',
     531             'rif_MA' => 'Tarifit (Morocco)',
     532             'rjb_IN' => 'Rajbanshi (India)',
     533             'rm_CH' => 'Rhaeto-Romance (Switzerland)',
     534             'rmt_IR' => 'Domari (Iran)',
     535             'rn_BI' => 'Rundi (Burundi)',
     536             'ro_MD' => 'Romanian (Moldova)',
     537             'ro_RO' => 'Romanian (Romania)',
     538             'ro_RS' => 'Romanian (Serbia)',
     539             'ru_BY' => 'Russian (Belarus)',
     540             'ru_KG' => 'Russian (Kyrgyzstan)',
     541             'ru_KZ' => 'Russian (Kazakhstan)',
     542             'ru_RU' => 'Russian (Russia)',
     543             'ru_SJ' => 'Russian (Svalbard and Jan Mayen)',
     544             'ru_UA' => 'Russian (Ukraine)',
     545             'rw_RW' => 'Kinyarwanda (Rwanda)',
     546             'sa_IN' => 'Sanskrit (India)',
     547             'sah_RU' => 'Yakut (Russia)',
     548             'sas_ID' => 'Sasak (Indonesia)',
     549             'sat_IN' => 'Santali (India)',
     550             'sck_IN' => 'Sadri (India)',
     551             'sco_GB' => 'Scots (United Kingdom)',
     552             'sco_SCO' => 'Scots',
     553             'sd_IN' => 'Sindhi (India)',
     554             'sd_PK' => 'Sindhi (Pakistan)',
     555             'se_NO' => 'Northern Sami (Norway)',
     556             'sg_CF' => 'Sango (Central African Republic)',
     557             'si_LK' => 'Sinhalese (Sri Lanka)',
     558             'sid_ET' => 'Sidamo (Ethiopia)',
     559             'sk_RS' => 'Slovak (Serbia)',
     560             'sk_SK' => 'Slovak (Slovakia)',
     561             'sl_AT' => 'Slovenian (Austria)',
     562             'sl_SI' => 'Slovenian (Slovenia)',
     563             'sm_AS' => 'Samoan (American Samoa)',
     564             'sm_WS' => 'Samoan (Samoa)',
     565             'sn_ZW' => 'Shona (Zimbabwe)',
     566             'so_DJ' => 'Somali (Djibouti)',
     567             'so_ET' => 'Somali (Ethiopia)',
     568             'so_SO' => 'Somali (Somalia)',
     569             'sou_TH' => 'Thai, Southern (Thailand)',
     570             'sq_AL' => 'Albanian (Albania)',
     571             'sr_BA' => 'Serbian (Bosnia and Herzegovina)',
     572             'sr_ME' => 'Serbian (Montenegro)',
     573             'sr_RS' => 'Serbian (Serbia)',
     574             'ss_SZ' => 'Swati (Swaziland)',
     575             'ss_ZA' => 'Swati (South Africa)',
     576             'st_LS' => 'Southern Sotho (Lesotho)',
     577             'st_ZA' => 'Southern Sotho (South Africa)',
     578             'su_ID' => 'Sundanese (Indonesia)',
     579             'sv_AX' => 'Swedish (Aland Islands)',
     580             'sv_FI' => 'Swedish (Finland)',
     581             'sv_SE' => 'Swedish (Sweden)',
     582             'sw_KE' => 'Swahili (Kenya)',
     583             'sw_SO' => 'Swahili (Somalia)',
     584             'sw_TZ' => 'Swahili (Tanzania)',
     585             'sw_UG' => 'Swahili (Uganda)',
     586             'swb_KM' => 'Comorian (Comoros)',
     587             'swb_YT' => 'Comorian (Mayotte)',
     588             'swv_IN' => 'Shekhawati (India)',
     589             'ta_IN' => 'Tamil (India)',
     590             'ta_LK' => 'Tamil (Sri Lanka)',
     591             'ta_MY' => 'Tamil (Malaysia)',
     592             'ta_SG' => 'Tamil (Singapore)',
     593             'tcy_IN' => 'Tulu (India)',
     594             'te_IN' => 'Telugu (India)',
     595             'tet_TL' => 'Tetum (East Timor)',
     596             'tg_TJ' => 'Tajik (Tajikistan)',
     597             'th_TH' => 'Thai (Thailand)',
     598             'ti_ER' => 'Tigrinya (Eritrea)',
     599             'ti_ET' => 'Tigrinya (Ethiopia)',
     600             'tk_IR' => 'Turkmen (Iran)',
     601             'tk_TM' => 'Turkmen (Turkmenistan)',
     602             'tkl_TK' => 'Tokelau (Tokelau)',
     603             'tl_PH' => 'Tagalog (Philippines)',
     604             'tl_US' => 'Tagalog (United States)',
     605             'tn_BW' => 'Tswana (Botswana)',
     606             'tn_ZA' => 'Tswana (South Africa)',
     607             'to_TO' => 'Tonga (Tonga)',
     608             'tr_CY' => 'Turkish (Cyprus)',
     609             'tr_DE' => 'Turkish (Germany)',
     610             'tr_MK' => 'Turkish (Macedonia)',
     611             'tr_TR' => 'Turkish (Turkey)',
     612             'ts_MZ' => 'Tsonga (Mozambique)',
     613             'ts_ZA' => 'Tsonga (South Africa)',
     614             'tsg_PH' => 'Tausug (Philippines)',
     615             'tt_RU' => 'Tatar (Russia)',
     616             'tts_TH' => 'Thai, Northeastern (Thailand)',
     617             'tvl_TV' => 'Tuvalu (Tuvalu)',
     618             'tw_GH' => 'Twi (Ghana)',
     619             'ty_PF' => 'Tahitian (French Polynesia)',
     620             'tyv_RU' => 'Tuvinian (Russia)',
     621             'tzm_MA' => 'Tamazight, Central Atlas (Morocco)',
     622             'udm_RU' => 'Udmurt (Russia)',
     623             'ug_CN' => 'Uighur (China)',
     624             'uk_UA' => 'Ukrainian (Ukraine)',
     625             'uli_FM' => 'Ulithian (Micronesia)',
     626             'ur_IN' => 'Urdu (India)',
     627             'ur_PK' => 'Urdu (Pakistan)',
     628             'uz_AF' => 'Uzbek (Afghanistan)',
     629             'uz_UZ' => 'Uzbek (Uzbekistan)',
     630             've_ZA' => 'Venda (South Africa)',
     631             'vi_US' => 'Vietnamese (United States)',
     632             'vi_VN' => 'Vietnamese (Vietnam)',
     633             'vmw_MZ' => 'Waddar (Mozambique)',
     634             'wal_ET' => 'Walamo (Ethiopia)',
     635             'war_PH' => 'Waray (Philippines)',
     636             'wbq_IN' => 'Waddar (India)',
     637             'wbr_IN' => 'Wagdi (India)',
     638             'wo_MR' => 'Wolof (Mauritania)',
     639             'wo_SN' => 'Wolof (Senegal)',
     640             'wtm_IN' => 'Mewati (India)',
     641             'xh_ZA' => 'Xhosa (South Africa)',
     642             'xnr_IN' => 'Kangri (India)',
     643             'xog_UG' => 'Soga (Uganda)',
     644             'yap_FM' => 'Yapese (Micronesia)',
     645             'yo_NG' => 'Yoruba (Nigeria)',
     646             'za_CN' => 'Zhuang (China)',
     647             'zh_CN' => 'Chinese (China)',
     648             'zh_HK' => 'Chinese (Hong Kong SAR China)',
     649             'zh_MO' => 'Chinese (Macao SAR China)',
     650             'zh_SG' => 'Chinese (Singapore)',
     651             'zh_TW' => 'Chinese (Taiwan)',
     652             'zh_US' => 'Chinese (United States)',
     653             'zh_cmn' => 'Chinese (Mandarin)',
     654             'zh_yue' => 'Chinese (Cantonese)',
     655             'zu_ZA' => 'Zulu (South Africa)'
     656     );
     657
     658     /**
     659      * @var array $common_languages A list of commonly used languages, in the form
     660      * langcode => array( EnglishName, NativeName)
     661      */
     662     public static $common_languages = array(
     663         'af' => array('Afrikaans', 'Afrikaans'),
     664         'sq' => array('Albanian', 'shqip'),
     665         'ar' => array('Arabic', '&#1575;&#1604;&#1593;&#1585;&#1576;&#1610;&#1577;'),
     666         'eu' => array('Basque', 'euskera'),
     667         'be' => array('Belarusian', '&#1041;&#1077;&#1083;&#1072;&#1088;&#1091;&#1089;&#1082;&#1072;&#1103; &#1084;&#1086;&#1074;&#1072;'),
     668         'bn' => array('Bengali', '&#2476;&#2494;&#2434;&#2482;&#2494;'),
     669         'bg' => array('Bulgarian', '&#1073;&#1098;&#1083;&#1075;&#1072;&#1088;&#1089;&#1082;&#1080;'),
     670         'ca' => array('Catalan', 'catal&agrave;'),
     671         'zh_yue' => array('Chinese (Cantonese)', '&#24291;&#26481;&#35441; [&#24191;&#19996;&#35805;]'),
     672         'zh_cmn' => array('Chinese (Mandarin)', '&#26222;&#36890;&#35441; [&#26222;&#36890;&#35805;]'),
     673         'hr' => array('Croatian', 'Hrvatski'),
     674         'cs' => array('Czech', '&#x010D;e&#353;tina'),
     675         'cy' => array('Welsh', 'Welsh/Cymraeg'),
     676         'da' => array('Danish', 'dansk'),
     677         'nl' => array('Dutch',  'Nederlands'),
     678         'en' => array('English', 'English'),
     679         'eo' => array('Esperanto', 'Esperanto'),
     680         'et' => array('Estonian', 'eesti keel'),
     681         'fo' => array('Faroese', 'F&oslash;royska'),
     682         'fi' => array('Finnish', 'suomi'),
     683         'fr' => array('French', 'fran&ccedil;ais'),
     684         'gd' => array('Gaelic', 'Gaeilge'),
     685         'gl' => array('Galician', 'Galego'),
     686         'de' => array('German', 'Deutsch'),
     687         'el' => array('Greek', '&#949;&#955;&#955;&#951;&#957;&#953;&#954;&#940;'),
     688         'gu' => array('Gujarati', '&#2711;&#2753;&#2716;&#2736;&#2750;&#2724;&#2752;'),
     689         'ha' => array('Hausa', '&#1581;&#1614;&#1608;&#1618;&#1587;&#1614;'),
     690         'he' => array('Hebrew', '&#1506;&#1489;&#1512;&#1497;&#1514;'),
     691         'hi' => array('Hindi', '&#2361;&#2367;&#2344;&#2381;&#2342;&#2368;'),
     692         'hu' => array('Hungarian', 'magyar'),
     693         'is' => array('Icelandic', '&Iacute;slenska'),
     694         'io' => array('Ido', 'Ido'),
     695         'id' => array('Indonesian', 'Bahasa Indonesia'),
     696         'ga' => array('Irish', 'Irish'),
     697         'it' => array('Italian', 'italiano'),
     698         'ja' => array('Japanese', '&#26085;&#26412;&#35486;'),
     699         'jv' => array('Javanese', 'basa Jawa'),
     700         'ko' => array('Korean', '&#54620;&#44397;&#50612; [&#38867;&#22283;&#35486;]'),
     701         'ku' => array('Kurdish', 'Kurd&iacute;'),
     702         'lv' => array('Latvian', 'latvie&#353;u'),
     703         'lt' => array('Lithuanian', 'lietuvi&#353;kai'),
     704         'lmo' => array('Lombard', 'Lombardo'),
     705         'mk' => array('Macedonian', '&#1084;&#1072;&#1082;&#1077;&#1076;&#1086;&#1085;&#1089;&#1082;&#1080;'),
     706         'mi' => array('Maori', 'Maori'),
     707         'ms' => array('Malay', 'Bahasa melayu'),
     708         'mt' => array('Maltese', 'Malti'),
     709         'mr' => array('Marathi', '&#2350;&#2352;&#2366;&#2336;&#2368;'),
     710         'ne' => array('Nepali', '&#2344;&#2375;&#2346;&#2366;&#2354;&#2368;'),
     711         'no' => array('Norwegian', 'Norsk'),
     712         'om' => array('Oromo', 'Afaan Oromo'),
     713         'fa' => array('Persian', '&#1601;&#1575;&#1585;&#1587;&#1609;'),
     714         'pl' => array('Polish', 'polski'),
     715         'pt-PT' => array('Portuguese (Portugal)', 'portugu&ecirc;s (Portugal)'),
     716         'pt-BR' => array('Portuguese (Brazil)', 'portugu&ecirc;s (Brazil)'),
     717         'pa' => array('Punjabi', '&#2602;&#2672;&#2588;&#2622;&#2604;&#2624;'),
     718         'qu' => array('Quechua', 'Quechua'),
     719         'rm' => array('Romansh', 'rumantsch'),
     720         'ro' => array('Romanian', 'rom&acirc;n'),
     721         'ru' => array('Russian', '&#1056;&#1091;&#1089;&#1089;&#1082;&#1080;&#1081;'),
     722         'sco' => array('Scots', 'Scoats leid, Lallans'),
     723         'sr' => array('Serbian', '&#1089;&#1088;&#1087;&#1089;&#1082;&#1080;'),
     724         'sk' => array('Slovak', 'sloven&#269;ina'),
     725         'sl' => array('Slovenian', 'sloven&#353;&#269;ina'),
     726         'es' => array('Spanish', 'espa&ntilde;ol'),
     727         'sv' => array('Swedish', 'Svenska'),
     728         'tl' => array('Tagalog', 'Tagalog'),
     729         'ta' => array('Tamil', '&#2980;&#2990;&#3007;&#2996;&#3021;'),
     730         'te' => array('Telugu', '&#3108;&#3142;&#3122;&#3137;&#3095;&#3137;'),
     731         'to' => array('Tonga', 'chiTonga'),
     732         'ts' => array('Tsonga', 'xiTshonga'),
     733         'tn' => array('Tswana', 'seTswana'),
     734         'tr' => array('Turkish', 'T&uuml;rk&ccedil;e'),
     735         'tk' => array('Turkmen', '&#1090;&#1199;&#1088;&#1082;m&#1077;&#1085;&#1095;&#1077;'),
     736         'tw' => array('Twi', 'twi'),
     737         'uk' => array('Ukrainian', '&#1059;&#1082;&#1088;&#1072;&#1111;&#1085;&#1089;&#1100;&#1082;&#1072;'),
     738         'ur' => array('Urdu', '&#1575;&#1585;&#1583;&#1608;'),
     739         'uz' => array('Uzbek', '&#1118;&#1079;&#1073;&#1077;&#1082;'),
     740         've' => array('Venda', 'tshiVen&#x1E13;a'),
     741         'vi' => array('Vietnamese', 'ti&#7871;ng vi&#7879;t'),
     742         'wa' => array('Walloon', 'walon'),
     743         'wo' => array('Wolof', 'Wollof'),
     744         'xh' => array('Xhosa', 'isiXhosa'),
     745         'yi' => array('Yiddish', '&#1522;&#1460;&#1491;&#1497;&#1513;'),
     746         'zu' => array('Zulu', 'isiZulu')
     747     );
     748
     749     /**
     750      * @var array $common_locales
     751      * Sorted alphabtically by the common language name,
     752      * not the locale key.
     753      */
     754     public static $common_locales = array(
     755         'af_ZA' => array('Afrikaans', 'Afrikaans'),
     756         'sq_AL' => array('Albanian', 'shqip'),
     757         'ar_EG' => array('Arabic', '&#1575;&#1604;&#1593;&#1585;&#1576;&#1610;&#1577;'),
     758         'eu_ES' => array('Basque', 'euskera'),
     759         'be_BY' => array('Belarusian', '&#1041;&#1077;&#1083;&#1072;&#1088;&#1091;&#1089;&#1082;&#1072;&#1103; &#1084;&#1086;&#1074;&#1072;'),
     760         'bn_BD' => array('Bengali', '&#2476;&#2494;&#2434;&#2482;&#2494;'),
     761         'bg_BG' => array('Bulgarian', '&#1073;&#1098;&#1083;&#1075;&#1072;&#1088;&#1089;&#1082;&#1080;'),
     762         'ca_ES' => array('Catalan', 'catal&agrave;'),
     763         'zh_yue' => array('Chinese (Cantonese)', '&#24291;&#26481;&#35441; [&#24191;&#19996;&#35805;]'),
     764         'zh_cmn' => array('Chinese (Mandarin)', '&#26222;&#36890;&#35441; [&#26222;&#36890;&#35805;]'),
     765         'hr_HR' => array('Croatian', 'Hrvatski'),
     766         'cs_CZ' => array('Czech', '&#x010D;e&#353;tina'),
     767         'cy_GB' => array('Welsh', 'Welsh/Cymraeg'),
     768         'da_DK' => array('Danish', 'dansk'),
     769         'nl_NL' => array('Dutch', 'Nederlands'),
     770         'en_NZ' => array('English (NZ)', 'English (NZ)'),
     771         'en_US' => array('English (US)', 'English (US)'),
     772         'en_GB' => array('English (UK)', 'English (UK)'),
     773         'eo_XX' => array('Esperanto', 'Esperanto'),
     774         'et_EE' => array('Estonian', 'eesti keel'),
     775         'fo_FO' => array('Faroese', 'F&oslash;royska'),
     776         'fi_FI' => array('Finnish', 'suomi'),
     777         'fr_FR' => array('French', 'fran&ccedil;ais'),
     778         'gd_GB' => array('Gaelic', 'Gaeilge'),
     779         'gl_ES' => array('Galician', 'Galego'),
     780         'ka_GE' => array('Georgian (Georgia)','Georgian (Georgia)'),
     781         'de_DE' => array('German', 'Deutsch'),
     782         'el_GR' => array('Greek', '&#949;&#955;&#955;&#951;&#957;&#953;&#954;&#940;'),
     783         'gu_IN' => array('Gujarati', '&#2711;&#2753;&#2716;&#2736;&#2750;&#2724;&#2752;'),
     784         'ha_NG' => array('Hausa', '&#1581;&#1614;&#1608;&#1618;&#1587;&#1614;'),
     785         'he_IL' => array('Hebrew', '&#1506;&#1489;&#1512;&#1497;&#1514;'),
     786         'hi_IN' => array('Hindi', '&#2361;&#2367;&#2344;&#2381;&#2342;&#2368;'),
     787         'hu_HU' => array('Hungarian', 'magyar'),
     788         'is_IS' => array('Icelandic', '&Iacute;slenska'),
     789         'id_ID' => array('Indonesian', 'Bahasa Indonesia'),
     790         'ga_IE' => array('Irish', 'Irish'),
     791         'it_IT' => array('Italian', 'italiano'),
     792         'ja_JP' => array('Japanese', '&#26085;&#26412;&#35486;'),
     793         'jv_ID' => array('Javanese', 'basa Jawa'),
     794         'ko_KR' => array('Korean', '&#54620;&#44397;&#50612; [&#38867;&#22283;&#35486;]'),
     795         'ku_IQ' => array('Kurdish', 'Kurd&iacute;'),
     796         'lv_LV' => array('Latvian', 'latvie&#353;u'),
     797         'lt_LT' => array('Lithuanian', 'lietuvi&#353;kai'),
     798         'mk_MK' => array('Macedonian', '&#1084;&#1072;&#1082;&#1077;&#1076;&#1086;&#1085;&#1089;&#1082;&#1080;'),
     799         'mi_NZ' => array('Maori', 'Maori'),
     800         'ms_MY' => array('Malay', 'Bahasa melayu'),
     801         'mt_MT' => array('Maltese', 'Malti'),
     802         'mr_IN' => array('Marathi', '&#2350;&#2352;&#2366;&#2336;&#2368;'),
     803         'ne_NP' => array('Nepali', '&#2344;&#2375;&#2346;&#2366;&#2354;&#2368;'),
     804         'nb_NO' => array('Norwegian', 'Norsk'),
     805         'om_ET' => array('Oromo', 'Afaan Oromo'),
     806         'fa_IR' => array('Persian', '&#1601;&#1575;&#1585;&#1587;&#1609;'),
     807         'pl_PL' => array('Polish', 'polski'),
     808         'pt_PT' => array('Portuguese (Portugal)', 'portugu&ecirc;s (Portugal)'),
     809         'pt_BR' => array('Portuguese (Brazil)', 'portugu&ecirc;s (Brazil)'),
     810         'pa_IN' => array('Punjabi', '&#2602;&#2672;&#2588;&#2622;&#2604;&#2624;'),
     811         'qu_PE' => array('Quechua', 'Quechua'),
     812         'rm_CH' => array('Romansh', 'rumantsch'),
     813         'ro_RO' => array('Romanian', 'rom&acirc;n'),
     814         'ru_RU' => array('Russian', '&#1056;&#1091;&#1089;&#1089;&#1082;&#1080;&#1081;'),
     815         'sco_SCO' => array('Scots', 'Scoats leid, Lallans'),
     816         'sr_RS' => array('Serbian', '&#1089;&#1088;&#1087;&#1089;&#1082;&#1080;'),
     817         'sk_SK' => array('Slovak', 'sloven&#269;ina'),
     818         'sl_SI' => array('Slovenian', 'sloven&#353;&#269;ina'),
     819         'es_ES' => array('Spanish', 'espa&ntilde;ol'),
     820         'sv_SE' => array('Swedish', 'Svenska'),
     821         'tl_PH' => array('Tagalog', 'Tagalog'),
     822         'ta_IN' => array('Tamil', '&#2980;&#2990;&#3007;&#2996;&#3021;'),
     823         'te_IN' => array('Telugu', '&#3108;&#3142;&#3122;&#3137;&#3095;&#3137;'),
     824         'to_TO' => array('Tonga', 'chiTonga'),
     825         'ts_ZA' => array('Tsonga', 'xiTshonga'),
     826         'tn_ZA' => array('Tswana', 'seTswana'),
     827         'tr_TR' => array('Turkish', 'T&uuml;rk&ccedil;e'),
     828         'tk_TM' => array('Turkmen', '&#1090;&#1199;&#1088;&#1082;m&#1077;&#1085;&#1095;&#1077;'),
     829         'tw_GH' => array('Twi', 'twi'),
     830         'uk_UA' => array('Ukrainian', '&#1059;&#1082;&#1088;&#1072;&#1111;&#1085;&#1089;&#1100;&#1082;&#1072;'),
     831         'ur_PK' => array('Urdu', '&#1575;&#1585;&#1583;&#1608;'),
     832         'uz_UZ' => array('Uzbek', '&#1118;&#1079;&#1073;&#1077;&#1082;'),
     833         've_ZA' => array('Venda', 'tshiVen&#x1E13;a'),
     834         'vi_VN' => array('Vietnamese', 'ti&#7871;ng vi&#7879;t'),
     835         'wo_SN' => array('Wolof', 'Wollof'),
     836         'xh_ZA' => array('Xhosa', 'isiXhosa'),
     837         'zu_ZA' => array('Zulu', 'isiZulu'),
     838     );
     839
     840     static $tinymce_lang = array(
     841         'ca_AD' => 'ca',
     842         'ca_ES' => 'ca',
     843         'cs_CZ' => 'cs',
     844         'cy_GB' => 'cy',
     845         'da_DK' => 'da',
     846         'da_GL' => 'da',
     847         'de_AT' => 'de',
     848         'de_BE' => 'de',
     849         'de_CH' => 'de',
     850         'de_DE' => 'de',
     851         'de_LI' => 'de',
     852         'de_LU' => 'de',
     853         'de_BR' => 'de',
     854         'de_US' => 'de',
     855         'el_CY' => 'el',
     856         'el_GR' => 'el',
     857         'es_AR' => 'es',
     858         'es_BO' => 'es',
     859         'es_CL' => 'es',
     860         'es_CO' => 'es',
     861         'es_CR' => 'es',
     862         'es_CU' => 'es',
     863         'es_DO' => 'es',
     864         'es_EC' => 'es',
     865         'es_ES' => 'es',
     866         'es_GQ' => 'es',
     867         'es_GT' => 'es',
     868         'es_HN' => 'es',
     869         'es_MX' => 'es',
     870         'es_NI' => 'es',
     871         'es_PA' => 'es',
     872         'es_PE' => 'es',
     873         'es_PH' => 'es',
     874         'es_PR' => 'es',
     875         'es_PY' => 'es',
     876         'es_SV' => 'es',
     877         'es_UY' => 'es',
     878         'es_VE' => 'es',
     879         'es_AD' => 'es',
     880         'es_BZ' => 'es',
     881         'es_US' => 'es',
     882         'fa_AF' => 'fa',
     883         'fa_IR' => 'fa',
     884         'fa_PK' => 'fa',
     885         'fi_FI' => 'fi',
     886         'fi_SE' => 'fi',
     887         'fr_BE' => 'fr',
     888         'fr_BF' => 'fr',
     889         'fr_BI' => 'fr',
     890         'fr_BJ' => 'fr',
     891         'fr_CA' => 'fr_ca',
     892         'fr_CF' => 'fr',
     893         'fr_CG' => 'fr',
     894         'fr_CH' => 'fr',
     895         'fr_CI' => 'fr',
     896         'fr_CM' => 'fr',
     897         'fr_DJ' => 'fr',
     898         'fr_DZ' => 'fr',
     899         'fr_FR' => 'fr',
     900         'fr_GA' => 'fr',
     901         'fr_GF' => 'fr',
     902         'fr_GN' => 'fr',
     903         'fr_GP' => 'fr',
     904         'fr_HT' => 'fr',
     905         'fr_KM' => 'fr',
     906         'fr_LU' => 'fr',
     907         'fr_MA' => 'fr',
     908         'fr_MC' => 'fr',
     909         'fr_MG' => 'fr',
     910         'fr_ML' => 'fr',
     911         'fr_MQ' => 'fr',
     912         'fr_MU' => 'fr',
     913         'fr_NC' => 'fr',
     914         'fr_NE' => 'fr',
     915         'fr_PF' => 'fr',
     916         'fr_PM' => 'fr',
     917         'fr_RE' => 'fr',
     918         'fr_RW' => 'fr',
     919         'fr_SC' => 'fr',
     920         'fr_SN' => 'fr',
     921         'fr_SY' => 'fr',
     922         'fr_TD' => 'fr',
     923         'fr_TG' => 'fr',
     924         'fr_TN' => 'fr',
     925         'fr_VU' => 'fr',
     926         'fr_WF' => 'fr',
     927         'fr_YT' => 'fr',
     928         'fr_GB' => 'fr',
     929         'fr_US' => 'fr',
     930         'he_IL' => 'he',
     931         'hu_HU' => 'hu',
     932         'hu_AT' => 'hu',
     933         'hu_RO' => 'hu',
     934         'hu_RS' => 'hu',
     935         'is_IS' => 'is',
     936         'it_CH' => 'it',
     937         'it_IT' => 'it',
     938         'it_SM' => 'it',
     939         'it_FR' => 'it',
     940         'it_HR' => 'it',
     941         'it_US' => 'it',
     942         'it_VA' => 'it',
     943         'ja_JP' => 'ja',
     944         'ko_KP' => 'ko',
     945         'ko_KR' => 'ko',
     946         'ko_CN' => 'ko',
     947         'nb_NO' => 'nb',
     948         'nb_SJ' => 'nb',
     949         'nl_AN' => 'nl',
     950         'nl_AW' => 'nl',
     951         'nl_BE' => 'nl',
     952         'nl_NL' => 'nl',
     953         'nl_SR' => 'nl',
     954         'nn_NO' => 'nn',
     955         'pl_PL' => 'pl',
     956         'pl_UA' => 'pl',
     957         'pt_AO' => 'pt_br',
     958         'pt_BR' => 'pt_br',
     959         'pt_CV' => 'pt_br',
     960         'pt_GW' => 'pt_br',
     961         'pt_MZ' => 'pt_br',
     962         'pt_PT' => 'pt_br',
     963         'pt_ST' => 'pt_br',
     964         'pt_TL' => 'pt_br',
     965         'ro_MD' => 'ro',
     966         'ro_RO' => 'ro',
     967         'ro_RS' => 'ro',
     968         'ru_BY' => 'ru',
     969         'ru_KG' => 'ru',
     970         'ru_KZ' => 'ru',
     971         'ru_RU' => 'ru',
     972         'ru_SJ' => 'ru',
     973         'ru_UA' => 'ru',
     974         'si_LK' => 'si',
     975         'sk_SK' => 'sk',
     976         'sk_RS' => 'sk',
     977         'sq_AL' => 'sq',
     978         'sr_BA' => 'sr',
     979         'sr_ME' => 'sr',
     980         'sr_RS' => 'sr',
     981         'sv_FI' => 'sv',
     982         'sv_SE' => 'sv',
     983         'tr_CY' => 'tr',
     984         'tr_TR' => 'tr',
     985         'tr_DE' => 'tr',
     986         'tr_MK' => 'tr',
     987         'uk_UA' => 'uk',
     988         'vi_VN' => 'vi',
     989         'vi_US' => 'vi',
     990         'zh_CN' => 'zh_cn',
     991         'zh_HK' => 'zh_cn',
     992         'zh_MO' => 'zh_cn',
     993         'zh_SG' => 'zh_cn',
     994         'zh_TW' => 'zh_tw',
     995         'zh_ID' => 'zh_cn',
     996         'zh_MY' => 'zh_cn',
     997         'zh_TH' => 'zh_cn',
     998         'zh_US' => 'zn_cn',
     999
    1000     );
    1001
    1002     /**
    1003      * @var array $likely_subtags Provides you "likely locales"
    1004      * for a given "short" language code. This is a guess,
    1005      * as we can't disambiguate from e.g. "en" to "en_US" - it
    1006      * could also mean "en_UK".
    1007      * @see http://www.unicode.org/cldr/data/charts/supplemental/likely_subtags.html
    1008      */
    1009     static $likely_subtags = array(
    1010         'aa' => 'aa_ET',
    1011         'ab' => 'ab_GE',
    1012         'ady' => 'ady_RU',
    1013         'af' => 'af_ZA',
    1014         'ak' => 'ak_GH',
    1015         'am' => 'am_ET',
    1016         'ar' => 'ar_EG',
    1017         'as' => 'as_IN',
    1018         'ast' => 'ast_ES',
    1019         'av' => 'av_RU',
    1020         'ay' => 'ay_BO',
    1021         'az' => 'az_AZ',
    1022         'az_Cyrl' => 'az_AZ',
    1023         'az_Arab' => 'az_IR',
    1024         'az_IR' => 'az_IR',
    1025         'ba' => 'ba_RU',
    1026         'be' => 'be_BY',
    1027         'bg' => 'bg_BG',
    1028         'bi' => 'bi_VU',
    1029         'bn' => 'bn_BD',
    1030         'bo' => 'bo_CN',
    1031         'bs' => 'bs_BA',
    1032         'ca' => 'ca_ES',
    1033         'ce' => 'ce_RU',
    1034         'ceb' => 'ceb_PH',
    1035         'ch' => 'ch_GU',
    1036         'chk' => 'chk_FM',
    1037         'crk' => 'crk_CA',
    1038         'cs' => 'cs_CZ',
    1039         'cwd' => 'cwd_CA',
    1040         'cy' => 'cy_GB',
    1041         'da' => 'da_DK',
    1042         'de' => 'de_DE',
    1043         'dv' => 'dv_MV',
    1044         'dz' => 'dz_BT',
    1045         'ee' => 'ee_GH',
    1046         'efi' => 'efi_NG',
    1047         'el' => 'el_GR',
    1048         'en' => 'en_US',
    1049         'es' => 'es_ES',
    1050         'et' => 'et_EE',
    1051         'eu' => 'eu_ES',
    1052         'fa' => 'fa_IR',
    1053         'fi' => 'fi_FI',
    1054         'fil' => 'fil_PH',
    1055         'fj' => 'fj_FJ',
    1056         'fo' => 'fo_FO',
    1057         'fr' => 'fr_FR',
    1058         'fur' => 'fur_IT',
    1059         'fy' => 'fy_NL',
    1060         'ga' => 'ga_IE',
    1061         'gaa' => 'gaa_GH',
    1062         'gd' => 'gd_GB',
    1063         'gil' => 'gil_KI',
    1064         'gl' => 'gl_ES',
    1065         'gn' => 'gn_PY',
    1066         'gu' => 'gu_IN',
    1067         'ha' => 'ha_NG',
    1068         'ha_Arab' => 'ha_SD',
    1069         'ha_SD' => 'ha_SD',
    1070         'haw' => 'haw_US',
    1071         'he' => 'he_IL',
    1072         'hi' => 'hi_IN',
    1073         'hil' => 'hil_PH',
    1074         'ho' => 'ho_PG',
    1075         'hr' => 'hr_HR',
    1076         'ht' => 'ht_HT',
    1077         'hu' => 'hu_HU',
    1078         'hy' => 'hy_AM',
    1079         'id' => 'id_ID',
    1080         'ig' => 'ig_NG',
    1081         'ii' => 'ii_CN',
    1082         'ilo' => 'ilo_PH',
    1083         'inh' => 'inh_RU',
    1084         'is' => 'is_IS',
    1085         'it' => 'it_IT',
    1086         'iu' => 'iu_CA',
    1087         'ja' => 'ja_JP',
    1088         'jv' => 'jv_ID',
    1089         'ka' => 'ka_GE',
    1090         'kaj' => 'kaj_NG',
    1091         'kam' => 'kam_KE',
    1092         'kbd' => 'kbd_RU',
    1093         'kha' => 'kha_IN',
    1094         'kk' => 'kk_KZ',
    1095         'kl' => 'kl_GL',
    1096         'km' => 'km_KH',
    1097         'kn' => 'kn_IN',
    1098         'ko' => 'ko_KR',
    1099         'koi' => 'koi_RU',
    1100         'kok' => 'kok_IN',
    1101         'kos' => 'kos_FM',
    1102         'kpe' => 'kpe_LR',
    1103         'kpv' => 'kpv_RU',
    1104         'krc' => 'krc_RU',
    1105         'ks' => 'ks_IN',
    1106         'ku' => 'ku_IQ',
    1107         'ku_Latn' => 'ku_TR',
    1108         'ku_TR' => 'ku_TR',
    1109         'kum' => 'kum_RU',
    1110         'ky' => 'ky_KG',
    1111         'la' => 'la_VA',
    1112         'lah' => 'lah_PK',
    1113         'lb' => 'lb_LU',
    1114         'lbe' => 'lbe_RU',
    1115         'lez' => 'lez_RU',
    1116         'ln' => 'ln_CD',
    1117         'lo' => 'lo_LA',
    1118         'lt' => 'lt_LT',
    1119         'lv' => 'lv_LV',
    1120         'mai' => 'mai_IN',
    1121         'mdf' => 'mdf_RU',
    1122         'mdh' => 'mdh_PH',
    1123         'mg' => 'mg_MG',
    1124         'mh' => 'mh_MH',
    1125         'mi' => 'mi_NZ',
    1126         'mk' => 'mk_MK',
    1127         'ml' => 'ml_IN',
    1128         'mn' => 'mn_MN',
    1129         'mn_CN' => 'mn_CN',
    1130         'mn_Mong' => 'mn_CN',
    1131         'mr' => 'mr_IN',
    1132         'ms' => 'ms_MY',
    1133         'mt' => 'mt_MT',
    1134         'my' => 'my_MM',
    1135         'myv' => 'myv_RU',
    1136         'na' => 'na_NR',
    1137         'nb' => 'nb_NO',
    1138         'ne' => 'ne_NP',
    1139         'niu' => 'niu_NU',
    1140         'nl' => 'nl_NL',
    1141         'nn' => 'nn_NO',
    1142         'nr' => 'nr_ZA',
    1143         'nso' => 'nso_ZA',
    1144         'ny' => 'ny_MW',
    1145         'om' => 'om_ET',
    1146         'or' => 'or_IN',
    1147         'os' => 'os_GE',
    1148         'pa' => 'pa_IN',
    1149         'pa_Arab' => 'pa_PK',
    1150         'pa_PK' => 'pa_PK',
    1151         'pag' => 'pag_PH',
    1152         'pap' => 'pap_AN',
    1153         'pau' => 'pau_PW',
    1154         'pl' => 'pl_PL',
    1155         'pon' => 'pon_FM',
    1156         'ps' => 'ps_AF',
    1157         'pt' => 'pt_BR',
    1158         'qu' => 'qu_PE',
    1159         'rm' => 'rm_CH',
    1160         'rn' => 'rn_BI',
    1161         'ro' => 'ro_RO',
    1162         'ru' => 'ru_RU',
    1163         'rw' => 'rw_RW',
    1164         'sa' => 'sa_IN',
    1165         'sah' => 'sah_RU',
    1166         'sat' => 'sat_IN',
    1167         'sd' => 'sd_IN',
    1168         'se' => 'se_NO',
    1169         'sg' => 'sg_CF',
    1170         'si' => 'si_LK',
    1171         'sid' => 'sid_ET',
    1172         'sk' => 'sk_SK',
    1173         'sl' => 'sl_SI',
    1174         'sm' => 'sm_WS',
    1175         'sn' => 'sn_ZW',
    1176         'so' => 'so_SO',
    1177         'sq' => 'sq_AL',
    1178         'sr' => 'sr_RS',
    1179         'ss' => 'ss_ZA',
    1180         'st' => 'st_ZA',
    1181         'su' => 'su_ID',
    1182         'sv' => 'sv_SE',
    1183         'sw' => 'sw_TZ',
    1184         'swb' => 'swb_KM',
    1185         'ta' => 'ta_IN',
    1186         'te' => 'te_IN',
    1187         'tet' => 'tet_TL',
    1188         'tg' => 'tg_TJ',
    1189         'th' => 'th_TH',
    1190         'ti' => 'ti_ET',
    1191         'tig' => 'tig_ER',
    1192         'tk' => 'tk_TM',
    1193         'tkl' => 'tkl_TK',
    1194         'tl' => 'tl_PH',
    1195         'tn' => 'tn_ZA',
    1196         'to' => 'to_TO',
    1197         'tpi' => 'tpi_PG',
    1198         'tr' => 'tr_TR',
    1199         'trv' => 'trv_TW',
    1200         'ts' => 'ts_ZA',
    1201         'tsg' => 'tsg_PH',
    1202         'tt' => 'tt_RU',
    1203         'tts' => 'tts_TH',
    1204         'tvl' => 'tvl_TV',
    1205         'tw' => 'tw_GH',
    1206         'ty' => 'ty_PF',
    1207         'tyv' => 'tyv_RU',
    1208         'udm' => 'udm_RU',
    1209         'ug' => 'ug_CN',
    1210         'uk' => 'uk_UA',
    1211         'uli' => 'uli_FM',
    1212         'und' => 'en_US',
    1213         'und_AD' => 'ca_AD',
    1214         'und_AE' => 'ar_AE',
    1215         'und_AF' => 'fa_AF',
    1216         'und_AL' => 'sq_AL',
    1217         'und_AM' => 'hy_AM',
    1218         'und_AN' => 'pap_AN',
    1219         'und_AO' => 'pt_AO',
    1220         'und_AR' => 'es_AR',
    1221         'und_AS' => 'sm_AS',
    1222         'und_AT' => 'de_AT',
    1223         'und_AW' => 'nl_AW',
    1224         'und_AX' => 'sv_AX',
    1225         'und_AZ' => 'az_AZ',
    1226         'und_Arab' => 'ar_EG',
    1227         'und_Arab_CN' => 'ug_CN',
    1228         'und_Arab_DJ' => 'ar_DJ',
    1229         'und_Arab_ER' => 'ar_ER',
    1230         'und_Arab_IL' => 'ar_IL',
    1231         'und_Arab_IN' => 'ur_IN',
    1232         'und_Arab_PK' => 'ur_PK',
    1233         'und_Armn' => 'hy_AM',
    1234         'und_BA' => 'bs_BA',
    1235         'und_BD' => 'bn_BD',
    1236         'und_BE' => 'nl_BE',
    1237         'und_BF' => 'fr_BF',
    1238         'und_BG' => 'bg_BG',
    1239         'und_BH' => 'ar_BH',
    1240         'und_BI' => 'rn_BI',
    1241         'und_BJ' => 'fr_BJ',
    1242         'und_BL' => 'fr_BL',
    1243         'und_BN' => 'ms_BN',
    1244         'und_BO' => 'es_BO',
    1245         'und_BR' => 'pt_BR',
    1246         'und_BT' => 'dz_BT',
    1247         'und_BY' => 'be_BY',
    1248         'und_Beng' => 'bn_BD',
    1249         'und_CD' => 'fr_CD',
    1250         'und_CF' => 'sg_CF',
    1251         'und_CG' => 'ln_CG',
    1252         'und_CH' => 'de_CH',
    1253         'und_CI' => 'fr_CI',
    1254         'und_CL' => 'es_CL',
    1255         'und_CM' => 'fr_CM',
    1256         'und_CN' => 'zh_CN',
    1257         'und_CO' => 'es_CO',
    1258         'und_CR' => 'es_CR',
    1259         'und_CU' => 'es_CU',
    1260         'und_CV' => 'pt_CV',
    1261         'und_CY' => 'el_CY',
    1262         'und_CZ' => 'cs_CZ',
    1263         'und_Cans' => 'cwd_CA',
    1264         'und_Cyrl' => 'ru_RU',
    1265         'und_Cyrl_BA' => 'sr_BA',
    1266         'und_Cyrl_GE' => 'ab_GE',
    1267         'und_DE' => 'de_DE',
    1268         'und_DJ' => 'aa_DJ',
    1269         'und_DK' => 'da_DK',
    1270         'und_DO' => 'es_DO',
    1271         'und_DZ' => 'ar_DZ',
    1272         'und_Deva' => 'hi_IN',
    1273         'und_EC' => 'es_EC',
    1274         'und_EE' => 'et_EE',
    1275         'und_EG' => 'ar_EG',
    1276         'und_EH' => 'ar_EH',
    1277         'und_ER' => 'ti_ER',
    1278         'und_ES' => 'es_ES',
    1279         'und_ET' => 'am_ET',
    1280         'und_Ethi' => 'am_ET',
    1281         'und_FI' => 'fi_FI',
    1282         'und_FJ' => 'fj_FJ',
    1283         'und_FM' => 'chk_FM',
    1284         'und_FO' => 'fo_FO',
    1285         'und_FR' => 'fr_FR',
    1286         'und_GA' => 'fr_GA',
    1287         'und_GE' => 'ka_GE',
    1288         'und_GF' => 'fr_GF',
    1289         'und_GH' => 'ak_GH',
    1290         'und_GL' => 'kl_GL',
    1291         'und_GN' => 'fr_GN',
    1292         'und_GP' => 'fr_GP',
    1293         'und_GQ' => 'fr_GQ',
    1294         'und_GR' => 'el_GR',
    1295         'und_GT' => 'es_GT',
    1296         'und_GU' => 'ch_GU',
    1297         'und_GW' => 'pt_GW',
    1298         'und_Geor' => 'ka_GE',
    1299         'und_Grek' => 'el_GR',
    1300         'und_Gujr' => 'gu_IN',
    1301         'und_Guru' => 'pa_IN',
    1302         'und_HK' => 'zh_HK',
    1303         'und_HN' => 'es_HN',
    1304         'und_HR' => 'hr_HR',
    1305         'und_HT' => 'ht_HT',
    1306         'und_HU' => 'hu_HU',
    1307         'und_Hani' => 'zh_CN',
    1308         'und_Hans' => 'zh_CN',
    1309         'und_Hant' => 'zh_TW',
    1310         'und_Hebr' => 'he_IL',
    1311         'und_ID' => 'id_ID',
    1312         'und_IL' => 'he_IL',
    1313         'und_IN' => 'hi_IN',
    1314         'und_IQ' => 'ar_IQ',
    1315         'und_IR' => 'fa_IR',
    1316         'und_IS' => 'is_IS',
    1317         'und_IT' => 'it_IT',
    1318         'und_JO' => 'ar_JO',
    1319         'und_JP' => 'ja_JP',
    1320         'und_Jpan' => 'ja_JP',
    1321         'und_KG' => 'ky_KG',
    1322         'und_KH' => 'km_KH',
    1323         'und_KM' => 'ar_KM',
    1324         'und_KP' => 'ko_KP',
    1325         'und_KR' => 'ko_KR',
    1326         'und_KW' => 'ar_KW',
    1327         'und_KZ' => 'ru_KZ',
    1328         'und_Khmr' => 'km_KH',
    1329         'und_Knda' => 'kn_IN',
    1330         'und_Kore' => 'ko_KR',
    1331         'und_LA' => 'lo_LA',
    1332         'und_LB' => 'ar_LB',
    1333         'und_LI' => 'de_LI',
    1334         'und_LK' => 'si_LK',
    1335         'und_LS' => 'st_LS',
    1336         'und_LT' => 'lt_LT',
    1337         'und_LU' => 'fr_LU',
    1338         'und_LV' => 'lv_LV',
    1339         'und_LY' => 'ar_LY',
    1340         'und_Laoo' => 'lo_LA',
    1341         'und_Latn_CN' => 'ii_CN',
    1342         'und_Latn_CY' => 'tr_CY',
    1343         'und_Latn_DZ' => 'fr_DZ',
    1344         'und_Latn_ET' => 'om_ET',
    1345         'und_Latn_KM' => 'fr_KM',
    1346         'und_Latn_MA' => 'fr_MA',
    1347         'und_Latn_MK' => 'sq_MK',
    1348         'und_Latn_SY' => 'fr_SY',
    1349         'und_Latn_TD' => 'fr_TD',
    1350         'und_Latn_TN' => 'fr_TN',
    1351         'und_MA' => 'ar_MA',
    1352         'und_MC' => 'fr_MC',
    1353         'und_MD' => 'ro_MD',
    1354         'und_ME' => 'sr_ME',
    1355         'und_MF' => 'fr_MF',
    1356         'und_MG' => 'mg_MG',
    1357         'und_MH' => 'mh_MH',
    1358         'und_MK' => 'mk_MK',
    1359         'und_ML' => 'fr_ML',
    1360         'und_MM' => 'my_MM',
    1361         'und_MN' => 'mn_MN',
    1362         'und_MO' => 'zh_MO',
    1363         'und_MQ' => 'fr_MQ',
    1364         'und_MR' => 'ar_MR',
    1365         'und_MT' => 'mt_MT',
    1366         'und_MV' => 'dv_MV',
    1367         'und_MW' => 'ny_MW',
    1368         'und_MX' => 'es_MX',
    1369         'und_MY' => 'ms_MY',
    1370         'und_MZ' => 'pt_MZ',
    1371         'und_Mlym' => 'ml_IN',
    1372         'und_Mong' => 'mn_CN',
    1373         'und_Mymr' => 'my_MM',
    1374         'und_NC' => 'fr_NC',
    1375         'und_NE' => 'ha_NE',
    1376         'und_NG' => 'ha_NG',
    1377         'und_NI' => 'es_NI',
    1378         'und_NL' => 'nl_NL',
    1379         'und_NO' => 'nb_NO',
    1380         'und_NP' => 'ne_NP',
    1381         'und_NR' => 'na_NR',
    1382         'und_NU' => 'niu_NU',
    1383         'und_OM' => 'ar_OM',
    1384         'und_Orya' => 'or_IN',
    1385         'und_PA' => 'es_PA',
    1386         'und_PE' => 'es_PE',
    1387         'und_PF' => 'ty_PF',
    1388         'und_PG' => 'tpi_PG',
    1389         'und_PH' => 'fil_PH',
    1390         'und_PK' => 'ur_PK',
    1391         'und_PL' => 'pl_PL',
    1392         'und_PM' => 'fr_PM',
    1393         'und_PR' => 'es_PR',
    1394         'und_PS' => 'ar_PS',
    1395         'und_PT' => 'pt_PT',
    1396         'und_PW' => 'pau_PW',
    1397         'und_PY' => 'gn_PY',
    1398         'und_QA' => 'ar_QA',
    1399         'und_RE' => 'fr_RE',
    1400         'und_RO' => 'ro_RO',
    1401         'und_RS' => 'sr_RS',
    1402         'und_RU' => 'ru_RU',
    1403         'und_RW' => 'rw_RW',
    1404         'und_SA' => 'ar_SA',
    1405         'und_SD' => 'ar_SD',
    1406         'und_SE' => 'sv_SE',
    1407         'und_SI' => 'sl_SI',
    1408         'und_SJ' => 'nb_SJ',
    1409         'und_SK' => 'sk_SK',
    1410         'und_SM' => 'it_SM',
    1411         'und_SN' => 'fr_SN',
    1412         'und_SO' => 'so_SO',
    1413         'und_SR' => 'nl_SR',
    1414         'und_ST' => 'pt_ST',
    1415         'und_SV' => 'es_SV',
    1416         'und_SY' => 'ar_SY',
    1417         'und_Sinh' => 'si_LK',
    1418         'und_TD' => 'ar_TD',
    1419         'und_TG' => 'ee_TG',
    1420         'und_TH' => 'th_TH',
    1421         'und_TJ' => 'tg_TJ',
    1422         'und_TK' => 'tkl_TK',
    1423         'und_TL' => 'tet_TL',
    1424         'und_TM' => 'tk_TM',
    1425         'und_TN' => 'ar_TN',
    1426         'und_TO' => 'to_TO',
    1427         'und_TR' => 'tr_TR',
    1428         'und_TV' => 'tvl_TV',
    1429         'und_TW' => 'zh_TW',
    1430         'und_Taml' => 'ta_IN',
    1431         'und_Telu' => 'te_IN',
    1432         'und_Thaa' => 'dv_MV',
    1433         'und_Thai' => 'th_TH',
    1434         'und_Tibt' => 'bo_CN',
    1435         'und_UA' => 'uk_UA',
    1436         'und_UY' => 'es_UY',
    1437         'und_UZ' => 'uz_UZ',
    1438         'und_VA' => 'la_VA',
    1439         'und_VE' => 'es_VE',
    1440         'und_VN' => 'vi_VN',
    1441         'und_VU' => 'fr_VU',
    1442         'und_WF' => 'fr_WF',
    1443         'und_WS' => 'sm_WS',
    1444         'und_YE' => 'ar_YE',
    1445         'und_YT' => 'fr_YT',
    1446         'und_ZW' => 'sn_ZW',
    1447         'ur' => 'ur_PK',
    1448         'uz' => 'uz_UZ',
    1449         'uz_AF' => 'uz_AF',
    1450         'uz_Arab' => 'uz_AF',
    1451         've' => 've_ZA',
    1452         'vi' => 'vi_VN',
    1453         'wal' => 'wal_ET',
    1454         'war' => 'war_PH',
    1455         'wo' => 'wo_SN',
    1456         'xh' => 'xh_ZA',
    1457         'yap' => 'yap_FM',
    1458         'yo' => 'yo_NG',
    1459         'za' => 'za_CN',
    1460         'zh' => 'zh_CN',
    1461         'zh_HK' => 'zh_HK',
    1462         'zh_Hani' => 'zh_CN',
    1463         'zh_Hant' => 'zh_TW',
    1464         'zh_MO' => 'zh_MO',
    1465         'zh_TW' => 'zh_TW',
    1466         'zu' => 'zu_ZA',
    1467     );
    1468
    1469     /**
    1470      * This is the main translator function. Returns the string defined by $class and $entity according to the currently set locale.
    1471      *
    1472      * @param string $entity Entity that identifies the string. It must be in the form "Namespace.Entity" where Namespace will be usually
    1473      *                       the class name where this string is used and Entity identifies the string inside the namespace.
    1474      * @param string $string The original string itself. In a usual call this is a mandatory parameter, but if you are reusing a string which
    1475      *               has already been "declared" (using another call to this function, with the same class and entity), you can omit it.
    1476      * @param string $priority Optional parameter to set a translation priority. If a string is widely used, should have a high priority (PR_HIGH),
    1477      *                  in this way translators will be able to prioritise this strings. If a string is rarely shown, you should use PR_LOW.
    1478      *                  You can use PR_MEDIUM as well. Leaving this field blank will be interpretated as a "normal" priority (less than PR_MEDIUM).
    1479      * @param string $context If the string can be difficult to translate by any reason, you can help translators with some more info using this param
    1480      *
    1481      * @return string The translated string, according to the currently set locale {@link i18n::set_locale()}
    1482      */
    1483     static function _t($entity, $string = "", $priority = 40, $context = "") {
    1484         global $lang;
    1485
    1486         // get current locale (either default or user preference)
    1487         $locale = i18n::get_locale();
    1488
    1489         // parse $entity into its parts
    1490         $entityParts = explode('.',$entity);
    1491         $realEntity = array_pop($entityParts);
    1492         $class = implode('.',$entityParts);
    1493
    1494         // if language table isn't loaded for this locale, get it for each of the modules
    1495         if(!isset($lang[$locale])) i18n::include_by_locale($locale);
    1496
    1497             // dvp. to easy find entity names in dev mode
    1498             if (Director::isDev()) $string .= " ($locale.$entity)";
    1499
    1500                 // fallback to the passed $string if no translation is present
    1501         $transEntity = isset($lang[$locale][$class][$realEntity]) ? $lang[$locale][$class][$realEntity] : $string;
    1502
    1503         // entities can be stored in both array and literal values in the language tables
    1504         return (is_array($transEntity) ? $transEntity[0] : $transEntity);
    1505     }
    1506
    1507     /**
    1508      * Get a list of commonly used languages
    1509      *
    1510      * @param boolean $native Use native names for languages instead of English ones
    1511      * @return list of languages in the form 'code' => 'name'
    1512      */
    1513     static function get_common_languages($native = false) {
    1514         $languages = array();
    1515         foreach (self::$common_languages as $code => $name) {
    1516             $languages[$code] = ($native ? $name[1] : $name[0]);
    1517         }
    1518         return $languages;
    1519     }
    1520
    1521     /**
    1522      * Get a list of commonly used locales
    1523      *
    1524      * @param boolean $native Use native names for locale instead of English ones
    1525      * @return list of languages in the form 'code' => 'name'
    1526      */
    1527     static function get_common_locales($native = false) {
    1528         $languages = array();
    1529         foreach (self::$common_locales as $code => $name) {
    1530             $languages[$code] = ($native ? $name[1] : $name[0]);
    1531         }
    1532         return $languages;
    1533     }
    1534
    1535     /**
    1536      * Get a list of locales (code => language and country)
    1537      *
    1538      * @return list of languages in the form 'code' => 'name'
    1539      */
    1540     static function get_locale_list() {
    1541         return self::$all_locales;
    1542     }
    1543
    1544     /**
    1545      * Searches the root-directory for module-directories
    1546      * (identified by having a _config.php on their first directory-level).
    1547      * Returns all found locales.
    1548      *
    1549      * @return array
    1550      */
    1551     static function get_existing_translations() {
    1552         $locales = array();
    1553
    1554         $baseDir = Director::baseFolder();
    1555         $modules = scandir($baseDir);
    1556         foreach($modules as $module) {
    1557             if($module[0] == '.') continue;
    1558
    1559             $moduleDir = $baseDir . DIRECTORY_SEPARATOR . $module;
    1560             $langDir = $moduleDir . DIRECTORY_SEPARATOR . "lang";
    1561             if(is_dir($moduleDir) && is_file($moduleDir . DIRECTORY_SEPARATOR . "_config.php") && is_dir($langDir)) {
    1562                 $moduleLocales = scandir($langDir);
    1563                 foreach($moduleLocales as $moduleLocale) {
    1564                     if(preg_match('/(.*)\.php$/',$moduleLocale, $matches)) {
    1565                         if(isset($matches[1]) && isset(self::$all_locales[$matches[1]])) {
    1566                             $locales[$matches[1]] = self::$all_locales[$matches[1]];
    1567                         }
    1568                     }
    1569                 }
    1570             }
    1571         }
    1572
    1573         // sort by title (not locale)
    1574         asort($locales);
    1575
    1576         return $locales;
    1577     }
    1578
    1579     /**
    1580      * Get a name from a language code (two characters, e.g. "en").
    1581      *
    1582      * @see get_locale_name()
    1583      *
    1584      * @param mixed $code Language code
    1585      * @param boolean $native If true, the native name will be returned
    1586      * @return Name of the language
    1587      */
    1588     static function get_language_name($code, $native = false) {
    1589         $langs = self::$common_languages;
    1590         if($native) {
    1591             return (isset($langs[$code][1])) ? $langs[$code][1] : false;
    1592         } else {
    1593             return (isset($langs[$code][0])) ? $langs[$code][0] : false;
    1594         }
    1595     }
    1596
    1597     /**
    1598      * Get a name from a locale code (xx_YY).
    1599      *
    1600      * @see get_language_name()
    1601      *
    1602      * @param mixed $code locale code
    1603      * @return Name of the locale
    1604      */
    1605     static function get_locale_name($code) {
    1606         $langs = self::get_locale_list();
    1607         return isset($langs[$code]) ? $langs[$code] : false;
    1608     }
    1609
    1610     /**
    1611      * Get a code from an English language name
    1612      *
    1613      * @param mixed $name Name of the language
    1614      * @return Language code (if the name is not found, it'll return the passed name)
    1615      */
    1616     static function get_language_code($name) {
    1617         $code = array_search($name,self::get_common_languages());
    1618         return ($code ? $code : $name);
    1619     }
    1620
    1621     /**
    1622      * Get the current tinyMCE language
    1623      *
    1624      * @return Language
    1625      */
    1626     static function get_tinymce_lang() {
    1627         if(isset(self::$tinymce_lang[self::get_locale()])) {
    1628             return self::$tinymce_lang[self::get_locale()];
    1629         }
    1630
    1631         return 'en';
    1632     }
    1633
    1634     /**
    1635      * Searches the root-directory for module-directories
    1636      * (identified by having a _config.php on their first directory-level
    1637      * and a language-file with the default locale in the /lang-subdirectory).
    1638      *
    1639      * @return array
    1640      */
    1641     static function get_translatable_modules() {
    1642         $translatableModules = array();
    1643
    1644         $baseDir = Director::baseFolder();
    1645         $modules = scandir($baseDir);
    1646         foreach($modules as $module) {
    1647             $moduleDir = $baseDir . DIRECTORY_SEPARATOR . $module;
    1648             if(
    1649                 is_dir($moduleDir)
    1650                 && is_file($moduleDir . DIRECTORY_SEPARATOR . "_config.php")
    1651                 && is_file($moduleDir . DIRECTORY_SEPARATOR . "lang" . DIRECTORY_SEPARATOR . self::$default_locale . ".php")
    1652             ) {
    1653                 $translatableModules[] = $module;
    1654             }
    1655         }
    1656         return $translatableModules;
    1657     }
    1658
    1659     /**
    1660      * Returns the "short" language name from a locale,
    1661      * e.g. "en_US" would return "en". This conversion
    1662      * is determined internally by the {@link $tinymce_lang}
    1663      * lookup table. If no match can be found in this lookup,
    1664      * the characters before the underscore ("_") are returned.
    1665      *
    1666      * @todo More generic lookup table, don't rely on tinymce specific conversion
    1667      *
    1668      * @param string $locale E.g. "en_US"
    1669      * @return string Short language code, e.g. "en"
    1670      */
    1671     static function get_lang_from_locale($locale) {
    1672         if(isset(self::$tinymce_lang[$locale])) {
    1673             return self::$tinymce_lang[$locale];
    1674         } else {
    1675             return preg_replace('/(_|-).*/', '', $locale);
    1676         }
    1677     }
    1678
    1679     /**
    1680      * Provides you "likely locales"
    1681      * for a given "short" language code. This is a guess,
    1682      * as we can't disambiguate from e.g. "en" to "en_US" - it
    1683      * could also mean "en_UK". Based on the Unicode CLDR
    1684      * project.
    1685      * @see http://www.unicode.org/cldr/data/charts/supplemental/likely_subtags.html
    1686      *
    1687      * @param string $lang Short language code, e.g. "en"
    1688      * @return string Long locale, e.g. "en_US"
    1689      */
    1690     static function get_locale_from_lang($lang) {
    1691         if(preg_match('/\-|_/', $lang)) {
    1692             return $lang;
    1693         } else if(isset(self::$likely_subtags[$lang])) {
    1694             return self::$likely_subtags[$lang];
    1695         } else {
    1696             return $lang . '_' . strtoupper($lang);
    1697         }
    1698     }
    1699
    1700     /**
    1701      * Gets a RFC 1766 compatible language code,
    1702      * e.g. "en-US".
    1703      *
    1704      * @see http://www.ietf.org/rfc/rfc1766.txt
    1705      * @see http://tools.ietf.org/html/rfc2616#section-3.10
    1706      *
    1707      * @param string $locale
    1708      * @return string
    1709      */
    1710     static function convert_rfc1766($locale) {
    1711         return str_replace('_','-', $locale);
    1712     }
    1713
    1714     /**
    1715      * Given a file name (a php class name, without the .php ext, or a template name, including the .ss extension)
    1716      * this helper function determines the module where this file is located
    1717      *
    1718      * @param string $name php class name or template file name (including *.ss extension)
    1719      * @return string Module where the file is located
    1720      */
    1721     public static function get_owner_module($name) {
    1722         // if $name is a template file
    1723         if(substr($name,-3) == '.ss') {
    1724             global $_TEMPLATE_MANIFEST;
    1725             $templateManifest = $_TEMPLATE_MANIFEST[substr($name,0,-3)];
    1726             if(is_array($templateManifest) && isset($templateManifest['themes'])) {
    1727                 $absolutePath = $templateManifest['themes'][SSViewer::current_theme()];
    1728             } else {
    1729                 $absolutePath = $templateManifest;
    1730             }
    1731
    1732             $path = str_replace('\\','/',Director::makeRelative(current($absolutePath)));
    1733
    1734             ereg('/([^/]+)/',$path,$module);
    1735         }
    1736         // $name is assumed to be a PHP class
    1737         else {
    1738             global $_CLASS_MANIFEST;
    1739             if(strpos($name,'_') !== false) $name = strtok($name,'_');
    1740             $name = strtolower($name); // Necessary because of r101131
    1741             if(isset($_CLASS_MANIFEST[$name])) {
    1742                 $path = str_replace('\\','/',Director::makeRelative($_CLASS_MANIFEST[$name]));
    1743                 ereg('/([^/]+)/', $path, $module);
    1744             }
    1745         }
    1746         return (isset($module)) ? $module[1] : false;
    1747
    1748     }
    1749
    1750     /**
    1751      * Validates a "long" locale format (e.g. "en_US")
    1752      * by checking it against {@link $all_locales}.
    1753      *
    1754      * To add a locale to {@link $all_locales}, use the following example
    1755      * in your mysite/_config.php:
    1756      * <code>
    1757      * i18n::$allowed_locales['xx_XX'] = '<Language name>';
    1758      * </code>
    1759      *
    1760      * Note: Does not check for {@link $allowed_locales}.
    1761      *
    1762      * @return boolean
    1763      */
    1764     static function validate_locale($locale) {
    1765         // Convert en-US to en_US
    1766         $locale = str_replace('-', '_', $locale);
    1767         return (array_key_exists($locale, self::$all_locales));
    1768     }
    1769
    1770     /**
    1771      * Set the current locale, used as the default for
    1772      * any localized classes, such as {@link FormField} or {@link DBField}
    1773      * instances. Locales can also be persisted in {@link Member->Locale},
    1774      * for example in the {@link CMSMain} interface the Member locale
    1775      * overrules the global locale value set here.
    1776      *
    1777      * See {@link Translatable::set_locale()}.
    1778      *
    1779      * @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.
    1780      */
    1781     static function set_locale($locale) {
    1782         if(!self::validate_locale($locale)) throw new InvalidArgumentException(sprintf('Invalid locale "%s"', $locale));
    1783
    1784         if ($locale) {
    1785             self::$current_locale = $locale;
    1786             setlocale(LC_ALL, $locale . '.UTF-8');
    1787             setlocale(LC_NUMERIC, 'en_US.UTF-8');
    1788         }
    1789     }
    1790
    1791     /**
    1792      * Get the current locale.
    1793      * Used by {@link Member::populateDefaults()}
    1794      *
    1795      * @return string Current locale in the system
    1796      */
    1797     static function get_locale() {
    1798         return (!empty(self::$current_locale)) ? self::$current_locale : self::$default_locale;
    1799     }
    1800
    1801     /**
    1802      * @deprecated 2.4 Use Translatable::set_default_locale() or i18n::set_default_locale()
    1803      * @param $lang String
    1804      */
    1805     static function set_default_lang($lang) {
    1806         Translatable::set_default_lang($lang);
    1807     }
    1808
    1809     /**
    1810      * @deprecated 2.4 Use Translatable::default_locale() or i18n::default_locale()
    1811      * @return String
    1812      */
    1813     static function default_lang() {
    1814         return Translatable::default_locale();
    1815     }
    1816
    1817     /**
    1818      * This is the "fallback locale", in case resources with the "current locale"
    1819      * (set through {@link set_locale()}) can't be found.
    1820      *
    1821      * If you just want to globally read/write a different locale (e.g. in a CMS interface),
    1822      * please use {@link get_locale()} and {@link set_locale()} instead.
    1823      *
    1824      * For example, {@link Requirements::add_i18n_javascript()} and {@link i18n::include_by_class()}
    1825      * use this "fallback locale" value to include fallback language files.
    1826      *
    1827      * @return String
    1828      */
    1829     static function default_locale() {
    1830         return self::$default_locale;
    1831     }
    1832
    1833     /**
    1834      * See {@link default_locale()} for usage.
    1835      *
    1836      *
    1837      * @param String $locale
    1838      */
    1839     static function set_default_locale($locale) {
    1840         if(!self::validate_locale($locale)) throw new InvalidArgumentException(sprintf('Invalid locale "%s"', $locale));
    1841
    1842         self::$default_locale = $locale;
    1843     }
    1844
    1845     /**
    1846      * Enables the multilingual content feature (proxy for Translatable::enable()).
    1847      *
    1848      * @deprecated 2.4 Use Object::add_extension('Page', 'Translatable');
    1849      */
    1850     static function enable() {
    1851         Translatable::enable();
    1852     }
    1853
    1854     /**
    1855      * Disable the multilingual content feature (proxy for Translatable::disable())
    1856      *
    1857      * @deprecated 2.4 Use Object::add_extension('Page', 'Translatable');
    1858      */
    1859     static function disable() {
    1860         Translatable::disable();
    1861     }
    1862
    1863     /**
    1864      * Include a locale file determined by module name and locale
    1865      *
    1866      * @param string $module Module that contains the locale file
    1867      * @param string $locale Locale to be loaded
    1868      */
    1869     static function include_locale_file($module, $locale) {
    1870         if(!self::validate_locale($locale)) throw new InvalidArgumentException(sprintf('Invalid locale "%s"', $locale));
    1871
    1872         if (file_exists($file = Director::getAbsFile("$module/lang/$locale.php"))) include_once($file);
    1873     }
    1874
    1875     /**
    1876      * Includes all available language files for a certain defined locale
    1877      *
    1878      * @param string $locale All resources from any module in locale $locale will be loaded
    1879      * @param boolean $load_plugins     If true (default), load extra translations from registered plugins
    1880      * @param boolean $force_load       If true (not default), we force the inclusion. Generally this should be off
    1881      *                                  for performance, but enabling this is useful for interfaces like
    1882      *                                  CustomTranslationAdmin which need to load more than the usual locales,
    1883      *                                  and may need to reload them.
    1884      */
    1885     static function include_by_locale($locale, $load_plugins = true, $force_load = false) {
    1886         if(!self::validate_locale($locale)) throw new InvalidArgumentException(sprintf('Invalid locale "%s"', $locale));
    1887
    1888         global $lang,$project;
    1889
    1890         $base = Director::baseFolder();
    1891         $topLevel = scandir($base);
    1892
    1893         foreach($topLevel as $module) {
    1894             // $topLevel is the website root, some servers are configured not to allow excess website root's parent level
    1895             // and we don't need to check website root's parent level and website root level for its lang folder, so
    1896             // we skip these 2 levels checking.
    1897             if($module[0] == '.') continue;
    1898
    1899             // project include at last for ability to overwrite other modules
    1900             if ($module == $project) continue;
    1901
    1902             if (
    1903                 is_dir("$base/$module")
    1904                 && file_exists("$base/$module/_config.php")
    1905               && file_exists($file = "$base/$module/lang/$locale.php")
    1906             ) {
    1907                 if ($force_load) include($file);
    1908                 else include_once($file);
    1909             }
    1910         }
    1911
    1912         if (file_exists($file = "$base/$project/lang/$locale.php")) {
    1913             if ($force_load) include($file);
    1914             else include_once($file);
    1915         }
    1916
    1917         // Load translations from themes
    1918         $themesBase = $base . '/themes';
    1919                 if(file_exists($themesBase)){
    1920                     foreach(scandir($themesBase) as $theme) {
    1921                             if(
    1922                                     strpos($theme, SSViewer::current_theme()) === 0
    1923                                     && file_exists($file = "$themesBase/$theme/lang/$locale.php")
    1924                             ) {
    1925                                     if ($force_load) include($file);
    1926                                     else include_once($file);
    1927                             }
    1928                     }
    1929                 }
    1930
    1931         // Finally, load any translations from registered plugins
    1932         if ($load_plugins) self::plugins_load($locale);
    1933     }
    1934
    1935     /**
    1936      * Given a class name (a "locale namespace"), will search for its module and, if available,
    1937      * will load the resources for the currently defined locale.
    1938      * If not available, the original English resource will be loaded instead (to avoid blanks)
    1939      *
    1940      * @param string $class Resources for this class will be included, according to the set locale.
    1941      */
    1942     static function include_by_class($class) {
    1943         $module = self::get_owner_module($class);
    1944
    1945         if(!$module) user_error("i18n::include_by_class: Class {$class} not found", E_USER_WARNING);
    1946         $locale = self::get_locale();
    1947
    1948         if (file_exists($file = Director::getAbsFile("$module/lang/". self::get_locale() . '.php'))) {
    1949             include($file);
    1950         } else if (self::get_locale() != self::$default_locale) {
    1951                 $old = self::get_locale();
    1952             self::set_locale(self::$default_locale);
    1953             self::include_by_class($class);
    1954             self::set_locale($old);
    1955
    1956         } else if(file_exists(Director::getAbsFile("$module/lang"))) {
    1957             user_error("i18n::include_by_class: Locale file $file should exist", E_USER_WARNING);
    1958         }
    1959
    1960         // If the language file wasn't included for this class, include an empty array to prevent
    1961         // this method from being called again
    1962         global $lang;
    1963         if(!isset($lang[$locale][$class])) $lang[$locale][$class] = array();
    1964
    1965     }
    1966
    1967     //-----------------------------------------------------------------------------------------------//
    1968
    1969     /**
    1970      * This method will delete every SiteTree instance in the given language
    1971      */
    1972     public function removelang() {
    1973         if (!Permission::check("ADMIN")) user_error("You must be an admin to remove a language", E_USER_ERROR);
    1974         $translatedToDelete = Translatable::get_by_locale('SiteTree',$this->urlParams['ID']);
    1975         foreach ($translatedToDelete as $object) {
    1976             $object->delete();
    1977         }
    1978         echo "Language {$this->urlParams['ID']} successfully removed";
    1979     }
    1980
    1981     /**
    1982      * This variable holds translation plugins that are invoked on a call to _t. It is a two dimensional array,
    1983      * priority the first dimension and name the second, mapping to the callback.
    1984      * Translations from lower priority plugins are used first, and callback is a callback for call_user_func_array.
    1985      *
    1986      * Callback functions are passed one parameter:
    1987      * - locale string
    1988      * The callback function should return an array that can be merged with $lang[$locale], overriding values read
    1989      * from the language file.
    1990      *
    1991      * @var array
    1992      */
    1993     private static $plugins = array();
    1994
    1995     /**
    1996      * Register a named translation plug-in function.
    1997      * Plug-ins are assumed to be registered before any call to _t. If registered after a call to _t
    1998      * for a given local, it will not be called.
    1999      * @static
    2000      * @throws Exception
    2001      * @param  $name        String      A unique name for the translation plug-in. If the plug-in is already registered,
    2002      *                                  it is replaced, including if its a different priority.
    2003      * @param  $callback                A callback function as given to call_user_func_array.
    2004      * @param int $priority             An integer priority, default 10.
    2005      * @return void
    2006      */
    2007     static function register_plugin($name, $callback, $priority = 10) {
    2008         // Validate
    2009         if (!is_int($priority)) throw new Exception("register_plugin expects an int priority");
    2010
    2011         // Ensure it's not there. If it is, we're replacing it. It may exist in a different priority.
    2012         self::unregister_plugin($name);
    2013
    2014         // Add it.
    2015         self::$plugins[$priority][$name] = $callback;
    2016     }
    2017
    2018     /**
    2019      * Unregister a plugin by name.
    2020      * @static
    2021      * @param  $name    String      Name of previously registered plugin
    2022      * @return Boolean              Returns true if remove, false if not.
    2023      */
    2024     static function unregister_plugin($name) {
    2025         foreach (self::$plugins as $priority => $plugins) {
    2026             if (isset($plugins[$name])) unset(self::$plugins[$priority][$name]);
    2027         }
    2028     }
    2029
    2030     /**
    2031      * Load any translations from registered plugins. Merges them directly into $lang.
    2032      * @static
    2033      * @param  $local
    2034      * @param  $value
    2035      * @return void
    2036      */
    2037     static function plugins_load($locale) {
    2038         // sort the plugins by lowest priority (highest value) first, as each one replaces translations of the provider
    2039         // before it.
    2040         krsort(self::$plugins);
    2041         foreach (self::$plugins as $priority => $plugins) {
    2042             foreach ($plugins as $name => $callback) {
    2043                 self::merge_locale_data($locale, call_user_func_array($callback, array($locale)));
    2044             }
    2045         }
    2046     }
    2047
    2048     /**
    2049      * Merge an extra of language translations into $lang[$locale]. We'd use array_merge_recursive, except
    2050      * it doesn't work for translations that specify priorities and comments, because they are indexed by number.
    2051      * @static
    2052      * @param $locale String        The locale we are merging into
    2053      * @param $extra Array          An array of [locale][class][entity]=> translation, keyed on entity, that are to be
    2054      *                              merged for this locale.
    2055      * @return void
    2056      */
    2057     static function merge_locale_data($locale, $extra) {
    2058         global $lang;
    2059         if (!$extra || count($extra) == 0 || !isset($extra[$locale])) return;
    2060         foreach ($extra[$locale] as $class => $entities) {
    2061             foreach ($entities as $entity => $translation) {
    2062                 $lang[$locale][$class][$entity] = $translation;
    2063             }
    2064         }
    2065     }
    2066 }
    2067
    2068 ?>
    2069

[Raise a SilverStripe Framework issue/bug](https://github.com/silverstripe/silverstripe-framework/issues/new)
- [Raise a SilverStripe CMS issue/bug](https://github.com/silverstripe/silverstripe-cms/issues/new)
- Please use the Silverstripe Forums to ask development related questions. -