diff --git a/packages/cli/src/commands/dashboard-tui/app.tsx b/packages/cli/src/commands/dashboard-tui/app.tsx index b9d1c18f2a..af48b5409c 100644 --- a/packages/cli/src/commands/dashboard-tui/app.tsx +++ b/packages/cli/src/commands/dashboard-tui/app.tsx @@ -2,6 +2,7 @@ import React, { useState, useSyncExternalStore, useCallback, useEffect, useRef } import { Box, Text, useInput, useApp, useStdout } from "ink"; import Spinner from "ink-spinner"; import TextInput from "ink-text-input"; +import { useTranslation } from "react-i18next"; import { spawn } from "node:child_process"; import { appendFileSync } from "node:fs"; @@ -1695,6 +1696,7 @@ function groupTasksByColumn(tasks: TaskItem[]): Record } function BoardView({ state, controller }: { state: DashboardState; controller: DashboardTUI }) { + const { t } = useTranslation("cli"); const { stdout } = useStdout(); const cols = stdout?.columns ?? 80; const rows = stdout?.rows ?? 24; @@ -1937,12 +1939,13 @@ function BoardView({ state, controller }: { state: DashboardState; controller: D ) : tasksState.loading ? ( - Loading tasks… + {t("tui.loadingTasks", "Loading tasks…")} ) : tasksState.tasks.length === 0 ? ( - No tasks in this project. - Press [p] to switch projects. + {t("tui.noTasks", "No tasks in this project.")} + {/* Keybinding accelerator [{{key}}] stays literal — never translated. */} + {t("tui.switchProjectsHint", { key: "p", defaultValue: "Press [{{key}}] to switch projects." })} ) : ( diff --git a/packages/cli/src/i18n/__tests__/i18n.test.tsx b/packages/cli/src/i18n/__tests__/i18n.test.tsx index ff8a113af9..7c2c0702df 100644 --- a/packages/cli/src/i18n/__tests__/i18n.test.tsx +++ b/packages/cli/src/i18n/__tests__/i18n.test.tsx @@ -67,3 +67,36 @@ describe("react-i18next under the Ink reconciler", () => { expect(lastFrame()).toContain("加载中…"); }); }); + +// Keybinding accelerators must stay literal even inside translated hints. +function ProjectHint() { + const { t } = useTranslation("cli"); + return createElement( + Text, + null, + t("tui.switchProjectsHint", { key: "p", defaultValue: "Press [{{key}}] to switch projects." }), + ); +} + +describe("CLI string migration", () => { + it("renders the migrated en label", () => { + const i18n = initCliI18n("en"); + function Tasks() { + const { t } = useTranslation("cli"); + return createElement(Text, null, t("tui.loadingTasks", "Loading tasks…")); + } + const { lastFrame } = render( + createElement(I18nextProvider, { i18n }, createElement(Tasks)), + ); + expect(lastFrame()).toContain("Loading tasks…"); + }); + + it("keeps the [p] accelerator literal in an interpolated hint", () => { + const i18n = initCliI18n("en"); + const { lastFrame } = render( + createElement(I18nextProvider, { i18n }, createElement(ProjectHint)), + ); + expect(lastFrame()).toContain("[p]"); + expect(lastFrame()).toContain("Press"); + }); +}); diff --git a/packages/i18n/locales/en/cli.json b/packages/i18n/locales/en/cli.json index 7bfc7fe5c3..0bd13e9baa 100644 --- a/packages/i18n/locales/en/cli.json +++ b/packages/i18n/locales/en/cli.json @@ -1,6 +1,9 @@ { "tui": { "loading": "Loading…", + "loadingTasks": "Loading tasks…", + "noTasks": "No tasks in this project.", + "switchProjectsHint": "Press [{{key}}] to switch projects.", "quit": "Quit" } } diff --git a/packages/i18n/locales/es/cli.json b/packages/i18n/locales/es/cli.json index 8ccc6193b3..8a0833db51 100644 --- a/packages/i18n/locales/es/cli.json +++ b/packages/i18n/locales/es/cli.json @@ -1,6 +1,9 @@ { "tui": { "loading": "", - "quit": "" + "quit": "", + "loadingTasks": "", + "noTasks": "", + "switchProjectsHint": "" } } diff --git a/packages/i18n/locales/fr/cli.json b/packages/i18n/locales/fr/cli.json index 8ccc6193b3..8a0833db51 100644 --- a/packages/i18n/locales/fr/cli.json +++ b/packages/i18n/locales/fr/cli.json @@ -1,6 +1,9 @@ { "tui": { "loading": "", - "quit": "" + "quit": "", + "loadingTasks": "", + "noTasks": "", + "switchProjectsHint": "" } } diff --git a/packages/i18n/locales/zh-CN/cli.json b/packages/i18n/locales/zh-CN/cli.json index 8ccc6193b3..8a0833db51 100644 --- a/packages/i18n/locales/zh-CN/cli.json +++ b/packages/i18n/locales/zh-CN/cli.json @@ -1,6 +1,9 @@ { "tui": { "loading": "", - "quit": "" + "quit": "", + "loadingTasks": "", + "noTasks": "", + "switchProjectsHint": "" } } diff --git a/packages/i18n/locales/zh-TW/cli.json b/packages/i18n/locales/zh-TW/cli.json index 8ccc6193b3..8a0833db51 100644 --- a/packages/i18n/locales/zh-TW/cli.json +++ b/packages/i18n/locales/zh-TW/cli.json @@ -1,6 +1,9 @@ { "tui": { "loading": "", - "quit": "" + "quit": "", + "loadingTasks": "", + "noTasks": "", + "switchProjectsHint": "" } }