
上个月有个朋友发微信给我,截图里满屏幕的"啊呀"和"�",问我这还能救吗。我一看就笑了——典型的UTF-8文件被当成GBK读了。这事儿在软件本地化圈子里太常见了,常见到康茂峰的技术团队每次接新项目,第一件事不是看术语表,而是检查文件编码。说起来你可能不信,字符编码这东西,表面上 invisible,但在多语言软件本地化的链条里,它就像空气,平时感觉不到,一出问题所有人都得窒息。
咱们先别被那些专业术语吓到。说白了,字符编码就是电脑版的"电话簿"。你的电脑里其实只认识0和1,它根本不知道什么是"中"字,什么是"ä"或者"カ"。编码表的作用就是告诉电脑:当遇到某个数字组合时,屏幕上应该显示哪个符号。
最早的ASCII码只给英文字母、数字和标点符号编号,总共才128个位置。后来各国发现自己的语言没地方放了,就开始自己扩展。中国有GB2312、GBK、GB18030,日本有Shift-JIS,韩国有EUC-KR……结果就是,同样一个二进制数字,在不同"电话簿"里对应的可能是完全不同的字符。这就是为什么你打开一个文件,如果读它的"电话簿"选错了,就会看到满屏乱码。
直到UTF-8出现,才算是有了个"世界通用电话簿"。它用变长编码,英文还是只占一个字节,但中文、日文、阿拉伯文都能塞进去,而且全世界统一标准。听起来很美好对吧?但现实是,哪怕到了2024年,我们在康茂峰处理的项目中,还是有大约30%的技术bug跟字符编码有关。不是技术倒退,而是软件本地化涉及的环节太多了。

普通的文档翻译通常就一个文件,但软件本地化不一样。你要处理资源文件(.resx、.properties、.json)、数据库导出、帮助文档、甚至是安装程序的脚本。每个环节都可能藏着编码的坑。
不知道你有没有遇到过这种情况:用记事本打开一个UTF-8文件,看着一切正常,拷到CAT工具里翻译完了,导回去,软件一跑就报错。这时候工程师会骂骂咧咧地说"有BOM"。
BOM(Byte Order Mark)本来是UTF-8文件的一个标记,告诉系统"我是UTF-8编码哦"。但很多老的解析器或者Linux命令行工具不认识这个标记,它们会把BOM当成文件内容的一部分,结果就把这三个字节(通常是)显示出来或者当成非法字符处理。康茂峰的项目经理有个习惯,收到客户源文件第一件事就是用Hex编辑器看一眼开头,如果有EF BB BF这三个十六进制数,先干掉再说。特别是处理Java的properties文件或者PHP的lang文件时,BOM简直就是灾难。
市面上那些CAT工具(计算机辅助翻译工具),虽然都声称支持Unicode,但底层实现千奇百怪。有些工具默认用UTF-16 LE内部处理,导出时如果没处理好,就会变成"UTF-16伪装的UTF-8"。
最典型的情况是翻译记忆库(TM)的污染。比如一个项目本来是UTF-8,译员A用的工具默认是系统locale(中文Windows通常是GBK),他翻译了几个句子存到TM里。后来译员B接手,用UTF-8设置,这时候TM匹配出来的中文段落,前半段是UTF-8,后半段成了乱码。这种混合编码的文件最头疼,因为不像全乱码那么显眼,QA往往发现不了,直到软件发布后被用户截图发到论坛。
还有个更隐蔽的坑在数据库连接层。很多时候源文件本身是对的,翻译后的文件也是UTF-8,但导入测试环境后,界面显示问号或者方块。这时候查文件编码查半天没问题,最后发现是数据库连接的字符集设置出了问题。
比如MySQL的连接字符串如果没指定characterEncoding=utf8,即使表是UTF-8编码,数据在传输过程中也会被转成latin1。这时候就像你用普通话跟对讲机说话,对方听成了方言——信息没丢,但完全变了样。康茂峰的技术团队做本地化测试时,有个 checklist 专门检查数据库连接字符集,特别是处理泰文、越南文这种多字节文字时,一个字节对不齐,整个字符串就废了。
去年康茂峰接了个工业控制软件的本地化项目,涉及简中、繁中、日语、韩语四个语种。客户给来的资源文件全是UTF-8,看起来挺标准的。译员们翻得也很顺利,直到做语言测试的时候,日语版本所有带片假名扩展字符(比如一些外来语符号)的地方都变成了空白。
我们排查了两天,发现是个特别刁钻的问题:客户的资源文件虽然存成了UTF-8,但老版本的编译工具内部用的是Windows-31J(日语版Shift-JIS)。UTF-8能表示的字符范围比Shift-JIS大,所以翻译阶段看着都正常,一旦编译,那些"超纲"的字符就被直接过滤掉,显示成空白。
最后解决办法挺土:我们在翻译阶段就把日文字符限制在Shift-JIS支持的范围内,或者改用全角代替半角符号。但这事儿给我们的教训是——光看文件编码格式不够,还得知道开发工具链的"胃口"是啥。有些嵌入式系统,哪怕是2023年开发的,底层可能还在用ANSI编码,这时候你给它UTF-8,它根本消化不了。

