國際化那點兒事兒

前言

前陣子對一個歷史項目做了國際化改造,時下流行框架基本上都提供了自己的國際化標準,預留了國際化模塊。本文講的是從零開始的國際化方案,適用於改造已有項目。如果您使用的是這些標準規範的框架,那可以參考您的框架文檔。

方案調研

市面上提供國際化解決方案有很多種,大致原理都是標記一個帶翻譯的key, 提供一個語言轉換函數和對應的語言包,翻譯這些標記的key。這裏介紹一個使用廣泛的,提供跨語言支持的方案 —GNU gettext utilities。規範完整,考慮周全,提供了各種語言示例,就是它了,我調研時整理了一份國際化解讀

建議如果做之前,詳細的閱讀3遍,如果遇到問題了,仔細看規範,上面有你想要的一切答案。

規範說明

系統運行說明

由於gettext運行時,跟系統已安裝的本地化語言包有關。如果你要設置的語言和編碼,系統沒有會導致翻譯失效。可以通過locale -m 查看環境支持的編碼列表。locale -a 查看已安裝的語言列表。如果沒有,請參考規範安裝對應的語言包。

重要概念

  • po文件 可移植對象(Portable Object)

標記完成待翻譯的內容之後,使用命令可以提取這些待翻譯標籤到po文件中,由翻譯人員去審覈校驗。

#: lib/error.c:116
msgid "Unknown system error"
msgstr "Error desconegut del sistema"

#: src/msgcmp.c:338 src/po-lex.c:699
#, c-format
msgid "found %d fatal error"
msgid_plural "found %d fatal errors"
msgstr[0] "s'ha trobat %d error fatal"
msgstr[1] "s'han trobat %d errors fatals"
  • mo文件 機器對象(Machine Object)

po文件校驗無誤之後,使用編譯命令,將po文件編譯成mo文件,項目加載時,讀取的是mo文件。

實施流程

這裏以後端語言PHP爲例來演示下,如何實現國際化。

  1. 預裝gettext系統命令及php gettext擴展。如Mac下:brew install gettext
  2. 設置語言和編碼環境變量,兼容寫法,環境變量參考規範
$lang = "zh_CN";//語言爲了方便映射翻譯目錄,最好跟規範保持一致
$codeset = "UTF-8";
$language = $lang.'.'.$codeset;
//坑1:必須先更改環境變量LANG或LC_ALL的值(不能是默認值'C'),才能通過LANGUAGE使用優先級列表
$putRes1 = putenv('LANG='.$language);// 坑2: 如果失敗NULL,說明當前運行環境不支持該語言或編碼
$putRes2 = putenv('LC_ALL='.$language);
$putRes3 = putenv('LANGUAGE='.$language);
$category = defined('LC_MESSAGES') ? LC_MESSAGES : LC_ALL;
//兼容優化: 提供一個優先級列表,可以優先設置目標語言,萬一失敗,可以向後兼容,直到默認。 
setlocale($category,array($language,$lang,'zh_CN.UTF-8'));
//設置默認語言包位置
bindtextdomain("messages", LOCALE_ROOT);
bind_textdomain_codeset("messages", 'UTF-8');
textdomain("messages");
  1. 代碼標記
/** php **/
//正常翻譯 gettext || _
_(),gettext(),ngettext()
//多個domain切換 dgettext()/dcgettext()
bindtextdomain("module1", "/path/to/my/locale/folder");
bindtextdomain("module2", "/path/to/my/locale/folder");
textdomain("module1");
// this call will get the message from module1
echo _("Label1"); 
// this call will get the message from module2
echo dgettext("module2", "Label1"); 
  1. 使用xgettext提取翻譯標記生成po文件
xgettext --add-comments=TRANSLATORS: --keyword=gettext --keyword=_  --output=messages.po
  1. 使用msgfmt編譯po文件生成mo文件
msgfmt -o test.mo test.po
  1. 切換語言en_US驗證
  1. 除了php需要國際化外,有些模板引擎也需要,比如smarty,有插件支持,js 不需要這個,js依賴json。也可以先生成po,再轉換成json。
  2. gettext 命令功能強大,支持你想要的各種需求處理,包括合併,分割,去重等等,建議仔細看下。
  3. gettext需要固定的語言包子路徑:locale/en_US/LC_MESSAGES/messages.po,不可以隨便建。

改造過程

  • 已有代碼處理
    歷史代碼,因爲沒有什麼規範,各種情況都有,主要靠寫正則腳本去替換了99%的翻譯標記。有些特殊寫法的,靠腳本和人工去審覈標記。
  • 日常開發規範約定
    處理完歷史代碼之後,爲了減少國際化的開發工作量,約定了國際化規範,待翻譯的內容必須帶翻譯後綴,例如: $msg_i18n = “我是待翻譯的內容”,方便自動化腳本去匹配處理。
  • 自動化腳本功能說明
    由於國際化流程比較複雜,需要標記->生成po文件->去重->編譯。爲了儘可能的降低國際化帶來的開發速度影響,我開發了幾個腳本,來自動化處理翻譯內容,實現了:
  1. 自動標記並生成po文件
  2. 自動翻譯並生成英文版po文件
  3. 自動去重並編譯成mo文件

當然,如果遇到錯誤異常,仍然需要人工干預,但是正常是不需要的。下面是腳本演示:

MacBook-2:www lemon$ php MarkTag.php -f project/index.php
No syntax errors detected in project/index.php
Mark ok, total: 1, pid is 43884, start translating ...
1.read cn ok
2.get en ok
3.merged cn & en ok
4.replace cn ok
Great, translate ok! start compiling ...
check en-us fuzzy and deal with it
msg unique and compile ok !
No js msg to translate

(完)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章