' Maps 3-letter ISO 639-2 codes (T = terminological, B = bibliographic) and
' deprecated ISO 639-1 codes to the 2-letter base codes used by the app's
' translation system. Only languages the app supports as a UI locale are
' covered (see scripts/update-translations.cjs LANGUAGE_METADATA).
'
' Why this exists: ffmpeg/Jellyfin pass through whatever the container
' metadata holds — most modern files use 639-2/T (e.g., "fra"), older or
' hand-tagged files use 639-2/B (e.g., "fre"), and some use 639-1 ("fr"
' already, no alias needed). MediaStream.Language is therefore not a single
' standardized format; this map normalizes to one canonical form.
'
' 3-letter codes that have no 2-letter ISO 639-1 equivalent (ckb, fil, gsw,
' jbo, kab) are not aliased here — they're looked up directly in
' languageTranslationKeys() under their 3-letter form.
'
' Cached on the caller's m scope after first build to avoid reallocating
' on every audio/subtitle render.
'
' @returns {object} - AssocArray of 3-letter alias to 2-letter base code
function mediaLanguageAliases() as object
if isValid(m.mediaLanguageAliasesCache) then return m.mediaLanguageAliasesCache
m.mediaLanguageAliasesCache = {
"afr": "af",
"ara": "ar",
"asm": "as",
"bel": "be",
"bul": "bg",
"ben": "bn",
"bre": "br",
"cat": "ca",
"ces": "cs", "cze": "cs",
"cym": "cy", "wel": "cy",
"dan": "da",
"deu": "de", "ger": "de",
"div": "dv",
"ell": "el", "gre": "el",
"eng": "en",
"epo": "eo",
"spa": "es",
"est": "et",
"eus": "eu", "baq": "eu",
"fas": "fa", "per": "fa",
"fin": "fi",
"fao": "fo",
"fra": "fr", "fre": "fr",
"frc": "fr",
"gle": "ga",
"glg": "gl",
"guj": "gu",
"heb": "he", "iw": "he",
"hin": "hi",
"hrv": "hr",
"hat": "ht",
"hun": "hu",
"hye": "hy", "arm": "hy",
"ind": "id", "in": "id",
"isl": "is", "ice": "is",
"ita": "it",
"jpn": "ja",
"ji": "yid",
"kat": "ka", "geo": "ka",
"kaz": "kk",
"kan": "kn",
"kor": "ko",
"cor": "kw",
"ltz": "lb",
"lit": "lt",
"lav": "lv",
"mlg": "mg",
"mri": "mi", "mao": "mi",
"mkd": "mk", "mac": "mk",
"mal": "ml",
"mon": "mn",
"mar": "mr",
"msa": "ms", "may": "ms",
"mlt": "mt",
"mya": "my", "bur": "my",
"nob": "nb",
"nep": "ne",
"nld": "nl", "dut": "nl",
"nno": "nn",
"nor": "no",
"pan": "pa",
"pob": "pt",
"pol": "pl",
"por": "pt",
"ron": "ro", "rum": "ro",
"rus": "ru",
"sin": "si",
"slk": "sk", "slo": "sk",
"slv": "sl",
"som": "so",
"sqi": "sq", "alb": "sq",
"srp": "sr",
"swe": "sv",
"tam": "ta",
"tel": "te",
"tha": "th",
"tur": "tr",
"uig": "ug",
"ukr": "uk",
"urd": "ur",
"uzb": "uz",
"vie": "vi",
"zho": "zh", "chi": "zh",
"zul": "zu"
}
return m.mediaLanguageAliasesCache
end function
' Maps a normalized 2-letter base code (or a 3-letter code for languages
' without a 639-1 equivalent) to the translation key used by translate()
' for the localized language name. Keys must exist in locale/custom/en_US.json.
'
' Region/script suffixes (e.g., "_BR", "_Hant") are stripped before lookup
' by resolveLanguageName(), so en_GB and en_US both land at "en" → LanguageEn.
'
' Cached on the caller's m scope after first build.
'
' @returns {object} - AssocArray of base code to translationKeys.* string
function languageTranslationKeys() as object
if isValid(m.languageTranslationKeysCache) then return m.languageTranslationKeysCache
m.languageTranslationKeysCache = {
"af": translationKeys.LanguageAf,
"ar": translationKeys.LanguageAr,
"as": translationKeys.LanguageAs,
"be": translationKeys.LanguageBe,
"bg": translationKeys.LanguageBg,
"bn": translationKeys.LanguageBn,
"br": translationKeys.LanguageBr,
"ca": translationKeys.LanguageCa,
"ckb": translationKeys.LanguageCkb,
"cs": translationKeys.LanguageCs,
"cy": translationKeys.LanguageCy,
"da": translationKeys.LanguageDa,
"de": translationKeys.LanguageDe,
"dv": translationKeys.LanguageDv,
"el": translationKeys.LanguageEl,
"en": translationKeys.LanguageEn,
"eo": translationKeys.LanguageEo,
"es": translationKeys.LanguageEs,
"et": translationKeys.LanguageEt,
"eu": translationKeys.LanguageEu,
"fa": translationKeys.LanguageFa,
"fi": translationKeys.LanguageFi,
"fil": translationKeys.LanguageFil,
"fo": translationKeys.LanguageFo,
"fr": translationKeys.LanguageFr,
"ga": translationKeys.LanguageGa,
"gl": translationKeys.LanguageGl,
"gsw": translationKeys.LanguageGsw,
"gu": translationKeys.LanguageGu,
"he": translationKeys.LanguageHe,
"hi": translationKeys.LanguageHi,
"hr": translationKeys.LanguageHr,
"ht": translationKeys.LanguageHt,
"hu": translationKeys.LanguageHu,
"hy": translationKeys.LanguageHy,
"id": translationKeys.LanguageId,
"is": translationKeys.LanguageIs,
"it": translationKeys.LanguageIt,
"ja": translationKeys.LanguageJa,
"jbo": translationKeys.LanguageJbo,
"ka": translationKeys.LanguageKa,
"kab": translationKeys.LanguageKab,
"kk": translationKeys.LanguageKk,
"kn": translationKeys.LanguageKn,
"ko": translationKeys.LanguageKo,
"kw": translationKeys.LanguageKw,
"lb": translationKeys.LanguageLb,
"lt": translationKeys.LanguageLt,
"lv": translationKeys.LanguageLv,
"mg": translationKeys.LanguageMg,
"mi": translationKeys.LanguageMi,
"mk": translationKeys.LanguageMk,
"ml": translationKeys.LanguageMl,
"mn": translationKeys.LanguageMn,
"mr": translationKeys.LanguageMr,
"ms": translationKeys.LanguageMs,
"mt": translationKeys.LanguageMt,
"my": translationKeys.LanguageMy,
"nb": translationKeys.LanguageNb,
"ne": translationKeys.LanguageNe,
"nl": translationKeys.LanguageNl,
"nn": translationKeys.LanguageNn,
"no": translationKeys.LanguageNo,
"pa": translationKeys.LanguagePa,
"pl": translationKeys.LanguagePl,
"pt": translationKeys.LanguagePt,
"ro": translationKeys.LanguageRo,
"ru": translationKeys.LanguageRu,
"si": translationKeys.LanguageSi,
"sk": translationKeys.LanguageSk,
"sl": translationKeys.LanguageSl,
"so": translationKeys.LanguageSo,
"sq": translationKeys.LanguageSq,
"sr": translationKeys.LanguageSr,
"sv": translationKeys.LanguageSv,
"ta": translationKeys.LanguageTa,
"te": translationKeys.LanguageTe,
"th": translationKeys.LanguageTh,
"tr": translationKeys.LanguageTr,
"ug": translationKeys.LanguageUg,
"uk": translationKeys.LanguageUk,
"ur": translationKeys.LanguageUr,
"uz": translationKeys.LanguageUz,
"vi": translationKeys.LanguageVi,
"zh": translationKeys.LanguageZh,
"zu": translationKeys.LanguageZu
}
return m.languageTranslationKeysCache
end function
' Comprehensive English-name fallback for ISO 639-2 codes that the app does
' NOT have a UI translation for (e.g., "lat" Latin, "swa" Swahili). Used as
' tier 2 by resolveLanguageName() — translatable languages always go through
' translate() first; this only fires when no LanguageX key exists for the
' resolved base code, ensuring no recognized 3-letter code ever displays raw.
'
' Names are the official ISO 639-2 forms. They are intentionally NOT routed
' through the i18n pipeline: these languages are unlikely to be a UI locale
' for our users, so the maintenance cost of translating "Akkadian" into
' Polish (etc.) outweighs the UX benefit. Users see the English name in any
' UI when an audio/subtitle track is in one of these less-common languages.
'
' Cached on the caller's m scope after first build.
'
' @returns {object} - AssocArray of 3-letter ISO 639-2 code to English name
function languageEnglishFallbacks() as object
if isValid(m.languageEnglishFallbacksCache) then return m.languageEnglishFallbacksCache
m.languageEnglishFallbacksCache = {
"aar": "Afar",
"abk": "Abkhazian",
"ace": "Achinese",
"ach": "Acoli",
"ada": "Adangme",
"ady": "Adyghe",
"afa": "Afro-Asiatic languages",
"afh": "Afrihili",
"ain": "Ainu",
"aka": "Akan",
"akk": "Akkadian",
"ale": "Aleut",
"alg": "Algonquian languages",
"alt": "Southern Altai",
"amh": "Amharic",
"ang": "Old English",
"anp": "Angika",
"apa": "Apache languages",
"arc": "Aramaic",
"arg": "Aragonese",
"arn": "Mapudungun",
"arp": "Arapaho",
"art": "Artificial languages",
"arw": "Arawak",
"ast": "Asturian",
"ath": "Athapascan languages",
"aus": "Australian languages",
"ava": "Avaric",
"ave": "Avestan",
"awa": "Awadhi",
"aym": "Aymara",
"aze": "Azerbaijani",
"bad": "Banda languages",
"bai": "Bamileke languages",
"bak": "Bashkir",
"bal": "Baluchi",
"bam": "Bambara",
"ban": "Balinese",
"bas": "Basa",
"bat": "Baltic languages",
"bej": "Beja",
"bem": "Bemba",
"ber": "Berber languages",
"bho": "Bhojpuri",
"bih": "Bihari languages",
"bik": "Bikol",
"bin": "Bini",
"bis": "Bislama",
"bla": "Siksika",
"bnt": "Bantu languages",
"bod": "Tibetan",
"bos": "Bosnian",
"bra": "Braj",
"btk": "Batak languages",
"bua": "Buriat",
"bug": "Buginese",
"byn": "Blin",
"cad": "Caddo",
"cai": "Central American Indian languages",
"car": "Galibi Carib",
"cau": "Caucasian languages",
"ceb": "Cebuano",
"cel": "Celtic languages",
"cha": "Chamorro",
"chb": "Chibcha",
"che": "Chechen",
"chg": "Chagatai",
"chk": "Chuukese",
"chm": "Mari",
"chn": "Chinook jargon",
"cho": "Choctaw",
"chp": "Chipewyan",
"chr": "Cherokee",
"chu": "Church Slavic",
"chv": "Chuvash",
"chy": "Cheyenne",
"cmc": "Chamic languages",
"cnr": "Montenegrin",
"cop": "Coptic",
"cos": "Corsican",
"cpe": "Creoles and pidgins, English-based",
"cpf": "Creoles and pidgins, French-based",
"cpp": "Creoles and pidgins, Portuguese-based",
"cre": "Cree",
"crh": "Crimean Tatar",
"crp": "Creoles and pidgins",
"csb": "Kashubian",
"cus": "Cushitic languages",
"dak": "Dakota",
"dar": "Dargwa",
"day": "Land Dayak languages",
"del": "Delaware",
"den": "Slave",
"dgr": "Dogrib",
"din": "Dinka",
"doi": "Dogri",
"dra": "Dravidian languages",
"dsb": "Lower Sorbian",
"dua": "Duala",
"dum": "Middle Dutch",
"dyu": "Dyula",
"dzo": "Dzongkha",
"efi": "Efik",
"egy": "Ancient Egyptian",
"eka": "Ekajuk",
"elx": "Elamite",
"enm": "Middle English",
"ewe": "Ewe",
"ewo": "Ewondo",
"fan": "Fang",
"fat": "Fanti",
"fij": "Fijian",
"fiu": "Finno-Ugrian languages",
"fon": "Fon",
"frc": "Cajun French",
"frm": "Middle French",
"fro": "Old French",
"frr": "Northern Frisian",
"frs": "Eastern Frisian",
"fry": "Western Frisian",
"ful": "Fulah",
"fur": "Friulian",
"gaa": "Ga",
"gay": "Gayo",
"gba": "Gbaya",
"gem": "Germanic languages",
"gez": "Geez",
"gil": "Gilbertese",
"gla": "Scottish Gaelic",
"gmh": "Middle High German",
"goh": "Old High German",
"gon": "Gondi",
"gor": "Gorontalo",
"got": "Gothic",
"glv": "Manx",
"grb": "Grebo",
"grc": "Ancient Greek",
"grn": "Guarani",
"gwi": "Gwich'in",
"hai": "Haida",
"hau": "Hausa",
"haw": "Hawaiian",
"her": "Herero",
"hil": "Hiligaynon",
"him": "Himachali languages",
"hit": "Hittite",
"hmn": "Hmong",
"hmo": "Hiri Motu",
"hsb": "Upper Sorbian",
"hup": "Hupa",
"iba": "Iban",
"ibo": "Igbo",
"ido": "Ido",
"iii": "Sichuan Yi",
"ijo": "Ijo languages",
"iku": "Inuktitut",
"ile": "Interlingue",
"ilo": "Iloko",
"ina": "Interlingua",
"inc": "Indic languages",
"ine": "Indo-European languages",
"inh": "Ingush",
"ipk": "Inupiaq",
"ira": "Iranian languages",
"iro": "Iroquoian languages",
"jav": "Javanese",
"jbo": "Lojban",
"jpr": "Judeo-Persian",
"jrb": "Judeo-Arabic",
"kaa": "Kara-Kalpak",
"kac": "Kachin",
"kal": "Kalaallisut",
"kam": "Kamba",
"kar": "Karen languages",
"kas": "Kashmiri",
"kau": "Kanuri",
"kaw": "Kawi",
"kbd": "Kabardian",
"kha": "Khasi",
"khi": "Khoisan languages",
"khm": "Khmer",
"kho": "Khotanese",
"kik": "Kikuyu",
"kin": "Kinyarwanda",
"kir": "Kirghiz",
"kmb": "Kimbundu",
"kok": "Konkani",
"kom": "Komi",
"kon": "Kongo",
"kos": "Kosraean",
"kpe": "Kpelle",
"krc": "Karachay-Balkar",
"krl": "Karelian",
"kro": "Kru languages",
"kru": "Kurukh",
"kua": "Kuanyama",
"kum": "Kumyk",
"kur": "Kurdish",
"kut": "Kutenai",
"lad": "Ladino",
"lah": "Lahnda",
"lam": "Lamba",
"lao": "Lao",
"lat": "Latin",
"lez": "Lezghian",
"lim": "Limburgish",
"lin": "Lingala",
"lol": "Mongo",
"loz": "Lozi",
"lua": "Luba-Lulua",
"lub": "Luba-Katanga",
"lug": "Ganda",
"lui": "Luiseno",
"lun": "Lunda",
"luo": "Luo",
"lus": "Lushai",
"mad": "Madurese",
"mag": "Magahi",
"mah": "Marshallese",
"mai": "Maithili",
"mak": "Makasar",
"man": "Mandingo",
"map": "Austronesian languages",
"mas": "Masai",
"mdf": "Moksha",
"mdr": "Mandar",
"men": "Mende",
"mga": "Middle Irish",
"mic": "Mi'kmaq",
"min": "Minangkabau",
"mis": "Uncoded languages",
"mkh": "Mon-Khmer languages",
"mnc": "Manchu",
"mni": "Manipuri",
"mno": "Manobo languages",
"moh": "Mohawk",
"mos": "Mossi",
"mul": "Multiple languages",
"mun": "Munda languages",
"mus": "Creek",
"mwl": "Mirandese",
"mwr": "Marwari",
"myn": "Mayan languages",
"myv": "Erzya",
"nah": "Nahuatl languages",
"nai": "North American Indian languages",
"nap": "Neapolitan",
"nau": "Nauru",
"nav": "Navajo",
"nbl": "South Ndebele",
"nde": "North Ndebele",
"ndo": "Ndonga",
"nds": "Low German",
"new": "Newari",
"nia": "Nias",
"nic": "Niger-Kordofanian languages",
"niu": "Niuean",
"nog": "Nogai",
"non": "Old Norse",
"nqo": "N'Ko",
"nso": "Northern Sotho",
"nub": "Nubian languages",
"nwc": "Classical Newari",
"nya": "Nyanja",
"nym": "Nyamwezi",
"nyn": "Nyankole",
"nyo": "Nyoro",
"nzi": "Nzima",
"oci": "Occitan",
"oji": "Ojibwa",
"ori": "Oriya",
"orm": "Oromo",
"osa": "Osage",
"oss": "Ossetian",
"ota": "Ottoman Turkish",
"oto": "Otomian languages",
"paa": "Papuan languages",
"pag": "Pangasinan",
"pal": "Pahlavi",
"pam": "Pampanga",
"pap": "Papiamento",
"pau": "Palauan",
"peo": "Old Persian",
"phi": "Philippine languages",
"phn": "Phoenician",
"pli": "Pali",
"pon": "Pohnpeian",
"pra": "Prakrit languages",
"pro": "Old Provençal",
"pus": "Pashto",
"que": "Quechua",
"raj": "Rajasthani",
"rap": "Rapanui",
"rar": "Rarotongan",
"roa": "Romance languages",
"roh": "Romansh",
"rom": "Romany",
"run": "Rundi",
"rup": "Aromanian",
"sad": "Sandawe",
"sag": "Sango",
"sah": "Yakut",
"sai": "South American Indian languages",
"sal": "Salishan languages",
"sam": "Samaritan Aramaic",
"san": "Sanskrit",
"sas": "Sasak",
"sat": "Santali",
"scn": "Sicilian",
"sco": "Scots",
"sel": "Selkup",
"sem": "Semitic languages",
"sga": "Old Irish",
"sgn": "Sign Languages",
"shn": "Shan",
"sid": "Sidamo",
"sio": "Siouan languages",
"sit": "Sino-Tibetan languages",
"sla": "Slavic languages",
"sma": "Southern Sami",
"sme": "Northern Sami",
"smi": "Sami languages",
"smj": "Lule Sami",
"smn": "Inari Sami",
"smo": "Samoan",
"sms": "Skolt Sami",
"sna": "Shona",
"snd": "Sindhi",
"snk": "Soninke",
"sog": "Sogdian",
"son": "Songhai languages",
"sot": "Southern Sotho",
"srd": "Sardinian",
"srn": "Sranan Tongo",
"srr": "Serer",
"ssa": "Nilo-Saharan languages",
"ssw": "Swati",
"suk": "Sukuma",
"sun": "Sundanese",
"sus": "Susu",
"sux": "Sumerian",
"swa": "Swahili",
"syc": "Classical Syriac",
"syr": "Syriac",
"tah": "Tahitian",
"tai": "Tai languages",
"tat": "Tatar",
"tem": "Timne",
"ter": "Tereno",
"tet": "Tetum",
"tgk": "Tajik",
"tgl": "Tagalog",
"tib": "Tibetan",
"tig": "Tigre",
"tir": "Tigrinya",
"tiv": "Tiv",
"tkl": "Tokelau",
"tlh": "Klingon",
"tli": "Tlingit",
"tmh": "Tamashek",
"tog": "Nyasa Tonga",
"ton": "Tonga",
"tpi": "Tok Pisin",
"tsi": "Tsimshian",
"tsn": "Tswana",
"tso": "Tsonga",
"tuk": "Turkmen",
"tum": "Tumbuka",
"tup": "Tupi languages",
"tut": "Altaic languages",
"tvl": "Tuvalu",
"twi": "Twi",
"tyv": "Tuvinian",
"udm": "Udmurt",
"uga": "Ugaritic",
"umb": "Umbundu",
"vai": "Vai",
"ven": "Venda",
"vol": "Volapük",
"vot": "Votic",
"wak": "Wakashan languages",
"wal": "Walamo",
"war": "Waray",
"was": "Washo",
"wen": "Sorbian languages",
"wln": "Walloon",
"wol": "Wolof",
"xal": "Kalmyk",
"xho": "Xhosa",
"yao": "Yao",
"yap": "Yapese",
"yid": "Yiddish",
"yor": "Yoruba",
"ypk": "Yupik languages",
"zap": "Zapotec",
"zbl": "Blissymbols",
"zen": "Zenaga",
"zgh": "Standard Moroccan Tamazight",
"zha": "Zhuang",
"znd": "Zande languages",
"zun": "Zuni",
"zza": "Zaza"
}
return m.languageEnglishFallbacksCache
end function