假設有以下的程序, 輸出: “Hello, world!”.
package main
import (
"fmt"
)
func main() {
fmt.Println("Hello, world!")
}
現在要讓改程序支持不同語言的用戶, 然後以本地語言輸出相同意思的信息. 這就是很多程序面臨的國際化問題.
程序的國際化一般涉及到編碼和翻譯兩個概念. 其中編碼一般采用 UTF8 編碼標准, Go 語言已經完美支持. 而目前常見翻譯技術是 Qt 的 tr
函數和 GNU gettext 提供的 gettext
函數, 另外微軟的 MFC
也有自己的多國語言支持方式.
Go 語言目前還沒有標准的多國語言翻譯方式. 不過筆者已經初步將 gettext
的運行時環境移植到了 Go 語言(采用純 Go 實現, 無其他依賴). Go 語言版的 gettext
名字為 gettext-go
, 項目地址在: http://code.google.com/p/gettext-go.
gettext-go 同時也借鑒了 Qt 的翻譯上下文特性. 在 GNU gettext 的 po
和 mo
翻譯文件中都是含有 msgctxt
上下文信息的, 但是 C/C++ 的翻譯接口函數並沒有上下文的參數, 因此 傳統的 gettext
函數沒有設置上下文的參數.
可以去 godoc.org 或 gowalker.org 查看 gettext-go 的文檔.
基於 gettext-go , 我們可以很容易給 Go 程序增加多國語言的支持:
package main
import (
"fmt"
"code.google.com/p/gettext-go/gettext"
)
func main() {
gettext.BindTextdomain("hello", "local")
gettext.Textdomain("hello")
fmt.Println(gettext.Gettext("Hello, world!"))
}
其中 gettext.BindTextdomain
是綁定翻譯的空間, 其中 "hello"
是對應翻譯一類信息的翻譯, "local"
為翻譯文件的所在路徑(這裡當前目錄下的”local”)子目錄.
按照 GNU gettext 的習慣, 簡體中文對應的翻譯文件為 "local/zh_CN/LC_MESSAGES/hello.mo"
. 不同語言的命名有一個國際規范, 比如繁體中文對應 "zh_TW"
, 美國英文對應 "en_US"
等等. 但是 gettext-go 對名字並沒有特殊的要求.
gettext.BindTextdomain
可以綁定多個翻譯空間, 但是同一個時刻只能使用一個翻譯空間.
這裡我們使用 gettext.Textdomain
指定當前的翻譯空間為 "hello"
.
運行新的程序程序, 發現輸出還是: “Hello, world!”.
這是因為缺少翻譯文件…
未來, gettext-go 會開發一個 GNU gettext 工具集 中 的 xgettext
類似工具, 用於從程序中提取要翻譯的字符串.
不過目前, 我們只能手工支持翻譯文件了(還好這個例子只有一個字符串需要翻譯).
創建 "local/zh_CN/LC_MESSAGES/hello.po"
文件, 內容如下:
msgid ""
msgstr ""
msgctxt "main.main"
msgid "Hello, world!"
msgstr "你好, 世界!"
保存為UTF8編碼格式.
然後用 GNU gettext 工具集中的 msgfmt
命令將 hello.po
文件編譯為 hello.mo
文件:
msgfmt -o hello.mo hello.po
如果是 Windows 用戶, 可以下載 poedit 翻譯工具. 然後用 poedit 打開 hello.po
文件, 點擊保存後會自動生成 hello.mo
文件(也是 poedit 的bin目錄下自帶的msgfmt
命令生成的).
重新運行新的程序程序, 還是輸出: “Hello, world!” ?
在上一節, 我們已經制作了簡體中文的翻譯文件 "local/zh_CN/LC_MESSAGES/hello.mo"
, 然後輸出依然是英文.
這是因為 gettext-go 翻譯時不僅要依賴對應語言的翻譯文件, 還需要知道要范圍為哪種語言(和網上翻譯類似, 需要知道翻譯的目標語言).
如果沒有指定翻譯語言, gettext-go 會嘗試獲取本地的默認語言環境, 主要是通過檢查 $(LC_MESSAGES)
和 $(LANG)
兩個環境變量. 如果兩個環境變量都沒有設置, 那麼默認是不進行翻譯的.
我們設置環境變量後重新運行程序(Windows):
set LANG=zh_CN
go run hello.go
這裡時候應該可以輸出中文了.
如果不想使用默認的本地語言環境, 也可以用 gettext.SetLocale
接口設置本地語言環境.
func main() {
gettext.SetLocale("zh_CN")
gettext.BindTextdomain("hello", "local")
gettext.Textdomain("hello")
fmt.Println(gettext.Gettext("Hello, world!"))
}
這樣可以可以需要采用合適的語言翻譯文件.
Go 語言版的 gettext-go 的每個 gettext.Gettext
調用都有一個隱含的上下文信息(如果想自己指定上下文可以使用 gettext.PGettext
).
默認的上下文為包含 gettext.Gettext
調用的函數名稱, 比如:
main.init
main.init
main.main
main.func
上下文對應 Go 的運行時調用者名稱, 具體實現在這裡: caller.go .
-local
參數, 用於設置本地語言comments powered by Disqus