fix(i18n): Translate UI text built from template literals

Template literals such as `Remove data source '${name}'?` reached props and
calls that look text up exactly (dialog messages, labels, aria-labels, menu
items, action titles), so they always showed in English. Some notification
titles also spliced lowercase English words into a sentence.

- `tx` also works as a tag: tx`Remove data source '${name}'?` looks up
  "Remove data source '{name}'?" by its literal parts, fills in the values
  (translated when the catalog has them) and remembers the result, so the
  primitives that pass strings through `tx` translate it again after a locale
  change or once the catalog has loaded (actions registered at startup).
- 269 template literals at exact-lookup sites now use the tag; text that
  spliced words or plural suffixes is written as one sentence per variant
  (safe delete, Docker delete/prune, rebase, conflicts, VCS "No … in
  progress", database copy errors, inspection descriptions, ...).
- zh: 280 new entries; stale and duplicate-shape entries removed.
- textCatalog guard: untagged template literals at exact-lookup sites fail
  (with an allowlist for code-like text), tagged text is checked against the
  catalog by shape, case-changing values and `c ? "a" : "b"` suffixes inside
  translated templates fail, and a catalog may hold one entry per template shape.

Claude-Session: https://claude.ai/code/session_01VUYsdXA2nkF5v6zsC93dpn
Цей коміт міститься в:
l0ng-ai committed 2026-10-27 18:43:53 +08:00
1 джерело 2616e3fe6d
коміт b03983847c
142 files changed
+1032 -363

No files matched your search

+4 -3
Переглянути файл
@@ -32,6 +32,7 @@ import { runWithExecutor } from "./executors";
import { goModulePath, loadGoModules } from "./goModules";
import { toolEnv } from "./runtimes";
import { adoptTemporary, loadConfigs, modifyTemporary, startRun, useRun } from "./store";
import { tx } from "@/platform/i18n";
const CONTENT_LIMIT = 400_000;
@@ -180,7 +181,7 @@ export function choiceItems(choices: readonly Produced[], debug: boolean): MenuI
{ kind: "header", label: debug ? "Choose configuration to debug" : "Choose configuration to run" },
...choices.map(
(p): MenuItem => ({
label: `${debug ? "Debug" : "Run"} '${p.name}'`,
label: debug ? tx`Debug '${p.name}'` : tx`Run '${p.name}'`,
verbatim: true,
icon: debug ? "debug" : "run",
shortcut: p.detail,
@@ -316,10 +317,10 @@ export function contextMenuItems(menu: ContextMenuId, ctx: MenuGroupContext): Me
if (!p) return [];
const arg: ContextArg = { path, line, dir, content: dir ? undefined : input.content };
return [
{ kind: "action", action: "run.runContext", arg, label: `Run '${p.name}'`, icon: "run" },
{ kind: "action", action: "run.runContext", arg, label: tx`Run '${p.name}'`, icon: "run" },
// Like IDEA, Debug only where a debugger can run it.
...(p.debug || p.config?.debugAdapter || p.missing
? ([{ kind: "action", action: "run.debugContext", arg, label: `Debug '${p.name}'`, icon: "debug" }] as MenuItem[])
? ([{ kind: "action", action: "run.debugContext", arg, label: tx`Debug '${p.name}'`, icon: "debug" }] as MenuItem[])
: []),
{
label: "More Run/Debug",