diff --git a/core/string/locales.h b/core/string/locales.h index 6b684bf1123..e66a1079a79 100644 --- a/core/string/locales.h +++ b/core/string/locales.h @@ -469,7 +469,6 @@ static const char *language_list[][2] = { { "chy", "Cheyenne" }, { "cic", "Chickasaw" }, { "ckb", "Central Kurdish" }, - { "csb", "Kashubian" }, { "cmn", "Mandarin Chinese" }, { "co", "Corsican" }, { "cop", "Coptic" }, @@ -662,7 +661,6 @@ static const char *language_list[][2] = { { "kv", "Komi" }, { "kw", "Cornish" }, { "ky", "Kirghiz" }, - { "lag", "Langi" }, { "la", "Latin" }, { "lad", "Ladino" }, { "lag", "Langi" }, @@ -982,7 +980,630 @@ static const char *language_list[][2] = { { "zu", "Zulu" }, { "zun", "Zuni" }, { "zza", "Zaza" }, - { nullptr, nullptr } + { nullptr, nullptr }, +}; + +// Scripts used by languages. +// Reference: +// - https://www.unicode.org/cldr/charts/48/supplemental/languages_and_scripts.html + +static const char *language_script_list[][2] = { + { "aa", "Latn" }, + { "ab", "Cyrl" }, + { "ace", "Latn" }, + { "ach", "Latn" }, + { "ada", "Latn" }, + { "ady", "Cyrl" }, + { "ae", "Avst" }, + { "aeb", "Arab" }, + { "af", "Latn" }, + { "afh", "Latn" }, + { "agq", "Latn" }, + { "ain", "Kana Latn" }, + { "agr", "Latn" }, + { "ak", "Latn" }, + { "akk", "Xsux" }, + { "akz", "Latn" }, + { "ale", "Latn" }, + { "aln", "Latn" }, + { "alt", "Cyrl" }, + { "am", "Ethi" }, + { "an", "Latn" }, + { "ang", "Latn" }, + { "anp", "Deva" }, + { "ar", "Arab Syrc" }, + { "arc", "Armi Nbat Palm" }, + { "arn", "Latn" }, + { "aro", "Latn" }, + { "arp", "Latn" }, + { "arq", "Arab" }, + { "ars", "Arab" }, + { "arw", "Latn" }, + { "ary", "Arab" }, + { "arz", "Arab" }, + { "as", "Beng" }, + { "asa", "Latn" }, + { "ast", "Latn" }, + { "av", "Cyrl" }, + { "avk", "Latn" }, + { "awa", "Deva" }, + { "ayc", "Latn" }, + { "ay", "Latn" }, + { "az", "Latn Arab Cyrl" }, + { "ba", "Cyrl" }, + { "bal", "Arab Latn" }, + { "ban", "Latn Bali" }, + { "bar", "Latn" }, + { "bas", "Latn" }, + { "bax", "Bamu" }, + { "bbc", "Latn Batk" }, + { "bbj", "Latn" }, + { "be", "Cyrl" }, + { "bej", "Arab" }, + { "bem", "Latn" }, + { "ber", "Latn Arab Tfng" }, + { "bew", "Latn" }, + { "bez", "Latn" }, + { "bfd", "Latn" }, + { "bfq", "Taml" }, + { "bg", "Cyrl" }, + { "bhb", "Deva" }, + { "bgn", "Arab" }, + { "bho", "Deva" }, + { "bi", "Latn" }, + { "bik", "Latn" }, + { "bin", "Latn" }, + { "bjn", "Latn" }, + { "bkm", "Latn" }, + { "bla", "Latn" }, + { "bm", "Latn Nkoo" }, + { "bn", "Beng" }, + { "bo", "Tibt" }, + { "bpy", "Beng" }, + { "bqi", "Arab" }, + { "br", "Latn" }, + { "brh", "Arab Latn" }, + { "brx", "Deva" }, + { "bs", "Latn Cyrl" }, + { "bss", "Latn" }, + { "bua", "Cyrl" }, + { "bug", "Latn Bug" }, + { "bum", "Latn" }, + { "byn", "Ethi" }, + { "byv", "Latn" }, + { "ca", "Latn" }, + { "cad", "Latn" }, + { "car", "Latn" }, + { "cay", "Latn" }, + { "cch", "Latn" }, + { "ccp", "Cakm Beng" }, + { "ce", "Cyrl" }, + { "ceb", "Latn" }, + { "cgg", "Latn" }, + { "ch", "Latn" }, + { "chb", "Latn" }, + { "chg", "Arab" }, + { "chk", "Latn" }, + { "chm", "Cyrl" }, + { "chn", "Latn" }, + { "cho", "Latn" }, + { "chp", "Latn Cans" }, + { "chr", "Cher" }, + { "chy", "Latn" }, + { "cic", "Latn" }, + { "ckb", "Arab" }, + { "cmn", "Hant Hans Hani" }, + { "co", "Latn" }, + { "cop", "Copt Arab Grek" }, + { "cps", "Latn" }, + { "cr", "Cans Latn" }, + { "crh", "Cyrl" }, + { "crs", "Latn" }, + { "cs", "Latn" }, + { "csb", "Latn" }, + { "cu", "Cyrl" }, + { "cv", "Cyrl" }, + { "cy", "Latn" }, + { "da", "Latn" }, + { "dak", "Latn" }, + { "dar", "Cyrl" }, + { "dav", "Latn" }, + { "de", "Latn Runr" }, + { "del", "Latn" }, + { "den", "Latn Cans" }, + { "dgr", "Latn" }, + { "din", "Latn" }, + { "dje", "Latn" }, + { "doi", "Deva Arab Takr" }, + { "dsb", "Latn" }, + { "dtp", "Latn" }, + { "dua", "Latn" }, + { "dum", "Latn" }, + { "dv", "Thaa" }, + { "dyo", "Latn Arab" }, + { "dyu", "Latn" }, + { "dz", "Tibt" }, + { "dzg", "Latn" }, + { "ebu", "Latn" }, + { "ee", "Latn" }, + { "efi", "Latn" }, + { "egl", "Latn" }, + { "egy", "Egyp" }, + { "eka", "Latn" }, + { "el", "Grek" }, + { "elx", "Xsux" }, + { "en", "Latn Dsrt Shaw" }, + { "enm", "Latn" }, + { "eo", "Latn" }, + { "es", "Latn" }, + { "esu", "Latn" }, + { "et", "Latn" }, + { "eu", "Latn" }, + { "ewo", "Latn" }, + { "ext", "Latn" }, + { "fa", "Arab" }, + { "fan", "Latn" }, + { "fat", "Latn" }, + { "ff", "Latn Adlm" }, + { "fi", "Latn" }, + { "fil", "Latn Tglg" }, + { "fit", "Latn" }, + { "fj", "Latn" }, + { "fo", "Latn" }, + { "fon", "Latn" }, + { "fr", "Latn Dupl" }, + { "frc", "Latn" }, + { "frm", "Latn" }, + { "fro", "Latn" }, + { "frp", "Latn" }, + { "frr", "Latn" }, + { "frs", "Latn" }, + { "fur", "Latn" }, + { "fy", "Latn" }, + { "ga", "Latn" }, + { "gaa", "Latn" }, + { "gag", "Latn Cyrl" }, + { "gan", "Hans Hani" }, + { "gay", "Latn" }, + { "gba", "Latn" }, + { "gbz", "Arab" }, + { "gd", "Latn" }, + { "gez", "Ethi" }, + { "gil", "Latn" }, + { "gl", "Latn" }, + { "glk", "Arab" }, + { "gmh", "Latn" }, + { "gn", "Latn" }, + { "goh", "Latn" }, + { "gom", "Deav Latn Knda Mlym Arab" }, + { "gon", "Deva Telu" }, + { "gor", "Latn" }, + { "got", "Goth" }, + { "grb", "Latn" }, + { "grc", "Grek" }, + { "gsw", "Latn" }, + { "gu", "Gujr" }, + { "guc", "Latn" }, + { "gur", "Latn" }, + { "guz", "Latn" }, + { "gv", "Latn" }, + { "gwi", "Latn" }, + { "ha", "Latn Arab" }, + { "hai", "Latn" }, + { "hak", "Hans Hant Hani" }, + { "haw", "Latn" }, + { "he", "Hebr" }, + { "hi", "Deva Latn Mahj" }, + { "hif", "Deva Latn" }, + { "hil", "Latn" }, + { "hit", "Xsux" }, + { "hmn", "Latn Hmng" }, + { "ho", "Latn" }, + { "hne", "Deva" }, + { "hr", "Latn" }, + { "hsb", "Latn" }, + { "hsn", "Hans Hani" }, + { "ht", "Latn" }, + { "hu", "Latn" }, + { "hup", "Latn" }, + { "hus", "Latn" }, + { "hy", "Armn" }, + { "hz", "Latn" }, + { "ia", "Latn" }, + { "iba", "Latn" }, + { "ibb", "Latn" }, + { "id", "Latn Arab" }, + { "ie", "Latn" }, + { "ig", "Latn" }, + { "ii", "Yiii Latn" }, + { "ik", "Latn" }, + { "ilo", "Latn" }, + { "inh", "Cyrl" }, + { "io", "Latn" }, + { "is", "Latn" }, + { "it", "Latn" }, + { "iu", "Cans Latn" }, + { "izh", "Latn" }, + { "ja", "Jpan Hani Hira Kana" }, + { "jam", "Latn" }, + { "jbo", "Latn" }, + { "jgo", "Latn" }, + { "jmc", "Latn" }, + { "jpr", "Hebr" }, + { "jrb", "Hebr" }, + { "jut", "Latn" }, + { "jv", "Latn Java" }, + { "ka", "Geor" }, + { "kaa", "Cyrl Latn" }, + { "kab", "Latn" }, + { "kac", "Latn" }, + { "kaj", "Latn" }, + { "kam", "Latn" }, + { "kaw", "Bali Java Kawi" }, + { "kbd", "Cyrl" }, + { "kbl", "Arab" }, + { "kcg", "Latn" }, + { "kde", "Latn" }, + { "kea", "Latn" }, + { "ken", "Latn" }, + { "kfo", "Latn" }, + { "kg", "Latn" }, + { "kgp", "Latn" }, + { "kha", "Latn Beng" }, + { "kho", "Brah Khar" }, + { "khq", "Latn" }, + { "khw", "Arab" }, + { "ki", "Latn" }, + { "kiu", "Latn" }, + { "kj", "Latn" }, + { "kk", "Cyrl Arab" }, + { "kkj", "Latn" }, + { "kl", "Latn" }, + { "kln", "Latn" }, + { "km", "Khmr" }, + { "kmb", "Latn" }, + { "kn", "Knda" }, + { "ko", "Kore Hang Hani" }, + { "koi", "Cyrl" }, + { "kok", "Deva Latn" }, + { "kos", "Latn" }, + { "kpe", "Latn" }, + { "kr", "Latn" }, + { "krc", "Cyrl" }, + { "kri", "Latn" }, + { "krj", "Latn" }, + { "krl", "Latn" }, + { "kru", "Deva" }, + { "ks", "Arab Deva" }, + { "ksb", "Latn" }, + { "ksf", "Latn" }, + { "ksh", "Latn" }, + { "ku", "Latn Arab Cyrl" }, + { "kum", "Cyrl" }, + { "kut", "Latn" }, + { "kv", "Cyrl Perm" }, + { "kw", "Latn" }, + { "ky", "Cyrl Arab Latn" }, + { "la", "Latn" }, + { "lad", "Hebr" }, + { "lag", "Latn" }, + { "lah", "Arab" }, + { "lam", "Latn" }, + { "lb", "Latn" }, + { "lez", "Cyrl Aghb" }, + { "lfn", "Latn Cyrl" }, + { "lg", "Latn" }, + { "li", "Latn" }, + { "lij", "Latn" }, + { "liv", "Latn" }, + { "lkt", "Latn" }, + { "lmo", "Latn" }, + { "ln", "Latn" }, + { "lo", "Laoo" }, + { "lol", "Latn" }, + { "lou", "Latn" }, + { "loz", "Latn" }, + { "lrc", "Arab" }, + { "lt", "Latn" }, + { "ltg", "Latn" }, + { "lu", "Latn" }, + { "lua", "Latn" }, + { "lui", "Latn" }, + { "lun", "Latn" }, + { "luo", "Latn" }, + { "lus", "Beng" }, + { "luy", "Latn" }, + { "lv", "Latn" }, + { "lzh", "Hant" }, + { "lzz", "Latn Geor" }, + { "mad", "Latn" }, + { "maf", "Latn" }, + { "mag", "Deva" }, + { "mai", "Deva Tirh" }, + { "mak", "Latn Bugi" }, + { "man", "Latn Nkoo" }, + { "mas", "Latn" }, + { "mde", "Arab Latn" }, + { "mdf", "Cyrl" }, + { "mdr", "Latn Bugi" }, + { "men", "Latn Mend" }, + { "mer", "Latn" }, + { "mfe", "Latn" }, + { "mg", "Latn" }, + { "mga", "Latn" }, + { "mgh", "Latn" }, + { "mgo", "Latn" }, + { "mh", "Latn" }, + { "mhr", "Cyrl" }, + { "mi", "Latn" }, + { "mic", "Latn" }, + { "min", "Latn" }, + { "miq", "Latn" }, + { "mjw", "Latn" }, + { "mk", "Cyrl" }, + { "ml", "Mlym" }, + { "mn", "Cyrl Mong Phag" }, + { "mnc", "Mong" }, + { "mni", "Beng Mtei" }, + { "mnw", "Mymr" }, + { "mos", "Latn" }, + { "moh", "Latn" }, + { "mr", "Deva Modi" }, + { "mrj", "Cyrl" }, + { "ms", "Latn Arab" }, + { "mt", "Latn" }, + { "mua", "Latn" }, + { "mus", "Latn" }, + { "mwl", "Latn" }, + { "mwr", "Deva" }, + { "mwv", "Latn" }, + { "my", "Mymr" }, + { "mye", "Latn" }, + { "myv", "Cyrl" }, + { "mzn", "Arab" }, + { "na", "Latn" }, + { "nah", "Latn" }, + { "nan", "Hans Hant Hani" }, + { "nap", "Latn" }, + { "naq", "Latn" }, + { "nan", "Latn Hans Hant Hani" }, + { "nb", "Latn" }, + { "nd", "Latn" }, + { "nds", "Latn" }, + { "ne", "Deva" }, + { "new", "Deva" }, + { "nhn", "Latn" }, + { "ng", "Latn" }, + { "nia", "Latn" }, + { "niu", "Latn" }, + { "njo", "Latn" }, + { "nl", "Latn" }, + { "nmg", "Latn" }, + { "nn", "Latn" }, + { "nnh", "Latn" }, + { "nog", "Cyrl" }, + { "non", "Runr" }, + { "nov", "Latn" }, + { "nqo", "Nkoo" }, + { "nr", "Latn" }, + { "nso", "Latn" }, + { "nus", "Latn" }, + { "nv", "Latn" }, + { "nwc", "Ranj Newa Deva Sidd Brah" }, + { "ny", "Latn" }, + { "nym", "Latn" }, + { "nyn", "Latn" }, + { "nyo", "Latn" }, + { "nzi", "Latn" }, + { "oc", "Latn" }, + { "oj", "Cans Latn" }, + { "om", "Latn Ethi" }, + { "or", "Orya" }, + { "os", "Cyrl" }, + { "osa", "Osge Latn" }, + { "ota", "Arab" }, + { "pa", "Guru Arab" }, + { "pag", "Latn" }, + { "pal", "Phli Phlp" }, + { "pam", "Latn" }, + { "pap", "Latn" }, + { "pau", "Latn" }, + { "pcd", "Latn" }, + { "pcm", "Latn" }, + { "pdc", "Latn" }, + { "pdt", "Latn" }, + { "peo", "Xpeo" }, + { "pfl", "Latn" }, + { "phn", "Phnx" }, + { "pi", "Latn Deva Mymr Sinh Thai" }, + { "pl", "Latn" }, + { "pms", "Latn" }, + { "pnt", "Grek Cyrl Latn" }, + { "pon", "Latn" }, + { "pr", "Latn" }, + { "prg", "Latn" }, + { "pro", "Latn" }, + { "prs", "Arab" }, + { "ps", "Arab" }, + { "pt", "Latn" }, + { "qu", "Latn" }, + { "quc", "Latn" }, + { "qug", "Latn" }, + { "quy", "Latn" }, + { "quz", "Latn" }, + { "raj", "Deva" }, + { "rap", "Latn" }, + { "rar", "Latn" }, + { "rgn", "Latn" }, + { "rif", "Latn Tfng" }, + { "rm", "Latn" }, + { "rn", "Latn" }, + { "ro", "Latn Cyrl" }, + { "rof", "Latn" }, + { "rom", "Latn Cyrl" }, + { "rtm", "Latn" }, + { "ru", "Cyrl" }, + { "rue", "Cyrl" }, + { "rug", "Latn" }, + { "rup", "Latn" }, + { "rw", "Latn" }, + { "rwk", "Latn" }, + { "sa", "Deva Gran Shrd Sidd Sinh" }, + { "sad", "Latn" }, + { "sah", "Cyrl" }, + { "sam", "Samr Hebr" }, + { "saq", "Latn" }, + { "sas", "Latn" }, + { "sat", "Olck Beng Deva Latn Orya" }, + { "saz", "Saur" }, + { "sba", "Latn" }, + { "sbp", "Latn" }, + { "sc", "Latn" }, + { "scn", "Latn" }, + { "sco", "Latn" }, + { "sd", "Arab Deva Khoj Sind" }, + { "sdc", "Latn" }, + { "sdh", "Arab" }, + { "se", "Latn Cyrl Arab" }, + { "see", "Latn" }, + { "seh", "Latn" }, + { "sei", "Latn" }, + { "sel", "Cyrl" }, + { "ses", "Latn" }, + { "sg", "Latn" }, + { "sga", "Latn Ogam" }, + { "sgs", "Latn" }, + { "sh", "Latn Cyrl" }, + { "shi", "Tfng Latn Arab" }, + { "shn", "Mymr" }, + { "shs", "Latn Dupl" }, + { "shu", "Arab" }, + { "si", "Sinh" }, + { "sid", "Latn" }, + { "sk", "Latn" }, + { "sl", "Latn" }, + { "sli", "Latn" }, + { "sly", "Latn" }, + { "sm", "Latn" }, + { "sma", "Latn" }, + { "smj", "Latn" }, + { "smn", "Latn" }, + { "sms", "Latn" }, + { "sn", "Latn" }, + { "snk", "Latn" }, + { "so", "Latn Arab Osma" }, + { "sog", "Sogd Syrc Mani" }, + { "son", "Latn Arab" }, + { "sq", "Latn Elba" }, + { "sr", "Cyrl Latn" }, + { "srn", "Latn" }, + { "srr", "Latn" }, + { "ss", "Latn" }, + { "ssy", "Latn" }, + { "st", "Latn" }, + { "stq", "Latn" }, + { "su", "Latn Sund" }, + { "suk", "Latn" }, + { "sus", "Latn Arab" }, + { "sux", "Xsux" }, + { "sv", "Latn" }, + { "sw", "Latn" }, + { "swb", "Arab Latn" }, + { "swc", "Arab Latn" }, + { "syc", "Syrc" }, + { "syr", "Syrc" }, + { "szl", "Latn" }, + { "ta", "Taml" }, + { "tcy", "Knda" }, + { "te", "Telu" }, + { "tem", "Latn" }, + { "teo", "Latn" }, + { "ter", "Latn" }, + { "tet", "Latn" }, + { "tg", "Cyrl Arab Latn" }, + { "th", "Thai" }, + { "the", "Deva" }, + { "ti", "Ethi" }, + { "tig", "Ethi" }, + { "tiv", "Latn" }, + { "tk", "Latn Arab Cyrl" }, + { "tkl", "Latn" }, + { "tkr", "Latn Cyrl" }, + { "tl", "Latn Arab Blis" }, + { "tlh", "Piqd Latn" }, + { "tli", "Latn" }, + { "tly", "Latn Arab Cyrl" }, + { "tmh", "Latn" }, + { "tn", "Latn" }, + { "to", "Latn" }, + { "tog", "Latn" }, + { "tpi", "Latn" }, + { "tr", "Latn Arab" }, + { "tru", "Latn Syrc" }, + { "trv", "Latn" }, + { "ts", "Latn" }, + { "tsd", "Grek" }, + { "tsi", "Latn" }, + { "tt", "Cyrl" }, + { "ttt", "Latn Arab Cyrl" }, + { "tum", "Latn" }, + { "tvl", "Latn" }, + { "tw", "Latn" }, + { "twq", "Latn" }, + { "ty", "Latn" }, + { "tyv", "Cyrl" }, + { "tzm", "Latn Tfng" }, + { "udm", "Cyrl Latn" }, + { "ug", "Arab Cyrl Latn" }, + { "uga", "Ugar" }, + { "uk", "Cyrl" }, + { "umb", "Latn" }, + { "unm", "Latn" }, + { "ur", "Arab" }, + { "uz", "Latn Cyrl Arab" }, + { "vai", "Vaii Latn" }, + { "ve", "Latn" }, + { "vec", "Latn" }, + { "vep", "Latn" }, + { "vi", "Latn Hani" }, + { "vls", "Latn" }, + { "vmf", "Latn" }, + { "vo", "Latn" }, + { "vot", "Latn" }, + { "vro", "Latn" }, + { "vun", "Latn" }, + { "wa", "Latn" }, + { "wae", "Latn" }, + { "wal", "Ethi" }, + { "war", "Latn" }, + { "was", "Latn" }, + { "wbp", "Latn" }, + { "wo", "Latn Arab" }, + { "wuu", "Hans Hani" }, + { "xal", "Cyrl" }, + { "xh", "Latn" }, + { "xmf", "Geor" }, + { "xog", "Latn" }, + { "yao", "Latn" }, + { "yap", "Latn" }, + { "yav", "Latn" }, + { "ybb", "Latn" }, + { "yi", "Hebr" }, + { "yo", "Latn" }, + { "yrl", "Latn" }, + { "yue", "Hant Hans Hani" }, + { "yuw", "Latn" }, + { "za", "Latn Hans Hani" }, + { "zap", "Latn" }, + { "zbl", "Blis" }, + { "zea", "Latn" }, + { "zen", "Tfng" }, + { "zgh", "Tfng" }, + { "zh", "Hans Hant Hani Bopo Latn Phag" }, + { "zu", "Latn" }, + { "zun", "Latn" }, + { "zza", "Latn" }, + { nullptr, nullptr }, }; // Additional regional variants. diff --git a/core/string/translation_server.cpp b/core/string/translation_server.cpp index 890338a08ed..d33bad67b2b 100644 --- a/core/string/translation_server.cpp +++ b/core/string/translation_server.cpp @@ -89,6 +89,18 @@ void TranslationServer::init_locale_info() { idx++; } + // Init locale scripts. + language_script_map.clear(); + idx = 0; + while (language_script_list[idx][0] != nullptr) { + HashSet &set = language_script_map[language_script_list[idx][0]]; + Vector scripts = String(language_script_list[idx][1]).split(" "); + for (const String &s : scripts) { + set.insert(s); + } + idx++; + } + // Init country names. country_name_map.clear(); idx = 0; @@ -492,6 +504,17 @@ String TranslationServer::get_fallback_locale() const { return fallback; } +bool TranslationServer::is_script_suppored_by_locale(const String &p_locale, const String &p_script) const { + Locale l = Locale(*this, p_locale, true); + if (l.script == p_script) { + return true; + } + if (!language_script_map.has(l.language)) { + return false; + } + return language_script_map[l.language].has(p_script); +} + PackedStringArray TranslationServer::get_loaded_locales() const { return main_domain->get_loaded_locales(); } diff --git a/core/string/translation_server.h b/core/string/translation_server.h index 0528f6e2d2e..aa64c81bc88 100644 --- a/core/string/translation_server.h +++ b/core/string/translation_server.h @@ -92,6 +92,7 @@ class TranslationServer : public Object { static inline HashMap variant_map; static inline HashMap plural_rules_map; static inline HashMap num_system_map; + static inline HashMap> language_script_map; void init_locale_info(); @@ -132,6 +133,8 @@ public: String get_locale_name(const String &p_locale) const; String get_plural_rules(const String &p_locale) const; + bool is_script_suppored_by_locale(const String &p_locale, const String &p_script) const; + PackedStringArray get_loaded_locales() const; void add_translation(const Ref &p_translation); diff --git a/editor/themes/editor_fonts.cpp b/editor/themes/editor_fonts.cpp index 91810c923c7..feee442396d 100644 --- a/editor/themes/editor_fonts.cpp +++ b/editor/themes/editor_fonts.cpp @@ -32,6 +32,7 @@ #include "core/io/dir_access.h" #include "core/os/os.h" +#include "core/string/translation_server.h" #include "editor/editor_string_names.h" #include "editor/settings/editor_settings.h" #include "editor/themes/builtin_fonts.gen.h" @@ -164,13 +165,34 @@ void editor_register_fonts(const Ref &p_theme) { String noto_cjk_path; String noto_cjk_bold_path; - String var_suffix[] = { "HK", "KR", "SC", "TC", "JP" }; // Note: All Noto Sans CJK versions support all glyph variations, it should not match current locale. - for (size_t i = 0; i < std_size(var_suffix); i++) { - if (noto_cjk_path.is_empty()) { - noto_cjk_path = OS::get_singleton()->get_system_font_path("Noto Sans CJK " + var_suffix[i], 400, 100); + { + Vector var_suffix; + + // Note: Most Noto Sans CJK versions support all glyph variations, but select the best matching one in case it's not. + String locale = TranslationServer::get_singleton()->get_tool_locale(); + if (!locale.begins_with("zh") && !locale.begins_with("ja") && !locale.begins_with("ko")) { + locale = OS::get_singleton()->get_locale(); } - if (noto_cjk_bold_path.is_empty()) { - noto_cjk_bold_path = OS::get_singleton()->get_system_font_path("Noto Sans CJK " + var_suffix[i], 800, 100); + if (locale.begins_with("zh") && (locale.contains("Hans") || locale.contains("CN") || locale.contains("SG"))) { + var_suffix = { "SC", "TC", "HK", "KR", "JP" }; + } else if (locale.begins_with("zh") && locale.contains("HK")) { + var_suffix = { "HK", "TC", "SC", "KR", "JP" }; + } else if (locale.begins_with("zh") && (locale.contains("Hant") || locale.contains("MO") || locale.contains("TW"))) { + var_suffix = { "TC", "HK", "SC", "KR", "JP" }; + } else if (locale.begins_with("ko")) { + var_suffix = { "KR", "HK", "SC", "TC", "JP" }; + } else if (locale.begins_with("ko")) { + var_suffix = { "JP", "HK", "KR", "SC", "TC" }; + } else { + var_suffix = { "HK", "KR", "SC", "TC", "JP" }; + } + for (int64_t i = 0; i < var_suffix.size(); i++) { + if (noto_cjk_path.is_empty()) { + noto_cjk_path = OS::get_singleton()->get_system_font_path("Noto Sans CJK " + var_suffix[i], 400, 100); + } + if (noto_cjk_bold_path.is_empty()) { + noto_cjk_bold_path = OS::get_singleton()->get_system_font_path("Noto Sans CJK " + var_suffix[i], 800, 100); + } } } @@ -190,7 +212,15 @@ void editor_register_fonts(const Ref &p_theme) { load_external_font(noto_cjk_path, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, false, &fallbacks); } Ref fallback_font = load_internal_font(_font_DroidSansFallback, _font_DroidSansFallback_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, false, &fallbacks); + fallback_font->set_language_support_override("ja", false); + fallback_font->set_language_support_override("zh", true); + fallback_font->set_language_support_override("ko", true); + fallback_font->set_language_support_override("*", false); Ref japanese_font = load_internal_font(_font_DroidSansJapanese, _font_DroidSansJapanese_size, font_hinting, font_antialiasing, true, font_subpixel_positioning, font_disable_embedded_bitmaps, false, &fallbacks); + japanese_font->set_language_support_override("ja", true); + japanese_font->set_language_support_override("zh", false); + japanese_font->set_language_support_override("ko", false); + japanese_font->set_language_support_override("*", false); default_font->set_fallbacks(fallbacks); default_font_msdf->set_fallbacks(fallbacks); diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index 55303452dea..2f05757f41e 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -4240,6 +4240,9 @@ bool TextServerAdvanced::_font_is_language_supported(const RID &p_font_rid, cons if (fd->language_support_overrides.has(p_language)) { return fd->language_support_overrides[p_language]; } else { + if (fd->language_support_overrides.has("*")) { + return fd->language_support_overrides["*"]; + } return true; } } @@ -4288,6 +4291,9 @@ bool TextServerAdvanced::_font_is_script_supported(const RID &p_font_rid, const if (fd->script_support_overrides.has(p_script)) { return fd->script_support_overrides[p_script]; } else { + if (fd->script_support_overrides.has("*")) { + return fd->script_support_overrides["*"]; + } Vector2i size = _get_size(fd, 16); FontForSizeAdvanced *ffsd = nullptr; ERR_FAIL_COND_V(!_ensure_cache_for_size(fd, size, ffsd), false); @@ -6756,7 +6762,7 @@ UBreakIterator *TextServerAdvanced::_create_line_break_iterator_for_locale(const return ubrk_clone(bi, r_err); } -void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, FontPriorityList &p_fonts, int64_t p_span, int64_t p_fb_index, int64_t p_prev_start, int64_t p_prev_end, RID p_prev_font) { +void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, const String &p_language, hb_script_t p_script, hb_direction_t p_direction, FontPriorityList &p_fonts, int64_t p_span, int64_t p_fb_index, int64_t p_prev_start, int64_t p_prev_end, RID p_prev_font) { RID f; int fs = p_sd->spans[p_span].font_size; if (p_fb_index >= 0 && p_fb_index < p_fonts.size()) { @@ -6779,7 +6785,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star String script_code = String(scr_buffer); String text = p_sd->text.substr(p_start, next - p_start); - f = _find_sys_font_for_text(p_fonts[0], script_code, p_sd->spans[p_span].language, text); + f = _find_sys_font_for_text(p_fonts[0], script_code, p_language, text); } } @@ -6874,7 +6880,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star if (p_script == HB_TAG('Z', 's', 'y', 'e') && !color && _font_is_allow_system_fallback(p_fonts[0])) { // Color emoji is requested, skip non-color font. - _shape_run(p_sd, p_start, p_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1, p_start, p_end, f); + _shape_run(p_sd, p_start, p_end, p_language, p_script, p_direction, p_fonts, p_span, p_fb_index + 1, p_start, p_end, f); return; } @@ -6900,13 +6906,8 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star hb_buffer_set_flags(p_sd->hb_buffer, (hb_buffer_flags_t)flags); hb_buffer_set_script(p_sd->hb_buffer, (p_script == HB_TAG('Z', 's', 'y', 'e')) ? HB_SCRIPT_COMMON : p_script); - if (p_sd->spans[p_span].language.is_empty()) { - hb_language_t lang = hb_language_from_string(TranslationServer::get_singleton()->get_tool_locale().ascii().get_data(), -1); - hb_buffer_set_language(p_sd->hb_buffer, lang); - } else { - hb_language_t lang = hb_language_from_string(p_sd->spans[p_span].language.ascii().get_data(), -1); - hb_buffer_set_language(p_sd->hb_buffer, lang); - } + hb_language_t lang = hb_language_from_string(p_language.ascii().get_data(), -1); + hb_buffer_set_language(p_sd->hb_buffer, lang); hb_buffer_add_utf32(p_sd->hb_buffer, (const uint32_t *)p_sd->text.ptr(), p_sd->text.length(), p_start, p_end - p_start); @@ -7090,7 +7091,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star for (unsigned int i = 0; i < glyph_count; i++) { if ((w[i].flags & GRAPHEME_IS_VALID) == GRAPHEME_IS_VALID) { if (failed_subrun_start != p_end + 1) { - _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1, p_start, p_end, (p_fb_index >= p_fonts.size()) ? f : RID()); + _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_language, p_script, p_direction, p_fonts, p_span, p_fb_index + 1, p_start, p_end, (p_fb_index >= p_fonts.size()) ? f : RID()); failed_subrun_start = p_end + 1; failed_subrun_end = p_start; } @@ -7121,7 +7122,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star } memfree(w); if (failed_subrun_start != p_end + 1) { - _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1, p_start, p_end, (p_fb_index >= p_fonts.size()) ? f : RID()); + _shape_run(p_sd, failed_subrun_start, failed_subrun_end, p_language, p_script, p_direction, p_fonts, p_span, p_fb_index + 1, p_start, p_end, (p_fb_index >= p_fonts.size()) ? f : RID()); } p_sd->ascent = MAX(p_sd->ascent, _font_get_ascent(f, fs) + _font_get_spacing(f, SPACING_TOP)); p_sd->descent = MAX(p_sd->descent, _font_get_descent(f, fs) + _font_get_spacing(f, SPACING_BOTTOM)); @@ -7143,7 +7144,7 @@ void TextServerAdvanced::_shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_star p_sd->upos = MAX(p_sd->upos, _font_get_underline_position(f, fs)); p_sd->uthk = MAX(p_sd->uthk, _font_get_underline_thickness(f, fs)); } else { - _shape_run(p_sd, p_start, p_end, p_script, p_direction, p_fonts, p_span, p_fb_index + 1, p_start, p_end, f); + _shape_run(p_sd, p_start, p_end, p_language, p_script, p_direction, p_fonts, p_span, p_fb_index + 1, p_start, p_end, f); } } } @@ -7172,6 +7173,8 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) { return true; } + const String &project_locale = TranslationServer::get_singleton()->get_tool_locale(); + sd->utf16 = sd->text.utf16(); const UChar *data = sd->utf16.get_data(); @@ -7339,8 +7342,18 @@ bool TextServerAdvanced::_shaped_text_shape(const RID &p_shaped) { } sd->glyphs.push_back(gl); } else { - FontPriorityList fonts(this, span.fonts, span.language, script_code, sd->script_iter->script_ranges[j].script == HB_TAG('Z', 's', 'y', 'e')); - _shape_run(sd, MAX(span.start - sd->start, script_run_start), MIN(span.end - sd->start, script_run_end), sd->script_iter->script_ranges[j].script, bidi_run_direction, fonts, k, 0, 0, 0, RID()); + // Select best matching language for the run. + String language = span.language; + if (!language.contains("force")) { + if (language.is_empty() || !TranslationServer::get_singleton()->is_script_suppored_by_locale(language, script_code)) { + language = project_locale; + if (language.is_empty() || !TranslationServer::get_singleton()->is_script_suppored_by_locale(language, script_code)) { + language = os_locale; + } + } + } + FontPriorityList fonts(this, span.fonts, language.left(3).remove_char('_'), script_code, sd->script_iter->script_ranges[j].script == HB_TAG('Z', 's', 'y', 'e')); + _shape_run(sd, MAX(span.start - sd->start, script_run_start), MIN(span.end - sd->start, script_run_end), language, sd->script_iter->script_ranges[j].script, bidi_run_direction, fonts, k, 0, 0, 0, RID()); } } } @@ -8090,6 +8103,8 @@ void TextServerAdvanced::_update_settings() { } TextServerAdvanced::TextServerAdvanced() { + os_locale = OS::get_singleton()->get_locale(); + _insert_feature_sets(); _bmp_create_font_funcs(); _update_settings(); diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index 448a365fdfd..5c0702d8511 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -703,8 +703,8 @@ class TextServerAdvanced : public TextServerExtension { int current_priority = 0; uint32_t current_index = 0; uint32_t font_count = 0; - const String *language; - const String *script_code; + String language; + String script_code; bool color = false; LocalVector> unprocessed_fonts; LocalVector fonts; @@ -712,8 +712,8 @@ class TextServerAdvanced : public TextServerExtension { FontPriorityList(const TextServerAdvanced *p_text_server, const Array &p_fonts, const String &p_language, const String &p_script_code, bool p_color) { text_server = p_text_server; - language = &p_language; - script_code = &p_script_code; + language = p_language; + script_code = p_script_code; font_count = p_fonts.size(); color = p_color; @@ -738,7 +738,7 @@ class TextServerAdvanced : public TextServerExtension { if (color && text_server->_font_is_color(p_font)) { return 0; } - return text_server->_font_is_script_supported(p_font, *script_code) ? (text_server->_font_is_language_supported(p_font, *language) ? 0 : 1) : 2; + return text_server->_font_is_script_supported(p_font, script_code) ? (text_server->_font_is_language_supported(p_font, language) ? 0 : 1) : 2; } RID operator[](uint32_t p_index) { @@ -767,12 +767,14 @@ class TextServerAdvanced : public TextServerExtension { return RID(); } }; - void _shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, hb_script_t p_script, hb_direction_t p_direction, FontPriorityList &p_fonts, int64_t p_span, int64_t p_fb_index, int64_t p_prev_start, int64_t p_prev_end, RID p_prev_font); + void _shape_run(ShapedTextDataAdvanced *p_sd, int64_t p_start, int64_t p_end, const String &p_language, hb_script_t p_script, hb_direction_t p_direction, FontPriorityList &p_fonts, int64_t p_span, int64_t p_fb_index, int64_t p_prev_start, int64_t p_prev_end, RID p_prev_font); Glyph _shape_single_glyph(ShapedTextDataAdvanced *p_sd, char32_t p_char, hb_script_t p_script, hb_direction_t p_direction, const RID &p_font, int64_t p_font_size); _FORCE_INLINE_ RID _find_sys_font_for_text(const RID &p_fdef, const String &p_script_code, const String &p_language, const String &p_text); _FORCE_INLINE_ void _add_features(const Dictionary &p_source, Vector &r_ftrs); + String os_locale; + Mutex ft_mutex; // HarfBuzz bitmap font interface. diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index 7edd1ed8487..7f15af7abeb 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -3131,6 +3131,9 @@ bool TextServerFallback::_font_is_language_supported(const RID &p_font_rid, cons if (fd->language_support_overrides.has(p_language)) { return fd->language_support_overrides[p_language]; } else { + if (fd->language_support_overrides.has("*")) { + return fd->language_support_overrides["*"]; + } return true; } } @@ -3179,6 +3182,9 @@ bool TextServerFallback::_font_is_script_supported(const RID &p_font_rid, const if (fd->script_support_overrides.has(p_script)) { return fd->script_support_overrides[p_script]; } else { + if (fd->script_support_overrides.has("*")) { + return fd->script_support_overrides["*"]; + } return true; } } diff --git a/scene/resources/font.cpp b/scene/resources/font.cpp index c8629b0e98c..0a1104f9ced 100644 --- a/scene/resources/font.cpp +++ b/scene/resources/font.cpp @@ -609,6 +609,13 @@ _FORCE_INLINE_ void FontFile::_ensure_rid(int p_cache_index, int p_make_linked_f TS->font_set_subpixel_positioning(cache[p_cache_index], subpixel_positioning); TS->font_set_keep_rounding_remainders(cache[p_cache_index], keep_rounding_remainders); TS->font_set_oversampling(cache[p_cache_index], oversampling_override); + for (const KeyValue &E : script_support_overrides) { + TS->font_set_script_support_override(cache[p_cache_index], E.key, E.value); + } + for (const KeyValue &E : language_support_overrides) { + TS->font_set_language_support_override(cache[p_cache_index], E.key, E.value); + } + TS->font_set_opentype_feature_overrides(cache[p_cache_index], feature_overrides); } } } @@ -2770,53 +2777,83 @@ void FontFile::render_glyph(int p_cache_index, const Vector2i &p_size, int32_t p } void FontFile::set_language_support_override(const String &p_language, bool p_supported) { - _ensure_rid(0); - TS->font_set_language_support_override(cache[0], p_language, p_supported); + language_support_overrides[p_language] = p_supported; + for (int i = 0; i < cache.size(); i++) { + _ensure_rid(i); + TS->font_set_language_support_override(cache[i], p_language, p_supported); + } } bool FontFile::get_language_support_override(const String &p_language) const { - _ensure_rid(0); - return TS->font_get_language_support_override(cache[0], p_language); + if (language_support_overrides.has(p_language)) { + return language_support_overrides[p_language]; + } else { + return false; + } } void FontFile::remove_language_support_override(const String &p_language) { - _ensure_rid(0); - TS->font_remove_language_support_override(cache[0], p_language); + if (language_support_overrides.has(p_language)) { + language_support_overrides.erase(p_language); + for (int i = 0; i < cache.size(); i++) { + _ensure_rid(i); + TS->font_remove_language_support_override(cache[i], p_language); + } + } } Vector FontFile::get_language_support_overrides() const { - _ensure_rid(0); - return TS->font_get_language_support_overrides(cache[0]); + PackedStringArray out; + for (const KeyValue &E : language_support_overrides) { + out.push_back(E.key); + } + return out; } void FontFile::set_script_support_override(const String &p_script, bool p_supported) { - _ensure_rid(0); - TS->font_set_script_support_override(cache[0], p_script, p_supported); + script_support_overrides[p_script] = p_supported; + for (int i = 0; i < cache.size(); i++) { + _ensure_rid(i); + TS->font_set_script_support_override(cache[i], p_script, p_supported); + } } bool FontFile::get_script_support_override(const String &p_script) const { - _ensure_rid(0); - return TS->font_get_script_support_override(cache[0], p_script); + if (script_support_overrides.has(p_script)) { + return script_support_overrides[p_script]; + } else { + return false; + } } void FontFile::remove_script_support_override(const String &p_script) { - _ensure_rid(0); - TS->font_remove_script_support_override(cache[0], p_script); + if (script_support_overrides.has(p_script)) { + script_support_overrides.erase(p_script); + for (int i = 0; i < cache.size(); i++) { + _ensure_rid(i); + TS->font_remove_script_support_override(cache[i], p_script); + } + } } Vector FontFile::get_script_support_overrides() const { - _ensure_rid(0); - return TS->font_get_script_support_overrides(cache[0]); + PackedStringArray out; + for (const KeyValue &E : script_support_overrides) { + out.push_back(E.key); + } + return out; } void FontFile::set_opentype_feature_overrides(const Dictionary &p_overrides) { - _ensure_rid(0); - TS->font_set_opentype_feature_overrides(cache[0], p_overrides); + feature_overrides = p_overrides; + for (int i = 0; i < cache.size(); i++) { + _ensure_rid(i); + TS->font_set_opentype_feature_overrides(cache[i], p_overrides); + } } Dictionary FontFile::get_opentype_feature_overrides() const { - _ensure_rid(0); - return TS->font_get_opentype_feature_overrides(cache[0]); + return feature_overrides; } int32_t FontFile::get_glyph_index(int p_size, char32_t p_char, char32_t p_variation_selector) const { diff --git a/scene/resources/font.h b/scene/resources/font.h index 843ba2d9f80..741a6b7eecf 100644 --- a/scene/resources/font.h +++ b/scene/resources/font.h @@ -205,6 +205,10 @@ class FontFile : public Font { bool keep_rounding_remainders = true; double oversampling_override = 0.0; + HashMap language_support_overrides; + HashMap script_support_overrides; + Dictionary feature_overrides; + #ifndef DISABLE_DEPRECATED real_t bmp_height = 0.0; real_t bmp_ascent = 0.0;