何謂 L10n?
開發人員把 Localization 簡寫成 L10N,中間的數字 10 是前後兩個字母 L 和 N 之間的字母個數。它允許開發人員寫一個簡單的文件,就可以將顯示的功能表和文本翻譯成本地語言。
為什麼要使用 L10N?
L10N 標準能夠很好地支援您查看、輸入或處理非英語語言。
本地化設置需要具備三個條件:
- 語言代碼 ( Language Code)
- 國家代碼 ( Country Code )
- 編碼 ( Encoding )
本地名字可以用下面這些部分組成:語言代碼_國家代碼.編碼。例如(zh_CN.UTF-8, en_US等)。
哪些軟體用到 gettext?
- 大名鼎鼎的個人 blog 系統 wordpress。
- Linux 系統上大部分應用軟體。
本文主要討論利用 PHP 的 built-in 函數 gettext 來實現程式的本地化。本文假定編碼 ( encoding ) 都為 UTF-8。
自 PHP4 版本開始,PHP已經內建支援 gettext function了,不需要額外安裝。直接打開 php.ini,找到 extension=php_gettext.dll 把前面的分號去掉,重啟 Apache Server。
Gettext 的實現方式
程式設計者在程式碼中寫入所要顯示的欄位的標籤或是相關 messages,在運行程式時並不會直接顯示程式設計師所寫的資訊,它會根據設定的語系去找對應語系的文字,如果沒有找到才會去顯示程式碼中的原本的語言。例如:程式設計者在源代碼裏使用的是繁體中文,如果系統在 run 的時候設定成簡體中文( zh_CN ) 語系,程式會自動到相關的目錄 ( local/zh_CN/LC_MESSAGES/ ) 中尋找簡體中文的語系,如果找不到就會顯示原來的繁體中文,而這一切我們只需要做簡單的設定即可,gettext 會自動幫我們完成這些瑣碎的工作。
相關函數
- bind_textdomain_codeset — Specify the character encoding in which the messages from the DOMAIN message catalog will be returned
- bindtextdomain — Sets the path for a domain
- dcgettext — Overrides the domain for a single lookup
- dcngettext — Plural version of dcgettext
- dgettext — Override the current domain
- dngettext — Plural version of dgettext
- gettext — Lookup a message in the current domain
- ngettext — Plural version of gettext
- textdomain — Sets the default domain
本文只涉及到 bintextdomain()
、textdomain()
及 bind_text_domain_codeset()
這三個 functions。
Directories Structure
Local: 自定義名稱,可以按喜好改成 languages 等等,代碼中做相應調整就可以了。
Local\en_US: 英文語系所在資料夾,也可改成自己喜歡的,但是推薦用標準的 language code。
Local\zh_CN: 簡體中文語系所在資料夾,也可改成自己喜歡的,但是推薦用標準的 language code。
LC_MESSAGES:固定名稱,請不要修改。
Messages.mo/po: 多語的檔案名稱,可以按自己的喜好來命名,程式代碼中要做相關調整. .po 可編輯,.mo 不可編輯。
PHP 代碼實現
在程式初始化時,初始化相關多語參數:
- 語系代碼
- .mo 檔案名稱
- 多語文件所在資料夾
/**
* Init Local Language
* 目錄結構必須是
* xxx/local/zh_CN/LC_MESSAGES/messages.po/.mo
* xxx/local/zh_TW/LC_MESSAGES/messages.po/.mo
* @param string $lang_code 標準語言代碼,如: zh_TW, zh_CN
* @param string $mo_filename mo 檔案名
* @param string $localfile_url "local" 目錄所在的相對或是絕對路徑
* @author Dennis 20090826
* @return void
*/
function _initLocal ($lang_code,$mo_filename,$localfile_url)
{
//設置目的語言
putenv("LANG=$lang_code");
setlocale(LC_ALL, $lang_code);
//$package為 mo 文件的名字
$package = $mo_filename;
//綁定 mo 檔的路徑,
bindtextdomain($package, $localfile_url);
//設置搜索的 mo 檔的檔案名
textdomain($package);
//指定mo檔的返回到gettext的編碼
bind_textdomain_codeset($package, 'UTF-8');
}
_initLocal('en_US','messages', '/ehr/local');
多語書寫方式
include_once 'func.php';
echo gettext ('資通電腦');
// 也可以寫成如下
echo _('資通電腦');
創建 .po 多語二進位檔
編輯指令如下:xgettext -d [您定義的 PACKAGE 名稱] [程序文件名]
WIN32 下面的 xgettext、msgfmt 程序檔可以從 ( http://switch.dl.sourceforge.net ... ext-0.10.40-bin.zip (http://switch.dl.sourceforge.net ... ext-0.10.40-bin.zip) ) 下載,需要 libiconv.dll、libintl.dll 的支援。具體的指令使用方法都是相同的。
以上面 test.php 檔為例,xgettext -d message test.php
,運行後將產生一個 message.po 檔,內容如下:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR , YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-08-31 15:24+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: test.php:2
msgid "資通電腦"
msgstr ""
"message.po" 21L, 607C 21,1 All
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR , YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-08-31 15:24+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: test.php:2
msgid "資通電腦"
msgstr ""
裏面列出 test.php 檔裏所有調用 gettext 函式的字串,翻譯的時候只需將 msgid 值翻譯填入msgstr 即可,如翻譯成中文。
File message.po 內容如下:
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR , YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-08-31 15:24+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: test.php:2
msgid "資通電腦"
msgstr ""
"message.po" 21L, 607C 21,1 All
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR , YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2009-08-31 15:24+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME \n"
"Language-Team: LANGUAGE \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
#: test.php:2
msgid "資通電腦"
msgstr "ARES"
創建 MO 檔
.mo 多語文件是二進位,需要用特別的指令或是軟體來創建,檔的內容已編譯成二進位(打開看到的都是亂碼),不能用一般的 text editor 軟體編輯。創建指令如下:msgfmt -o message.mo message.po
,運行後將產生一個 message.mo 二進位檔。
最後將 message.po、message.mo 拷貝到相關語系(這裡翻譯的是一個英文語系,把 message.mo、message.po 放到 local/en_US/LC_MESSAGES/)的目錄下即可。
上面討論了利用 PHP gettext built-in functions 來實現應用程式的本地化。其優點:
- 執行效率高(有興趣的可以測試一下,其效率明顯高於把多語文件放在 php 的 array () 中和 database table 中)。
- 多語的唯一性,也就是說一個多語在多處引用時,只需要翻譯一次就好(例如 ehr系統中的「員工代碼」,幾乎系統中到處都有用到,用此種方式只需要翻譯一次就好)。
- 不會遺漏多語未本地化(沒有翻譯的仍會顯示原來的語言,在前台一下就可以辨別)。
當然事情都有兩面性,同樣 gettext 也有缺點:
- 必須要有特別的編輯(譯)工具。
- 學習 gettext 相關用法。