前面小節介紹了如何處理本地化資源,即 Locale 一個相應的配置檔案,那麼如果處理多個的本地化資源呢?而對於一些我們經常用到的例如:簡單的文字翻譯、時間日期、數字等如果處理呢?本小節將一一解決這些問題。
在開發一個應用的時候,首先我們要決定是隻支援一種語言,還是多種語言,如果要支援多種語言,我們則需要制定一個組織結構,以方便將來更多語言的新增。在此我們設計如下:Locale 有關的檔案放置在 config/locales 下,假設你要支援中文和英文,那麼你需要在這個資料夾下放置 en.json 和 zh.json。大概的內容如下所示:
# zh.json
{
"zh": {
"submit": "提交",
"create": "建立"
}
}
# en.json
{
"en": {
"submit": "Submit",
"create": "Create"
}
}
為了支援國際化,在此我們使用了一個國際化相關的套件——go-i18n,首先我們向 go-i18n 套件註冊 config/locales 這個目錄,以載入所有的 locale 檔案
Tr:=i18n.NewLocale()
Tr.LoadPath("config/locales")
這個套件使用起來很簡單,你可以透過下面的方式進行測試:
fmt.Println(Tr.Translate("submit"))
//輸出 Submit
Tr.SetLocale("zh")
fmt.Println(Tr.Translate("submit"))
//輸出“提交”
上面我們介紹了如何自動載入自訂語言套件,其實 go-i18n 函式庫已經預載入了很多預設的格式資訊,例如時間格式、貨幣格式,使用者可以在自訂配置時改寫這些預設配置,請看下面的處理過程:
//載入預設配置檔案,這些檔案都放在 go-i18n/locales 下面
//檔案命名 zh.json、en.json、en-US.json 等,可以不斷的擴充套件支援更多的語言
func (il *IL) loadDefaultTranslations(dirPath string) error {
dir, err := os.Open(dirPath)
if err != nil {
return err
}
defer dir.Close()
names, err := dir.Readdirnames(-1)
if err != nil {
return err
}
for _, name := range names {
fullPath := path.Join(dirPath, name)
fi, err := os.Stat(fullPath)
if err != nil {
return err
}
if fi.IsDir() {
if err := il.loadTranslations(fullPath); err != nil {
return err
}
} else if locale := il.matchingLocaleFromFileName(name); locale != "" {
file, err := os.Open(fullPath)
if err != nil {
return err
}
defer file.Close()
if err := il.loadTranslation(file, locale); err != nil {
return err
}
}
}
return nil
}
透過上面的方法載入配置資訊到預設的檔案,這樣我們就可以在我們沒有自訂時間資訊的時候執行如下的程式碼取得對應的資訊:
//locale=zh 的情況下,執行如下程式碼:
fmt.Println(Tr.Time(time.Now()))
//輸出:2009 年 1 月 08 日 星期四 20:37:58 CST
fmt.Println(Tr.Time(time.Now(),"long"))
//輸出:2009 年 1 月 08 日
fmt.Println(Tr.Money(11.11))
//輸出:¥11.11
上面我們實現了多個語言套件的管理和載入,而一些函式的實現是基於邏輯層的,例如:"Tr.Translate"、"Tr.Time"、"Tr.Money"等,雖然我們在邏輯層可以利用這些函式把需要的參數進行轉換後在範本層渲染的時候直接輸出,但是如果我們想在模版層直接使用這些函式該怎麼實現呢?不知你是否還記得,在前面介紹範本的時候說過:Go 語言的範本支援自訂範本函式,下面是我們實現的方便操作的 mapfunc:
- 文字資訊
文字資訊呼叫Tr.Translate
來實現相應的資訊轉換,mapFunc 的實現如下:
func I18nT(args ...interface{}) string {
ok := false
var s string
if len(args) == 1 {
s, ok = args[0].(string)
}
if !ok {
s = fmt.Sprint(args...)
}
return Tr.Translate(s)
}
註冊函式如下:
t.Funcs(template.FuncMap{"T": I18nT})
範本中使用如下:
{{.V.Submit | T}}
- 時間日期
時間日期呼叫Tr.Time
函式來實現相應的時間轉換,mapFunc 的實現如下:
func I18nTimeDate(args ...interface{}) string {
ok := false
var s string
if len(args) == 1 {
s, ok = args[0].(string)
}
if !ok {
s = fmt.Sprint(args...)
}
return Tr.Time(s)
}
註冊函式如下:
t.Funcs(template.FuncMap{"TD": I18nTimeDate})
範本中使用如下:
{{.V.Now | TD}}
- 貨幣資訊
貨幣呼叫Tr.Money
函式來實現相應的時間轉換,mapFunc 的實現如下:
func I18nMoney(args ...interface{}) string {
ok := false
var s string
if len(args) == 1 {
s, ok = args[0].(string)
}
if !ok {
s = fmt.Sprint(args...)
}
return Tr.Money(s)
}
註冊函式如下:
t.Funcs(template.FuncMap{"M": I18nMoney})
範本中使用如下:
{{.V.Money | M}}
透過這小節我們知道了如何實現一個多語言套件的 Web 應用,透過自訂語言套件我們可以方便的實現多語言,而且透過配置檔案能夠非常方便的擴充多語言,預設情況下,go-i18n 會自定載入一些公共的配置資訊,例如時間、貨幣等,我們就可以非常方便的使用,同時為了支援在範本中使用這些函式,也實現了相應的範本函式,這樣就允許我們在開發 Web 應用的時候直接在範本中透過 pipeline 的方式來操作多語言套件。