背景:最近做全球化的项目,涉及到一个10年前的老系统,在测试中发现了中文乱码,于是有了这个故事。
概要:Latin1是不支持中文的,所以如果存储用的Latin1而应用用的UTF8,那么HTML Escape就需要在中间做过渡。写存读简单抽象是这样的流程:
写UTF8 -> HTML Escape -> 转Latin1 -> 存Latin1 -> 读Latin1 -> 转UTF8 -> HTML Unescape -> 读UTF8
问题:在把业务扩展到A国时,在前端页面发现了中文乱码(如安全
)。有个新同事研究了几天没有进展我就一起看一下。也想借此机会了解一下底层存储数据的系统是怎么考虑I18N的问题的。
分析:调研后发现,这个系统很老了,一部分存储是Latin1,另一部分是UTF8。其实公司已经决定迁移到更新的完全基于UTF8的新系统。只不过这个迁移很漫长,有部分业务还是基于老系统,而我们做全球化几乎所有的业务都会涉及到。这个老系统最早只是考虑了欧美市场,所以数据库是Latin1的编码,而后来的亚太市场则采用了UTF8的存储编码。A国被安置在了亚太的技术栈,所以数据是UTF8的。
虽然存储在各国不同,但是访问存储的客户端代码是一个包,这里边对国家有区分。如果是欧美国家,读到的Latin1的内容要转UTF8,而且这是默认配置;如果是亚太国家,经过配置就不用转。A国显然不用转。乱码的直接原因就是多转了一次。佐证如下:
yingjil$ cat utf8.txt
安全
yingjil$ iconv -f latin1 -t utf8 utf8.txt
安全
所以解决问题只需要让客户端代码知道A国属于亚太即可。
延伸问题:读到这里,你可能也会有一个疑问:欧美国家难道就不用支持中文吗?答案显然是肯定的,因为中文用户到处都有。那如何理解这个矛盾:Latin1的数据库和前端的中文支持。为了回答这个问题,我继续调研,这才有了本文的主题。Latin1的数据库里其实存的是经过HTML Escape处理的UTF8内容。
举个例子:
“安”经过HTML Escape后是安
[1],于是Latin1的数据库就存这些字符了。(这个是我目前的理解,并没有验证过,欢迎指正)
UTF8在经过HTML Escape后和Latin1之间是否还需要转换可能是另一个有趣的问题,不在此文展开了,[2]可能对此有帮助。
参考:
[1] http://www.w3school.com.cn/tags/html_ref_entities.html
[2] http://blog.csdn.net/zhangxinrun/article/details/8188053