Skip to content
Draft
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
Empty file removed selfdrive/ui/layouts/__init__.py
Empty file.
4 changes: 2 additions & 2 deletions selfdrive/ui/layouts/home.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
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, MousePos
from openpilot.system.ui.lib.application import tr, gui_app, FontWeight, MousePos
from openpilot.system.ui.widgets.label import gui_label
from openpilot.system.ui.widgets import Widget

Expand Down Expand Up @@ -151,7 +151,7 @@ def _render_header(self):
highlight_color = rl.Color(75, 95, 255, 255) if self.current_state == HomeLayoutState.UPDATE else rl.Color(54, 77, 239, 255)
rl.draw_rectangle_rounded(self.update_notif_rect, 0.3, 10, highlight_color)

text = "UPDATE"
text = tr(("UPDATE")
text_size = measure_text_cached(font, text, HEAD_BUTTON_FONT_SIZE)
text_x = self.update_notif_rect.x + (self.update_notif_rect.width - text_size.x) // 2
text_y = self.update_notif_rect.y + (self.update_notif_rect.height - text_size.y) // 2
Expand Down
23 changes: 23 additions & 0 deletions selfdrive/ui/update_translations.sh
Original file line number Diff line number Diff line change
@@ -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
47 changes: 47 additions & 0 deletions selfdrive/ui/update_translations_raylib.py
Original file line number Diff line number Diff line change
@@ -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()
14 changes: 7 additions & 7 deletions selfdrive/ui/widgets/prime.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down Expand Up @@ -29,21 +29,21 @@ def _render_for_non_prime_users(self, rect: rl.Rectangle):
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))
Expand All @@ -58,5 +58,5 @@ def _render_for_prime_user(self, rect: rl.Rectangle):
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)
6 changes: 3 additions & 3 deletions selfdrive/ui/widgets/setup.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pyray as rl
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, FONT_SCALE
from openpilot.system.ui.lib.application import tr, gui_app, FontWeight, FONT_SCALE
from openpilot.system.ui.lib.wrap_text import wrap_text
from openpilot.system.ui.widgets import Widget
from openpilot.system.ui.widgets.button import Button, ButtonStyle
Expand All @@ -16,7 +16,7 @@ def __init__(self):
self._pair_device_btn = Button("Pair device", self._show_pairing, button_style=ButtonStyle.PRIMARY)
self._open_settings_btn = Button("Open", lambda: self._open_settings_callback() if self._open_settings_callback else None,
button_style=ButtonStyle.PRIMARY)
self._firehose_label = Label("🔥 Firehose Mode 🔥", font_weight=FontWeight.MEDIUM, font_size=64)
self._firehose_label = Label(tr("🔥 Firehose Mode 🔥"), font_weight=FontWeight.MEDIUM, font_size=64)

def set_open_settings_callback(self, callback):
self._open_settings_callback = callback
Expand All @@ -42,7 +42,7 @@ def _render_registration(self, rect: rl.Rectangle):
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:
Expand Down
4 changes: 4 additions & 0 deletions system/ui/lib/application.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
from importlib.resources import as_file, files
from openpilot.common.swaglog import cloudlog
from openpilot.system.hardware import HARDWARE, PC, TICI
from openpilot.system.ui.lib.multilang import Multilang
from openpilot.common.realtime import Ratekeeper

_DEFAULT_FPS = int(os.getenv("FPS", 20 if TICI else 60))
Expand Down Expand Up @@ -434,4 +435,7 @@ def _monitor_fps(self):
os._exit(1)


multilang = Multilang()
tr = multilang.translate

gui_app = GuiApplication(2160, 1080)
46 changes: 46 additions & 0 deletions system/ui/lib/multilang.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import os
import pyray as rl
from typing import Union
from openpilot.common.params import Params
from openpilot.common.basedir import BASEDIR


class Multilang:
def __init__(self):
self._language: str = "main_en"
self._translations: dict[str, dict[str, str]] = {}

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

LANGUAGE_DIR = os.path.join(BASEDIR, "selfdrive", "ui", "translations")
for file in os.listdir(LANGUAGE_DIR):
if file.endswith(".ts"):
pass

def _hook_draw_text(self):
# hook rl.draw_text* to get text for multilang
# TODO: and measure text
original_draw_text = rl.draw_text
original_draw_text_ex = rl.draw_text_ex

def draw_text_wrapper(text: str, posX: int, posY: int, fontSize: int, color: Union[rl.Color, list, tuple]) -> None:
assert False
text = self._get_translated_text(text)
return original_draw_text(text, posX, posY, fontSize, color)

def draw_text_ex_wrapper(font: Union[rl.Font, list, tuple], text: str, position: Union[rl.Vector2, list, tuple], fontSize: float, spacing: float,
tint: Union[rl.Color, list, tuple]) -> None:
text = self._get_translated_text(text)
return original_draw_text_ex(font, text, position, fontSize, spacing, tint)

rl.draw_text = draw_text_wrapper
rl.draw_text_ex = draw_text_ex_wrapper
Loading