From f088a4afb0cf9df5afb333c60e51158b325d7405 Mon Sep 17 00:00:00 2001 From: l0ng-ai <24760907+l0ng-ai@users.noreply.github.com> Date: Wed, 4 Nov 2026 09:53:56 +0800 Subject: [PATCH] fix(i18n): translate notification titles passed through helpers (#1142) Debugger action failures, project tree clipboard / create-file failures and Docker operation failures pass their title through a helper before notify, so the text catalog check never saw them and they showed in English. The call sites now tag the text with tx, and Docker failures name each operation in a full sentence instead of splicing an English verb or object into one. --- src/features/debugger/actions.ts | 22 +++++++-------- src/features/project-tree/operations.ts | 6 ++-- src/features/services/store.ts | 37 ++++++++++++++++--------- src/platform/locales/text/zh.json | 26 ++++++++++++++++- 4 files changed, 63 insertions(+), 28 deletions(-) diff --git a/src/features/debugger/actions.ts b/src/features/debugger/actions.ts index 2db2c3e6..381f291c 100644 --- a/src/features/debugger/actions.ts +++ b/src/features/debugger/actions.ts @@ -91,7 +91,7 @@ export function runToLine(path: string, line: number) { const t = pausedTarget(); if (t && breakpointPathAllowed(path)) void report( - "Run to Cursor failed", + tx("Run to Cursor failed"), resumeExecution(t.s.id, () => call(commands.debugRunTo(t.s.id, t.thread, path, line, false))), ); } @@ -340,7 +340,7 @@ export function registerDebugActions(): () => void { category: "Debug", icon: "debug", description: "Start debugging a run configuration", - run: ({ arg }) => report("Cannot start debugging", startConfig(arg)), + run: ({ arg }) => report(tx("Cannot start debugging"), startConfig(arg)), }, { id: "debug.attach", @@ -351,7 +351,7 @@ export function registerDebugActions(): () => void { "Attach the Java debugger to a JVM listening for JDWP (arg: { name, module, hostName, port, process })", run: ({ arg }) => report( - "Cannot attach the debugger", + tx("Cannot attach the debugger"), (async () => { const root = useProject.getState().project?.root; if (!root || !isJavaAttachTarget(arg)) return; @@ -414,7 +414,7 @@ export function registerDebugActions(): () => void { const t = pausedTarget(); if (t) void report( - "Resume failed", + tx("Resume failed"), resumeExecution(t.s.id, () => call(commands.debugResume(t.s.id, t.thread))), ); }, @@ -433,7 +433,7 @@ export function registerDebugActions(): () => void { const threads = await call(commands.debugThreads(s.id)).catch(() => []); thread = threads[0]?.id ?? 0; } - await report("Pause failed", call(commands.debugPause(s.id, thread))); + await report(tx("Pause failed"), call(commands.debugPause(s.id, thread))); }, }, step("debug.stepOver", "Step Over", "over", "step-over"), @@ -450,7 +450,7 @@ export function registerDebugActions(): () => void { const t = pausedTarget(); if (t) void report( - "Force Step Over failed", + tx("Force Step Over failed"), resumeExecution(t.s.id, () => call(commands.debugForceStepOver(t.s.id, t.thread))), ); }, @@ -467,7 +467,7 @@ export function registerDebugActions(): () => void { const t = pausedTarget(); if (t) void report( - "Force Step Into failed", + tx("Force Step Into failed"), resumeExecution(t.s.id, () => call(commands.debugStep(t.s.id, t.thread, "into"))), ); }, @@ -479,7 +479,7 @@ export function registerDebugActions(): () => void { icon: "smart-step-into", description: "Choose which call on the current line to step into", when: paused, - run: () => void report("Smart Step Into failed", smartStepInto()), + run: () => void report(tx("Smart Step Into failed"), smartStepInto()), }, { id: "debug.showExecutionPoint", @@ -509,7 +509,7 @@ export function registerDebugActions(): () => void { return; } void report( - "Reset Frame failed", + tx("Reset Frame failed"), resumeExecution(t.s.id, () => call(commands.debugRestartFrame(t.s.id, frame.id))), ); }, @@ -559,7 +559,7 @@ export function registerDebugActions(): () => void { const c = cursor(); if (t && c && breakpointPathAllowed(c.path)) void report( - "Run to Cursor failed", + tx("Run to Cursor failed"), resumeExecution(t.s.id, () => call(commands.debugRunTo(t.s.id, t.thread, c.path, c.line, false))), ); }, @@ -576,7 +576,7 @@ export function registerDebugActions(): () => void { const c = cursor(); if (t && c && breakpointPathAllowed(c.path)) void report( - "Force Run to Cursor failed", + tx("Force Run to Cursor failed"), resumeExecution(t.s.id, () => call(commands.debugRunTo(t.s.id, t.thread, c.path, c.line, true))), ); }, diff --git a/src/features/project-tree/operations.ts b/src/features/project-tree/operations.ts index 528dc3b0..0ff55c87 100644 --- a/src/features/project-tree/operations.ts +++ b/src/features/project-tree/operations.ts @@ -266,7 +266,7 @@ export async function commitEditing(name: string): Promise { // An existing intermediate package keeps its own `__init__.py`. await fs.createFile(joinPath(dir, "__init__.py")).catch(() => {}); } catch (e) { - fail(editing.nodeKind === "file" ? "Cannot create file" : "Cannot create folder", e); + fail(editing.nodeKind === "file" ? tx("Cannot create file") : tx("Cannot create folder"), e); return true; } await refreshAfterCreate(editing.parent, path); @@ -598,7 +598,7 @@ export async function paste(target = targetFolder()) { external = files; } catch (e) { if (!get().clipboard?.paths.length) { - fail("Cannot access the clipboard", e); + fail(tx("Cannot access the clipboard"), e); return; } } @@ -797,7 +797,7 @@ export async function copyPaths(kind: PathKind, nodes: ProjectNode[] = selectedN try { await navigator.clipboard.writeText(text); } catch (e) { - fail("Cannot access the clipboard", e); + fail(tx("Cannot access the clipboard"), e); } } diff --git a/src/features/services/store.ts b/src/features/services/store.ts index 7b51c2e9..27d1b835 100644 --- a/src/features/services/store.ts +++ b/src/features/services/store.ts @@ -19,6 +19,7 @@ import type { PruneTarget, RequestId, } from "@/platform/ipc"; +import { tx } from "@/platform/i18n"; import { call, commands, isCancelled, isTauri, nextRequestId } from "@/platform/ipc"; import { notifications } from "@/workbench/contributions"; @@ -292,24 +293,32 @@ async function guarded(connId: string, title: string, run: () => Promise { - const verb = { start: "start", stop: "stop", restart: "restart", pause: "pause", unpause: "unpause", kill: "kill" }[ - op - ]; - return guarded(connId, `Could not ${verb} container`, () => - call(commands.dockerContainerOp(endpointOf(connId), ids, op)), - ); + const failure = { + start: tx("Could not start container"), + stop: tx("Could not stop container"), + restart: tx("Could not restart container"), + pause: tx("Could not pause container"), + unpause: tx("Could not unpause container"), + kill: tx("Could not kill container"), + }[op]; + return guarded(connId, failure, () => call(commands.dockerContainerOp(endpointOf(connId), ids, op))); } export function removeObjects(connId: string, object: DockerObject, ids: string[], force: boolean): Promise { - return guarded(connId, `Could not remove ${object}`, () => - call(commands.dockerRemove(endpointOf(connId), object, ids, force)), - ); + const failure = { + container: tx("Could not remove container"), + image: tx("Could not remove image"), + volume: tx("Could not remove volume"), + network: tx("Could not remove network"), + }[object]; + return guarded(connId, failure, () => call(commands.dockerRemove(endpointOf(connId), object, ids, force))); } /** Runs a streaming operation, showing its latest output line in the view's banner. */ async function task( connId: string, title: string, + failure: string, run: (request: RequestId, out: Channel) => Promise, ): Promise { if (useServices.getState().task) { @@ -330,7 +339,7 @@ async function task( }; useServices.setState({ task: { title, line: "", request } }); try { - return await guarded(connId, title.replace(/…$/, "") + " failed", () => run(request, out)); + return await guarded(connId, failure, () => run(request, out)); } finally { useServices.setState({ task: null }); if (last) @@ -350,15 +359,17 @@ export function cancelTask() { } export const pullImage = (connId: string, reference: string) => - task(connId, `Pulling ${reference}…`, (r, out) => call(commands.dockerPull(r, endpointOf(connId), reference, out))); + task(connId, `Pulling ${reference}…`, tx`Could not pull ${reference}`, (r, out) => + call(commands.dockerPull(r, endpointOf(connId), reference, out)), + ); export const prune = (connId: string, target: PruneTarget) => - task(connId, `Pruning ${target === "allImages" ? "unused images" : target}…`, (r, out) => + task(connId, `Pruning ${target === "allImages" ? "unused images" : target}…`, tx("Could not prune"), (r, out) => call(commands.dockerPrune(r, endpointOf(connId), target, out)), ); export function composeOp(connId: string, project: string, files: string[], workingDir: string | null, op: ComposeOp) { - return task(connId, `Compose ${op} ${project}…`, (r, out) => + return task(connId, `Compose ${op} ${project}…`, tx`Compose ${op} failed for ${project}`, (r, out) => call(commands.dockerComposeOp(r, endpointOf(connId), project, files, workingDir, op, out)), ); } diff --git a/src/platform/locales/text/zh.json b/src/platform/locales/text/zh.json index be6dbdaf..ed0d5867 100644 --- a/src/platform/locales/text/zh.json +++ b/src/platform/locales/text/zh.json @@ -5823,5 +5823,29 @@ "Open {addr} in Browser": "在浏览器中打开 {addr}", "Copy {addr}": "复制 {addr}", "Stop Forwarding Port {remotePort}": "停止转发端口 {remotePort}", - "Ports": "端口" + "Ports": "端口", + "Run to Cursor failed": "运行到光标处失败", + "Cannot start debugging": "无法开始调试", + "Cannot attach the debugger": "无法附加调试器", + "Resume failed": "恢复失败", + "Pause failed": "暂停失败", + "Force Step Over failed": "强制步过失败", + "Force Step Into failed": "强制步入失败", + "Reset Frame failed": "重置帧失败", + "Force Run to Cursor failed": "强制运行到光标处失败", + "Cannot create file": "无法创建文件", + "Cannot access the clipboard": "无法访问剪贴板", + "Could not start container": "无法启动容器", + "Could not stop container": "无法停止容器", + "Could not restart container": "无法重启容器", + "Could not pause container": "无法暂停容器", + "Could not unpause container": "无法恢复容器", + "Could not kill container": "无法强制终止容器", + "Could not remove container": "无法删除容器", + "Could not remove image": "无法删除镜像", + "Could not remove volume": "无法删除卷", + "Could not remove network": "无法删除网络", + "Could not pull {reference}": "无法拉取 {reference}", + "Could not prune": "无法清理", + "Compose {op} failed for {project}": "Compose {op} 在 {project} 上失败" }