use std::borrow::Cow;
use super::messages::Msg;
pub(super) fn zh_cn(msg: Msg<'_>) -> Cow<'static, str> {
match msg {
Msg::WelcomeBannerLine1 =>
"欢迎使用 AtomCode,请选择一项开始:".into(),
Msg::WelcomeBannerLine2 =>
"(↑↓ 切换,Enter 确认,Esc 跳过)".into(),
Msg::WelcomeOptionCodingPlan => "配置 CodingPlan".into(),
Msg::WelcomeOptionCodingPlanHint => "免费额度 · 推荐".into(),
Msg::WelcomeOptionConfigureManually => "手动配置".into(),
Msg::WelcomeOptionConfigureManuallyHint => "使用 API key".into(),
Msg::WelcomeOptionSkip => "暂时跳过".into(),
Msg::WelcomeOptionSkipHint => "稍后再说".into(),
Msg::CodingPlanSetupFailed { error } =>
format!("CodingPlan 设置失败:{error}").into(),
Msg::CpReauthAfter401 =>
" ⚠ 登录凭证已失效 — 正在重新登录...\n".into(),
Msg::ChatAuthExpired =>
"认证已过期,请执行 /login 重新登录".into(),
Msg::CpSetupHeader =>
" AtomCode CodingPlan 配置:\n\n".into(),
Msg::CpLoggedIn { who, username, email } =>
format!(" ✓ 已登录:{} ({},{})\n", who, username, email).into(),
Msg::CpStepSkipped { reason } =>
format!(" ✓ {}\n", reason).into(),
Msg::CpLoginFailed { error } =>
format!(" ✗ 登录失败 — {}\n", error).into(),
Msg::CpClaimed { message, plan_type } =>
format!(" ✓ CodingPlan 已领取 — {}(CodingPlan {})\n", message, plan_type).into(),
Msg::CpClaimSuccessFallback => "成功".into(),
Msg::CpAlreadyClaimed { reason } =>
format!(" ✓ CodingPlan 已领取 — {}\n", reason).into(),
Msg::CpClaimFailed { error } =>
format!(" ✗ CodingPlan 领取失败 — {}\n", error).into(),
Msg::CpClaimTierSucceeded { tier } =>
format!(" ✓ CodingPlan {} 领取成功\n", tier).into(),
Msg::CpClaimTierAlreadyHeld { tier } =>
format!(" ✓ CodingPlan {} 已领取\n", tier).into(),
Msg::CpClaimTierFailed { tier, reason } =>
format!(" ✗ CodingPlan {} 领取失败 — {}\n", tier, reason).into(),
Msg::CpAddedProviders { count, plural_s: _ } =>
format!(" ✓ 已添加 {} 个 Provider:\n", count).into(),
Msg::CpLocked { name } =>
format!(" \x1b[31m✗ {} (需要升级成 Pro 以上套餐)\x1b[39m\n", name).into(),
Msg::CpProviderRow { provider, model, default_suffix } =>
format!(" • {} → {}{}\n", provider, model, default_suffix).into(),
Msg::CpDefaultSuffix => " (默认)".into(),
Msg::CpVisionAuto { kind } =>
format!(" ✓ 视觉预处理器 → {} (自动检测)\n", kind).into(),
Msg::CpVisionUserSupplied { kind } =>
format!(" ✓ 视觉预处理器 → {} (保留用户设置)\n", kind).into(),
Msg::CpVisionCleared =>
" ⚠ 视觉预处理器已清除 — 当前模型列表中没有可用的 VL/OCR 模型\n".into(),
Msg::CpModelsSkipped { reason } =>
format!(" ✓ 模型步骤已跳过 — {}\n", reason).into(),
Msg::CpModelsFailed { error } =>
format!(" ✗ 模型步骤失败 — {}\n", error).into(),
Msg::CpStatusHeader =>
" ✓ CodingPlan 状态:\n".into(),
Msg::CpPlanPending { plan } =>
format!(" 套餐:{} · 正在激活\n", plan).into(),
Msg::CpPlanActive { plan, expires_at, remaining_days, total_days } =>
format!(
" 套餐:{} · 到期时间 {}(剩余 {}d / 共 {}d)\n",
plan, expires_at, remaining_days, total_days,
).into(),
Msg::CpUsageLine { usage, reset_at, duration } =>
format!(" 用量:{} · 重置于 {}({} 后)\n", usage, reset_at, duration).into(),
Msg::CpWindowQuotaExhausted =>
" ⚠ 当前窗口配额已耗尽\n".into(),
Msg::CpWindowQuotaHint { hint } =>
format!(" ⚠ {}\n", hint).into(),
Msg::CpStatusFetchSkipped { reason } =>
format!(" ⚠ 状态获取已跳过 — {}\n", reason).into(),
Msg::CpStatusFetchFailed { error } =>
format!(" ⚠ 状态获取失败(非致命) — {}\n", error).into(),
Msg::CpOfficialBuildRequired => Cow::Borrowed(
"此功能需要官方 AtomCode 构建,请前往 \
https://atomgit.com/atomgit_atomcode/atomcode/releases 下载安装。",
),
Msg::CpAuthRequired => Cow::Borrowed(
"未登录 AtomCode CodingPlan。请运行 /codingplan 完成登录后再发送请求。",
),
Msg::CpSignStaleClockSkew => Cow::Borrowed(
"请求被服务端拒绝:签名时间戳已过期。请校准本地系统时间(NTP 同步)后重试。",
),
Msg::CpSignReplayPersisted => Cow::Borrowed(
"请求多次被识别为重放,请重新运行命令。",
),
Msg::CpSignVersionTooOld => Cow::Borrowed(
"当前 AtomCode 版本过旧,已不兼容 CodingPlan。请升级 AtomCode 后继续使用。",
),
Msg::CpUpgradeRequired => Cow::Borrowed(
"需要升级才能继续使用 CodingPlan,请前往官方发布页安装最新版 AtomCode。",
),
Msg::ErrUnsupportedLocale { input } =>
format!("不支持的语言:{input}").into(),
Msg::StatusNoProvider =>
"未配置 Provider · 使用 /provider 配置".into(),
Msg::StatusUpgradeHint { version } =>
format!("↑ {version} 可用 · 使用 /upgrade 升级").into(),
Msg::StatusModelNotConfigured =>
"(未配置)".into(),
Msg::StatusClipboardImageHint =>
"剪贴板有图片 · ctrl+v 粘贴".into(),
Msg::StatusClipboardImageHintSlash =>
"剪贴板有图片 · /paste 粘贴".into(),
Msg::StatusBody { model, dir, config, tokens } =>
format!(
" 模型: {}\n 目录: {}\n 配置文件:{}\n Token: {}\n",
model, dir, config, tokens,
).into(),
Msg::StatusCpNotSignedIn =>
" CodingPlan:(未登录 — 运行 /codingplan 进行配置)\n".into(),
Msg::StatusCpFetchFailed { error } =>
format!(" CodingPlan:(状态获取失败 — {})\n", error).into(),
Msg::StatusCpNoActive =>
" CodingPlan:(无激活套餐 — 运行 /codingplan)\n".into(),
Msg::StatusCpLine { plan, expires_at, remaining_days, total_days } =>
format!(
" CodingPlan:{} · 到期 {}({}d / 共 {}d)\n",
plan, expires_at, remaining_days, total_days,
).into(),
Msg::StatusCpUsage { usage, reset_at, seconds } =>
format!(" 用量:{} · 重置于 {}({}s 后)\n", usage, reset_at, seconds).into(),
Msg::StatusCpWindowExhausted =>
" ⚠ 当前窗口配额已耗尽\n".into(),
Msg::StatusCpWindowHint { hint } =>
format!(" ⚠ {}\n", hint).into(),
Msg::StatusInstructionFilesHeader =>
" 指令文件:\n".into(),
Msg::StatusInstructionPresent { path, label } =>
format!(" ✓ {} ({})\n", path, label).into(),
Msg::StatusInstructionMissing { label } =>
format!(" ✗ {} — 未找到\n", label).into(),
Msg::LoginSignedInWithCpHint { name, username } =>
format!(
" 已登录:{}({})。现在可以开始对话;运行 /codingplan 同步最新的模型权限。\n",
name, username,
).into(),
Msg::HelpAvailableCommands =>
" 可用命令:\n".into(),
Msg::KeybindingsHelp => r#" 键盘快捷键
── 输入 ──
Enter 发送消息
Ctrl+J 插入换行(所有终端通用)
\ 后接 Enter 插入换行(atomcode 兜底,所有终端通用)
Alt+Enter 插入换行 *
Shift+Enter 插入换行 **
/ 打开斜杠命令菜单
Tab 自动补全
Backspace / Ctrl+H 删除上一个字符
Delete / Ctrl+? 删除下一个字符
Ctrl+W 删除前一个单词
Ctrl+U 清空当前行
Ctrl+K 删除到行尾
Ctrl+A / Home 跳到行首
Ctrl+E / End 跳到行尾
Left / Right 光标左右移动
── 历史 ──
Up 上一条输入
Down 下一条输入
── 会话 ──
Ctrl+C 取消当前轮 / 关闭弹层
Ctrl+D 退出 atomcode
Ctrl+L 清屏
Ctrl+O 切换工具实时输出
Ctrl+V 粘贴(文本 + 图片)
── 斜杠菜单 / 弹层导航 ──
Up / Down 移动选择
Enter 确认
Esc 取消 / 关闭弹层
Tab 插入当前高亮命令
* Alt+Enter 在多数终端可用;macOS Apple Terminal 需在
Settings → Profiles → Keyboard 启用 "Use Option as Meta key"
才会发送换行。
** Shift+Enter 需要终端区分该按键,目前已知支持的有:
Kitty / WezTerm / iTerm2(启用 Report Modifiers)/
Windows Terminal / Ghostty / Warp。其他终端(包括 macOS
Apple Terminal、默认 xterm、GNOME Terminal、VS Code 集成
终端)不区分 Shift+Enter 与 Enter,请用 Ctrl+J 或 \ + Enter。
提示:输入 /help 查看完整斜杠命令列表。
"#.into(),
Msg::ProviderWizardHeader =>
" Provider 管理 — 添加 / 编辑 / 删除 / 设为默认。按 Esc 取消。\n".into(),
Msg::ProviderWizardCancelled =>
"(已取消)".into(),
Msg::ProviderMenuAdd => "添加".into(),
Msg::ProviderMenuAddDesc => "添加新 Provider".into(),
Msg::ProviderMenuEdit => "编辑".into(),
Msg::ProviderMenuEditDesc => "编辑已有 Provider".into(),
Msg::ProviderMenuDelete => "删除".into(),
Msg::ProviderMenuDeleteDesc => "移除 Provider".into(),
Msg::ProviderMenuSetDefault => "设为默认".into(),
Msg::ProviderMenuSetDefaultDesc => "切换默认 Provider".into(),
Msg::ProviderNoProviders =>
"尚未配置任何 Provider。".into(),
Msg::ProviderDeleteConfirm { name } =>
format!("删除 \"{name}\"?[y/N]").into(),
Msg::ProviderDeleted { name } =>
format!("已移除 \"{name}\"。").into(),
Msg::ProviderDeleteKept => "(已保留)".into(),
Msg::ProviderDefaultSet { name } =>
format!("默认已设为 {name}。").into(),
Msg::ProviderAdded { name, model } =>
format!("已添加 Provider \"{name}\",并切换到 {name} · {model}。").into(),
Msg::ProviderUpdated { name } =>
format!("已更新 \"{name}\"。").into(),
Msg::ProviderStepName => "Provider 名称?".into(),
Msg::ProviderStepType => "类型?(openai / claude / ollama)".into(),
Msg::ProviderStepTypeWithHint { current } =>
format!("类型?[{current}](openai / claude / ollama,留空保持不变)").into(),
Msg::ProviderStepBaseUrl =>
"Base URL?(留空使用默认值)".into(),
Msg::ProviderStepBaseUrlWithHint { current } =>
format!("Base URL?[{current}](留空保持不变)").into(),
Msg::ProviderDefaultHint => "Provider 默认值".into(),
Msg::ProviderStepApiKey =>
"API 密钥?(留空不设置)".into(),
Msg::ProviderStepApiKeyWithHint { hint } =>
format!("API 密钥?[{hint}]").into(),
Msg::ProviderStepApiKeySet => "已设置 — 留空保持不变".into(),
Msg::ProviderStepApiKeyUnset => "未设置".into(),
Msg::ProviderStepModel => "模型?".into(),
Msg::ProviderStepModelWithHint { current } =>
format!("模型?[{current}](留空保持不变)").into(),
Msg::ProviderNameEmpty => "名称不能为空。".into(),
Msg::ProviderUnknownType =>
"未知类型。请选择 openai / claude / ollama。".into(),
Msg::ProviderUnknownTypeEdit =>
"未知类型。请选择 openai / claude / ollama 或留空。".into(),
Msg::ProviderModelEmpty => "模型不能为空。".into(),
Msg::ProviderEditKeep => "(保持不变)".into(),
Msg::ModelSwitched { provider, model } =>
format!(" 已切换到 {provider} · {model}\n").into(),
Msg::SessionLoadFailed { error } =>
format!("加载会话失败:{error}").into(),
Msg::SessionResumedLabel { name } =>
format!("已恢复:{name}").into(),
Msg::SessionTimeJustNow => "刚刚".into(),
Msg::SessionTimeMinAgo { n } => format!("{n}分钟前").into(),
Msg::SessionTimeHourAgo { n } => format!("{n}小时前").into(),
Msg::SessionTimeDayAgo { n } => format!("{n}天前").into(),
Msg::SessionMsgCount { count } =>
format!("{count} 条消息").into(),
Msg::SessionNameEmpty =>
"会话名不能为空".into(),
Msg::SessionNameTooLong { max } =>
format!("会话名过长(最多 {max} 个字符)").into(),
Msg::SessionNameControlChars =>
"会话名不能包含控制字符".into(),
Msg::SessionListFailed { error } =>
format!("列出会话失败:{error}").into(),
Msg::SessionRenamed { old, new } =>
format!(" 已重命名:'{old}' -> '{new}'").into(),
Msg::SessionSaveFailed { error } =>
format!("保存会话失败:{error}。未持久化新名称。").into(),
Msg::SessionNoneSelected =>
"未选中会话".into(),
Msg::SessionRenameEditing { buffer } =>
format!("> {buffer}_ [Enter: 确认, Esc: 取消]").into(),
Msg::DirCurrent => "当前".into(),
Msg::DirNotExists { path } =>
format!("目录已不存在:{path}").into(),
Msg::DirChanged { path } =>
format!(" 已切换到:{path}\n").into(),
Msg::DirNotADirectory { path } =>
format!("不是目录:{path}").into(),
Msg::IssueCancelled => "(已取消)".into(),
Msg::IssueNewOn { owner, repo } =>
format!("在 atomgit.com/{owner}/{repo} 创建 Issue").into(),
Msg::IssueStep1 =>
"步骤 1/2 — 输入标题(必填,按 Esc 取消):".into(),
Msg::IssueStep2 =>
"步骤 2/2 — 输入描述(Shift+Enter 换行,Enter 提交,Esc 取消):".into(),
Msg::IssueTitleConfirmed { title } =>
format!("✓ 标题:{title}").into(),
Msg::IssueCreated { number, title, url } =>
format!(" [issue] ✓ 已创建 #{number}:{title}\n {url}\n").into(),
Msg::IssueCreateFailed { error } =>
format!(" [issue] ✗ 创建失败:{error}\n").into(),
Msg::IssueRequiredField { field } =>
format!("(必填 — 请输入 {field},或按 Esc 取消)").into(),
Msg::LanguageSwitched { label, locale } =>
format!(" ✓ 已切换语言为 {label}({locale})。\n").into(),
Msg::IdleHintPrefix =>
"输入内容,或按 ".into(),
Msg::IdleHintSlash => "/".into(),
Msg::IdleHintSuffix =>
" 浏览命令".into(),
Msg::IdleHintFull =>
"输入内容,或按 / 浏览命令".into(),
Msg::IdleHintProvider => "/provider".into(),
Msg::IdleHintProviderSuffix =>
"添加自定义模型".into(),
Msg::IdleHintProviderFull =>
"使用 /provider 添加自定义模型".into(),
Msg::IdleHintCodingplan => "/codingplan".into(),
Msg::IdleHintCodingplanSuffix =>
"领取免费 Token 额度".into(),
Msg::IdleHintCodingplanFull =>
"使用 /codingplan 领取免费 Token 额度".into(),
Msg::CmdSwitchedPlanMode =>
" 已切换到 Plan 模式(只读探索)。\n".into(),
Msg::CmdSwitchedBuildMode =>
" 已切换到 Build 模式(完整执行)。\n".into(),
Msg::CmdNewSession =>
" 新会话已开始。\n".into(),
Msg::CmdNoProviders =>
" 未配置任何 Provider。\n".into(),
Msg::CmdNoSessions =>
" 未找到历史会话。请先开始一段对话。\n".into(),
Msg::CmdUnknownCommand { name } =>
format!("未知命令:/{name}").into(),
Msg::CmdLoginFailed { error } =>
format!("登录失败:{error}").into(),
Msg::CmdLogoutDone =>
" 已退出 AtomGit 登录。权限已刷新。\n".into(),
Msg::CmdLogoutFailed { error } =>
format!("退出登录失败:{error}").into(),
Msg::CmdWhoamiNotSignedIn =>
" 尚未登录。使用 /login 进行认证。\n".into(),
Msg::CmdReloadDone { provider, model } =>
format!(" 配置已重载。当前:{provider} · {model}\n").into(),
Msg::CmdReloadFailed { error } =>
format!("重载失败:{error}(保留先前配置)").into(),
Msg::CmdUndoNotSupported =>
" 撤销功能暂不支持。\n".into(),
Msg::CmdNoChanges =>
" (无变更)\n".into(),
Msg::CmdCheckingUpdate =>
" 正在检查更新...\n".into(),
Msg::CmdNoActiveProvider =>
"未配置活跃的 Provider。使用 /provider 添加一个。".into(),
Msg::ApprovalPromptAlt { tool, detail } =>
format!("允许 {}({})?[Y]是 / [N]否 / [A]总是", tool, detail).into(),
Msg::ApprovalWaitingLabel =>
"▶ 等待审批:".into(),
Msg::ApprovalAllow => " 允许 ".into(),
Msg::ApprovalAlways => " 总是 ".into(),
Msg::ApprovalDeny => " 拒绝".into(),
Msg::Cancelled => "(已取消)".into(),
Msg::ErrorPrefix { msg } =>
format!("[错误:{msg}]").into(),
Msg::UpgradeSuccess { from, to } =>
format!(" ✓ 已升级 {} → {}\n", from, to).into(),
Msg::UpgradeManifestFetched { version } =>
format!(" 最新版本: {}\n", version).into(),
Msg::UpgradeDownloading { pct, bytes, total } =>
format!(" 下载中 {}% ({} / {} bytes)\n", pct, bytes, total).into(),
Msg::UpgradeVerifying =>
" 正在校验 SHA256\n".into(),
Msg::UpgradeReplacing =>
" 正在替换二进制文件\n".into(),
Msg::UpgradeDone { version, backup } =>
format!("\n✓ 已升级到 {}(旧版本保留为 {})\n 正在重启新版本...\n", version, backup).into(),
Msg::UpgradeAlreadyLatest { current, latest } =>
format!(
" ✓ 已是最新版本,无需更新(当前 {},远端最新 {})。如需重装请加 --force。\n",
current, latest
).into(),
Msg::UpgradeFailed { error } =>
format!("升级失败: {}", error).into(),
Msg::UpgradeRolledBack { exe, backup } =>
format!("\n✓ 已回滚。当前二进制: {};另一版本保存在 {}\n 正在重启回滚版本...\n", exe, backup).into(),
Msg::KbdHintMacos =>
" ⚠ 终端不支持增强键盘协议。\n 请使用 Ctrl+Enter 插入换行(Shift+Enter 不可用)。\n\n".into(),
Msg::KbdHintOther =>
" ⚠ 终端不支持增强键盘协议。\n 请使用 Alt+Enter 或 Ctrl+Enter 插入换行(Shift+Enter 不可用)。\n\n".into(),
Msg::JediTermFallback =>
" ⓘ 检测到 JetBrains IDE 终端 — 运行在备用屏幕模式下。\n \
使用鼠标滚轮、PageUp/PageDown 或 Shift+Up/Down 滚动历史。\n \
AtomCode 运行期间无法使用宿主终端的原生回滚;\n \
退出后宿主终端将恢复到 AtomCode 之前的状态。\n \
设置 ATOMCODE_PLAIN=1 使用基础 CI 风格输出,或\n \
设置 ATOMCODE_RETAIN=1 绕过此回退(可能导致对齐问题)。\n\n".into(),
Msg::LegacyConhostFallback =>
" ⓘ 检测到旧版 Windows 控制台 — 运行在备用屏幕模式下。\n \
使用鼠标滚轮、PageUp/PageDown 或 Shift+Up/Down 滚动历史。\n \
AtomCode 运行期间无法使用宿主终端的原生回滚。\n \
要获得完整的宿主终端回滚支持,请安装 Windows Terminal\n \
(免费,Microsoft Store)、ConEmu、Alacritty 或 WezTerm。\n \
设置 ATOMCODE_PLAIN=1 使用基础输出,或设置 ATOMCODE_RETAIN=1\n \
绕过此回退(滚动时可能出现重复内容)。\n\n".into(),
Msg::BackgroundComplete { turns } =>
format!(" 后台任务完成({} 轮):\n", turns).into(),
Msg::BackgroundFailed { turns } =>
format!(" 后台任务失败,共 {} 轮:\n", turns).into(),
Msg::BackgroundFilesEdited =>
" 已编辑的文件:\n".into(),
Msg::ConfigProviderLabel { provider, path } =>
format!(" Provider:{}\n 配置文件:{}\n\n", provider, path).into(),
Msg::CostReport { prompt, completion, cached, cache_rate, total, cost } =>
format!(
" 提示 Token: {}\n 补全 Token: {}\n 缓存 Token: {}({}% 命中率)\n Token 总计: {}\n 预估费用: {}\n",
prompt, completion, cached, cache_rate, total, cost
).into(),
Msg::ThinkStatus { status, budget, provider } =>
format!(
" 深度思考:{}\n 预算:{} Token\n Provider:{}\n\n 用法:/think on | off | budget <N>\n",
status, budget, provider
).into(),
Msg::ThinkEnabled { budget } =>
format!(" 深度思考已启用(预算:{} Token)。\n", budget).into(),
Msg::ThinkDisabled =>
" 深度思考已禁用。\n".into(),
Msg::ThinkBudgetSet { n } =>
format!(" 思考预算已设为 {} Token。\n", n).into(),
Msg::ThinkBudgetTooSmall { n } =>
format!("预算必须 >= 1024(当前 {})", n).into(),
Msg::ThinkBudgetUsage =>
"用法:/think budget <数字>".into(),
Msg::ThinkUsage =>
" 用法:/think [on | off | budget <N>]\n".into(),
Msg::RememberUsage =>
"用法:/remember <要记住的内容>(--global 为全局范围)".into(),
Msg::ForgetUsage =>
"用法:/forget <关键词>".into(),
Msg::BackgroundUsage =>
" 用法:/background <任务描述>\n".into(),
Msg::InitAlreadyExists { path } =>
format!(" {} 已存在。使用 `/init --force` 覆盖。\n", path).into(),
Msg::InitWrote { path, bytes } =>
format!(" 已写入 {}({} 字节)。编辑以自定义;下一条消息生效。\n", path, bytes).into(),
Msg::InitFailed { error } =>
format!(" /init 失败:{}\n", error).into(),
Msg::CdWorkingDir { cwd } =>
format!(" 工作目录:{}\n 无最近项目。使用 `/cd <路径>` 切换。\n", cwd).into(),
Msg::DiffFailed { error } =>
format!("git diff 失败:{}", error).into(),
Msg::UpgradeUnknownArg { arg } =>
format!("未知的 /upgrade 参数:{}\n 用法:/upgrade [rollback|--force]", arg).into(),
Msg::SkillsNone =>
" 没有可调用的技能。\n".into(),
Msg::SkillsAvailable =>
" 可用技能:\n".into(),
Msg::SkillUnknown { name } =>
format!("未知技能:{}(输入 /skills 查看列表)", name).into(),
Msg::McpReloading { count } =>
format!(" 正在重载 MCP 服务器...({} 个已配置)\n", count).into(),
Msg::McpConnecting =>
" 正在连接:\n".into(),
Msg::McpConnectingServer { name } =>
format!(" - {} 连接中...\n", name).into(),
Msg::McpNoServersConfigured =>
" 未配置 MCP 服务器。\n".into(),
Msg::McpClearedReconnecting { removed } =>
format!(" ✓ 已清除 {} 个 MCP 工具。正在后台重新连接...\n", removed).into(),
Msg::McpClearedNoServers { removed } =>
format!(" ✓ 已清除 {} 个 MCP 工具。无需连接。\n", removed).into(),
Msg::McpToolsUsage =>
" 用法:/mcp tools <服务器名>\n 示例:/mcp tools filesystem\n".into(),
Msg::McpToolsListing { server } =>
format!(" 正在列出 '{}' 的 MCP 工具...\n", server).into(),
Msg::McpNoRegistry =>
" MCP 注册表未加载。请先运行 /mcp reload。\n".into(),
Msg::McpServersHeader =>
" MCP 服务器:\n".into(),
Msg::McpReloadFailed { error } =>
format!("MCP 重载失败:无法加载 .mcp.json / $ATOMCODE_HOME/mcp.json:{:#}", error).into(),
Msg::McpOAuthLoginUsage =>
" 用法:/mcp login <服务名>\n 示例:/mcp login github\n".into(),
Msg::McpOAuthLogoutUsage =>
" 用法:/mcp logout <服务名>\n 示例:/mcp logout github\n".into(),
Msg::McpOAuthLoadConfigFailed { error } =>
format!(" MCP OAuth 登录失败:无法加载配置:{error}\n").into(),
Msg::McpOAuthServerNotFound { server } =>
format!(" MCP OAuth 登录失败:配置中未找到服务 '{server}'。\n").into(),
Msg::McpOAuthStarting { server } =>
format!(" 正在浏览器中启动 '{server}' 的 MCP OAuth 流程...\n").into(),
Msg::McpOAuthSaved { provider, server } =>
format!(" 已保存 MCP 服务 '{server}' 的 {provider} OAuth Token。运行 /mcp reload 完成连接。\n").into(),
Msg::McpOAuthFailed { error } =>
format!(" MCP OAuth 失败:{error}\n").into(),
Msg::McpOAuthTokenRemoved { server } =>
format!(" 已移除 MCP 服务 '{server}' 保存的 OAuth Token。\n").into(),
Msg::McpOAuthNoToken { server } =>
format!(" 未找到 MCP 服务 '{server}' 保存的 OAuth Token。\n").into(),
Msg::McpOAuthLogoutFailed { error } =>
format!(" MCP OAuth 登出失败:{error}\n").into(),
Msg::McpServerConnected { name } =>
format!("✓ MCP 服务 '{name}' 已连接").into(),
Msg::McpServerFailed { name, error } =>
format!("✗ MCP 服务 '{name}' 失败:{error}").into(),
Msg::LspServerStarted { name, ext } =>
format!("✓ LSP 服务 '{name}' 已为 .{ext} 启动").into(),
Msg::LspServerFailed { name, ext, error } =>
format!("✗ LSP 服务 '{name}'(.{ext})失败:{error}").into(),
Msg::WorktreeUsage =>
" 用法:\n /worktree create <分支> [基准] 创建工作树并切换\n /worktree list 列出所有工作树\n /worktree done 切回原始目录\n /worktree cleanup <分支> 清理工作树\n".into(),
Msg::WorktreeCreateUsage =>
" 用法:/worktree create <分支> [基准]\n 示例:/worktree create fix-bug main\n".into(),
Msg::WorktreeCreated { branch, base, path } =>
format!(" ✓ 工作树已创建\n 分支:{}(基于 {})\n 路径:{}\n 工作目录已切换\n", branch, base, path).into(),
Msg::WorktreeCreateFailed { error } =>
format!("工作树创建失败:{}", error).into(),
Msg::WorktreeNoActive =>
" 没有活跃的工作树。\n".into(),
Msg::WorktreeListFailed { error } =>
format!("工作树列表失败:{}", error).into(),
Msg::WorktreeActiveHeader =>
" 活跃工作树:\n".into(),
Msg::WorktreeHasChanges => "(有变更)".into(),
Msg::WorktreeClean => "(无变更)".into(),
Msg::WorktreeCurrent => " ← 当前".into(),
Msg::WorktreeDoneBack { path } =>
format!(" ✓ 工作目录已切回:{}\n", path).into(),
Msg::WorktreeDoneMergeHint { branch } =>
format!(" 提示:使用 'git merge {}' 或创建 PR 合入主分支\n", branch).into(),
Msg::WorktreeNoSession =>
" 没有活跃的工作树会话。先使用 /worktree create 创建一个。\n".into(),
Msg::WorktreeCleanupUsage =>
" 用法:/worktree cleanup <分支> [--force]\n".into(),
Msg::WorktreeCleaned { branch } =>
format!(" ✓ 工作树 '{}' 已清理\n", branch).into(),
Msg::WorktreeCleanedSwitched { path } =>
format!(" 工作目录已切回:{}\n", path).into(),
Msg::WorktreeCleanupUncommitted { branch } =>
format!(" ⚠ 工作树 '{}' 有未提交的变更。\n 使用 /worktree cleanup {} --force 强制清理\n", branch, branch).into(),
Msg::WorktreeCleanupFailed { error } =>
format!("工作树清理失败:{}", error).into(),
Msg::HelpCustomCommandsHeader =>
" 自定义命令:\n".into(),
Msg::HelpCustomNone =>
" (无)\n\n".into(),
Msg::HelpCustomCreateHint =>
" 创建方式:~/.atomcode/commands/<名称>.md 或 .atomcode/commands/<名称>.md\n".into(),
Msg::HelpSourceGlobal => "全局".into(),
Msg::HelpSourceProject => "项目".into(),
Msg::SetupHeader { installed, skipped, failed, duration_ms } =>
format!("\n✅ Setup 完成 — {} 装好, {} 跳过, {} 失败 · 耗时 {}ms\n\n", installed, skipped, failed, duration_ms).into(),
Msg::SetupInstalledLabel =>
"已安装:\n".into(),
Msg::SetupSkippedLabel =>
"\n跳过:\n".into(),
Msg::SetupFailedLabel =>
"\n失败:\n".into(),
Msg::SetupInstalledRow { kind, slug, path } =>
format!(" ✓ {}:{} → {}\n", kind, slug, path).into(),
Msg::SetupSkippedRow { kind, slug, reason } =>
format!(" - {}:{} ({:?})\n", kind, slug, reason).into(),
Msg::SetupFailedRow { kind, slug, error } =>
format!(" ✗ {}:{} — {}\n", kind, slug, error).into(),
Msg::CmdSetupTip =>
"\u{1f4a1} 提示:运行 \x1b[1;96m/setup\x1b[0m 可自动为该项目配置 hooks、skills 和 MCP。".into(),
Msg::CmdSetupRunning =>
"正在运行 atomcode setup...".into(),
Msg::CmdSetupSkillsReloaded { count } =>
format!(" 🔄 Skills 已重载 — {} 个可用", count).into(),
Msg::CmdSetupError { error } =>
format!("setup 错误:{error}").into(),
Msg::CmdSetupRunningSkill =>
" 🚀 正在运行 setup skill — 分析项目并生成推荐...".into(),
Msg::CmdSetupSkillMissing =>
"setup skill 未找到 — 请重新运行 /setup 以重新安装".into(),
Msg::PluginUsage =>
"用法:/plugin [marketplace add|remove|update|list | install <p>@<m> | uninstall <p>@<m> | list]".into(),
Msg::PluginMarketplaceUsage =>
"用法:/plugin marketplace [add|remove|update|list] <参数>".into(),
Msg::PluginInstallUsage =>
"用法:/plugin install <插件>@<市场>".into(),
Msg::PluginUninstallUsage =>
"用法:/plugin uninstall <插件>@<市场>".into(),
Msg::PluginNoMarketplaces =>
"未注册任何市场".into(),
Msg::PluginMarketplacesHeader =>
"已注册的市场:".into(),
Msg::PluginNoInstalled =>
"未安装任何插件".into(),
Msg::PluginInstalledHeader =>
"已安装的插件:".into(),
Msg::PluginMarketplaceCloning { url } =>
format!("正在从 {url} 克隆 marketplace…").into(),
Msg::PluginMarketplaceRemoved { name } =>
format!("已移除 marketplace `{name}`").into(),
Msg::PluginMarketplaceRemoveFailed { error } =>
format!("移除 marketplace 失败:{error}").into(),
Msg::PluginMarketplaceUpdating { name } =>
format!("正在更新 marketplace `{name}`…").into(),
Msg::PluginMarketplaceListFailed { error } =>
format!("列出 marketplace 失败:{error}").into(),
Msg::PluginInstalling { plugin, marketplace } =>
format!("正在安装 `{plugin}@{marketplace}`…").into(),
Msg::PluginUninstalled { plugin, marketplace } =>
format!("已卸载 `{plugin}@{marketplace}`").into(),
Msg::PluginUninstallFailed { error } =>
format!("卸载失败:{error}").into(),
Msg::PluginListFailed { error } =>
format!("列出插件失败:{error}").into(),
Msg::CmdDescSetup =>
"扫描项目、安装种子文件并运行 setup skill [hooks|mcp|skills|all]".into(),
Msg::CmdDescCodingplan =>
"领取 CodingPlan 并从计划的模型列表中配置模型".into(),
Msg::CmdDescResume => "恢复上次会话".into(),
Msg::CmdDescRename => "重命名当前会话".into(),
Msg::CmdDescLogin => "使用 AtomGit OAuth 登录".into(),
Msg::CmdDescLogout => "退出 AtomGit 登录".into(),
Msg::CmdDescWhoami => "显示当前登录用户".into(),
Msg::CmdDescModel => "切换 Provider / 模型".into(),
Msg::CmdDescProvider => "管理 Provider(添加 / 编辑 / 删除)".into(),
Msg::CmdDescStatus => "显示会话状态".into(),
Msg::CmdDescConfig => "显示配置文件路径".into(),
Msg::CmdDescReload => "从磁盘重新加载 $ATOMCODE_HOME/config.toml".into(),
Msg::CmdDescCd => "切换工作目录".into(),
Msg::CmdDescInit => "从工作目录生成 .atomcode.md 项目指令".into(),
Msg::CmdDescBg => "后台会话:/bg、/bg list、/bg <N>、/bg drop <N>".into(),
Msg::CmdDescBackground => "在隔离的后台上下文中运行一次性任务(只读工具子集)".into(),
Msg::CmdDescDiff => "显示 git diff".into(),
Msg::CmdDescClear => "清屏".into(),
Msg::CmdDescSession => "开始新会话(清除对话)".into(),
Msg::CmdDescCost => "显示 Token 费用".into(),
Msg::CmdDescContext => "显示上下文预算明细".into(),
Msg::CmdDescCompact => "压缩对话历史".into(),
Msg::CmdDescRemember => "保存记忆(/remember --global 为全局)".into(),
Msg::CmdDescForget => "删除匹配的记忆".into(),
Msg::CmdDescMemory => "显示所有已保存的记忆".into(),
Msg::CmdDescMcp => "显示 MCP 服务器状态(子命令:reload)".into(),
Msg::CmdDescUndo => "撤销上次变更(暂不支持)".into(),
Msg::CmdDescWorktree => "Git 工作树隔离(create/list/done/cleanup)".into(),
Msg::CmdDescUpgrade => "升级到最新版本(子命令:rollback)".into(),
Msg::CmdDescIssue => "为 AtomCode 报告 Bug / 提出功能建议(交互式向导)".into(),
Msg::CmdDescPlan => "切换到 Plan 模式(只读探索)".into(),
Msg::CmdDescBuild => "切换到 Build 模式(完整执行)".into(),
Msg::CmdDescThink => "深度思考控制(on/off/budget N)".into(),
Msg::CmdDescHelp => "显示帮助".into(),
Msg::CmdDescKeys => "显示键盘快捷键".into(),
Msg::CmdDescLanguage => "切换显示语言".into(),
Msg::CmdDescQuit => "退出 AtomCode".into(),
Msg::CmdDescSkills => "浏览已加载的技能".into(),
Msg::CmdDescPlugin => "插件市场(子命令:marketplace, install, uninstall, list)".into(),
Msg::CmdDescPaste => "从剪贴板粘贴图片(Windows 下 Ctrl+V 被终端拦截时的备用入口)".into(),
Msg::CmdPasteNoImage => "剪贴板中没有图片。".into(),
Msg::ConfigSaveFailed { error } =>
format!("配置保存失败:{}", error).into(),
Msg::OnboardingStepHeaderWelcome => "第 1/3 步 · 欢迎".into(),
Msg::OnboardingStepHeaderLanguage => "第 2/3 步 · 语言".into(),
Msg::OnboardingStepHeaderSetup => "第 3/3 步 · 配置".into(),
Msg::OnboardingPanelTitle => "AtomCode".into(),
Msg::OnboardingIntroVersionLine { v } =>
format!("版本 {v} · 在终端里运行的 AI 编程代理").into(),
Msg::OnboardingIntroBullet1 =>
"• 多步骤 agent loop · 内置代码图工具".into(),
Msg::OnboardingIntroBullet2 =>
"• 兼容所有 OpenAI 风格 API".into(),
Msg::OnboardingIntroBullet3 =>
"• 通过 CodingPlan 获取免费额度".into(),
Msg::OnboardingIntroPressEnter => "按 Enter 继续。".into(),
Msg::OnboardingIntroCtrlC => "Ctrl+C 可随时退出。".into(),
Msg::OnboardingIntroCompactTagline =>
"在终端里运行的 AI 编程代理。".into(),
Msg::OnboardingLanguageTitleBilingual =>
"Choose your language / 选择语言".into(),
Msg::OnboardingLanguagePrompt =>
"选择界面语言。任何时候都可以用 `/language` 修改。".into(),
Msg::OnboardingLanguageOptionAuto =>
"自动检测 (LC_ALL / LANG)".into(),
Msg::OnboardingLanguageOptionEn => "English".into(),
Msg::OnboardingLanguageOptionZhCn => "简体中文 (Simplified Chinese)".into(),
Msg::OnboardingSetupTitle => "想怎么开始?".into(),
Msg::OnboardingNavHint =>
"1-3 选择 · Enter 确认 · ← 返回 · Esc 跳过".into(),
Msg::OnboardingConfirmClear =>
"/welcome 会清屏。是否继续?[y/N]".into(),
Msg::CmdWelcomeDescription => "重新运行 onboarding 向导".into(),
Msg::VisionPreprocessSuccess { char_count } =>
format!("✓ VL 识别图片成功,返回 {char_count} chars").into(),
Msg::TurnSummary { done, turn_count, tool_call_count, duration, total_tokens } =>
format!("✓ {done} · {turn_count} 轮 · {tool_call_count} 工具 · {duration} · {total_tokens} tokens").into(),
Msg::LoginQrHeader =>
" 登录 AtomGit — 使用微信扫描下方二维码:\n\n".into(),
Msg::LoginUrlAfterQr =>
"\n\n 或在浏览器打开下方链接:\n ".into(),
Msg::LoginNoQrNoUrl =>
" 当前终端无法渲染二维码,\n \
且该平台不支持基于 URL 的登录。\n \
请改用支持 Unicode 的终端以显示二维码。".into(),
Msg::LoginUrlOnly =>
" 在浏览器中打开此链接以登录 AtomGit:\n ".into(),
Msg::LoginCancelHint => "\n\n 按 ESC 取消\n".into(),
Msg::CtxUsageHeader => "上下文用量".into(),
Msg::CtxUsageNoTurns => "(请至少完成一轮对话 — 统计在每轮结束时记录)".into(),
Msg::CtxUsageWaiting => "(等待首轮完成 — 当前仅为部分统计)".into(),
Msg::CtxProvider => "Provider".into(),
Msg::CtxCtxName => "ctx".into(),
Msg::CtxLabelSystemPrompt => "系统提示".into(),
Msg::CtxLabelToolDefs => "工具定义".into(),
Msg::CtxLabelColdZone => "冷区".into(),
Msg::CtxLabelMessages => "消息".into(),
Msg::CtxLabelFree => "空闲".into(),
Msg::CtxMessagesInWindow { n } => format!("窗口内消息数:{n}").into(),
Msg::CtxSystemPromptHeader => "=== 系统提示 ===".into(),
Msg::CtxSystemPromptEmpty => "(为空 — 完成一轮对话后捕获)".into(),
Msg::CtxTokensSuffix => "tokens".into(),
Msg::CompactNothingShort => "(无需压缩 — 当前对话较短)\n".into(),
Msg::CompactStarting => "(正在使用 LLM 摘要进行压缩...)\n".into(),
Msg::CompactNothingNoSavings { before, after } =>
format!("(无需压缩 — 压缩后不会节省 token:{} → {})\n", before, after).into(),
Msg::CompactDropped { messages, before, after } =>
format!("(已压缩 — 丢弃 {} 条消息,{} → {} tokens)\n", messages, before, after).into(),
Msg::ModelNoImageSupport { model } => format!(
"当前模型 \"{}\" 不支持图片输入,且未配置 vision_preprocessor_provider。\
请用 /model 切换到支持视觉的模型,或在配置中设置 vision_preprocessor_provider。",
model
)
.into(),
Msg::CtrlCAgainToExit => " (再次按 Ctrl+C 退出)\n".into(),
Msg::HintMultiLineInput =>
" \u{24d8} 多行输入:在行尾加 `\\` 再按 Enter。\n \
所有终端均可用。(Shift / Alt / Ctrl + Enter 在部分终端也支持,\n \
取决于该终端的键盘协议 — 可以试试看。)\n\n"
.into(),
Msg::BgHelp =>
" /bg 将当前会话放到后台,打开新的前台会话\n /bg list 列出后台会话\n /bg <N> 恢复第 N 号后台会话\n /bg drop <N> 丢弃第 N 号后台会话\n /bg help 显示此帮助\n".into(),
Msg::BgListEmpty => " 没有后台会话。\n".into(),
Msg::BgListHeader => " # ID 状态 创建时间 摘要\n".into(),
Msg::BgListRow { slot, short_id, state, age, summary } =>
format!(" {:<3} {:<8} {:<9} {:<8} {}\n", slot, short_id, state, age, summary).into(),
Msg::BgStateRunning => "运行中".into(),
Msg::BgStateIdle => "空闲".into(),
Msg::BgStateDone => "已完成".into(),
Msg::BgStateCancelled => "已取消".into(),
Msg::BgStateError => "错误".into(),
Msg::BgAgeNow => "刚刚".into(),
Msg::BgAgeMinutes { n } => format!("{n} 分钟").into(),
Msg::BgAgeHours { n } => format!("{n} 小时").into(),
Msg::BgAgeDays { n } => format!("{n} 天").into(),
Msg::BgSlotLimitReached { max } =>
format!("后台槽位已达上限({max})").into(),
Msg::BgBackgroundCurrent { new_id, slot, old_id, state } =>
format!(" 新前台会话 [{new_id}]\n 后台:[#{slot}] {old_id}(状态:{state})\n").into(),
Msg::BgInvalidSlot { slot, available } =>
format!("无效的后台槽位 {slot}(可用:{available})").into(),
Msg::BgNoRuntimeClient => "后台槽位没有运行时客户端".into(),
Msg::BgResumed { slot, short_id } =>
format!(" 已恢复后台 [#{slot}] {short_id}\n").into(),
Msg::BgPreviousForegroundMoved { slot } =>
format!(" 原前台会话已移至 [#{slot}]\n").into(),
Msg::BgDropped { slot, short_id } =>
format!(" 已丢弃后台 [#{slot}] {short_id}\n").into(),
Msg::BgTaskStarted { slot, short_id } =>
format!(" 后台:[#{slot}] {short_id}(状态:运行中)\n").into(),
Msg::BgTaskTimedOut { secs } =>
format!("后台任务超时({secs} 秒)。").into(),
Msg::BgTaskError { error } =>
format!("错误:{error}").into(),
Msg::BgTaskCancelled => "已取消。".into(),
Msg::BgTaskNoSummary => "任务完成(无摘要文本)。".into(),
}
}
#[cfg(test)]
mod codingplan_crypto_tests {
use super::*;
use crate::i18n::Msg;
#[test]
fn zh_official_build_required_mentions_official_and_releases() {
let s = zh_cn(Msg::CpOfficialBuildRequired);
assert!(s.contains("官方"));
assert!(s.contains("releases") || s.contains("发布"));
}
#[test]
fn zh_stale_clock_mentions_time() {
let s = zh_cn(Msg::CpSignStaleClockSkew);
assert!(s.contains("时间") || s.contains("时钟"));
}
#[test]
fn zh_replay_persisted_is_non_empty() {
let s = zh_cn(Msg::CpSignReplayPersisted);
assert!(!s.is_empty());
}
#[test]
fn zh_version_too_old_mentions_upgrade() {
let s = zh_cn(Msg::CpSignVersionTooOld);
assert!(s.contains("升级") || s.contains("更新"));
}
#[test]
fn zh_upgrade_required_is_non_empty() {
let s = zh_cn(Msg::CpUpgradeRequired);
assert!(!s.is_empty());
}
}