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 1cb241b953..03a1724014 100644 Binary files a/archinstall/locales/uk/LC_MESSAGES/base.mo and b/archinstall/locales/uk/LC_MESSAGES/base.mo differ diff --git a/archinstall/locales/uk/LC_MESSAGES/base.po b/archinstall/locales/uk/LC_MESSAGES/base.po index a13d825cfe..f4923fb22b 100644 --- a/archinstall/locales/uk/LC_MESSAGES/base.po +++ b/archinstall/locales/uk/LC_MESSAGES/base.po @@ -175,6 +175,12 @@ msgstr "Введіть бажаний тип файлової системи д msgid "Archinstall language" msgstr "Мова Archinstall" +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 "Стерти усі вибрані диски та використовувати отпимальну схему розмітки розділів" @@ -2214,3 +2220,9 @@ msgstr "Шифрування диска" #, fuzzy msgid "Select a flavor of KDE Plasma to install" msgstr "Оберіть завантажувач для встановлення" + +msgid "Arial/Times/Courier replacement, Cyrillic support for Steam/games" +msgstr "" + +msgid "wide Unicode coverage, good fallback font" +msgstr "" 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