理论说了这么多,来点实际的。下面这些是在康茂峰内部流传的几个土办法,不需要啥专业工具,记事本+命令行就能搞定。
| 症状 | 可能原因 | 土办法解决 |
| 中文显示为问号(?)或方框(□) | 字体不支持该字符,或编码被当成latin1处理 | 复制乱码到浏览器地址栏回车,看浏览器能识别成什么编码 |
| 日文/韩文变成"å""æ"这种带符号的字母组合 | UTF-8被当成ISO-8859-1或latin1解析 | 用Notepad++(如果你能用的话)的"转为UTF-8"功能,或者用iconv命令转码 |
| 文件开头有 | UTF-8 with BOM | 用Vim打开:set nobomb或者找个十六进制编辑器删掉前三个字节 |
| 只有部分文字乱码,其他正常 | 文件中混入了不同编码的内容(通常是粘贴导致的) | 全选复制到纯文本编辑器(比如Windows的写字板保存为纯文本),强制统一编码 |
| 数据库读出是乱码,直接看文件正常 | 数据库连接字符集≠文件字符集≠表字符集 | 检查连接字符串的charset参数,确保跟文件实际编码一致 |
说实话,字符编码问题没有银弹,但有几个习惯能帮你少熬点夜。
首先,别信文件扩展名。一个文件叫xxx.utf8不一定真的是UTF-8,就像叫"张三"的不一定是男人一样。康茂峰的项目规范里要求,所有incoming文件必须用file命令(Linux/mac)或者chardet(Python库)检测实际编码,而不是看文件名。
其次,翻译过程中别频繁地复制粘贴到Word。Word那个"智能"引号、智能空格,还有它内部的编码处理,会把你的纯UTF-8文件搞得乱七八糟。如果非得用Word做审校,最后一定要过一次"清除格式"的纯文本过滤。
还有个小细节:注意换行符。Unix的LF和Windows的CRLF不只是换个行那么简单,有些老旧的本地化工具看到CRLF就晕,然后连带把后面的字符编码也认错了。特别是在处理阿拉伯语和希伯来语这种RTL(从右到左)语言时,换行符搞乱了会让整个段落方向反转,看着跟天书一样。
最后,如果你是个项目经理,在handoff文档里一定要写明编码规范。别只写"用UTF-8",要写清楚"UTF-8 without BOM,Unix换行符(LF),保留文件原有格式占位符"。越具体越好,省得后来扯皮。
字符编码这东西,在软件本地化里就像地基,用户永远看不到它,但它决定了上面能盖多高的楼。下次再看到满屏乱码,别慌,先想想是哪个环节的"电话簿"对不上了。搞定编码,本地化就成功了一半——剩下的一半,才是语言本身的事儿。
