/** * UI copy (bilingual): this file holds the Chinese dictionary `zh` and the runtime * active dictionary `S`; the English dictionary lives in strings-en.ts (constrained * to the same shape by the `Strings` type). Locale preference is resolved by * state/locale.tsx, which calls `setActiveStrings` to switch and remounts the whole * tree keyed by locale, so `S.x` reads in components always reflect the current * language (module-level constants do not update on switch — keep reads inside components). * Keep domain terms capitalized in English — Agent, Workspace, Token, Task, etc. */ export const zh = { appName: "PenguinHarness", nav: { chat: "对话", newChat: "新对话", agents: "智能体仓库", skills: "技能库", models: "模型仓库", usage: "成本中心", traces: "轨迹观测", benchmark: "评估中心", collapseSidebar: "收起侧栏", expandSidebar: "展开侧栏", collapseGroup: "折叠", expandGroup: "展开", }, settings: { language: "语言", theme: "主题", themeLight: "浅色", themeDark: "深色", followSystem: "跟随系统", langZh: "中文", langEn: "English", fontSize: "字号", fontSmall: "小", fontMedium: "中", fontLarge: "大", accent: "主题色", accentNames: { neutral: "灰白", blue: "蓝", green: "绿", violet: "紫", rose: "红", amber: "橙", } as Record, }, common: { save: "保存", cancel: "取消", create: "创建", delete: "删除", edit: "编辑", settings: "设置", confirm: "确认", close: "关闭", loading: "加载中…", loadMore: "加载更多", saved: "已保存", saving: "保存中…", none: "(无)", retry: "重试", unknownError: "请求失败,请稍后重试", requiredField: "此项必填", copied: "已复制", ownerOnly: "仅 owner 可修改", }, auth: { loginTitle: "登录 PenguinHarness", username: "用户名", usernameHint: "2~32 位:小写字母开头,仅小写字母、数字与下划线", password: "密码", passwordHint: "至少 8 个字符", showPassword: "显示密码", hidePassword: "隐藏密码", login: "登录", logout: "登出", admin: "管理员", defaultAdminNote: "首次使用请以内置管理员登录:admin / penguin-2026,登录后请尽快修改密码", }, account: { changePassword: "修改密码", oldPassword: "当前密码", oldPasswordHint: "内置管理员的默认初始密码为 penguin-2026", newPassword: "新密码", confirmPassword: "确认新密码", passwordMismatch: "两次输入的新密码不一致", initialPasswordBanner: "当前账号正在使用初始密码,建议尽快修改", changeNow: "去修改", }, admin: { users: "用户管理", userId: "用户名", role: "角色", roleAdmin: "管理员", roleUser: "用户", createdAt: "创建时间", actions: "操作", createUser: "新增用户", initialPassword: "初始密码", initialPasswordFlag: "初始密码", defaultProjectNote: (id: string): string => `将自动创建默认 Project:${id}`, resetPassword: "重置密码", resetPasswordTitle: (u: string): string => `重置 ${u} 的密码`, resetPasswordNote: "重置后该用户的登录会话全部失效,需用新密码重新登录", deleteUser: "删除", deleteUserTitle: (u: string): string => `删除用户 ${u}`, deleteUserConfirm: (u: string): string => `将删除用户 ${u} 及其名下全部 Project(含数据目录),不可恢复。`, }, project: { switcher: "Project", create: "新建 Project", createTitle: "新建 Project", id: "Project id", idHint: "2~64 位:小写字母开头,仅小写字母、数字与下划线;创建后不可修改", idPrefixHint: "id 固定以「用户名-」为前缀,后接小写字母、数字或下划线;创建后不可修改", name: "显示名(可选,缺省为 Project id)", settings: "Project 设置", settingsTitle: "Project 设置", members: "成员", addMember: "添加成员", memberUsername: "用户名", memberRole: "角色", memberActions: "操作", removeMember: "移除", owner: "owner", member: "member", deleteProject: "删除 Project", deleteConfirm: "确认删除该 Project?项目目录将被递归删除,不可恢复。", deleteDefaultForbidden: "default_project 与 CLI 共用,不允许在 Web 端删除", deleteLastForbidden: "这是当前账号最后一个 Project,删除后将无 Project 可用;请先创建新的 Project", noCredentialTitle: "尚未配置模型 credential", noCredentialBody: "当前 Project 的默认模型尚未配置 API key,发起对话前请先前往模型页配置。", goToModels: "前往模型页", later: "稍后再说", }, agent: { listTitle: "Agents", create: "创建 Agent", createTitle: "创建 Agent", id: "Agent id", idHint: "2~64 位:小写字母开头,仅小写字母、数字与下划线;创建后不可修改", name: "名称", nameHint: "留空则使用 Agent id 作为名称", description: "描述", activeSessions: "活跃 Session", sessionCount: (n: number): string => `${n} 个 Session`, toolCount: (n: number): string => `${n} 个工具`, vaultKeyCount: (n: number): string => `${n} 个密钥`, scheduleCount: (n: number): string => `${n} 个定时任务`, updatedAt: "最后修改", activity: (days: number): string => `近 ${days} 天 Session 活跃度`, settings: "Agent 设置", backToList: "返回 Agents", tabOverview: "概览", tabPrompt: "Prompt", tabRuntime: "运行参数", tabTools: "工具", tabVault: "密钥保险柜", tabSchedules: "定时任务", stateDir: "State 路径", agentsMd: "AGENTS.md", systemPrompt: "system_prompt 模板", placeholdersTitle: "可用占位符(点击插入)", insertPlaceholder: "插入到 system_prompt 光标处", /** Order must match the default system prompt (core default-config.ts DEFAULT_SYSTEM_PROMPT). */ placeholders: [ ["{{AGENTS_MD}}", "注入 AGENTS.md 内容"], ["{{VAULT_KEYS}}", "注入密钥保险柜的键名小节(无键时为空)"], ["{{PLATFORM}}", "运行平台"], ["{{OS_VERSION}}", "操作系统版本"], ["{{DATE}}", "当前日期"], ["{{CWD}}", "Workspace 绝对路径"], ["{{AGENT_ID}}", "当前 Agent id"], ["{{PROJECT_DIR}}", "Project 目录绝对路径(Agent State/scratchpad 由此拼出)"], ["{{SESSION_ID}}", "当前 Session id"], ] as ReadonlyArray, maxTurns: "max_turns(单 Task 最大轮次,-1 不限制)", maxTokens: "model.max_tokens", thinkingLevel: "model.thinking_level", thinkingLevelOptions: [ ["", "不提交覆盖值,沿用当前生效的配置。"], ["none", "关闭扩展推理,响应最快。"], ["low", "开启较低强度的扩展推理。"], ["medium", "开启中等强度的扩展推理(新建 Agent 的缺省档位)。"], ["high", "开启较高强度的扩展推理,响应更慢。"], ["xhigh", "开启最高强度的扩展推理,部分模型上效果与 high 相同。"], ] as ReadonlyArray, timeoutMs: "model.timeoutMs", timeoutMsHint: "单次 Request 超时,毫秒", compaction: "上下文压缩(compaction)", maxContextLength: "max_context_length", maxContextLengthHint: "触发压缩的上下文阈值", maxSessionTurns: "max_session_turns", maxSessionTurnsHint: "触发压缩的轮数阈值", compactionMode: "mode(压缩方式)", compactionModeOptions: [ ["", "不提交覆盖值,沿用当前生效的配置。"], ["summarize", "先让模型为旧上下文生成摘要,再从摘要续接新的上下文窗口(缺省)。"], ["discard", "不生成摘要,直接丢弃旧上下文,下一轮从新窗口重新开始。"], ] as ReadonlyArray, compactionPrompt: "prompt(摘要提示词)", maxTurnsInvalid: "max_turns 必须 > 0 或为 -1", timeoutInvalid: "timeoutMs 必须 > 0 或为 -1", toolFieldInvalid: (name: string, field: string) => `${name}: ${field} 必须是 > 0 的整数或 -1`, builtinTools: "内置工具", toolName: "名称", toolPermission: "permission", permissionReadLabel: "Read-only", permissionReadDescription: "仅读取。审批模式为 read-only 时自动放行,无需确认。", permissionReadWriteLabel: "Read & write", permissionReadWriteDescription: "可修改。审批模式为 read-only 时需人工确认。", toolTimeout: "timeoutMs", toolMaxOutput: "maxOutputLength", mcpServers: "MCP Server(只读)", defaultValue: "(缺省)", deleteAgent: "删除 Agent", builtinUndeletable: "内置 Agent 不可被删除", deleteConfirm: (name: string): string => `确认删除 Agent「${name}」?其目录(含全部 Trace)将被递归删除,不可恢复。`, stateVersion: "Agent State 版本", transferTitle: "导出 / 导入", transferDesc: "导出当前 Agent State 快照包(tar.gz);导入整目录覆盖,并以包内版本为准。", exportSnapshot: "导出快照", importSnapshot: "导入快照", importing: "导入中…", importDone: (v: number): string => `导入完成,Agent State 版本 v${v}`, importConflictTitle: "版本冲突", importConflictBody: "快照包版本不高于当前版本,导入将覆盖现有 Agent State。确认继续?", }, models: { title: "模型配置", addCustom: "添加自定义模型", addToGroup: "新增模型", editTitle: "模型配置", addTitle: "新增模型(OpenAI 协议)", addTitleVendor: "新增模型(按 id 自动路由)", addProtocolHint: "新增模型固定走 OpenAI Chat Completions 兼容协议(不按模型 id 自动路由),base URL 填其兼容端点", addAutoRouteHint: "该分组的新模型按上游 id 由 AgentHub 自动路由到厂商官方客户端:base URL 留空即官方端点,API key 留空按解析出的客户端读取环境变量", autoRouteNone: "该 id 无法被 AgentHub 自动路由:请核对 id,或改在 Custom / 自建分组下以 OpenAI 协议接入", addGroup: "新增分组", addGroupTitle: "新增分组", addGroupDesc: "自建分组与 Custom 同语义:组内模型走 OpenAI Chat Completions 兼容协议(base URL 必填,API key 留空读取 OPENAI_API_KEY)。分组由模型条目承载,保存首个模型后即出现。", groupNameLabel: "分组名", groupNameHint: "小写字母 / 数字开头,可含 - 与 _", groupNameInvalid: "分组名只能用小写字母、数字、- 与 _(首字符为字母或数字),长度不超过 32", groupNameExists: "该分组名已被内置分组或既有条目占用", groupEmptyHint: "该分组暂无模型,点「新增模型」添加", searchPlaceholder: "搜索模型:id / 名称 / 厂商", noSearchResults: "没有匹配的模型", syncCatalog: "同步预置", syncCatalogHint: "用内置目录更新预置模型:新增缺失条目、以目录字段为准刷新差异;本地新增模型与 API key 保持不变", syncDone: (added: number, updated: number) => `预置模型已同步:新增 ${added}、更新 ${updated}`, syncUpToDate: "预置模型已是最新", homepage: "模型主页", speedTest: "测速", speedTestTitle: "分组测速", speedTestConfirm: (n: number): string => `将对该分组的 ${n} 个模型逐个发起一次真实请求,测量首 token 延迟(TTFT)与输出速率(TPS),会消耗少量 API 额度。是否继续?`, speedTestStart: "开始测速", speedPending: "测速中…", speedFailed: "测速失败", ttftTitle: "首 token 延迟(TTFT)", tpsTitle: "输出速率(TPS)", modelCount: (n: number): string => `${n} 个模型`, modelId: "模型 ID", modelIdHint: "上游 API 使用的模型 id,如 gpt-5.5", displayName: "模型名称", displayNameHint: "留空则展示模型 ID", providerGroup: "分组", contextWindow: "上下文窗口", contextWindowUnit: "tokens", contextWindowHint: "留空表示未知", clientTypeLocked: (t: string): string => `协议:${t}(沿用原配置,不可修改)`, vision: "支持图片输入(视觉/多模态)", visionHint: "取消勾选后,对话中上传的图片将以文件路径转交,读图经 describe_image 由视觉模型代读", visionBadge: "视觉", visionModelBadge: "视觉代理", setVisionModel: "设为视觉代理模型", visionModelHint: "供不支持图片的模型经 describe_image 代读图片", priceUnitShort: "/M tok", testConnection: "测试连通性", testing: "测试中…", testOk: (ms: number): string => `连通正常(${ms} ms)`, testFailed: (msg: string): string => `连通失败:${msg}`, modelIdRenameHint: "改 id 后原配置与 API key 一并迁移", priceCacheRead: "缓存读取", priceCacheWrite: "缓存写入", priceOutput: "输出", pricing: "价格", priceUnit: "每百万 Token", currency: "币种", currencyUsd: "美元 $", currencyCny: "人民币 ¥", currencyHint: "价格按 1 美元 ≈ 7 人民币折算;存储始终为美元", firstModelDefault: "已自动设为默认模型", credential: "凭据", apiKey: "API key", apiKeyKeepHint: "留空保留现有 key", apiKeyEnvHint: (envKey: string): string => `留空则使用环境变量 ${envKey}`, useEnvVar: (envKey: string): string => `环境变量 ${envKey}`, keyConfigured: "已配置 key", clearApiKey: "清除已存 API key", baseUrl: "自定义 base URL", baseUrlHint: "留空使用厂商默认地址", baseUrlRequired: "必须填写 base URL", contextWindowDefaultHint: (n: number): string => `留空按 ${n} 计`, visionOnHint: "读图用 read_image:图片直接回灌模型", visionOffHint: "读图用 describe_image:图片交给视觉代理模型描述、只回文本;对话中上传的图片以文件路径转交", confirmDeleteTitle: "删除模型", confirmDelete: (name: string): string => `确定删除「${name}」?该模型的配置与 API key 将一并移除。`, groupApiKey: "统一配置 API key", groupApiKeyTitle: (label: string): string => `为「${label}」统一配置 API key`, groupApiKeyHint: (n: number): string => `将写入该分组下全部 ${n} 个模型;留空不改动。`, getApiKey: "获取 API key", getModelIds: "获取模型 id", groupKeyApplied: (n: number): string => `已为 ${n} 个模型配置 API key`, // Providers with separate domestic / international endpoints: note on the default // endpoint used when left blank via env var (the other side's key needs an explicit // base URL). Written to match AgentHub's actual behavior; rendered wherever the env fallback hint appears. providerEnvNotes: { zhipu: "缺省端点为 Z.AI 国际版(api.z.ai);智谱开放平台(bigmodel.cn)的 key 需填 base URL https://open.bigmodel.cn/api/paas/v4", moonshot: "缺省端点为国内版(api.moonshot.cn);platform.kimi.com(国际)的 key 需填 base URL https://api.moonshot.ai/v1", } as Record, confirmVisionModelTitle: "设为视觉代理模型", confirmVisionModel: (name: string): string => `确定把「${name}」设为视觉代理模型?不支持图片的模型将由它经 describe_image 代读图片。`, confirmSaveTitle: "保存模型配置", confirmSave: (name: string): string => `确定保存对「${name}」的配置修改?`, confirmDefaultTitle: "设为默认模型", confirmDefault: (name: string): string => `确定把「${name}」设为默认模型?新建的 Session 将默认使用它。`, createdAt: "创建时间", default: "默认", setDefault: "设为默认模型", remove: "删除模型", saveAll: "保存全部", readOnlyHint: "member 只读;模型与 credential 修改仅 owner 可执行", empty: "尚未配置任何模型", noKey: "未配置 key", /** Chat model dropdown's bottom expander row: reveals the models hidden by the configured-key filter. */ showModelsWithoutKey: (n: number): string => `显示未配置 key 的模型(${n} 个)`, pendingSave: "(待保存)", modelIdExists: "该模型 id 已存在", pricingAllOrNone: "三项价格需一并填写", pricingInvalid: "必须为数字", contextWindowInvalid: "必须为数字", }, vault: { desc: "本 Agent 专属的环境变量(存于 agent_state/.vault.toml):键值对注入其 shell 命令(exec_command)的子进程环境;键名会告知模型,值不进入模型上下文。子 Agent 使用各自的保险柜,不继承。保存后自下一个任务起生效(进行中的任务不受影响)。", key: "键名", value: "值", valueMasked: "值(掩码)", add: "添加", addTitle: "添加环境变量", remove: "删除", deleteTitle: "删除环境变量", deleteConfirm: (key: string): string => `确认删除环境变量「${key}」?值不可恢复。`, empty: "尚未配置任何环境变量", readOnlyHint: "member 只读;Vault 修改仅 owner 可执行", keyHint: "字母、数字与下划线,不能以数字开头", keyInvalid: "键名不合法:仅字母、数字与下划线,且不能以数字开头", valueRequired: "值不能为空", }, schedule: { desc: "定时任务(agent_state/schedule/*.toml):到点自动向目标 Session 发送 prompt;文件亦可手工编辑,Web 端修改后即时生效。", readOnlyHint: "member 只读;定时任务修改仅 owner 可执行", colName: "名称", colStatus: "状态", colPeriod: "周期", colTarget: "目标", colFireTimes: "下次 / 最近触发", colQueued: "排队", statusNames: { active: "生效", disabled: "停用", expired: "已过期", done: "已完成", missed: "已错过", invalid: "无效", } as Record, queued: "排队中", once: "一次性", newSession: "新建会话", invalidFiles: "解析失败的文件(已跳过调度)", empty: "尚未配置定时任务", enable: "启用", disable: "停用", addTitle: "新建定时任务", editTitle: (name: string): string => `编辑定时任务「${name}」`, name: "名称", nameHint: "即文件名(不含 .toml),创建后不可改", prompt: "Prompt", enabled: "启用", startAt: "开始时间", endAt: "结束时间(可选)", period: "周期", periodPlaceholder: "30m / 12h / 7d,留空为一次性", target: "目标", targetNew: "每次新建会话", targetSession: "绑定 Session", sessionId: "Session id", workspace: "Workspace(可选,留空自动创建)", model: "Model", modelDefault: "Project 默认", deleteTitle: "删除定时任务", deleteConfirm: (name: string): string => `确认删除定时任务「${name}」?`, }, skills: { pageTitle: "技能库", pageDesc: "内置 Skill 库:浏览、快捷调用,或安装到 Agent。", quickInvoke: "快捷调用", /** Pre-filled body for quick invoke (per UI language; English is `use the skill`). */ quickInvokeText: (name: string): string => `使用 ${name} 技能`, manageInstall: "管理安装", manageInstallTitle: (name: string): string => `管理安装:${name}`, install: "安装", installed: "已安装", uninstall: "卸载", /** Skill count in the group header (small text to the right of the group name). */ skillCount: (n: number): string => `${n} 个技能`, /** Usage count in the card metadata (shows "unused" instead of a bare 0). */ usedByAgents: (n: number): string => (n === 0 ? "未被使用" : `${n} 个 Agent 在用`), /** Top toast shown on successful install / uninstall. */ installedToast: (skill: string, agent: string): string => `已将 ${skill} 安装到 ${agent}`, updateOutdated: (n: number): string => `有新版本:更新 ${n} 个 Agent 的安装`, updateAction: "更新", updatedToast: (skill: string, n: number): string => `已将 ${skill} 更新到最新版(${n} 个 Agent)`, uninstalledToast: (skill: string, agent: string): string => `已从 ${agent} 卸载 ${skill}`, }, chat: { newSessionMenu: "新建对话", chooseAgent: "选择 Agent", chooseModel: "选择模型", workspaceUseThis: "使用此目录", workspaceUp: "上级目录", workspaceNoSubdirs: "无子目录", workspaceAuto: "自动临时目录", workspaceClear: "改用自动临时目录", workspaceDirInvalid: "目录不存在或无法访问,已回退", /** 侧栏对话列表的分组切换(默认按工作区)与工作区分组。 */ groupByWorkspace: "按工作区分组", groupByAgent: "按 Agent 分组", tempWorkspaces: "临时工作区", newSessionInWorkspace: "在此工作区新建对话", draftSubtitle: "最擅长 AI 开发任务的自进化 Agent", /** * Example task cards on the draft screen: one click auto-submits the canned prompt (game * card first, RAG card below/after it). These are the FULL working prompts — the README and * landing page show a condensed one-sentence version of the RAG example for reading, and * the cards' own desc lines stay short, but what actually gets submitted stays detailed: * build quality depends on it. */ exampleTasks: { game: { label: "示例:2D 企鹅雪橇越野小游戏", desc: "可爱南极企鹅滑雪橇跳石头,难度由易到难的 2D 纯前端小游戏", prompt: "做一个可爱的南极企鹅滑雪橇越野 2D 小游戏:按空格键起跳,跃过冰面上迎面而来的石头;" + "开局要足够简单、上手无压力,滑行速度与障碍密度随时间平滑、循序渐进地上升,避免突然变难," + "实时计分,撞上石头即结束并可一键重新开始。" + "2D 横版画面、可爱卡通风,纯前端实现(单个 HTML 文件即可),界面遵循 web-design 技能。" + "完成后在浏览器里自测一次,确认开局能轻松玩过几秒,并告诉我怎么打开和怎么玩。", }, lol: { label: "示例:英雄联盟音乐播放器", desc: "用 SoundCloud Widget API 播放历届 Worlds 主题曲,单文件即开即用", prompt: `用 SoundCloud Widget API(见 https://developers.soundcloud.com/docs/api/html5-widget)做一个英雄联盟 Worlds 主题曲播放器,单文件 index.html,file:// 打开即用。 ## 技术约束 - 使用 SC.Widget JS API(widget.load / widget.toggle / widget.setVolume / widget.seekTo),引入 https://w.soundcloud.com/player/api.js - iframe 必须可见(180px 高),visual=true color=f0b90b single_active=true - 仅包含以下 8 首已确认可播曲目(oEmbed 验证通过),不要添加未经 oEmbed 验证的曲目: - Warriors (S4) — soundcloud.com/leagueoflegends/warriors - Worlds Collide (S5) — soundcloud.com/leagueoflegends/worlds-collide - Legends Never Die (S7) — soundcloud.com/leagueoflegends/legends-never-die - Phoenix (S9) — soundcloud.com/leagueoflegends/phoenix - Burn It All Down (S11) — soundcloud.com/leagueoflegends/burn-it-all-down - GODS (S13) — soundcloud.com/leagueoflegends/gods - Heavy Is The Crown (S14) — soundcloud.com/linkinpark/heavy-is-the-crown - Sacrifice (S15) — soundcloud.com/leagueoflegends/sacrifice ## 布局 - 左侧 260px 粘性侧边栏:曲目列表(S4/S5/… 标签 + emoji + 曲名 + 年份),点击高亮金色边框,SC.Widget.load() 切歌 + auto_play - 右侧主区域:Hero 标题 + 桌面时钟(80px 等宽金色 HH:MM:SS,每秒刷新,冒号闪烁)+ 心情标签 - 播放器卡片:SoundCloud iframe + 自定义控制栏(⏮ ▶/⏸ ⏭ + 曲目信息 + 音量滑块,点击喇叭图标静音切换) - 心情波动区:15 根金色动画柱,切歌时重新随机生成 - 键盘快捷键:空格播放暂停、← → 切歌、↑ ↓ 调音量 ## 设计 Penguin 视觉风格(见 web-design 技能),深色/浅色主题(),默认深色,localStorage 记忆。响应式:手机端侧边栏变为顶部横向滚动。 完成后在浏览器打开 index.html 自测一次。`, }, rag: { label: "示例:构建 Claude Code 文档专家", desc: "收集 claude-code-docs 仓库,生成可对话、带来源引用的 RAG 知识应用", prompt: "收集 https://github.com/ericbuess/claude-code-docs 的文档,构建一个 RAG 知识应用:" + "克隆仓库并整理语料,建立检索索引;应用化身 Claude Code 配置专家," + "检索增强回答 Claude Code 相关问题并标注可点击的来源引用——" + "引用要能展示命中的原文片段,并链接到真实文档;" + "按 web-design 技能提供美观的 Web 聊天界面,空态展示几个示例问题。" + "完成后运行应用、自测一个问题验证流式回答,并告诉我访问方式。", }, }, sessionList: "Session", defaultSessionTitle: "新对话", model: "Model", workspace: "Workspace", workspaceHint: "留空自动创建临时目录;指定时必须是服务器上已存在的目录", approvalMode: "审批模式", /** Short description (the trigger button shows only the description, not the mode id). */ approvalModeNames: { "allow-all": "全部放行", "deny-all": "全部拒绝", "read-only": "放行只读", "always-ask": "总是询问", } as Record, approvalModes: { "allow-all": "全部放行(allow-all)", "deny-all": "全部拒绝(deny-all)", "read-only": "放行只读(read-only)", "always-ask": "总是询问(always-ask)", } as Record, statusIdle: "空闲", statusRunning: "运行中", statusCompacting: "压缩中", pendingApprovals: (n: number) => `${n} 个待审批`, jumpToLatest: "回到最新消息", inputPlaceholder: "输入消息,Enter 发送,Shift+Enter 换行,可粘贴图片", inputPlaceholderShort: "输入消息…", send: "发送", stop: "停止", compactingHint: "上下文压缩中,暂不接受新输入", compact: "压缩上下文", approve: "允许", deny: "拒绝", decisionAllow: "已批准", decisionDeny: "已拒绝", decisionManual: "手动", decisionAuto: "自动", thinking: "思考", subagent: "子会话", subagentRunning: "运行中", aborted: (reason?: string) => `[已中断]${reason ? `:${reason}` : ""}`, reconnect: ( status: "timeout" | "malformed", state: "waiting" | "retried" | "gaveUp", attempt: number, ) => { const cause = status === "timeout" ? "连接超时或网络中断" : "响应不完整或无法解析"; const action = state === "gaveUp" ? "已停止重试" : state === "retried" ? `已发起第 ${attempt} 次重试` : `正在发起第 ${attempt} 次重试…`; return `[重试] ${cause},${action}`; }, imageAlt: "用户上传的图片", toolImageAlt: "工具输出的图片", imagesAsPathHint: "当前模型不支持直接查看图片:发送时图片将保存到会话临时目录,以文件路径转交(模型经 describe_image 查看)", infoPanel: "Session 信息", sessionStats: "统计", statTokens: "Token 累计", statCost: "成本", statElapsed: "用时", statInput: "输入 tokens", statCached: "已缓存", statOutput: "输出 tokens", statTps: "输出 TPS", /** Copied-stats-line parenthesis wrappers around the cached amount (fullwidth for zh typography). */ statParenOpen: "(", statParenClose: ")", noSessions: "还没有 Session", emptyStream: "发送一条消息开始对话", historyLoadFailed: "历史消息加载失败", sessionHealed: "Session 已自愈为新 id", statsLabel: "统计信息", removeImage: "移除图片", openWorkspace: "打开工作区", /** File summary card at the end of a message (Codex-style): title, inline preview action, and collapsed row. */ filesInMessage: (n: number) => `${n} 个文件`, openPreview: "点击预览", showMoreFiles: (n: number) => `显示其余 ${n} 个文件`, showLess: "收起", contextUsage: "上下文占用", contextUnknown: "上下文占用:压缩后待下次请求回报", slashHint: "输入 / 使用命令", mentionHint: "@ handoff 给其他 Agent", mentionRemove: "移除 @ 目标", /** Skill multi-select dropdown (input toolbar): button text, search box, empty state, and no-match hint. */ skillsSelect: "技能", skillRemove: "移除技能", skillsSearchPlaceholder: "搜索技能", skillsNoMatch: "没有匹配的技能", skillsEmptyHint: "暂无已装技能,去技能库添加", /** Auto-generated invocation text when skills are selected and the body is empty (wrapped in before sending). */ skillsAutoMessage: (names: string[]): string => `使用 ${names.join("、")} 技能`, handoffFrom: (agent: string) => `由 ${agent} 的对话交接而来`, handoffBack: (title?: string) => (title ? `回到原对话:${title}` : "回到原对话"), scheduledFrom: (name: string) => `由定时任务「${name}」触发`, /** Source badge on session list rows (user-created sessions have no source). */ sourceNames: { schedule: "定时", subagent: "子", } as Record, commandCompact: "/compact", emptyGreeting: "开始一段新对话", compactionRunning: (mode: string) => `压缩进行中(${mode})…`, compactionDone: (mode: string): string => mode === "discard" ? "[压缩] 完成,旧上下文已丢弃" : "[压缩] 完成,已切换到摘要后的新上下文", compactionFailed: (status: string) => `[压缩] ${status === "aborted" ? "已中断" : "失败"},保留当前上下文`, unknownTool: "(未知工具)", toolCall: "工具调用", workGroupTitle: "推理与工具", workRunning: "运行中", workDone: "运行完毕", workGroupSteps: (n: number) => `${n} 步`, approvalWaiting: "待审批", copyCode: "复制代码", copyStats: "复制统计", copyReply: "复制回复", copyMessage: "复制消息", copied: "已复制", deleteSession: "删除对话", renameSession: "重命名对话", renameSessionLabel: "标题", deleteSessionConfirm: (title: string) => `确定删除「${title}」?该对话的消息与 Trace 将被移除,且不可恢复。`, archiveSession: "归档", unarchiveSession: "取消归档", archivedGroup: (n: number) => `已归档(${n})`, skillsBanner: (names: string[]): string => `使用技能:${names.join("、")}`, }, files: { title: "文件", upload: "上传", download: "下载", openInNewTab: "新页面打开", refresh: "刷新", root: "根目录", empty: "空目录", name: "名称", size: "大小", modified: "修改时间", previewUnsupported: "该类型不支持预览,请下载查看", uploaded: "已上传", loadFailed: "加载失败", previewTruncated: "内容过大,预览已截断,请下载查看完整文件", details: "详情", workspacePath: "Workspace 路径", htmlRendered: "渲染视图", htmlSource: "源码", backToList: "返回列表", resizeHandle: "拖拽调整宽度,双击恢复默认", }, usage: { title: "成本与统计", today: "今日", last7d: "近 7 天", total: "累计", tokens: "Token", cost: "成本", requests: "Requests", from: "起始日期", to: "结束日期", colCacheRead: "cache_read", colCacheWrite: "cache_write", colOutput: "output", colCost: "成本", trendTitle: "近 30 天趋势", uncostedNote: "* 含未配置 pricing 的记录,成本为不完全统计", filterAllAgents: "全部 Agent", filterAllModels: "全部模型", chartAgentCalls: "各 Agent 调用次数", chartSuccessRate: "各模型成功率", chartTokenTrend: "Token 逐日变化", chartCostTrend: "成本逐日变化", empty: "暂无用量记录", successAborted: "已中断(不计入)", errors: "异常", errorsTotal: "总数", errorsUnexpected: "未预期", errorsExpected: "预期内", errorsTopCode: "最常见", errorsColTime: "时间", errorsColCode: "来源 · 错误码", errorsColKind: "类型", errorsColMessage: "消息", errorsEmpty: "暂无异常", }, traces: { title: "轨迹观测", filterAll: "全部", filterModel: "model_msg", filterEvent: "event_msg", analysis: "轨迹观测", timeline: "执行时间线", laneLLM: "模型", laneTools: "工具", phaseApproval: "审批", phaseExec: "执行", kindThinking: "思考", kindModelReply: "模型回复", kindToolGen: "工具调用生成", legendToolExec: "工具调用执行", legendApprovalWait: "审批等待", task: (n: number) => `第 ${n} 轮`, globalSummary: "全局统计", tasksLabel: "轮次", messages: "消息", truncatedNote: (shown: number, total: number) => `仅展示前 ${shown} / ${total} 条消息`, zoom: "缩放", zoomReset: "双击复位缩放", zoomOut: "缩小", zoomIn: "放大", linkHint: "鼠标移到时间线段或消息行即可联动高亮,点击时间线段跳转到对应消息;图例可高亮同类;拖动下方滑块平移/缩放", filesTitle: "Trace 文件", selectSession: "在左侧选择一个 Session", toolCalls: "工具调用", taskInput: "本轮输入 tokens", taskOutput: "本轮输出 tokens", cacheHit: "命中缓存", hitRate: "命中率", toolFailRate: "失败率", compactions: "压缩次数", compactionRound: "压缩", usageTrend: "Token 用量趋势", selectFile: "在左侧选择一个 Trace 文件", empty: "该 Agent 暂无 Trace", colTime: "时间", colType: "类型", colSummary: "摘要", lines: (n: number) => `${n} 行`, inProgress: "进行中", systemPrompt: "系统提示词", toolDefs: (n: number) => `工具定义(${n})`, }, benchmark: { title: "评估中心", selectBenchmark: "在左侧选择一个 Benchmark", emptyAgent: "该 Agent 暂无 Benchmark", caseCount: (n: number): string => `${n} 题`, /** Chart title, varies by selected metric (score / cost / duration over time). */ trendTitle: (metric: string): string => `${metric}随时间变化`, evaluations: "评估明细", noEvaluations: "暂无评估记录", /** Evaluation notes (scoreboard's summary: score source and notes on this round's changes). */ summaryLabel: "评估说明", /** Chart legend: older evaluation records with no model label (gray series). */ legendUnlabeled: "未标注模型", colTime: "时间", colVersion: "版本", colModel: "模型", colScore: "总分", colCost: "成本", colDuration: "耗时", colCase: "题目", colRun: "运行", colSession: "Session", }, // Server error code → localized copy (the server's message is hardcoded Chinese; this is only a fallback for unknown codes). errors: { unauthorized: "登录已过期,请重新登录", networkError: "网络错误,请检查连接", modelCredentialMissing: (modelId: string) => `模型 ${modelId} 还没有可用的 API key,请先在「模型」页为它配置`, noDefaultModel: "该 Project 还没有默认模型,请先在「模型」页添加模型并设为默认", }, }; /** Dictionary shape (constrains the English dictionary so keys and function signatures line up). */ export type Strings = typeof zh; /** * Runtime active dictionary (live binding): the locale Provider calls setActiveStrings * to switch before render, and remounts the whole tree keyed by locale so every `S.x` * read reflects the current language. */ export let S: Strings = zh; export function setActiveStrings(next: Strings): void { S = next; }