diff --git a/src/borg/cockpit/app.py b/src/borg/cockpit/app.py index e1ed1419ed..9dd1f55f97 100644 --- a/src/borg/cockpit/app.py +++ b/src/borg/cockpit/app.py @@ -10,6 +10,7 @@ from textual.containers import Horizontal, Container from .theme import theme +from .translator import T, TRANSLATOR class BorgCockpitApp(App): @@ -17,9 +18,9 @@ class BorgCockpitApp(App): from .. import __version__ as BORG_VERSION - TITLE = f"Cockpit for BorgBackup {BORG_VERSION}" + TITLE = T(f"Cockpit for BorgBackup {BORG_VERSION}") CSS_PATH = "cockpit.tcss" - BINDINGS = [("q", "quit", "Quit"), ("ctrl+c", "quit", "Quit"), ("t", "toggle_translator", "Toggle Translator")] + BINDINGS = [("q", "quit", T("Quit")), ("ctrl+c", "quit", T("Quit")), ("t", "toggle_translator", T("Toggle Translator"))] def compose(self) -> ComposeResult: """Create child widgets for the app.""" @@ -105,8 +106,6 @@ async def action_quit(self) -> None: def action_toggle_translator(self) -> None: """Toggle the universal translator.""" - from .translator import TRANSLATOR - TRANSLATOR.toggle() # Refresh dynamic UI elements self.query_one("#status").refresh_ui_labels() diff --git a/src/borg/cockpit/translator.py b/src/borg/cockpit/translator.py index 2302d451c4..6c7bba3895 100644 --- a/src/borg/cockpit/translator.py +++ b/src/borg/cockpit/translator.py @@ -1,6 +1,6 @@ -""" -Universal Translator - Converts standard English into Borg Speak. -""" +"""Cockpit UI translations.""" + +import os BORG_DICTIONARY = { # English -> Borg # UI Strings @@ -16,15 +16,43 @@ } +SWEDISH_DICTIONARY = { # English -> Swedish + "**** You're welcome! ****": "**** Varsågod! ****", + "Files: ": "Filer: ", + "Unchanged: ": "Oförändrade: ", + "Modified: ": "Ändrade: ", + "Added: ": "Tillagda: ", + "Other: ": "Övriga: ", + "Errors: ": "Fel: ", + "RC: ": "Returkod: ", + "Speed: ": "Hastighet: ", + "Elapsed: ": "Förfluten tid: ", + "RC: RUNNING": "Returkod: KÖRS", + "Log": "Logg", + "Quit": "Avsluta", + "Toggle Translator": "Växla Borg-språk", + "Cockpit for BorgBackup": "Kontrollpanel för BorgBackup", +} + + +def get_language() -> str: + """Return the requested Cockpit UI language, falling back to English.""" + language = os.environ.get("BORG_COCKPIT_LANGUAGE") + if language is None: + language = os.environ.get("LC_ALL") or os.environ.get("LC_MESSAGES") or os.environ.get("LANG", "") + return language.split(".", maxsplit=1)[0].split("_", maxsplit=1)[0].lower() + + class UniversalTranslator: """ Handles translation of log messages. """ - def __init__(self, enabled: bool = True): + def __init__(self, enabled: bool = True, language: str | None = None): # self.enabled is the opposite of "Translator active" on the TUI, # because in the source, we translate English to Borg. self.enabled = enabled # True: English -> Borg + self.language = language or get_language() def toggle(self): """Toggle translation state.""" @@ -32,16 +60,15 @@ def toggle(self): return self.enabled def translate(self, message: str) -> str: - """Translate a message if enabled.""" - if not self.enabled: - return message + """Translate a message for the active Cockpit language.""" + dictionary = BORG_DICTIONARY if self.enabled else SWEDISH_DICTIONARY if self.language == "sv" else {} # Full matching first - if message in BORG_DICTIONARY: - return BORG_DICTIONARY[message] + if message in dictionary: + return dictionary[message] # Substring matching next - for key, value in BORG_DICTIONARY.items(): + for key, value in dictionary.items(): if key in message: return message.replace(key, value) diff --git a/src/borg/cockpit/widgets.py b/src/borg/cockpit/widgets.py index 4aef72e88f..4d2357f7d1 100644 --- a/src/borg/cockpit/widgets.py +++ b/src/borg/cockpit/widgets.py @@ -107,7 +107,7 @@ def watch_elapsed_time(self, elapsed: float) -> None: days, seconds = divmod(seconds, 86400) h, m, s = seconds // 3600, (seconds % 3600) // 60, seconds % 60 msg = f"Elapsed: {days:02d}d {h:02d}:{m:02d}:{s:02d}" - self.query_one("#status-elapsed").update(msg) + self.query_one("#status-elapsed").update(T(msg)) def refresh_ui_labels(self): """Update static UI labels with current translation.""" diff --git a/src/borg/testsuite/cockpit_translator_test.py b/src/borg/testsuite/cockpit_translator_test.py new file mode 100644 index 0000000000..5695cf2230 --- /dev/null +++ b/src/borg/testsuite/cockpit_translator_test.py @@ -0,0 +1,21 @@ +from borg.cockpit.translator import UniversalTranslator + + +def test_swedish_cockpit_translations(): + translator = UniversalTranslator(enabled=False, language="sv") + + assert translator.translate("Log") == "Logg" + assert translator.translate("Files: 3") == "Filer: 3" + assert translator.translate("Cockpit for BorgBackup 2.0") == "Kontrollpanel för BorgBackup 2.0" + + +def test_english_is_the_default_language(): + translator = UniversalTranslator(enabled=False, language="en") + + assert translator.translate("Files: 3") == "Files: 3" + + +def test_borg_mode_takes_precedence_over_locale(): + translator = UniversalTranslator(enabled=True, language="sv") + + assert translator.translate("Files: 3") == "Drones: 3"