本文简述了自己在汉化游戏 《卡牌之声:残次的巫女(Voice of Cards: The Forsaken Maiden)》过程中的经历和感想
缘起
在这个游戏竞相追逐狂拽酷炫的年代,却总有一些游戏选择特立独行,《卡牌之声》系列便是其中之一,游戏虽然以卡牌为主题,内核其实就是王道 JRPG,系统简单传统,节奏也非常缓慢(有时候甚至有些拖沓),但自己对这种气质的游戏却颇有好感,目前该游戏系列已经出品了三款作品(分别是 《龙之岛》,《残次的巫女》和《被囚禁的魔物》),《龙之岛》有汉化版本,自己先前也通关了,虽然游戏流程简短,但仍觉得饶有趣味,之后想继续尝试《残次的巫女》和《被囚禁的魔物》的时候,发现暂时都还没有汉化,没办法,以英文方式打开了《残次的巫女》一段时间,终归还是觉得味道不对,注意到游戏是基于 Unity 引擎开发的,而 Unity 引擎的资源解包和导入目前应该没有什么技术难点了(甚至都有工具支持),唯一有门槛的文本翻译,目前似乎也能交给大语言模型来解决了(当然,想保持翻译效果的流畅统一估计还不太行,但翻译质量于我并不是重点,自己只要求译文不影响游戏理解便可以了)
于是乎说干就干,自己便开启了《残次的巫女》的汉化工作 ~
初步
首先当然是需要了解下当前的 Unity 游戏汉化方法了,网上的相关资料还是很多的,主体看下来目前 Unity 游戏汉化主要有两种方式:
- 资源解包替换
第一种自然是传统的资源解包替换了,我接触到的汉化游戏基本都是这个模式,只是早期游戏格式五花八门,汉化人员通常需要写定制的程序来完成解包和替换工作,不过目前 Unity 的资源解包和替换都已经有比较完善的工具了(譬如 AssetStudio),一般不再需要我们去写另外的代码程序了.
- 实时文本翻译
第二种是采用 Hook 等方式拦截 Unity 的文本渲染请求,然后调用翻译引擎(谷歌翻译等)来实现实时翻译,通用性非常强,使用上也很简单,但是限制也非常大,个人不太喜欢这种汉化方式,有兴趣的朋友可以继续深入了解(XUnity.AutoTranslator)
比较来看自己还是倾向于传统汉化方式,于是使用资源工具搜索了一下游戏中相关的文本资源(即日文资源,这次汉化基本上就是想把原先的日文替换为中文),发现需要修改的地方其实不多,主要就两块内容:
- asset_text_jp
这个文件包本质上就是几个 CSV 文件,采用的都是 '编号,文本' 的格式,导入和导出也比较简单,基本没有遇到什么问题.
- sharedassets0.assets
主要是要修改这个文件包中的字体资源,一开始我还天真的以为游戏是不是使用了系统字体,这样我就不用去替换字体资源了,后面发现游戏使用的还是 TMP (TextMesh Pro) 字体资源(自然的也没有包含中文),没办法,自己还是找了个免费字体(我选了MiScans)处理了下,替换字体贴图没什么难度,但替换字体脚本还是需要花一些精力的,有兴趣的朋友可以参考相关文章(譬如这里)进行了解,这里就不再赘述了,但总的来说这一步也没卡多少时间.
困苦
然后我就来到了文本翻译环节,一个我一开始以为直接把文本塞给 AI 就能坐等完工的环节,事实证明自己还是太"天真幼稚"了 ...
最大的问题出在文本量上,简单统计来看,游戏的文本量大概在 12000 句左右,直接扔给大语言模型,没翻几句就直接报上下文过长,然后就罢工了,于是自己整了个本地的 Sakura 模型配合 AiNiee 跑了跑,发现确实是可行的,尽管时间上有些慢,但至少所有文本都翻译完成了,但当我欢心鼓舞的将翻译好的文本导入到游戏里的时候,却发现游戏卡死了 ...
尝试开启游戏日志看了下,没啥有用信息,于是开始肉眼对比原文和译文,没看几下就发现了一个大问题:
文本格式 ! 文本格式 ! 文本格式 !
原文中有很多特殊的文本格式符号,譬如 <highlight>, {CARDNAME:*} 等等,正常来讲是不用翻译的,但在译文中要么就整体消失了,要么就被替换为了其他符号,只有很少的文本格式符号能够正确保留下来,更不用提一些 符号(譬如双引号(")) 不对称之类的问题了,把这些译文导入游戏,就算不卡死,显示效果也可想而知 ...
尝试添加"保留文本格式符号"的翻译提示词重新翻译,几轮(每轮都要花不少时间 ...)下来问题依旧,情况似乎有些一筹莫展 ...
最终自己还是放弃了跑本地模型来翻译文本的方式(毕竟本地模型还是能力有限),考虑还是使用外部模型来处理,之前所说的上下文过长问题,就通过人工拆分的方法来改善,一开始自己就简单拆成了几份文本,发现效果仍然不理想,最后干脆细拆了很多份文本,并且按单独对话的方式喂给了外部模型(如果在一次对话中翻译多份文本的话,最后还是会遇到因上下文过长而导致翻译质量陡降的问题),并且添加了很多诸如"保持文本格式"等限制提示词,等所有文本翻译结束之后,再进行最终的人工合并.
几轮尝试过后,确实获得了不错的译文效果,肉眼目测下来也没发现什么明显错误,文本格式之类的符号似乎也保持的不错,为了完整验证我还写了些测试代码,最终也确实没找出什么问题,于是自己又一次欢心鼓舞的将翻译过的文本导入到游戏里,却发现游戏又一次卡死了 ...
老眼昏花的对比了几轮文本,实在找不出有什么问题;程序侧也验证了多次,也没有报什么疑问,万般无奈下,最终还是不得不拿出了大杀器: 二分(试错)法 ...
既然文本导入会导致游戏卡死,那么就以二分试错的方式来定位出错的文本段,虽然一定可以定位到错误,但是却需要付出极大的时间和精力,不到万不得已自己确实不太想用,但目前也实在想不到更好的办法了 ... 二分试错的过程比较痛苦,这里就不去回忆了,总之结果上确实找到了问题所在:
编号重复 ! 编号重复 ! 编号重复 !
前面提过,原文是由 "编号,文本" 的方式存储的,翻译时自然也不用去翻译编号,但实际上,在大语言模型翻译过程中,会随机的修改某个编号的数值,规律上看的话似乎是会根据前面编号来做修改(大概就是会出现 ABA 这种编号调整模式),当然,由于大语言模型内部是黑盒,确实也不知道实际原理,但总之会导致部分编号重复(庆幸的是概率不高),游戏中读取到重复的文本编号应该是简单做了报错处理,遂而导致了卡死.
知道了问题原因自然就好办了,简单写了几行程序来检查重复编号,然后人工修复即可,期间还发现了几百项引号的格式问题(严格来说不算是文本格式符号),想了想还是不找 AI 去返工了(免的又出其他问题),咬咬牙人工修复了.
最后将修正过的文本导入到游戏,游戏终于正常运行了 !真是不容易啊 ...
结果
虽然文本都翻译为了中文,但毕竟还是属于机翻,总体效果只能说差强人意,本着持续改善的原则,建了个相关 repo (汉化包资源也在里面), 有兴趣的朋友可以继续帮忙修缮译文 ~
尽管过程坎坷,但最终结果还是不错的,自己学到了一些知识,后面还可以愉快的游戏 ~


