最近我用 AI Agent 完整跑通了一款日文 Unity 游戏的简体中文化。这个过程里,最值得写下来的并不是“AI 会翻译”——这件事已经不新鲜了。真正有意思的是:当我们把“汉化”这种高度工程化、又充满隐性陷阱的任务交给 Agent 端到端执行时,它到底要处理哪些技术环节?每个环节会在哪里翻车?人和 Agent 又应该如何分工?
这款游戏只是一个入口。下面提到的许多经验,不只适用于 Unity 游戏汉化,也可以迁移到更广义的“逆向 + 改造”类任务里。
一件事,其实是六件事
刚开始做的时候,很容易把汉化想象成一个单一动作:把日文翻成中文。真正拆开之后才发现,它至少由六个相对独立的技术模块串起来,而“翻译”反而是其中最不费力的一环:
- 文本抽取:从二进制资源里,把真正需要翻译的文本干净地分离出来;
- 机器翻译:在质量和稳定性可控的前提下,完成上万条文本的翻译;
- 资源回注:把译文准确写回原资源,做到字节级可控;
- 资源系统适配:让引擎的资源校验机制接受被改动过的文件;
- 字体处理:确保译文真的能在屏幕上正常显示;
- 打包与签名:把改造后的产物重新组装成可安装的形态。
这六块之间耦合得很紧。抽取规则错一点,回注就会对不上;回注改了字节,资源校验就可能失败;翻译全部完成,字体如果不支持简中字符,最终还是满屏豆腐块。更麻烦的是,很多错误不会在发生的那一刻暴露,而是要等到很后面的真机运行阶段才炸出来。
这也是它适合交给 Agent、同时也最考验工作流设计的地方。
模块一:文本抽取——先分清“文字”和“非文字”
这款游戏的剧本本质上是一段段类 Lua 脚本。函数调用参数里混着两类内容:一类是要显示给玩家看的文本,另一类是绝对不能动的内部键,比如角色名、表情键、资源路径、数值参数。
Talk("こくご、むずかしい……", 0) -- 第一个参数是台词
SetExpression("chara01_01_1_03", "アルト") -- 这里是资源键,不能翻
ViewSelect({"我也不擅长", "试着代入主角"}) -- 数组里是选项文本
如果不加区分地把所有字符串都送去翻译,角色名、表情键、资源路径就会被一起改掉,游戏轻则显示异常,重则直接加载失败。
所以抽取阶段的核心,不是“尽可能多地找出字符串”,而是先建立一张规则表:声明每个函数的哪些参数位是显示文本,哪些参数位是内部键。抽取器只处理被规则表标记为文本的位置,其他内容一律原样保留。
这件事 Agent 做起来很顺手。它可以先扫描全部脚本,统计出现过的函数和参数模式,据此生成规则表;然后再用规则表反过来抽取文本。在这款游戏的 4973 个脚本里,可显示文本一共出现了 42375 处。经过去重后,也就是同一句话只翻一次,最后只剩 35071 条唯一字符串,少翻了大约 17%,一致性也更好。
这一阶段的经验很明确:抽取时就要把“键”和“文本”彻底分开,同时建立翻译记忆做去重。前者保证不误伤资源,后者既节省成本,也能让同一句话在全局保持一致。
模块二:机器翻译——执行边界比模型选型更关键
翻译本身看起来是最简单的一步,但它反而是这个项目里反复翻车最多的一块。
最初的方案是调用在线翻译接口,看起来省事,实际踩了两个坑。
第一个坑是限流。并发稍微一高,请求就开始失败。更糟糕的是,当时脚本里有一段“翻译失败就回退原文”的兜底逻辑,结果三万多条文本里有相当一部分悄悄退回了日文原文。
第二个坑是校验不够实质。合并阶段只检查占位符和换行有没有保持,并不检查“这句话到底有没有被翻译成中文”。于是那些没翻成功的文本一路绿灯通过,直到真机启动后,才发现界面和台词里仍然残留大量日文。
这件事给我的教训有三点。
第一,任何“失败回退”都必须计入失败率,并且显式报告。静默失败是最危险的设计,因为它会一路潜伏到流程最后,等你发现时已经很难定位源头。
第二,校验不能只查格式对不对,还要查事情有没有真的做成。比如可以统计译文中残留的日文假名比例,超过阈值就判定该分片翻译失败,要求重做。
第三,对于质量和稳定性都敏感的任务,一个本地可控的专用模型,往往比一个不可控的在线通用接口更靠谱。
于是第二版方案改成本地翻译:在本机用 llama.cpp 跑一个专门做多语翻译的小模型。我用的是腾讯混元的 Hunyuan-MT,一个 1.8B 参数量的多语翻译模型,GGUF 量化后,单卡十几 GB 显存就能跑。
这个模型给我的印象不错。它是专门为翻译训练的,对日译中这类东亚语种处理得比较自然。体量不大,但用在游戏台词这种日常文本上已经很能打。换句话说,对于这类高重复、强上下文但句子本身不复杂的任务,与其每条都丢给通用大模型,不如用一个专用小模型来顶住主流程:质量够用,本地可控,成本也低。
整套翻译流程通过 OpenAI 兼容接口接入,再用线程池并发处理分片。这里有几个细节值得记录。
1. 提示词模板要遵循模型官方格式
这类小模型对 chat template 很敏感。模板套错了,轻则答非所问,重则直接拒答。
一开始我们用了通用对话模板,模型反复回复“抱歉无法提供”,一度以为是内容审查问题。后来换成模型卡里推荐的翻译专用指令模板,立刻恢复正常。
将以下文本翻译为<目标语言>,注意只需要输出翻译后的结果,不要额外解释:
<待翻译文本>
这个模板看起来很朴素,但有两个关键点。
第一,要明确要求“只输出翻译结果,不要解释”。否则小模型很容易自作主张补一句“这句话的意思是……”
第二,目标语言最好写语言全称,比如“中文”“英语”,不要写成 zh、en 这类代码。
模型卡里还提供了几种进阶模板,例如带术语表的、带背景信息的,以及专门强调“保留分隔符数量,不要翻译或转义这些符号”的版本。后面这个对处理占位符很有参考价值。
采样参数也直接使用官方推荐值,没有额外乱调:
temperature: 0.7
top_p: 0.6
top_k: 20
repetition_penalty: 1.05
翻译任务本来就应该偏确定性。这组参数足够稳定,温度再高反而容易让小模型“发挥”,造成改写、漏译或语气漂移。另外,这个模型没有默认 system prompt,就不要硬塞一个进去。按官方说明留空即可。
2. 占位符先切分,再翻译
游戏文本里经常会混入占位符和控制标记,比如 {Name}、<color=...>、\n。这些东西不应该交给模型处理。
更稳的做法是:先用代码把这些受保护标记从文本中切出来,只把中间的纯文本片段送给模型;翻译完成后,再把标记按原位置拼回去。
这样占位符数量天然守恒,后续校验也更容易通过。相比在提示词里反复叮嘱模型“不要动这些符号”,代码切分是确定性的。小模型未必每次都听话,但代码不会临场发挥。
3. 翻译后再做术语后处理
模型输出之后,还需要再过一遍术语表,强制统一角色名、地点名和专有名词的译法。
最终三万多条文本全部翻完后,残留假名比例不到 1%。其中大部分还是韩文、零宽空格之类的脏数据,而不是正文漏翻。
模块三:资源回注——做到字节级无损
资源回注有一条铁律:没有改动的文本,重新打包后必须与原文件逐字节一致。
否则,一旦真机运行出问题,你根本无法判断是“译文改坏了”,还是“打包工具顺手动了别的地方”。
具体做法是用“资源名 + 字符串序号”精确定位每一处要替换的字面量。只改目标位置,其余字节全部原样保留。
为了证明这件事可靠,还需要做一个回环测试:在“不改任何文本”的前提下,跑完整条回注流程,然后断言输出文件与原始资源完全一致。只有这条测试通过,后续真正写入译文时,才有资格相信“流程只改了该改的东西”。
这种“先证明无损,再做改动”的思路,是让 Agent 在二进制层面工作时保持可靠的关键。它不需要凭感觉小心翼翼,而是有一个可以自动验证的正确性基线。
模块四:资源系统适配——最隐蔽的坑
Unity 的 Addressables 资源系统在加载资源包时会校验 CRC。一旦资源包被改动,CRC 对不上,就需要在资源目录文件里把对应条目的校验值置零,让系统跳过校验,同时更新记录的资源包大小。
第一次这么改完,真机直接黑屏。日志显示资源目录解析失败,资源包路径被解析成了空字符串。
根因非常隐蔽。
这个资源目录文件里有一段 Base64 编码的二进制块。块内每个对象的结构大致是:
类型 + 程序集名 + 类名 + 32 位长度前缀 + UTF-16 编码的 JSON
更麻烦的是,每个对象的绝对字节偏移还被记录在另一处索引表里。
我们把 CRC 从一个十位数改成了 0,这段 JSON 短了十几个字节。结果长度前缀没有同步更新,后面所有对象的偏移也全部错位。引擎继续按旧偏移读取,自然读到一段越界的垃圾数据,最后解析失败。
正确做法不是“把 JSON 改成更短的 JSON”,而是做长度保持式修改:改完 JSON 后,在闭合括号前用空格补齐,让这一块的字节长度与原来完全一致。这样长度前缀和所有偏移都不需要变化,只有 CRC 和资源包大小这两个字段的取值发生改变。
这件事可以抽象成一条通用经验:面对“序列化二进制 + 绝对偏移”这类结构,任何修改都要优先保持长度,不能把它当普通文本随意增删。
更要命的是,这类 bug 不一定会在自己的解析脚本里暴露。脚本自己读、自己写,通常都是自洽的。真正会炸的,是目标引擎按它自己的读取逻辑加载资源时。
这也直接引出了下一条原则:真机验证不能省。
模块五:字体处理——翻译让文字“对”,字体决定文字“能不能显示”
黑屏修好,中文文本也成功写进资源之后,又出现了新问题:简体中文里的很多常用字显示成了方块。
“这、吗、你、们、为”之类全是豆腐块,而日文汉字却能正常显示。
排查后发现,游戏使用的是 TextMeshPro 的静态 SDF 字体图集。它的字符集是日文标准字符集,覆盖了假名和日本汉字,但简体中文独有的字形并没有被烤进图集。
量化之后问题更清楚:译文一共用到了 2858 个不同字符,其中 892 个不在原字体里。这 892 个字占全部渲染字符的将近四分之一。满屏豆腐块,也就不奇怪了。
静态 SDF 图集是在打包期生成好的,运行时不会动态补字,也没有可借力的备用字体表。干净的解法只有一条:重新生成字体资源。
这个过程本身又分成几步。
首先,要选一款同时覆盖日文假名、日文汉字和简体中文,并且风格接近原字体的字体。它不仅要“能显示”,还要和原游戏的视觉风格协调。
其次,用 FreeType 的 SDF 光栅器,按原素材参数逐字渲染。这里的渲染参数需要和原图集对齐,包括扩散半径、采样像素、字形紧框与内边距的关系。
然后,把完整字符集排进新的图集里,同步重写 glyph table 和 character table,保证图集、字形表、字符表三者自洽。
最后,把新图集写回纹理,重新打包。这个新的字体资源,同样要走上一节提到的资源目录修补流程。
字体标定过程中也有不少容易踩坑的细节。
比如 FreeType 渲染出的 SDF 位图会比字形紧框大一圈,每边多出扩散半径对应的像素。而图集里记录的字形矩形是紧框,真正贴进去的却是带扩散边缘的完整位图。相邻字形的扩散边缘还允许互相重叠。理解了这层关系,才能把图集排得足够紧,不至于过早溢出。
再比如 Unity 的纹理坐标是自下而上的,写回前要记得垂直翻转。
字体这一块给我的体会很深:它是本地化里最容易被低估,却几乎绕不过去的一环。凡是使用 SDF 或位图字体的引擎,尤其是大量 Unity 游戏,只要更换语种,就很可能需要重制字体图集。
翻译只是让文字内容“对”,字体才决定这些文字能不能被玩家看见。
顺带一提,字体的粗细和风格也有审美空间。我们一开始用了一款偏细的黑体,显示没问题,但笔画偏单薄。后来换成更厚一些、更有亲和力的字体,并且对加粗字重做了轻微轮廓加粗,才把原本“粗体 / 中等”两档视觉层次保留下来。
这类取舍,正是适合由人来拍板的地方。
模块六:打包与签名——最后一公里
把改造后的资源重新组装成可安装的形态,也有一些固定但绕不开的工程细节。
一个应用的多个分包,比如主体包、架构相关包、资源包,必须用同一个密钥签名,否则系统会直接拒绝整组安装。
自签名产物无法覆盖安装来源不同的旧版本,通常需要先卸载旧版本。
某些定制系统在通过调试桥安装时,还会有额外拦截,需要放开对应限制,或者调整安装参数。
这些问题本身不复杂,但都很琐碎,而且每次手动处理都容易漏。最好的方式是把它们固化进脚本,一次写好,之后让 Agent 按流程跑。
把这套流程交给 Agent,真正意味着什么
回过头看,真正让这件事能交给 Agent 的,不是某个神奇提示词,而是整条流程的结构。
我们把任务拆成编号清晰的阶段。每个阶段的输入输出都是磁盘上的文件,每一步都可以单独重跑:
01 抽取 -> source.jsonl / units.jsonl
02 分片 -> chunks/chunk_XXX.jsonl
03 说明书 -> _brief.md / _glossary.json
04 翻译 -> translated/chunk_XXX.jsonl
05 合并校验 -> translated.jsonl
06 回注 -> build/<bundle>
07 目录修补 -> build/catalog.json
08 打包签名 -> output/*.apk
文件即接口、编号阶段、幂等可重跑,这是让 Agent 稳定工作的地基。
它出错时不会污染上游,重跑时不会重复劳动,断点续跑也天然成立。在这个结构之上,有几条协作原则被反复验证。
1. 每个阶段都要有自动校验,而且要查实质
校验不能只查占位符、换行、JSON 格式这些表层问题,还要检查任务是否真的完成。
翻译阶段要查残留原文比例,字体阶段要查字形覆盖率,回注阶段要查无修改时是否逐字节一致。静默兜底是最危险的设计,因为它会把失败藏到流程最后。
2. 端到端真机验证不能省
解析脚本“自己读自己写”永远是自洽的。只有目标引擎和真机运行,才能暴露偏移错位、资源校验失败、字形缺失这类问题。
这个项目里两个最大的坑——资源目录黑屏和字体豆腐块——都是在真机上才真正暴露出来的。
3. 一个思路失败两次,就该换思路
在线接口反复限流回退时,继续调并发参数只是局部打补丁。更有效的做法是承认根因不在参数,而在方案本身,于是直接换成本地专用模型。
Agent 很擅长执行,但人需要及时判断:现在是该修补,还是该换路。
4. 人定方向,Agent 做执行
选哪个翻译模型、用哪款字体、字体要多粗,这些带审美和取舍的问题,应该由人来拍板。
抽取、回注、打包、失败重试、日志比对这些重复且边界明确的劳动,则交给 Agent 反复跑。
5. 保留可观测性
每个阶段都应该输出报告:抽取了多少文本、去重比例是多少、翻译失败率是多少、残留原文比例是多少、字体覆盖率是多少、改了哪些资源、CRC 和文件大小有什么变化、签名指纹是什么。
这些信息不是装饰。出问题时,Agent 能靠它们自查定位,而不是盲目地改来改去。
结语
这次汉化里,最有意思的部分从来不是“AI 把日文翻成了中文”。那只是六个模块里相对轻松的一环。
真正有意思的是,一个 Agent 能自己搭起流水线,自己抽取文本,自己回注资源,自己读真机日志定位黑屏,再自己重制一套字体图集。一个传统上需要多种专业工具、大量手工操作,而且很容易翻车的任务,最后被收敛成了一条可重跑、可验证、可观测的工程流程。
游戏只是引子。真正可迁移的,是这套协作范式:
结构化阶段 + 实质性校验 + 端到端真机验证 + 人定方向、Agent 做执行。
它适用于任何环节多、耦合强、失败信号滞后的改造类任务。
下次再遇到一个看起来又脏又累、还特别容易出错的工程任务,第一反应也许不该是立刻挽起袖子自己上,而是先想清楚一件事:
怎样把它拆成一条 Agent 能稳定跑、也能自己发现自己跑错了的流水线?