дзеркало
https://github.com/l0ng-ai/openidea.git
синхронізовано 2026-11-09 03:05:40 +02:00
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
Цей коміт міститься в:
1 джерело
2616e3fe6d
коміт
b03983847c
142 files changed
+1032
-363
No files matched your search
@@ -7,6 +7,7 @@ import type { RunConfig } from "@/platform/ipc";
|
||||
import { commandLine, configIcon, groupConfigs } from "./configs";
|
||||
import { canDeleteConfig, CURRENT_FILE, deleteConfig, saveTemporary, selectConfig, startRun, useRun } from "./store";
|
||||
import styles from "./Run.module.css";
|
||||
import { tx } from "@/platform/i18n";
|
||||
|
||||
type Row =
|
||||
| { type: "section"; key: string; label: string }
|
||||
@@ -281,7 +282,7 @@ function ConfigChooser() {
|
||||
icon="run"
|
||||
size="small"
|
||||
iconSize={11}
|
||||
title={`Run '${c.name}'`}
|
||||
title={tx`Run '${c.name}'`}
|
||||
tabIndex={-1}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
@@ -292,7 +293,7 @@ function ConfigChooser() {
|
||||
icon="debug"
|
||||
size="small"
|
||||
iconSize={13}
|
||||
title={`Debug '${c.name}'`}
|
||||
title={tx`Debug '${c.name}'`}
|
||||
tabIndex={-1}
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
|
||||
@@ -545,7 +545,7 @@ export function ConfigForm({ config, all, type, errors, template, onChange, modi
|
||||
icon="close"
|
||||
size="small"
|
||||
iconSize={10}
|
||||
title={`Remove '${OPTION_LABELS[id]}'`}
|
||||
title={tx`Remove '${OPTION_LABELS[id]}'`}
|
||||
onClick={() => update(optionPatch(id, false))}
|
||||
/>
|
||||
</span>
|
||||
|
||||
@@ -28,6 +28,7 @@ import {
|
||||
import { TestTreeView, formatDuration } from "./TestTreeView";
|
||||
import { StackTracePanel, closeStackTrace, useStackTraces, type StackTraceTab } from "./stackTrace";
|
||||
import styles from "./Run.module.css";
|
||||
import { tx } from "@/platform/i18n";
|
||||
|
||||
function failed(s: RunSession): boolean {
|
||||
return s.status === "failed" || (s.status === "finished" && s.exitCode !== 0 && !s.stopped);
|
||||
@@ -81,7 +82,7 @@ function SessionPanel({ session }: { session: RunSession }) {
|
||||
cwd={cwd}
|
||||
onData={(data) => writeTerminal(session.id, data)}
|
||||
onResize={(cols, rows) => resizeTerminal(session.id, cols, rows)}
|
||||
aria-label={`${session.config.name} terminal`}
|
||||
aria-label={tx`${session.config.name} terminal`}
|
||||
/>
|
||||
) : (
|
||||
<ConsoleView
|
||||
@@ -102,7 +103,7 @@ function SessionPanel({ session }: { session: RunSession }) {
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
aria-label={`${session.config.name} output`}
|
||||
aria-label={tx`${session.config.name} output`}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
@@ -196,7 +197,7 @@ function sessionItems(sessions: readonly RunSession[]): TabItem[] {
|
||||
closable: true,
|
||||
tooltip:
|
||||
s.status === "finished"
|
||||
? `Finished in ${formatDuration((s.endedAt ?? s.startedAt) - s.startedAt)}, exit code ${s.exitCode ?? "?"}`
|
||||
? tx`Finished in ${formatDuration((s.endedAt ?? s.startedAt) - s.startedAt)}, exit code ${s.exitCode ?? "?"}`
|
||||
: undefined,
|
||||
}));
|
||||
}
|
||||
|
||||
@@ -85,7 +85,7 @@ export function RunWidget() {
|
||||
size="large"
|
||||
iconSize={16}
|
||||
className={clsx(styles.widgetButton, styles.widgetFilled, styles.widgetFilledRun)}
|
||||
title={`Rerun '${compoundRunning.length && selected ? selected.name : target.config.name}'`}
|
||||
title={tx`Rerun '${compoundRunning.length && selected ? selected.name : target.config.name}'`}
|
||||
shortcut={shortcutLabel("run.run")}
|
||||
onClick={() => (compoundRunning.length && selected ? void startRun(selected) : rerunSession(target.id))}
|
||||
/>
|
||||
@@ -96,10 +96,10 @@ export function RunWidget() {
|
||||
className={clsx(styles.widgetButton, styles.widgetFilled, styles.widgetFilledStop)}
|
||||
title={
|
||||
compoundRunning.length && selected
|
||||
? `Stop '${selected.name}'`
|
||||
? tx`Stop '${selected.name}'`
|
||||
: running.length > 1
|
||||
? `Stop (${running.length} running)`
|
||||
: `Stop '${target.config.name}'`
|
||||
? tx`Stop (${running.length} running)`
|
||||
: tx`Stop '${target.config.name}'`
|
||||
}
|
||||
shortcut={shortcutLabel("run.stop")}
|
||||
onClick={() =>
|
||||
@@ -155,8 +155,8 @@ export function RunWidget() {
|
||||
title={
|
||||
selected
|
||||
? debugOnly
|
||||
? `'${selected.name}' can only be debugged`
|
||||
: `Run '${selected.name}'`
|
||||
? tx`'${selected.name}' can only be debugged`
|
||||
: tx`Run '${selected.name}'`
|
||||
: currentFile
|
||||
? "Run Current File"
|
||||
: "Run"
|
||||
@@ -170,7 +170,7 @@ export function RunWidget() {
|
||||
size="large"
|
||||
iconSize={18}
|
||||
className={styles.widgetButton}
|
||||
title={selected ? `Debug '${selected.name}'` : currentFile ? "Debug Current File" : "Debug"}
|
||||
title={selected ? tx`Debug '${selected.name}'` : currentFile ? "Debug Current File" : "Debug"}
|
||||
shortcut={shortcutLabel("run.debug")}
|
||||
disabled={!selected && !currentFile}
|
||||
onClick={() => void executeAction("run.debug")}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { openTarget } from "./navigate";
|
||||
import { nodeStatus, type TestNode, type TestResults, type TestStatus } from "./testing";
|
||||
import { parseComparison } from "./testing/comparison";
|
||||
import styles from "./Run.module.css";
|
||||
import { tx } from "@/platform/i18n";
|
||||
|
||||
/** Test tree filters, kept across runs and restarts (IDEA hides passed tests by default). */
|
||||
const FILTERS_KEY = "openidea.run.testFilters";
|
||||
@@ -39,7 +40,7 @@ export function showTestDiff(node: TestNode): boolean {
|
||||
if (!c) return false;
|
||||
void executeAction("diff.compareTexts", {
|
||||
path: `${node.path.join("/")}.txt`,
|
||||
title: `Comparison Failure: ${node.name}`,
|
||||
title: tx`Comparison Failure: ${node.name}`,
|
||||
left: { text: c.expected, label: c.labels?.expected ?? "Expected" },
|
||||
right: { text: c.actual, label: c.labels?.actual ?? "Actual" },
|
||||
});
|
||||
@@ -136,8 +137,8 @@ export function TestTreeView({ results, version, cwd, selected, onSelect, runnin
|
||||
const items: MenuItem[] = [];
|
||||
if (onRunTests) {
|
||||
items.push(
|
||||
{ label: `Run '${name}'`, verbatim: true, icon: "run", run: () => onRunTests(ids, false) },
|
||||
{ label: `Debug '${name}'`, verbatim: true, icon: "debug", run: () => onRunTests(ids, true) },
|
||||
{ label: tx`Run '${name}'`, verbatim: true, icon: "run", run: () => onRunTests(ids, false) },
|
||||
{ label: tx`Debug '${name}'`, verbatim: true, icon: "debug", run: () => onRunTests(ids, true) },
|
||||
{ kind: "separator" },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -6,6 +6,7 @@ import { loadSettings } from "@/platform/project";
|
||||
import { Dialog, Input } from "@/ui";
|
||||
import { confirmChecked } from "@/workbench/messages";
|
||||
import styles from "./Run.module.css";
|
||||
import { tx } from "@/platform/i18n";
|
||||
|
||||
interface Pending {
|
||||
title: string;
|
||||
@@ -20,8 +21,11 @@ const useRunDialog = create<{ pending: Pending | null }>(() => ({ pending: null
|
||||
*/
|
||||
export async function confirmRerun(name: string, instances = 1): Promise<boolean> {
|
||||
const answer = await confirmChecked({
|
||||
title: `Process '${name}' Is Running`,
|
||||
message: `'${name}' is not allowed to run in parallel.\nWould you like to stop ${instances > 1 ? `${instances} running instances` : "the running one"}?`,
|
||||
title: tx`Process '${name}' Is Running`,
|
||||
message:
|
||||
instances > 1
|
||||
? tx`'${name}' is not allowed to run in parallel.\nWould you like to stop ${instances} running instances?`
|
||||
: tx`'${name}' is not allowed to run in parallel.\nWould you like to stop the running one?`,
|
||||
ok: "Stop and Rerun",
|
||||
checkbox: "Do not ask again",
|
||||
});
|
||||
@@ -39,8 +43,8 @@ export async function confirmRerun(name: string, instances = 1): Promise<boolean
|
||||
/** IDEA's question before closing a Run tab whose process still runs. Resolves true for Terminate. */
|
||||
export async function confirmTerminate(name: string): Promise<boolean> {
|
||||
const answer = await confirmChecked({
|
||||
title: `Process '${name}' Is Running`,
|
||||
message: `Do you want to terminate the process '${name}'?`,
|
||||
title: tx`Process '${name}' Is Running`,
|
||||
message: tx`Do you want to terminate the process '${name}'?`,
|
||||
ok: "Terminate",
|
||||
checkbox: "Do not ask again",
|
||||
});
|
||||
|
||||
@@ -65,6 +65,7 @@ import {
|
||||
stopSession,
|
||||
useRun,
|
||||
} from "./store";
|
||||
import { tx } from "@/platform/i18n";
|
||||
|
||||
function hasProject() {
|
||||
return !!useProject.getState().project;
|
||||
@@ -117,7 +118,7 @@ async function debugConfig(config: RunConfig, chain: readonly string[] = []) {
|
||||
group: "run",
|
||||
level: "warning",
|
||||
title: "Debugger is not available",
|
||||
body: `Running '${config.name}' without the debugger.`,
|
||||
body: tx`Running '${config.name}' without the debugger.`,
|
||||
key: "run-no-debugger",
|
||||
});
|
||||
await startRun(config, undefined, undefined, { prepared: true });
|
||||
|
||||
@@ -3,6 +3,7 @@ import { call, commands, isTauri, nextRequestId } from "@/platform/ipc";
|
||||
import { useProject } from "@/platform/project";
|
||||
import { editorService, notifications } from "@/workbench/contributions";
|
||||
import { joinPath, type FileTarget } from "./links";
|
||||
import { tx } from "@/platform/i18n";
|
||||
|
||||
async function exists(path: string): Promise<boolean> {
|
||||
const slash = path.lastIndexOf("/");
|
||||
@@ -99,7 +100,7 @@ export async function openTarget(target: FileTarget, cwd: string, focus = true):
|
||||
group: "run",
|
||||
level: "info",
|
||||
title: "No sources for this class",
|
||||
body: `${target.className.split("$")[0]} is not part of the project, and no library sources are available for it.`,
|
||||
body: tx`${target.className.split("$")[0]!} is not part of the project, and no library sources are available for it.`,
|
||||
key: "run-link",
|
||||
});
|
||||
return;
|
||||
|
||||
@@ -16,6 +16,7 @@ import { applicationConfig, jvmModuleDir } from "./application";
|
||||
import { formatArgs } from "./configs";
|
||||
import { contextConfig, fileRole, projectPython, testBasesFor, testConfigFor } from "./context";
|
||||
import { detectRunTargets, targetConfig, targetLanguage, type RunTarget } from "./targets";
|
||||
import { tx } from "@/platform/i18n";
|
||||
|
||||
export interface LocatedTool {
|
||||
path: string | null;
|
||||
@@ -300,7 +301,7 @@ const LANGUAGE_NAMES: Partial<Record<ScriptLanguage, string>> = {
|
||||
function missing(lang: ScriptLanguage, file: string, tools: string[]): Missing {
|
||||
const what = LANGUAGE_NAMES[lang] ?? tools[0] ?? lang;
|
||||
return {
|
||||
title: `Cannot run '${file}'`,
|
||||
title: tx`Cannot run '${file}'`,
|
||||
body: `${what} was not found (looked for ${tools.map((t) => `\`${t}\``).join(", ")}). ${INSTALL[lang] ?? ""}`.trim(),
|
||||
};
|
||||
}
|
||||
@@ -421,7 +422,7 @@ function jsRuntime(lang: "js" | "ts", env: ToolEnv, s: FileSite): Produced {
|
||||
name,
|
||||
scope: "file",
|
||||
missing: {
|
||||
title: `Cannot run '${name}'`,
|
||||
title: tx`Cannot run '${name}'`,
|
||||
body:
|
||||
`Running TypeScript needs Node.js 22.6+ (type stripping), tsx or ts-node. ${found}` +
|
||||
"Upgrade Node.js (https://nodejs.org), or install tsx: `npm install -D tsx` (or `npm install -g tsx`).",
|
||||
|
||||
@@ -22,6 +22,7 @@ import { onTestStatesChanged, testState, testStatesVersion, type TestState } fro
|
||||
/** Languages with gutter run targets (C / C++ `main` included). */
|
||||
const hasGutterTargets = (path: string) => !!targetLanguage(path) || /\.(c|cpp|cc|cxx)$/.test(path);
|
||||
import styles from "./Run.module.css";
|
||||
import { tx } from "@/platform/i18n";
|
||||
|
||||
interface PlacedTarget extends RunTarget {
|
||||
/** Line start offset in the current state. */
|
||||
@@ -104,9 +105,9 @@ export function targetMenuItems(target: RunTarget, path: string, content: string
|
||||
// Like IDEA, Debug only where a debugger can run it.
|
||||
const debuggable = !p || !!(p.debug || p.config?.debugAdapter || p.missing);
|
||||
return [
|
||||
{ label: `Run '${name}'`, icon: "run", shortcut: shortcutLabel("run.runContext"), ...pick(false) } as MenuItem,
|
||||
{ label: tx`Run '${name}'`, icon: "run", shortcut: shortcutLabel("run.runContext"), ...pick(false) } as MenuItem,
|
||||
...(debuggable
|
||||
? [{ label: `Debug '${name}'`, icon: "debug", shortcut: shortcutLabel("run.debugContext"), ...pick(true) } as MenuItem]
|
||||
? [{ label: tx`Debug '${name}'`, icon: "debug", shortcut: shortcutLabel("run.debugContext"), ...pick(true) } as MenuItem]
|
||||
: []),
|
||||
...runExecutors()
|
||||
.filter((e) => !!p?.config && e.supports(p.config))
|
||||
|
||||
@@ -9,7 +9,7 @@ import { create } from "zustand";
|
||||
import { executeAction } from "@/platform/actions";
|
||||
import type { RunConfig } from "@/platform/ipc";
|
||||
import { useProject } from "@/platform/project";
|
||||
import { useT, useTxTemplate } from "@/platform/i18n";
|
||||
import { tx, useT, useTxTemplate } from "@/platform/i18n";
|
||||
import { Button, EmptyState, T } from "@/ui";
|
||||
import type { MenuItem } from "@/ui";
|
||||
import {
|
||||
@@ -120,7 +120,7 @@ export function runServiceItems(
|
||||
key: TYPE_PREFIX + kind,
|
||||
label: kindLabel(kind),
|
||||
icon: configIcon({ kind, isTest: false }),
|
||||
detail: running ? `${running} running` : undefined,
|
||||
detail: running ? tx`${running} running` : undefined,
|
||||
children,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -15,6 +15,7 @@ import { ConsoleView } from "./ConsoleView";
|
||||
import { findOutputLinks } from "./links";
|
||||
import { useRun } from "./store";
|
||||
import styles from "./StackTrace.module.css";
|
||||
import { tx } from "@/platform/i18n";
|
||||
|
||||
export interface StackTraceTab {
|
||||
id: string;
|
||||
@@ -142,7 +143,7 @@ export function StackTracePanel({ tab }: { tab: StackTraceTab }) {
|
||||
follow={false}
|
||||
onFollowChange={() => {}}
|
||||
cwd={root}
|
||||
aria-label={`Stack trace ${tab.title}`}
|
||||
aria-label={tx`Stack trace ${tab.title}`}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ import { failedTestPaths, leafTestPaths, rerunFailedConfig, testsConfig } from "
|
||||
import { compoundChildren } from "./configs";
|
||||
import { TerminalOutput } from "./terminalOutput";
|
||||
import { typeFor } from "./types";
|
||||
import { tx } from "@/platform/i18n";
|
||||
|
||||
/** The permanent "Current File" pseudo-configuration of the chooser (runs the active file's context). */
|
||||
export const CURRENT_FILE = "current-file";
|
||||
@@ -549,7 +550,7 @@ export async function startRun(
|
||||
}
|
||||
} catch (e) {
|
||||
const message = e instanceof Error ? e.message : String(e);
|
||||
enqueue(sessionId, { kind: "stderr", text: `Error running '${config.name}': ${message}\n` });
|
||||
enqueue(sessionId, { kind: "stderr", text: tx`Error running '${config.name}': ${message}\n` });
|
||||
flush(sessionId);
|
||||
patchSession(sessionId, { status: "failed", endedAt: Date.now() });
|
||||
notifications().notify({ group: "run", level: "error", title: `Error running '${config.name}'`, body: message });
|
||||
@@ -795,7 +796,7 @@ export async function sendStdin(sessionId: string, text: string): Promise<void>
|
||||
try {
|
||||
await call(commands.runStdin(session.runId, text + "\n"));
|
||||
} catch (e) {
|
||||
enqueue(sessionId, { kind: "system", text: `Cannot write to stdin: ${e instanceof Error ? e.message : String(e)}\n` });
|
||||
enqueue(sessionId, { kind: "system", text: tx`Cannot write to stdin: ${e instanceof Error ? e.message : String(e)}\n` });
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ import {
|
||||
type RunConfigTypeContribution,
|
||||
} from "@/workbench/contributions";
|
||||
import { formatArgs, parseArgs, uid, uniqueName } from "./configs";
|
||||
import { tx } from "@/platform/i18n";
|
||||
|
||||
export const COMPOUND = "compound";
|
||||
|
||||
@@ -203,21 +204,21 @@ function buildToolType(
|
||||
id: string,
|
||||
label: string,
|
||||
icon: string,
|
||||
tasksLabel: string,
|
||||
tasks: { label: string; description: string },
|
||||
wrapper: { unix: string; windows: string; plain: string; label: string; byDefault: boolean },
|
||||
): RunConfigTypeContribution {
|
||||
return {
|
||||
id,
|
||||
label,
|
||||
icon,
|
||||
description: `Run ${label} ${tasksLabel.toLowerCase()}`,
|
||||
description: tasks.description,
|
||||
defaults: wrapper.byDefault ? { wrapper: "true" } : {},
|
||||
ownsWorkingDirectory: true,
|
||||
fields: [
|
||||
{ key: "projectDir", label: `${label} project`, mono: true, placeholder: "Project root" },
|
||||
{ key: "projectDir", label: tx`${label} project`, mono: true, placeholder: "Project root" },
|
||||
{
|
||||
key: "tasks",
|
||||
label: tasksLabel,
|
||||
label: tasks.label,
|
||||
mono: true,
|
||||
required: true,
|
||||
suggestions: (configs) => argsOf(configs, id, 0),
|
||||
@@ -244,7 +245,7 @@ function buildToolType(
|
||||
};
|
||||
}
|
||||
|
||||
const gradle = buildToolType("gradle", "Gradle", "file-gradle", "Tasks", {
|
||||
const gradle = buildToolType("gradle", "Gradle", "file-gradle", { label: "Tasks", description: "Run Gradle tasks" }, {
|
||||
unix: "gradlew",
|
||||
windows: "gradlew.bat",
|
||||
plain: "gradle",
|
||||
@@ -252,7 +253,7 @@ const gradle = buildToolType("gradle", "Gradle", "file-gradle", "Tasks", {
|
||||
byDefault: true,
|
||||
});
|
||||
|
||||
const maven = buildToolType("maven", "Maven", "file-maven", "Goals", {
|
||||
const maven = buildToolType("maven", "Maven", "file-maven", { label: "Goals", description: "Run Maven goals" }, {
|
||||
unix: "mvnw",
|
||||
windows: "mvnw.cmd",
|
||||
plain: "mvn",
|
||||
|
||||
Reference in new issue
Заблокувати користувача