国际化之字符串翻译

支持多国语言的存储(Unicode)和显示(字体)是GUI必需要做的,但字符串的翻译完全可以用gettext等第三方库函数来实现。AWTK之所以选择自己实现,主要出于以下几点考虑:

  • 减少不必要的第三方库的依赖。运行时需要的代码也就几十行,自己实现更简单代码更少。

  • 方便支持实时切换当前语言。自己实现字符串的翻译,不要应用程序做额外的工作,即可实现实时切换当前语言。

XML格式

我们采用XML文件(UTF-8)保存字符串的各个语言的对应关系,方便程序员和翻译人员进行编辑。如:

  1. <string name="ok">
  2. <language name="en_US">OK</language>
  3. <language name="zh_CN">确定</language>
  4. </string>
  5. <string name="cancel">
  6. <language name="en_US">Cancel</language>
  7. <language name="zh_CN">取消</language>
  8. </string>
  9. <string name="value is %d">
  10. <language name="en_US">value is %d</language>
  11. <language name="zh_CN">值为%d</language>
  12. </string>

在实际工作中,由程序员提供一个模板,让翻译人员翻译,翻译好之后交给程序员放到项目中。

二进制格式

XML格式方便人类阅读和编辑,但不适合计算机处理。AWTK使用了二进制格式保存,每种语言的字符串放在独立的文件中,字符串按升序存储,查询时使用二分查找。

文件名(资源名)使用『语言』+’_‘+『国家/地区』,如en_US,表示美国英语。如果不想搞得太精细,也可以只用语言名,如en,表示缺省的英语。在XML文件中的language的name中指定即可。

转换工具

我们提供了一个strgen的工具,它负责把XML的字符串文件转成紧凑高效的二进制文件(也可以直接转成资源常量)。

  1. Usage: ./bin/strgen input outputidr [bin]

实时切换语言

为了支持实时切换语言,我们要保存没有翻译之前的字符串,这样在当前语言改变后,才能用该字符串重新进行翻译。这会有一点额外的内存开销,在PC上和手机上这点开销可以忽略不计,在低端嵌入式系统中这需要考虑了,所以AWTK提供一个宏WITH_DYNAMIC_TR来决定是否启用该功能。

动态字符串翻译

有些字符串是在运行时,用一个模板根据当时的数据动态生成的。这类字符串的翻译,需要程序员先用函数locale_info_tr获取当前语言的模板,生成真正要显示的字符串,再设置到控件中去。如:

  1. char str[64];
  2. const char* format = locale_info_tr(locale_info(), "value is %d");
  3. tk_snprintf(str, sizeof(str), format, value);
  4. widget_set_text_utf8(widget, str);

在切换当前语言时,这类字符串也需要特殊处理。注册窗口的的EVT_LOCALE_CHANGED事件的处理函数,然后做上面的处理即可:

  1. static ret_t on_locale_changed(void* ctx, event_t* e) {
  2. ...
  3. return RET_OK;
  4. }
  5. ...
  6. widget_on(win, EVT_LOCALE_CHANGED, on_locale_changed, win);

图片翻译

在一些应用程序中,有些文字是直接绘制在图片上的。所以在切换到不同的语言时,需要加载不同的图片。这时只要在图片名称中包含『$locale$』即可,加载时自动替换成当前的语言。

如:图片名称为『flag_$locale$』,当前语言为en_US,加载图片时会按下列顺序查找:

  • flag_en_US
  • flag_en
  • flag_

XML中的用法:

  1. <image style="border" image="flag_$locale$" draw_type="icon" />
  1. image_set_image(image, "flag_$locale$");

也可以使用资源名称的高级用法来实现同样的功能。

使用方法

如果控件的文本需要翻译,则通过函数widget_set_tr_text设置文本(如果不需要翻译,则用widget_set_text设置):

  1. widget_set_tr_text(radio_button, "Chinese");

示例

demotr提供了实时切换语言的示例,代码可以参考demos/demo_tr_app.c。运行下面的程序可以看到效果:

  1. ./bin/demotr

缺省的demo中并没有带完整的中文字体,如果翻译之后的文字无法显示,请把default.ttf换成自己的字体文件。