生成Chromium字符串资源ID


发布于 2019-11-12


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