From 070b55f336165e25ecea578de2a56bd39744db3e Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sun, 26 Apr 2026 14:58:16 -0700 Subject: [PATCH] UI: localize command palette labels (#72378) --- CHANGELOG.md | 1 + ui/src/i18n/.i18n/de.meta.json | 8 ++-- ui/src/i18n/.i18n/es.meta.json | 8 ++-- ui/src/i18n/.i18n/fr.meta.json | 8 ++-- ui/src/i18n/.i18n/id.meta.json | 8 ++-- ui/src/i18n/.i18n/ja-JP.meta.json | 8 ++-- ui/src/i18n/.i18n/ko.meta.json | 8 ++-- ui/src/i18n/.i18n/pl.meta.json | 8 ++-- ui/src/i18n/.i18n/pt-BR.meta.json | 8 ++-- ui/src/i18n/.i18n/th.meta.json | 8 ++-- ui/src/i18n/.i18n/tr.meta.json | 8 ++-- ui/src/i18n/.i18n/uk.meta.json | 8 ++-- ui/src/i18n/.i18n/zh-CN.meta.json | 8 ++-- ui/src/i18n/.i18n/zh-TW.meta.json | 8 ++-- ui/src/i18n/locales/de.ts | 26 +++++++++++++ ui/src/i18n/locales/en.ts | 26 +++++++++++++ ui/src/i18n/locales/es.ts | 26 +++++++++++++ ui/src/i18n/locales/fr.ts | 26 +++++++++++++ ui/src/i18n/locales/id.ts | 26 +++++++++++++ ui/src/i18n/locales/ja-JP.ts | 26 +++++++++++++ ui/src/i18n/locales/ko.ts | 26 +++++++++++++ ui/src/i18n/locales/pl.ts | 26 +++++++++++++ ui/src/i18n/locales/pt-BR.ts | 26 +++++++++++++ ui/src/i18n/locales/th.ts | 26 +++++++++++++ ui/src/i18n/locales/tr.ts | 26 +++++++++++++ ui/src/i18n/locales/uk.ts | 26 +++++++++++++ ui/src/i18n/locales/zh-CN.ts | 26 +++++++++++++ ui/src/i18n/locales/zh-TW.ts | 26 +++++++++++++ ui/src/ui/views/command-palette.test.ts | 22 ++++++++++- ui/src/ui/views/command-palette.ts | 51 +++++++++++++++---------- ui/src/ui/views/connect-command.ts | 8 ++-- 31 files changed, 472 insertions(+), 78 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2684c042d0c..4c852645601 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -31,6 +31,7 @@ Docs: https://docs.openclaw.ai - WhatsApp/Web: keep quiet but healthy linked-device sessions connected by basing the watchdog on WhatsApp Web transport activity, while retaining a longer app-silence cap so frame activity cannot mask a stuck session forever. Fixes #70678; carries forward the focused #71466 approach and keeps #63939 as related configurable-timeout follow-up. Thanks @vincentkoc and @oromeis. - Discord/gateway: count failed health-monitor restart attempts toward cooldown and hourly caps, and evict stale account lifecycle state during channel reloads so repeated Discord gateway recovery cannot loop on old status. Fixes #38596. (#40413) Thanks @jellyAI-dev and @vashquez. - Cron/context engine: run isolated cron jobs under run-scoped context-engine session keys so prior runs of the same job are not inherited unless the job is explicitly session-bound. (#72292) Thanks @jalehman. +- Control UI: localize command palette labels, categories, skill shortcuts, footer hints, and connect-command copy labels while preserving localized command palette search matching. (#61130, #61119) Thanks @rubensfox20. ## 2026.4.26 diff --git a/ui/src/i18n/.i18n/de.meta.json b/ui/src/i18n/.i18n/de.meta.json index a7c729f0264..c57012c4a9a 100644 --- a/ui/src/i18n/.i18n/de.meta.json +++ b/ui/src/i18n/.i18n/de.meta.json @@ -1,11 +1,11 @@ { "fallbackKeys": [], - "generatedAt": "2026-04-25T07:56:05.494Z", + "generatedAt": "2026-04-26T21:47:11.631Z", "locale": "de", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "2af900ae253948aab69216e38e0fce2dfde89801d178dee0ebb8dd28df2e11ef", - "totalKeys": 734, - "translatedKeys": 734, + "sourceHash": "0b1690213c6431759bd87ed8a231c4f523c79bac42dfac74028698fb18e7ebba", + "totalKeys": 752, + "translatedKeys": 752, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/es.meta.json b/ui/src/i18n/.i18n/es.meta.json index 1219508cae3..6dff35f4b68 100644 --- a/ui/src/i18n/.i18n/es.meta.json +++ b/ui/src/i18n/.i18n/es.meta.json @@ -1,11 +1,11 @@ { "fallbackKeys": [], - "generatedAt": "2026-04-25T07:56:33.676Z", + "generatedAt": "2026-04-26T21:47:11.941Z", "locale": "es", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "2af900ae253948aab69216e38e0fce2dfde89801d178dee0ebb8dd28df2e11ef", - "totalKeys": 734, - "translatedKeys": 734, + "sourceHash": "0b1690213c6431759bd87ed8a231c4f523c79bac42dfac74028698fb18e7ebba", + "totalKeys": 752, + "translatedKeys": 752, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/fr.meta.json b/ui/src/i18n/.i18n/fr.meta.json index 3f701d2fd3e..249647f03dc 100644 --- a/ui/src/i18n/.i18n/fr.meta.json +++ b/ui/src/i18n/.i18n/fr.meta.json @@ -1,11 +1,11 @@ { "fallbackKeys": [], - "generatedAt": "2026-04-25T07:56:45.086Z", + "generatedAt": "2026-04-26T21:47:12.883Z", "locale": "fr", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "2af900ae253948aab69216e38e0fce2dfde89801d178dee0ebb8dd28df2e11ef", - "totalKeys": 734, - "translatedKeys": 734, + "sourceHash": "0b1690213c6431759bd87ed8a231c4f523c79bac42dfac74028698fb18e7ebba", + "totalKeys": 752, + "translatedKeys": 752, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/id.meta.json b/ui/src/i18n/.i18n/id.meta.json index 62a33e12728..12dad3e096e 100644 --- a/ui/src/i18n/.i18n/id.meta.json +++ b/ui/src/i18n/.i18n/id.meta.json @@ -1,11 +1,11 @@ { "fallbackKeys": [], - "generatedAt": "2026-04-25T07:57:25.463Z", + "generatedAt": "2026-04-26T21:47:13.865Z", "locale": "id", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "2af900ae253948aab69216e38e0fce2dfde89801d178dee0ebb8dd28df2e11ef", - "totalKeys": 734, - "translatedKeys": 734, + "sourceHash": "0b1690213c6431759bd87ed8a231c4f523c79bac42dfac74028698fb18e7ebba", + "totalKeys": 752, + "translatedKeys": 752, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/ja-JP.meta.json b/ui/src/i18n/.i18n/ja-JP.meta.json index 70b27b59d1e..3a1243bc1a4 100644 --- a/ui/src/i18n/.i18n/ja-JP.meta.json +++ b/ui/src/i18n/.i18n/ja-JP.meta.json @@ -1,11 +1,11 @@ { "fallbackKeys": [], - "generatedAt": "2026-04-25T07:56:47.425Z", + "generatedAt": "2026-04-26T21:47:12.252Z", "locale": "ja-JP", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "2af900ae253948aab69216e38e0fce2dfde89801d178dee0ebb8dd28df2e11ef", - "totalKeys": 734, - "translatedKeys": 734, + "sourceHash": "0b1690213c6431759bd87ed8a231c4f523c79bac42dfac74028698fb18e7ebba", + "totalKeys": 752, + "translatedKeys": 752, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/ko.meta.json b/ui/src/i18n/.i18n/ko.meta.json index 14c92310b98..7d85db0dea6 100644 --- a/ui/src/i18n/.i18n/ko.meta.json +++ b/ui/src/i18n/.i18n/ko.meta.json @@ -1,11 +1,11 @@ { "fallbackKeys": [], - "generatedAt": "2026-04-25T07:56:44.197Z", + "generatedAt": "2026-04-26T21:47:12.563Z", "locale": "ko", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "2af900ae253948aab69216e38e0fce2dfde89801d178dee0ebb8dd28df2e11ef", - "totalKeys": 734, - "translatedKeys": 734, + "sourceHash": "0b1690213c6431759bd87ed8a231c4f523c79bac42dfac74028698fb18e7ebba", + "totalKeys": 752, + "translatedKeys": 752, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/pl.meta.json b/ui/src/i18n/.i18n/pl.meta.json index 110ef121d72..6f6c1806ed3 100644 --- a/ui/src/i18n/.i18n/pl.meta.json +++ b/ui/src/i18n/.i18n/pl.meta.json @@ -1,11 +1,11 @@ { "fallbackKeys": [], - "generatedAt": "2026-04-25T07:57:26.422Z", + "generatedAt": "2026-04-26T21:47:14.204Z", "locale": "pl", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "2af900ae253948aab69216e38e0fce2dfde89801d178dee0ebb8dd28df2e11ef", - "totalKeys": 734, - "translatedKeys": 734, + "sourceHash": "0b1690213c6431759bd87ed8a231c4f523c79bac42dfac74028698fb18e7ebba", + "totalKeys": 752, + "translatedKeys": 752, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/pt-BR.meta.json b/ui/src/i18n/.i18n/pt-BR.meta.json index 1ac8d1f7880..548782bdfc8 100644 --- a/ui/src/i18n/.i18n/pt-BR.meta.json +++ b/ui/src/i18n/.i18n/pt-BR.meta.json @@ -1,11 +1,11 @@ { "fallbackKeys": [], - "generatedAt": "2026-04-25T07:56:04.004Z", + "generatedAt": "2026-04-26T21:47:11.305Z", "locale": "pt-BR", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "2af900ae253948aab69216e38e0fce2dfde89801d178dee0ebb8dd28df2e11ef", - "totalKeys": 734, - "translatedKeys": 734, + "sourceHash": "0b1690213c6431759bd87ed8a231c4f523c79bac42dfac74028698fb18e7ebba", + "totalKeys": 752, + "translatedKeys": 752, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/th.meta.json b/ui/src/i18n/.i18n/th.meta.json index 0bf27654741..b319d6dfebb 100644 --- a/ui/src/i18n/.i18n/th.meta.json +++ b/ui/src/i18n/.i18n/th.meta.json @@ -1,11 +1,11 @@ { "fallbackKeys": [], - "generatedAt": "2026-04-25T07:57:38.352Z", + "generatedAt": "2026-04-26T21:47:14.524Z", "locale": "th", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "2af900ae253948aab69216e38e0fce2dfde89801d178dee0ebb8dd28df2e11ef", - "totalKeys": 734, - "translatedKeys": 734, + "sourceHash": "0b1690213c6431759bd87ed8a231c4f523c79bac42dfac74028698fb18e7ebba", + "totalKeys": 752, + "translatedKeys": 752, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/tr.meta.json b/ui/src/i18n/.i18n/tr.meta.json index f239f57063d..a62cf41c7a2 100644 --- a/ui/src/i18n/.i18n/tr.meta.json +++ b/ui/src/i18n/.i18n/tr.meta.json @@ -1,11 +1,11 @@ { "fallbackKeys": [], - "generatedAt": "2026-04-25T07:57:03.203Z", + "generatedAt": "2026-04-26T21:47:13.204Z", "locale": "tr", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "2af900ae253948aab69216e38e0fce2dfde89801d178dee0ebb8dd28df2e11ef", - "totalKeys": 734, - "translatedKeys": 734, + "sourceHash": "0b1690213c6431759bd87ed8a231c4f523c79bac42dfac74028698fb18e7ebba", + "totalKeys": 752, + "translatedKeys": 752, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/uk.meta.json b/ui/src/i18n/.i18n/uk.meta.json index 71d1ccf1ca6..2ee549e5df4 100644 --- a/ui/src/i18n/.i18n/uk.meta.json +++ b/ui/src/i18n/.i18n/uk.meta.json @@ -1,11 +1,11 @@ { "fallbackKeys": [], - "generatedAt": "2026-04-25T07:57:11.008Z", + "generatedAt": "2026-04-26T21:47:13.531Z", "locale": "uk", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "2af900ae253948aab69216e38e0fce2dfde89801d178dee0ebb8dd28df2e11ef", - "totalKeys": 734, - "translatedKeys": 734, + "sourceHash": "0b1690213c6431759bd87ed8a231c4f523c79bac42dfac74028698fb18e7ebba", + "totalKeys": 752, + "translatedKeys": 752, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/zh-CN.meta.json b/ui/src/i18n/.i18n/zh-CN.meta.json index 7c38a9c6694..bdbc7466567 100644 --- a/ui/src/i18n/.i18n/zh-CN.meta.json +++ b/ui/src/i18n/.i18n/zh-CN.meta.json @@ -1,11 +1,11 @@ { "fallbackKeys": [], - "generatedAt": "2026-04-25T07:56:08.786Z", + "generatedAt": "2026-04-26T21:47:10.673Z", "locale": "zh-CN", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "2af900ae253948aab69216e38e0fce2dfde89801d178dee0ebb8dd28df2e11ef", - "totalKeys": 734, - "translatedKeys": 734, + "sourceHash": "0b1690213c6431759bd87ed8a231c4f523c79bac42dfac74028698fb18e7ebba", + "totalKeys": 752, + "translatedKeys": 752, "workflow": 1 } diff --git a/ui/src/i18n/.i18n/zh-TW.meta.json b/ui/src/i18n/.i18n/zh-TW.meta.json index 10b35add9a0..0e712cf7b9d 100644 --- a/ui/src/i18n/.i18n/zh-TW.meta.json +++ b/ui/src/i18n/.i18n/zh-TW.meta.json @@ -1,11 +1,11 @@ { "fallbackKeys": [], - "generatedAt": "2026-04-25T07:56:00.104Z", + "generatedAt": "2026-04-26T21:47:10.990Z", "locale": "zh-TW", "model": "gpt-5.5", "provider": "openai", - "sourceHash": "2af900ae253948aab69216e38e0fce2dfde89801d178dee0ebb8dd28df2e11ef", - "totalKeys": 734, - "translatedKeys": 734, + "sourceHash": "0b1690213c6431759bd87ed8a231c4f523c79bac42dfac74028698fb18e7ebba", + "totalKeys": 752, + "translatedKeys": 752, "workflow": 1 } diff --git a/ui/src/i18n/locales/de.ts b/ui/src/i18n/locales/de.ts index ab513558ba1..91304601f0c 100644 --- a/ui/src/i18n/locales/de.ts +++ b/ui/src/i18n/locales/de.ts @@ -280,6 +280,8 @@ export const de: TranslationMap = { tailscaleDocsLink: "Docs: Tailscale Serve", insecureHttpDocsTitle: "Dokumentation zu unsicherem HTTP (öffnet sich in neuem Tab)", insecureHttpDocsLink: "Docs: Unsicheres HTTP", + copyCommand: "Befehl kopieren", + copyCommandAria: "Befehl kopieren: {command}", }, cards: { cost: "Kosten", @@ -316,6 +318,30 @@ export const de: TranslationMap = { palette: { placeholder: "Befehl eingeben…", noResults: "Keine Ergebnisse", + categories: { + search: "Suchen", + navigation: "Navigation", + skills: "Skills", + }, + items: { + overview: "Übersicht", + sessions: "Sitzungen", + scheduled: "Geplant", + skills: "Skills", + settings: "Einstellungen", + agents: "Agenten", + shellCommand: "Shell-Befehl", + debugMode: "Debug-Modus", + }, + descriptions: { + shellCommand: "Shell ausführen", + debugMode: "Debug umschalten", + }, + footer: { + navigate: "navigieren", + select: "auswählen", + close: "schließen", + }, }, }, dreaming: { diff --git a/ui/src/i18n/locales/en.ts b/ui/src/i18n/locales/en.ts index 3151ab23a3b..e8bbfc6b848 100644 --- a/ui/src/i18n/locales/en.ts +++ b/ui/src/i18n/locales/en.ts @@ -271,6 +271,8 @@ export const en: TranslationMap = { tailscaleDocsLink: "Docs: Tailscale Serve", insecureHttpDocsTitle: "Insecure HTTP docs (opens in new tab)", insecureHttpDocsLink: "Docs: Insecure HTTP", + copyCommand: "Copy command", + copyCommandAria: "Copy command: {command}", }, cards: { cost: "Cost", @@ -306,6 +308,30 @@ export const en: TranslationMap = { palette: { placeholder: "Type a command…", noResults: "No results", + categories: { + search: "Search", + navigation: "Navigation", + skills: "Skills", + }, + items: { + overview: "Overview", + sessions: "Sessions", + scheduled: "Scheduled", + skills: "Skills", + settings: "Settings", + agents: "Agents", + shellCommand: "Shell Command", + debugMode: "Debug Mode", + }, + descriptions: { + shellCommand: "Run shell", + debugMode: "Toggle debug", + }, + footer: { + navigate: "navigate", + select: "select", + close: "close", + }, }, }, dreaming: { diff --git a/ui/src/i18n/locales/es.ts b/ui/src/i18n/locales/es.ts index 7aa68e68a3b..92d8d922d1b 100644 --- a/ui/src/i18n/locales/es.ts +++ b/ui/src/i18n/locales/es.ts @@ -275,6 +275,8 @@ export const es: TranslationMap = { tailscaleDocsLink: "Documentación: Tailscale Serve", insecureHttpDocsTitle: "Documentación de HTTP inseguro (se abre en una pestaña nueva)", insecureHttpDocsLink: "Documentación: HTTP inseguro", + copyCommand: "Copiar comando", + copyCommandAria: "Copiar comando: {command}", }, cards: { cost: "Costo", @@ -310,6 +312,30 @@ export const es: TranslationMap = { palette: { placeholder: "Escribe un comando…", noResults: "Sin resultados", + categories: { + search: "Buscar", + navigation: "Navegación", + skills: "Skills", + }, + items: { + overview: "Resumen", + sessions: "Sesiones", + scheduled: "Programado", + skills: "Skills", + settings: "Configuración", + agents: "Agentes", + shellCommand: "Comando de shell", + debugMode: "Modo de depuración", + }, + descriptions: { + shellCommand: "Ejecutar shell", + debugMode: "Alternar depuración", + }, + footer: { + navigate: "navegar", + select: "seleccionar", + close: "cerrar", + }, }, }, dreaming: { diff --git a/ui/src/i18n/locales/fr.ts b/ui/src/i18n/locales/fr.ts index 85174290617..960f6acfb68 100644 --- a/ui/src/i18n/locales/fr.ts +++ b/ui/src/i18n/locales/fr.ts @@ -279,6 +279,8 @@ export const fr: TranslationMap = { tailscaleDocsLink: "Documentation : Tailscale Serve", insecureHttpDocsTitle: "Documentation sur HTTP non sécurisé (s’ouvre dans un nouvel onglet)", insecureHttpDocsLink: "Documentation : HTTP non sécurisé", + copyCommand: "Copier la commande", + copyCommandAria: "Copier la commande : {command}", }, cards: { cost: "Coût", @@ -314,6 +316,30 @@ export const fr: TranslationMap = { palette: { placeholder: "Saisissez une commande…", noResults: "Aucun résultat", + categories: { + search: "Recherche", + navigation: "Navigation", + skills: "Skills", + }, + items: { + overview: "Vue d’ensemble", + sessions: "Sessions", + scheduled: "Planifié", + skills: "Skills", + settings: "Paramètres", + agents: "Agents", + shellCommand: "Commande shell", + debugMode: "Mode débogage", + }, + descriptions: { + shellCommand: "Exécuter le shell", + debugMode: "Basculer le débogage", + }, + footer: { + navigate: "naviguer", + select: "sélectionner", + close: "fermer", + }, }, }, dreaming: { diff --git a/ui/src/i18n/locales/id.ts b/ui/src/i18n/locales/id.ts index 004e3db169b..119db021594 100644 --- a/ui/src/i18n/locales/id.ts +++ b/ui/src/i18n/locales/id.ts @@ -275,6 +275,8 @@ export const id: TranslationMap = { tailscaleDocsLink: "Dokumentasi: Tailscale Serve", insecureHttpDocsTitle: "Dokumentasi HTTP tidak aman (dibuka di tab baru)", insecureHttpDocsLink: "Dokumentasi: HTTP tidak aman", + copyCommand: "Salin perintah", + copyCommandAria: "Salin perintah: {command}", }, cards: { cost: "Biaya", @@ -310,6 +312,30 @@ export const id: TranslationMap = { palette: { placeholder: "Ketik perintah…", noResults: "Tidak ada hasil", + categories: { + search: "Cari", + navigation: "Navigasi", + skills: "Skills", + }, + items: { + overview: "Ikhtisar", + sessions: "Sesi", + scheduled: "Terjadwal", + skills: "Skills", + settings: "Pengaturan", + agents: "Agen", + shellCommand: "Perintah shell", + debugMode: "Mode debug", + }, + descriptions: { + shellCommand: "Jalankan shell", + debugMode: "Alihkan debug", + }, + footer: { + navigate: "navigasi", + select: "pilih", + close: "tutup", + }, }, }, dreaming: { diff --git a/ui/src/i18n/locales/ja-JP.ts b/ui/src/i18n/locales/ja-JP.ts index 12e3d46332f..026b116042e 100644 --- a/ui/src/i18n/locales/ja-JP.ts +++ b/ui/src/i18n/locales/ja-JP.ts @@ -279,6 +279,8 @@ export const ja_JP: TranslationMap = { tailscaleDocsLink: "ドキュメント: Tailscale Serve", insecureHttpDocsTitle: "安全でない HTTP に関するドキュメント(新しいタブで開きます)", insecureHttpDocsLink: "ドキュメント: 安全でない HTTP", + copyCommand: "コマンドをコピー", + copyCommandAria: "コマンドをコピー: {command}", }, cards: { cost: "コスト", @@ -314,6 +316,30 @@ export const ja_JP: TranslationMap = { palette: { placeholder: "コマンドを入力…", noResults: "結果がありません", + categories: { + search: "検索", + navigation: "ナビゲーション", + skills: "Skills", + }, + items: { + overview: "概要", + sessions: "セッション", + scheduled: "スケジュール済み", + skills: "Skills", + settings: "設定", + agents: "エージェント", + shellCommand: "シェルコマンド", + debugMode: "デバッグモード", + }, + descriptions: { + shellCommand: "シェルを実行", + debugMode: "デバッグを切り替え", + }, + footer: { + navigate: "移動", + select: "選択", + close: "閉じる", + }, }, }, dreaming: { diff --git a/ui/src/i18n/locales/ko.ts b/ui/src/i18n/locales/ko.ts index 022289f3448..693be5d6ce6 100644 --- a/ui/src/i18n/locales/ko.ts +++ b/ui/src/i18n/locales/ko.ts @@ -274,6 +274,8 @@ export const ko: TranslationMap = { tailscaleDocsLink: "문서: Tailscale Serve", insecureHttpDocsTitle: "안전하지 않은 HTTP 문서(새 탭에서 열림)", insecureHttpDocsLink: "문서: 안전하지 않은 HTTP", + copyCommand: "명령 복사", + copyCommandAria: "명령 복사: {command}", }, cards: { cost: "비용", @@ -309,6 +311,30 @@ export const ko: TranslationMap = { palette: { placeholder: "명령을 입력하세요…", noResults: "결과 없음", + categories: { + search: "검색", + navigation: "탐색", + skills: "Skills", + }, + items: { + overview: "개요", + sessions: "세션", + scheduled: "예약됨", + skills: "Skills", + settings: "설정", + agents: "에이전트", + shellCommand: "셸 명령", + debugMode: "디버그 모드", + }, + descriptions: { + shellCommand: "셸 실행", + debugMode: "디버그 전환", + }, + footer: { + navigate: "탐색", + select: "선택", + close: "닫기", + }, }, }, dreaming: { diff --git a/ui/src/i18n/locales/pl.ts b/ui/src/i18n/locales/pl.ts index 972ab6f93ad..575fbf05284 100644 --- a/ui/src/i18n/locales/pl.ts +++ b/ui/src/i18n/locales/pl.ts @@ -276,6 +276,8 @@ export const pl: TranslationMap = { tailscaleDocsLink: "Dokumentacja: Tailscale Serve", insecureHttpDocsTitle: "Dokumentacja niezabezpieczonego HTTP (otwiera się w nowej karcie)", insecureHttpDocsLink: "Dokumentacja: Niezabezpieczone HTTP", + copyCommand: "Kopiuj polecenie", + copyCommandAria: "Kopiuj polecenie: {command}", }, cards: { cost: "Koszt", @@ -312,6 +314,30 @@ export const pl: TranslationMap = { palette: { placeholder: "Wpisz polecenie…", noResults: "Brak wyników", + categories: { + search: "Szukaj", + navigation: "Nawigacja", + skills: "Skills", + }, + items: { + overview: "Przegląd", + sessions: "Sesje", + scheduled: "Zaplanowane", + skills: "Skills", + settings: "Ustawienia", + agents: "Agenci", + shellCommand: "Polecenie powłoki", + debugMode: "Tryb debugowania", + }, + descriptions: { + shellCommand: "Uruchom powłokę", + debugMode: "Przełącz debugowanie", + }, + footer: { + navigate: "nawiguj", + select: "wybierz", + close: "zamknij", + }, }, }, dreaming: { diff --git a/ui/src/i18n/locales/pt-BR.ts b/ui/src/i18n/locales/pt-BR.ts index 57508ac7c9f..5297af310a7 100644 --- a/ui/src/i18n/locales/pt-BR.ts +++ b/ui/src/i18n/locales/pt-BR.ts @@ -275,6 +275,8 @@ export const pt_BR: TranslationMap = { tailscaleDocsLink: "Docs: Tailscale Serve", insecureHttpDocsTitle: "Documentação de HTTP inseguro (abre em nova aba)", insecureHttpDocsLink: "Docs: HTTP inseguro", + copyCommand: "Copiar comando", + copyCommandAria: "Copiar comando: {command}", }, cards: { cost: "Custo", @@ -310,6 +312,30 @@ export const pt_BR: TranslationMap = { palette: { placeholder: "Digite um comando…", noResults: "Sem resultados", + categories: { + search: "Pesquisar", + navigation: "Navegação", + skills: "Skills", + }, + items: { + overview: "Visão geral", + sessions: "Sessões", + scheduled: "Agendado", + skills: "Skills", + settings: "Configurações", + agents: "Agentes", + shellCommand: "Comando de shell", + debugMode: "Modo de depuração", + }, + descriptions: { + shellCommand: "Executar shell", + debugMode: "Alternar depuração", + }, + footer: { + navigate: "navegar", + select: "selecionar", + close: "fechar", + }, }, }, dreaming: { diff --git a/ui/src/i18n/locales/th.ts b/ui/src/i18n/locales/th.ts index 7ecc0ad2d8d..0eee24989fe 100644 --- a/ui/src/i18n/locales/th.ts +++ b/ui/src/i18n/locales/th.ts @@ -268,6 +268,8 @@ export const th: TranslationMap = { tailscaleDocsLink: "เอกสาร: Tailscale Serve", insecureHttpDocsTitle: "เอกสาร HTTP ที่ไม่ปลอดภัย (เปิดในแท็บใหม่)", insecureHttpDocsLink: "เอกสาร: HTTP ที่ไม่ปลอดภัย", + copyCommand: "คัดลอกคำสั่ง", + copyCommandAria: "คัดลอกคำสั่ง: {command}", }, cards: { cost: "ค่าใช้จ่าย", @@ -303,6 +305,30 @@ export const th: TranslationMap = { palette: { placeholder: "พิมพ์คำสั่ง…", noResults: "ไม่พบผลลัพธ์", + categories: { + search: "ค้นหา", + navigation: "การนำทาง", + skills: "ทักษะ", + }, + items: { + overview: "ภาพรวม", + sessions: "เซสชัน", + scheduled: "กำหนดเวลาแล้ว", + skills: "ทักษะ", + settings: "การตั้งค่า", + agents: "เอเจนต์", + shellCommand: "คำสั่งเชลล์", + debugMode: "โหมดดีบัก", + }, + descriptions: { + shellCommand: "เรียกใช้เชลล์", + debugMode: "สลับดีบัก", + }, + footer: { + navigate: "นำทาง", + select: "เลือก", + close: "ปิด", + }, }, }, dreaming: { diff --git a/ui/src/i18n/locales/tr.ts b/ui/src/i18n/locales/tr.ts index abbada6598f..81a46c5a6ac 100644 --- a/ui/src/i18n/locales/tr.ts +++ b/ui/src/i18n/locales/tr.ts @@ -279,6 +279,8 @@ export const tr: TranslationMap = { tailscaleDocsLink: "Belgeler: Tailscale Serve", insecureHttpDocsTitle: "Güvenli olmayan HTTP belgeleri (yeni sekmede açılır)", insecureHttpDocsLink: "Belgeler: Güvenli olmayan HTTP", + copyCommand: "Komutu kopyala", + copyCommandAria: "Komutu kopyala: {command}", }, cards: { cost: "Maliyet", @@ -315,6 +317,30 @@ export const tr: TranslationMap = { palette: { placeholder: "Bir komut yazın…", noResults: "Sonuç yok", + categories: { + search: "Arama", + navigation: "Navigation", + skills: "Skills", + }, + items: { + overview: "Genel Bakış", + sessions: "Oturumlar", + scheduled: "Zamanlanmış", + skills: "Skills", + settings: "Ayarlar", + agents: "Ajanlar", + shellCommand: "Shell Komutu", + debugMode: "Hata Ayıklama Modu", + }, + descriptions: { + shellCommand: "Shell çalıştır", + debugMode: "Hata ayıklamayı değiştir", + }, + footer: { + navigate: "gezin", + select: "seç", + close: "kapat", + }, }, }, dreaming: { diff --git a/ui/src/i18n/locales/uk.ts b/ui/src/i18n/locales/uk.ts index 5c72be915c2..c26e3626d2e 100644 --- a/ui/src/i18n/locales/uk.ts +++ b/ui/src/i18n/locales/uk.ts @@ -277,6 +277,8 @@ export const uk: TranslationMap = { tailscaleDocsLink: "Документація: Tailscale Serve", insecureHttpDocsTitle: "Документація щодо незахищеного HTTP (відкривається в новій вкладці)", insecureHttpDocsLink: "Документація: незахищений HTTP", + copyCommand: "Копіювати команду", + copyCommandAria: "Копіювати команду: {command}", }, cards: { cost: "Вартість", @@ -313,6 +315,30 @@ export const uk: TranslationMap = { palette: { placeholder: "Введіть команду…", noResults: "Немає результатів", + categories: { + search: "Пошук", + navigation: "Навігація", + skills: "Навички", + }, + items: { + overview: "Огляд", + sessions: "Сеанси", + scheduled: "Заплановано", + skills: "Навички", + settings: "Налаштування", + agents: "Агенти", + shellCommand: "Команда оболонки", + debugMode: "Режим налагодження", + }, + descriptions: { + shellCommand: "Запустити оболонку", + debugMode: "Перемкнути налагодження", + }, + footer: { + navigate: "навігація", + select: "вибрати", + close: "закрити", + }, }, }, dreaming: { diff --git a/ui/src/i18n/locales/zh-CN.ts b/ui/src/i18n/locales/zh-CN.ts index 64ca4b228f6..b64c90a71ef 100644 --- a/ui/src/i18n/locales/zh-CN.ts +++ b/ui/src/i18n/locales/zh-CN.ts @@ -268,6 +268,8 @@ export const zh_CN: TranslationMap = { tailscaleDocsLink: "文档:Tailscale Serve", insecureHttpDocsTitle: "不安全 HTTP 文档(在新标签页中打开)", insecureHttpDocsLink: "文档:不安全 HTTP", + copyCommand: "复制命令", + copyCommandAria: "复制命令:{command}", }, cards: { cost: "费用", @@ -303,6 +305,30 @@ export const zh_CN: TranslationMap = { palette: { placeholder: "输入命令…", noResults: "无结果", + categories: { + search: "搜索", + navigation: "导航", + skills: "技能", + }, + items: { + overview: "概览", + sessions: "会话", + scheduled: "已计划", + skills: "技能", + settings: "设置", + agents: "代理", + shellCommand: "Shell 命令", + debugMode: "调试模式", + }, + descriptions: { + shellCommand: "运行 shell", + debugMode: "切换调试", + }, + footer: { + navigate: "导航", + select: "选择", + close: "关闭", + }, }, }, dreaming: { diff --git a/ui/src/i18n/locales/zh-TW.ts b/ui/src/i18n/locales/zh-TW.ts index bdfc79fdfec..64b3c404b23 100644 --- a/ui/src/i18n/locales/zh-TW.ts +++ b/ui/src/i18n/locales/zh-TW.ts @@ -268,6 +268,8 @@ export const zh_TW: TranslationMap = { tailscaleDocsLink: "文件:Tailscale Serve", insecureHttpDocsTitle: "不安全 HTTP 文件(在新分頁中開啟)", insecureHttpDocsLink: "文件:不安全 HTTP", + copyCommand: "複製指令", + copyCommandAria: "複製指令:{command}", }, cards: { cost: "費用", @@ -303,6 +305,30 @@ export const zh_TW: TranslationMap = { palette: { placeholder: "輸入指令…", noResults: "無結果", + categories: { + search: "搜尋", + navigation: "導覽", + skills: "技能", + }, + items: { + overview: "概覽", + sessions: "工作階段", + scheduled: "已排程", + skills: "技能", + settings: "設定", + agents: "代理", + shellCommand: "Shell 指令", + debugMode: "偵錯模式", + }, + descriptions: { + shellCommand: "執行 shell", + debugMode: "切換偵錯", + }, + footer: { + navigate: "導覽", + select: "選取", + close: "關閉", + }, }, }, dreaming: { diff --git a/ui/src/ui/views/command-palette.test.ts b/ui/src/ui/views/command-palette.test.ts index f02ce02fea0..b6a888cd67d 100644 --- a/ui/src/ui/views/command-palette.test.ts +++ b/ui/src/ui/views/command-palette.test.ts @@ -1,9 +1,11 @@ import { afterEach, describe, expect, it } from "vitest"; +import { i18n } from "../../i18n/index.ts"; import { refreshSlashCommands, resetSlashCommandsForTest } from "../chat/slash-commands.ts"; -import { getPaletteItems } from "./command-palette.ts"; +import { getFilteredPaletteItems, getPaletteItems } from "./command-palette.ts"; -afterEach(() => { +afterEach(async () => { resetSlashCommandsForTest(); + await i18n.setLocale("en"); }); describe("command palette", () => { @@ -51,4 +53,20 @@ describe("command palette", () => { }), ); }); + + it("matches localized base item labels and descriptions", async () => { + await i18n.setLocale("zh-CN"); + + expect(getPaletteItems()).toContainEqual( + expect.objectContaining({ + id: "nav-config", + label: "设置", + }), + ); + expect(getFilteredPaletteItems("切换调试")).toContainEqual( + expect.objectContaining({ + id: "skill-debug", + }), + ); + }); }); diff --git a/ui/src/ui/views/command-palette.ts b/ui/src/ui/views/command-palette.ts index 8432e782743..28a6d219d8c 100644 --- a/ui/src/ui/views/command-palette.ts +++ b/ui/src/ui/views/command-palette.ts @@ -29,61 +29,61 @@ function getPaletteBaseItems(): PaletteItem[] { return [ { id: "nav-overview", - label: "Overview", + label: t("overview.palette.items.overview"), icon: "barChart", category: "navigation", action: "nav:overview", }, { id: "nav-sessions", - label: "Sessions", + label: t("overview.palette.items.sessions"), icon: "fileText", category: "navigation", action: "nav:sessions", }, { id: "nav-cron", - label: "Scheduled", + label: t("overview.palette.items.scheduled"), icon: "scrollText", category: "navigation", action: "nav:cron", }, { id: "nav-skills", - label: "Skills", + label: t("overview.palette.items.skills"), icon: "zap", category: "navigation", action: "nav:skills", }, { id: "nav-config", - label: "Settings", + label: t("overview.palette.items.settings"), icon: "settings", category: "navigation", action: "nav:config", }, { id: "nav-agents", - label: "Agents", + label: t("overview.palette.items.agents"), icon: "folder", category: "navigation", action: "nav:agents", }, { id: "skill-shell", - label: "Shell Command", + label: t("overview.palette.items.shellCommand"), icon: "monitor", category: "skills", action: "/skill shell", - description: "Run shell", + description: t("overview.palette.descriptions.shellCommand"), }, { id: "skill-debug", - label: "Debug Mode", + label: t("overview.palette.items.debugMode"), icon: "bug", category: "skills", action: "/verbose full", - description: "Toggle debug", + description: t("overview.palette.descriptions.debugMode"), }, ]; } @@ -120,6 +120,10 @@ function filteredItems(query: string): PaletteItem[] { ); } +export function getFilteredPaletteItems(query: string): readonly PaletteItem[] { + return filteredItems(query); +} + function groupItems(items: PaletteItem[]): Array<[string, PaletteItem[]]> { const map = new Map(); for (const item of items) { @@ -190,11 +194,18 @@ function handleKeydown(e: KeyboardEvent, props: CommandPaletteProps) { } } -const CATEGORY_LABELS: Record = { - search: "Search", - navigation: "Navigation", - skills: "Skills", -}; +function getCategoryLabel(category: string): string { + switch (category) { + case "search": + return t("overview.palette.categories.search"); + case "navigation": + return t("overview.palette.categories.navigation"); + case "skills": + return t("overview.palette.categories.skills"); + default: + return category; + } +} function focusInput(el: Element | undefined) { if (el) { @@ -244,9 +255,7 @@ export function renderCommandPalette(props: CommandPaletteProps) { ` : grouped.map( ([category, groupedItems]) => html` -
- ${CATEGORY_LABELS[category] ?? category} -
+
${getCategoryLabel(category)}
${groupedItems.map((item) => { const globalIndex = items.indexOf(item); const isActive = globalIndex === props.activeIndex; @@ -273,9 +282,9 @@ export function renderCommandPalette(props: CommandPaletteProps) { )} diff --git a/ui/src/ui/views/connect-command.ts b/ui/src/ui/views/connect-command.ts index a609577072e..bf4e27b77eb 100644 --- a/ui/src/ui/views/connect-command.ts +++ b/ui/src/ui/views/connect-command.ts @@ -1,4 +1,5 @@ import { html } from "lit"; +import { t } from "../../i18n/index.ts"; import { renderCopyButton } from "../chat/copy-as-markdown.ts"; async function copyCommand(command: string) { @@ -10,13 +11,14 @@ async function copyCommand(command: string) { } export function renderConnectCommand(command: string) { + const copyLabel = t("overview.connection.copyCommand"); return html` `; }