OpenCode 多语言同步机制详解translate:app 脚本、Drift 检测与受限 Agent 翻译流水线【免费下载链接】opencodeThe open source coding agent.项目地址: https://gitcode.com/GitHub_Trending/openc/opencodeOpenCode 仓库的script/translate-app.md并非面向人的说明文档而是一份被script/translate-app.ts程序化消费的翻译提示词模板。本文以该模板为骨架结合驱动它的同步脚本、i18n 词典目录结构和测试用例完整讲清楚 OpenCode 如何把 60 多个语言的 UI 文案与英文源词典保持精确同步从 missing/extra/placeholder 三类 drift 的自动检测到权限被严格收窄到目标文件的 OpenCode Agent 运行再到会话模型验证与工作树快照双重校验读者可以据此复现整条翻译流水线并理解每一道安全边界。translate-app.md一份被程序消费的提示词模板script/translate-app.md 只有短短二十多行但它的每一条内容都是对翻译 Agent 的硬性指令。模板中有两个占位符由 translate-app.ts 在运行时做字符串替换$1被替换为目标 locale 代码如fr、zh出现在模板首句 “Translate the product app locale$1from the English source dictionaries.”$ARGUMENTS被替换为一段格式化的 JSON 请求体包含locale、language语言名称、可选的glossary词条表文件名与全文以及每个字典域的source、target路径和检测结果missing、extra、placeholders三个键值列表。因此 Agent 拿到的提示词已经“自带任务清单”它不需要自己去 diff 词典只需按 JSON 里列出的差异去修复目标文件。模板开篇即确立了最高原则英文是唯一事实来源read-only source of truth其文案是刻意设计的绝不能被修改、改写或“优化”。模板的 Requirements 部分是对 Agent 行为的完整契约逐条继承如下只编辑请求中列出的目标文件绝不修改英文文件、其他语言文件、测试、注册表、文档或其他包视每个英文 key 与 value 为有意设计从英文出发翻译任何情况下都不改动英文源文件补齐所有缺失 key翻译要自然、简洁适合应用 UI 场景删除 extra 列出的 key并修复placeholders列出的值使其{{tokens}}与英文完全一致保留既有翻译除非该 key 出现在 placeholder 不匹配列表中保留语义、意图、语气、大小写、标点、空白与格式原样保留技术制品OpenCode、API 名称、标识符、代码、命令、flag、路径、URL、版本号、错误信息、配置键以及占位符 token开发者术语跟随社区习惯优先使用目标语言开发者社区已认可的词而非词典式直译至少核对 Firefox、KDE、VS Code 中两个仍在维护的本地化语料并以 Microsoft 或官方语言机构为佐证在完整产品上下文中翻译短语对 session、prompt、agent、model、provider、fork、shell、terminal、workspace、worktree、context、permission、tool、server 等反复出现的概念保持术语一致与语法正确若开发者语料保留了英文借词则继续保留语料稀疏或有分歧时保守措辞在最终回复中点出不确定的术语而不是自造术语应用请求中附带的 locale 词条表glossaryui.sessionTurn.diffs.changed.one与ui.sessionTurn.diffs.changed.other是完整的计数短语必须保留{{count}}并整体自然翻译不能拼凑翻译片段工具白名单只允许使用 read、glob、grep、webfetch、websearch 和 edit 工具不得运行命令不得委派任务完成标准只有当所有要求的 key 全部同步、且没有其他文件被改动时任务才算完成。第 12 条解释了为什么计数短语不能拆分本地化文案中{{count}} file这类带数字的表达往往在目标语言里是整体句法结构例如中文“共 {{count}} 个文件”逐词翻译会破坏可读性。如何触发translate:app 命令与全部参数模板由根目录 package.json 中的 npm script 暴露translate:app: bun run script/translate-app.ts脚本入口 translate-app.ts 使用util.parseArgs解析参数完整的用法帮助与示例如下-h输出原文Usage: bun run translate:app -- locale|all [options] Synchronizes product app translations with the English app, UI, and desktop dictionaries. Options: -c, --concurrency count Maximum parallel OpenCode runs for all (default: 4) --model provider/id OpenCode model (default: opencode/gpt-5.5) --variant name Model variant (default: xhigh) --dry-run Report drift without running OpenCode --check Exit nonzero when translation drift exists -h, --help Show this help message Examples: bun run translate:app -- fr bun run translate:app -- all --concurrency 4各参数的行为细节由 parseTranslationArgs 实现并有测试覆盖参数默认值行为位置参数all单个 locale如fr或all不支持en英文是源见测试 “rejects unsupported targets”一次只能传一个位置参数-c, --concurrency4all模式下并行运行的 OpenCode 会话数上限正整数校验指定单个 locale 时并发被强制降为 1--modelopencode/gpt-5.5必须为provider/model语法且 variant 需在该模型的 verbose 输出中真实存在--variantxhigh模型变体运行前会经opencode --pure models provider --verbose解析校验--dry-runfalse只报告 drift不启动 OpenCode--checkfalse存在任何 drift 时以非零码退出适合 CI 卡点不做翻译-h, --helpfalse打印帮助同步范围英文源、三个目标字典与词条表每个 locale 的同步范围由 targetFiles 决定测试 translate-app.test.ts 明确验证了其形状packages/app/src/i18n/{locale}.ts—— 产品应用主词典packages/ui/src/i18n/{locale}.ts—— UI 组件库词典packages/desktop/src/renderer/i18n/{locale}.ts—— 桌面端渲染层词典仅当该 locale 属于 desktop-native.ts 中DESKTOP_NATIVE_LOCALES列表时才加入。源文件是把目标路径中的/{locale}.ts替换为/en.ts得到见 inspect。以 packages/app/src/i18n/en.ts 为例英文词典以export const dict导出并直接展开DESKTOP_NATIVE_ENGLISH即桌面端菜单、更新器、恢复界面等文案也随应用词典一起参与漂移比对packages/ui/src/i18n/en.ts 与 packages/desktop/src/renderer/i18n/en.ts 则各自维护desktop./ui.前缀的键。词条表由 glossaryFile 映射到.opencode/glossary/目录特殊映射为zh→zh-cn.md、zht→zh-tw.md其余为{locale}.md。以 zh-cn.md 为例词条表包含三个部分Do Not Translate如OpenCode保留大小写、OpenCode Zen、CLI、TUI、MCP、OAuthPreferred Terms英文 → 推荐译法的对照表如 prompt → 提示词、session → 会话、provider → 提供商、keybind → 快捷键并附注如 “Keep--promptunchanged in flags/code”Guidance / Avoid措辞风格指引例如枚举型字面量default、json保持英文、同一概念全局统一术语。词条表存在时其文件名与全文会一并注入$ARGUMENTS的glossary字段不存在则该字段为undefinedAgent 只能依赖模板自身规则。Drift 检测missing、extra 与占位符不匹配同步与否的判断完全基于 findDrift它对比源词典与目标词典并输出三类差异missing英文存在而目标缺失的 keyextra目标存在而英文没有的 keyAgent 需要删除它们placeholders双方都有但{{tokens}}集合不一致的 key。token 提取由 tokens 完成用正则{{\s*([^}]?)\s*}}匹配后排序比较因此 token 顺序和空白差异不影响判定只比“有哪些 token”。CLDR 复数变体是这套检测中最精细的部分。英文词典普遍只有.one/.other两个复数形态但阿拉伯语等语言按 CLDR 还有two、few、many、zero等类别。findDrift接收可选的 locale 参数通过 desktopNativePluralCategories底层是Intl.PluralRules(...).resolvedOptions().pluralCategories枚举目标语言的全部复数类别把“除 one/other 外的每个${key}.${category}”视为合法的 plural 变体以${key}.other为占位符比对基准变体 key 缺失时计入 missing测试 “reports missing locale-specific CLDR plural variants” 验证阿拉伯语缺files.few的情况变体的 token 与.other源不一致时计入 placeholders测试 “reports placeholder drift in locale-specific CLDR plural variants” 中阿拉伯语files.many漏掉{{count}}即被捕获变体 key 不会被误判为 extra测试 “accepts locale-specific CLDR plural variants” 中阿拉伯语全变体齐全时 drift 为空。复数家族的识别由 pluralFamilies 完成仅当.one与.other两个键都包含{{count}}时才视为计数短语家族——这与模板第 22 条对ui.sessionTurn.diffs.changed的特殊关照互为呼应。--dry-run与--check的输出正是基于这份 driftreport 按 locale 逐域打印app: N missing, N extra, N placeholder mismatches; ui: ...; desktop: ...任何非零 drift 都会使--check模式以退出码 1 结束可直接作为 CI 门禁。运行时隔离环境 受限 Agent真正执行翻译时translate 为每个 locale 构造一个一次性 Agent 会话。隔离环境。isolatedEnvironment 先剥离宿主进程中的OPENCODE_CONFIG、OPENCODE_CONFIG_DIR、OPENCODE_CONFIG_CONTENT、OPENCODE_PERMISSION、OPENCODE_AUTO_SHARE再显式设置OPENCODE_DISABLE_PROJECT_CONFIG1保证 Agent 不受仓库里.opencode/项目配置或本地凭证影响Agent 名称为translate-app-{locale}-{pid}每次进程唯一。权限配置。translationConfig 生成的OPENCODE_CONFIG_CONTENT结构是安全模型的核心{ $schema: https://opencode.ai/config.json, model: opencode/gpt-5.5, default_agent: translate-app-fr-12345, share: disabled, formatter: false, lsp: false, snapshot: false, agent: { translate-app-fr-12345: { mode: primary, model: opencode/gpt-5.5, permission: { *: deny, read: allow, glob: allow, grep: allow, webfetch: allow, websearch: allow, edit: { *: deny, packages/app/src/i18n/fr.ts: allow, ...: allow } } } } }测试 “disables side effects and scopes edits for the translation agent” 验证了关键点会话分享被禁用、formatter/LSP/snapshot 全部关闭工具权限默认全拒*: deny只放开 read/glob/grep/webfetch/websearch 以及仅限目标 locale 文件的 edit——这与模板第 13 条“只允许 read、glob、grep、webfetch、websearch、edit不运行命令”一一对应模板约束在配置层被机器化强制而不是仅靠提示词自觉。会话启动。最终执行的命令为translateopencode --pure run --dir repo root --agent translate-app-{locale}-{pid} \ --model opencode/gpt-5.5 --variant xhigh --title Translate app {locale} --format json提示词经 stdin 写入--format json的输出是逐行事件流。textFromEvents 从中提取type: text事件拼出 Agent 的最终回复sessionIDFromEvents 用正则抓取会话 ID 供后续验证。三重验证模型核验、漂移复检与工作树快照翻译会话退出码为 0 并不等于任务完成流水线还有三道独立验证全部通过才会打印Translated {locales}会话模型核验。脚本随后执行opencode --pure export {sessionID} --sanitize导出会话 JSONsessionModels 遍历messages中所有role: assistant的消息读取providerID/modelID/variant并要求与请求的 model/variant 完全一致测试 “reads the actual model and variant from the completed session” 覆盖此解析。出现不一致时退出码置 1stderr 报告 “Requested X, but session used Y”。变体本身在执行前也要过 resolveModelVariant 的解析校验——从opencode --pure models provider --verbose的输出里按 modelVariants 截取该模型的variants元数据variant 不存在直接报错。漂移复检。对每个 pending locale 再以--check模式重跑自身check用与 CI 完全相同的标准确认 missing/extra/placeholders 已清零未清零者报 “Translation remains incomplete”。越权改动检测。执行前 worktreeSnapshot 通过git diff --name-only HEAD加git ls-files --others枚举所有脏文件与未跟踪文件并计算 SHA-256 哈希执行后再次快照unexpectedChanges 找出“允许目标之外且内容发生变化”的文件测试 “detects edits outside the locale targets” 同时覆盖了工作区原本就有脏文件的场景。这为模板第 11 条“Never edit English, another locale, tests, registries, docs, or other packages”提供了事后审计。并发调度由 runPool 实现起min(concurrency, items.length)个 worker 递归消费迭代器结果按输入顺序输出测试 “runs work with the requested maximum concurrency” 断言峰值并行度恰好等于配置的 concurrency。与 i18n 目录的协同关系把整条链路串起来看仓库里的 i18n 布局就是这套机制的“账本”路径角色packages/app/src/i18n/en.ts、packages/ui/src/i18n/en.ts、packages/desktop/src/renderer/i18n/en.ts英文源词典只读事实来源packages/{app,ui}/src/i18n/{locale}.ts、packages/desktop/src/renderer/i18n/{locale}.ts各 locale 目标词典唯一允许被 Agent 编辑的文件packages/app/src/i18n/desktop-native.tsDESKTOP_NATIVE_LOCALES全量 locale 注册表、locale→标签/BCP 47 tag 映射、CLDR 复数类别查询、桌面端英文文案.opencode/glossary/各语言词条表zh → zh-cn.md、zht → zh-tw.md注入提示词packages/app/src/i18n/parity.test.ts应用侧的词典 parity 测试与--check同向把关script/translate-app.test.ts对参数解析、目标文件映射、drift 检测、并发池、会话解析、权限配置、越权检测的完整单元覆盖从源码结构看DESKTOP_NATIVE_LOCALES同时驱动两件事targetFiles决定某 locale 是否有第三个desktop目标文件desktopNativePluralCategories决定 drift 检测需要容忍哪些 CLDR 复数变体。新增一个语言时先在该注册表登记再补齐 app/ui 词典文件bun run translate:app -- {locale}即可自动纳入流水线。实操要点小结只读检查bun run translate:app -- all --dry-run输出各 locale 每域的 missing/extra/placeholder 数量不产生任何变更CI 门禁bun run translate:app -- {locale} --check存在 drift 即非零退出单语言全量同步bun run translate:app -- fr默认opencode/gpt-5.5variantxhigh只写该 locale 的三个目标文件批量同步bun run translate:app -- all --concurrency 4每个 locale 一个独立、权限收窄到目标文件的 OpenCode 会话换模型--model provider/model --variant namevariant 必须真实存在于该模型的 verbose 输出中且会话结束后会被逐消息复核防止实际调用走样失败信号stdout/stderr 按[locale]与[locale verification]分组打印退出码 1 分别对应 OpenCode 执行失败、drift 未清零或越权文件改动三类失败互不掩盖。这套机制的设计思路值得借鉴把提示词写成带机器可读任务清单的模板$1/$ARGUMENTS把“不许做什么”同时写进提示词与 Agent 权限配置两处再用模型核验、漂移复检、工作树快照三道独立验证兜底——即使 Agent 不守规矩流水线也能发现并拒绝它。【免费下载链接】opencodeThe open source coding agent.项目地址: https://gitcode.com/GitHub_Trending/openc/opencode创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考