2014-02-10 02:10:30 +01:00
/*************************************************************************/
/* translation.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 14:16:55 +02:00
/* https://godotengine.org */
2014-02-10 02:10:30 +01:00
/*************************************************************************/
2020-01-01 11:16:22 +01:00
/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.md). */
2014-02-10 02:10:30 +01:00
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/*************************************************************************/
2018-01-05 00:50:27 +01:00
2014-02-10 02:10:30 +01:00
# include "translation.h"
2017-01-16 08:04:19 +01:00
2020-11-07 23:33:38 +01:00
# include "core/config/project_settings.h"
2018-09-11 18:13:45 +02:00
# include "core/io/resource_loader.h"
# include "core/os/os.h"
2014-02-10 02:10:30 +01:00
2018-02-14 18:49:27 +01:00
// ISO 639-1 language codes, with the addition of glibc locales with their
// regional identifiers. This list must match the language names (in English)
// of locale_names.
//
// References:
// - https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
// - https://lh.2xlibre.net/locales/
2017-03-05 16:44:50 +01:00
static const char * locale_list [ ] = {
" aa " , // Afar
" aa_DJ " , // Afar (Djibouti)
" aa_ER " , // Afar (Eritrea)
" aa_ET " , // Afar (Ethiopia)
" af " , // Afrikaans
" af_ZA " , // Afrikaans (South Africa)
" agr_PE " , // Aguaruna (Peru)
" ak_GH " , // Akan (Ghana)
" am_ET " , // Amharic (Ethiopia)
" an_ES " , // Aragonese (Spain)
" anp_IN " , // Angika (India)
" ar " , // Arabic
" ar_AE " , // Arabic (United Arab Emirates)
" ar_BH " , // Arabic (Bahrain)
" ar_DZ " , // Arabic (Algeria)
" ar_EG " , // Arabic (Egypt)
" ar_IN " , // Arabic (India)
" ar_IQ " , // Arabic (Iraq)
" ar_JO " , // Arabic (Jordan)
" ar_KW " , // Arabic (Kuwait)
" ar_LB " , // Arabic (Lebanon)
" ar_LY " , // Arabic (Libya)
" ar_MA " , // Arabic (Morocco)
" ar_OM " , // Arabic (Oman)
" ar_QA " , // Arabic (Qatar)
" ar_SA " , // Arabic (Saudi Arabia)
" ar_SD " , // Arabic (Sudan)
" ar_SS " , // Arabic (South Soudan)
" ar_SY " , // Arabic (Syria)
" ar_TN " , // Arabic (Tunisia)
" ar_YE " , // Arabic (Yemen)
" as_IN " , // Assamese (India)
" ast_ES " , // Asturian (Spain)
" ayc_PE " , // Southern Aymara (Peru)
" ay_PE " , // Aymara (Peru)
" az_AZ " , // Azerbaijani (Azerbaijan)
" be " , // Belarusian
" be_BY " , // Belarusian (Belarus)
" bem_ZM " , // Bemba (Zambia)
" ber_DZ " , // Berber languages (Algeria)
" ber_MA " , // Berber languages (Morocco)
" bg " , // Bulgarian
" bg_BG " , // Bulgarian (Bulgaria)
" bhb_IN " , // Bhili (India)
" bho_IN " , // Bhojpuri (India)
" bi_TV " , // Bislama (Tuvalu)
" bn " , // Bengali
" bn_BD " , // Bengali (Bangladesh)
" bn_IN " , // Bengali (India)
" bo " , // Tibetan
" bo_CN " , // Tibetan (China)
" bo_IN " , // Tibetan (India)
" br_FR " , // Breton (France)
" brx_IN " , // Bodo (India)
" bs_BA " , // Bosnian (Bosnia and Herzegovina)
" byn_ER " , // Bilin (Eritrea)
" ca " , // Catalan
" ca_AD " , // Catalan (Andorra)
" ca_ES " , // Catalan (Spain)
" ca_FR " , // Catalan (France)
" ca_IT " , // Catalan (Italy)
" ce_RU " , // Chechen (Russia)
" chr_US " , // Cherokee (United States)
" cmn_TW " , // Mandarin Chinese (Taiwan)
" crh_UA " , // Crimean Tatar (Ukraine)
" csb_PL " , // Kashubian (Poland)
" cs " , // Czech
" cs_CZ " , // Czech (Czech Republic)
" cv_RU " , // Chuvash (Russia)
" cy_GB " , // Welsh (United Kingdom)
" da " , // Danish
" da_DK " , // Danish (Denmark)
" de " , // German
" de_AT " , // German (Austria)
" de_BE " , // German (Belgium)
" de_CH " , // German (Switzerland)
" de_DE " , // German (Germany)
" de_IT " , // German (Italy)
" de_LU " , // German (Luxembourg)
" doi_IN " , // Dogri (India)
" dv_MV " , // Dhivehi (Maldives)
" dz_BT " , // Dzongkha (Bhutan)
" el " , // Greek
" el_CY " , // Greek (Cyprus)
" el_GR " , // Greek (Greece)
" en " , // English
" en_AG " , // English (Antigua and Barbuda)
" en_AU " , // English (Australia)
" en_BW " , // English (Botswana)
" en_CA " , // English (Canada)
" en_DK " , // English (Denmark)
" en_GB " , // English (United Kingdom)
" en_HK " , // English (Hong Kong)
" en_IE " , // English (Ireland)
" en_IL " , // English (Israel)
" en_IN " , // English (India)
" en_NG " , // English (Nigeria)
" en_NZ " , // English (New Zealand)
" en_PH " , // English (Philippines)
" en_SG " , // English (Singapore)
" en_US " , // English (United States)
" en_ZA " , // English (South Africa)
" en_ZM " , // English (Zambia)
" en_ZW " , // English (Zimbabwe)
" eo " , // Esperanto
" es " , // Spanish
" es_AR " , // Spanish (Argentina)
" es_BO " , // Spanish (Bolivia)
" es_CL " , // Spanish (Chile)
" es_CO " , // Spanish (Colombia)
" es_CR " , // Spanish (Costa Rica)
" es_CU " , // Spanish (Cuba)
" es_DO " , // Spanish (Dominican Republic)
" es_EC " , // Spanish (Ecuador)
" es_ES " , // Spanish (Spain)
" es_GT " , // Spanish (Guatemala)
" es_HN " , // Spanish (Honduras)
" es_MX " , // Spanish (Mexico)
" es_NI " , // Spanish (Nicaragua)
" es_PA " , // Spanish (Panama)
" es_PE " , // Spanish (Peru)
" es_PR " , // Spanish (Puerto Rico)
" es_PY " , // Spanish (Paraguay)
" es_SV " , // Spanish (El Salvador)
" es_US " , // Spanish (United States)
" es_UY " , // Spanish (Uruguay)
" es_VE " , // Spanish (Venezuela)
" et " , // Estonian
" et_EE " , // Estonian (Estonia)
" eu " , // Basque
" eu_ES " , // Basque (Spain)
" fa " , // Persian
" fa_IR " , // Persian (Iran)
" ff_SN " , // Fulah (Senegal)
" fi " , // Finnish
" fi_FI " , // Finnish (Finland)
2019-05-02 09:22:35 +02:00
" fil " , // Filipino
2017-03-05 16:44:50 +01:00
" fil_PH " , // Filipino (Philippines)
" fo_FO " , // Faroese (Faroe Islands)
" fr " , // French
" fr_BE " , // French (Belgium)
" fr_CA " , // French (Canada)
" fr_CH " , // French (Switzerland)
" fr_FR " , // French (France)
" fr_LU " , // French (Luxembourg)
" fur_IT " , // Friulian (Italy)
" fy_DE " , // Western Frisian (Germany)
" fy_NL " , // Western Frisian (Netherlands)
" ga " , // Irish
" ga_IE " , // Irish (Ireland)
" gd_GB " , // Scottish Gaelic (United Kingdom)
" gez_ER " , // Geez (Eritrea)
" gez_ET " , // Geez (Ethiopia)
" gl_ES " , // Galician (Spain)
" gu_IN " , // Gujarati (India)
" gv_GB " , // Manx (United Kingdom)
" hak_TW " , // Hakka Chinese (Taiwan)
" ha_NG " , // Hausa (Nigeria)
" he " , // Hebrew
" he_IL " , // Hebrew (Israel)
" hi " , // Hindi
" hi_IN " , // Hindi (India)
" hne_IN " , // Chhattisgarhi (India)
" hr " , // Croatian
" hr_HR " , // Croatian (Croatia)
" hsb_DE " , // Upper Sorbian (Germany)
" ht_HT " , // Haitian (Haiti)
" hu " , // Hungarian
" hu_HU " , // Hungarian (Hungary)
" hus_MX " , // Huastec (Mexico)
" hy_AM " , // Armenian (Armenia)
" ia_FR " , // Interlingua (France)
" id " , // Indonesian
" id_ID " , // Indonesian (Indonesia)
" ig_NG " , // Igbo (Nigeria)
" ik_CA " , // Inupiaq (Canada)
" is " , // Icelandic
" is_IS " , // Icelandic (Iceland)
" it " , // Italian
" it_CH " , // Italian (Switzerland)
" it_IT " , // Italian (Italy)
" iu_CA " , // Inuktitut (Canada)
" ja " , // Japanese
" ja_JP " , // Japanese (Japan)
" kab_DZ " , // Kabyle (Algeria)
2019-05-02 09:22:35 +02:00
" ka " , // Georgian
2017-03-05 16:44:50 +01:00
" ka_GE " , // Georgian (Georgia)
" kk_KZ " , // Kazakh (Kazakhstan)
" kl_GL " , // Kalaallisut (Greenland)
" km_KH " , // Central Khmer (Cambodia)
" kn_IN " , // Kannada (India)
" kok_IN " , // Konkani (India)
" ko " , // Korean
" ko_KR " , // Korean (South Korea)
" ks_IN " , // Kashmiri (India)
" ku " , // Kurdish
" ku_TR " , // Kurdish (Turkey)
" kw_GB " , // Cornish (United Kingdom)
" ky_KG " , // Kirghiz (Kyrgyzstan)
" lb_LU " , // Luxembourgish (Luxembourg)
" lg_UG " , // Ganda (Uganda)
" li_BE " , // Limburgan (Belgium)
" li_NL " , // Limburgan (Netherlands)
" lij_IT " , // Ligurian (Italy)
" ln_CD " , // Lingala (Congo)
" lo_LA " , // Lao (Laos)
" lt " , // Lithuanian
" lt_LT " , // Lithuanian (Lithuania)
" lv " , // Latvian
" lv_LV " , // Latvian (Latvia)
" lzh_TW " , // Literary Chinese (Taiwan)
" mag_IN " , // Magahi (India)
" mai_IN " , // Maithili (India)
" mg_MG " , // Malagasy (Madagascar)
" mh_MH " , // Marshallese (Marshall Islands)
" mhr_RU " , // Eastern Mari (Russia)
2019-05-02 09:22:35 +02:00
" mi " , // Māori
" mi_NZ " , // Māori (New Zealand)
2017-03-05 16:44:50 +01:00
" miq_NI " , // Mískito (Nicaragua)
" mk " , // Macedonian
" mk_MK " , // Macedonian (Macedonia)
2019-05-02 09:22:35 +02:00
" ml " , // Malayalam
2017-03-05 16:44:50 +01:00
" ml_IN " , // Malayalam (India)
" mni_IN " , // Manipuri (India)
" mn_MN " , // Mongolian (Mongolia)
" mr_IN " , // Marathi (India)
" ms " , // Malay
" ms_MY " , // Malay (Malaysia)
" mt " , // Maltese
" mt_MT " , // Maltese (Malta)
" my_MM " , // Burmese (Myanmar)
" myv_RU " , // Erzya (Russia)
" nah_MX " , // Nahuatl languages (Mexico)
" nan_TW " , // Min Nan Chinese (Taiwan)
" nb " , // Norwegian Bokmål
" nb_NO " , // Norwegian Bokmål (Norway)
" nds_DE " , // Low German (Germany)
" nds_NL " , // Low German (Netherlands)
" ne_NP " , // Nepali (Nepal)
" nhn_MX " , // Central Nahuatl (Mexico)
" niu_NU " , // Niuean (Niue)
" niu_NZ " , // Niuean (New Zealand)
" nl " , // Dutch
" nl_AW " , // Dutch (Aruba)
" nl_BE " , // Dutch (Belgium)
" nl_NL " , // Dutch (Netherlands)
" nn " , // Norwegian Nynorsk
" nn_NO " , // Norwegian Nynorsk (Norway)
" nr_ZA " , // South Ndebele (South Africa)
" nso_ZA " , // Pedi (South Africa)
" oc_FR " , // Occitan (France)
" om " , // Oromo
" om_ET " , // Oromo (Ethiopia)
" om_KE " , // Oromo (Kenya)
" or_IN " , // Oriya (India)
" os_RU " , // Ossetian (Russia)
" pa_IN " , // Panjabi (India)
" pap " , // Papiamento
" pap_AN " , // Papiamento (Netherlands Antilles)
" pap_AW " , // Papiamento (Aruba)
" pap_CW " , // Papiamento (Curaçao)
" pa_PK " , // Panjabi (Pakistan)
" pl " , // Polish
" pl_PL " , // Polish (Poland)
" pr " , // Pirate
" ps_AF " , // Pushto (Afghanistan)
" pt " , // Portuguese
" pt_BR " , // Portuguese (Brazil)
" pt_PT " , // Portuguese (Portugal)
" quy_PE " , // Ayacucho Quechua (Peru)
" quz_PE " , // Cusco Quechua (Peru)
" raj_IN " , // Rajasthani (India)
" ro " , // Romanian
" ro_RO " , // Romanian (Romania)
" ru " , // Russian
" ru_RU " , // Russian (Russia)
" ru_UA " , // Russian (Ukraine)
" rw_RW " , // Kinyarwanda (Rwanda)
" sa_IN " , // Sanskrit (India)
" sat_IN " , // Santali (India)
" sc_IT " , // Sardinian (Italy)
" sco " , // Scots
" sd_IN " , // Sindhi (India)
" se_NO " , // Northern Sami (Norway)
" sgs_LT " , // Samogitian (Lithuania)
" shs_CA " , // Shuswap (Canada)
" sid_ET " , // Sidamo (Ethiopia)
2019-05-02 09:22:35 +02:00
" si " , // Sinhala
2017-03-05 16:44:50 +01:00
" si_LK " , // Sinhala (Sri Lanka)
" sk " , // Slovak
" sk_SK " , // Slovak (Slovakia)
" sl " , // Slovenian
" sl_SI " , // Slovenian (Slovenia)
" so " , // Somali
" so_DJ " , // Somali (Djibouti)
" so_ET " , // Somali (Ethiopia)
" so_KE " , // Somali (Kenya)
" so_SO " , // Somali (Somalia)
" son_ML " , // Songhai languages (Mali)
" sq " , // Albanian
" sq_AL " , // Albanian (Albania)
" sq_KV " , // Albanian (Kosovo)
" sq_MK " , // Albanian (Macedonia)
" sr " , // Serbian
2017-11-22 13:19:01 +01:00
" sr_Cyrl " , // Serbian (Cyrillic)
2019-05-02 09:22:35 +02:00
" sr_Latn " , // Serbian (Latin)
2017-03-05 16:44:50 +01:00
" sr_ME " , // Serbian (Montenegro)
" sr_RS " , // Serbian (Serbia)
" ss_ZA " , // Swati (South Africa)
" st_ZA " , // Southern Sotho (South Africa)
" sv " , // Swedish
" sv_FI " , // Swedish (Finland)
" sv_SE " , // Swedish (Sweden)
" sw_KE " , // Swahili (Kenya)
" sw_TZ " , // Swahili (Tanzania)
" szl_PL " , // Silesian (Poland)
" ta " , // Tamil
" ta_IN " , // Tamil (India)
" ta_LK " , // Tamil (Sri Lanka)
" tcy_IN " , // Tulu (India)
2019-05-02 09:22:35 +02:00
" te " , // Telugu
2017-03-05 16:44:50 +01:00
" te_IN " , // Telugu (India)
" tg_TJ " , // Tajik (Tajikistan)
" the_NP " , // Chitwania Tharu (Nepal)
" th " , // Thai
" th_TH " , // Thai (Thailand)
" ti " , // Tigrinya
" ti_ER " , // Tigrinya (Eritrea)
" ti_ET " , // Tigrinya (Ethiopia)
" tig_ER " , // Tigre (Eritrea)
" tk_TM " , // Turkmen (Turkmenistan)
" tl_PH " , // Tagalog (Philippines)
" tn_ZA " , // Tswana (South Africa)
" tr " , // Turkish
" tr_CY " , // Turkish (Cyprus)
" tr_TR " , // Turkish (Turkey)
" ts_ZA " , // Tsonga (South Africa)
" tt_RU " , // Tatar (Russia)
" ug_CN " , // Uighur (China)
" uk " , // Ukrainian
" uk_UA " , // Ukrainian (Ukraine)
" unm_US " , // Unami (United States)
" ur " , // Urdu
" ur_IN " , // Urdu (India)
" ur_PK " , // Urdu (Pakistan)
" uz " , // Uzbek
" uz_UZ " , // Uzbek (Uzbekistan)
" ve_ZA " , // Venda (South Africa)
" vi " , // Vietnamese
" vi_VN " , // Vietnamese (Vietnam)
" wa_BE " , // Walloon (Belgium)
" wae_CH " , // Walser (Switzerland)
" wal_ET " , // Wolaytta (Ethiopia)
" wo_SN " , // Wolof (Senegal)
" xh_ZA " , // Xhosa (South Africa)
" yi_US " , // Yiddish (United States)
" yo_NG " , // Yoruba (Nigeria)
" yue_HK " , // Yue Chinese (Hong Kong)
" zh " , // Chinese
" zh_CN " , // Chinese (China)
" zh_HK " , // Chinese (Hong Kong)
" zh_SG " , // Chinese (Singapore)
" zh_TW " , // Chinese (Taiwan)
" zu_ZA " , // Zulu (South Africa)
2020-04-02 01:20:12 +02:00
nullptr
2014-02-10 02:10:30 +01:00
} ;
2017-03-05 16:44:50 +01:00
static const char * locale_names [ ] = {
" Afar " ,
" Afar (Djibouti) " ,
" Afar (Eritrea) " ,
" Afar (Ethiopia) " ,
" Afrikaans " ,
" Afrikaans (South Africa) " ,
" Aguaruna (Peru) " ,
" Akan (Ghana) " ,
" Amharic (Ethiopia) " ,
" Aragonese (Spain) " ,
" Angika (India) " ,
" Arabic " ,
" Arabic (United Arab Emirates) " ,
" Arabic (Bahrain) " ,
" Arabic (Algeria) " ,
" Arabic (Egypt) " ,
" Arabic (India) " ,
" Arabic (Iraq) " ,
" Arabic (Jordan) " ,
" Arabic (Kuwait) " ,
" Arabic (Lebanon) " ,
" Arabic (Libya) " ,
" Arabic (Morocco) " ,
" Arabic (Oman) " ,
" Arabic (Qatar) " ,
" Arabic (Saudi Arabia) " ,
" Arabic (Sudan) " ,
" Arabic (South Soudan) " ,
" Arabic (Syria) " ,
" Arabic (Tunisia) " ,
" Arabic (Yemen) " ,
" Assamese (India) " ,
" Asturian (Spain) " ,
" Southern Aymara (Peru) " ,
" Aymara (Peru) " ,
" Azerbaijani (Azerbaijan) " ,
" Belarusian " ,
" Belarusian (Belarus) " ,
" Bemba (Zambia) " ,
" Berber languages (Algeria) " ,
" Berber languages (Morocco) " ,
" Bulgarian " ,
" Bulgarian (Bulgaria) " ,
" Bhili (India) " ,
" Bhojpuri (India) " ,
" Bislama (Tuvalu) " ,
" Bengali " ,
" Bengali (Bangladesh) " ,
" Bengali (India) " ,
" Tibetan " ,
" Tibetan (China) " ,
" Tibetan (India) " ,
" Breton (France) " ,
" Bodo (India) " ,
" Bosnian (Bosnia and Herzegovina) " ,
" Bilin (Eritrea) " ,
" Catalan " ,
" Catalan (Andorra) " ,
" Catalan (Spain) " ,
" Catalan (France) " ,
" Catalan (Italy) " ,
" Chechen (Russia) " ,
" Cherokee (United States) " ,
" Mandarin Chinese (Taiwan) " ,
" Crimean Tatar (Ukraine) " ,
" Kashubian (Poland) " ,
" Czech " ,
" Czech (Czech Republic) " ,
" Chuvash (Russia) " ,
" Welsh (United Kingdom) " ,
" Danish " ,
" Danish (Denmark) " ,
" German " ,
" German (Austria) " ,
" German (Belgium) " ,
" German (Switzerland) " ,
" German (Germany) " ,
" German (Italy) " ,
" German (Luxembourg) " ,
" Dogri (India) " ,
" Dhivehi (Maldives) " ,
" Dzongkha (Bhutan) " ,
" Greek " ,
" Greek (Cyprus) " ,
" Greek (Greece) " ,
" English " ,
" English (Antigua and Barbuda) " ,
" English (Australia) " ,
" English (Botswana) " ,
" English (Canada) " ,
" English (Denmark) " ,
" English (United Kingdom) " ,
" English (Hong Kong) " ,
" English (Ireland) " ,
" English (Israel) " ,
" English (India) " ,
" English (Nigeria) " ,
" English (New Zealand) " ,
" English (Philippines) " ,
" English (Singapore) " ,
" English (United States) " ,
" English (South Africa) " ,
" English (Zambia) " ,
" English (Zimbabwe) " ,
" Esperanto " ,
" Spanish " ,
" Spanish (Argentina) " ,
" Spanish (Bolivia) " ,
" Spanish (Chile) " ,
" Spanish (Colombia) " ,
" Spanish (Costa Rica) " ,
" Spanish (Cuba) " ,
" Spanish (Dominican Republic) " ,
" Spanish (Ecuador) " ,
" Spanish (Spain) " ,
" Spanish (Guatemala) " ,
" Spanish (Honduras) " ,
" Spanish (Mexico) " ,
" Spanish (Nicaragua) " ,
" Spanish (Panama) " ,
" Spanish (Peru) " ,
" Spanish (Puerto Rico) " ,
" Spanish (Paraguay) " ,
" Spanish (El Salvador) " ,
" Spanish (United States) " ,
" Spanish (Uruguay) " ,
" Spanish (Venezuela) " ,
" Estonian " ,
" Estonian (Estonia) " ,
" Basque " ,
" Basque (Spain) " ,
" Persian " ,
" Persian (Iran) " ,
" Fulah (Senegal) " ,
" Finnish " ,
" Finnish (Finland) " ,
2019-05-02 09:22:35 +02:00
" Filipino " ,
2017-03-05 16:44:50 +01:00
" Filipino (Philippines) " ,
" Faroese (Faroe Islands) " ,
" French " ,
" French (Belgium) " ,
" French (Canada) " ,
" French (Switzerland) " ,
" French (France) " ,
" French (Luxembourg) " ,
" Friulian (Italy) " ,
" Western Frisian (Germany) " ,
" Western Frisian (Netherlands) " ,
" Irish " ,
" Irish (Ireland) " ,
" Scottish Gaelic (United Kingdom) " ,
" Geez (Eritrea) " ,
" Geez (Ethiopia) " ,
" Galician (Spain) " ,
" Gujarati (India) " ,
" Manx (United Kingdom) " ,
" Hakka Chinese (Taiwan) " ,
" Hausa (Nigeria) " ,
" Hebrew " ,
" Hebrew (Israel) " ,
" Hindi " ,
" Hindi (India) " ,
" Chhattisgarhi (India) " ,
" Croatian " ,
" Croatian (Croatia) " ,
" Upper Sorbian (Germany) " ,
" Haitian (Haiti) " ,
" Hungarian " ,
" Hungarian (Hungary) " ,
" Huastec (Mexico) " ,
" Armenian (Armenia) " ,
" Interlingua (France) " ,
" Indonesian " ,
" Indonesian (Indonesia) " ,
" Igbo (Nigeria) " ,
" Inupiaq (Canada) " ,
" Icelandic " ,
" Icelandic (Iceland) " ,
" Italian " ,
" Italian (Switzerland) " ,
" Italian (Italy) " ,
" Inuktitut (Canada) " ,
" Japanese " ,
" Japanese (Japan) " ,
" Kabyle (Algeria) " ,
2019-05-02 09:22:35 +02:00
" Georgian " ,
2017-03-05 16:44:50 +01:00
" Georgian (Georgia) " ,
" Kazakh (Kazakhstan) " ,
" Kalaallisut (Greenland) " ,
" Central Khmer (Cambodia) " ,
" Kannada (India) " ,
" Konkani (India) " ,
" Korean " ,
" Korean (South Korea) " ,
" Kashmiri (India) " ,
" Kurdish " ,
" Kurdish (Turkey) " ,
" Cornish (United Kingdom) " ,
" Kirghiz (Kyrgyzstan) " ,
" Luxembourgish (Luxembourg) " ,
" Ganda (Uganda) " ,
" Limburgan (Belgium) " ,
" Limburgan (Netherlands) " ,
" Ligurian (Italy) " ,
" Lingala (Congo) " ,
" Lao (Laos) " ,
" Lithuanian " ,
" Lithuanian (Lithuania) " ,
" Latvian " ,
" Latvian (Latvia) " ,
" Literary Chinese (Taiwan) " ,
" Magahi (India) " ,
" Maithili (India) " ,
" Malagasy (Madagascar) " ,
" Marshallese (Marshall Islands) " ,
" Eastern Mari (Russia) " ,
2019-05-02 09:22:35 +02:00
" Māori " ,
" Māori (New Zealand) " ,
2017-03-05 16:44:50 +01:00
" Mískito (Nicaragua) " ,
" Macedonian " ,
" Macedonian (Macedonia) " ,
2019-05-02 09:22:35 +02:00
" Malayalam " ,
2017-03-05 16:44:50 +01:00
" Malayalam (India) " ,
" Manipuri (India) " ,
" Mongolian (Mongolia) " ,
" Marathi (India) " ,
" Malay " ,
" Malay (Malaysia) " ,
" Maltese " ,
" Maltese (Malta) " ,
" Burmese (Myanmar) " ,
" Erzya (Russia) " ,
" Nahuatl languages (Mexico) " ,
" Min Nan Chinese (Taiwan) " ,
" Norwegian Bokmål " ,
" Norwegian Bokmål (Norway) " ,
" Low German (Germany) " ,
" Low German (Netherlands) " ,
" Nepali (Nepal) " ,
" Central Nahuatl (Mexico) " ,
" Niuean (Niue) " ,
" Niuean (New Zealand) " ,
" Dutch " ,
" Dutch (Aruba) " ,
" Dutch (Belgium) " ,
" Dutch (Netherlands) " ,
" Norwegian Nynorsk " ,
" Norwegian Nynorsk (Norway) " ,
" South Ndebele (South Africa) " ,
" Pedi (South Africa) " ,
" Occitan (France) " ,
" Oromo " ,
" Oromo (Ethiopia) " ,
" Oromo (Kenya) " ,
" Oriya (India) " ,
" Ossetian (Russia) " ,
" Panjabi (India) " ,
" Papiamento " ,
" Papiamento (Netherlands Antilles) " ,
" Papiamento (Aruba) " ,
" Papiamento (Curaçao) " ,
" Panjabi (Pakistan) " ,
" Polish " ,
" Polish (Poland) " ,
" Pirate " ,
" Pushto (Afghanistan) " ,
" Portuguese " ,
" Portuguese (Brazil) " ,
" Portuguese (Portugal) " ,
" Ayacucho Quechua (Peru) " ,
" Cusco Quechua (Peru) " ,
" Rajasthani (India) " ,
" Romanian " ,
" Romanian (Romania) " ,
" Russian " ,
" Russian (Russia) " ,
" Russian (Ukraine) " ,
" Kinyarwanda (Rwanda) " ,
" Sanskrit (India) " ,
" Santali (India) " ,
" Sardinian (Italy) " ,
" Scots (Scotland) " ,
" Sindhi (India) " ,
" Northern Sami (Norway) " ,
" Samogitian (Lithuania) " ,
" Shuswap (Canada) " ,
" Sidamo (Ethiopia) " ,
2019-05-02 09:22:35 +02:00
" Sinhala " ,
2017-03-05 16:44:50 +01:00
" Sinhala (Sri Lanka) " ,
" Slovak " ,
" Slovak (Slovakia) " ,
" Slovenian " ,
" Slovenian (Slovenia) " ,
" Somali " ,
" Somali (Djibouti) " ,
" Somali (Ethiopia) " ,
" Somali (Kenya) " ,
" Somali (Somalia) " ,
" Songhai languages (Mali) " ,
" Albanian " ,
" Albanian (Albania) " ,
" Albanian (Kosovo) " ,
" Albanian (Macedonia) " ,
" Serbian " ,
2017-11-22 13:19:01 +01:00
" Serbian (Cyrillic) " ,
2019-05-02 09:22:35 +02:00
" Serbian (Latin) " ,
2017-03-05 16:44:50 +01:00
" Serbian (Montenegro) " ,
" Serbian (Serbia) " ,
" Swati (South Africa) " ,
" Southern Sotho (South Africa) " ,
" Swedish " ,
" Swedish (Finland) " ,
" Swedish (Sweden) " ,
" Swahili (Kenya) " ,
" Swahili (Tanzania) " ,
" Silesian (Poland) " ,
" Tamil " ,
" Tamil (India) " ,
" Tamil (Sri Lanka) " ,
" Tulu (India) " ,
2019-05-02 09:22:35 +02:00
" Telugu " ,
2017-03-05 16:44:50 +01:00
" Telugu (India) " ,
" Tajik (Tajikistan) " ,
" Chitwania Tharu (Nepal) " ,
" Thai " ,
" Thai (Thailand) " ,
" Tigrinya " ,
" Tigrinya (Eritrea) " ,
" Tigrinya (Ethiopia) " ,
" Tigre (Eritrea) " ,
" Turkmen (Turkmenistan) " ,
" Tagalog (Philippines) " ,
" Tswana (South Africa) " ,
" Turkish " ,
" Turkish (Cyprus) " ,
" Turkish (Turkey) " ,
" Tsonga (South Africa) " ,
" Tatar (Russia) " ,
" Uighur (China) " ,
" Ukrainian " ,
" Ukrainian (Ukraine) " ,
" Unami (United States) " ,
" Urdu " ,
" Urdu (India) " ,
" Urdu (Pakistan) " ,
" Uzbek " ,
" Uzbek (Uzbekistan) " ,
" Venda (South Africa) " ,
" Vietnamese " ,
" Vietnamese (Vietnam) " ,
" Walloon (Belgium) " ,
" Walser (Switzerland) " ,
" Wolaytta (Ethiopia) " ,
" Wolof (Senegal) " ,
" Xhosa (South Africa) " ,
" Yiddish (United States) " ,
" Yoruba (Nigeria) " ,
" Yue Chinese (Hong Kong) " ,
" Chinese " ,
" Chinese (China) " ,
" Chinese (Hong Kong) " ,
" Chinese (Singapore) " ,
" Chinese (Taiwan) " ,
" Zulu (South Africa) " ,
2020-04-02 01:20:12 +02:00
nullptr
2014-02-10 02:10:30 +01:00
} ;
2018-02-14 18:49:27 +01:00
// Windows has some weird locale identifiers which do not honor the ISO 639-1
// standardized nomenclature. Whenever those don't conflict with existing ISO
// identifiers, we override them.
//
// Reference:
// - https://msdn.microsoft.com/en-us/library/windows/desktop/ms693062(v=vs.85).aspx
2017-10-29 22:26:09 +01:00
static const char * locale_renames [ ] [ 2 ] = {
2018-02-14 18:49:27 +01:00
{ " in " , " id " } , // Indonesian
{ " iw " , " he " } , // Hebrew
{ " no " , " nb " } , // Norwegian Bokmål
2020-04-02 01:20:12 +02:00
{ nullptr , nullptr }
2017-10-29 22:26:09 +01:00
} ;
2014-02-10 02:10:30 +01:00
2017-10-29 22:26:09 +01:00
///////////////////////////////////////////////
2014-02-10 02:10:30 +01:00
2020-07-16 10:52:06 +02:00
Dictionary Translation : : _get_messages ( ) const {
Dictionary d ;
2020-08-07 13:17:12 +02:00
for ( const Map < StringName , StringName > : : Element * E = translation_map . front ( ) ; E ; E = E - > next ( ) ) {
d [ E - > key ( ) ] = E - > value ( ) ;
2014-02-10 02:10:30 +01:00
}
2020-07-16 10:52:06 +02:00
return d ;
}
2020-02-17 22:06:54 +01:00
Vector < String > Translation : : _get_message_list ( ) const {
2020-08-07 13:17:12 +02:00
Vector < String > msgs ;
msgs . resize ( translation_map . size ( ) ) ;
int idx = 0 ;
for ( const Map < StringName , StringName > : : Element * E = translation_map . front ( ) ; E ; E = E - > next ( ) ) {
msgs . set ( idx , E - > key ( ) ) ;
idx + = 1 ;
2014-02-10 02:10:30 +01:00
}
2020-08-07 13:17:12 +02:00
return msgs ;
2020-07-16 10:52:06 +02:00
}
2014-02-10 02:10:30 +01:00
2020-08-07 13:17:12 +02:00
void Translation : : _set_messages ( const Dictionary & p_messages ) {
List < Variant > keys ;
p_messages . get_key_list ( & keys ) ;
for ( auto E = keys . front ( ) ; E ; E = E - > next ( ) ) {
translation_map [ E - > get ( ) ] = p_messages [ E - > get ( ) ] ;
2020-07-16 10:52:06 +02:00
}
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void Translation : : set_locale ( const String & p_locale ) {
2017-10-29 22:26:09 +01:00
String univ_locale = TranslationServer : : standardize_locale ( p_locale ) ;
2017-03-05 16:44:50 +01:00
2017-10-29 22:26:09 +01:00
if ( ! TranslationServer : : is_locale_valid ( univ_locale ) ) {
2019-12-04 16:47:30 +01:00
String trimmed_locale = TranslationServer : : get_language_code ( univ_locale ) ;
2017-03-05 16:44:50 +01:00
2019-08-15 04:57:49 +02:00
ERR_FAIL_COND_MSG ( ! TranslationServer : : is_locale_valid ( trimmed_locale ) , " Invalid locale: " + trimmed_locale + " . " ) ;
2017-03-05 16:44:50 +01:00
locale = trimmed_locale ;
} else {
locale = univ_locale ;
2016-10-27 10:36:32 +02:00
}
2017-06-28 22:00:18 +02:00
if ( OS : : get_singleton ( ) - > get_main_loop ( ) ) {
OS : : get_singleton ( ) - > get_main_loop ( ) - > notification ( MainLoop : : NOTIFICATION_TRANSLATION_CHANGED ) ;
}
2014-02-10 02:10:30 +01:00
}
2020-07-16 10:52:06 +02:00
void Translation : : add_message ( const StringName & p_src_text , const StringName & p_xlated_text , const StringName & p_context ) {
2020-08-07 13:17:12 +02:00
translation_map [ p_src_text ] = p_xlated_text ;
2020-07-16 10:52:06 +02:00
}
2020-08-07 13:17:12 +02:00
void Translation : : add_plural_message ( const StringName & p_src_text , const Vector < String > & p_plural_xlated_texts , const StringName & p_context ) {
WARN_PRINT ( " Translation class doesn't handle plural messages. Calling add_plural_message() on a Translation instance is probably a mistake. \n Use a derived Translation class that handles plurals, such as TranslationPO class " ) ;
ERR_FAIL_COND_MSG ( p_plural_xlated_texts . empty ( ) , " Parameter vector p_plural_xlated_texts passed in is empty. " ) ;
translation_map [ p_src_text ] = p_plural_xlated_texts [ 0 ] ;
2020-07-16 10:52:06 +02:00
}
StringName Translation : : get_message ( const StringName & p_src_text , const StringName & p_context ) const {
2020-08-07 13:17:12 +02:00
if ( p_context ! = StringName ( ) ) {
WARN_PRINT ( " Translation class doesn't handle context. Using context in get_message() on a Translation instance is probably a mistake. \n Use a derived Translation class that handles context, such as TranslationPO class " ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2020-08-07 13:17:12 +02:00
const Map < StringName , StringName > : : Element * E = translation_map . find ( p_src_text ) ;
if ( ! E ) {
2020-07-16 10:52:06 +02:00
return StringName ( ) ;
}
2020-08-07 13:17:12 +02:00
return E - > get ( ) ;
}
2020-07-16 10:52:06 +02:00
2020-08-07 13:17:12 +02:00
StringName Translation : : get_plural_message ( const StringName & p_src_text , const StringName & p_plural_text , int p_n , const StringName & p_context ) const {
WARN_PRINT ( " Translation class doesn't handle plural messages. Calling get_plural_message() on a Translation instance is probably a mistake. \n Use a derived Translation class that handles plurals, such as TranslationPO class " ) ;
return get_message ( p_src_text ) ;
2020-07-16 10:52:06 +02:00
}
void Translation : : erase_message ( const StringName & p_src_text , const StringName & p_context ) {
2020-08-07 13:17:12 +02:00
if ( p_context ! = StringName ( ) ) {
WARN_PRINT ( " Translation class doesn't handle context. Using context in erase_message() on a Translation instance is probably a mistake. \n Use a derived Translation class that handles context, such as TranslationPO class " ) ;
2020-07-16 10:52:06 +02:00
}
2020-08-07 13:17:12 +02:00
translation_map . erase ( p_src_text ) ;
2014-02-10 02:10:30 +01:00
}
void Translation : : get_message_list ( List < StringName > * r_messages ) const {
2020-08-07 13:17:12 +02:00
for ( const Map < StringName , StringName > : : Element * E = translation_map . front ( ) ; E ; E = E - > next ( ) ) {
r_messages - > push_back ( E - > key ( ) ) ;
2014-02-10 02:10:30 +01:00
}
}
2014-08-02 03:10:38 +02:00
int Translation : : get_message_count ( ) const {
2020-08-07 13:17:12 +02:00
return translation_map . size ( ) ;
2020-05-19 15:46:49 +02:00
}
2014-08-02 03:10:38 +02:00
2014-02-10 02:10:30 +01:00
void Translation : : _bind_methods ( ) {
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_locale " , " locale " ) , & Translation : : set_locale ) ;
ClassDB : : bind_method ( D_METHOD ( " get_locale " ) , & Translation : : get_locale ) ;
2020-07-16 10:52:06 +02:00
ClassDB : : bind_method ( D_METHOD ( " add_message " , " src_message " , " xlated_message " , " context " ) , & Translation : : add_message , DEFVAL ( " " ) ) ;
ClassDB : : bind_method ( D_METHOD ( " add_plural_message " , " src_message " , " xlated_messages " , " context " ) , & Translation : : add_plural_message , DEFVAL ( " " ) ) ;
ClassDB : : bind_method ( D_METHOD ( " get_message " , " src_message " , " context " ) , & Translation : : get_message , DEFVAL ( " " ) ) ;
ClassDB : : bind_method ( D_METHOD ( " get_plural_message " , " src_message " , " src_plural_message " , " n " , " context " ) , & Translation : : get_plural_message , DEFVAL ( " " ) ) ;
ClassDB : : bind_method ( D_METHOD ( " erase_message " , " src_message " , " context " ) , & Translation : : erase_message , DEFVAL ( " " ) ) ;
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " get_message_list " ) , & Translation : : _get_message_list ) ;
ClassDB : : bind_method ( D_METHOD ( " get_message_count " ) , & Translation : : get_message_count ) ;
ClassDB : : bind_method ( D_METHOD ( " _set_messages " ) , & Translation : : _set_messages ) ;
ClassDB : : bind_method ( D_METHOD ( " _get_messages " ) , & Translation : : _get_messages ) ;
2020-07-16 10:52:06 +02:00
ADD_PROPERTY ( PropertyInfo ( Variant : : DICTIONARY , " messages " , PROPERTY_HINT_NONE , " " , PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL ) , " _set_messages " , " _get_messages " ) ;
2017-03-05 16:44:50 +01:00
ADD_PROPERTY ( PropertyInfo ( Variant : : STRING , " locale " ) , " set_locale " , " get_locale " ) ;
2014-02-10 02:10:30 +01:00
}
///////////////////////////////////////////////
2017-10-29 22:26:09 +01:00
bool TranslationServer : : is_locale_valid ( const String & p_locale ) {
const char * * ptr = locale_list ;
while ( * ptr ) {
2020-05-14 16:41:43 +02:00
if ( * ptr = = p_locale ) {
2017-10-29 22:26:09 +01:00
return true ;
2020-05-14 16:41:43 +02:00
}
2017-10-29 22:26:09 +01:00
ptr + + ;
}
return false ;
}
2014-02-10 02:10:30 +01:00
2017-10-29 22:26:09 +01:00
String TranslationServer : : standardize_locale ( const String & p_locale ) {
// Replaces '-' with '_' for macOS Sierra-style locales
2016-10-27 14:29:00 +02:00
String univ_locale = p_locale . replace ( " - " , " _ " ) ;
2017-03-05 16:44:50 +01:00
2017-10-29 22:26:09 +01:00
// Handles known non-ISO locale names used e.g. on Windows
int idx = 0 ;
2020-04-02 01:20:12 +02:00
while ( locale_renames [ idx ] [ 0 ] ! = nullptr ) {
2017-10-29 22:26:09 +01:00
if ( locale_renames [ idx ] [ 0 ] = = univ_locale ) {
univ_locale = locale_renames [ idx ] [ 1 ] ;
break ;
}
idx + + ;
}
return univ_locale ;
}
2019-12-04 16:47:30 +01:00
String TranslationServer : : get_language_code ( const String & p_locale ) {
ERR_FAIL_COND_V_MSG ( p_locale . length ( ) < 2 , p_locale , " Invalid locale ' " + p_locale + " '. " ) ;
// Most language codes are two letters, but some are three,
// so we have to look for a regional code separator ('_' or '-')
// to extract the left part.
// For example we get 'nah_MX' as input and should return 'nah'.
int split = p_locale . find ( " _ " ) ;
if ( split = = - 1 ) {
split = p_locale . find ( " - " ) ;
}
if ( split = = - 1 ) { // No separator, so the locale is already only a language code.
return p_locale ;
}
return p_locale . left ( split ) ;
}
2017-10-29 22:26:09 +01:00
void TranslationServer : : set_locale ( const String & p_locale ) {
String univ_locale = standardize_locale ( p_locale ) ;
if ( ! is_locale_valid ( univ_locale ) ) {
2019-12-04 16:47:30 +01:00
String trimmed_locale = get_language_code ( univ_locale ) ;
2018-09-23 13:58:01 +02:00
print_verbose ( vformat ( " Unsupported locale '%s', falling back to '%s'. " , p_locale , trimmed_locale ) ) ;
2017-03-05 16:44:50 +01:00
2018-09-23 13:58:01 +02:00
if ( ! is_locale_valid ( trimmed_locale ) ) {
2019-11-06 17:03:04 +01:00
ERR_PRINT ( vformat ( " Unsupported locale '%s', falling back to 'en'. " , trimmed_locale ) ) ;
2018-09-23 13:58:01 +02:00
locale = " en " ;
} else {
locale = trimmed_locale ;
}
2017-03-05 16:44:50 +01:00
} else {
locale = univ_locale ;
2016-10-27 10:36:32 +02:00
}
2017-01-09 20:43:44 +01:00
if ( OS : : get_singleton ( ) - > get_main_loop ( ) ) {
OS : : get_singleton ( ) - > get_main_loop ( ) - > notification ( MainLoop : : NOTIFICATION_TRANSLATION_CHANGED ) ;
}
2017-06-28 22:00:18 +02:00
ResourceLoader : : reload_translation_remaps ( ) ;
2014-02-10 02:10:30 +01:00
}
String TranslationServer : : get_locale ( ) const {
return locale ;
}
2017-10-25 07:29:20 +02:00
String TranslationServer : : get_locale_name ( const String & p_locale ) const {
2020-05-14 16:41:43 +02:00
if ( ! locale_name_map . has ( p_locale ) ) {
2020-05-10 12:56:01 +02:00
return String ( ) ;
2020-05-14 16:41:43 +02:00
}
2017-10-25 07:29:20 +02:00
return locale_name_map [ p_locale ] ;
}
2019-04-25 03:39:29 +02:00
Array TranslationServer : : get_loaded_locales ( ) const {
Array locales ;
2020-03-17 07:33:00 +01:00
for ( const Set < Ref < Translation > > : : Element * E = translations . front ( ) ; E ; E = E - > next ( ) ) {
2019-04-25 03:39:29 +02:00
const Ref < Translation > & t = E - > get ( ) ;
2019-10-28 08:07:29 +01:00
ERR_FAIL_COND_V ( t . is_null ( ) , Array ( ) ) ;
2019-04-25 03:39:29 +02:00
String l = t - > get_locale ( ) ;
locales . push_back ( l ) ;
}
return locales ;
}
2017-10-29 22:26:09 +01:00
Vector < String > TranslationServer : : get_all_locales ( ) {
Vector < String > locales ;
const char * * ptr = locale_list ;
while ( * ptr ) {
locales . push_back ( * ptr ) ;
ptr + + ;
}
return locales ;
}
Vector < String > TranslationServer : : get_all_locale_names ( ) {
Vector < String > locales ;
const char * * ptr = locale_names ;
while ( * ptr ) {
2017-11-07 21:55:14 +01:00
locales . push_back ( String : : utf8 ( * ptr ) ) ;
2017-10-29 22:26:09 +01:00
ptr + + ;
}
return locales ;
}
2014-02-10 02:10:30 +01:00
void TranslationServer : : add_translation ( const Ref < Translation > & p_translation ) {
translations . insert ( p_translation ) ;
}
2020-05-14 14:29:06 +02:00
2014-02-10 02:10:30 +01:00
void TranslationServer : : remove_translation ( const Ref < Translation > & p_translation ) {
translations . erase ( p_translation ) ;
}
2020-08-07 13:17:12 +02:00
Ref < Translation > TranslationServer : : get_translation_object ( const String & p_locale ) {
Ref < Translation > res ;
String lang = get_language_code ( p_locale ) ;
bool near_match_found = false ;
for ( const Set < Ref < Translation > > : : Element * E = translations . front ( ) ; E ; E = E - > next ( ) ) {
const Ref < Translation > & t = E - > get ( ) ;
ERR_FAIL_COND_V ( t . is_null ( ) , nullptr ) ;
String l = t - > get_locale ( ) ;
// Exact match.
if ( l = = p_locale ) {
return t ;
}
// If near match found, keep that match, but keep looking to try to look for perfect match.
if ( get_language_code ( l ) = = lang & & ! near_match_found ) {
res = t ;
near_match_found = true ;
}
}
return res ;
}
2014-08-02 03:10:38 +02:00
void TranslationServer : : clear ( ) {
translations . clear ( ) ;
2020-05-19 15:46:49 +02:00
}
2014-08-02 03:10:38 +02:00
2020-07-16 10:52:06 +02:00
StringName TranslationServer : : translate ( const StringName & p_message , const StringName & p_context ) const {
2019-12-04 16:47:30 +01:00
// Match given message against the translation catalog for the project locale.
2014-02-10 02:10:30 +01:00
2020-05-14 16:41:43 +02:00
if ( ! enabled ) {
2014-02-10 02:10:30 +01:00
return p_message ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
2019-12-04 16:47:30 +01:00
ERR_FAIL_COND_V_MSG ( locale . length ( ) < 2 , p_message , " Could not translate message as configured locale ' " + locale + " ' is invalid. " ) ;
2020-08-07 13:17:12 +02:00
StringName res = _get_message_from_translations ( p_message , p_context , locale , false ) ;
2020-07-16 10:52:06 +02:00
if ( ! res & & fallback . length ( ) > = 2 ) {
2020-08-07 13:17:12 +02:00
res = _get_message_from_translations ( p_message , p_context , fallback , false ) ;
2020-07-16 10:52:06 +02:00
}
if ( ! res ) {
return p_message ;
}
return res ;
}
StringName TranslationServer : : translate_plural ( const StringName & p_message , const StringName & p_message_plural , int p_n , const StringName & p_context ) const {
if ( ! enabled ) {
if ( p_n = = 1 ) {
return p_message ;
}
2020-08-07 13:17:12 +02:00
return p_message_plural ;
2020-07-16 10:52:06 +02:00
}
ERR_FAIL_COND_V_MSG ( locale . length ( ) < 2 , p_message , " Could not translate message as configured locale ' " + locale + " ' is invalid. " ) ;
2020-08-07 13:17:12 +02:00
StringName res = _get_message_from_translations ( p_message , p_context , locale , true , p_message_plural , p_n ) ;
2020-07-16 10:52:06 +02:00
if ( ! res & & fallback . length ( ) > = 2 ) {
2020-08-07 13:17:12 +02:00
res = _get_message_from_translations ( p_message , p_context , fallback , true , p_message_plural , p_n ) ;
2020-07-16 10:52:06 +02:00
}
if ( ! res ) {
if ( p_n = = 1 ) {
return p_message ;
}
2020-08-07 13:17:12 +02:00
return p_message_plural ;
2020-07-16 10:52:06 +02:00
}
return res ;
}
2020-08-07 13:17:12 +02:00
StringName TranslationServer : : _get_message_from_translations ( const StringName & p_message , const StringName & p_context , const String & p_locale , bool plural , const String & p_message_plural , int p_n ) const {
2019-08-08 10:15:55 +02:00
// Locale can be of the form 'll_CC', i.e. language code and regional code,
// e.g. 'en_US', 'en_GB', etc. It might also be simply 'll', e.g. 'en'.
// To find the relevant translation, we look for those with locale starting
// with the language code, and then if any is an exact match for the long
// form. If not found, we fall back to a near match (another locale with
// same language code).
2019-12-04 16:50:43 +01:00
// Note: ResourceLoader::_path_remap reproduces this locale near matching
// logic, so be sure to propagate changes there when changing things here.
2014-02-10 02:10:30 +01:00
StringName res ;
2020-07-16 10:52:06 +02:00
String lang = get_language_code ( p_locale ) ;
2017-03-05 16:44:50 +01:00
bool near_match = false ;
2014-02-10 02:10:30 +01:00
2020-03-17 07:33:00 +01:00
for ( const Set < Ref < Translation > > : : Element * E = translations . front ( ) ; E ; E = E - > next ( ) ) {
2017-03-05 16:44:50 +01:00
const Ref < Translation > & t = E - > get ( ) ;
2019-12-04 16:47:30 +01:00
ERR_FAIL_COND_V ( t . is_null ( ) , p_message ) ;
2014-02-10 02:10:30 +01:00
String l = t - > get_locale ( ) ;
2020-07-16 10:52:06 +02:00
bool exact_match = ( l = = p_locale ) ;
2019-12-04 16:47:30 +01:00
if ( ! exact_match ) {
if ( near_match ) {
continue ; // Only near-match once, but keep looking for exact matches.
}
if ( get_language_code ( l ) ! = lang ) {
continue ; // Language code does not match.
}
}
2014-02-10 02:10:30 +01:00
2020-07-16 10:52:06 +02:00
StringName r ;
2020-08-07 13:17:12 +02:00
if ( ! plural ) {
2020-07-16 10:52:06 +02:00
r = t - > get_message ( p_message , p_context ) ;
} else {
r = t - > get_plural_message ( p_message , p_message_plural , p_n , p_context ) ;
}
2019-12-04 16:47:30 +01:00
if ( ! r ) {
2014-02-10 02:10:30 +01:00
continue ;
2019-12-04 16:47:30 +01:00
}
2017-03-05 16:44:50 +01:00
res = r ;
2014-02-10 02:10:30 +01:00
2019-12-04 16:47:30 +01:00
if ( exact_match ) {
2014-02-10 02:10:30 +01:00
break ;
2019-12-04 16:47:30 +01:00
} else {
2017-03-05 16:44:50 +01:00
near_match = true ;
2019-12-04 16:47:30 +01:00
}
2014-02-10 02:10:30 +01:00
}
return res ;
}
2020-04-02 01:20:12 +02:00
TranslationServer * TranslationServer : : singleton = nullptr ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
bool TranslationServer : : _load_translations ( const String & p_from ) {
2017-10-05 20:34:34 +02:00
if ( ProjectSettings : : get_singleton ( ) - > has_setting ( p_from ) ) {
2020-02-17 22:06:54 +01:00
Vector < String > translations = ProjectSettings : : get_singleton ( ) - > get ( p_from ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
int tcount = translations . size ( ) ;
2014-02-10 02:10:30 +01:00
if ( tcount ) {
2020-02-17 22:06:54 +01:00
const String * r = translations . ptr ( ) ;
2014-02-10 02:10:30 +01:00
2017-03-05 16:44:50 +01:00
for ( int i = 0 ; i < tcount ; i + + ) {
2014-02-10 02:10:30 +01:00
Ref < Translation > tr = ResourceLoader : : load ( r [ i ] ) ;
2020-05-14 16:41:43 +02:00
if ( tr . is_valid ( ) ) {
2014-02-10 02:10:30 +01:00
add_translation ( tr ) ;
2020-05-14 16:41:43 +02:00
}
2014-02-10 02:10:30 +01:00
}
}
return true ;
}
return false ;
}
void TranslationServer : : setup ( ) {
2017-03-05 16:44:50 +01:00
String test = GLOBAL_DEF ( " locale/test " , " " ) ;
test = test . strip_edges ( ) ;
2020-05-14 16:41:43 +02:00
if ( test ! = " " ) {
2017-03-05 16:44:50 +01:00
set_locale ( test ) ;
2020-05-14 16:41:43 +02:00
} else {
2017-03-05 16:44:50 +01:00
set_locale ( OS : : get_singleton ( ) - > get_locale ( ) ) ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
fallback = GLOBAL_DEF ( " locale/fallback " , " en " ) ;
2015-01-04 15:03:31 +01:00
# ifdef TOOLS_ENABLED
{
2017-03-05 16:44:50 +01:00
String options = " " ;
int idx = 0 ;
while ( locale_list [ idx ] ) {
2020-05-14 16:41:43 +02:00
if ( idx > 0 ) {
2017-04-25 21:48:03 +02:00
options + = " , " ;
2020-05-14 16:41:43 +02:00
}
2017-03-05 16:44:50 +01:00
options + = locale_list [ idx ] ;
2015-01-04 15:03:31 +01:00
idx + + ;
}
2017-07-19 22:00:46 +02:00
ProjectSettings : : get_singleton ( ) - > set_custom_property_info ( " locale/fallback " , PropertyInfo ( Variant : : STRING , " locale/fallback " , PROPERTY_HINT_ENUM , options ) ) ;
2015-01-04 15:03:31 +01:00
}
# endif
2014-02-10 02:10:30 +01:00
}
2017-03-05 16:44:50 +01:00
void TranslationServer : : set_tool_translation ( const Ref < Translation > & p_translation ) {
tool_translation = p_translation ;
2016-05-04 03:25:37 +02:00
}
2020-07-16 10:52:06 +02:00
StringName TranslationServer : : tool_translate ( const StringName & p_message , const StringName & p_context ) const {
2016-05-04 03:25:37 +02:00
if ( tool_translation . is_valid ( ) ) {
2020-07-16 10:52:06 +02:00
StringName r = tool_translation - > get_message ( p_message , p_context ) ;
2016-05-28 00:58:28 +02:00
if ( r ) {
2016-05-04 03:25:37 +02:00
return r ;
2016-05-28 00:58:28 +02:00
}
2016-05-04 03:25:37 +02:00
}
2020-03-18 18:34:36 +01:00
return p_message ;
}
2016-05-04 03:25:37 +02:00
2020-07-16 10:52:06 +02:00
StringName TranslationServer : : tool_translate_plural ( const StringName & p_message , const StringName & p_message_plural , int p_n , const StringName & p_context ) const {
if ( tool_translation . is_valid ( ) ) {
StringName r = tool_translation - > get_plural_message ( p_message , p_message_plural , p_n , p_context ) ;
if ( r ) {
return r ;
}
}
if ( p_n = = 1 ) {
return p_message ;
}
2020-08-07 13:17:12 +02:00
return p_message_plural ;
2020-07-16 10:52:06 +02:00
}
2020-03-18 18:34:36 +01:00
void TranslationServer : : set_doc_translation ( const Ref < Translation > & p_translation ) {
doc_translation = p_translation ;
}
2020-07-16 10:52:06 +02:00
StringName TranslationServer : : doc_translate ( const StringName & p_message , const StringName & p_context ) const {
2020-03-18 18:34:36 +01:00
if ( doc_translation . is_valid ( ) ) {
2020-07-16 10:52:06 +02:00
StringName r = doc_translation - > get_message ( p_message , p_context ) ;
2020-03-18 18:34:36 +01:00
if ( r ) {
return r ;
}
}
2016-05-04 03:25:37 +02:00
return p_message ;
}
2020-07-16 10:52:06 +02:00
StringName TranslationServer : : doc_translate_plural ( const StringName & p_message , const StringName & p_message_plural , int p_n , const StringName & p_context ) const {
if ( doc_translation . is_valid ( ) ) {
StringName r = doc_translation - > get_plural_message ( p_message , p_message_plural , p_n , p_context ) ;
if ( r ) {
return r ;
}
}
if ( p_n = = 1 ) {
return p_message ;
}
2020-08-07 13:17:12 +02:00
return p_message_plural ;
2020-07-16 10:52:06 +02:00
}
2014-02-10 02:10:30 +01:00
void TranslationServer : : _bind_methods ( ) {
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " set_locale " , " locale " ) , & TranslationServer : : set_locale ) ;
ClassDB : : bind_method ( D_METHOD ( " get_locale " ) , & TranslationServer : : get_locale ) ;
2014-02-10 02:10:30 +01:00
2017-10-25 07:29:20 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_locale_name " , " locale " ) , & TranslationServer : : get_locale_name ) ;
2020-08-07 13:17:12 +02:00
ClassDB : : bind_method ( D_METHOD ( " translate " , " message " , " context " ) , & TranslationServer : : translate , DEFVAL ( " " ) ) ;
2020-07-16 10:52:06 +02:00
ClassDB : : bind_method ( D_METHOD ( " translate_plural " , " message " , " plural_message " , " n " , " context " ) , & TranslationServer : : translate_plural , DEFVAL ( " " ) ) ;
2014-08-02 03:10:38 +02:00
2017-08-09 13:19:41 +02:00
ClassDB : : bind_method ( D_METHOD ( " add_translation " , " translation " ) , & TranslationServer : : add_translation ) ;
ClassDB : : bind_method ( D_METHOD ( " remove_translation " , " translation " ) , & TranslationServer : : remove_translation ) ;
2020-08-07 13:17:12 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_translation_object " , " locale " ) , & TranslationServer : : get_translation_object ) ;
2014-08-02 03:10:38 +02:00
2017-03-05 16:44:50 +01:00
ClassDB : : bind_method ( D_METHOD ( " clear " ) , & TranslationServer : : clear ) ;
2019-04-25 03:39:29 +02:00
ClassDB : : bind_method ( D_METHOD ( " get_loaded_locales " ) , & TranslationServer : : get_loaded_locales ) ;
2014-02-10 02:10:30 +01:00
}
void TranslationServer : : load_translations ( ) {
String locale = get_locale ( ) ;
2018-03-26 01:36:34 +02:00
_load_translations ( " locale/translations " ) ; //all
_load_translations ( " locale/translations_ " + locale . substr ( 0 , 2 ) ) ;
2015-01-04 15:03:31 +01:00
2017-03-05 16:44:50 +01:00
if ( locale . substr ( 0 , 2 ) ! = locale ) {
2018-03-26 01:36:34 +02:00
_load_translations ( " locale/translations_ " + locale ) ;
2014-02-10 02:10:30 +01:00
}
}
2020-05-12 17:01:17 +02:00
TranslationServer : : TranslationServer ( ) {
2017-03-05 16:44:50 +01:00
singleton = this ;
2017-10-25 07:29:20 +02:00
for ( int i = 0 ; locale_list [ i ] ; + + i ) {
2017-11-07 21:55:14 +01:00
locale_name_map . insert ( locale_list [ i ] , String : : utf8 ( locale_names [ i ] ) ) ;
2017-10-25 07:29:20 +02:00
}
2014-02-10 02:10:30 +01:00
}