diff --git a/selfdrive/ui/qt/util.cc b/selfdrive/ui/qt/util.cc
index a7d5438ae4..381c065901 100644
--- a/selfdrive/ui/qt/util.cc
+++ b/selfdrive/ui/qt/util.cc
@@ -79,13 +79,13 @@ QString timeAgo(const QDateTime &date) {
s = "now";
} else if (diff < 60 * 60) {
int minutes = diff / 60;
- s = QObject::tr("%1 minute%2 ago").arg(minutes).arg(minutes > 1 ? "s" : "");
+ s = QObject::tr("%n minute(s) ago", "", minutes);
} else if (diff < 60 * 60 * 24) {
int hours = diff / (60 * 60);
- s = QObject::tr("%1 hour%2 ago").arg(hours).arg(hours > 1 ? "s" : "");
+ s = QObject::tr("%n hour(s) ago", "", hours);
} else if (diff < 3600 * 24 * 7) {
int days = diff / (60 * 60 * 24);
- s = QObject::tr("%1 day%2 ago").arg(days).arg(days > 1 ? "s" : "");
+ s = QObject::tr("%n day(s) ago", "", days);
} else {
s = date.date().toString();
}
diff --git a/selfdrive/ui/qt/widgets/input.cc b/selfdrive/ui/qt/widgets/input.cc
index dc54a3621c..d703825885 100644
--- a/selfdrive/ui/qt/widgets/input.cc
+++ b/selfdrive/ui/qt/widgets/input.cc
@@ -165,7 +165,7 @@ void InputDialog::handleEnter() {
done(QDialog::Accepted);
emitText(line->text());
} else {
- setMessage(tr("Need at least %1 characters!").arg(minLength), false);
+ setMessage(tr("Need at least %n character(s)!", "", minLength), false);
}
}
diff --git a/selfdrive/ui/tests/test_translations.py b/selfdrive/ui/tests/test_translations.py
index b44ab11ede..ae91dd6f83 100755
--- a/selfdrive/ui/tests/test_translations.py
+++ b/selfdrive/ui/tests/test_translations.py
@@ -39,7 +39,7 @@ class TestTranslations(unittest.TestCase):
f"{name} has no XML translation file, run selfdrive/ui/update_translations.py")
def test_translations_updated(self):
- update_translations(translations_dir=TMP_TRANSLATIONS_DIR)
+ update_translations(plural_only=["main_en"], translations_dir=TMP_TRANSLATIONS_DIR)
for name, file in self.translation_files.items():
with self.subTest(name=name, file=file):
diff --git a/selfdrive/ui/translations/languages.json b/selfdrive/ui/translations/languages.json
index 48f0948674..cc3995738e 100644
--- a/selfdrive/ui/translations/languages.json
+++ b/selfdrive/ui/translations/languages.json
@@ -1,5 +1,5 @@
{
- "English": "",
+ "English": "main_en",
"中文(繁體)": "main_zh-CHT",
"中文(简体)": "main_zh-CHS",
"한국어": "main_ko"
diff --git a/selfdrive/ui/translations/main_en.ts b/selfdrive/ui/translations/main_en.ts
new file mode 100644
index 0000000000..3f9692e5fa
--- /dev/null
+++ b/selfdrive/ui/translations/main_en.ts
@@ -0,0 +1,38 @@
+
+
+
+
+ InputDialog
+
+ Need at least %n character(s)!
+
+ Need at least %n character!
+ Need at least %n characters!
+
+
+
+
+ QObject
+
+ %n minute(s) ago
+
+ %n minute ago
+ %n minutes ago
+
+
+
+ %n hour(s) ago
+
+ %n hour ago
+ %n hours ago
+
+
+
+ %n day(s) ago
+
+ %n day ago
+ %n days ago
+
+
+
+
diff --git a/selfdrive/ui/translations/main_ja.ts b/selfdrive/ui/translations/main_ja.ts
index c3b2b850ee..67f48341b7 100644
--- a/selfdrive/ui/translations/main_ja.ts
+++ b/selfdrive/ui/translations/main_ja.ts
@@ -108,152 +108,152 @@
DevicePanel
-
+
Dongle ID
ドングル番号 (Dongle ID)
-
+
N/A
N/A
-
+
Serial
シリアル番号
-
+
Driver Camera
車内カメラ
-
+
PREVIEW
見る
-
+
Preview the driver facing camera to ensure that driver monitoring has good visibility. (vehicle must be off)
車内カメラをプレビューして、ドライバー監視システムの視界を確認ができます。(車両の電源を切る必要があります)
-
+
Reset Calibration
キャリブレーションをリセット
-
+
RESET
リセット
-
+
Are you sure you want to reset calibration?
キャリブレーションをリセットしてもよろしいですか?
-
+
Review Training Guide
入門書を見る
-
+
REVIEW
見る
-
+
Review the rules, features, and limitations of openpilot
openpilot の特徴を見る
-
+
Are you sure you want to review the training guide?
入門書を見てもよろしいですか?
-
+
Regulatory
認証情報
-
+
VIEW
見る
-
+
Change Language
言語を変更
-
+
CHANGE
変更
-
+
Select a language
言語を選択
-
+
Reboot
再起動
-
+
Power Off
電源を切る
-
+
openpilot requires the device to be mounted within 4° left or right and within 5° up or 8° down. openpilot is continuously calibrating, resetting is rarely required.
openpilot は、左または右の4°以内、上の5°または下の8°以内にデバイスを取付ける必要があります。キャリブレーションを引き続きます、リセットはほとんど必要ありません。
-
+
Your device is pointed %1° %2 and %3° %4.
このデバイスは%2の%1°、%4の%3°に向けます。
-
+
down
下
-
+
up
上
-
+
left
左
-
+
right
右
-
+
Are you sure you want to reboot?
再起動してもよろしいですか?
-
+
Disengage to Reboot
openpilot をキャンセルして再起動ができます
-
+
Are you sure you want to power off?
シャットダウンしてもよろしいですか?
-
+
Disengage to Power Off
openpilot をキャンセルしてシャットダウンができます
@@ -294,7 +294,7 @@
DriverViewScene
-
+
camera starting
カメラを起動しています
@@ -306,10 +306,16 @@
Cancel
キャンセル
-
+
+ Need at least %n character(s)!
+
+ パスワードは%%n文字以上で入力してください!
+
+
+
Need at least %1 characters!
- パスワードは%%1文字以上で入力してください!
+ パスワードは%%1文字以上で入力してください!
@@ -488,30 +494,30 @@ location set
NvgWindow
-
+
km/h
km/h
-
+
mph
mph
-
-
+
+
MAX
最高速度
-
-
+
+
SPEED
速度
-
-
+
+
LIMIT
制限速度
@@ -635,20 +641,38 @@ location set
openpilot
openpilot
-
+
+ %n minute(s) ago
+
+ %n 分前
+
+
+
+
+ %n hour(s) ago
+
+ %n 時間前
+
+
+
+
+ %n day(s) ago
+
+ %n 日前
+
+
+
%1 minute%2 ago
- %1 分%2 前
+ %1 分%2 前
-
%1 hour%2 ago
- %1 時間%2 前
+ %1 時間%2 前
-
%1 day%2 ago
- %1 日%2 前
+ %1 日%2 前
@@ -710,33 +734,33 @@ location set
SettingsWindow
-
+
×
×
-
+
Device
デバイス
-
-
+
+
Network
ネットワーク
-
+
Toggles
切り替え
-
+
Software
ソフトウェア
-
+
Navigation
ナビゲーション
@@ -975,89 +999,89 @@ location set
SoftwarePanel
-
+
Git Branch
Git ブランチ
-
+
Git Commit
Git コミット
-
+
OS Version
OS バージョン
-
+
Version
バージョン
-
+
Last Update Check
最終更新確認
-
+
The last time openpilot successfully checked for an update. The updater only runs while the car is off.
openpilotが最後にアップデートの確認に成功してからの時間です。アップデート処理は、車の電源が切れているときのみ実行されます。
-
+
Check for Update
更新プログラムをチェック
-
+
CHECKING
確認中
-
+
Switch Branch
-
+
ENTER
-
-
+
+
The new branch will be pulled the next time the updater runs.
-
+
Enter branch name
-
+
UNINSTALL
アンインストール
-
+
Uninstall %1
%1をアンインストール
-
+
Are you sure you want to uninstall?
アンインストールしてもよろしいですか?
-
+
failed to fetch update
更新のダウンロードにエラーが発生しました
-
-
+
+
CHECK
確認
@@ -1165,72 +1189,70 @@ location set
時速31マイル(50km)を超えるスピードで走行中、方向指示器を作動させずに検出された車線ライン上に車両が触れた場合、車線に戻るアラートを受信します。
-
Enable Right-Hand Drive
- 右ハンドルを有効化
+ 右ハンドルを有効化
-
Allow openpilot to obey left-hand traffic conventions and perform driver monitoring on right driver seat.
- openpilot が左側通行規則を遵守し、右ハンドルでドライバー監視を行うことを可能にします。
+ openpilot が左側通行規則を遵守し、右ハンドルでドライバー監視を行うことを可能にします。
-
+
Use Metric System
メートル法を有効化
-
+
Display speed in km/h instead of mph.
速度は mph ではなく km/h で表示されます。
-
+
Record and Upload Driver Camera
車内カメラの録画とアップロード
-
+
Upload data from the driver facing camera and help improve the driver monitoring algorithm.
車内カメラの映像をアップロードし、ドライバー監視システムのアルゴリズムの向上に役立てます。
-
+
Disengage On Accelerator Pedal
アクセル踏むと openpilot をキャンセル
-
+
When enabled, pressing the accelerator pedal will disengage openpilot.
有効な場合は、アクセルを踏むと openpilot をキャンセルします。
-
+
Show ETA in 24h format
24時間表示
-
+
Use 24h format instead of am/pm
AM/PM の代わりに24時間形式を使用します
-
+
Show Map on Left Side of UI
ディスプレイの左側にマップを表示
-
+
Show map on left side when in split screen view.
分割画面表示の場合、ディスプレイの左側にマップを表示します。
-
+
openpilot Longitudinal Control
openpilot 縦方向制御
-
+
openpilot will disable the car's radar and will take over control of gas and brakes. Warning: this disables AEB!
openpilot は、車のレーダーを無効化し、アクセルとブレーキの制御を引き継ぎます。注意:AEB を無効化にします!
diff --git a/selfdrive/ui/translations/main_ko.ts b/selfdrive/ui/translations/main_ko.ts
index fe10008635..bc93fb7bda 100644
--- a/selfdrive/ui/translations/main_ko.ts
+++ b/selfdrive/ui/translations/main_ko.ts
@@ -306,10 +306,16 @@
Cancel
취소
-
+
+ Need at least %n character(s)!
+
+
+
+
+
Need at least %1 characters!
- 최소 %1 자가 필요합니다!
+ 최소 %1 자가 필요합니다!
@@ -635,20 +641,38 @@ location set
openpilot
openpilot
-
+
+ %n minute(s) ago
+
+
+
+
+
+
+ %n hour(s) ago
+
+
+
+
+
+
+ %n day(s) ago
+
+
+
+
+
%1 minute%2 ago
- %1 분전
+ %1 분전
-
%1 hour%2 ago
- %1 시간전
+ %1 시간전
-
%1 day%2 ago
- %1 일전
+ %1 일전
diff --git a/selfdrive/ui/translations/main_zh-CHS.ts b/selfdrive/ui/translations/main_zh-CHS.ts
index 5c949b0d3b..b1c8928cac 100644
--- a/selfdrive/ui/translations/main_zh-CHS.ts
+++ b/selfdrive/ui/translations/main_zh-CHS.ts
@@ -306,10 +306,16 @@
Cancel
取消
-
+
+ Need at least %n character(s)!
+
+ 至少需要 %n 个字符!
+
+
+
Need at least %1 characters!
- 至少需要 %1 个字符!
+ 至少需要 %1 个字符!
@@ -633,20 +639,38 @@ location set
openpilot
openpilot
-
+
+ %n minute(s) ago
+
+ %n 分钟前
+
+
+
+
+ %n hour(s) ago
+
+ %n 小时前
+
+
+
+
+ %n day(s) ago
+
+ %n 天前
+
+
+
%1 minute%2 ago
- %1 分钟%2 前
+ %1 分钟%2 前
-
%1 hour%2 ago
- %1 小时%2 前
+ %1 小时%2 前
-
%1 day%2 ago
- %1 天%2 前
+ %1 天%2 前
diff --git a/selfdrive/ui/translations/main_zh-CHT.ts b/selfdrive/ui/translations/main_zh-CHT.ts
index d1b1566a5a..b79e275116 100644
--- a/selfdrive/ui/translations/main_zh-CHT.ts
+++ b/selfdrive/ui/translations/main_zh-CHT.ts
@@ -306,10 +306,16 @@
Cancel
取消
-
+
+ Need at least %n character(s)!
+
+ 需要至少 %n 個字元!
+
+
+
Need at least %1 characters!
- 需要至少 %1 個字元!
+ 需要至少 %1 個字元!
@@ -635,23 +641,41 @@ location set
openpilot
openpilot
-
+
+ %n minute(s) ago
+
+ %n 分鐘前
+
+
+
+
+ %n hour(s) ago
+
+ %n 小時前
+
+
+
+
+ %n day(s) ago
+
+ %n 天前
+
+
+
%1 minute%2 ago
we don't need %2
- %1 分鐘前
+ %1 分鐘前
-
%1 hour%2 ago
we don't need %2
- %1 小時前
+ %1 小時前
-
%1 day%2 ago
we don't need %2
- %1 天前
+ %1 天前
diff --git a/selfdrive/ui/update_translations.py b/selfdrive/ui/update_translations.py
index 9731160752..78c973c86b 100755
--- a/selfdrive/ui/update_translations.py
+++ b/selfdrive/ui/update_translations.py
@@ -10,7 +10,10 @@ TRANSLATIONS_DIR = os.path.join(UI_DIR, "translations")
LANGUAGES_FILE = os.path.join(TRANSLATIONS_DIR, "languages.json")
-def update_translations(vanish=False, translations_dir=TRANSLATIONS_DIR):
+def update_translations(vanish=False, plural_only=None, translations_dir=TRANSLATIONS_DIR):
+ if plural_only is None:
+ plural_only = []
+
with open(LANGUAGES_FILE, "r") as f:
translation_files = json.load(f)
@@ -23,6 +26,8 @@ def update_translations(vanish=False, translations_dir=TRANSLATIONS_DIR):
args = f"lupdate -recursive {UI_DIR} -ts {tr_file}"
if vanish:
args += " -no-obsolete"
+ if file in plural_only:
+ args += " -pluralonly"
ret = os.system(args)
assert ret == 0
@@ -31,6 +36,7 @@ if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Update translation files for UI",
formatter_class=argparse.ArgumentDefaultsHelpFormatter)
parser.add_argument("--vanish", action="store_true", help="Remove translations with source text no longer found")
+ parser.add_argument("--plural-only", type=str, nargs="*", default=["main_en"], help="Translation codes to only create plural translations for (ie. the base language)")
args = parser.parse_args()
- update_translations(args.vanish)
+ update_translations(args.vanish, args.plural_only)