From a2f6950dd1f52d5cecc681634830b6547372c374 Mon Sep 17 00:00:00 2001 From: Softer Date: Tue, 28 Apr 2026 17:19:38 +0300 Subject: [PATCH 1/2] Offer to apply archinstall language to target system locale and timezone After picking an installer language with a known locale mapping, prompt the user to apply sys_lang, sys_enc, console_font, and (where the country maps to a single IANA zone) timezone to the target system. Each row is shown only when its target value differs from the current setting, so re-picking a language with fewer mappings resets stale fields too. default_timezone is set in languages.json only for unambiguous single mainland zones; multi-zone countries (en_US, ru_RU, es_ES, pt_BR, ...) and codes that span countries are left without a default. Timezone candidates are validated against list_timezones(). --- archinstall/lib/global_menu.py | 82 ++++++++++++++++++++- archinstall/lib/models/locale.py | 63 +++++++++++++++- archinstall/lib/translationhandler.py | 28 ++++++- archinstall/locales/base.pot | 6 ++ archinstall/locales/languages.json | 74 +++++++++---------- archinstall/locales/uk/LC_MESSAGES/base.mo | Bin 83620 -> 84583 bytes archinstall/locales/uk/LC_MESSAGES/base.po | 12 +++ 7 files changed, 220 insertions(+), 45 deletions(-) diff --git a/archinstall/lib/global_menu.py b/archinstall/lib/global_menu.py index cdd820a943..51b18869fa 100644 --- a/archinstall/lib/global_menu.py +++ b/archinstall/lib/global_menu.py @@ -8,11 +8,14 @@ from archinstall.lib.bootloader.utils import validate_bootloader_layout from archinstall.lib.configuration import ConfigurationOutput, save_config from archinstall.lib.disk.disk_menu import DiskLayoutConfigurationMenu +from archinstall.lib.exceptions import SysCallError from archinstall.lib.general.general_menu import select_hostname, select_ntp, select_timezone from archinstall.lib.general.system_menu import select_kernel, select_swap from archinstall.lib.hardware import SysInfo +from archinstall.lib.locale import list_timezones from archinstall.lib.locale.locale_menu import LocaleMenu from archinstall.lib.menu.abstract_menu import AbstractMenu, SpecialMenuKey +from archinstall.lib.menu.helpers import Confirmation from archinstall.lib.mirror.mirror_handler import MirrorListHandler from archinstall.lib.mirror.mirror_menu import MirrorMenu from archinstall.lib.models.application import ApplicationConfiguration, ZramConfiguration @@ -27,13 +30,14 @@ from archinstall.lib.models.pacman import PacmanConfiguration from archinstall.lib.models.profile import ProfileConfiguration from archinstall.lib.network.network_menu import select_network -from archinstall.lib.output import FormattedOutput +from archinstall.lib.output import FormattedOutput, debug from archinstall.lib.packages.packages import list_available_packages, select_additional_packages from archinstall.lib.pacman.config import PacmanConfig from archinstall.lib.pacman.pacman_menu import PacmanMenu -from archinstall.lib.translationhandler import Language, tr, translation_handler +from archinstall.lib.translationhandler import DEFAULT_TIMEZONE, Language, tr, translation_handler from archinstall.tui.ui.components import tui from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup +from archinstall.tui.ui.result import ResultType class GlobalMenu(AbstractMenu[None]): @@ -160,7 +164,7 @@ def _get_menu_options(self) -> list[MenuItem]: MenuItem( text=tr('Timezone'), action=select_timezone, - value='UTC', + value=DEFAULT_TIMEZONE, preview_action=self._prev_tz, key='timezone', ), @@ -254,8 +258,80 @@ async def _select_archinstall_language(self, preset: Language) -> Language: self._update_lang_text() + await self._maybe_apply_language_to_locale(language) + return language + async def _maybe_apply_language_to_locale(self, language: Language) -> None: + """Offer to mirror the selected archinstall language into the target system locale. + + Triggered only when the language has a sys_lang mapping, since otherwise + there is no target locale to offer. Console font and timezone rows are + added to the prompt only when their language-derived target value differs + from the current setting, so re-picking a language with fewer mappings + (for example switching from Ukrainian to German, which has no console_font + of its own) resets the stale Ukrainian font alongside the new locale. + """ + if not language.sys_lang: + return + + locale_item = self._item_group.find_by_key('locale_config') + locale_config: LocaleConfiguration | None = locale_item.value + if not locale_config: + return + + tz_item = self._item_group.find_by_key('timezone') + current_tz: str = tz_item.value or DEFAULT_TIMEZONE + target_tz = language.target_timezone + offer_tz = self._is_timezone_offerable(target_tz, current_tz) + + diff = locale_config.language_diff(language) + if diff.is_empty() and not offer_tz: + return + + rows = diff.labeled_rows() + if offer_tz: + rows.append((tr('Timezone'), target_tz)) + + if not await self._confirm_locale_apply(rows): + return + + locale_config.apply_language_diff(diff) + if offer_tz: + tz_item.value = target_tz + + def _is_timezone_offerable(self, target_tz: str, current_tz: str) -> bool: + """Return True when the candidate differs from the current and exists in tzdata. + + The same source the timezone menu reads from, so we never offer a value + the user could not have selected manually. UTC is always present, so this + is effectively a no-op for the reset-to-default case. + """ + if target_tz == current_tz: + return False + try: + return target_tz in list_timezones() + except SysCallError as err: + debug(f'Failed to validate target timezone {target_tz}: {err}') + return False + + async def _confirm_locale_apply(self, rows: list[tuple[str, str]]) -> bool: + """Render and show the confirmation dialog for the locale changes.""" + label_w = max(len(label) for label, _ in rows) + data_lines = [f' {label.ljust(label_w)} : {value}' for label, value in rows] + + question = tr('Use this language as the target system language as well?') + header = tr('The following settings will be applied:') + + # The TUI centers every line of the prompt independently, so pad all + # lines to a common width; otherwise the colon column drifts. + width = max(len(question), len(header), *(len(line) for line in data_lines)) + separator = '=' * width + prompt = question.ljust(width) + '\n\n' + header.ljust(width) + '\n' + separator + '\n' + '\n'.join(line.ljust(width) for line in data_lines) + '\n' + + result = await Confirmation(header=prompt, preset=True).show() + return result.type_ == ResultType.Selection and result.item() == MenuItem.yes() + def _prev_archinstall_language(self, item: MenuItem) -> str | None: if not item.value: return None diff --git a/archinstall/lib/models/locale.py b/archinstall/lib/models/locale.py index 8f58098915..e45df61cde 100644 --- a/archinstall/lib/models/locale.py +++ b/archinstall/lib/models/locale.py @@ -2,7 +2,36 @@ from typing import Any, Self from archinstall.lib.locale.utils import get_kb_layout -from archinstall.lib.translationhandler import tr +from archinstall.lib.translationhandler import DEFAULT_CONSOLE_FONT, Language, tr + + +@dataclass +class LocaleLanguageDiff: + """Locale fields to write when applying a Language to a LocaleConfiguration. + + Each field carries the new value, or None when no change is needed. sys_enc + is paired with sys_lang so the encoding row is shown alongside the locale + row in the confirmation dialog, even when the encoding portion itself does + not change. + """ + + sys_lang: str | None = None + sys_enc: str | None = None + console_font: str | None = None + + def is_empty(self) -> bool: + return self.sys_lang is None and self.sys_enc is None and self.console_font is None + + def labeled_rows(self) -> list[tuple[str, str]]: + """Return [(label, value)] for fields that would change.""" + rows: list[tuple[str, str]] = [] + if self.sys_lang is not None: + rows.append((tr('Locale language'), self.sys_lang)) + if self.sys_enc is not None: + rows.append((tr('Locale encoding'), self.sys_enc)) + if self.console_font is not None: + rows.append((tr('Console font'), self.console_font)) + return rows @dataclass @@ -14,7 +43,7 @@ class LocaleConfiguration: # can be checked using # zgrep "CONFIG_FONT" /proc/config.gz # https://wiki.archlinux.org/title/Linux_console#Font - console_font: str = 'default8x16' + console_font: str = DEFAULT_CONSOLE_FONT @classmethod def default(cls) -> Self: @@ -38,6 +67,36 @@ def preview(self) -> str: output += '{}: {}'.format(tr('Console font'), self.console_font) return output + def language_diff(self, language: Language) -> LocaleLanguageDiff: + """Compute the locale fields that would change if applying this language. + + Returns an empty diff for languages without a sys_lang mapping. console_font + is offered when the language-derived target value differs - so re-picking + a language with fewer mappings still resets stale fonts left over from a + previous pick. + """ + diff = LocaleLanguageDiff() + if not language.sys_lang: + return diff + + if self.sys_lang != language.sys_lang: + diff.sys_lang = language.sys_lang + diff.sys_enc = language.target_sys_enc or self.sys_enc + + target_font = language.target_console_font + if self.console_font != target_font: + diff.console_font = target_font + + return diff + + def apply_language_diff(self, diff: LocaleLanguageDiff) -> None: + if diff.sys_lang is not None: + self.sys_lang = diff.sys_lang + if diff.sys_enc is not None: + self.sys_enc = diff.sys_enc + if diff.console_font is not None: + self.console_font = diff.console_font + def _load_config(self, args: dict[str, str]) -> None: if 'sys_lang' in args: self.sys_lang = args['sys_lang'] diff --git a/archinstall/lib/translationhandler.py b/archinstall/lib/translationhandler.py index 093ca6dea1..1eda4f4313 100644 --- a/archinstall/lib/translationhandler.py +++ b/archinstall/lib/translationhandler.py @@ -21,12 +21,31 @@ class Language: translation_percent: int translated_lang: str | None console_font: str | None = None + sys_lang: str | None = None + default_timezone: str | None = None @property def display_name(self) -> str: name = self.name_en return f'{name} ({self.translation_percent}%)' + @property + def target_sys_enc(self) -> str | None: + """Encoding portion of sys_lang (e.g. 'UTF-8' from 'uk_UA.UTF-8'). None when sys_lang has no '.'.""" + if self.sys_lang and '.' in self.sys_lang: + return self.sys_lang.split('.', 1)[1] + return None + + @property + def target_console_font(self) -> str: + """Console font implied by this language; falls back to the system default.""" + return self.console_font or DEFAULT_CONSOLE_FONT + + @property + def target_timezone(self) -> str: + """Timezone implied by this language; falls back to UTC.""" + return self.default_timezone or DEFAULT_TIMEZONE + def is_match(self, lang_or_translated_lang: str) -> bool: if self.name_en == lang_or_translated_lang: return True @@ -38,7 +57,8 @@ def json(self) -> str: return self.name_en -_DEFAULT_FONT = 'default8x16' +DEFAULT_CONSOLE_FONT = 'default8x16' +DEFAULT_TIMEZONE = 'UTC' _ENV_FONT = os.environ.get('FONT') @@ -69,7 +89,7 @@ def _set_font(self, font_name: str | None) -> bool: if not running_from_iso(): return False - target = font_name or _DEFAULT_FONT + target = font_name or DEFAULT_CONSOLE_FONT try: SysCommand(['setfont', target]) return True @@ -132,6 +152,8 @@ def _get_translations(self) -> list[Language]: lang = mapping_entry['lang'] translated_lang = mapping_entry.get('translated_lang', None) console_font = mapping_entry.get('console_font', None) + sys_lang = mapping_entry.get('sys_lang', None) + default_timezone = mapping_entry.get('default_timezone', None) try: # get a translation for a specific language @@ -146,7 +168,7 @@ def _get_translations(self) -> list[Language]: # prevent cases where the .pot file is out of date and the percentage is above 100 percent = min(100, percent) - language = Language(abbr, lang, translation, percent, translated_lang, console_font) + language = Language(abbr, lang, translation, percent, translated_lang, console_font, sys_lang, default_timezone) languages.append(language) except FileNotFoundError as err: raise FileNotFoundError(f"Could not locate language file for '{lang}': {err}") diff --git a/archinstall/locales/base.pot b/archinstall/locales/base.pot index f0b311d445..2636a782f3 100644 --- a/archinstall/locales/base.pot +++ b/archinstall/locales/base.pot @@ -163,6 +163,12 @@ msgstr "" msgid "Archinstall language" msgstr "" +msgid "Use this language as the target system language as well?" +msgstr "" + +msgid "The following settings will be applied:" +msgstr "" + msgid "Wipe all selected drives and use a best-effort default partition layout" msgstr "" diff --git a/archinstall/locales/languages.json b/archinstall/locales/languages.json index 2dbcf3b4d4..aabc9d7c6d 100644 --- a/archinstall/locales/languages.json +++ b/archinstall/locales/languages.json @@ -3,7 +3,7 @@ {"abbr": "af", "lang": "Afrikaans"}, {"abbr": "ak", "lang": "Akan"}, {"abbr": "am", "lang": "Amharic"}, - {"abbr": "ar", "lang": "Arabic"}, + {"abbr": "ar", "lang": "Arabic", "sys_lang": "ar_SA.UTF-8", "default_timezone": "Asia/Riyadh"}, {"abbr": "an", "lang": "Aragonese"}, {"abbr": "as", "lang": "Assamese"}, {"abbr": "av", "lang": "Avaric"}, @@ -19,8 +19,8 @@ {"abbr": "bs", "lang": "Bosnian"}, {"abbr": "br", "lang": "Breton"}, {"abbr": "bg", "lang": "Bulgarian"}, - {"abbr": "ca", "lang": "Catalan"}, - {"abbr": "cs", "lang": "Czech", "translated_lang": "čeština"}, + {"abbr": "ca", "lang": "Catalan", "sys_lang": "ca_ES.UTF-8"}, + {"abbr": "cs", "lang": "Czech", "translated_lang": "čeština", "sys_lang": "cs_CZ.UTF-8", "default_timezone": "Europe/Prague"}, {"abbr": "ch", "lang": "Chamorro"}, {"abbr": "ce", "lang": "Chechen"}, {"abbr": "cu", "lang": "Church Slavic"}, @@ -30,37 +30,37 @@ {"abbr": "cr", "lang": "Cree"}, {"abbr": "cy", "lang": "Welsh"}, {"abbr": "da", "lang": "Danish", "translated_lang": "Dansk"}, - {"abbr": "de", "lang": "German", "translated_lang": "Deutsch"}, + {"abbr": "de", "lang": "German", "translated_lang": "Deutsch", "sys_lang": "de_DE.UTF-8", "default_timezone": "Europe/Berlin"}, {"abbr": "dv", "lang": "Dhivehi"}, {"abbr": "dz", "lang": "Dzongkha"}, - {"abbr": "el", "lang": "Modern Greek (1453-)", "translated_lang": "Ελληνικά"}, - {"abbr": "en", "lang": "English"}, + {"abbr": "el", "lang": "Modern Greek (1453-)", "translated_lang": "Ελληνικά", "sys_lang": "el_GR.UTF-8", "default_timezone": "Europe/Athens"}, + {"abbr": "en", "lang": "English", "sys_lang": "en_US.UTF-8"}, {"abbr": "eo", "lang": "Esperanto"}, - {"abbr": "et", "lang": "Estonian", "translated_lang": "Eesti" }, + {"abbr": "et", "lang": "Estonian", "translated_lang": "Eesti", "sys_lang": "et_EE.UTF-8" , "default_timezone": "Europe/Tallinn"}, {"abbr": "eu", "lang": "Basque"}, {"abbr": "ee", "lang": "Ewe"}, {"abbr": "fo", "lang": "Faroese"}, {"abbr": "fa", "lang": "Persian"}, {"abbr": "fj", "lang": "Fijian"}, - {"abbr": "fi", "lang": "Finnish"}, - {"abbr": "fr", "lang": "French", "translated_lang": "Français"}, + {"abbr": "fi", "lang": "Finnish", "sys_lang": "fi_FI.UTF-8", "default_timezone": "Europe/Helsinki"}, + {"abbr": "fr", "lang": "French", "translated_lang": "Français", "sys_lang": "fr_FR.UTF-8", "default_timezone": "Europe/Paris"}, {"abbr": "fy", "lang": "Western Frisian"}, {"abbr": "ff", "lang": "Fulah"}, {"abbr": "gd", "lang": "Scottish Gaelic"}, - {"abbr": "ga", "lang": "Irish"}, - {"abbr": "gl", "lang": "Galician"}, + {"abbr": "ga", "lang": "Irish", "sys_lang": "ga_IE.UTF-8", "default_timezone": "Europe/Dublin"}, + {"abbr": "gl", "lang": "Galician", "sys_lang": "gl_ES.UTF-8"}, {"abbr": "gv", "lang": "Manx"}, {"abbr": "gn", "lang": "Guarani"}, {"abbr": "gu", "lang": "Gujarati"}, {"abbr": "ht", "lang": "Haitian"}, {"abbr": "ha", "lang": "Hausa"}, {"abbr": "sh", "lang": "Serbo-Croatian"}, - {"abbr": "he", "lang": "Hebrew"}, + {"abbr": "he", "lang": "Hebrew", "sys_lang": "he_IL.UTF-8", "default_timezone": "Asia/Jerusalem"}, {"abbr": "hz", "lang": "Herero"}, - {"abbr": "hi", "lang": "Hindi"}, + {"abbr": "hi", "lang": "Hindi", "sys_lang": "hi_IN.UTF-8", "default_timezone": "Asia/Kolkata"}, {"abbr": "ho", "lang": "Hiri Motu"}, {"abbr": "hr", "lang": "Croatian"}, - {"abbr": "hu", "lang": "Hungarian"}, + {"abbr": "hu", "lang": "Hungarian", "sys_lang": "hu_HU.UTF-8", "default_timezone": "Europe/Budapest"}, {"abbr": "hy", "lang": "Armenian"}, {"abbr": "ig", "lang": "Igbo"}, {"abbr": "io", "lang": "Ido"}, @@ -68,16 +68,16 @@ {"abbr": "iu", "lang": "Inuktitut"}, {"abbr": "ie", "lang": "Interlingue"}, {"abbr": "ia", "lang": "Interlingua (International Auxiliary Language Association)"}, - {"abbr": "id", "lang": "Indonesian", "translated_lang": "Indonesian"}, + {"abbr": "id", "lang": "Indonesian", "translated_lang": "Indonesian", "sys_lang": "id_ID.UTF-8"}, {"abbr": "ik", "lang": "Inupiaq"}, {"abbr": "is", "lang": "Icelandic"}, - {"abbr": "it", "lang": "Italian", "translated_lang": "Italiano"}, + {"abbr": "it", "lang": "Italian", "translated_lang": "Italiano", "sys_lang": "it_IT.UTF-8", "default_timezone": "Europe/Rome"}, {"abbr": "jv", "lang": "Javanese"}, - {"abbr": "ja", "lang": "Japanese"}, + {"abbr": "ja", "lang": "Japanese", "sys_lang": "ja_JP.UTF-8", "default_timezone": "Asia/Tokyo"}, {"abbr": "kl", "lang": "Kalaallisut"}, {"abbr": "kn", "lang": "Kannada"}, {"abbr": "ks", "lang": "Kashmiri"}, - {"abbr": "ka", "lang": "Georgian"}, + {"abbr": "ka", "lang": "Georgian", "sys_lang": "ka_GE.UTF-8", "default_timezone": "Asia/Tbilisi"}, {"abbr": "kr", "lang": "Kanuri"}, {"abbr": "kk", "lang": "Kazakh"}, {"abbr": "km", "lang": "Central Khmer"}, @@ -86,15 +86,15 @@ {"abbr": "ky", "lang": "Kirghiz"}, {"abbr": "kv", "lang": "Komi"}, {"abbr": "kg", "lang": "Kongo"}, - {"abbr": "ko", "lang": "Korean", "translated_lang": "한글", "external_dep": true}, + {"abbr": "ko", "lang": "Korean", "translated_lang": "한글", "external_dep": true, "sys_lang": "ko_KR.UTF-8", "default_timezone": "Asia/Seoul"}, {"abbr": "kj", "lang": "Kuanyama"}, - {"abbr": "ku", "lang": "Kurdish"}, + {"abbr": "ku", "lang": "Kurdish", "sys_lang": "ku_TR.UTF-8", "default_timezone": "Europe/Istanbul"}, {"abbr": "lo", "lang": "Lao"}, {"abbr": "la", "lang": "Latin"}, {"abbr": "lv", "lang": "Latvian"}, {"abbr": "li", "lang": "Limburgan"}, {"abbr": "ln", "lang": "Lingala"}, - {"abbr": "lt", "lang": "Lithuanian", "translated_lang": "Lietuvių"}, + {"abbr": "lt", "lang": "Lithuanian", "translated_lang": "Lietuvių", "sys_lang": "lt_LT.UTF-8", "default_timezone": "Europe/Vilnius"}, {"abbr": "lb", "lang": "Luxembourgish"}, {"abbr": "lu", "lang": "Luba-Katanga"}, {"abbr": "lg", "lang": "Ganda"}, @@ -113,8 +113,8 @@ {"abbr": "nr", "lang": "South Ndebele"}, {"abbr": "nd", "lang": "North Ndebele"}, {"abbr": "ng", "lang": "Ndonga"}, - {"abbr": "ne", "lang": "Nepali", "translated_lang": "नेपाली"}, - {"abbr": "nl", "lang": "Dutch", "translated_lang": "Nederlands"}, + {"abbr": "ne", "lang": "Nepali", "translated_lang": "नेपाली", "sys_lang": "ne_NP.UTF-8", "default_timezone": "Asia/Kathmandu"}, + {"abbr": "nl", "lang": "Dutch", "translated_lang": "Nederlands", "sys_lang": "nl_NL.UTF-8", "default_timezone": "Europe/Amsterdam"}, {"abbr": "nn", "lang": "Norwegian Nynorsk"}, {"abbr": "nb", "lang": "Norwegian Bokmål"}, {"abbr": "no", "lang": "Norwegian"}, @@ -126,15 +126,15 @@ {"abbr": "os", "lang": "Ossetian"}, {"abbr": "pa", "lang": "Panjabi"}, {"abbr": "pi", "lang": "Pali"}, - {"abbr": "pl", "lang": "Polish", "translated_lang": "Polski"}, - {"abbr": "pt", "lang": "Portuguese", "translated_lang": "Português"}, - {"abbr": "pt_BR", "lang": "Brazilian Portuguese", "translated_lang": "Português do Brasil"}, + {"abbr": "pl", "lang": "Polish", "translated_lang": "Polski", "sys_lang": "pl_PL.UTF-8", "default_timezone": "Europe/Warsaw"}, + {"abbr": "pt", "lang": "Portuguese", "translated_lang": "Português", "sys_lang": "pt_PT.UTF-8"}, + {"abbr": "pt_BR", "lang": "Brazilian Portuguese", "translated_lang": "Português do Brasil", "sys_lang": "pt_BR.UTF-8"}, {"abbr": "ps", "lang": "Pushto"}, {"abbr": "qu", "lang": "Quechua"}, {"abbr": "rm", "lang": "Romansh"}, - {"abbr": "ro", "lang": "Romanian", "translated_lang": "Română"}, + {"abbr": "ro", "lang": "Romanian", "translated_lang": "Română", "sys_lang": "ro_RO.UTF-8", "default_timezone": "Europe/Bucharest"}, {"abbr": "rn", "lang": "Rundi"}, - {"abbr": "ru", "lang": "Russian", "translated_lang": "Русский"}, + {"abbr": "ru", "lang": "Russian", "translated_lang": "Русский", "sys_lang": "ru_RU.UTF-8"}, {"abbr": "sg", "lang": "Sango"}, {"abbr": "sa", "lang": "Sanskrit"}, {"abbr": "si", "lang": "Sinhala"}, @@ -146,16 +146,16 @@ {"abbr": "sd", "lang": "Sindhi"}, {"abbr": "so", "lang": "Somali"}, {"abbr": "st", "lang": "Southern Sotho"}, - {"abbr": "es", "lang": "Spanish", "translated_lang": "Español"}, + {"abbr": "es", "lang": "Spanish", "translated_lang": "Español", "sys_lang": "es_ES.UTF-8"}, {"abbr": "sq", "lang": "Albanian"}, {"abbr": "sc", "lang": "Sardinian"}, {"abbr": "sr", "lang": "Serbian"}, {"abbr": "ss", "lang": "Swati"}, {"abbr": "su", "lang": "Sundanese"}, {"abbr": "sw", "lang": "Swahili (macrolanguage)"}, - {"abbr": "sv", "lang": "Swedish", "translated_lang": "Svenska"}, + {"abbr": "sv", "lang": "Swedish", "translated_lang": "Svenska", "sys_lang": "sv_SE.UTF-8", "default_timezone": "Europe/Stockholm"}, {"abbr": "ty", "lang": "Tahitian"}, - {"abbr": "ta", "lang": "Tamil", "translated_lang": "தமிழ்"}, + {"abbr": "ta", "lang": "Tamil", "translated_lang": "தமிழ்", "sys_lang": "ta_IN.UTF-8", "default_timezone": "Asia/Kolkata"}, {"abbr": "tt", "lang": "Tatar"}, {"abbr": "te", "lang": "Telugu"}, {"abbr": "tg", "lang": "Tajik"}, @@ -166,12 +166,12 @@ {"abbr": "tn", "lang": "Tswana"}, {"abbr": "ts", "lang": "Tsonga"}, {"abbr": "tk", "lang": "Turkmen"}, - {"abbr": "tr", "lang": "Turkish", "translated_lang" : "Türkçe"}, + {"abbr": "tr", "lang": "Turkish", "translated_lang" : "Türkçe", "sys_lang": "tr_TR.UTF-8", "default_timezone": "Europe/Istanbul"}, {"abbr": "tw", "lang": "Twi"}, {"abbr": "ug", "lang": "Uighur"}, - {"abbr": "uk", "lang": "Ukrainian", "console_font": "UniCyr_8x16"}, - {"abbr": "ur", "lang": "Urdu", "translated_lang": "اردو"}, - {"abbr": "uz", "lang": "Uzbek", "translated_lang": "O'zbek"}, + {"abbr": "uk", "lang": "Ukrainian", "console_font": "UniCyr_8x16", "sys_lang": "uk_UA.UTF-8", "default_timezone": "Europe/Kyiv"}, + {"abbr": "ur", "lang": "Urdu", "translated_lang": "اردو", "sys_lang": "ur_PK.UTF-8", "default_timezone": "Asia/Karachi"}, + {"abbr": "uz", "lang": "Uzbek", "translated_lang": "O'zbek", "sys_lang": "uz_UZ.UTF-8", "default_timezone": "Asia/Tashkent"}, {"abbr": "ve", "lang": "Venda"}, {"abbr": "vi", "lang": "Vietnamese"}, {"abbr": "vo", "lang": "Volapük"}, @@ -181,6 +181,6 @@ {"abbr": "yi", "lang": "Yiddish"}, {"abbr": "yo", "lang": "Yoruba"}, {"abbr": "za", "lang": "Zhuang"}, - {"abbr": "zh-CN", "lang": "Simplified Chinese", "translated_lang": "简体中文"}, - {"abbr": "zh-TW", "lang": "Traditional Chinese", "translated_lang": "繁體中文"}, + {"abbr": "zh-CN", "lang": "Simplified Chinese", "translated_lang": "简体中文", "sys_lang": "zh_CN.UTF-8"}, + {"abbr": "zh-TW", "lang": "Traditional Chinese", "translated_lang": "繁體中文", "sys_lang": "zh_TW.UTF-8", "default_timezone": "Asia/Taipei"}, {"abbr": "zu", "lang": "Zulu"}] diff --git a/archinstall/locales/uk/LC_MESSAGES/base.mo b/archinstall/locales/uk/LC_MESSAGES/base.mo index 1cb241b953ad7bfa04c89f0cc971028c6c9b569b..03a172401411d4241d2f088d70dd38b3b8d686c4 100644 GIT binary patch delta 15094 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 Date: Tue, 28 Apr 2026 17:19:38 +0300 Subject: [PATCH 2/2] Remove Yes/No singleton cache on MenuItem MenuItem.yes() and .no() cached their first-call result on the class, so labels stayed stuck in the language active at first call. With the new locale-apply Confirmation firing right after a language switch this became user-visible. Drop the cache and construct fresh items per call - allocation cost is negligible and equality continues to work via the dataclass __eq__. --- archinstall/tui/ui/menu_item.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/archinstall/tui/ui/menu_item.py b/archinstall/tui/ui/menu_item.py index 4c10e275ef..ac198aa6ee 100644 --- a/archinstall/tui/ui/menu_item.py +++ b/archinstall/tui/ui/menu_item.py @@ -2,7 +2,7 @@ from dataclasses import dataclass, field from enum import Enum from functools import cached_property -from typing import Any, ClassVar, Self, override +from typing import Any, Self, override from archinstall.lib.translationhandler import tr @@ -23,9 +23,6 @@ class MenuItem: _id: str = '' - _yes: ClassVar[Self | None] = None - _no: ClassVar[Self | None] = None - def __post_init__(self) -> None: if self.key is not None: self._id = self.key @@ -45,17 +42,11 @@ def get_value(self) -> Any: @classmethod def yes(cls, action: Callable[[Any], Any] | None = None) -> Self: - if cls._yes is None: - cls._yes = cls(tr('Yes'), value=True, key='yes', action=action) - - return cls._yes + return cls(tr('Yes'), value=True, key='yes', action=action) @classmethod def no(cls, action: Callable[[Any], Any] | None = None) -> Self: - if cls._no is None: - cls._no = cls(tr('No'), value=False, key='no', action=action) - - return cls._no + return cls(tr('No'), value=False, key='no', action=action) def is_empty(self) -> bool: return self.text == '' or self.text is None