8fb382afb8
Remove translation change notification when setting the locale of a Translation resource
1337 lines
39 KiB
C++
1337 lines
39 KiB
C++
/*************************************************************************/
|
|
/* 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() && TranslationServer::get_singleton()->get_loaded_locales().has(this)) {
|
|
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]));
|
|
}
|
|
}
|