translation.cpp 48 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611
  1. /*************************************************************************/
  2. /* translation.cpp */
  3. /*************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /*************************************************************************/
  8. /* Copyright (c) 2007-2021 Juan Linietsky, Ariel Manzur. */
  9. /* Copyright (c) 2014-2021 Godot Engine contributors (cf. AUTHORS.md). */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.*/
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /*************************************************************************/
  30. #include "translation.h"
  31. #include "core/config/project_settings.h"
  32. #include "core/io/resource_loader.h"
  33. #include "core/os/os.h"
  34. #ifdef TOOLS_ENABLED
  35. #include "editor/editor_settings.h"
  36. #include "main/main.h"
  37. #endif
  38. // ISO 639-1 language codes (and a couple of three-letter ISO 639-2 codes),
  39. // with the addition of glibc locales with their regional identifiers.
  40. // This list must match the language names (in English) of locale_names.
  41. //
  42. // References:
  43. // - https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes
  44. // - https://lh.2xlibre.net/locales/
  45. // - https://iso639-3.sil.org/
  46. static const char *locale_list[] = {
  47. "aa", // Afar
  48. "aa_DJ", // Afar (Djibouti)
  49. "aa_ER", // Afar (Eritrea)
  50. "aa_ET", // Afar (Ethiopia)
  51. "af", // Afrikaans
  52. "af_ZA", // Afrikaans (South Africa)
  53. "agr_PE", // Aguaruna (Peru)
  54. "ak_GH", // Akan (Ghana)
  55. "am_ET", // Amharic (Ethiopia)
  56. "an_ES", // Aragonese (Spain)
  57. "anp_IN", // Angika (India)
  58. "ar", // Arabic
  59. "ar_AE", // Arabic (United Arab Emirates)
  60. "ar_BH", // Arabic (Bahrain)
  61. "ar_DZ", // Arabic (Algeria)
  62. "ar_EG", // Arabic (Egypt)
  63. "ar_IN", // Arabic (India)
  64. "ar_IQ", // Arabic (Iraq)
  65. "ar_JO", // Arabic (Jordan)
  66. "ar_KW", // Arabic (Kuwait)
  67. "ar_LB", // Arabic (Lebanon)
  68. "ar_LY", // Arabic (Libya)
  69. "ar_MA", // Arabic (Morocco)
  70. "ar_OM", // Arabic (Oman)
  71. "ar_QA", // Arabic (Qatar)
  72. "ar_SA", // Arabic (Saudi Arabia)
  73. "ar_SD", // Arabic (Sudan)
  74. "ar_SS", // Arabic (South Soudan)
  75. "ar_SY", // Arabic (Syria)
  76. "ar_TN", // Arabic (Tunisia)
  77. "ar_YE", // Arabic (Yemen)
  78. "as_IN", // Assamese (India)
  79. "ast_ES", // Asturian (Spain)
  80. "ayc_PE", // Southern Aymara (Peru)
  81. "ay_PE", // Aymara (Peru)
  82. "az", // Azerbaijani
  83. "az_AZ", // Azerbaijani (Azerbaijan)
  84. "be", // Belarusian
  85. "be_BY", // Belarusian (Belarus)
  86. "bem_ZM", // Bemba (Zambia)
  87. "ber_DZ", // Berber languages (Algeria)
  88. "ber_MA", // Berber languages (Morocco)
  89. "bg", // Bulgarian
  90. "bg_BG", // Bulgarian (Bulgaria)
  91. "bhb_IN", // Bhili (India)
  92. "bho_IN", // Bhojpuri (India)
  93. "bi_TV", // Bislama (Tuvalu)
  94. "bn", // Bengali
  95. "bn_BD", // Bengali (Bangladesh)
  96. "bn_IN", // Bengali (India)
  97. "bo", // Tibetan
  98. "bo_CN", // Tibetan (China)
  99. "bo_IN", // Tibetan (India)
  100. "br", // Breton
  101. "br_FR", // Breton (France)
  102. "brx_IN", // Bodo (India)
  103. "bs_BA", // Bosnian (Bosnia and Herzegovina)
  104. "byn_ER", // Bilin (Eritrea)
  105. "ca", // Catalan
  106. "ca_AD", // Catalan (Andorra)
  107. "ca_ES", // Catalan (Spain)
  108. "ca_FR", // Catalan (France)
  109. "ca_IT", // Catalan (Italy)
  110. "ce_RU", // Chechen (Russia)
  111. "chr_US", // Cherokee (United States)
  112. "cmn_TW", // Mandarin Chinese (Taiwan)
  113. "crh_UA", // Crimean Tatar (Ukraine)
  114. "csb_PL", // Kashubian (Poland)
  115. "cs", // Czech
  116. "cs_CZ", // Czech (Czech Republic)
  117. "cv_RU", // Chuvash (Russia)
  118. "cy_GB", // Welsh (United Kingdom)
  119. "da", // Danish
  120. "da_DK", // Danish (Denmark)
  121. "de", // German
  122. "de_AT", // German (Austria)
  123. "de_BE", // German (Belgium)
  124. "de_CH", // German (Switzerland)
  125. "de_DE", // German (Germany)
  126. "de_IT", // German (Italy)
  127. "de_LU", // German (Luxembourg)
  128. "doi_IN", // Dogri (India)
  129. "dv_MV", // Dhivehi (Maldives)
  130. "dz_BT", // Dzongkha (Bhutan)
  131. "el", // Greek
  132. "el_CY", // Greek (Cyprus)
  133. "el_GR", // Greek (Greece)
  134. "en", // English
  135. "en_AG", // English (Antigua and Barbuda)
  136. "en_AU", // English (Australia)
  137. "en_BW", // English (Botswana)
  138. "en_CA", // English (Canada)
  139. "en_DK", // English (Denmark)
  140. "en_GB", // English (United Kingdom)
  141. "en_HK", // English (Hong Kong)
  142. "en_IE", // English (Ireland)
  143. "en_IL", // English (Israel)
  144. "en_IN", // English (India)
  145. "en_NG", // English (Nigeria)
  146. "en_NZ", // English (New Zealand)
  147. "en_PH", // English (Philippines)
  148. "en_SG", // English (Singapore)
  149. "en_US", // English (United States)
  150. "en_ZA", // English (South Africa)
  151. "en_ZM", // English (Zambia)
  152. "en_ZW", // English (Zimbabwe)
  153. "eo", // Esperanto
  154. "es", // Spanish
  155. "es_AR", // Spanish (Argentina)
  156. "es_BO", // Spanish (Bolivia)
  157. "es_CL", // Spanish (Chile)
  158. "es_CO", // Spanish (Colombia)
  159. "es_CR", // Spanish (Costa Rica)
  160. "es_CU", // Spanish (Cuba)
  161. "es_DO", // Spanish (Dominican Republic)
  162. "es_EC", // Spanish (Ecuador)
  163. "es_ES", // Spanish (Spain)
  164. "es_GT", // Spanish (Guatemala)
  165. "es_HN", // Spanish (Honduras)
  166. "es_MX", // Spanish (Mexico)
  167. "es_NI", // Spanish (Nicaragua)
  168. "es_PA", // Spanish (Panama)
  169. "es_PE", // Spanish (Peru)
  170. "es_PR", // Spanish (Puerto Rico)
  171. "es_PY", // Spanish (Paraguay)
  172. "es_SV", // Spanish (El Salvador)
  173. "es_US", // Spanish (United States)
  174. "es_UY", // Spanish (Uruguay)
  175. "es_VE", // Spanish (Venezuela)
  176. "et", // Estonian
  177. "et_EE", // Estonian (Estonia)
  178. "eu", // Basque
  179. "eu_ES", // Basque (Spain)
  180. "fa", // Persian
  181. "fa_IR", // Persian (Iran)
  182. "ff_SN", // Fulah (Senegal)
  183. "fi", // Finnish
  184. "fi_FI", // Finnish (Finland)
  185. "fil", // Filipino
  186. "fil_PH", // Filipino (Philippines)
  187. "fo_FO", // Faroese (Faroe Islands)
  188. "fr", // French
  189. "fr_BE", // French (Belgium)
  190. "fr_CA", // French (Canada)
  191. "fr_CH", // French (Switzerland)
  192. "fr_FR", // French (France)
  193. "fr_LU", // French (Luxembourg)
  194. "fur_IT", // Friulian (Italy)
  195. "fy_DE", // Western Frisian (Germany)
  196. "fy_NL", // Western Frisian (Netherlands)
  197. "ga", // Irish
  198. "ga_IE", // Irish (Ireland)
  199. "gd_GB", // Scottish Gaelic (United Kingdom)
  200. "gez_ER", // Geez (Eritrea)
  201. "gez_ET", // Geez (Ethiopia)
  202. "gl", // Galician
  203. "gl_ES", // Galician (Spain)
  204. "gu_IN", // Gujarati (India)
  205. "gv_GB", // Manx (United Kingdom)
  206. "hak_TW", // Hakka Chinese (Taiwan)
  207. "ha_NG", // Hausa (Nigeria)
  208. "he", // Hebrew
  209. "he_IL", // Hebrew (Israel)
  210. "hi", // Hindi
  211. "hi_IN", // Hindi (India)
  212. "hne_IN", // Chhattisgarhi (India)
  213. "hr", // Croatian
  214. "hr_HR", // Croatian (Croatia)
  215. "hsb_DE", // Upper Sorbian (Germany)
  216. "ht_HT", // Haitian (Haiti)
  217. "hu", // Hungarian
  218. "hu_HU", // Hungarian (Hungary)
  219. "hus_MX", // Huastec (Mexico)
  220. "hy_AM", // Armenian (Armenia)
  221. "ia_FR", // Interlingua (France)
  222. "id", // Indonesian
  223. "id_ID", // Indonesian (Indonesia)
  224. "ig_NG", // Igbo (Nigeria)
  225. "ik_CA", // Inupiaq (Canada)
  226. "is", // Icelandic
  227. "is_IS", // Icelandic (Iceland)
  228. "it", // Italian
  229. "it_CH", // Italian (Switzerland)
  230. "it_IT", // Italian (Italy)
  231. "iu_CA", // Inuktitut (Canada)
  232. "ja", // Japanese
  233. "ja_JP", // Japanese (Japan)
  234. "kab_DZ", // Kabyle (Algeria)
  235. "ka", // Georgian
  236. "ka_GE", // Georgian (Georgia)
  237. "kk_KZ", // Kazakh (Kazakhstan)
  238. "kl_GL", // Kalaallisut (Greenland)
  239. "km", // Central Khmer
  240. "km_KH", // Central Khmer (Cambodia)
  241. "kn_IN", // Kannada (India)
  242. "kok_IN", // Konkani (India)
  243. "ko", // Korean
  244. "ko_KR", // Korean (South Korea)
  245. "ks_IN", // Kashmiri (India)
  246. "ku", // Kurdish
  247. "ku_TR", // Kurdish (Turkey)
  248. "kw_GB", // Cornish (United Kingdom)
  249. "ky_KG", // Kirghiz (Kyrgyzstan)
  250. "lb_LU", // Luxembourgish (Luxembourg)
  251. "lg_UG", // Ganda (Uganda)
  252. "li_BE", // Limburgan (Belgium)
  253. "li_NL", // Limburgan (Netherlands)
  254. "lij_IT", // Ligurian (Italy)
  255. "ln_CD", // Lingala (Congo)
  256. "lo_LA", // Lao (Laos)
  257. "lt", // Lithuanian
  258. "lt_LT", // Lithuanian (Lithuania)
  259. "lv", // Latvian
  260. "lv_LV", // Latvian (Latvia)
  261. "lzh_TW", // Literary Chinese (Taiwan)
  262. "mag_IN", // Magahi (India)
  263. "mai_IN", // Maithili (India)
  264. "mg_MG", // Malagasy (Madagascar)
  265. "mh_MH", // Marshallese (Marshall Islands)
  266. "mhr_RU", // Eastern Mari (Russia)
  267. "mi", // Māori
  268. "mi_NZ", // Māori (New Zealand)
  269. "miq_NI", // Mískito (Nicaragua)
  270. "mk", // Macedonian
  271. "mk_MK", // Macedonian (Macedonia)
  272. "ml", // Malayalam
  273. "ml_IN", // Malayalam (India)
  274. "mni_IN", // Manipuri (India)
  275. "mn_MN", // Mongolian (Mongolia)
  276. "mr", // Marathi
  277. "mr_IN", // Marathi (India)
  278. "ms", // Malay
  279. "ms_MY", // Malay (Malaysia)
  280. "mt", // Maltese
  281. "mt_MT", // Maltese (Malta)
  282. "my_MM", // Burmese (Myanmar)
  283. "myv_RU", // Erzya (Russia)
  284. "nah_MX", // Nahuatl languages (Mexico)
  285. "nan_TW", // Min Nan Chinese (Taiwan)
  286. "nb", // Norwegian Bokmål
  287. "nb_NO", // Norwegian Bokmål (Norway)
  288. "nds_DE", // Low German (Germany)
  289. "nds_NL", // Low German (Netherlands)
  290. "ne_NP", // Nepali (Nepal)
  291. "nhn_MX", // Central Nahuatl (Mexico)
  292. "niu_NU", // Niuean (Niue)
  293. "niu_NZ", // Niuean (New Zealand)
  294. "nl", // Dutch
  295. "nl_AW", // Dutch (Aruba)
  296. "nl_BE", // Dutch (Belgium)
  297. "nl_NL", // Dutch (Netherlands)
  298. "nn", // Norwegian Nynorsk
  299. "nn_NO", // Norwegian Nynorsk (Norway)
  300. "nr_ZA", // South Ndebele (South Africa)
  301. "nso_ZA", // Pedi (South Africa)
  302. "oc_FR", // Occitan (France)
  303. "om", // Oromo
  304. "om_ET", // Oromo (Ethiopia)
  305. "om_KE", // Oromo (Kenya)
  306. "or", // Oriya
  307. "or_IN", // Oriya (India)
  308. "os_RU", // Ossetian (Russia)
  309. "pa_IN", // Panjabi (India)
  310. "pap", // Papiamento
  311. "pap_AN", // Papiamento (Netherlands Antilles)
  312. "pap_AW", // Papiamento (Aruba)
  313. "pap_CW", // Papiamento (Curaçao)
  314. "pa_PK", // Panjabi (Pakistan)
  315. "pl", // Polish
  316. "pl_PL", // Polish (Poland)
  317. "pr", // Pirate
  318. "ps_AF", // Pushto (Afghanistan)
  319. "pt", // Portuguese
  320. "pt_BR", // Portuguese (Brazil)
  321. "pt_PT", // Portuguese (Portugal)
  322. "quy_PE", // Ayacucho Quechua (Peru)
  323. "quz_PE", // Cusco Quechua (Peru)
  324. "raj_IN", // Rajasthani (India)
  325. "ro", // Romanian
  326. "ro_RO", // Romanian (Romania)
  327. "ru", // Russian
  328. "ru_RU", // Russian (Russia)
  329. "ru_UA", // Russian (Ukraine)
  330. "rw_RW", // Kinyarwanda (Rwanda)
  331. "sa_IN", // Sanskrit (India)
  332. "sat_IN", // Santali (India)
  333. "sc_IT", // Sardinian (Italy)
  334. "sco", // Scots
  335. "sd_IN", // Sindhi (India)
  336. "se_NO", // Northern Sami (Norway)
  337. "sgs_LT", // Samogitian (Lithuania)
  338. "shs_CA", // Shuswap (Canada)
  339. "sid_ET", // Sidamo (Ethiopia)
  340. "si", // Sinhala
  341. "si_LK", // Sinhala (Sri Lanka)
  342. "sk", // Slovak
  343. "sk_SK", // Slovak (Slovakia)
  344. "sl", // Slovenian
  345. "sl_SI", // Slovenian (Slovenia)
  346. "so", // Somali
  347. "so_DJ", // Somali (Djibouti)
  348. "so_ET", // Somali (Ethiopia)
  349. "so_KE", // Somali (Kenya)
  350. "so_SO", // Somali (Somalia)
  351. "son_ML", // Songhai languages (Mali)
  352. "sq", // Albanian
  353. "sq_AL", // Albanian (Albania)
  354. "sq_KV", // Albanian (Kosovo)
  355. "sq_MK", // Albanian (Macedonia)
  356. "sr", // Serbian
  357. "sr_Cyrl", // Serbian (Cyrillic)
  358. "sr_Latn", // Serbian (Latin)
  359. "sr_ME", // Serbian (Montenegro)
  360. "sr_RS", // Serbian (Serbia)
  361. "ss_ZA", // Swati (South Africa)
  362. "st_ZA", // Southern Sotho (South Africa)
  363. "sv", // Swedish
  364. "sv_FI", // Swedish (Finland)
  365. "sv_SE", // Swedish (Sweden)
  366. "sw_KE", // Swahili (Kenya)
  367. "sw_TZ", // Swahili (Tanzania)
  368. "szl_PL", // Silesian (Poland)
  369. "ta", // Tamil
  370. "ta_IN", // Tamil (India)
  371. "ta_LK", // Tamil (Sri Lanka)
  372. "tcy_IN", // Tulu (India)
  373. "te", // Telugu
  374. "te_IN", // Telugu (India)
  375. "tg_TJ", // Tajik (Tajikistan)
  376. "the_NP", // Chitwania Tharu (Nepal)
  377. "th", // Thai
  378. "th_TH", // Thai (Thailand)
  379. "ti", // Tigrinya
  380. "ti_ER", // Tigrinya (Eritrea)
  381. "ti_ET", // Tigrinya (Ethiopia)
  382. "tig_ER", // Tigre (Eritrea)
  383. "tk_TM", // Turkmen (Turkmenistan)
  384. "tl_PH", // Tagalog (Philippines)
  385. "tn_ZA", // Tswana (South Africa)
  386. "tr", // Turkish
  387. "tr_CY", // Turkish (Cyprus)
  388. "tr_TR", // Turkish (Turkey)
  389. "ts_ZA", // Tsonga (South Africa)
  390. "tt", // Tatar
  391. "tt_RU", // Tatar (Russia)
  392. "tzm", // Central Atlas Tamazight
  393. "tzm_MA", // Central Atlas Tamazight (Marrocos)
  394. "ug_CN", // Uighur (China)
  395. "uk", // Ukrainian
  396. "uk_UA", // Ukrainian (Ukraine)
  397. "unm_US", // Unami (United States)
  398. "ur", // Urdu
  399. "ur_IN", // Urdu (India)
  400. "ur_PK", // Urdu (Pakistan)
  401. "uz", // Uzbek
  402. "uz_UZ", // Uzbek (Uzbekistan)
  403. "ve_ZA", // Venda (South Africa)
  404. "vi", // Vietnamese
  405. "vi_VN", // Vietnamese (Vietnam)
  406. "wa_BE", // Walloon (Belgium)
  407. "wae_CH", // Walser (Switzerland)
  408. "wal_ET", // Wolaytta (Ethiopia)
  409. "wo_SN", // Wolof (Senegal)
  410. "xh_ZA", // Xhosa (South Africa)
  411. "yi_US", // Yiddish (United States)
  412. "yo_NG", // Yoruba (Nigeria)
  413. "yue_HK", // Yue Chinese (Hong Kong)
  414. "zh", // Chinese
  415. "zh_CN", // Chinese (China)
  416. "zh_HK", // Chinese (Hong Kong)
  417. "zh_SG", // Chinese (Singapore)
  418. "zh_TW", // Chinese (Taiwan)
  419. "zu_ZA", // Zulu (South Africa)
  420. nullptr
  421. };
  422. static const char *locale_names[] = {
  423. "Afar",
  424. "Afar (Djibouti)",
  425. "Afar (Eritrea)",
  426. "Afar (Ethiopia)",
  427. "Afrikaans",
  428. "Afrikaans (South Africa)",
  429. "Aguaruna (Peru)",
  430. "Akan (Ghana)",
  431. "Amharic (Ethiopia)",
  432. "Aragonese (Spain)",
  433. "Angika (India)",
  434. "Arabic",
  435. "Arabic (United Arab Emirates)",
  436. "Arabic (Bahrain)",
  437. "Arabic (Algeria)",
  438. "Arabic (Egypt)",
  439. "Arabic (India)",
  440. "Arabic (Iraq)",
  441. "Arabic (Jordan)",
  442. "Arabic (Kuwait)",
  443. "Arabic (Lebanon)",
  444. "Arabic (Libya)",
  445. "Arabic (Morocco)",
  446. "Arabic (Oman)",
  447. "Arabic (Qatar)",
  448. "Arabic (Saudi Arabia)",
  449. "Arabic (Sudan)",
  450. "Arabic (South Soudan)",
  451. "Arabic (Syria)",
  452. "Arabic (Tunisia)",
  453. "Arabic (Yemen)",
  454. "Assamese (India)",
  455. "Asturian (Spain)",
  456. "Southern Aymara (Peru)",
  457. "Aymara (Peru)",
  458. "Azerbaijani",
  459. "Azerbaijani (Azerbaijan)",
  460. "Belarusian",
  461. "Belarusian (Belarus)",
  462. "Bemba (Zambia)",
  463. "Berber languages (Algeria)",
  464. "Berber languages (Morocco)",
  465. "Bulgarian",
  466. "Bulgarian (Bulgaria)",
  467. "Bhili (India)",
  468. "Bhojpuri (India)",
  469. "Bislama (Tuvalu)",
  470. "Bengali",
  471. "Bengali (Bangladesh)",
  472. "Bengali (India)",
  473. "Tibetan",
  474. "Tibetan (China)",
  475. "Tibetan (India)",
  476. "Breton",
  477. "Breton (France)",
  478. "Bodo (India)",
  479. "Bosnian (Bosnia and Herzegovina)",
  480. "Bilin (Eritrea)",
  481. "Catalan",
  482. "Catalan (Andorra)",
  483. "Catalan (Spain)",
  484. "Catalan (France)",
  485. "Catalan (Italy)",
  486. "Chechen (Russia)",
  487. "Cherokee (United States)",
  488. "Mandarin Chinese (Taiwan)",
  489. "Crimean Tatar (Ukraine)",
  490. "Kashubian (Poland)",
  491. "Czech",
  492. "Czech (Czech Republic)",
  493. "Chuvash (Russia)",
  494. "Welsh (United Kingdom)",
  495. "Danish",
  496. "Danish (Denmark)",
  497. "German",
  498. "German (Austria)",
  499. "German (Belgium)",
  500. "German (Switzerland)",
  501. "German (Germany)",
  502. "German (Italy)",
  503. "German (Luxembourg)",
  504. "Dogri (India)",
  505. "Dhivehi (Maldives)",
  506. "Dzongkha (Bhutan)",
  507. "Greek",
  508. "Greek (Cyprus)",
  509. "Greek (Greece)",
  510. "English",
  511. "English (Antigua and Barbuda)",
  512. "English (Australia)",
  513. "English (Botswana)",
  514. "English (Canada)",
  515. "English (Denmark)",
  516. "English (United Kingdom)",
  517. "English (Hong Kong)",
  518. "English (Ireland)",
  519. "English (Israel)",
  520. "English (India)",
  521. "English (Nigeria)",
  522. "English (New Zealand)",
  523. "English (Philippines)",
  524. "English (Singapore)",
  525. "English (United States)",
  526. "English (South Africa)",
  527. "English (Zambia)",
  528. "English (Zimbabwe)",
  529. "Esperanto",
  530. "Spanish",
  531. "Spanish (Argentina)",
  532. "Spanish (Bolivia)",
  533. "Spanish (Chile)",
  534. "Spanish (Colombia)",
  535. "Spanish (Costa Rica)",
  536. "Spanish (Cuba)",
  537. "Spanish (Dominican Republic)",
  538. "Spanish (Ecuador)",
  539. "Spanish (Spain)",
  540. "Spanish (Guatemala)",
  541. "Spanish (Honduras)",
  542. "Spanish (Mexico)",
  543. "Spanish (Nicaragua)",
  544. "Spanish (Panama)",
  545. "Spanish (Peru)",
  546. "Spanish (Puerto Rico)",
  547. "Spanish (Paraguay)",
  548. "Spanish (El Salvador)",
  549. "Spanish (United States)",
  550. "Spanish (Uruguay)",
  551. "Spanish (Venezuela)",
  552. "Estonian",
  553. "Estonian (Estonia)",
  554. "Basque",
  555. "Basque (Spain)",
  556. "Persian",
  557. "Persian (Iran)",
  558. "Fulah (Senegal)",
  559. "Finnish",
  560. "Finnish (Finland)",
  561. "Filipino",
  562. "Filipino (Philippines)",
  563. "Faroese (Faroe Islands)",
  564. "French",
  565. "French (Belgium)",
  566. "French (Canada)",
  567. "French (Switzerland)",
  568. "French (France)",
  569. "French (Luxembourg)",
  570. "Friulian (Italy)",
  571. "Western Frisian (Germany)",
  572. "Western Frisian (Netherlands)",
  573. "Irish",
  574. "Irish (Ireland)",
  575. "Scottish Gaelic (United Kingdom)",
  576. "Geez (Eritrea)",
  577. "Geez (Ethiopia)",
  578. "Galician",
  579. "Galician (Spain)",
  580. "Gujarati (India)",
  581. "Manx (United Kingdom)",
  582. "Hakka Chinese (Taiwan)",
  583. "Hausa (Nigeria)",
  584. "Hebrew",
  585. "Hebrew (Israel)",
  586. "Hindi",
  587. "Hindi (India)",
  588. "Chhattisgarhi (India)",
  589. "Croatian",
  590. "Croatian (Croatia)",
  591. "Upper Sorbian (Germany)",
  592. "Haitian (Haiti)",
  593. "Hungarian",
  594. "Hungarian (Hungary)",
  595. "Huastec (Mexico)",
  596. "Armenian (Armenia)",
  597. "Interlingua (France)",
  598. "Indonesian",
  599. "Indonesian (Indonesia)",
  600. "Igbo (Nigeria)",
  601. "Inupiaq (Canada)",
  602. "Icelandic",
  603. "Icelandic (Iceland)",
  604. "Italian",
  605. "Italian (Switzerland)",
  606. "Italian (Italy)",
  607. "Inuktitut (Canada)",
  608. "Japanese",
  609. "Japanese (Japan)",
  610. "Kabyle (Algeria)",
  611. "Georgian",
  612. "Georgian (Georgia)",
  613. "Kazakh (Kazakhstan)",
  614. "Kalaallisut (Greenland)",
  615. "Central Khmer",
  616. "Central Khmer (Cambodia)",
  617. "Kannada (India)",
  618. "Konkani (India)",
  619. "Korean",
  620. "Korean (South Korea)",
  621. "Kashmiri (India)",
  622. "Kurdish",
  623. "Kurdish (Turkey)",
  624. "Cornish (United Kingdom)",
  625. "Kirghiz (Kyrgyzstan)",
  626. "Luxembourgish (Luxembourg)",
  627. "Ganda (Uganda)",
  628. "Limburgan (Belgium)",
  629. "Limburgan (Netherlands)",
  630. "Ligurian (Italy)",
  631. "Lingala (Congo)",
  632. "Lao (Laos)",
  633. "Lithuanian",
  634. "Lithuanian (Lithuania)",
  635. "Latvian",
  636. "Latvian (Latvia)",
  637. "Literary Chinese (Taiwan)",
  638. "Magahi (India)",
  639. "Maithili (India)",
  640. "Malagasy (Madagascar)",
  641. "Marshallese (Marshall Islands)",
  642. "Eastern Mari (Russia)",
  643. "Māori",
  644. "Māori (New Zealand)",
  645. "Mískito (Nicaragua)",
  646. "Macedonian",
  647. "Macedonian (Macedonia)",
  648. "Malayalam",
  649. "Malayalam (India)",
  650. "Manipuri (India)",
  651. "Mongolian (Mongolia)",
  652. "Marathi",
  653. "Marathi (India)",
  654. "Malay",
  655. "Malay (Malaysia)",
  656. "Maltese",
  657. "Maltese (Malta)",
  658. "Burmese (Myanmar)",
  659. "Erzya (Russia)",
  660. "Nahuatl languages (Mexico)",
  661. "Min Nan Chinese (Taiwan)",
  662. "Norwegian Bokmål",
  663. "Norwegian Bokmål (Norway)",
  664. "Low German (Germany)",
  665. "Low German (Netherlands)",
  666. "Nepali (Nepal)",
  667. "Central Nahuatl (Mexico)",
  668. "Niuean (Niue)",
  669. "Niuean (New Zealand)",
  670. "Dutch",
  671. "Dutch (Aruba)",
  672. "Dutch (Belgium)",
  673. "Dutch (Netherlands)",
  674. "Norwegian Nynorsk",
  675. "Norwegian Nynorsk (Norway)",
  676. "South Ndebele (South Africa)",
  677. "Pedi (South Africa)",
  678. "Occitan (France)",
  679. "Oromo",
  680. "Oromo (Ethiopia)",
  681. "Oromo (Kenya)",
  682. "Oriya",
  683. "Oriya (India)",
  684. "Ossetian (Russia)",
  685. "Panjabi (India)",
  686. "Papiamento",
  687. "Papiamento (Netherlands Antilles)",
  688. "Papiamento (Aruba)",
  689. "Papiamento (Curaçao)",
  690. "Panjabi (Pakistan)",
  691. "Polish",
  692. "Polish (Poland)",
  693. "Pirate",
  694. "Pushto (Afghanistan)",
  695. "Portuguese",
  696. "Portuguese (Brazil)",
  697. "Portuguese (Portugal)",
  698. "Ayacucho Quechua (Peru)",
  699. "Cusco Quechua (Peru)",
  700. "Rajasthani (India)",
  701. "Romanian",
  702. "Romanian (Romania)",
  703. "Russian",
  704. "Russian (Russia)",
  705. "Russian (Ukraine)",
  706. "Kinyarwanda (Rwanda)",
  707. "Sanskrit (India)",
  708. "Santali (India)",
  709. "Sardinian (Italy)",
  710. "Scots (Scotland)",
  711. "Sindhi (India)",
  712. "Northern Sami (Norway)",
  713. "Samogitian (Lithuania)",
  714. "Shuswap (Canada)",
  715. "Sidamo (Ethiopia)",
  716. "Sinhala",
  717. "Sinhala (Sri Lanka)",
  718. "Slovak",
  719. "Slovak (Slovakia)",
  720. "Slovenian",
  721. "Slovenian (Slovenia)",
  722. "Somali",
  723. "Somali (Djibouti)",
  724. "Somali (Ethiopia)",
  725. "Somali (Kenya)",
  726. "Somali (Somalia)",
  727. "Songhai languages (Mali)",
  728. "Albanian",
  729. "Albanian (Albania)",
  730. "Albanian (Kosovo)",
  731. "Albanian (Macedonia)",
  732. "Serbian",
  733. "Serbian (Cyrillic)",
  734. "Serbian (Latin)",
  735. "Serbian (Montenegro)",
  736. "Serbian (Serbia)",
  737. "Swati (South Africa)",
  738. "Southern Sotho (South Africa)",
  739. "Swedish",
  740. "Swedish (Finland)",
  741. "Swedish (Sweden)",
  742. "Swahili (Kenya)",
  743. "Swahili (Tanzania)",
  744. "Silesian (Poland)",
  745. "Tamil",
  746. "Tamil (India)",
  747. "Tamil (Sri Lanka)",
  748. "Tulu (India)",
  749. "Telugu",
  750. "Telugu (India)",
  751. "Tajik (Tajikistan)",
  752. "Chitwania Tharu (Nepal)",
  753. "Thai",
  754. "Thai (Thailand)",
  755. "Tigrinya",
  756. "Tigrinya (Eritrea)",
  757. "Tigrinya (Ethiopia)",
  758. "Tigre (Eritrea)",
  759. "Turkmen (Turkmenistan)",
  760. "Tagalog (Philippines)",
  761. "Tswana (South Africa)",
  762. "Turkish",
  763. "Turkish (Cyprus)",
  764. "Turkish (Turkey)",
  765. "Tsonga (South Africa)",
  766. "Tatar",
  767. "Tatar (Russia)",
  768. "Central Atlas Tamazight",
  769. "Central Atlas Tamazight (Marrocos)",
  770. "Uighur (China)",
  771. "Ukrainian",
  772. "Ukrainian (Ukraine)",
  773. "Unami (United States)",
  774. "Urdu",
  775. "Urdu (India)",
  776. "Urdu (Pakistan)",
  777. "Uzbek",
  778. "Uzbek (Uzbekistan)",
  779. "Venda (South Africa)",
  780. "Vietnamese",
  781. "Vietnamese (Vietnam)",
  782. "Walloon (Belgium)",
  783. "Walser (Switzerland)",
  784. "Wolaytta (Ethiopia)",
  785. "Wolof (Senegal)",
  786. "Xhosa (South Africa)",
  787. "Yiddish (United States)",
  788. "Yoruba (Nigeria)",
  789. "Yue Chinese (Hong Kong)",
  790. "Chinese",
  791. "Chinese (China)",
  792. "Chinese (Hong Kong)",
  793. "Chinese (Singapore)",
  794. "Chinese (Taiwan)",
  795. "Zulu (South Africa)",
  796. nullptr
  797. };
  798. // Windows has some weird locale identifiers which do not honor the ISO 639-1
  799. // standardized nomenclature. Whenever those don't conflict with existing ISO
  800. // identifiers, we override them.
  801. //
  802. // Reference:
  803. // - https://msdn.microsoft.com/en-us/library/windows/desktop/ms693062(v=vs.85).aspx
  804. static const char *locale_renames[][2] = {
  805. { "in", "id" }, // Indonesian
  806. { "iw", "he" }, // Hebrew
  807. { "no", "nb" }, // Norwegian Bokmål
  808. { "C", "en" }, // "C" is the simple/default/untranslated Computer locale.
  809. // ASCII-only, English, no currency symbols. Godot treats this as "en".
  810. // See https://unix.stackexchange.com/a/87763/164141 "The C locale is"...
  811. { nullptr, nullptr }
  812. };
  813. ///////////////////////////////////////////////
  814. Dictionary Translation::_get_messages() const {
  815. Dictionary d;
  816. for (const Map<StringName, StringName>::Element *E = translation_map.front(); E; E = E->next()) {
  817. d[E->key()] = E->value();
  818. }
  819. return d;
  820. }
  821. Vector<String> Translation::_get_message_list() const {
  822. Vector<String> msgs;
  823. msgs.resize(translation_map.size());
  824. int idx = 0;
  825. for (const Map<StringName, StringName>::Element *E = translation_map.front(); E; E = E->next()) {
  826. msgs.set(idx, E->key());
  827. idx += 1;
  828. }
  829. return msgs;
  830. }
  831. void Translation::_set_messages(const Dictionary &p_messages) {
  832. List<Variant> keys;
  833. p_messages.get_key_list(&keys);
  834. for (const Variant &E : keys) {
  835. translation_map[E] = p_messages[E];
  836. }
  837. }
  838. void Translation::set_locale(const String &p_locale) {
  839. String univ_locale = TranslationServer::standardize_locale(p_locale);
  840. if (!TranslationServer::is_locale_valid(univ_locale)) {
  841. String trimmed_locale = TranslationServer::get_language_code(univ_locale);
  842. ERR_FAIL_COND_MSG(!TranslationServer::is_locale_valid(trimmed_locale), "Invalid locale: " + trimmed_locale + ".");
  843. locale = trimmed_locale;
  844. } else {
  845. locale = univ_locale;
  846. }
  847. if (OS::get_singleton()->get_main_loop() && TranslationServer::get_singleton()->get_loaded_locales().has(this)) {
  848. OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
  849. }
  850. }
  851. void Translation::add_message(const StringName &p_src_text, const StringName &p_xlated_text, const StringName &p_context) {
  852. translation_map[p_src_text] = p_xlated_text;
  853. }
  854. void Translation::add_plural_message(const StringName &p_src_text, const Vector<String> &p_plural_xlated_texts, const StringName &p_context) {
  855. 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");
  856. ERR_FAIL_COND_MSG(p_plural_xlated_texts.is_empty(), "Parameter vector p_plural_xlated_texts passed in is empty.");
  857. translation_map[p_src_text] = p_plural_xlated_texts[0];
  858. }
  859. StringName Translation::get_message(const StringName &p_src_text, const StringName &p_context) const {
  860. StringName ret;
  861. if (GDVIRTUAL_CALL(_get_message, p_src_text, p_context, ret)) {
  862. return ret;
  863. }
  864. if (p_context != StringName()) {
  865. 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");
  866. }
  867. const Map<StringName, StringName>::Element *E = translation_map.find(p_src_text);
  868. if (!E) {
  869. return StringName();
  870. }
  871. return E->get();
  872. }
  873. StringName Translation::get_plural_message(const StringName &p_src_text, const StringName &p_plural_text, int p_n, const StringName &p_context) const {
  874. StringName ret;
  875. if (GDVIRTUAL_CALL(_get_plural_message, p_src_text, p_plural_text, p_n, p_context, ret)) {
  876. return ret;
  877. }
  878. 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");
  879. return get_message(p_src_text);
  880. }
  881. void Translation::erase_message(const StringName &p_src_text, const StringName &p_context) {
  882. if (p_context != StringName()) {
  883. 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");
  884. }
  885. translation_map.erase(p_src_text);
  886. }
  887. void Translation::get_message_list(List<StringName> *r_messages) const {
  888. for (const Map<StringName, StringName>::Element *E = translation_map.front(); E; E = E->next()) {
  889. r_messages->push_back(E->key());
  890. }
  891. }
  892. int Translation::get_message_count() const {
  893. return translation_map.size();
  894. }
  895. void Translation::_bind_methods() {
  896. ClassDB::bind_method(D_METHOD("set_locale", "locale"), &Translation::set_locale);
  897. ClassDB::bind_method(D_METHOD("get_locale"), &Translation::get_locale);
  898. ClassDB::bind_method(D_METHOD("add_message", "src_message", "xlated_message", "context"), &Translation::add_message, DEFVAL(""));
  899. ClassDB::bind_method(D_METHOD("add_plural_message", "src_message", "xlated_messages", "context"), &Translation::add_plural_message, DEFVAL(""));
  900. ClassDB::bind_method(D_METHOD("get_message", "src_message", "context"), &Translation::get_message, DEFVAL(""));
  901. ClassDB::bind_method(D_METHOD("get_plural_message", "src_message", "src_plural_message", "n", "context"), &Translation::get_plural_message, DEFVAL(""));
  902. ClassDB::bind_method(D_METHOD("erase_message", "src_message", "context"), &Translation::erase_message, DEFVAL(""));
  903. ClassDB::bind_method(D_METHOD("get_message_list"), &Translation::_get_message_list);
  904. ClassDB::bind_method(D_METHOD("get_message_count"), &Translation::get_message_count);
  905. ClassDB::bind_method(D_METHOD("_set_messages"), &Translation::_set_messages);
  906. ClassDB::bind_method(D_METHOD("_get_messages"), &Translation::_get_messages);
  907. GDVIRTUAL_BIND(_get_plural_message, "src_message", "src_plural_message", "n", "context");
  908. GDVIRTUAL_BIND(_get_message, "src_message", "context");
  909. ADD_PROPERTY(PropertyInfo(Variant::DICTIONARY, "messages", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_NOEDITOR | PROPERTY_USAGE_INTERNAL), "_set_messages", "_get_messages");
  910. ADD_PROPERTY(PropertyInfo(Variant::STRING, "locale"), "set_locale", "get_locale");
  911. }
  912. ///////////////////////////////////////////////
  913. struct _character_accent_pair {
  914. const char32_t character;
  915. const char32_t *accented_character;
  916. };
  917. static _character_accent_pair _character_to_accented[] = {
  918. { 'A', U"Å" },
  919. { 'B', U"ß" },
  920. { 'C', U"Ç" },
  921. { 'D', U"Ð" },
  922. { 'E', U"É" },
  923. { 'F', U"F́" },
  924. { 'G', U"Ĝ" },
  925. { 'H', U"Ĥ" },
  926. { 'I', U"Ĩ" },
  927. { 'J', U"Ĵ" },
  928. { 'K', U"ĸ" },
  929. { 'L', U"Ł" },
  930. { 'M', U"Ḿ" },
  931. { 'N', U"й" },
  932. { 'O', U"Ö" },
  933. { 'P', U"Ṕ" },
  934. { 'Q', U"Q́" },
  935. { 'R', U"Ř" },
  936. { 'S', U"Ŝ" },
  937. { 'T', U"Ŧ" },
  938. { 'U', U"Ũ" },
  939. { 'V', U"Ṽ" },
  940. { 'W', U"Ŵ" },
  941. { 'X', U"X́" },
  942. { 'Y', U"Ÿ" },
  943. { 'Z', U"Ž" },
  944. { 'a', U"á" },
  945. { 'b', U"ḅ" },
  946. { 'c', U"ć" },
  947. { 'd', U"d́" },
  948. { 'e', U"é" },
  949. { 'f', U"f́" },
  950. { 'g', U"ǵ" },
  951. { 'h', U"h̀" },
  952. { 'i', U"í" },
  953. { 'j', U"ǰ" },
  954. { 'k', U"ḱ" },
  955. { 'l', U"ł" },
  956. { 'm', U"m̀" },
  957. { 'n', U"ή" },
  958. { 'o', U"ô" },
  959. { 'p', U"ṕ" },
  960. { 'q', U"q́" },
  961. { 'r', U"ŕ" },
  962. { 's', U"š" },
  963. { 't', U"ŧ" },
  964. { 'u', U"ü" },
  965. { 'v', U"ṽ" },
  966. { 'w', U"ŵ" },
  967. { 'x', U"x́" },
  968. { 'y', U"ý" },
  969. { 'z', U"ź" },
  970. };
  971. bool TranslationServer::is_locale_valid(const String &p_locale) {
  972. const char **ptr = locale_list;
  973. while (*ptr) {
  974. if (*ptr == p_locale) {
  975. return true;
  976. }
  977. ptr++;
  978. }
  979. return false;
  980. }
  981. String TranslationServer::standardize_locale(const String &p_locale) {
  982. // Replaces '-' with '_' for macOS Sierra-style locales
  983. String univ_locale = p_locale.replace("-", "_");
  984. // Handles known non-ISO locale names used e.g. on Windows
  985. int idx = 0;
  986. while (locale_renames[idx][0] != nullptr) {
  987. if (locale_renames[idx][0] == univ_locale) {
  988. univ_locale = locale_renames[idx][1];
  989. break;
  990. }
  991. idx++;
  992. }
  993. return univ_locale;
  994. }
  995. String TranslationServer::get_language_code(const String &p_locale) {
  996. ERR_FAIL_COND_V_MSG(p_locale.length() < 2, p_locale, "Invalid locale '" + p_locale + "'.");
  997. // Most language codes are two letters, but some are three,
  998. // so we have to look for a regional code separator ('_' or '-')
  999. // to extract the left part.
  1000. // For example we get 'nah_MX' as input and should return 'nah'.
  1001. int split = p_locale.find("_");
  1002. if (split == -1) {
  1003. split = p_locale.find("-");
  1004. }
  1005. if (split == -1) { // No separator, so the locale is already only a language code.
  1006. return p_locale;
  1007. }
  1008. return p_locale.left(split);
  1009. }
  1010. void TranslationServer::set_locale(const String &p_locale) {
  1011. String univ_locale = standardize_locale(p_locale);
  1012. if (!is_locale_valid(univ_locale)) {
  1013. String trimmed_locale = get_language_code(univ_locale);
  1014. print_verbose(vformat("Unsupported locale '%s', falling back to '%s'.", p_locale, trimmed_locale));
  1015. if (!is_locale_valid(trimmed_locale)) {
  1016. ERR_PRINT(vformat("Unsupported locale '%s', falling back to 'en'.", trimmed_locale));
  1017. locale = "en";
  1018. } else {
  1019. locale = trimmed_locale;
  1020. }
  1021. } else {
  1022. locale = univ_locale;
  1023. }
  1024. if (OS::get_singleton()->get_main_loop()) {
  1025. OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
  1026. }
  1027. ResourceLoader::reload_translation_remaps();
  1028. }
  1029. String TranslationServer::get_locale() const {
  1030. return locale;
  1031. }
  1032. String TranslationServer::get_locale_name(const String &p_locale) const {
  1033. if (!locale_name_map.has(p_locale)) {
  1034. return String();
  1035. }
  1036. return locale_name_map[p_locale];
  1037. }
  1038. Array TranslationServer::get_loaded_locales() const {
  1039. Array locales;
  1040. for (const Set<Ref<Translation>>::Element *E = translations.front(); E; E = E->next()) {
  1041. const Ref<Translation> &t = E->get();
  1042. ERR_FAIL_COND_V(t.is_null(), Array());
  1043. String l = t->get_locale();
  1044. locales.push_back(l);
  1045. }
  1046. return locales;
  1047. }
  1048. Vector<String> TranslationServer::get_all_locales() {
  1049. Vector<String> locales;
  1050. const char **ptr = locale_list;
  1051. while (*ptr) {
  1052. locales.push_back(*ptr);
  1053. ptr++;
  1054. }
  1055. return locales;
  1056. }
  1057. Vector<String> TranslationServer::get_all_locale_names() {
  1058. Vector<String> locales;
  1059. const char **ptr = locale_names;
  1060. while (*ptr) {
  1061. locales.push_back(String::utf8(*ptr));
  1062. ptr++;
  1063. }
  1064. return locales;
  1065. }
  1066. void TranslationServer::add_translation(const Ref<Translation> &p_translation) {
  1067. translations.insert(p_translation);
  1068. }
  1069. void TranslationServer::remove_translation(const Ref<Translation> &p_translation) {
  1070. translations.erase(p_translation);
  1071. }
  1072. Ref<Translation> TranslationServer::get_translation_object(const String &p_locale) {
  1073. Ref<Translation> res;
  1074. String lang = get_language_code(p_locale);
  1075. bool near_match_found = false;
  1076. for (const Set<Ref<Translation>>::Element *E = translations.front(); E; E = E->next()) {
  1077. const Ref<Translation> &t = E->get();
  1078. ERR_FAIL_COND_V(t.is_null(), nullptr);
  1079. String l = t->get_locale();
  1080. // Exact match.
  1081. if (l == p_locale) {
  1082. return t;
  1083. }
  1084. // If near match found, keep that match, but keep looking to try to look for perfect match.
  1085. if (get_language_code(l) == lang && !near_match_found) {
  1086. res = t;
  1087. near_match_found = true;
  1088. }
  1089. }
  1090. return res;
  1091. }
  1092. void TranslationServer::clear() {
  1093. translations.clear();
  1094. }
  1095. StringName TranslationServer::translate(const StringName &p_message, const StringName &p_context) const {
  1096. // Match given message against the translation catalog for the project locale.
  1097. if (!enabled) {
  1098. return p_message;
  1099. }
  1100. ERR_FAIL_COND_V_MSG(locale.length() < 2, p_message, "Could not translate message as configured locale '" + locale + "' is invalid.");
  1101. StringName res = _get_message_from_translations(p_message, p_context, locale, false);
  1102. if (!res && fallback.length() >= 2) {
  1103. res = _get_message_from_translations(p_message, p_context, fallback, false);
  1104. }
  1105. if (!res) {
  1106. return pseudolocalization_enabled ? pseudolocalize(p_message) : p_message;
  1107. }
  1108. return pseudolocalization_enabled ? pseudolocalize(res) : res;
  1109. }
  1110. StringName TranslationServer::translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
  1111. if (!enabled) {
  1112. if (p_n == 1) {
  1113. return p_message;
  1114. }
  1115. return p_message_plural;
  1116. }
  1117. ERR_FAIL_COND_V_MSG(locale.length() < 2, p_message, "Could not translate message as configured locale '" + locale + "' is invalid.");
  1118. StringName res = _get_message_from_translations(p_message, p_context, locale, true, p_message_plural, p_n);
  1119. if (!res && fallback.length() >= 2) {
  1120. res = _get_message_from_translations(p_message, p_context, fallback, true, p_message_plural, p_n);
  1121. }
  1122. if (!res) {
  1123. if (p_n == 1) {
  1124. return p_message;
  1125. }
  1126. return p_message_plural;
  1127. }
  1128. return res;
  1129. }
  1130. 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 {
  1131. // Locale can be of the form 'll_CC', i.e. language code and regional code,
  1132. // e.g. 'en_US', 'en_GB', etc. It might also be simply 'll', e.g. 'en'.
  1133. // To find the relevant translation, we look for those with locale starting
  1134. // with the language code, and then if any is an exact match for the long
  1135. // form. If not found, we fall back to a near match (another locale with
  1136. // same language code).
  1137. // Note: ResourceLoader::_path_remap reproduces this locale near matching
  1138. // logic, so be sure to propagate changes there when changing things here.
  1139. StringName res;
  1140. String lang = get_language_code(p_locale);
  1141. bool near_match = false;
  1142. for (const Set<Ref<Translation>>::Element *E = translations.front(); E; E = E->next()) {
  1143. const Ref<Translation> &t = E->get();
  1144. ERR_FAIL_COND_V(t.is_null(), p_message);
  1145. String l = t->get_locale();
  1146. bool exact_match = (l == p_locale);
  1147. if (!exact_match) {
  1148. if (near_match) {
  1149. continue; // Only near-match once, but keep looking for exact matches.
  1150. }
  1151. if (get_language_code(l) != lang) {
  1152. continue; // Language code does not match.
  1153. }
  1154. }
  1155. StringName r;
  1156. if (!plural) {
  1157. r = t->get_message(p_message, p_context);
  1158. } else {
  1159. r = t->get_plural_message(p_message, p_message_plural, p_n, p_context);
  1160. }
  1161. if (!r) {
  1162. continue;
  1163. }
  1164. res = r;
  1165. if (exact_match) {
  1166. break;
  1167. } else {
  1168. near_match = true;
  1169. }
  1170. }
  1171. return res;
  1172. }
  1173. TranslationServer *TranslationServer::singleton = nullptr;
  1174. bool TranslationServer::_load_translations(const String &p_from) {
  1175. if (ProjectSettings::get_singleton()->has_setting(p_from)) {
  1176. Vector<String> translations = ProjectSettings::get_singleton()->get(p_from);
  1177. int tcount = translations.size();
  1178. if (tcount) {
  1179. const String *r = translations.ptr();
  1180. for (int i = 0; i < tcount; i++) {
  1181. Ref<Translation> tr = ResourceLoader::load(r[i]);
  1182. if (tr.is_valid()) {
  1183. add_translation(tr);
  1184. }
  1185. }
  1186. }
  1187. return true;
  1188. }
  1189. return false;
  1190. }
  1191. void TranslationServer::setup() {
  1192. String test = GLOBAL_DEF("internationalization/locale/test", "");
  1193. test = test.strip_edges();
  1194. if (test != "") {
  1195. set_locale(test);
  1196. } else {
  1197. set_locale(OS::get_singleton()->get_locale());
  1198. }
  1199. fallback = GLOBAL_DEF("internationalization/locale/fallback", "en");
  1200. pseudolocalization_enabled = GLOBAL_DEF("internationalization/pseudolocalization/use_pseudolocalization", false);
  1201. pseudolocalization_accents_enabled = GLOBAL_DEF("internationalization/pseudolocalization/replace_with_accents", true);
  1202. pseudolocalization_double_vowels_enabled = GLOBAL_DEF("internationalization/pseudolocalization/double_vowels", false);
  1203. pseudolocalization_fake_bidi_enabled = GLOBAL_DEF("internationalization/pseudolocalization/fake_bidi", false);
  1204. pseudolocalization_override_enabled = GLOBAL_DEF("internationalization/pseudolocalization/override", false);
  1205. expansion_ratio = GLOBAL_DEF("internationalization/pseudolocalization/expansion_ratio", 0.0);
  1206. pseudolocalization_prefix = GLOBAL_DEF("internationalization/pseudolocalization/prefix", "[");
  1207. pseudolocalization_suffix = GLOBAL_DEF("internationalization/pseudolocalization/suffix", "]");
  1208. pseudolocalization_skip_placeholders_enabled = GLOBAL_DEF("internationalization/pseudolocalization/skip_placeholders", true);
  1209. #ifdef TOOLS_ENABLED
  1210. {
  1211. String options = "";
  1212. int idx = 0;
  1213. while (locale_list[idx]) {
  1214. if (idx > 0) {
  1215. options += ",";
  1216. }
  1217. options += locale_list[idx];
  1218. idx++;
  1219. }
  1220. ProjectSettings::get_singleton()->set_custom_property_info("internationalization/locale/fallback", PropertyInfo(Variant::STRING, "internationalization/locale/fallback", PROPERTY_HINT_ENUM, options));
  1221. }
  1222. #endif
  1223. }
  1224. void TranslationServer::set_tool_translation(const Ref<Translation> &p_translation) {
  1225. tool_translation = p_translation;
  1226. }
  1227. Ref<Translation> TranslationServer::get_tool_translation() const {
  1228. return tool_translation;
  1229. }
  1230. String TranslationServer::get_tool_locale() {
  1231. #ifdef TOOLS_ENABLED
  1232. if (TranslationServer::get_singleton()->get_tool_translation().is_valid() && (Engine::get_singleton()->is_editor_hint() || Main::is_project_manager())) {
  1233. return tool_translation->get_locale();
  1234. } else {
  1235. #else
  1236. {
  1237. #endif
  1238. return get_locale();
  1239. }
  1240. }
  1241. StringName TranslationServer::tool_translate(const StringName &p_message, const StringName &p_context) const {
  1242. if (tool_translation.is_valid()) {
  1243. StringName r = tool_translation->get_message(p_message, p_context);
  1244. if (r) {
  1245. return editor_pseudolocalization ? tool_pseudolocalize(r) : r;
  1246. }
  1247. }
  1248. return editor_pseudolocalization ? tool_pseudolocalize(p_message) : p_message;
  1249. }
  1250. StringName TranslationServer::tool_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
  1251. if (tool_translation.is_valid()) {
  1252. StringName r = tool_translation->get_plural_message(p_message, p_message_plural, p_n, p_context);
  1253. if (r) {
  1254. return r;
  1255. }
  1256. }
  1257. if (p_n == 1) {
  1258. return p_message;
  1259. }
  1260. return p_message_plural;
  1261. }
  1262. void TranslationServer::set_doc_translation(const Ref<Translation> &p_translation) {
  1263. doc_translation = p_translation;
  1264. }
  1265. StringName TranslationServer::doc_translate(const StringName &p_message, const StringName &p_context) const {
  1266. if (doc_translation.is_valid()) {
  1267. StringName r = doc_translation->get_message(p_message, p_context);
  1268. if (r) {
  1269. return r;
  1270. }
  1271. }
  1272. return p_message;
  1273. }
  1274. StringName TranslationServer::doc_translate_plural(const StringName &p_message, const StringName &p_message_plural, int p_n, const StringName &p_context) const {
  1275. if (doc_translation.is_valid()) {
  1276. StringName r = doc_translation->get_plural_message(p_message, p_message_plural, p_n, p_context);
  1277. if (r) {
  1278. return r;
  1279. }
  1280. }
  1281. if (p_n == 1) {
  1282. return p_message;
  1283. }
  1284. return p_message_plural;
  1285. }
  1286. bool TranslationServer::is_pseudolocalization_enabled() const {
  1287. return pseudolocalization_enabled;
  1288. }
  1289. void TranslationServer::set_pseudolocalization_enabled(bool p_enabled) {
  1290. pseudolocalization_enabled = p_enabled;
  1291. if (OS::get_singleton()->get_main_loop()) {
  1292. OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
  1293. }
  1294. ResourceLoader::reload_translation_remaps();
  1295. }
  1296. void TranslationServer::set_editor_pseudolocalization(bool p_enabled) {
  1297. editor_pseudolocalization = p_enabled;
  1298. }
  1299. void TranslationServer::reload_pseudolocalization() {
  1300. pseudolocalization_accents_enabled = GLOBAL_GET("internationalization/pseudolocalization/replace_with_accents");
  1301. pseudolocalization_double_vowels_enabled = GLOBAL_GET("internationalization/pseudolocalization/double_vowels");
  1302. pseudolocalization_fake_bidi_enabled = GLOBAL_GET("internationalization/pseudolocalization/fake_bidi");
  1303. pseudolocalization_override_enabled = GLOBAL_GET("internationalization/pseudolocalization/override");
  1304. expansion_ratio = GLOBAL_GET("internationalization/pseudolocalization/expansion_ratio");
  1305. pseudolocalization_prefix = GLOBAL_GET("internationalization/pseudolocalization/prefix");
  1306. pseudolocalization_suffix = GLOBAL_GET("internationalization/pseudolocalization/suffix");
  1307. pseudolocalization_skip_placeholders_enabled = GLOBAL_GET("internationalization/pseudolocalization/skip_placeholders");
  1308. if (OS::get_singleton()->get_main_loop()) {
  1309. OS::get_singleton()->get_main_loop()->notification(MainLoop::NOTIFICATION_TRANSLATION_CHANGED);
  1310. }
  1311. ResourceLoader::reload_translation_remaps();
  1312. }
  1313. StringName TranslationServer::pseudolocalize(const StringName &p_message) const {
  1314. String message = p_message;
  1315. int length = message.length();
  1316. if (pseudolocalization_override_enabled) {
  1317. message = get_override_string(message);
  1318. }
  1319. if (pseudolocalization_double_vowels_enabled) {
  1320. message = double_vowels(message);
  1321. }
  1322. if (pseudolocalization_accents_enabled) {
  1323. message = replace_with_accented_string(message);
  1324. }
  1325. if (pseudolocalization_fake_bidi_enabled) {
  1326. message = wrap_with_fakebidi_characters(message);
  1327. }
  1328. StringName res = add_padding(message, length);
  1329. return res;
  1330. }
  1331. StringName TranslationServer::tool_pseudolocalize(const StringName &p_message) const {
  1332. String message = p_message;
  1333. message = double_vowels(message);
  1334. message = replace_with_accented_string(message);
  1335. StringName res = "[!!! " + message + " !!!]";
  1336. return res;
  1337. }
  1338. String TranslationServer::get_override_string(String &p_message) const {
  1339. String res;
  1340. for (int i = 0; i < p_message.size(); i++) {
  1341. if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
  1342. res += p_message[i];
  1343. res += p_message[i + 1];
  1344. i++;
  1345. continue;
  1346. }
  1347. res += '*';
  1348. }
  1349. return res;
  1350. }
  1351. String TranslationServer::double_vowels(String &p_message) const {
  1352. String res;
  1353. for (int i = 0; i < p_message.size(); i++) {
  1354. if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
  1355. res += p_message[i];
  1356. res += p_message[i + 1];
  1357. i++;
  1358. continue;
  1359. }
  1360. res += p_message[i];
  1361. if (p_message[i] == 'a' || p_message[i] == 'e' || p_message[i] == 'i' || p_message[i] == 'o' || p_message[i] == 'u' ||
  1362. p_message[i] == 'A' || p_message[i] == 'E' || p_message[i] == 'I' || p_message[i] == 'O' || p_message[i] == 'U') {
  1363. res += p_message[i];
  1364. }
  1365. }
  1366. return res;
  1367. };
  1368. String TranslationServer::replace_with_accented_string(String &p_message) const {
  1369. String res;
  1370. for (int i = 0; i < p_message.size(); i++) {
  1371. if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
  1372. res += p_message[i];
  1373. res += p_message[i + 1];
  1374. i++;
  1375. continue;
  1376. }
  1377. const char32_t *accented = get_accented_version(p_message[i]);
  1378. if (accented) {
  1379. res += accented;
  1380. } else {
  1381. res += p_message[i];
  1382. }
  1383. }
  1384. return res;
  1385. }
  1386. String TranslationServer::wrap_with_fakebidi_characters(String &p_message) const {
  1387. String res;
  1388. char32_t fakebidiprefix = U'\u202e';
  1389. char32_t fakebidisuffix = U'\u202c';
  1390. res += fakebidiprefix;
  1391. // The fake bidi unicode gets popped at every newline so pushing it back at every newline.
  1392. for (int i = 0; i < p_message.size(); i++) {
  1393. if (p_message[i] == '\n') {
  1394. res += fakebidisuffix;
  1395. res += p_message[i];
  1396. res += fakebidiprefix;
  1397. } else if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) {
  1398. res += fakebidisuffix;
  1399. res += p_message[i];
  1400. res += p_message[i + 1];
  1401. res += fakebidiprefix;
  1402. i++;
  1403. } else {
  1404. res += p_message[i];
  1405. }
  1406. }
  1407. res += fakebidisuffix;
  1408. return res;
  1409. }
  1410. String TranslationServer::add_padding(String &p_message, int p_length) const {
  1411. String res;
  1412. String prefix = pseudolocalization_prefix;
  1413. String suffix;
  1414. for (int i = 0; i < p_length * expansion_ratio / 2; i++) {
  1415. prefix += "_";
  1416. suffix += "_";
  1417. }
  1418. suffix += pseudolocalization_suffix;
  1419. res += prefix;
  1420. res += p_message;
  1421. res += suffix;
  1422. return res;
  1423. }
  1424. const char32_t *TranslationServer::get_accented_version(char32_t p_character) const {
  1425. if (!((p_character >= 'a' && p_character <= 'z') || (p_character >= 'A' && p_character <= 'Z'))) {
  1426. return nullptr;
  1427. }
  1428. for (unsigned int i = 0; i < sizeof(_character_to_accented) / sizeof(_character_to_accented[0]); i++) {
  1429. if (_character_to_accented[i].character == p_character) {
  1430. return _character_to_accented[i].accented_character;
  1431. }
  1432. }
  1433. return nullptr;
  1434. }
  1435. bool TranslationServer::is_placeholder(String &p_message, int p_index) const {
  1436. return p_message[p_index] == '%' && p_index < p_message.size() - 1 &&
  1437. (p_message[p_index + 1] == 's' || p_message[p_index + 1] == 'c' || p_message[p_index + 1] == 'd' ||
  1438. p_message[p_index + 1] == 'o' || p_message[p_index + 1] == 'x' || p_message[p_index + 1] == 'X' || p_message[p_index + 1] == 'f');
  1439. }
  1440. void TranslationServer::_bind_methods() {
  1441. ClassDB::bind_method(D_METHOD("set_locale", "locale"), &TranslationServer::set_locale);
  1442. ClassDB::bind_method(D_METHOD("get_locale"), &TranslationServer::get_locale);
  1443. ClassDB::bind_method(D_METHOD("get_locale_name", "locale"), &TranslationServer::get_locale_name);
  1444. ClassDB::bind_method(D_METHOD("translate", "message", "context"), &TranslationServer::translate, DEFVAL(""));
  1445. ClassDB::bind_method(D_METHOD("translate_plural", "message", "plural_message", "n", "context"), &TranslationServer::translate_plural, DEFVAL(""));
  1446. ClassDB::bind_method(D_METHOD("add_translation", "translation"), &TranslationServer::add_translation);
  1447. ClassDB::bind_method(D_METHOD("remove_translation", "translation"), &TranslationServer::remove_translation);
  1448. ClassDB::bind_method(D_METHOD("get_translation_object", "locale"), &TranslationServer::get_translation_object);
  1449. ClassDB::bind_method(D_METHOD("clear"), &TranslationServer::clear);
  1450. ClassDB::bind_method(D_METHOD("get_loaded_locales"), &TranslationServer::get_loaded_locales);
  1451. ClassDB::bind_method(D_METHOD("is_pseudolocalization_enabled"), &TranslationServer::is_pseudolocalization_enabled);
  1452. ClassDB::bind_method(D_METHOD("set_pseudolocalization_enabled", "enabled"), &TranslationServer::set_pseudolocalization_enabled);
  1453. ClassDB::bind_method(D_METHOD("reload_pseudolocalization"), &TranslationServer::reload_pseudolocalization);
  1454. ClassDB::bind_method(D_METHOD("pseudolocalize", "message"), &TranslationServer::pseudolocalize);
  1455. ADD_PROPERTY(PropertyInfo(Variant::Type::BOOL, "pseudolocalization_enabled"), "set_pseudolocalization_enabled", "is_pseudolocalization_enabled");
  1456. }
  1457. void TranslationServer::load_translations() {
  1458. String locale = get_locale();
  1459. _load_translations("internationalization/locale/translations"); //all
  1460. _load_translations("internationalization/locale/translations_" + locale.substr(0, 2));
  1461. if (locale.substr(0, 2) != locale) {
  1462. _load_translations("internationalization/locale/translations_" + locale);
  1463. }
  1464. }
  1465. TranslationServer::TranslationServer() {
  1466. singleton = this;
  1467. for (int i = 0; locale_list[i]; ++i) {
  1468. locale_name_map.insert(locale_list[i], String::utf8(locale_names[i]));
  1469. }
  1470. }