# 国际化之字符串翻译 字符串翻译是 GUI 国际化时必须实现的功能之一,AWTK 没有选择 gettext 等第三方库,而是选择自己实现,主要出于以下几点考虑: * 减少不必要的第三方库的依赖。运行时需要的代码也就几十行,自己实现更简单代码更少。 * 方便支持实时切换当前语言。自己实现字符串的翻译,不需要应用程序做额外的工作,即可实现实时切换当前语言。 ## 一、XML 格式 我们采用 XML 文件 (UTF-8) 保存字符串的各个语言的对应关系,方便程序员和翻译人员进行编辑。如: ``` OK 确定 Cancel 取消 value is %d 值为%d ``` > 在实际工作中,由程序员提供一个模板,让翻译人员翻译,翻译好之后交给程序员放到项目中。 ## 二、二进制格式 XML 格式方便人类阅读和编辑,但不适合计算机处理。AWTK 使用了二进制格式保存,每种语言的字符串放在独立的文件中,字符串按升序存储,查询时使用二分查找。 文件名(资源名)使用『语言』+'\_'+『国家/地区』,如 en\_US,表示美国英语。如果不想搞得太精细,也可以只用语言名,如 en,表示缺省的英语。在 XML 文件中的 language 的 name 中指定即可。 ## 三、转换工具 我们提供了一个 strgen 的工具,它负责把 XML 的字符串文件转成紧凑高效的二进制文件(也可以直接转成资源常量)。 ``` Usage: ./bin/strgen input outputidr [bin] ``` ## 四、实时切换语言 为了支持实时切换语言,我们要保存没有翻译之前的字符串,这样在当前语言改变后,才能用该字符串重新进行翻译。这会有一点额外的内存开销,在 PC 上和手机上这点开销可以忽略不计,在低端嵌入式系统中这需要考虑了,所以 AWTK 提供一个宏 WITH\_DYNAMIC\_TR 来决定是否启用该功能。 ## 五、动态字符串翻译 有些字符串是在运行时动态生成的,用一个模板根据当时的数据动态生成。这类字符串的翻译,需要程序员先用函数 locale\_info\_tr 获取当前语言的模板,生成真正要显示的字符串,再设置到控件中去。如: ``` char str[64]; const char* format = locale_info_tr(locale_info(), "value is %d"); tk_snprintf(str, sizeof(str), format, value); widget_set_text_utf8(widget, str); ``` 在切换当前语言时,这类字符串也需要特殊处理。注册窗口的的 EVT\_LOCALE\_CHANGED 事件的处理函数,然后做上面的处理即可: ``` static ret_t on_locale_changed(void* ctx, event_t* e) { ... return RET_OK; } ... widget_on(win, EVT_LOCALE_CHANGED, on_locale_changed, win); ``` ## 六、图片翻译 在一些应用程序中,有些文字是直接绘制在图片上的。所以在切换到不同的语言时,需要加载不同的图片。这时只要在图片名称中包含『$locale$』即可,加载时自动替换成当前的语言。 如:图片名称为『flag\_$locale$』,当前语言为 en\_US,加载图片时会按下列顺序查找: * flag\_en\_US * flag_en * flag_ XML 中的用法: ``` ``` ``` image_set_image(image, "flag_$locale$"); ``` > 也可以使用 [资源名称的高级用法](assets_manager.md) 来实现同样的功能。 ## 七、数字时钟月份和星期的翻译 星期的格式用"W",如: ``` ``` 月份的格式用"MMM",如: ``` ``` 星期要翻译的字符串如下: ``` Mon 星期一 Tues 星期二 Wed 星期三 Thur 星期四 Fri 星期五 Sat 星期六 Sun 星期天 ``` 月份要翻译的字符串如下: ``` Jan 一月 Feb 二月 Mar 三月 Apr 四月 May 五月 Jun 六月 Jul 七月 Aug 八月 Sept 九月 Oct 十月 Nov 十一月 Dec 十二月 ``` ## 八、使用方法 * XML 中用 tr_text 属性指定。如: ```