Chromium的本地化方法
一、软件环境
chromium版本:6.0.482.0
os:windows xp sp2
二、chromium的本地化
chromium中需要本地化的部分有三部分:
1>chromium项目本地化,2>webkit本地化,3>setup本地化.
由于对第2种的本地化需求较少,setup部分的本地化较简单,本文只讲述1部分的本地化方法。
三、chromium项目本地化
1.通过编译chrome项目后,可以发现,在生成的locals中,共有51个.dll文件,分别是不同语言的本地化库。在sln文件中分别对应有这51个项目,而在每个项目中都包含名为generated_resources_xx.rc的资源文件(xx为语言代号,例如zh_CN为简体中文),在其下的stringtable中则是以此种语言的本地化字符串集。细心的朋友可以发现,整个chrome编译之前,这些rc文件是不存在的,它的生成是在编译时动态生成的。生成它的项目正是chrome_strings项目。
2.打开chrome_strings项目可以看到,其下包含了三个grd文件:chromium_strings.grd, generated_resources.grd, google_chrome_strings.grd。另外还有一批xtb文件。
google_chrome_strings.grd文件是用于编译chrome时才使用的,而我们是要编译chromium项目,因此不涉及此文件.
chromium_string.grd文件涉及到chromium项目的本地化和setup的本地化,其中包含安装卸载软件介绍等字符串的本地化。
generated_resources.grd主要涉及chromium项目的本地化,几乎包含所有chrome.exe中所要使用到的字符串的本地化。
综上所述,generated_resource.grd是我们需要关注的对象。打开该文件可以看到,该文件由3部分组成:
3.本地化原理
顾名思义,需要本地化,那么首先需要有一个“标准”字符串文件,然后在不同种语言下替换掉该语言。由此我们需要揭开上面所述的四种文件之间(grd, rc, xtb, dll)的生成关系:
grd ---------------> rc ---------> dll
grd:grd文件就是“标准”字符串文件,在其中定义好项目中所需使用的字符串id与对应的字符串值(一般为英文版本,因为比较通用)。
xtb:针对grd文件的不同语言翻译版本文件,例如,中文环境下的字符串翻译都需要写在generated_resources_zh-CN.xtb中,至于存放格式,这是后面讨论的重点。
rc:本文件对应在不同的语言项目中,并且被该项目所包含以便编译时生成到dll文件中。
dll:由rc文件生成出来的不同语言翻译版本的字符串“库”。
4.如何本地化
上面提到,对“标准”文件本地化时,要把对应语言的翻译版本写到对应的xtb文件中。因此翻译简体中文版本时,只要将对应“标准”文件中的字符串本地化到generated_resources.xtb文件中,打开此文件可以看到。
在generated_resources.grd中字符串值
(Keyword:
(注:$1是chromium中定义的字符替换标识符,此部分的原理不在本文讨论范围内。)
在generated_resources_zh_CN.xtb中对应的翻译版本如下:
在generated_resources.zh_TW.xtb中对应的翻译版本如下:
由此,我们可以预测程序中使用字符串id为IDS_AUTOCOMPLETE_KEYWORD_DESCRIPTION的地方,在中文环境下对应使用的字符串是翻译字符串中id=406259880812417922所对应的“(关键字:$1)”, 在繁体环境下对应的是(關鍵字:$1) 但是这是如何对应起来的?我们只能看到在下面的翻译版本和上面message中都含有同一个键“id”,但这两个键的值之间看不出有明显的联系。因此如果找到了这两者之间的联系,那么我们就找到了把“标准”字符串翻译成各种语言版本的根本方法。
到此,想从项目中去找到这个联系的头绪已经断了,此时我想到了官网。果然,官网上给出了答案:
"If you are adding a string to Chromium that you want for a different version of Chromium and you want to have translation in other languages, you need to add translations to the .xtb files in /src/chrome/app/resources. These files have a transalation id that is based on a hash of the en-US string (see /src/tools/grit/external/FP.py for details on the hash)"
答案就在/src/tools/grit/external/FP.py文件中,打开该文件看到如下代码:
def UnsignedFingerPrint(str, encoding='utf-8'):
"""Generate a 64-bit fingerprint by taking the first half of the md5
of the string."""
hex128 = _new_md5(str).hexdigest()
int64 = long(hex128[:16], 16)
return int64
def FingerPrint(str, encoding='utf-8'):
fp = UnsignedFingerPrint(str, encoding=encoding)
# interpret fingerprint as signed longs
if fp & 0x8000000000000000L:
fp = - ((~fp & 0xFFFFFFFFFFFFFFFFL) + 1)
return fp
从这两个python写的函数中可以看到基本的思路:FingerPrint函数接收一个字符串输入,然后调用函数UnsignedFingerPrint对字符串计算md5值hex128,并对hex128按照16进制取前一半,再经常一系列位运算之后返回其值,由此大致可以判断算出来的值是一列数字串,因此,我们再只需要确定输入的字符串的规则即可算出最后的数字串的值。
经过我对输入字符串进行各种可能的测试之后,发现有时算出的值与xtb中的值相同,有时不同,由此断定计算方法中还有未知的部分,因此我们需要找到调用FingerPrint函数的部分是否做了其它处理。因此我在源代码中搜索,结果如愿找到了文件/src/tools/grit/external/tclib.py中有如下代码:
def GenerateMessageId(message, meaning=''):
fp = FP.FingerPrint(message)
if meaning:
# combine the fingerprints of message and meaning
fp2 = FP.FingerPrint(meaning)
if fp < 0:
fp = fp2 + (fp << 1) + 1
else:
fp = fp2 + (fp << 1)
# To avoid negative ids we strip the high-order bit
fpid = str(fp & 0x7fffffffffffffffL)
return fpid
这个函数中,我们看到了另一个输入输入字符串meaning,根据名字猜测应该是在有被替换的字符串标识符的时候会被使用到,函数的逻辑比较简单,据此,我们得到了计算id值406259880812417922的大致方法。但被输入的两个字符串该选取
def GenerateMessageId(message, meaning=''):
fp = FP.FingerPrint(message)
if meaning:
# combine the fingerprints of message and meaning
fp2 = FP.FingerPrint(meaning)
if fp < 0:
fp = fp2 + (fp << 1) + 1
else:
fp = fp2 + (fp << 1)
# To avoid negative ids we strip the high-order bit
fpid = str(fp & 0x7fffffffffffffffL)
########
fp2 = file('c:/message_log.txt', 'a')
fp2.write('message=' + message + ', id=' + fpid + "/r/n")
fp2.close()
#######
return fpid
上面#所包含的四行代码将计算出的id值以xtb中的格式保存到c:/message_log.txt文件中,剩下需要做的就是编译chrome_strings项目,再去找到message_log.txt文件中对应所需要计算的
例如,上面所列举的IDS_AUTOCOMPLETE_KEYWORD_DESCRIPTION字符串在message_log.txt中对应的一列为:
message=(Keyword: KEYWORD), id=406259880812417922
当然,如果想要深究GenerateMessageId的两个输入参数也是很容易的,稍加改动write语句,把两个输入参数的值输出即可知道输入字符串的规则,但目的还是计算得知id,既然我们已经达到此目标,我就不去深究这个值。
至此,我们的问题全部解决了,再来理一下整体思路,如果要添加一个新的字符串到chromium项目中,并且需要翻译成本地版本,我们需要做的步骤如下:
1.添加字符串到generated_resources.grd文件中。
OK
2.only编译chrome_strings项目,在message_log中找到下面一行
message=OK, id=6965382102122355670
3.在generated_resrouces_zh_CN.xtb中添加如下一行翻译值
4.再编译chrome_string项目,就可以在zh_CN项目的generated_resources.rc的stringtable中找到下面这一行
IDS_OK 11028 确定
当编译出的chromium程序运行在简体中文环境下时,调用IDS_OK的地方则会显示出“确定”字符串。
后記:虽然本地化的问题已经解决,但我在完成此任务时,出现不少问题,在此列出来主要的几个,让大家少走一些弯路:
1.generated_resources.xtb中的id值不能重复,出现重复时,编译chrome_string项目时会给出提示,但它的提示未明显指出是重复问题。如果出现重复,删掉一条即可,因为id值如果重复,那么说明这两个字符串的值本身就是相同的,那么在生成rc文件时,两个
2.计算出id值后,填到xtb文件中时,如果字符串有有需要被替换的部分,即原字符串中包含诸如
3.有人说,可以直接在生成的rc文件中修改字符串的翻译以达到本地化的目的。当然,我承认这是最直接的方式,但不可忽视的是,如果你所使用的所有字符串是一次性生成,一次性在rc文件中修改完成,那么这种方法OK,没问题。但当你在grd中新添加一条字符串值时,或者做了rebuild时,rc文件会被重新生成,你所做的努力将前功尽弃,必须全盘重来。所以这是一种治标不治本的方法,不推荐使用。
(第一次写文章,逻辑,思维不甚严密,请见谅,谢谢)