diff --git a/src/cli/ui/Wizard.tsx b/src/cli/ui/Wizard.tsx index 9706f8b..101330a 100644 --- a/src/cli/ui/Wizard.tsx +++ b/src/cli/ui/Wizard.tsx @@ -36,7 +36,6 @@ import { import type { LanguageCode } from "../../i18n/types.js"; import { type CatalogEntry, MCP_CATALOG } from "../../mcp/catalog.js"; import { MultiSelect, type SelectItem, SingleSelect } from "./Select.js"; -import { PRESET_DESCRIPTIONS } from "./presets.js"; import { ThemeProvider, useTheme } from "./theme/context.js"; import { type ThemeName, listThemeNames } from "./theme/tokens.js"; @@ -705,11 +704,11 @@ function SummaryLine({ label, value }: { label: string; value: string }) { ); } -function presetItems(): SelectItem[] { +export function presetItems(): SelectItem[] { return (["auto", "flash", "pro"] as const).map((name) => ({ value: name as PresetName, - label: `${name} — ${PRESET_DESCRIPTIONS[name].headline}`, - hint: PRESET_DESCRIPTIONS[name].cost, + label: `${name} — ${t(`wizard.presetDescriptions.${name}.headline`)}`, + hint: t(`wizard.presetDescriptions.${name}.cost`), })); } diff --git a/src/i18n/EN.ts b/src/i18n/EN.ts index 0ac9539..c1c849f 100644 --- a/src/i18n/EN.ts +++ b/src/i18n/EN.ts @@ -451,6 +451,20 @@ export const EN: TranslationSchema = { "github-light": "GitHub light", "high-contrast": "Accessibility", }, + presetDescriptions: { + auto: { + headline: "flash → pro on hard turns", + cost: "default · ~96% turns stay on flash · pro kicks in only when needed", + }, + flash: { + headline: "v4-flash always", + cost: "cheapest · predictable · /pro still works for a one-turn bump", + }, + pro: { + headline: "v4-pro always", + cost: "~3× flash (5/31 discount) / ~12× full price · for hard multi-turn work", + }, + }, reviewLabelTheme: "Theme", presetTitle: "Pick a preset", mcpTitle: "Which MCP servers should Carbon Code wire up for you?", diff --git a/src/i18n/types.ts b/src/i18n/types.ts index 8282c0a..afc06a0 100644 --- a/src/i18n/types.ts +++ b/src/i18n/types.ts @@ -306,6 +306,7 @@ export interface TranslationSchema { themeSampleHeading: string; themeFooter: string; themeCaption: Record; + presetDescriptions: Record<"auto" | "flash" | "pro", { headline: string; cost: string }>; reviewTitle: string; reviewLabelApiKey: string; reviewLabelLanguage: string; diff --git a/src/i18n/zh-CN.ts b/src/i18n/zh-CN.ts index 7afc2e3..bd4b7c0 100644 --- a/src/i18n/zh-CN.ts +++ b/src/i18n/zh-CN.ts @@ -438,6 +438,20 @@ export const zhCN: TranslationSchema = { "github-light": "GitHub 浅色", "high-contrast": "高对比度(无障碍)", }, + presetDescriptions: { + auto: { + headline: "困难轮次从 flash 升级到 pro", + cost: "默认 · 大多数轮次使用 flash · 需要时才启用 pro", + }, + flash: { + headline: "始终使用 v4-flash", + cost: "最便宜 · 可预测 · 仍可用 /pro 临时提升一轮", + }, + pro: { + headline: "始终使用 v4-pro", + cost: "约 3 倍 flash(5/31 折扣)/ 原价约 12 倍 · 适合困难的多轮工作", + }, + }, reviewLabelTheme: "主题", presetTitle: "选择预设", mcpTitle: "Carbon Code 要为你接入哪些 MCP 服务器?", diff --git a/tests/wizard.test.tsx b/tests/wizard.test.tsx index 552e2b4..1b2ff16 100644 --- a/tests/wizard.test.tsx +++ b/tests/wizard.test.tsx @@ -3,7 +3,7 @@ import { render } from "ink-testing-library"; import React from "react"; import { afterEach, describe, expect, it } from "vitest"; -import { Wizard, buildSpec, validateDeepSeekApiKey } from "../src/cli/ui/Wizard.js"; +import { Wizard, buildSpec, presetItems, validateDeepSeekApiKey } from "../src/cli/ui/Wizard.js"; import { setLanguageRuntime } from "../src/i18n/index.js"; import { parseMcpSpec } from "../src/mcp/spec.js"; @@ -66,6 +66,24 @@ describe("Wizard — first-launch language picker", () => { }); }); +describe("Wizard — localized preset descriptions", () => { + afterEach(() => { + setLanguageRuntime("EN"); + }); + + it("shows preset descriptions in zh-CN when runtime language is Simplified Chinese", () => { + setLanguageRuntime("zh-CN"); + const items = presetItems(); + + expect(items.map((item) => item.label)).toEqual([ + "auto — 困难轮次从 flash 升级到 pro", + "flash — 始终使用 v4-flash", + "pro — 始终使用 v4-pro", + ]); + expect(items.map((item) => item.hint).join("\n")).not.toContain("hard turns"); + }); +}); + describe("Wizard API-key validation", () => { it("accepts a key when DeepSeek auth check succeeds", async () => { const fetcher = async () => new Response(JSON.stringify({ data: [] }), { status: 200 });