1
0
Fork 1
mirror of https://gitlab.postmarketos.org/postmarketOS/pmbootstrap.git synced 2025-07-12 19:09:56 +03:00

pmb.helpers.locale: new module (MR 2497)

Add module that provides utilities and information related to localization.

Initial implementation includes code to generate keyboard configs
for locale set by user.
This commit is contained in:
Anri Dellal 2024-12-02 23:31:26 +03:00 committed by Newbyte
parent 3e18183aed
commit 556160b200
No known key found for this signature in database
GPG key ID: ACD854892B38D898
4 changed files with 458 additions and 1 deletions

View file

@ -17,4 +17,5 @@ set -x
codespell \
-L crate \
-L hda \
-S "./pmb/helpers/locale.py" \
.

View file

@ -100,6 +100,15 @@ pmb.helpers.lint module
:undoc-members:
:show-inheritance:
pmb.helpers.locale module
-------------------------
.. automodule:: pmb.helpers.locale
:members:
:undoc-members:
:show-inheritance:
pmb.helpers.logging module
--------------------------

View file

@ -1,5 +1,5 @@
C
a_DJ
aa_DJ
aa_ER
aa_ET
af_ZA

447
pmb/helpers/locale.py Normal file
View file

@ -0,0 +1,447 @@
from pmb.helpers import logging
"""
Helper used to configure locale-related settings in pmOS installation.
Locale and layout information is taken from:
* https://en.wikipedia.org/wiki/List_of_ISO_639_language_codes
* https://en.wikipedia.org/wiki/List_of_ISO_3166_country_codes
* Xkb source code: /usr/share/X11/xkb/rules/base.lst
"""
class XkbLayout:
primary_layouts = [
"at",
"au",
"br",
"ca",
"ch",
"de",
"dk",
"ee",
"es",
"fi",
"fo",
"fr",
"gb",
"ie",
"is",
"it",
"latam",
"lt",
"mt",
"nz",
"pl",
"pt",
"ro",
"se",
"sk",
"tr",
"us",
"vn",
]
def __init__(self, layout: str = "us", variant: str = ""):
self.layout = layout
self.variant = variant
self.options = ""
self.layout_list: list[str] = []
self.variant_list: list[str] = []
if layout in XkbLayout.primary_layouts:
self.layout_list.append(layout)
if variant:
self.variant_list.append(variant)
elif not self.is_default():
self.layout_list.extend(["us", layout])
if variant:
self.variant_list.extend(["", variant])
self.options = "grp:alt_shift_toggle"
def is_default(self) -> bool:
return self.layout == "us" and not self.variant
def get_profile_vars(self) -> str | None:
if self.is_default():
return None
template = "XKB_DEFAULT_LAYOUT=" + ",".join(self.layout_list)
if self.variant_list:
template += "\nXKB_DEFAULT_VARIANT=" + ",".join(self.variant_list)
if self.options:
template += "\nXKB_DEFAULT_OPTIONS=" + self.options
return template
def get_keyboard_config(self) -> str | None:
if self.is_default() or not self.layout_list:
return None
template = f"""Section "InputClass"
\tIdentifier "keyboard"
\tMatchIsKeyboard "on"
\tOption "XkbLayout" "{",".join(self.layout_list)}"\n"""
if self.variant_list:
template += f'\tOption "XkbVariants" "{",".join(self.variant_list)}"\n'
if self.options:
template += f'\tOption "XkbOptions" "{self.options}"\n'
template += "EndSection"
return template
def __eq__(self, other: object) -> bool:
if not isinstance(other, XkbLayout):
return False
return self.layout == other.layout and self.variant == other.variant
def get_xkb_layout(locale: str) -> XkbLayout:
"""
Get Xkb layout for the given locale.
:param locale: Locale to get XkbLayout for.
:return: XkbLayout for the given locale.
"""
try:
(language, territory) = locale.split("_")
except ValueError:
logging.warning(f'Keyboard config for locale "{locale}" cannot be provided')
return XkbLayout()
if language in lang_layouts:
return XkbLayout(language)
elif language in lang_to_layout:
return XkbLayout(lang_to_layout[language])
match language:
case "af":
return XkbLayout("za")
case "ak":
return XkbLayout("gh", "akan")
case "ar":
if territory == "DZ":
return XkbLayout("dz", "ar")
elif territory == "EG":
return XkbLayout("eg")
elif territory == "IQ":
return XkbLayout("iq")
elif territory == "MA":
return XkbLayout("ma")
elif territory == "SY":
return XkbLayout("sy")
else:
return XkbLayout("ara")
case "as":
return XkbLayout("in", "asm-kagapa")
case "ast":
return XkbLayout("es", "ast")
case "ber":
if territory == "DZ":
return XkbLayout("dz")
else: # "MA"
return XkbLayout("ma")
case "bo":
return XkbLayout("cn", "tib")
case "ca":
return XkbLayout("es", "cat")
case "chr":
return XkbLayout("us", "chr")
case "crh":
return XkbLayout("ua", "crh")
case "de":
if territory == "AT":
return XkbLayout("at")
elif territory == "CH":
return XkbLayout("ch")
else:
return XkbLayout("de")
case "en":
if territory == "AU":
return XkbLayout("au")
elif territory == "CA":
return XkbLayout("ca", "eng")
elif territory == "GB":
return XkbLayout("gb")
elif territory == "IN":
return XkbLayout("in", "eng")
elif territory == "NG":
return XkbLayout("ng")
elif territory == "NZ":
return XkbLayout("nz")
elif territory == "ZA":
return XkbLayout("za")
else:
return XkbLayout("us")
case "es":
if territory in latam_es_territories:
return XkbLayout("latam")
else: # "ES", "US"
return XkbLayout("es")
case "fr":
if territory == "CA":
return XkbLayout("ca")
elif territory == "CH":
return XkbLayout("ch", "fr")
else: # "FR"
return XkbLayout("fr")
case "fur":
return XkbLayout("it", "fur")
case "gu":
return XkbLayout("in", "guj")
case "gv":
return XkbLayout("gb", "gla")
case "ha":
return XkbLayout("ng", "hausa")
case "hif" | "hne":
return XkbLayout("in")
case "ig":
return XkbLayout("ng", "igbo")
case "iu":
return XkbLayout("ca", "ike")
case "kab":
return XkbLayout("dz", "azerty-deadkeys")
case "kn":
return XkbLayout("in", "kan")
case "ku":
return XkbLayout("tr", "ku")
case "mhr":
return XkbLayout("ru", "chm")
case "ml":
return XkbLayout("in", "mal")
case "mni":
return XkbLayout("in", "mni")
case "mnw":
return XkbLayout("mm", "mnw")
case "mr":
return XkbLayout("in", "marathi")
case "nan":
return XkbLayout("cn") # Min Nan Chinese
case "nds":
return XkbLayout("de") # Low German
case "oc":
return XkbLayout("fr", "oci")
case "or":
return XkbLayout("in", "ori")
case "os":
return XkbLayout("ru", "os_winkeys")
case "pa":
return XkbLayout("in", "guru")
case "ps":
return XkbLayout("af", "ps")
case "pt":
if territory == "BR":
return XkbLayout("br")
else: # if territory == "PT":
return XkbLayout("pt")
case "sa":
return XkbLayout("in", "sas")
case "sah":
return XkbLayout("ru", "sah")
case "sat":
return XkbLayout("in", "sat")
case "sd":
return XkbLayout("in", "sdh")
case "se":
return XkbLayout("no", "smi")
case "sgs":
return XkbLayout("lt", "sgs")
case "shn":
return XkbLayout("mm", "shn")
case "si":
return XkbLayout("pk", "snd")
case "sw":
if territory == "KE":
return XkbLayout("ke")
else: # "TZ"
return XkbLayout("tz")
case "szl":
return XkbLayout("pl", "szl")
case "ta":
if territory == "LK":
return XkbLayout("lk", "tam_unicode")
else: # "IN"
return XkbLayout("in", "tamilnet")
case "te":
return XkbLayout("in", "tel")
case "tt":
return XkbLayout("ru", "tt")
case "ug":
return XkbLayout("cn", "ug")
case "ur":
return XkbLayout("pak", "urd-nla")
case "yo":
return XkbLayout("ng", "yoruba")
if language not in locales_without_layout:
logging.warning(f'Language "{language}" not found in language list')
return XkbLayout() # return default layout if no layout was found
# Layouts which have same code as language
lang_layouts = [
"az",
"bg",
"csb",
"cv",
"dsb",
"fi",
"fo",
"hr",
"hu",
"id",
"is",
"it",
"lt",
"lv",
"mk",
"mn",
"mt",
"nl",
"pl",
"ro",
"ru",
"sk",
"tg",
"th",
"tr",
"uk",
"uz",
]
lang_to_layout = {
"am": "et",
"be": "by",
"bn": "bd",
"br": "bre",
"bs": "ba",
"cmn": "cn",
"cs": "cz",
"da": "dk",
"dz": "bt",
"el": "gr",
"eo": "epo",
"et": "ee",
"eu": "es",
"fa": "ir",
"fil": "ph",
"ga": "ie",
"gd": "gla",
"hak": "cn",
"he": "il",
"hi": "in",
"hy": "am",
"ja": "jp",
"ka": "ge",
"kk": "kz",
"km": "kh",
"ko": "kr",
"ky": "kg",
"lo": "la",
"lzh": "cn",
"mai": "in",
"mi": "mao",
"ms": "my",
"my": "mm",
"nb": "no",
"ne": "np",
"nn": "no",
"sl": "si",
"sq": "al",
"sr": "rs",
"sv": "se",
"tk": "tm",
"tl": "in",
"tn": "bw",
"vi": "vn",
"wo": "sn",
"yi": "il",
"yue": "cn",
"zh": "cn",
}
# Locales which either have no layout or missing information about one
locales_without_layout = [
"aa", # Afar
"agr", # Aguaruna
"an", # Aragonese
"anp", # Angika
"ayc", # Aymara
"bem", # Bemba
"bhb", # Bhili
"bho", # Bhojpuri
"bi", # Bislama
"bn", # Bengali
"brx", # Bodo (India)
"byn", # Bilin
"ce", # Chechen
"ch", # Chamorro
"cy", # Wales
"doi", # Dogri
"dv", # Divehi
"ff", # Fulah
"fy", # Western Frisian
"gez", # Geez
"gl", # Galician
"hsb", # Upper Sorbian
"ht", # Haitian
"ia", # Interlingua
"ik", # Inupiaq
"kl", # Kalaallisut, Greenlandic
"kok", # Konkani
"ks", # Kashmiri
"kw", # Kinyarwanda
"lb", # Luxembourgish
"lg", # Ganda
"li", # Limburgish
"lij", # Ligurian
"ln", # Lingala
"mfe", # Morisyen
"mg", # Malagasy
"miq", # Miskito
"mjw", # Karbi
"nhn", # Nahuatl
"niu", # Niuean
"nr", # South Ndebele
"nso", # Northern Sotho
"om", # Oromo
"pap", # Papiamento
"quz", # Cusco Quechua
"raj", # Rajasthani
"rw", # Kinyarwanda
"sc", # Sardinian
"shs", # Shuswap
"sid", # Sidamo
"sm", # Samoan
"so", # Somali
"ss", # Swati
"st", # Southern Sotho
"tcy", # Tulu
"the", # Chitwania Tharu
"ti", # Tigrinya
"tig", # Tigre
"to", # Tonga
"tpi", # Tok Pisin
"ts", # Tsonga
"unm", # Unami
"ve", # Venda
"wa", # Walloon
"wae", # Walser
"wal", # Wolaitta
"xh", # Xhosa
"yuw", # Yau (Morobe province)
"zu", # Zulu
]
latam_es_territories = [
"AR",
"BO",
"CL",
"CO",
"CR",
"CU",
"DO",
"EC",
"GT",
"HN",
"MX",
"NI",
"PA",
"PE",
"PR",
"PY",
"SV",
"UY",
"VE",
]