From f6f6b4fbf1c9a0eb9ec66b0a94dfeabad4478b56 Mon Sep 17 00:00:00 2001 From: Softer Date: Thu, 2 Apr 2026 06:19:05 +0300 Subject: [PATCH 01/12] Set console font automatically when selecting language Add console_font field to languages.json and Language dataclass. When a language is activated, setfont is called automatically, falling back to default8x16 on error or for languages without a custom font. Also activate translation when loading language from config file. --- archinstall/lib/args.py | 1 + archinstall/lib/general/general_menu.py | 6 +++--- archinstall/lib/translationhandler.py | 28 ++++++++++++++++++++++++- archinstall/locales/languages.json | 2 +- 4 files changed, 32 insertions(+), 5 deletions(-) diff --git a/archinstall/lib/args.py b/archinstall/lib/args.py index c61bc5a15f..8989edbd50 100644 --- a/archinstall/lib/args.py +++ b/archinstall/lib/args.py @@ -142,6 +142,7 @@ def from_config(cls, args_config: dict[str, Any], args: Arguments) -> Self: if archinstall_lang := args_config.get('archinstall-language', None): arch_config.archinstall_language = translation_handler.get_language_by_name(archinstall_lang) + translation_handler.activate(arch_config.archinstall_language) if disk_config := args_config.get('disk_config', {}): enc_password = args_config.get('encryption_password', '') diff --git a/archinstall/lib/general/general_menu.py b/archinstall/lib/general/general_menu.py index 5a95762ead..bd74378f01 100644 --- a/archinstall/lib/general/general_menu.py +++ b/archinstall/lib/general/general_menu.py @@ -106,9 +106,9 @@ async def select_archinstall_language(languages: list[Language], preset: Languag group = MenuItemGroup(items, sort_items=True) group.set_focus_by_value(preset) - title = 'NOTE: If a language can not displayed properly, a proper font must be set manually in the console.\n' - title += 'All available fonts can be found in "/usr/share/kbd/consolefonts"\n' - title += 'e.g. setfont LatGrkCyr-8x16 (to display latin/greek/cyrillic characters)\n' + title = 'NOTE: Console font will be set automatically for supported languages.\n' + title += 'For other languages, fonts can be found in "/usr/share/kbd/consolefonts"\n' + title += 'and set manually with: setfont \n' result = await Selection[Language]( header=title, diff --git a/archinstall/lib/translationhandler.py b/archinstall/lib/translationhandler.py index c7ce959bec..d897207706 100644 --- a/archinstall/lib/translationhandler.py +++ b/archinstall/lib/translationhandler.py @@ -2,6 +2,7 @@ import gettext import json import os +import subprocess from dataclasses import dataclass from pathlib import Path from typing import override @@ -14,6 +15,7 @@ class Language: translation: gettext.NullTranslations translation_percent: int translated_lang: str | None + console_font: str | None = None @property def display_name(self) -> str: @@ -31,6 +33,28 @@ def json(self) -> str: return self.name_en +_DEFAULT_FONT = 'default8x16' + + +def _set_console_font(font_name: str | None) -> None: + """ + Set the console font via setfont. + Falls back to default8x16 if font_name is None or setfont fails. + """ + target = font_name or _DEFAULT_FONT + + try: + result = subprocess.run(['setfont', target], capture_output=True, timeout=10) + if result.returncode != 0 and target != _DEFAULT_FONT: + subprocess.run(['setfont', _DEFAULT_FONT], capture_output=True, timeout=10) + except (FileNotFoundError, subprocess.TimeoutExpired, OSError): + if target != _DEFAULT_FONT: + try: + subprocess.run(['setfont', _DEFAULT_FONT], capture_output=True, timeout=10) + except (FileNotFoundError, subprocess.TimeoutExpired, OSError): + pass + + class TranslationHandler: def __init__(self) -> None: self._base_pot = 'base.pot' @@ -57,6 +81,7 @@ def _get_translations(self) -> list[Language]: abbr = mapping_entry['abbr'] lang = mapping_entry['lang'] translated_lang = mapping_entry.get('translated_lang', None) + console_font = mapping_entry.get('console_font', None) try: # get a translation for a specific language @@ -71,7 +96,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) + language = Language(abbr, lang, translation, percent, translated_lang, console_font) languages.append(language) except FileNotFoundError as err: raise FileNotFoundError(f"Could not locate language file for '{lang}': {err}") @@ -133,6 +158,7 @@ def activate(self, language: Language) -> None: """ # The install() call has the side effect of assigning GNUTranslations.gettext to builtins._ language.translation.install() + _set_console_font(language.console_font) def _get_locales_dir(self) -> Path: """ diff --git a/archinstall/locales/languages.json b/archinstall/locales/languages.json index 1cc5cda434..2dbcf3b4d4 100644 --- a/archinstall/locales/languages.json +++ b/archinstall/locales/languages.json @@ -169,7 +169,7 @@ {"abbr": "tr", "lang": "Turkish", "translated_lang" : "Türkçe"}, {"abbr": "tw", "lang": "Twi"}, {"abbr": "ug", "lang": "Uighur"}, - {"abbr": "uk", "lang": "Ukrainian"}, + {"abbr": "uk", "lang": "Ukrainian", "console_font": "UniCyr_8x16"}, {"abbr": "ur", "lang": "Urdu", "translated_lang": "اردو"}, {"abbr": "uz", "lang": "Uzbek", "translated_lang": "O'zbek"}, {"abbr": "ve", "lang": "Venda"}, From e45995fa7103287dfe0a0b2841f9c887780f1ab3 Mon Sep 17 00:00:00 2001 From: Softer Date: Thu, 2 Apr 2026 21:47:15 +0300 Subject: [PATCH 02/12] Support FONT environment variable for console font override When FONT env var is set, use it as the console font instead of the language-specific font mapping from languages.json. The font is applied at startup and preserved across language switches. On exit (success or failure), restore the console font to default8x16. --- archinstall/lib/translationhandler.py | 4 +++- archinstall/main.py | 7 ++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/archinstall/lib/translationhandler.py b/archinstall/lib/translationhandler.py index d897207706..903185d010 100644 --- a/archinstall/lib/translationhandler.py +++ b/archinstall/lib/translationhandler.py @@ -34,6 +34,7 @@ def json(self) -> str: _DEFAULT_FONT = 'default8x16' +_ENV_FONT = os.environ.get('FONT') def _set_console_font(font_name: str | None) -> None: @@ -158,7 +159,8 @@ def activate(self, language: Language) -> None: """ # The install() call has the side effect of assigning GNUTranslations.gettext to builtins._ language.translation.install() - _set_console_font(language.console_font) + + _set_console_font(_ENV_FONT if _ENV_FONT else language.console_font) def _get_locales_dir(self) -> Path: """ diff --git a/archinstall/main.py b/archinstall/main.py index ac072b0e84..5d46348895 100644 --- a/archinstall/main.py +++ b/archinstall/main.py @@ -16,7 +16,7 @@ from archinstall.lib.output import debug, error, info, warn from archinstall.lib.packages.util import check_version_upgrade from archinstall.lib.pacman.pacman import Pacman -from archinstall.lib.translationhandler import tr +from archinstall.lib.translationhandler import _ENV_FONT, _set_console_font, tr from archinstall.lib.utils.util import running_from_iso from archinstall.tui.ui.components import tui @@ -79,6 +79,9 @@ def run() -> int: OR straight as a module: python -m archinstall In any case we will be attempting to load the provided script to be run from the scripts/ folder """ + if _ENV_FONT: + _set_console_font(_ENV_FONT) + arch_config_handler = ArchConfigHandler() if '--help' in sys.argv or '-h' in sys.argv: @@ -159,6 +162,8 @@ def main() -> int: _error_message(exc) rc = 1 + _set_console_font(None) + return rc From 92c990d917b9f8a07bfdc9fee13411e173e46073 Mon Sep 17 00:00:00 2001 From: Softer Date: Thu, 2 Apr 2026 21:57:30 +0300 Subject: [PATCH 03/12] CI checks fix --- archinstall/lib/translationhandler.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/archinstall/lib/translationhandler.py b/archinstall/lib/translationhandler.py index 903185d010..53cd5f08be 100644 --- a/archinstall/lib/translationhandler.py +++ b/archinstall/lib/translationhandler.py @@ -45,13 +45,13 @@ def _set_console_font(font_name: str | None) -> None: target = font_name or _DEFAULT_FONT try: - result = subprocess.run(['setfont', target], capture_output=True, timeout=10) + result = subprocess.run(['setfont', target], capture_output=True, check=False, timeout=10) if result.returncode != 0 and target != _DEFAULT_FONT: - subprocess.run(['setfont', _DEFAULT_FONT], capture_output=True, timeout=10) + subprocess.run(['setfont', _DEFAULT_FONT], capture_output=True, check=False, timeout=10) except (FileNotFoundError, subprocess.TimeoutExpired, OSError): if target != _DEFAULT_FONT: try: - subprocess.run(['setfont', _DEFAULT_FONT], capture_output=True, timeout=10) + subprocess.run(['setfont', _DEFAULT_FONT], capture_output=True, check=False, timeout=10) except (FileNotFoundError, subprocess.TimeoutExpired, OSError): pass From 960bc1bae6ebfe8bdcd5723b45a48ec4f229a077 Mon Sep 17 00:00:00 2001 From: Softer Date: Sun, 5 Apr 2026 03:39:16 +0300 Subject: [PATCH 04/12] Try to restore original console font with setfont -O --- archinstall/lib/translationhandler.py | 42 ++++++++++++++++++++++----- archinstall/main.py | 6 ++-- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/archinstall/lib/translationhandler.py b/archinstall/lib/translationhandler.py index 53cd5f08be..26b1fc577c 100644 --- a/archinstall/lib/translationhandler.py +++ b/archinstall/lib/translationhandler.py @@ -2,11 +2,14 @@ import gettext import json import os -import subprocess from dataclasses import dataclass from pathlib import Path from typing import override +from archinstall.lib.command import SysCommand +from archinstall.lib.exceptions import SysCallError +from archinstall.lib.output import debug + @dataclass class Language: @@ -35,6 +38,8 @@ def json(self) -> str: _DEFAULT_FONT = 'default8x16' _ENV_FONT = os.environ.get('FONT') +_FONT_BACKUP = Path('/tmp/archinstall_font_backup') +_CMAP_BACKUP = Path('/tmp/archinstall_cmap_backup') def _set_console_font(font_name: str | None) -> None: @@ -45,15 +50,36 @@ def _set_console_font(font_name: str | None) -> None: target = font_name or _DEFAULT_FONT try: - result = subprocess.run(['setfont', target], capture_output=True, check=False, timeout=10) - if result.returncode != 0 and target != _DEFAULT_FONT: - subprocess.run(['setfont', _DEFAULT_FONT], capture_output=True, check=False, timeout=10) - except (FileNotFoundError, subprocess.TimeoutExpired, OSError): + SysCommand(f'setfont {target}') + except SysCallError as err: + debug(f'Failed to set console font {target}: {err}') if target != _DEFAULT_FONT: try: - subprocess.run(['setfont', _DEFAULT_FONT], capture_output=True, check=False, timeout=10) - except (FileNotFoundError, subprocess.TimeoutExpired, OSError): - pass + SysCommand(f'setfont {_DEFAULT_FONT}') + except SysCallError as err: + debug(f'Failed to set default console font: {err}') + + +def _save_console_font() -> None: + """Save the current console font (with unicode map) and console map to backup files.""" + try: + SysCommand(f'setfont -O {_FONT_BACKUP} -om {_CMAP_BACKUP}') + except SysCallError as err: + debug(f'Failed to save console font: {err}') + + +def _restore_console_font() -> None: + """Restore console font (with unicode map) and console map from backup, falling back to default8x16.""" + if _FONT_BACKUP.exists(): + args = str(_FONT_BACKUP) + if _CMAP_BACKUP.exists(): + args += f' -m {_CMAP_BACKUP}' + _set_console_font(args) + else: + _set_console_font(None) + + _FONT_BACKUP.unlink(missing_ok=True) + _CMAP_BACKUP.unlink(missing_ok=True) class TranslationHandler: diff --git a/archinstall/main.py b/archinstall/main.py index 5d46348895..44b881303b 100644 --- a/archinstall/main.py +++ b/archinstall/main.py @@ -16,7 +16,7 @@ from archinstall.lib.output import debug, error, info, warn from archinstall.lib.packages.util import check_version_upgrade from archinstall.lib.pacman.pacman import Pacman -from archinstall.lib.translationhandler import _ENV_FONT, _set_console_font, tr +from archinstall.lib.translationhandler import _ENV_FONT, _restore_console_font, _save_console_font, _set_console_font, tr from archinstall.lib.utils.util import running_from_iso from archinstall.tui.ui.components import tui @@ -79,6 +79,8 @@ def run() -> int: OR straight as a module: python -m archinstall In any case we will be attempting to load the provided script to be run from the scripts/ folder """ + _save_console_font() + if _ENV_FONT: _set_console_font(_ENV_FONT) @@ -162,7 +164,7 @@ def main() -> int: _error_message(exc) rc = 1 - _set_console_font(None) + _restore_console_font() return rc From 81f62a06115fc974fce15d154ee22ec35d03a2fc Mon Sep 17 00:00:00 2001 From: Softer Date: Sun, 5 Apr 2026 10:44:44 +0300 Subject: [PATCH 05/12] Fix for pylint --- archinstall/lib/translationhandler.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/archinstall/lib/translationhandler.py b/archinstall/lib/translationhandler.py index 26b1fc577c..dbf4bfc742 100644 --- a/archinstall/lib/translationhandler.py +++ b/archinstall/lib/translationhandler.py @@ -56,8 +56,8 @@ def _set_console_font(font_name: str | None) -> None: if target != _DEFAULT_FONT: try: SysCommand(f'setfont {_DEFAULT_FONT}') - except SysCallError as err: - debug(f'Failed to set default console font: {err}') + except SysCallError as fallback_err: + debug(f'Failed to set default console font: {fallback_err}') def _save_console_font() -> None: From ceb8b2ef1cc548ffcdb1cb19564067512112b916 Mon Sep 17 00:00:00 2001 From: Softer Date: Fri, 10 Apr 2026 21:14:53 +0300 Subject: [PATCH 06/12] Restore console font before Textual exits application mode Move font set/restore into Textual lifecycle to prevent color artifacts from 256/512 glyph transitions. Apply FONT env var and language font in on_mount, restore in _on_exit_app. Skip font change when loading language from config (set_font=False) to defer it until TUI starts. --- archinstall/lib/args.py | 2 +- archinstall/lib/translationhandler.py | 42 ++++++++++++++++----------- archinstall/main.py | 9 ++---- archinstall/tui/ui/components.py | 9 ++++++ 4 files changed, 38 insertions(+), 24 deletions(-) diff --git a/archinstall/lib/args.py b/archinstall/lib/args.py index 8989edbd50..6b72acfcca 100644 --- a/archinstall/lib/args.py +++ b/archinstall/lib/args.py @@ -142,7 +142,7 @@ def from_config(cls, args_config: dict[str, Any], args: Arguments) -> Self: if archinstall_lang := args_config.get('archinstall-language', None): arch_config.archinstall_language = translation_handler.get_language_by_name(archinstall_lang) - translation_handler.activate(arch_config.archinstall_language) + translation_handler.activate(arch_config.archinstall_language, set_font=False) if disk_config := args_config.get('disk_config', {}): enc_password = args_config.get('encryption_password', '') diff --git a/archinstall/lib/translationhandler.py b/archinstall/lib/translationhandler.py index dbf4bfc742..989188adfb 100644 --- a/archinstall/lib/translationhandler.py +++ b/archinstall/lib/translationhandler.py @@ -42,22 +42,21 @@ def json(self) -> str: _CMAP_BACKUP = Path('/tmp/archinstall_cmap_backup') -def _set_console_font(font_name: str | None) -> None: +def _set_console_font(font_name: str | None) -> bool: """ Set the console font via setfont. - Falls back to default8x16 if font_name is None or setfont fails. + If font_name is None, sets default8x16. + On failure, keeps the current font unchanged. + Returns True on success, False on failure. """ target = font_name or _DEFAULT_FONT try: SysCommand(f'setfont {target}') + return True except SysCallError as err: debug(f'Failed to set console font {target}: {err}') - if target != _DEFAULT_FONT: - try: - SysCommand(f'setfont {_DEFAULT_FONT}') - except SysCallError as fallback_err: - debug(f'Failed to set default console font: {fallback_err}') + return False def _save_console_font() -> None: @@ -69,14 +68,14 @@ def _save_console_font() -> None: def _restore_console_font() -> None: - """Restore console font (with unicode map) and console map from backup, falling back to default8x16.""" - if _FONT_BACKUP.exists(): - args = str(_FONT_BACKUP) - if _CMAP_BACKUP.exists(): - args += f' -m {_CMAP_BACKUP}' - _set_console_font(args) - else: - _set_console_font(None) + """Restore console font (with unicode map) and console map from backup.""" + if not _FONT_BACKUP.exists(): + return + + args = str(_FONT_BACKUP) + if _CMAP_BACKUP.exists(): + args += f' -m {_CMAP_BACKUP}' + _set_console_font(args) _FONT_BACKUP.unlink(missing_ok=True) _CMAP_BACKUP.unlink(missing_ok=True) @@ -86,6 +85,7 @@ class TranslationHandler: def __init__(self) -> None: self._base_pot = 'base.pot' self._languages = 'languages.json' + self._active_language: Language | None = None self._total_messages = self._get_total_active_messages() self._translated_languages = self._get_translations() @@ -94,6 +94,12 @@ def __init__(self) -> None: def translated_languages(self) -> list[Language]: return self._translated_languages + @property + def active_font(self) -> str | None: + if self._active_language is not None: + return self._active_language.console_font + return None + def _get_translations(self) -> list[Language]: """ Load all translated languages and return a list of such @@ -179,14 +185,16 @@ def get_language_by_abbr(self, abbr: str) -> Language: except Exception: raise ValueError(f'No language with abbreviation "{abbr}" found') - def activate(self, language: Language) -> None: + def activate(self, language: Language, set_font: bool = True) -> None: """ Set the provided language as the current translation """ # The install() call has the side effect of assigning GNUTranslations.gettext to builtins._ language.translation.install() + self._active_language = language - _set_console_font(_ENV_FONT if _ENV_FONT else language.console_font) + if set_font: + _set_console_font(_ENV_FONT if _ENV_FONT else language.console_font) def _get_locales_dir(self) -> Path: """ diff --git a/archinstall/main.py b/archinstall/main.py index 44b881303b..2d4342f528 100644 --- a/archinstall/main.py +++ b/archinstall/main.py @@ -16,7 +16,7 @@ from archinstall.lib.output import debug, error, info, warn from archinstall.lib.packages.util import check_version_upgrade from archinstall.lib.pacman.pacman import Pacman -from archinstall.lib.translationhandler import _ENV_FONT, _restore_console_font, _save_console_font, _set_console_font, tr +from archinstall.lib.translationhandler import _restore_console_font, _save_console_font, tr from archinstall.lib.utils.util import running_from_iso from archinstall.tui.ui.components import tui @@ -79,11 +79,6 @@ def run() -> int: OR straight as a module: python -m archinstall In any case we will be attempting to load the provided script to be run from the scripts/ folder """ - _save_console_font() - - if _ENV_FONT: - _set_console_font(_ENV_FONT) - arch_config_handler = ArchConfigHandler() if '--help' in sys.argv or '-h' in sys.argv: @@ -100,6 +95,8 @@ def run() -> int: print(tr('Archinstall requires root privileges to run. See --help for more.')) return 1 + _save_console_font() + _log_sys_info() if not arch_config_handler.args.offline: diff --git a/archinstall/tui/ui/components.py b/archinstall/tui/ui/components.py index 7882b1ef2a..3a03ae9ffc 100644 --- a/archinstall/tui/ui/components.py +++ b/archinstall/tui/ui/components.py @@ -1236,6 +1236,11 @@ def __init__(self, main: InstanceRunnable[ValueT] | Callable[[], Awaitable[Value super().__init__(ansi_color=True) self._main = main + async def _on_exit_app(self) -> None: + from archinstall.lib.translationhandler import _restore_console_font + _restore_console_font() + await super()._on_exit_app() + def action_trigger_help(self) -> None: from textual.widgets import HelpPanel @@ -1245,6 +1250,10 @@ def action_trigger_help(self) -> None: _ = self.screen.mount(HelpPanel()) def on_mount(self) -> None: + from archinstall.lib.translationhandler import _ENV_FONT, _set_console_font, translation_handler + font = _ENV_FONT or translation_handler.active_font + if font: + _set_console_font(font) self._run_worker() @work From b7239097ffed61e70022b9825c87ce72a10d7ca7 Mon Sep 17 00:00:00 2001 From: Softer Date: Fri, 10 Apr 2026 21:44:46 +0300 Subject: [PATCH 07/12] Fall back to language font mapping when FONT env var is invalid Add _using_env_font flag to skip mapping only when FONT was successfully applied. Show info message after TUI exits if FONT could not be set. --- archinstall/lib/translationhandler.py | 5 +++-- archinstall/tui/ui/components.py | 21 ++++++++++++++++----- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/archinstall/lib/translationhandler.py b/archinstall/lib/translationhandler.py index 989188adfb..3cfbecbc7c 100644 --- a/archinstall/lib/translationhandler.py +++ b/archinstall/lib/translationhandler.py @@ -40,6 +40,7 @@ def json(self) -> str: _ENV_FONT = os.environ.get('FONT') _FONT_BACKUP = Path('/tmp/archinstall_font_backup') _CMAP_BACKUP = Path('/tmp/archinstall_cmap_backup') +_using_env_font = False def _set_console_font(font_name: str | None) -> bool: @@ -193,8 +194,8 @@ def activate(self, language: Language, set_font: bool = True) -> None: language.translation.install() self._active_language = language - if set_font: - _set_console_font(_ENV_FONT if _ENV_FONT else language.console_font) + if set_font and not _using_env_font: + _set_console_font(language.console_font) def _get_locales_dir(self) -> Path: """ diff --git a/archinstall/tui/ui/components.py b/archinstall/tui/ui/components.py index 3a03ae9ffc..d81e0b2feb 100644 --- a/archinstall/tui/ui/components.py +++ b/archinstall/tui/ui/components.py @@ -19,7 +19,7 @@ from textual.widgets.selection_list import Selection from textual.worker import WorkerCancelled -from archinstall.lib.output import debug +from archinstall.lib.output import debug, info from archinstall.lib.translationhandler import tr from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup from archinstall.tui.ui.result import Result, ResultType @@ -1250,10 +1250,16 @@ def action_trigger_help(self) -> None: _ = self.screen.mount(HelpPanel()) def on_mount(self) -> None: - from archinstall.lib.translationhandler import _ENV_FONT, _set_console_font, translation_handler - font = _ENV_FONT or translation_handler.active_font - if font: - _set_console_font(font) + import archinstall.lib.translationhandler as th + if th._ENV_FONT: + if th._set_console_font(th._ENV_FONT): + th._using_env_font = True + else: + debug(f'FONT={th._ENV_FONT} could not be set, using language font mapping') + if th.translation_handler.active_font: + th._set_console_font(th.translation_handler.active_font) + elif th.translation_handler.active_font: + th._set_console_font(th.translation_handler.active_font) self._run_worker() @work @@ -1284,9 +1290,14 @@ class TApp: app: _AppInstance[Any] | None = None def run(self, main: InstanceRunnable[ValueT] | Callable[[], Awaitable[ValueT]]) -> ValueT: + import archinstall.lib.translationhandler as th + TApp.app = _AppInstance(main) result: ValueT | Exception | None = TApp.app.run() + if th._ENV_FONT and not th._using_env_font: + info(f'FONT={th._ENV_FONT} could not be set, using language font mapping') + if isinstance(result, Exception): raise result From 3ed54eb34f6b748c783395428bc2736fd2ca49e8 Mon Sep 17 00:00:00 2001 From: Softer Date: Fri, 10 Apr 2026 22:24:54 +0300 Subject: [PATCH 08/12] Use tempfile for console font backup files --- archinstall/lib/translationhandler.py | 32 ++++++++++++++++++--------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/archinstall/lib/translationhandler.py b/archinstall/lib/translationhandler.py index 3cfbecbc7c..58c85286d6 100644 --- a/archinstall/lib/translationhandler.py +++ b/archinstall/lib/translationhandler.py @@ -2,6 +2,7 @@ import gettext import json import os +import tempfile from dataclasses import dataclass from pathlib import Path from typing import override @@ -38,8 +39,8 @@ def json(self) -> str: _DEFAULT_FONT = 'default8x16' _ENV_FONT = os.environ.get('FONT') -_FONT_BACKUP = Path('/tmp/archinstall_font_backup') -_CMAP_BACKUP = Path('/tmp/archinstall_cmap_backup') +_font_backup: Path | None = None +_cmap_backup: Path | None = None _using_env_font = False @@ -61,25 +62,36 @@ def _set_console_font(font_name: str | None) -> bool: def _save_console_font() -> None: - """Save the current console font (with unicode map) and console map to backup files.""" + """Save the current console font (with unicode map) and console map to temp files.""" + global _font_backup, _cmap_backup + try: - SysCommand(f'setfont -O {_FONT_BACKUP} -om {_CMAP_BACKUP}') + _font_backup = Path(tempfile.mktemp(prefix='archinstall_font_')) + _cmap_backup = Path(tempfile.mktemp(prefix='archinstall_cmap_')) + SysCommand(f'setfont -O {_font_backup} -om {_cmap_backup}') except SysCallError as err: debug(f'Failed to save console font: {err}') + _font_backup = None + _cmap_backup = None def _restore_console_font() -> None: """Restore console font (with unicode map) and console map from backup.""" - if not _FONT_BACKUP.exists(): + global _font_backup, _cmap_backup + + if _font_backup is None or not _font_backup.exists(): return - args = str(_FONT_BACKUP) - if _CMAP_BACKUP.exists(): - args += f' -m {_CMAP_BACKUP}' + args = str(_font_backup) + if _cmap_backup is not None and _cmap_backup.exists(): + args += f' -m {_cmap_backup}' _set_console_font(args) - _FONT_BACKUP.unlink(missing_ok=True) - _CMAP_BACKUP.unlink(missing_ok=True) + _font_backup.unlink(missing_ok=True) + _font_backup = None + if _cmap_backup is not None: + _cmap_backup.unlink(missing_ok=True) + _cmap_backup = None class TranslationHandler: From 511e8488c22bbd6457db7d66d5091340b7133edf Mon Sep 17 00:00:00 2001 From: Softer Date: Fri, 10 Apr 2026 22:38:35 +0300 Subject: [PATCH 09/12] Fix linter errors: use mkstemp, close fds, add @override --- archinstall/lib/translationhandler.py | 51 +++++++++++++++------------ archinstall/tui/ui/components.py | 5 +-- 2 files changed, 32 insertions(+), 24 deletions(-) diff --git a/archinstall/lib/translationhandler.py b/archinstall/lib/translationhandler.py index 58c85286d6..a3a9e5be9d 100644 --- a/archinstall/lib/translationhandler.py +++ b/archinstall/lib/translationhandler.py @@ -39,9 +39,16 @@ def json(self) -> str: _DEFAULT_FONT = 'default8x16' _ENV_FONT = os.environ.get('FONT') -_font_backup: Path | None = None -_cmap_backup: Path | None = None -_using_env_font = False + + +class _FontState: + def __init__(self) -> None: + self.font_backup: Path | None = None + self.cmap_backup: Path | None = None + self.using_env_font: bool = False + + +_font_state = _FontState() def _set_console_font(font_name: str | None) -> bool: @@ -63,35 +70,35 @@ def _set_console_font(font_name: str | None) -> bool: def _save_console_font() -> None: """Save the current console font (with unicode map) and console map to temp files.""" - global _font_backup, _cmap_backup - try: - _font_backup = Path(tempfile.mktemp(prefix='archinstall_font_')) - _cmap_backup = Path(tempfile.mktemp(prefix='archinstall_cmap_')) - SysCommand(f'setfont -O {_font_backup} -om {_cmap_backup}') + font_fd, font_path = tempfile.mkstemp(prefix='archinstall_font_') + cmap_fd, cmap_path = tempfile.mkstemp(prefix='archinstall_cmap_') + os.close(font_fd) + os.close(cmap_fd) + _font_state.font_backup = Path(font_path) + _font_state.cmap_backup = Path(cmap_path) + SysCommand(f'setfont -O {_font_state.font_backup} -om {_font_state.cmap_backup}') except SysCallError as err: debug(f'Failed to save console font: {err}') - _font_backup = None - _cmap_backup = None + _font_state.font_backup = None + _font_state.cmap_backup = None def _restore_console_font() -> None: """Restore console font (with unicode map) and console map from backup.""" - global _font_backup, _cmap_backup - - if _font_backup is None or not _font_backup.exists(): + if _font_state.font_backup is None or not _font_state.font_backup.exists(): return - args = str(_font_backup) - if _cmap_backup is not None and _cmap_backup.exists(): - args += f' -m {_cmap_backup}' + args = str(_font_state.font_backup) + if _font_state.cmap_backup is not None and _font_state.cmap_backup.exists(): + args += f' -m {_font_state.cmap_backup}' _set_console_font(args) - _font_backup.unlink(missing_ok=True) - _font_backup = None - if _cmap_backup is not None: - _cmap_backup.unlink(missing_ok=True) - _cmap_backup = None + _font_state.font_backup.unlink(missing_ok=True) + _font_state.font_backup = None + if _font_state.cmap_backup is not None: + _font_state.cmap_backup.unlink(missing_ok=True) + _font_state.cmap_backup = None class TranslationHandler: @@ -206,7 +213,7 @@ def activate(self, language: Language, set_font: bool = True) -> None: language.translation.install() self._active_language = language - if set_font and not _using_env_font: + if set_font and not _font_state.using_env_font: _set_console_font(language.console_font) def _get_locales_dir(self) -> Path: diff --git a/archinstall/tui/ui/components.py b/archinstall/tui/ui/components.py index d81e0b2feb..ea378e7eaf 100644 --- a/archinstall/tui/ui/components.py +++ b/archinstall/tui/ui/components.py @@ -1236,6 +1236,7 @@ def __init__(self, main: InstanceRunnable[ValueT] | Callable[[], Awaitable[Value super().__init__(ansi_color=True) self._main = main + @override async def _on_exit_app(self) -> None: from archinstall.lib.translationhandler import _restore_console_font _restore_console_font() @@ -1253,7 +1254,7 @@ def on_mount(self) -> None: import archinstall.lib.translationhandler as th if th._ENV_FONT: if th._set_console_font(th._ENV_FONT): - th._using_env_font = True + th._font_state.using_env_font = True else: debug(f'FONT={th._ENV_FONT} could not be set, using language font mapping') if th.translation_handler.active_font: @@ -1295,7 +1296,7 @@ def run(self, main: InstanceRunnable[ValueT] | Callable[[], Awaitable[ValueT]]) TApp.app = _AppInstance(main) result: ValueT | Exception | None = TApp.app.run() - if th._ENV_FONT and not th._using_env_font: + if th._ENV_FONT and not th._font_state.using_env_font: info(f'FONT={th._ENV_FONT} could not be set, using language font mapping') if isinstance(result, Exception): From 7a536185308dfaf4413df63fe7bae89ab2675444 Mon Sep 17 00:00:00 2001 From: Softer Date: Fri, 10 Apr 2026 22:42:36 +0300 Subject: [PATCH 10/12] Fix ruff formatting --- archinstall/tui/ui/components.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/archinstall/tui/ui/components.py b/archinstall/tui/ui/components.py index ea378e7eaf..16cddeaf49 100644 --- a/archinstall/tui/ui/components.py +++ b/archinstall/tui/ui/components.py @@ -1239,6 +1239,7 @@ def __init__(self, main: InstanceRunnable[ValueT] | Callable[[], Awaitable[Value @override async def _on_exit_app(self) -> None: from archinstall.lib.translationhandler import _restore_console_font + _restore_console_font() await super()._on_exit_app() @@ -1252,6 +1253,7 @@ def action_trigger_help(self) -> None: def on_mount(self) -> None: import archinstall.lib.translationhandler as th + if th._ENV_FONT: if th._set_console_font(th._ENV_FONT): th._font_state.using_env_font = True From 2334e3694243a4d391b5f6cc9a7ca488a0065fa2 Mon Sep 17 00:00:00 2001 From: Softer Date: Sat, 11 Apr 2026 04:50:31 +0300 Subject: [PATCH 11/12] Move font state from module singleton into TranslationHandler --- archinstall/lib/translationhandler.py | 43 +++++++++++---------------- archinstall/tui/ui/components.py | 4 +-- 2 files changed, 20 insertions(+), 27 deletions(-) diff --git a/archinstall/lib/translationhandler.py b/archinstall/lib/translationhandler.py index a3a9e5be9d..488b20059e 100644 --- a/archinstall/lib/translationhandler.py +++ b/archinstall/lib/translationhandler.py @@ -41,16 +41,6 @@ def json(self) -> str: _ENV_FONT = os.environ.get('FONT') -class _FontState: - def __init__(self) -> None: - self.font_backup: Path | None = None - self.cmap_backup: Path | None = None - self.using_env_font: bool = False - - -_font_state = _FontState() - - def _set_console_font(font_name: str | None) -> bool: """ Set the console font via setfont. @@ -75,30 +65,30 @@ def _save_console_font() -> None: cmap_fd, cmap_path = tempfile.mkstemp(prefix='archinstall_cmap_') os.close(font_fd) os.close(cmap_fd) - _font_state.font_backup = Path(font_path) - _font_state.cmap_backup = Path(cmap_path) - SysCommand(f'setfont -O {_font_state.font_backup} -om {_font_state.cmap_backup}') + translation_handler._font_backup = Path(font_path) + translation_handler._cmap_backup = Path(cmap_path) + SysCommand(f'setfont -O {translation_handler._font_backup} -om {translation_handler._cmap_backup}') except SysCallError as err: debug(f'Failed to save console font: {err}') - _font_state.font_backup = None - _font_state.cmap_backup = None + translation_handler._font_backup = None + translation_handler._cmap_backup = None def _restore_console_font() -> None: """Restore console font (with unicode map) and console map from backup.""" - if _font_state.font_backup is None or not _font_state.font_backup.exists(): + if translation_handler._font_backup is None or not translation_handler._font_backup.exists(): return - args = str(_font_state.font_backup) - if _font_state.cmap_backup is not None and _font_state.cmap_backup.exists(): - args += f' -m {_font_state.cmap_backup}' + args = str(translation_handler._font_backup) + if translation_handler._cmap_backup is not None and translation_handler._cmap_backup.exists(): + args += f' -m {translation_handler._cmap_backup}' _set_console_font(args) - _font_state.font_backup.unlink(missing_ok=True) - _font_state.font_backup = None - if _font_state.cmap_backup is not None: - _font_state.cmap_backup.unlink(missing_ok=True) - _font_state.cmap_backup = None + translation_handler._font_backup.unlink(missing_ok=True) + translation_handler._font_backup = None + if translation_handler._cmap_backup is not None: + translation_handler._cmap_backup.unlink(missing_ok=True) + translation_handler._cmap_backup = None class TranslationHandler: @@ -106,6 +96,9 @@ def __init__(self) -> None: self._base_pot = 'base.pot' self._languages = 'languages.json' self._active_language: Language | None = None + self._font_backup: Path | None = None + self._cmap_backup: Path | None = None + self._using_env_font: bool = False self._total_messages = self._get_total_active_messages() self._translated_languages = self._get_translations() @@ -213,7 +206,7 @@ def activate(self, language: Language, set_font: bool = True) -> None: language.translation.install() self._active_language = language - if set_font and not _font_state.using_env_font: + if set_font and not self._using_env_font: _set_console_font(language.console_font) def _get_locales_dir(self) -> Path: diff --git a/archinstall/tui/ui/components.py b/archinstall/tui/ui/components.py index 16cddeaf49..8658ad6229 100644 --- a/archinstall/tui/ui/components.py +++ b/archinstall/tui/ui/components.py @@ -1256,7 +1256,7 @@ def on_mount(self) -> None: if th._ENV_FONT: if th._set_console_font(th._ENV_FONT): - th._font_state.using_env_font = True + th.translation_handler._using_env_font = True else: debug(f'FONT={th._ENV_FONT} could not be set, using language font mapping') if th.translation_handler.active_font: @@ -1298,7 +1298,7 @@ def run(self, main: InstanceRunnable[ValueT] | Callable[[], Awaitable[ValueT]]) TApp.app = _AppInstance(main) result: ValueT | Exception | None = TApp.app.run() - if th._ENV_FONT and not th._font_state.using_env_font: + if th._ENV_FONT and not th.translation_handler._using_env_font: info(f'FONT={th._ENV_FONT} could not be set, using language font mapping') if isinstance(result, Exception): From 5d242440bdbecbc0bc687a4a2f80718eb48ccb69 Mon Sep 17 00:00:00 2001 From: Softer Date: Sun, 12 Apr 2026 08:48:12 +0300 Subject: [PATCH 12/12] Refactor font handling per review feedback --- archinstall/lib/translationhandler.py | 24 ++++++++++++++++++++++-- archinstall/main.py | 6 +++--- archinstall/tui/ui/components.py | 23 +++++------------------ 3 files changed, 30 insertions(+), 23 deletions(-) diff --git a/archinstall/lib/translationhandler.py b/archinstall/lib/translationhandler.py index 488b20059e..23afecb311 100644 --- a/archinstall/lib/translationhandler.py +++ b/archinstall/lib/translationhandler.py @@ -58,7 +58,7 @@ def _set_console_font(font_name: str | None) -> bool: return False -def _save_console_font() -> None: +def save_console_font() -> None: """Save the current console font (with unicode map) and console map to temp files.""" try: font_fd, font_path = tempfile.mkstemp(prefix='archinstall_font_') @@ -74,7 +74,7 @@ def _save_console_font() -> None: translation_handler._cmap_backup = None -def _restore_console_font() -> None: +def restore_console_font() -> None: """Restore console font (with unicode map) and console map from backup.""" if translation_handler._font_backup is None or not translation_handler._font_backup.exists(): return @@ -209,6 +209,26 @@ def activate(self, language: Language, set_font: bool = True) -> None: if set_font and not self._using_env_font: _set_console_font(language.console_font) + def apply_console_font(self) -> None: + """Apply console font from FONT env var or active language mapping. + + If FONT env var is set and valid, use it and skip language mapping. + If FONT is set but invalid, fall back to language font. + If FONT is not set, use active language font. + """ + if _ENV_FONT: + if _set_console_font(_ENV_FONT): + self._using_env_font = True + debug(f'Console font set from FONT env var: {_ENV_FONT}') + else: + debug(f'FONT={_ENV_FONT} could not be set, falling back to language font mapping') + if self.active_font: + _set_console_font(self.active_font) + debug(f'Console font set from language mapping: {self.active_font}') + elif self.active_font: + _set_console_font(self.active_font) + debug(f'Console font set from language mapping: {self.active_font}') + def _get_locales_dir(self) -> Path: """ Get the locales directory path diff --git a/archinstall/main.py b/archinstall/main.py index 2d4342f528..f25b63e6ad 100644 --- a/archinstall/main.py +++ b/archinstall/main.py @@ -16,7 +16,7 @@ from archinstall.lib.output import debug, error, info, warn from archinstall.lib.packages.util import check_version_upgrade from archinstall.lib.pacman.pacman import Pacman -from archinstall.lib.translationhandler import _restore_console_font, _save_console_font, tr +from archinstall.lib.translationhandler import restore_console_font, save_console_font, tr from archinstall.lib.utils.util import running_from_iso from archinstall.tui.ui.components import tui @@ -95,7 +95,7 @@ def run() -> int: print(tr('Archinstall requires root privileges to run. See --help for more.')) return 1 - _save_console_font() + save_console_font() _log_sys_info() @@ -161,7 +161,7 @@ def main() -> int: _error_message(exc) rc = 1 - _restore_console_font() + restore_console_font() return rc diff --git a/archinstall/tui/ui/components.py b/archinstall/tui/ui/components.py index 8658ad6229..bd0a8f2afa 100644 --- a/archinstall/tui/ui/components.py +++ b/archinstall/tui/ui/components.py @@ -19,7 +19,7 @@ from textual.widgets.selection_list import Selection from textual.worker import WorkerCancelled -from archinstall.lib.output import debug, info +from archinstall.lib.output import debug from archinstall.lib.translationhandler import tr from archinstall.tui.ui.menu_item import MenuItem, MenuItemGroup from archinstall.tui.ui.result import Result, ResultType @@ -1238,9 +1238,9 @@ def __init__(self, main: InstanceRunnable[ValueT] | Callable[[], Awaitable[Value @override async def _on_exit_app(self) -> None: - from archinstall.lib.translationhandler import _restore_console_font + from archinstall.lib.translationhandler import restore_console_font - _restore_console_font() + restore_console_font() await super()._on_exit_app() def action_trigger_help(self) -> None: @@ -1252,17 +1252,9 @@ def action_trigger_help(self) -> None: _ = self.screen.mount(HelpPanel()) def on_mount(self) -> None: - import archinstall.lib.translationhandler as th + from archinstall.lib.translationhandler import translation_handler - if th._ENV_FONT: - if th._set_console_font(th._ENV_FONT): - th.translation_handler._using_env_font = True - else: - debug(f'FONT={th._ENV_FONT} could not be set, using language font mapping') - if th.translation_handler.active_font: - th._set_console_font(th.translation_handler.active_font) - elif th.translation_handler.active_font: - th._set_console_font(th.translation_handler.active_font) + translation_handler.apply_console_font() self._run_worker() @work @@ -1293,14 +1285,9 @@ class TApp: app: _AppInstance[Any] | None = None def run(self, main: InstanceRunnable[ValueT] | Callable[[], Awaitable[ValueT]]) -> ValueT: - import archinstall.lib.translationhandler as th - TApp.app = _AppInstance(main) result: ValueT | Exception | None = TApp.app.run() - if th._ENV_FONT and not th.translation_handler._using_env_font: - info(f'FONT={th._ENV_FONT} could not be set, using language font mapping') - if isinstance(result, Exception): raise result