Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions src/borg/cockpit/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,17 @@
from textual.containers import Horizontal, Container

from .theme import theme
from .translator import T, TRANSLATOR


class BorgCockpitApp(App):
"""The main TUI Application class for Borg Cockpit."""

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"))]

Check failure on line 23 in src/borg/cockpit/app.py

View workflow job for this annotation

GitHub Actions / lint

ruff (E501)

src/borg/cockpit/app.py:23:121: E501 Line too long (124 > 120)

def compose(self) -> ComposeResult:
"""Create child widgets for the app."""
Expand Down Expand Up @@ -105,8 +106,6 @@

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()
Expand Down
47 changes: 37 additions & 10 deletions src/borg/cockpit/translator.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""
Universal Translator - Converts standard English into Borg Speak.
"""
"""Cockpit UI translations."""

import os

BORG_DICTIONARY = { # English -> Borg
# UI Strings
Expand All @@ -16,32 +16,59 @@
}


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."""
self.enabled = not self.enabled
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)

Expand Down
2 changes: 1 addition & 1 deletion src/borg/cockpit/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -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."""
Expand Down
21 changes: 21 additions & 0 deletions src/borg/testsuite/cockpit_translator_test.py
Original file line number Diff line number Diff line change
@@ -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"
Loading