2018-05-05 19:08:32 +08:00
# 国际化之字符串翻译
2019-05-24 19:22:05 +08:00
字符串翻译是 GUI 国际化时必须实现的功能之一, AWTK 没有选择 gettext 等第三方库,而是选择自己实现,主要出于以下几点考虑:
2018-05-05 19:08:32 +08:00
* 减少不必要的第三方库的依赖。运行时需要的代码也就几十行,自己实现更简单代码更少。
2019-05-24 19:22:05 +08:00
* 方便支持实时切换当前语言。自己实现字符串的翻译,不需要应用程序做额外的工作,即可实现实时切换当前语言。
2018-05-05 19:08:32 +08:00
2019-05-24 19:22:05 +08:00
## 一、XML 格式
2018-05-05 19:08:32 +08:00
2019-05-24 19:22:05 +08:00
我们采用 XML 文件 (UTF-8) 保存字符串的各个语言的对应关系,方便程序员和翻译人员进行编辑。如:
2018-05-05 19:08:32 +08:00
2021-07-12 08:56:30 +08:00
```xml
2018-05-05 19:08:32 +08:00
< string name = "ok" >
< language name = "en_US" > OK< / language >
< language name = "zh_CN" > 确定< / language >
< / string >
< string name = "cancel" >
< language name = "en_US" > Cancel< / language >
< language name = "zh_CN" > 取消< / language >
< / string >
2018-11-28 09:09:59 +08:00
< string name = "value is %d" >
< language name = "en_US" > value is %d< / language >
< language name = "zh_CN" > 值为%d< / language >
< / string >
2018-05-05 19:08:32 +08:00
```
> 在实际工作中,由程序员提供一个模板,让翻译人员翻译,翻译好之后交给程序员放到项目中。
2019-05-24 19:22:05 +08:00
## 二、二进制格式
2018-05-05 19:08:32 +08:00
2019-05-24 19:22:05 +08:00
XML 格式方便人类阅读和编辑, 但不适合计算机处理。AWTK 使用了二进制格式保存,每种语言的字符串放在独立的文件中,字符串按升序存储,查询时使用二分查找。
2018-05-05 19:08:32 +08:00
2019-05-24 19:22:05 +08:00
文件名(资源名)使用『语言』+'\_'+『国家/地区』,如 en\_US, 表示美国英语。如果不想搞得太精细, 也可以只用语言名, 如 en, 表示缺省的英语。在 XML 文件中的 language 的 name 中指定即可。
2018-05-05 19:08:32 +08:00
2019-05-24 19:22:05 +08:00
## 三、转换工具
2018-05-05 19:08:32 +08:00
2019-05-24 19:22:05 +08:00
我们提供了一个 strgen 的工具,它负责把 XML 的字符串文件转成紧凑高效的二进制文件(也可以直接转成资源常量)。
2018-05-05 19:08:32 +08:00
```
Usage: ./bin/strgen input outputidr [bin]
```
2019-05-24 19:22:05 +08:00
## 四、实时切换语言
2018-05-05 19:08:32 +08:00
2019-05-24 19:22:05 +08:00
为了支持实时切换语言,我们要保存没有翻译之前的字符串,这样在当前语言改变后,才能用该字符串重新进行翻译。这会有一点额外的内存开销,在 PC 上和手机上这点开销可以忽略不计,在低端嵌入式系统中这需要考虑了,所以 AWTK 提供一个宏 WITH\_DYNAMIC\_TR 来决定是否启用该功能。
2018-05-05 19:08:32 +08:00
2019-05-24 19:22:05 +08:00
## 五、动态字符串翻译
2018-05-05 19:08:32 +08:00
2019-05-24 19:22:05 +08:00
有些字符串是在运行时动态生成的,用一个模板根据当时的数据动态生成。这类字符串的翻译,需要程序员先用函数 locale\_info\_tr 获取当前语言的模板,生成真正要显示的字符串,再设置到控件中去。如:
2018-05-05 19:08:32 +08:00
```
2018-11-28 09:09:59 +08:00
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);
2018-05-05 19:08:32 +08:00
```
2019-05-24 19:22:05 +08:00
在切换当前语言时,这类字符串也需要特殊处理。注册窗口的的 EVT\_LOCALE\_CHANGED 事件的处理函数,然后做上面的处理即可:
2018-05-05 19:08:32 +08:00
```
static ret_t on_locale_changed(void* ctx, event_t* e) {
...
return RET_OK;
}
...
widget_on(win, EVT_LOCALE_CHANGED, on_locale_changed, win);
```
2019-05-24 19:22:05 +08:00
## 六、图片翻译
2018-05-05 19:08:32 +08:00
2019-01-26 07:53:51 +08:00
在一些应用程序中,有些文字是直接绘制在图片上的。所以在切换到不同的语言时,需要加载不同的图片。这时只要在图片名称中包含『$locale$』即可,加载时自动替换成当前的语言。
2019-05-24 19:22:05 +08:00
如: 图片名称为『flag\_$locale$』,当前语言为 en\_US, 加载图片时会按下列顺序查找:
2019-01-26 07:53:51 +08:00
* flag\_en\_US
* flag_en
* flag_
2019-05-24 19:22:05 +08:00
XML 中的用法:
2019-01-26 07:53:51 +08:00
```
< image style = "border" image = "flag_$locale$" draw_type = "icon" / >
```
```
image_set_image(image, "flag_$locale$");
```
2018-05-05 19:08:32 +08:00
2019-05-24 19:22:05 +08:00
> 也可以使用 [资源名称的高级用法](assets_manager.md) 来实现同样的功能。
2019-03-08 19:22:37 +08:00
2019-05-24 19:22:05 +08:00
## 七、数字时钟月份和星期的翻译
星期的格式用"W",如:
2018-05-05 19:08:32 +08:00
2019-05-24 19:22:05 +08:00
```
< digit_clock style = "green" format = "W" / >
```
2018-05-05 19:08:32 +08:00
2019-05-24 19:22:05 +08:00
月份的格式用"MMM",如:
2018-05-05 19:08:32 +08:00
```
2019-05-24 19:22:05 +08:00
< digit_clock style = "green" format = "MMM" / >
2018-05-05 19:08:32 +08:00
```
2019-05-24 19:22:05 +08:00
星期要翻译的字符串如下:
2018-05-05 19:08:32 +08:00
2019-05-24 19:22:05 +08:00
```
< string name = "Mon" >
< language name = "en_US" > Mon< / language >
< language name = "zh_CN" > 星期一< / language >
< / string >
2018-05-05 19:08:32 +08:00
2019-05-24 19:22:05 +08:00
< string name = "Tues" >
< language name = "en_US" > Tues< / language >
< language name = "zh_CN" > 星期二< / language >
< / string >
2018-05-05 19:08:32 +08:00
2019-05-24 19:22:05 +08:00
< string name = "Wed" >
< language name = "en_US" > Wed< / language >
< language name = "zh_CN" > 星期三< / language >
< / string >
< string name = "Thur" >
< language name = "en_US" > Thur< / language >
< language name = "zh_CN" > 星期四< / language >
< / string >
< string name = "Fri" >
< language name = "en_US" > Fri< / language >
< language name = "zh_CN" > 星期五< / language >
< / string >
< string name = "Sat" >
< language name = "en_US" > Sat< / language >
< language name = "zh_CN" > 星期六< / language >
< / string >
< string name = "Sun" >
< language name = "en_US" > Sun< / language >
< language name = "zh_CN" > 星期天< / language >
< / string >
2018-05-05 19:08:32 +08:00
```
2019-05-24 19:22:05 +08:00
月份要翻译的字符串如下:
```
< string name = "Jan" >
< language name = "en_US" > Jan< / language >
< language name = "zh_CN" > 一月< / language >
< / string >
< string name = "Feb" >
< language name = "en_US" > Feb< / language >
< language name = "zh_CN" > 二月< / language >
< / string >
< string name = "Mar" >
< language name = "en_US" > Mar< / language >
< language name = "zh_CN" > 三月< / language >
< / string >
< string name = "Apr" >
< language name = "en_US" > Apr< / language >
< language name = "zh_CN" > 四月< / language >
< / string >
< string name = "May" >
< language name = "en_US" > May< / language >
< language name = "zh_CN" > 五月< / language >
< / string >
< string name = "Jun" >
< language name = "en_US" > Jun< / language >
< language name = "zh_CN" > 六月< / language >
< / string >
< string name = "Jul" >
< language name = "en_US" > Jul< / language >
< language name = "zh_CN" > 七月< / language >
< / string >
< string name = "Aug" >
< language name = "en_US" > Aug< / language >
< language name = "zh_CN" > 八月< / language >
< / string >
< string name = "Sept" >
< language name = "en_US" > Sept< / language >
< language name = "zh_CN" > 九月< / language >
< / string >
< string name = "Oct" >
< language name = "en_US" > Oct< / language >
< language name = "zh_CN" > 十月< / language >
< / string >
< string name = "Nov" >
< language name = "en_US" > Nov< / language >
< language name = "zh_CN" > 十一月< / language >
< / string >
< string name = "Dec" >
< language name = "en_US" > Dec< / language >
< language name = "zh_CN" > 十二月< / language >
< / string >
2018-05-05 19:08:32 +08:00
```
2019-01-09 11:03:47 +08:00
2019-05-24 19:22:05 +08:00
## 八、使用方法
2019-01-09 11:03:47 +08:00
2019-05-24 19:22:05 +08:00
* XML 中用 tr_text 属性指定。如:
```
< label . . . tr_text = "ok" / >
```
* C 代码通过函数 widget\_set\_tr\_text 设置文本(如果不需要翻译,则用 widget\_set\_text 设置):
```
widget_set_tr_text(radio_button, "Chinese");
```
## 九、示例
demotr 提供了实时切换语言的示例,代码可以参考 demos/demo\_tr\_app.c。运行下面的程序可以看到效果:
```
./bin/demotr
```
2019-01-09 11:03:47 +08:00
2019-05-24 19:22:05 +08:00
> 缺省的 demo 中并没有带完整的中文字体,如果翻译之后的文字无法显示,请把 default.ttf 换成自己的字体文件。
2020-04-25 16:01:21 +08:00
## 十、注意事项
2020-04-29 17:15:14 +08:00
* 1. 在源文件中最好不要用非英文字符串。如果一定要用,源文件的格式一定要用 UTF8-BOM 编码,这个可以用 vscode 或记事本转换,否则在 Windows 平台可能出现乱码。
* 2. 在 XML 中,以下字符需要转义成 HTML 实体
| 字符 | 转义实体 |
| ---- | ---- |
| < | & lt; |
| > | & gt; |
| " | & quot; |
示例:
```xml
< string name = "<"en&zh">" >
< language name = "en_US" > < " en& zh" > < / language >
2021-07-12 08:56:30 +08:00
< language name = "zh_CN" > < " 英文& 中文" > < / language >
2020-04-29 17:15:14 +08:00
< / string >
```
2022-05-30 07:13:38 +08:00
* 3. tr_text 中的内容只是一个用来查找的 key, 如果翻译的内容比较长, 建议用一个精简且唯一的句子代替即可。这样, 写起来简单, 运行时又节省内存。
比如:用来介绍 AWTK 的文字,在 UI 文件中,可以这样写:
```
< label . . . tr_text = "awtk_intro" / >
```
```xml
< string name = "awtk_intro" >
< language name = "en_US" > AWTK , the abbreviation of Toolkit AnyWhere, is an open source GUI engine developed by ZLG. It is a cross-platform GUI engine for embedded systems, WEB, mini programs, mobile phones and PC. It is a powerful, efficient, reliable and easy-to-use GUI engine for users to design beautiful GUI applications.< / language >
< language name = "zh_CN" > AWTK 全称 Toolkit AnyWhere, 是 ZLG 开发的开源 GUI 引擎, 旨在为嵌入式系统、WEB、各种小程序、手机和 PC 打造的通用 GUI 引擎,为用户提供一个功能强大、高效可靠、简单易用、可轻松做出炫酷效果的 GUI 引擎。< / language >
< / string >
```