Chromium开发中有时候需要添加一些字符串资源。Chromium是通过GRIT来管理多语言的字符串资源。默认的字符串资源是en-US版本。然后根据en-US的字符串hash生成一个id,其他语言的字符串就根据id存在xtb文件里。
假如我们要添加一个zh-CN中文的字符串,则首先要根据en-US的字符串hash生成一个id,然后把在zh-CN的xtb文件里添加一项。实际上生成id的逻辑还是挺复杂的。因为Chromium字符串资源支持ph这样的占位符,另外字符串资源的meaning属性也会影响id的生成值。
以前同事写过一个脚本,自己根据各种规则去实现生成id。但是我觉得以后Chromium生成id的逻辑改变了,会导致的我们生成的id跟Chromium生成的id不一样。因此我看了GRIT生成id的相关代码,通过封装了一个脚本,调用它原有接口去生成。这样就能保持我们的生成id逻辑跟Chromium一样了。
这个脚本有以下命令行参数:
- -i, --init-template。初始化生成字符串模板input.xml。可把自己要生成ID的字符串资源按照摸板添加到xml中
- -g STRING, --gen-id STRING。生成字符串对应的ID
- -o, --output。根据input.xml的内容生成字符串的id,支持生成多条字符串ID
- -m MEANING, --string-meaning MEANING。通常跟-g参数配合使用,指定指定字符串的meaning
代码如下:
#!/usr/bin/env python
# encoding: utf-8
__author__ = 'gclxry@gmail.com'
import argparse
import logging
import os
import sys
from xml.dom.minidom import Document
sys.path.append("../../tools/grit")
from grit import util
from grit.node import message
def get_current_directory():
"""获得脚本当前运行的目录"""
current_directory = os.path.split(os.path.realpath(__file__))[0]
return current_directory
def get_input_xml_path():
"""获得input.xml文件路径"""
current_dir = get_current_directory()
xml_path = os.path.join(current_dir, 'input.xml')
return xml_path
def create_message_xml_node(doc, name, text, desc, meaning):
"""创建一个message的xml节点"""
content = doc.createTextNode(text)
message = doc.createElement("message")
message.setAttribute('name', name)
message.setAttribute('desc', desc)
message.setAttribute('meaning', meaning)
message.appendChild(content)
return message
def remove_xml_header(xml_str):
"""去掉xml文件开头的<?xml version="1.0" ?>"""
xml_str = xml_str.replace("""<?xml version="1.0" ?>""", '')
return xml_str
def init_template(context):
"""初始化生成字符串模板文件input.xml"""
input_xml_path = get_input_xml_path()
if os.path.exists(input_xml_path):
logging.info('init_template:path %s has exists' % input_xml_path)
else:
doc = Document()
root = doc.createElement("messages")
doc.appendChild(root)
name = 'IDS_CSR_EXAMPLE'
desc = 'This is a example'
meaning = ''
content = 'Chromium string resources example'
message = create_message_xml_node(doc, name, content, desc, meaning)
root.appendChild(message)
xml_str = doc.toprettyxml(indent=' ')
xml_str = remove_xml_header(xml_str)
with open(input_xml_path, 'w') as f:
f.write(xml_str)
logging.info('init_template:create %s' % input_xml_path)
def gen_id(context, string):
"""生成字符串对应的ID"""
messages_template = """<messages><message desc="" meaning="%s" name="IDS_CSR_TEMPLATE">%s</message></messages>"""
messages_string = messages_template % (context['string_meaning'], string)
root = util.ParseGrdForUnittest(messages_string)
msg, = root.GetChildrenOfType(message.MessageNode)
cliques = msg.GetCliques()
if len(cliques) == 0:
logging.error('cliques is empty')
return ''
clique = cliques[0]
content = clique.GetMessage().GetPresentableContent()
id = clique.GetId()
print(id)
def output(context):
"""根据input.xml的内容生成字符串的id"""
input_xml_path = get_input_xml_path()
if not os.path.exists(input_xml_path):
logging.error('output:input xml %s not exists' % input_xml_path)
init_template(context)
return
with open(input_xml_path) as f:
xml_str = f.read()
xml_str = remove_xml_header(xml_str)
root = util.ParseGrdForUnittest(xml_str)
msgs = root.GetChildrenOfType(message.MessageNode)
for m in msgs:
cliques = m.GetCliques()
if len(cliques) == 0:
logging.error('cliques is empty')
continue
clique = cliques[0]
content = clique.GetMessage().GetPresentableContent()
id = clique.GetId()
name = m.GetTextualIds()
print('output:%s -> %s' % (name[0], id))
def main():
logging_format = '[%(levelname)s %(filename)s:%(lineno)d] %(message)s'
logging.root.handlers = []
logging.basicConfig(format=logging_format, level=logging.INFO)
context = {}
context['string_meaning'] = ''
parser = argparse.ArgumentParser(description='Chromium 字符串资源工具')
parser.add_argument('-i', '--init-template', action='store_true', help='初始化生成字符串模板input.xml。可把自己要生成ID的字符串资源按照摸板添加到xml中')
parser.add_argument('-g', '--gen-id', metavar='STRING', help='生成字符串对应的ID')
parser.add_argument('-o', '--output', action='store_true', help='根据input.xml的内容生成字符串的ID,支持生成多条字符串ID')
parser.add_argument('-m', '--string-meaning', metavar='MEANING', help='通常跟-g参数配合使用,指定指定字符串的meaning')
args = parser.parse_args()
if not (args.init_template or args.gen_id or args.output):
parser.print_help()
if args.string_meaning:
context['string_meaning'] = args.string_meaning
if args.init_template:
init_template(context)
return
if args.gen_id:
gen_id(context, args.gen_id)
return
if args.output:
output(context)
return
if __name__ == '__main__':
sys.exit(main())
参考:
- https://www.chromium.org/developers/design-documents/ui-localization
- https://www.chromium.org/developers/tools-we-use-in-chromium/grit/grit-users-guide