diff --git a/selfdrive/ui/layouts/__init__.py b/selfdrive/ui/layouts/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/selfdrive/ui/layouts/home.py b/selfdrive/ui/layouts/home.py index f102481c3a..1d3959abd3 100644 --- a/selfdrive/ui/layouts/home.py +++ b/selfdrive/ui/layouts/home.py @@ -8,7 +8,7 @@ from openpilot.selfdrive.ui.widgets.exp_mode_button import ExperimentalModeButto from openpilot.selfdrive.ui.widgets.prime import PrimeWidget from openpilot.selfdrive.ui.widgets.setup import SetupWidget from openpilot.system.ui.lib.text_measure import measure_text_cached -from openpilot.system.ui.lib.application import gui_app, FontWeight, DEFAULT_TEXT_COLOR +from openpilot.system.ui.lib.application import tr, gui_app, FontWeight, DEFAULT_TEXT_COLOR from openpilot.system.ui.widgets import Widget HEADER_HEIGHT = 80 @@ -139,7 +139,7 @@ class HomeLayout(Widget): highlight_color = rl.Color(255, 140, 40, 255) if self.current_state == HomeLayoutState.UPDATE else rl.Color(255, 102, 0, 255) rl.draw_rectangle_rounded(self.update_notif_rect, 0.3, 10, highlight_color) - text = "UPDATE" + text = tr("UPDATE") text_width = measure_text_cached(font, text, HEAD_BUTTON_FONT_SIZE).x text_x = self.update_notif_rect.x + (self.update_notif_rect.width - text_width) // 2 text_y = self.update_notif_rect.y + (self.update_notif_rect.height - HEAD_BUTTON_FONT_SIZE) // 2 diff --git a/selfdrive/ui/update_translations.sh b/selfdrive/ui/update_translations.sh new file mode 100755 index 0000000000..c37c7da879 --- /dev/null +++ b/selfdrive/ui/update_translations.sh @@ -0,0 +1,23 @@ +#!/usr/bin/env bash + +BASEDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$BASEDIR" +pwd + +PY_FILES=$(git ls-files 'layouts/*.py' 'widgets/*.py') + +xgettext -L Python \ + --keyword=tr \ + --keyword=trn:1,2 \ + --keyword=pgettext:1c,2 \ + --from-code=UTF-8 \ + --flag=tr:1:python-brace-format \ + --flag=trn:1:python-brace-format --flag=trn:2:python-brace-format \ + -o translations/app.pot \ + $PY_FILES + +msginit \ + -l es \ + --no-translator \ + --input translations/app.pot \ + --output-file translations/app.po diff --git a/selfdrive/ui/update_translations_raylib.py b/selfdrive/ui/update_translations_raylib.py new file mode 100755 index 0000000000..963a1d07a5 --- /dev/null +++ b/selfdrive/ui/update_translations_raylib.py @@ -0,0 +1,47 @@ +#!/usr/bin/env python3 +import argparse +import json +import os + +from openpilot.common.basedir import BASEDIR + +UI_DIR = os.path.join(BASEDIR, "selfdrive", "ui") +TRANSLATIONS_DIR = os.path.join(UI_DIR, "translations") +LANGUAGES_FILE = os.path.join(TRANSLATIONS_DIR, "languages.json") + + +def update_translations(): + files = [] + for root, _, filenames in os.walk(os.path.join(UI_DIR, "widgets")): + for filename in filenames: + if filename.endswith(".py"): + files.append(os.path.join(root, filename)) + + # Create main translation file + print(files) + cmd = ("xgettext -L Python --keyword=tr --keyword=trn:1,2 --keyword=pgettext:1c,2 --from-code=UTF-8 " + + "--flag=tr:1:python-brace-format --flag=trn:1:python-brace-format --flag=trn:2:python-brace-format " + + "-o translations/app.pot {}").format(" ".join(files)) + print(cmd) + + ret = os.system(cmd) + assert ret == 0 + + # Generate/update translation files for each language + with open(LANGUAGES_FILE) as f: + translation_files = json.load(f).values() + + for file in translation_files: + name = file.replace("main_", "") + if os.path.exists(os.path.join(TRANSLATIONS_DIR, f"app_{name}.po")): + cmd = "msgmerge --update --backup=none --sort-output translations/app.pot translations/app_{}.po".format(name) + ret = os.system(cmd) + assert ret == 0 + else: + cmd = "msginit -l es --no-translator --input translations/app.pot --output-file translations/app_{}.po".format(name) + ret = os.system(cmd) + assert ret == 0 + + +if __name__ == "__main__": + update_translations() diff --git a/selfdrive/ui/widgets/prime.py b/selfdrive/ui/widgets/prime.py index 6b601f6dff..15cdaf7024 100644 --- a/selfdrive/ui/widgets/prime.py +++ b/selfdrive/ui/widgets/prime.py @@ -1,7 +1,7 @@ import pyray as rl from openpilot.selfdrive.ui.ui_state import ui_state -from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.application import tr, gui_app, FontWeight from openpilot.system.ui.lib.text_measure import measure_text_cached from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.widgets import Widget @@ -29,21 +29,21 @@ class PrimeWidget(Widget): w = rect.width - 160 # Title - gui_label(rl.Rectangle(x, y, w, 90), "Upgrade Now", 75, font_weight=FontWeight.BOLD) + gui_label(rl.Rectangle(x, y, w, 90), tr("Upgrade Now12345"), 75, font_weight=FontWeight.BOLD) # Description with wrapping desc_y = y + 140 font = gui_app.font(FontWeight.LIGHT) - wrapped_text = "\n".join(wrap_text(font, "Become a comma prime member at connect.comma.ai", 56, int(w))) + wrapped_text = "\n".join(wrap_text(font, tr("Become a comma prime member at connect.comma.ai"), 56, int(w))) text_size = measure_text_cached(font, wrapped_text, 56) rl.draw_text_ex(font, wrapped_text, rl.Vector2(x, desc_y), 56, 0, rl.WHITE) # Features section features_y = desc_y + text_size.y + 50 - gui_label(rl.Rectangle(x, features_y, w, 50), "PRIME FEATURES:", 41, font_weight=FontWeight.BOLD) + gui_label(rl.Rectangle(x, features_y, w, 50), tr("PRIME FEATURES:"), 41, font_weight=FontWeight.BOLD) # Feature list - features = ["Remote access", "24/7 LTE connectivity", "1 year of drive storage", "Remote snapshots"] + features = [tr("Remote access"), tr("24/7 LTE connectivity"), tr("1 year of drive storage"), tr("Remote snapshots")] for i, feature in enumerate(features): item_y = features_y + 80 + i * 65 gui_label(rl.Rectangle(x, item_y, 50, 60), "✓", 50, color=rl.Color(70, 91, 234, 255)) @@ -58,5 +58,5 @@ class PrimeWidget(Widget): y = rect.y + 40 font = gui_app.font(FontWeight.BOLD) - rl.draw_text_ex(font, "✓ SUBSCRIBED", rl.Vector2(x, y), 41, 0, rl.Color(134, 255, 78, 255)) - rl.draw_text_ex(font, "comma prime", rl.Vector2(x, y + 61), 75, 0, rl.WHITE) + rl.draw_text_ex(font, tr("✓ SUBSCRIBED"), rl.Vector2(x, y), 41, 0, rl.Color(134, 255, 78, 255)) + rl.draw_text_ex(font, tr("comma prime"), rl.Vector2(x, y + 61), 75, 0, rl.WHITE) diff --git a/selfdrive/ui/widgets/setup.py b/selfdrive/ui/widgets/setup.py index 35a7c4101c..cbdaa79d76 100644 --- a/selfdrive/ui/widgets/setup.py +++ b/selfdrive/ui/widgets/setup.py @@ -2,7 +2,7 @@ import pyray as rl from openpilot.selfdrive.ui.lib.prime_state import PrimeType from openpilot.selfdrive.ui.ui_state import ui_state from openpilot.selfdrive.ui.widgets.pairing_dialog import PairingDialog -from openpilot.system.ui.lib.application import gui_app, FontWeight +from openpilot.system.ui.lib.application import tr, gui_app, FontWeight from openpilot.system.ui.lib.wrap_text import wrap_text from openpilot.system.ui.widgets import Widget from openpilot.system.ui.widgets.button import gui_button, ButtonStyle @@ -38,7 +38,7 @@ class SetupWidget(Widget): y += 113 # 75 + 38 spacing # Description - desc = "Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer." + desc = tr("Pair your device with comma connect (connect.comma.ai) and claim your comma prime offer.") light_font = gui_app.font(FontWeight.LIGHT) wrapped = wrap_text(light_font, desc, 50, int(w)) for line in wrapped: @@ -62,7 +62,7 @@ class SetupWidget(Widget): # Title with fire emojis title_font = gui_app.font(FontWeight.MEDIUM) - title_text = "Firehose Mode" + title_text = tr('Firehose Mode1') rl.draw_text_ex(title_font, title_text, rl.Vector2(x, y), 64, 0, rl.WHITE) y += 64 + spacing diff --git a/system/ui/lib/application.py b/system/ui/lib/application.py index a2d3f3c2be..d1c49999f7 100644 --- a/system/ui/lib/application.py +++ b/system/ui/lib/application.py @@ -136,7 +136,6 @@ class GuiApplication: self._window_close_requested = False self._trace_log_callback = None self._modal_overlay = ModalOverlay() - self._multilang = Multilang() self._mouse = MouseState(self._scale) self._mouse_events: list[MouseEvent] = [] @@ -320,7 +319,6 @@ class GuiApplication: def height(self): return self._height - def _load_fonts(self): # Create a character set from our keyboard layouts from openpilot.system.ui.widgets.keyboard import KEYBOARD_LAYOUTS @@ -406,4 +404,7 @@ class GuiApplication: os._exit(1) +multilang = Multilang() +tr = multilang.translate + gui_app = GuiApplication(2160, 1080) diff --git a/system/ui/lib/multilang.py b/system/ui/lib/multilang.py index 234c06e50d..58d125ca9c 100644 --- a/system/ui/lib/multilang.py +++ b/system/ui/lib/multilang.py @@ -13,6 +13,11 @@ class Multilang: self._load_languages() self._hook_draw_text() + def translate(self, text: str) -> str: + if self._language not in self._translations: + return text + return self._translations[self._language].get(text, text) + def _load_languages(self): self._language = Params().get("LanguageSetting") @@ -21,11 +26,6 @@ class Multilang: if file.endswith(".ts"): pass - def _get_translated_text(self, text: str) -> str: - if self._language not in self._translations: - return text - return self._translations[self._language].get(text, text) - def _hook_draw_text(self): # hook rl.draw_text* to get text for multilang # TODO: and measure text