OpenCode 泰语(th)翻译词汇表:LLM 驱动国际化流水线中的术语治理规范
本文以 OpenCode 仓库中的泰语 locale 词汇表 th.md 为主体,解读其中"不可翻译项、优先术语、风格指引"三类规则的具体含义,并结合仓库中的翻译脚本 translate-app.ts 与提示词模板 translate-app.md 说明这些规则如何被自动注入到 AI 翻译流水线中生效。读完后,你将理解 OpenCode 如何用一个轻量 Markdown 文件约束 LLM 翻译的一致性,以及如何在为其他 locale 贡献术语表时复用同一套机制。
一、词汇表在整个国际化体系中的定位
OpenCode 的产品界面(app、ui、desktop 三个包)通过 i18n 字典实现多语言:每个语言对应一个 dict 字典文件,如泰语的 th.ts,英文 en.ts 是唯一只读的事实来源。而 th.md 属于另一层体系——locale 词汇表(locale glossary),其定位在 glossary/README.md 中有明确说明:
- 该目录存放"locale 级别的翻译指导",是对全局翻译规则的补充;
- 全局不可翻译术语(命令、代码、路径、产品名等)以全局规则为准,locale 文件只记录社区沉淀出来的措辞与术语偏好;
- 每个 locale 一个文件,文件名用小写 slug,语言级指引用语言码(如
fr.md),地区变体对齐文档 locale 命名(如zh-cn.md、zh-tw.md)。
翻译脚本中的 glossaryFile() 函数确认了这套命名映射的实现逻辑:
export function glossaryFile(locale: Locale) {
if (locale === "zh") return ".opencode/glossary/zh-cn.md"
if (locale === "zht") return ".opencode/glossary/zh-tw.md"
return `.opencode/glossary/${locale}.md`
}
也就是说,当流水线处理 th locale 时,它按约定直接定位到 .opencode/glossary/th.md 并将其全文读入——这就是词汇表与翻译流水线的绑定方式。
二、th 词汇表的内容逐节解读
2.1 Sources:规则出处溯源
词汇表开头列出了两条来源:
- PR #10809:标准化了泰语在各 locale 语言列表中的写法;
- PR #11496:确立"语言选择器中的语言名保持原生写法、不随当前 locale 翻译"的原则。
glossary/README.md 的贡献规范强调"每条新规则都要更新 Sources 段落,优先采用有 PR 支撑的指引,而非臆造的术语映射"。这种"规则必须可溯源到具体 PR/讨论"的设计,使词汇表更像一份可审计的术语决策日志,而不是随意堆砌的对照表。
2.2 Do Not Translate (Locale Additions):不可翻译项
这是泰语词汇表的核心清单,逐条含义如下:
| 条目 | 规则 |
|---|---|
OpenCode |
正文中保留原始大小写;只有在命令、包名、路径或代码中才写成小写 opencode |
OpenCode CLI |
产品组件名,不翻译 |
CLI、TUI、MCP、OAuth |
开发者社区通用缩写,保持英文 |
| 命令、flag、文件路径、代码字面量 | 一字不改地原样保留 |
前几条与全局规则(命令、代码、路径、产品名不翻译)有重叠,但后两条是泰语特有的补充:把 TUI、MCP、OAuth 明确列为泰语场景下的豁免项。这与 translate-app.md 提示词中"开发者术语优先使用目标语言开发者社区已认可的词,而非词典直译"的要求一脉相承。
2.3 Preferred Terms:优先术语表
| 英文 / 上下文 | 推荐写法 | 说明 |
|---|---|---|
| 语言列表中的"泰语"标签 | ไทย |
PR #10809 已将其在各 locale 间统一 |
| 语言选择器中的语言名 | 原生名称(静态) | PR #11496:English、Deutsch、ไทย 等在各 locale 下保持一致 |
第二条规则有直接的字典级证据:英文源字典 en.ts 中 "language.th": "ไทย",泰语字典 th.ts 中同样是 "language.th": "ไทย"——即"泰语"的显示名在所有 locale 下都静态显示为 ไทย,而不是在德语界面里显示成 "Thailändisch"。这正是"Preferred 且可能演进"(README 中要求把此类条目标注为可演进)的典型规则。
2.4 Guidance 与 Avoid:风格指引与反模式
- Guidance:泰语追求自然表达而非逐词直译;按钮和标签保持简短清晰的语气;命令、flag、代码、URL、模型 ID、文件路径等技术工件一律原样保留;语言选择器中语言名保持原生/静态。
- Avoid:不要在语言列表中按当前 locale 给语言名做不同翻译;除非产品标准变更,不要更改
ไทย的显示形式。
这两节给 LLM 划定了"边界":不是禁止翻译,而是禁止两类最容易破坏一致性的行为——语言名单元化漂移,以及技术工件被"顺手翻译"。
三、词汇表如何被翻译流水线消费(源码级机制)
理解这份 Markdown 的价值,需要看它的消费方 script/translate-app.ts。整个流水线的工作方式如下:
- 漂移检测。
inspect()对每个目标文件(packages/app/src/i18n/th.ts、packages/ui/src/i18n/th.ts,以及 desktop 包中存在的泰语字典)逐一对比英文源字典,findDrift()计算出三类差异:缺失 key(missing)、多余 key(extra)、占位符不匹配(placeholders)。占位符比较通过tokens()提取{{token}}并排序后比对,确保{{count}}等插值 token 与英文完全一致。 - 词汇表注入。
translate()中读取词汇表并直接拼进发给模型的请求体:
const glossary = glossaryFile(plan.locale)
const glossaryContent = (await Bun.file(path.join(root, glossary)).exists())
? await Bun.file(path.join(root, glossary)).text()
: undefined
const prompt = template.replaceAll("$1", plan.locale).replaceAll(
"$ARGUMENTS",
JSON.stringify({
locale: plan.locale,
language: plan.language,
glossary: glossaryContent ? { file: glossary, content: glossaryContent } : undefined,
domains: plan.domains.map((domain) => ({
source: domain.source,
target: domain.target,
...domain.drift,
})),
}, null, 2),
)
词汇表以 { file, content } 结构进入 translate-app.md 模板中的 $ARGUMENTS 位置。提示词末尾明确要求"Apply the locale glossary included in the request"——即词汇表不是可选参考,而是被执行的约束。若某个 locale 没有词汇表文件,glossary 字段为 undefined,流水线仍能运行,说明词汇表是增量增强而非硬依赖。
3. 隔离执行。子进程以 --pure 模式运行 opencode run,通过环境变量 OPENCODE_CONFIG_CONTENT 注入一个专用 agent:read/glob/grep/webfetch/websearch 允许,edit 仅允许三个目标字典文件,其余权限全部 deny。这保证 LLM 只能修改本 locale 的字典,不能"顺手"改英文源或其他 locale。
4. 验证收口。翻译完成后,脚本重新执行 --check 漂移检测,并比对 git diff 工作区快照(unexpectedChanges()),确认没有目标文件之外的变更;同时通过 opencode export <sessionID> --sanitize 导出会话,校验 assistant 消息实际使用的模型与请求的 --model/--variant 一致。任何一项不满足即退出码非零。
因此,th.md 里的每一条规则——保留 OpenCode 大小写、ไทย 静态显示、语言名单元化不翻译——最终都会成为一次真实翻译会话中模型必须遵守的指令,并由后续的漂移检查兜底验证结果。
四、实操:查看、运行与贡献
4.1 查看泰语翻译现状
- 词汇表本身:.opencode/glossary/th.md
- 泰语字典:packages/app/src/i18n/th.ts、packages/ui/src/i18n/th.ts(如存在)
- 词汇表目录约定:.opencode/glossary/README.md
4.2 运行漂移检查
仓库根 package.json 注册了脚本 "translate:app": "bun run script/translate-app.ts",帮助文档给出的用法:
# 仅报告泰语字典与英文源的漂移(缺失/多余/占位符不匹配),不执行翻译
bun run translate:app -- th --dry-run
# 存在漂移时以非零退出码结束,适合放进 CI
bun run translate:app -- th --check
# 完整执行泰语翻译(会调用 opencode 并写入三个目标字典)
bun run translate:app -- th
--model(默认 opencode/gpt-5.5)与 --variant(默认 xhigh)可在执行前解析校验,translate-app.ts 中的 resolveModelVariant() 会调用 opencode models <provider> --verbose 确认该 variant 真实存在,避免请求到未配置的推理档位。
4.3 为泰语词汇表贡献新规则
按照 glossary/README.md 的模板与贡献规范,新增规则时应:
- 沿用
Sources→Do Not Translate→Preferred Terms→Guidance→Avoid的骨架; - 规则要满足"跨多个界面/文档重复出现、易于一致执行、有社区贡献或评审讨论支撑"三条标准之一;
- 每新增一条规则就同步追加
Sources段落; - 可能演进的条目标注为 preferred,避免把临时偏好固化为硬规则。
五、小结
th.md 看似只有不到 40 行,实则是 OpenCode 国际化体系里"术语治理"的最小完整单元:它用 PR 溯源的规则表约束 LLM 翻译的一致性,再由 script/translate-app.ts 自动注入提示词、以最小权限执行、以漂移检查收口验证。对贡献者而言,它的价值在于给出了一个可复制的模式——任何 locale 都可以用同一套词汇表模板沉淀本语言的术语决策,从而让"机器翻译 + 自动校验"的流水线在数百个 key 的规模下依然保持术语稳定。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00