Ejabberd代码分析之-翻译辅助工具
Ejabberd支持国际化,在<iq/>
,<message/>
,<presence/>
等元素上添加xml:lang
属性,对应的英文文本会切换到指定的语言.
举个例子,模块mod_announce.erl
代码内有如下对translate:translate()
的使用:
1 | get_title(Lang, <<"announce">>) -> translate:translate(Lang, <<"Announcements">>); get_title(Lang, ?NS_ADMIN_ANNOUNCE_ALL) -> translate:translate(Lang, <<"Send announcement to all users">>); get_title(Lang, ?NS_ADMIN_ANNOUNCE_ALL_ALLHOSTS) -> translate:translate(Lang, <<"Send announcement to all users on all hosts">>); get_title(Lang, ?NS_ADMIN_ANNOUNCE) -> translate:translate(Lang, <<"Send announcement to all online users">>); get_title(Lang, ?NS_ADMIN_ANNOUNCE_ALLHOSTS) -> translate:translate(Lang, <<"Send announcement to all online users on all hosts">>); get_title(Lang, ?NS_ADMIN_SET_MOTD) -> translate:translate(Lang, <<"Set message of the day and send to online users">>); get_title(Lang, ?NS_ADMIN_SET_MOTD_ALLHOSTS) -> translate:translate(Lang, <<"Set message of the day on all hosts and send to online users">>); get_title(Lang, ?NS_ADMIN_EDIT_MOTD) -> translate:translate(Lang, <<"Update message of the day (don't send)">>); get_title(Lang, ?NS_ADMIN_EDIT_MOTD_ALLHOSTS) -> translate:translate(Lang, <<"Update message of the day on all hosts (don't send)">>); get_title(Lang, ?NS_ADMIN_DELETE_MOTD) -> translate:translate(Lang, <<"Delete message of the day">>); get_title(Lang, ?NS_ADMIN_DELETE_MOTD_ALLHOSTS) -> translate:translate(Lang, <<"Delete message of the day on all hosts">>). |
translate:translate()
定义在translate.erl
模块中.
1 | -spec translate(binary(), binary()) -> binary(). translate(Lang, Msg) -> LLang = ascii_tolower(Lang), case ets:lookup(translations, {LLang, Msg}) of [{_, Trans}] -> Trans; _ -> ShortLang = case str:tokens(LLang, <<"-">>) of [] -> LLang; [SL | _] -> SL end, case ShortLang of <<"en">> -> Msg; LLang -> translate(Msg); _ -> case ets:lookup(translations, {ShortLang, Msg}) of [{_, Trans}] -> Trans; _ -> translate(Msg) end end end. |
当你在XML节上定义了xml:lang='zh'
属性后,服务端的应答文本就变为中文了, 如下:
增加自定义字符串
搞清楚了原理后,我就可以为我的模块添加字符串了.
首先我在我的模块中使用英文作为默认的文本描述, 如下所示:
1 | #xmlel{ name = <<"item">>, attrs = [ {<<"jid">>, Server}, {<<"node">>, <<"http://www.example.com/protocol/messager#upgrade-full">>}, {<<"name">>, translate:translate(Lang, <<"Make a complete upgrade">>)} ], children = [] } |
然后根据$EJABBERD_SRC/contrib/extract_translations/README
的描述,
需要编译$EJABBERD_SRC/contrib/extract_translations/extract_translations.erl
模块.
1 | erlc $EJABBERD_SRC/contrib/extract_translations/extract_translations.erl |
Ejabberd提供了一个SHELL脚本来从*.po
文件生成Ejabberd需要的*.msg
文件.
1 | root@4850618fe551:~/ejabberd/contrib/extract_translations# ./prepare-translation.sh -h Options: -langall -lang LANGUAGE_FILE -srcmsg2po LANGUAGE Construct .msg file using source code to PO file -src2pot Generate template POT file from source code -popot2po LANGUAGE Update PO file with template POT file -po2msg LANGUAGE Export PO file to MSG file -updateall Generate POT and update all PO Example: ./prepare-translation.sh -lang es.msg |
首先切换到源码根目录, 执行:
1 | root@4850618fe551:~/ejabberd# ./contrib/extract_translations/prepare-translation.sh -src2pot ./contrib/extract_translations/prepare-translation.sh: line 183: msguniq: command not found |
找不到msguniq
命令,在Ubuntu上,msguniq
在gettext
包种, 下面安装需要的软件包:
1 | aptitude install -y gettext |
执行下面的命令, 更新ejabberd.pot
文件:
1 | root@4850618fe551:~/ejabberd# ./contrib/extract_translations/prepare-translation.sh -src2pot |
打开$EJABBERD_SRC/priv/msgs/ejabberd.pot
, 翻译相关条目后, 复制粘贴到到$EJABBERD_SRC/priv/msgs/zh.po
中,
并执行下面的命令,更新我的zh.msg
文件, 这个文件是在Erlang代码中直接使用的文件. 后面会看到最终的效果.
1 | ./contrib/extract_translations/prepare-translation.sh -po2msg zh |
更新后的消息文件在$EJABBERD_SRC/priv/msgs/zh.msg
生成了我最终要的zh.msg
文件后,重新编译一次ejabberd(之前已经编译过,这一步很快)
1 | root@4850618fe551:~/ejabberd# make /usr/lib/erlang/bin/escript rebar skip_deps=true compile ==> rel (compile) ==> ejabberd (compile) Compiled src/mod_online_users.erl Compiled src/mod_gbox_messager.erl Compiled src/mod_cputime.erl Compiled src/mod_system_information.erl |
复制编译的新文件
1 | cp ebin/*.beam /lib/ejabberd/ebin cp priv/*.msg /lib/ejabberd/priv/msgs |
最后重启,并测试
1 | ejabberdctl restart |
查询命令列表(注意属性xml:lang='zh'
)
1 | <iq type='get' to='xmpp.myserver.info' from='root@xmpp.myserver.info' xml:lang='zh' xmlns='jabber:client'> <query xmlns='http://jabber.org/protocol/disco#items' node='http://jabber.org/protocol/commands'/> </iq> |
国际化后的结果
参考资料
- $EJABBERD_SRC/src/mod_announce.erl
- $EJABBERD_SRC/contrib/extract_translations/README
- $EJABBERD_SRC/src/translate.erl