diff options
Diffstat (limited to 'core/string/translation.cpp')
| -rw-r--r-- | core/string/translation.cpp | 1337 | 
1 files changed, 1337 insertions, 0 deletions
| diff --git a/core/string/translation.cpp b/core/string/translation.cpp new file mode 100644 index 0000000000..9cee218735 --- /dev/null +++ b/core/string/translation.cpp @@ -0,0 +1,1337 @@ +/*************************************************************************/ +/*  translation.cpp                                                      */ +/*************************************************************************/ +/*                       This file is part of:                           */ +/*                           GODOT ENGINE                                */ +/*                      https://godotengine.org                          */ +/*************************************************************************/ +/* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur.                 */ +/* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md).   */ +/*                                                                       */ +/* 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.                */ +/*************************************************************************/ + +#include "translation.h" + +#include "core/config/project_settings.h" +#include "core/io/resource_loader.h" +#include "core/os/os.h" + +#ifdef TOOLS_ENABLED +#include "editor/editor_settings.h" +#include "main/main.h" +#endif + +// ISO 639-1 language codes (and a couple of three-letter ISO 639-2 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/ +// - https://iso639-3.sil.org/ + +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", //  Breton +	"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) +	"fil", //  Filipino +	"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", //  Galician +	"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) +	"ka", //  Georgian +	"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) +	"mi", //  Māori +	"mi_NZ", //  Māori (New Zealand) +	"miq_NI", //  Mískito (Nicaragua) +	"mk", //  Macedonian +	"mk_MK", //  Macedonian (Macedonia) +	"ml", //  Malayalam +	"ml_IN", //  Malayalam (India) +	"mni_IN", //  Manipuri (India) +	"mn_MN", //  Mongolian (Mongolia) +	"mr", //  Marathi +	"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", //  Oriya +	"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) +	"si", //  Sinhala +	"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 +	"sr_Cyrl", //  Serbian (Cyrillic) +	"sr_Latn", //  Serbian (Latin) +	"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) +	"te", //  Telugu +	"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) +	"tzm", // Central Atlas Tamazight +	"tzm_MA", // Central Atlas Tamazight (Marrocos) +	"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) +	nullptr +}; + +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", +	"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)", +	"Filipino", +	"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", +	"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)", +	"Georgian", +	"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)", +	"Māori", +	"Māori (New Zealand)", +	"Mískito (Nicaragua)", +	"Macedonian", +	"Macedonian (Macedonia)", +	"Malayalam", +	"Malayalam (India)", +	"Manipuri (India)", +	"Mongolian (Mongolia)", +	"Marathi", +	"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", +	"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)", +	"Sinhala", +	"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", +	"Serbian (Cyrillic)", +	"Serbian (Latin)", +	"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)", +	"Telugu", +	"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)", +	"Central Atlas Tamazight", +	"Central Atlas Tamazight (Marrocos)", +	"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)", +	nullptr +}; + +// 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 + +static const char *locale_renames[][2] = { +	{ "in", "id" }, //  Indonesian +	{ "iw", "he" }, //  Hebrew +	{ "no", "nb" }, //  Norwegian Bokmål +	{ nullptr, nullptr } +}; + +/////////////////////////////////////////////// + +Dictionary Translation::_get_messages() const { +	Dictionary d; +	for (const Map<StringName, StringName>::Element *E = translation_map.front(); E; E = E->next()) { +		d[E->key()] = E->value(); +	} +	return d; +} + +Vector<String> Translation::_get_message_list() const { +	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; +	} + +	return msgs; +} + +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()]; +	} +} + +void Translation::set_locale(const String &p_locale) { +	String univ_locale = TranslationServer::standardize_locale(p_locale); + +	if (!TranslationServer::is_locale_valid(univ_locale)) { +		String trimmed_locale = TranslationServer::get_language_code(univ_locale); + +		ERR_FAIL_COND_MSG(!TranslationServer::is_locale_valid(trimmed_locale), "Invalid locale: " + trimmed_locale + "."); + +		locale = trimmed_locale; +	} else { +		locale = univ_locale; +	} + +	if (OS::get_singleton()->get_main_loop()) { +		OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); +	} +} + +void Translation::add_message(const StringName &p_src_text, const StringName &p_xlated_text, const StringName &p_context) { +	translation_map[p_src_text] = p_xlated_text; +} + +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. \nUse a derived Translation class that handles plurals, such as TranslationPO class"); +	ERR_FAIL_COND_MSG(p_plural_xlated_texts.is_empty(), "Parameter vector p_plural_xlated_texts passed in is empty."); +	translation_map[p_src_text] = p_plural_xlated_texts[0]; +} + +StringName Translation::get_message(const StringName &p_src_text, const StringName &p_context) const { +	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. \nUse a derived Translation class that handles context, such as TranslationPO class"); +	} + +	const Map<StringName, StringName>::Element *E = translation_map.find(p_src_text); +	if (!E) { +		return StringName(); +	} + +	return E->get(); +} + +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. \nUse a derived Translation class that handles plurals, such as TranslationPO class"); +	return get_message(p_src_text); +} + +void Translation::erase_message(const StringName &p_src_text, const StringName &p_context) { +	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. \nUse a derived Translation class that handles context, such as TranslationPO class"); +	} + +	translation_map.erase(p_src_text); +} + +void Translation::get_message_list(List<StringName> *r_messages) const { +	for (const Map<StringName, StringName>::Element *E = translation_map.front(); E; E = E->next()) { +		r_messages->push_back(E->key()); +	} +} + +int Translation::get_message_count() const { +	return translation_map.size(); +} + +void Translation::_bind_methods() { +	ClassDB::bind_method(D_METHOD("set_locale", "locale"), &Translation::set_locale); +	ClassDB::bind_method(D_METHOD("get_locale"), &Translation::get_locale); +	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("")); +	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); + +	ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "messages", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_messages", "_get_messages"); +	ADD_PROPERTY(PropertyInfo(Variant::STRING, "locale"), "set_locale", "get_locale"); +} + +/////////////////////////////////////////////// + +bool TranslationServer::is_locale_valid(const String &p_locale) { +	const char **ptr = locale_list; + +	while (*ptr) { +		if (*ptr == p_locale) { +			return true; +		} +		ptr++; +	} + +	return false; +} + +String TranslationServer::standardize_locale(const String &p_locale) { +	// Replaces '-' with '_' for macOS Sierra-style locales +	String univ_locale = p_locale.replace("-", "_"); + +	// Handles known non-ISO locale names used e.g. on Windows +	int idx = 0; +	while (locale_renames[idx][0] != nullptr) { +		if (locale_renames[idx][0] == univ_locale) { +			univ_locale = locale_renames[idx][1]; +			break; +		} +		idx++; +	} + +	return univ_locale; +} + +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); +} + +void TranslationServer::set_locale(const String &p_locale) { +	String univ_locale = standardize_locale(p_locale); + +	if (!is_locale_valid(univ_locale)) { +		String trimmed_locale = get_language_code(univ_locale); +		print_verbose(vformat("Unsupported locale '%s', falling back to '%s'.", p_locale, trimmed_locale)); + +		if (!is_locale_valid(trimmed_locale)) { +			ERR_PRINT(vformat("Unsupported locale '%s', falling back to 'en'.", trimmed_locale)); +			locale = "en"; +		} else { +			locale = trimmed_locale; +		} +	} else { +		locale = univ_locale; +	} + +	if (OS::get_singleton()->get_main_loop()) { +		OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED); +	} + +	ResourceLoader::reload_translation_remaps(); +} + +String TranslationServer::get_locale() const { +	return locale; +} + +String TranslationServer::get_locale_name(const String &p_locale) const { +	if (!locale_name_map.has(p_locale)) { +		return String(); +	} +	return locale_name_map[p_locale]; +} + +Array TranslationServer::get_loaded_locales() const { +	Array locales; +	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(), Array()); +		String l = t->get_locale(); + +		locales.push_back(l); +	} + +	return locales; +} + +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) { +		locales.push_back(String::utf8(*ptr)); +		ptr++; +	} + +	return locales; +} + +void TranslationServer::add_translation(const Ref<Translation> &p_translation) { +	translations.insert(p_translation); +} + +void TranslationServer::remove_translation(const Ref<Translation> &p_translation) { +	translations.erase(p_translation); +} + +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; +} + +void TranslationServer::clear() { +	translations.clear(); +} + +StringName TranslationServer::translate(const StringName &p_message, const StringName &p_context) const { +	// Match given message against the translation catalog for the project locale. + +	if (!enabled) { +		return p_message; +	} + +	ERR_FAIL_COND_V_MSG(locale.length() < 2, p_message, "Could not translate message as configured locale '" + locale + "' is invalid."); + +	StringName res = _get_message_from_translations(p_message, p_context, locale, false); + +	if (!res && fallback.length() >= 2) { +		res = _get_message_from_translations(p_message, p_context, fallback, false); +	} + +	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; +		} +		return p_message_plural; +	} + +	ERR_FAIL_COND_V_MSG(locale.length() < 2, p_message, "Could not translate message as configured locale '" + locale + "' is invalid."); + +	StringName res = _get_message_from_translations(p_message, p_context, locale, true, p_message_plural, p_n); + +	if (!res && fallback.length() >= 2) { +		res = _get_message_from_translations(p_message, p_context, fallback, true, p_message_plural, p_n); +	} + +	if (!res) { +		if (p_n == 1) { +			return p_message; +		} +		return p_message_plural; +	} + +	return res; +} + +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 { +	// 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). + +	// Note: ResourceLoader::_path_remap reproduces this locale near matching +	// logic, so be sure to propagate changes there when changing things here. + +	StringName res; +	String lang = get_language_code(p_locale); +	bool near_match = 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(), p_message); +		String l = t->get_locale(); + +		bool exact_match = (l == p_locale); +		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. +			} +		} + +		StringName r; +		if (!plural) { +			r = t->get_message(p_message, p_context); +		} else { +			r = t->get_plural_message(p_message, p_message_plural, p_n, p_context); +		} + +		if (!r) { +			continue; +		} +		res = r; + +		if (exact_match) { +			break; +		} else { +			near_match = true; +		} +	} + +	return res; +} + +TranslationServer *TranslationServer::singleton = nullptr; + +bool TranslationServer::_load_translations(const String &p_from) { +	if (ProjectSettings::get_singleton()->has_setting(p_from)) { +		Vector<String> translations = ProjectSettings::get_singleton()->get(p_from); + +		int tcount = translations.size(); + +		if (tcount) { +			const String *r = translations.ptr(); + +			for (int i = 0; i < tcount; i++) { +				Ref<Translation> tr = ResourceLoader::load(r[i]); +				if (tr.is_valid()) { +					add_translation(tr); +				} +			} +		} +		return true; +	} + +	return false; +} + +void TranslationServer::setup() { +	String test = GLOBAL_DEF("internationalization/locale/test", ""); +	test = test.strip_edges(); +	if (test != "") { +		set_locale(test); +	} else { +		set_locale(OS::get_singleton()->get_locale()); +	} +	fallback = GLOBAL_DEF("internationalization/locale/fallback", "en"); +#ifdef TOOLS_ENABLED +	{ +		String options = ""; +		int idx = 0; +		while (locale_list[idx]) { +			if (idx > 0) { +				options += ","; +			} +			options += locale_list[idx]; +			idx++; +		} +		ProjectSettings::get_singleton()->set_custom_property_info("internationalization/locale/fallback", PropertyInfo(Variant::STRING, "internationalization/locale/fallback", PROPERTY_HINT_ENUM, options)); +	} +#endif +} + +void TranslationServer::set_tool_translation(const Ref<Translation> &p_translation) { +	tool_translation = p_translation; +} + +Ref<Translation> TranslationServer::get_tool_translation() const { +	return tool_translation; +} + +String TranslationServer::get_tool_locale() { +#ifdef TOOLS_ENABLED +	if (TranslationServer::get_singleton()->get_tool_translation().is_valid() && (Engine::get_singleton()->is_editor_hint() || Main::is_project_manager())) { +		return tool_translation->get_locale(); +	} else { +#else +	{ +#endif +		return get_locale(); +	} +} + +StringName TranslationServer::tool_translate(const StringName &p_message, const StringName &p_context) const { +	if (tool_translation.is_valid()) { +		StringName r = tool_translation->get_message(p_message, p_context); +		if (r) { +			return r; +		} +	} +	return p_message; +} + +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; +	} +	return p_message_plural; +} + +void TranslationServer::set_doc_translation(const Ref<Translation> &p_translation) { +	doc_translation = p_translation; +} + +StringName TranslationServer::doc_translate(const StringName &p_message, const StringName &p_context) const { +	if (doc_translation.is_valid()) { +		StringName r = doc_translation->get_message(p_message, p_context); +		if (r) { +			return r; +		} +	} +	return p_message; +} + +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; +	} +	return p_message_plural; +} + +void TranslationServer::_bind_methods() { +	ClassDB::bind_method(D_METHOD("set_locale", "locale"), &TranslationServer::set_locale); +	ClassDB::bind_method(D_METHOD("get_locale"), &TranslationServer::get_locale); + +	ClassDB::bind_method(D_METHOD("get_locale_name", "locale"), &TranslationServer::get_locale_name); + +	ClassDB::bind_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL("")); +	ClassDB::bind_method(D_METHOD("translate_plural", "message", "plural_message", "n", "context"), &TranslationServer::translate_plural, DEFVAL("")); + +	ClassDB::bind_method(D_METHOD("add_translation", "translation"), &TranslationServer::add_translation); +	ClassDB::bind_method(D_METHOD("remove_translation", "translation"), &TranslationServer::remove_translation); +	ClassDB::bind_method(D_METHOD("get_translation_object", "locale"), &TranslationServer::get_translation_object); + +	ClassDB::bind_method(D_METHOD("clear"), &TranslationServer::clear); + +	ClassDB::bind_method(D_METHOD("get_loaded_locales"), &TranslationServer::get_loaded_locales); +} + +void TranslationServer::load_translations() { +	String locale = get_locale(); +	_load_translations("internationalization/locale/translations"); //all +	_load_translations("internationalization/locale/translations_" + locale.substr(0, 2)); + +	if (locale.substr(0, 2) != locale) { +		_load_translations("internationalization/locale/translations_" + locale); +	} +} + +TranslationServer::TranslationServer() { +	singleton = this; + +	for (int i = 0; locale_list[i]; ++i) { +		locale_name_map.insert(locale_list[i], String::utf8(locale_names[i])); +	} +} |