我没打算把这款游戏本地化,因为它用的是自定义引擎,我的字体支持只是一张字母图片和一些矩形数据。我把排行榜接上了Steam,并在游戏中显示Steam用户名,当时觉得没什么大不了的,直到我某天醒来想到现实世界中的用户名长什么样,以及它们需要支持的所有字符。哦不——。
于是我开始思考怎么本地化那些Steam名字,既然要弄,也许我还能把其余文本也真正本地化。结果发现比我想象的容易得多,而且如果我能搞定,你也能!只有30%的Steam用户用英文浏览,所以我们必须做这些才能触达大多数玩家。
我最终本地化了7种语言,以下是我做的事情!
背景:
这款游戏是一个使用自定义引擎的射击游戏,几乎直接把四边形绘制到OpenGL上。游戏自己管理四边形和桶,底层依赖SDL3(窗口/输入/渲染/手柄)、stb_image(PNG/TGA)和miniaudio(WAV音效+MP3音乐)。小而快。
游戏文本量很少,这有帮助,但UI系统很原始,所以字符长度是个大问题。位图字体图集是从TTF烘焙出来的,大约240个字符串,没有换行。目标语言:日语、简体中文、繁体中文、韩语、法语、俄语、巴西葡萄牙语,另外还要支持更多字符集以显示用户名。
第一步,字符支持!
Steam用户名(个人资料名)是UTF-8编码的,可能包含任何内容。我原本把它们净化成对未知字符显示问号,但遇到非英文名字时这很糟糕。我可以尝试用我的位图字体系统烘焙出那些字符,但那样16384像素纹理上只能放得下600个字形。光中文就需要几万个字形。
这时stb_truetype救了我!那个公共领域库加上像NotoSans这样的大型免费字体,让我几乎能显示任何字符。支持我想要的所有字符意味着需要多个字体和回退链,但我只需要用到这些调用。
打开字体:
- stbtt_GetNumberOfFonts:文件中有多少个字面,TTF通常是一个
- stbtt_GetFontOffsetForIndex:文件中某个字面的字节偏移
- stbtt_InitFont:解析该字面的表数据到stbtt_fontinfo结构
- stbtt_GetFontNameString:从name表中读取字体族名称——用于区分日语、韩语、简体和繁体
每个字面设置一次度量:
- stbtt_ScaleForMappingEmToPixels:使1em等于N像素的缩放因子
- stbtt_GetGlyphBox:字形在字体单位中的包围盒。对字母H运行,得到大写高度,用于匹配字号
每个字形:
- stbtt_FindGlyphIndex:码点转字形索引,0表示缺失
- stbtt_GetGlyphHMetrics:前进宽度和左侧轴承,用于单元格宽度和笔位置
- stbtt_GetGlyphBitmapBox:给定缩放下的像素边界,用于调整单元格大小并检查字形是否垂直适配
- stbtt_GetGlyphBitmapBoxSubpixel:带亚像素笔偏移的相同函数
- stbtt_MakeGlyphBitmapSubpixel:按该缩放和偏移把字形栅格化进你的8位覆盖率缓冲区
渲染用户名时,我先尝试用原始字体渲染每个字形,然后是Noto Sans Bold,再是Noto CJK集合,然后Noto Sans Thai Bold,最后是Unifont。这样几乎能覆盖所有情况。按玩家语言选择CJK字面。Noto CJK在单个文件中包含了日语、韩语、简体和繁体字面,而日语文本如果通过中文字面显示,可能会引起负面评论。
最后时刻最大的坑是,我需要把四边形加高以支持带升部和降部的字符,还要处理组合标记。丢弃组合标记堆叠和零宽字符。
接下来,真正的本地化!
我把所有字符串整理成一张可以发给翻译人员的表格,但我的UI不灵活,所以长度是主要问题。起初我只想翻译成东亚语言,因为文本往往会变短而不是变长,但后来我找到了方法,可以测量并给翻译人员设定每行每个语言的字数上限。关键在于在调试版本中测量每个字符串在屏幕上的像素空间,然后除以英文每字符宽度和该语言的系数。俄语在标题字体中每字符1.22倍宽,CJK在标题中1.8倍、正文中2.45倍。对于欧洲语言,我还得压缩字距才能合理适配。
严格的长度限制让翻译人员很头疼,但他们处理了。有几个字段他们回来说确实需要更多空间,所以我为那些地方做了调整。如果游戏有更多字符串,我肯定需要一个更好的UI系统,能缩放文本或制作滚动字幕。
7种语言共635个单词的总成本是565美元。我还本地化了商店页面,这样费用差不多翻倍。字体为安装包增加了约26MB。我们会看看什么真正值得,但看到界面显示所有这些语言还是挺酷的,希望至少能让更多玩家玩到这款游戏。
你能行的!
评论 (0)