业务问题
这个站是中文写的,我想给它一份英文镜像——页面、博客、简历数据全套。人工维护不现实:中文源经常改,英文跟着追等于再养一个站。直接丢给 LLM 整页翻译,试过就知道有三个坑:
- 结构会被改坏——标签、属性、SVG 路径,模型「顺手优化」一下页面就花了
- 措辞会被升格——中文源老老实实写「AI 辅助开发」,译文动不动就变成「独立开发」「from-scratch」式的吹牛。译文比原文能干,在我这个站上是事故不是惊喜
- 细节会悄悄丢——数字、链接、限定词,丢了没人发现
所以真正的问题不是「怎么翻译」,而是怎么让机器翻译在没人盯着的情况下不越界。
方案:整页翻译 + 确定性重写 + 质量闸
- 翻译单元是整个文件:HTML、Markdown、JSON 三类源各配一份系统提示词,整页进整页出,不做碎片拼装
- 增量翻译:manifest 记录每个中文源翻译时的内容哈希,没变的绝不重翻——改一页只花一页的钱
- 确定性的事不交给模型:路径改写、canonical、语言标记这些机械变换,全部由代码在译文落盘前统一执行,模型只负责语言
- 四道质量闸,全过才写盘:标签骨架逐字节比对、措辞红线、数字防丢、中文残留检查
- 纯 Node 标准库,零 npm 依赖——和整个站一个原则,一个一千行上下的脚本就是全部
最硬的仗:防的不是翻错,是吹牛
做之前我以为质量闸主要防翻译错误,做完发现最大的敌人是模型的「热情」。中文源写「用自然语言指挥 Claude Code 完成」,模型翻着翻着就升格成不带任何限定词的「独立开发」——它以为是在帮我润色,实际是在替我撒谎。
所以措辞闸是硬编码的死规矩:「独立开发」「纯手写」「from-scratch」这类英文吹牛短语进禁词表,出现即拦;中文源里带「AI 辅助」限定的内容,译文必须保留 AI-assisted 对应表述,丢了也拦。数字闸把中文源里每个数字拿到译文里找,找不到就拦(认得「万/亿」到 million/billion 的换算形态,防误报)。
这不是杞人忧天。首翻 20 个文件,质量闸拦了 7 次:有想写 from-scratch 的,有把「AI 辅助」翻丢的,有弄丢「1400+」这个数字的。拦下后错误清单原样喂回模型重译一轮;两轮还不过就整个拒绝——不写盘、manifest 不记账,下次重跑从头再来。
生产加固
- 发布闸:发布脚本先比对 manifest,中文源变了还没重翻,发布直接停——英文镜像不会悄悄落后于中文
- 体检常态化:已生成的英文产物并入全站检查,每次全量重过质量闸,外加禁词全局扫描和内链存在性检查
- 通道容错:长文件生成时线路上几分钟没有字节,代理和中间层会掐掉连接。改走 SSE 流式输出,主通道不通自动回退 curl 备用通道,断了退避重试
- 防呆:超大源文件直接拒译防截断;输出被截断一律按失败处理,绝不落半个文件
结果与反思
现在的工作流:改中文,跑一条命令,英文镜像自己追上来;忘了跑,发布闸会把我拦住。这个案例没有客户——交付对象就是这个站本身,你正在读的这个页面,也会被这条管线翻成英文。
诚实边界也要写清楚:有两篇归档教程里的 ASCII 流程图和批处理内容,模型连续多轮拒绝翻译,我最终给这两篇开了显式豁免、记成待办,而不是假装它们过了闸。另外,质量闸只保得住「结构与事实」,保不住「译得地道」——文风好坏还得靠人抽查。闸门的边界必须说清楚,否则它给人的安全感会超过它真实提供的安全。