概念页
游戏本地化与 Unicode
Game Localization & Unicode
本地化(国际化,I18N)是把软件改编为其他语言版本,最好从第一天起规划并贯穿每个开发步骤。GEA 介绍了 Unicode 与 UTF-32/UTF-8/UTF-16/UCS-2 编码、char 与 wchar_t、Windows 的字符串 API 约定、主机上的选择,字符串之外的本地化问题,以及核心基础设施——字符串数据库 + 按 ID 查找当前语言字符串的运行时函数,并以 Naughty Dog 的本地化工具为例。
本地化要解决什么(卷 I 6.4.1、6.4.4)
- 所有显示给玩家的英文字符串都要翻译(只在程序内部使用的字符串不必)。要能显示目标语言的全部字形(合适的字体),处理不同书写方向(传统中文竖排,现代中日文常横排从左到右;希伯来文从右到左),并妥善应对译文比英文长得多或短得多的情况。
- GEA 坦言规划本地化「做得不如我们希望的那么多」,并推荐专门的软件本地化书籍(参考文献 [38])以及 Joel Spolsky 的文章《The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets》。
Unicode 与编码(卷 I 6.4.4.1)
- 英语开发者习惯把字符串看作 8 位 ASCII(ANSI)字符数组,对字母复杂、字符众多的语言不够用,于是有了 Unicode:给全球常用语言的每个字符或字形分配唯一的十六进制码位 (code point);在内存中存储时选择一种编码按规则排布比特。
| 编码 | 单位 | 定长/变长 | 要点 |
|---|---|---|---|
| UTF-32 | 32 位 | 定长 | 最简单,字节数 ÷ 4 即字符数;浪费空间:西欧语言平均每字符至少浪费 16 位,而最高码位 0x10FFFF 只需 21 位 |
| UTF-8 | 8 位 | 变长(多字节字符集 MBCS) | 与 ASCII 向后兼容:前 128 个码位(0–127)与 ASCII 相同,各占 1 字节;多字节序列的每个字节最高位都为 1(128–255),纯 ASCII 文本中不会出现,所以无歧义;字节数不等于字符数(书中表述的问题见下方注) |
| UTF-16 | 16 位 | 变长(宽字符集 WCS) | 码位分为 17 个平面,每个 个码位;第一个是基本多文种平面 (BMP),其字符占 1 个 16 位值,辅助平面字符占 2 个 |
| UCS-2 | 16 位 | 定长 | UTF-16 的受限子集,只用 BMP,无法表示 0xFFFF 以上的码位 |
- 原书问题:① 书中说「前 127 个码位」与 ANSI 相同;ASCII 实际是 128 个码位 (U+0000–U+007F),UTF-8 正是把这 128 个码位编码为单字节(Wikipedia「UTF-8」)。② 书中把「ANSI」当作 7 位 ASCII,并称 128–255 的字节「永远不会出现在 ANSI 字符串里」。但 Windows 所说的「ANSI」代码页(如 Windows-1252)本身就是 8 位编码,0x80–0xFF 用于 €、é 等字符(Wikipedia「Windows-1252」),所以含这些字符的「ANSI」文本不能直接当作 UTF-8 解释;向后兼容只对纯 ASCII 成立。③ 书中写 UCS-2 只用「basic multilingual page」,应为 plane(平面)。
- 只含 BMP 字符的 UTF-16(或 UCS-2)字符串,字节数 ÷ 2 即字符数;用到辅助平面就不行了。
- UTF-16(以及 UTF-32)有大端/小端之分,取决于目标 CPU 的原生字节序(见 数值表示与浮点数);存盘时常在文本前加字节序标记 (BOM)。
char 与 wchar_t(卷 I 6.4.4.2)
char用于传统 ANSI 字符串和多字节字符集(包括但不限于 UTF-8);wchar_t是「宽」字符,意在用一个整数表示任意有效码位,所以大小因编译器和系统而异:不支持 Unicode 的系统上可能是 8 位,假定 UCS-2 或用 UTF-16 时是 16 位,以 UTF-32 为宽字符编码时是 32 位。- 要写真正可移植的字符串代码,就得自定义字符类型并提供所需 Unicode 编码的函数库;只针对特定平台和编译器时,可以牺牲一些可移植性,在该实现的限制内编码。
Windows 下的 Unicode(卷 I 6.4.4.3)
- Windows 中
wchar_t专用于 UTF-16,char用于 ANSI 和旧的 Windows 代码页。所以 Windows API 文档里的「Unicode」总是指宽字符集和 UTF-16——这容易混淆,因为 Unicode 一般也可以用非宽的 UTF-8 编码。 - 三套字符串函数:单字节 ANSI(SBCS,老式 C 函数)、多字节 MBCS(主要处理旧代码页)、宽字符 WCS(UTF-16)。命名约定:前后缀「w」「wcs」「W」表示宽字符;「mb」表示多字节;「a」「A」或无前后缀表示 ANSI/代码页。C++ 标准库类似:
std::string与std::wstring。命名并不总一致。
| ANSI | WCS | MBCS |
|---|---|---|
strcmp() | wcscmp() | _mbscmp() |
strcpy() | wcscpy() | _mbscpy() |
strlen() | wcslen() | _mbstrlen() |
- 转换函数如
wcstombs()(按当前 locale 把宽 UTF-16 转为多字节)。 - TCHAR 预处理技巧:「ANSI 模式」下
TCHAR是char,「Unicode 模式」下是wchar_t;宏_T()在 Unicode 模式下把 8 位字面量变为宽字面量(L"...");一套无前后缀或带「t」「tcs」「T」前后缀的「伪」API 函数会自动变成相应的 8 位或 16 位版本。
主机上的 Unicode(卷 I 6.4.4.4)
- Xbox 开发包 (XDK) 几乎所有字符串(连文件路径等内部字符串)都用 WCS,处理一致,但 UTF-16 较费内存。
- Naughty Dog 全引擎使用 8 位
char字符串,用 UTF-8 处理外语。 - GEA 结论:选哪种编码并不特别重要,关键是在项目中尽早选定并始终如一。
字符串之外的本地化问题(卷 I 6.4.4.5)
- 语音等音频要翻译;纹理上画的英文要翻译;许多符号在不同文化中含义不同,连禁烟标志都可能被误解;各市场的分级界限不同——例如日本的 Teen 级游戏不允许出现任何血液,而北美认为少量红色血迹可以接受。
- 字符串方面:要管理全部人类可读字符串的数据库以便可靠翻译;按用户安装设置显示正确语言;格式可能完全不同(竖排、从右到左);长度差异很大;还要决定发行一张含全部语言的蓝光盘,还是按地区发行不同光盘。
核心基础设施:字符串数据库与按 ID 查找
- 最关键的两个组件:人类可读字符串的中央数据库,以及游戏内按 ID 查找字符串的系统。
- 例:HUD 上的「Player 1 Score」「Player 2 Score」「Player one wins!」「Player two wins!」在数据库中以开发者能看懂的 ID
p1score、p2score、p1wins、p2wins存储,每种语言一列(法语为「Joueur 1 Score」「Joueur un gagne!」等)。 - 数据库格式随意:可以简单到存成 CSV 的 Excel 表,也可以复杂到完整的 Oracle 数据库;对引擎而言只要能读入 ID 和各语言的 Unicode 字符串即可。但对组织很重要:有内部翻译的小工作室用网络盘上的 Excel 就够了,在英国、欧洲、南美、日本都有分部的大工作室更适合分布式数据库。
- 运行时提供
getLocalizedString(const char* id)返回当前语言的 Unicode 字符串(原书问题:书中示例声明为wchar_t getLocalizedString(const char* id);,只返回单个宽字符,应返回字符串指针,如const wchar_t*;按 Naughty Dog 的 UTF-8 方案则应为const char*),如renderer.displayTextOrtho(getLocalizedString("p1score"), score1Pos)。 - 「当前语言」可在安装时由配置固定,也可在游戏内菜单中切换;实现可以简单到一个全局整数,表示读字符串表的哪一列(第 1 列英语、第 2 列法语……)。配置机制见 引擎配置系统。
- 纪律:程序员绝不能直接向用户显示原始字符串,必须用数据库 ID 调查找函数。
案例:Naughty Dog 的本地化工具(卷 I 6.4.4.6)
- 后端是服务器上的 MySQL 数据库,Naughty Dog 内部和外部翻译公司都能访问;前端是 Web 界面,可浏览全部文本和音频资产、编辑内容、提供各语言翻译、按 ID 或内容搜索。
- 每个资产要么是字符串(菜单或 HUD 用),要么是带可选字幕的语音片段(游戏内对白或过场);每个资产有唯一 ID,用哈希 string id 表示(见 字符串与唯一标识符)。
- 查字符串返回可直接显示的 Unicode(UTF-8)字符串;播放对白时按 ID 查音频片段,再在引擎中查对应字幕,字幕同样以 UTF-8 字符串返回。
- 书中截图示例:搜索
MENU_NEWGAME得到主菜单的「NEW GAME」,点「Text Translations」按钮可录入或编辑其各语言译文;语音资产BADA_GAM_MIL_ESCAPE_OVERPASS_001(「We missed all the action」)及其多语言录音。
与 UE5 的对照
UE5 的具体本地化管线见 本地化;容器与字符串 记录 UE 要求所有面向玩家的文本使用 FText(关联本地化数据),这正是 GEA「绝不直接显示原始字符串、一律经由 ID 查找」原则在 UE 中的体现。
出处
- Game_Engine_Architecture_4Ed_-_Jason_Gregory.pdf
- Jason Gregory《Game Engine Architecture》第 4 版(2026)卷 I 第 6 章 6.4.1、6.4.4 节
相关页面
游戏开发字符串与唯一标识符Strings & Unique Identifiers字符串在 C/C++ 中只是字符数组,变长意味着要么硬编码长度上限、要么动态分配;比较和复制都是 O(n)。引擎内部大量用字符串做资源名和对象 ID,处理不当会严重拖慢游戏。GEA 的建议:运行时代码尽量避免字符串类,若用则了解其成本并按引用传递;路径类是值得专门做的例外;对象与资产的唯一标识用哈希字符串 ID(string id),兼得字符串的可读性与整数的比较速度。游戏开发 · UE5本地化LocalizationUE 的本地化以 FText 类型为中心,国际化(数字、日期、复数规则、文化回退)依赖 ICU 库。可本地化的文本由命名空间、键、源字符串三部分标识;本地化管线按目标 (Localization Target) 依次收集 (Gather) 到 manifest、翻译进各文化的 archive(通常经 PO 文件导出导入)、编译成运行时加载的 .locres。Localization Dashboard 是管理这条管线的推荐工具。游戏开发 · UE5容器与字符串Containers & StringsUE 不用 STL,而用自带的值类型容器:TArray(连续数组)、TMap/TMultiMap(哈希映射)和 TSet(哈希集合),都假定元素可按字节直接搬移。字符串分三种:FName 存索引、比较便宜,FText 用于可本地化的界面文本,FString 可以修改但开销最大。选错类型的代价主要是性能和本地化。游戏数学数值表示与浮点数Numeric Representation & Floating Point游戏引擎里到处是数值,所以要清楚它们在机器中如何存储:二进制和十六进制、补码整数、定点数、IEEE-754 浮点数(量级与精度此消彼长、次正规数、机器 epsilon、ULP),以及多字节值的大小端问题和安全的类型双关 (type punning) 方法。书中一个直观的例子是:用 32 位浮点数记录以秒为单位的游戏时钟,大约 12.14 天后,加 1/30 秒就不再改变它的值。图形学文本渲染Text Rendering文本中一个像素的变化就可能把 "l" 变成 "1",因此字形渲染格外讲究。主要路线有:按字号预生成字形位图纹理(配合微调和子像素渲染)、有符号距离场纹理(可在三维表面任意缩放旋转)、以及在 GPU 上直接求值字形曲线。游戏开发引擎配置系统Engine Configuration游戏引擎总会积累大量可配置选项:有的通过选项菜单暴露给玩家(画质、音量、手柄设置),有的只供开发团队使用、发售前隐藏或去除(如玩家最大行走速度在开发期可调、发售前改成硬编码)。GEA 讨论选项的存取方式(文本文件、压缩二进制、注册表、命令行、环境变量、在线档案)、全局与每用户选项,以及 Quake cvar、OGRE、Naughty Dog 三个真实例子。游戏开发资源管理器Resource Manager游戏由网格、材质、纹理、着色器、动画、音频、关卡布局、碰撞与物理参数等大量资源(也叫资产、媒体)构成。内存有限,所以引擎要保证每个资源在内存中只有一份(五个网格共用一张纹理时只载入一份)。每个引擎都有某种资源管理器:有的是统一集中的子系统,有的散布在多年间由不同人写的各个子系统中,但职责和要解决的问题是一样的。本页讲运行时部分:职责、文件组织与归档、文件格式、资源 GUID、资源注册表、生命周期(引用计数)和资源的内存管理;交叉引用与加载后初始化见 资源交叉引用与加载后初始化,离线工具链见 资产管线与工具链。图形学覆盖层与用户界面渲染Overlays & User Interface RenderingHUD、武器选择界面、各种菜单等图形用户界面,加上调试绘制,都在主 3D 场景渲染完之后才画,所以统称覆盖层 (overlay)。它们一般关闭深度测试、用屏幕空间四边形和正交投影绘制,由文字、位图、嵌入视频和矢量图形几类二维图元组成(GEA4 卷 II 12.8)。