From 8d5393f9840d21f4269907a81b1de847052911c5 Mon Sep 17 00:00:00 2001 From: Z User Date: Tue, 16 Jun 2026 02:09:03 +0000 Subject: [PATCH] fix: add i18n to status dialog and replace silent session abort catches with logging MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add useLanguage() to dialog-status.tsx, replacing 11 hardcoded English strings with i18n keys (tui.status.*) — covers MCP servers, LSP servers, formatters, plugins, and connection status messages - Add 12 i18n keys to en.ts and zh.ts with {{count}}/{{name}} interpolation - Replace 2 silent .catch(() => {}) in routes/session/index.tsx with log.warn for session abort failures (exit path + undo/revert path) - Also fix 'opencode mcp auth' → 'mimo mcp auth' in status dialog --- .../cli/cmd/tui/component/dialog-status.tsx | 26 ++++++++++--------- packages/opencode/src/cli/cmd/tui/i18n/en.ts | 16 +++++++++++- packages/opencode/src/cli/cmd/tui/i18n/zh.ts | 15 +++++++++++ .../src/cli/cmd/tui/routes/session/index.tsx | 6 +++-- 4 files changed, 48 insertions(+), 15 deletions(-) diff --git a/packages/opencode/src/cli/cmd/tui/component/dialog-status.tsx b/packages/opencode/src/cli/cmd/tui/component/dialog-status.tsx index 72019298..fe941fc0 100644 --- a/packages/opencode/src/cli/cmd/tui/component/dialog-status.tsx +++ b/packages/opencode/src/cli/cmd/tui/component/dialog-status.tsx @@ -4,6 +4,7 @@ import { useTheme } from "../context/theme" import { useDialog } from "@tui/ui/dialog" import { useSync } from "@tui/context/sync" import { For, Match, Switch, Show, createMemo } from "solid-js" +import { useLanguage } from "@tui/context/language" export type DialogStatusProps = {} @@ -11,6 +12,7 @@ export function DialogStatus() { const sync = useSync() const { theme } = useTheme() const dialog = useDialog() + const { t } = useLanguage() const enabledFormatters = createMemo(() => sync.data.formatter.filter((f) => f.enabled)) @@ -44,15 +46,15 @@ export function DialogStatus() { - Status + {t("tui.status.title")} dialog.clear()}> esc - 0} fallback={No MCP Servers}> + 0} fallback={{t("tui.status.no_mcp")}}> - {Object.keys(sync.data.mcp).length} MCP Servers + {t("tui.status.mcp_servers", { count: Object.keys(sync.data.mcp).length })} {([key, item]) => ( @@ -77,12 +79,12 @@ export function DialogStatus() { {key}{" "} - Connected + {t("tui.status.connected")} {(val) => val().error} - Pending approval - Disabled in configuration + {t("tui.status.pending_approval")} + {t("tui.status.disabled_config")} - Needs authentication (run: opencode mcp auth {key}) + {t("tui.status.needs_auth", { name: key })} {(val) => (val() as { error: string }).error} @@ -97,7 +99,7 @@ export function DialogStatus() { {sync.data.lsp.length > 0 && ( - {sync.data.lsp.length} LSP Servers + {t("tui.status.lsp_servers", { count: sync.data.lsp.length })} {(item) => ( @@ -120,9 +122,9 @@ export function DialogStatus() { )} - 0} fallback={No Formatters}> + 0} fallback={{t("tui.status.no_formatters")}}> - {enabledFormatters().length} Formatters + {t("tui.status.formatters", { count: enabledFormatters().length })} {(item) => ( @@ -142,9 +144,9 @@ export function DialogStatus() { - 0} fallback={No Plugins}> + 0} fallback={{t("tui.status.no_plugins")}}> - {plugins().length} Plugins + {t("tui.status.plugins", { count: plugins().length })} {(item) => ( diff --git a/packages/opencode/src/cli/cmd/tui/i18n/en.ts b/packages/opencode/src/cli/cmd/tui/i18n/en.ts index 68c9eb2a..a41c8d82 100644 --- a/packages/opencode/src/cli/cmd/tui/i18n/en.ts +++ b/packages/opencode/src/cli/cmd/tui/i18n/en.ts @@ -428,4 +428,18 @@ export const dict: Record = { "trust.dangerous.advice_root": "Unless you have a very specific reason, DO NOT trust the filesystem root.", "trust.dangerous.option.yes": "I understand the risks, trust for this session", "trust.dangerous.option.no": "Exit (recommended)", -} + + // Status dialog + "tui.status.title": "Status", + "tui.status.no_mcp": "No MCP Servers", + "tui.status.mcp_servers": "{{count}} MCP Servers", + "tui.status.connected": "Connected", + "tui.status.pending_approval": "Pending approval", + "tui.status.disabled_config": "Disabled in configuration", + "tui.status.needs_auth": "Needs authentication (run: mimo mcp auth {{name}})", + "tui.status.lsp_servers": "{{count}} LSP Servers", + "tui.status.no_formatters": "No Formatters", + "tui.status.formatters": "{{count}} Formatters", + "tui.status.no_plugins": "No Plugins", + "tui.status.plugins": "{{count}} Plugins", +} \ No newline at end of file diff --git a/packages/opencode/src/cli/cmd/tui/i18n/zh.ts b/packages/opencode/src/cli/cmd/tui/i18n/zh.ts index cf12b5e0..f6d6daad 100644 --- a/packages/opencode/src/cli/cmd/tui/i18n/zh.ts +++ b/packages/opencode/src/cli/cmd/tui/i18n/zh.ts @@ -421,4 +421,19 @@ export const dict = { "trust.dangerous.advice_root": "除非有明确的理由,否则不要信任文件系统根目录。", "trust.dangerous.option.yes": "我了解风险,仅本次信任", "trust.dangerous.option.no": "退出(推荐)", + + // Status dialog + "tui.status.title": "状态", + "tui.status.no_mcp": "无 MCP 服务器", + "tui.status.mcp_servers": "{{count}} 个 MCP 服务器", + "tui.status.connected": "已连接", + "tui.status.pending_approval": "等待授权", + "tui.status.disabled_config": "已在配置中禁用", + "tui.status.needs_auth": "需要认证(运行:mimo mcp auth {{name}})", + "tui.status.lsp_servers": "{{count}} 个 LSP 服务器", + "tui.status.no_formatters": "无格式化工具", + "tui.status.formatters": "{{count}} 个格式化工具", + "tui.status.no_plugins": "无插件", + "tui.status.plugins": "{{count}} 个插件", + } satisfies Partial> diff --git a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx index 8b1ea67e..3719f729 100644 --- a/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx +++ b/packages/opencode/src/cli/cmd/tui/routes/session/index.tsx @@ -91,6 +91,7 @@ import { TuiPluginRuntime } from "../../plugin" import { DialogGoUpsell } from "../../component/dialog-go-upsell" import { SessionRetry } from "@/session/retry" import { getRevertDiffFiles } from "../../util/revert-diff" +import { Log } from "@/util" addDefaultParsers(parsers.parsers) @@ -129,6 +130,7 @@ export function Session() { const tuiConfig = useTuiConfig() const kv = useKV() const { theme } = useTheme() + const log = Log.create({ service: "tui.session" }) const promptRef = usePromptRef() const session = createMemo(() => sync.session.get(route.sessionID)) const currentAgentID = useCurrentAgentID() @@ -295,7 +297,7 @@ export function Session() { if (keybind.match("app_exit", evt)) { const status = sync.data.session_status?.[route.sessionID] if (status && status.type !== "idle") { - void sdk.client.session.abort({ sessionID: route.sessionID }).catch(() => {}) + void sdk.client.session.abort({ sessionID: route.sessionID }).catch((err: any) => log.warn("Failed to abort session on exit", { error: String(err) })) return } void exit() @@ -544,7 +546,7 @@ export function Session() { }, onSelect: async (dialog) => { const status = sync.data.session_status?.[route.sessionID] - if (status?.type !== "idle") await sdk.client.session.abort({ sessionID: route.sessionID }).catch(() => {}) + if (status?.type !== "idle") await sdk.client.session.abort({ sessionID: route.sessionID }).catch((err: any) => log.warn("Failed to abort session before undo", { error: String(err) })) const revert = session()?.revert?.messageID const message = messages().findLast((x) => (!revert || x.id < revert) && x.role === "user") if (!message) return