# ABOUTME: Locale detection and string lookup for the greeter UI. # ABOUTME: Reads system locale (LANG or /etc/locale.conf) and provides DE or EN strings. import os from dataclasses import dataclass from pathlib import Path DEFAULT_LOCALE_CONF = Path("/etc/locale.conf") @dataclass(frozen=True) class Strings: """All user-visible strings for the greeter UI.""" # UI labels password_placeholder: str reboot_tooltip: str shutdown_tooltip: str # Error messages no_session_selected: str greetd_sock_not_set: str greetd_sock_not_absolute: str greetd_sock_not_socket: str greetd_sock_unreachable: str auth_failed: str wrong_password: str multi_stage_unsupported: str invalid_session_command: str session_start_failed: str reboot_failed: str shutdown_failed: str # Templates (use .format()) connection_error: str socket_error: str faillock_attempts_remaining: str faillock_locked: str _STRINGS_DE = Strings( password_placeholder="Passwort", reboot_tooltip="Neustart", shutdown_tooltip="Herunterfahren", no_session_selected="Keine Session ausgewählt", greetd_sock_not_set="GREETD_SOCK nicht gesetzt", greetd_sock_not_absolute="GREETD_SOCK ist kein absoluter Pfad", greetd_sock_not_socket="GREETD_SOCK zeigt nicht auf einen Socket", greetd_sock_unreachable="GREETD_SOCK nicht erreichbar", auth_failed="Authentifizierung fehlgeschlagen", wrong_password="Falsches Passwort", multi_stage_unsupported="Mehrstufige Authentifizierung wird nicht unterstützt", invalid_session_command="Ungültiger Session-Befehl", session_start_failed="Session konnte nicht gestartet werden", reboot_failed="Neustart fehlgeschlagen", shutdown_failed="Herunterfahren fehlgeschlagen", connection_error="Verbindungsfehler: {error}", socket_error="Socket-Fehler: {error}", faillock_attempts_remaining="Noch {n} Versuch(e) vor Kontosperrung!", faillock_locked="Konto ist möglicherweise gesperrt", ) _STRINGS_EN = Strings( password_placeholder="Password", reboot_tooltip="Reboot", shutdown_tooltip="Shut down", no_session_selected="No session selected", greetd_sock_not_set="GREETD_SOCK not set", greetd_sock_not_absolute="GREETD_SOCK is not an absolute path", greetd_sock_not_socket="GREETD_SOCK does not point to a socket", greetd_sock_unreachable="GREETD_SOCK unreachable", auth_failed="Authentication failed", wrong_password="Wrong password", multi_stage_unsupported="Multi-stage authentication is not supported", invalid_session_command="Invalid session command", session_start_failed="Failed to start session", reboot_failed="Reboot failed", shutdown_failed="Shutdown failed", connection_error="Connection error: {error}", socket_error="Socket error: {error}", faillock_attempts_remaining="{n} attempt(s) remaining before lockout!", faillock_locked="Account may be locked", ) _LOCALE_MAP: dict[str, Strings] = { "de": _STRINGS_DE, "en": _STRINGS_EN, } def detect_locale(locale_conf_path: Path = DEFAULT_LOCALE_CONF) -> str: """Determine the system language from LANG env var or /etc/locale.conf.""" lang = os.environ.get("LANG") if not lang and locale_conf_path.exists(): for line in locale_conf_path.read_text().splitlines(): if line.startswith("LANG="): lang = line.split("=", 1)[1].strip() break if not lang or lang in ("C", "POSIX"): return "en" # Extract language prefix: "de_DE.UTF-8" → "de" lang = lang.split("_")[0].split(".")[0] return lang def load_strings(locale: str | None = None) -> Strings: """Return the string table for the given locale, defaulting to English.""" if locale is None: locale = detect_locale() return _LOCALE_MAP.get(locale, _STRINGS_EN)