# 本地化資源

前面小節我們介紹了如何設定 Locale，設定好 Locale 之後我們需要解決的問題就是如何儲存相應的 Locale 對應的資訊呢？這裡面的資訊包括：文字資訊、時間和日期、貨幣值、圖片、包含檔案以及檢視等資源。那麼接下來我們將對這些資訊一一進行介紹，Go 語言中我們把這些格式資訊儲存在 JSON 中，然後透過合適的方式展現出來。(接下來以中文和英文兩種語言對比舉例，儲存格式檔案 en.json 和 zh-CN.json)

## 本地化文字訊息

文字資訊是編寫 Web 應用中最常用到的，也是本地化資源中最多的資訊，想要以適合本地語言的方式來顯示文字資訊，可行的一種方案是 : 建立需要的語言相應的 map 來維護一個 key-value 的關係，在輸出之前按需從適合的 map 中去取得相應的文字，如下是一個簡單的範例：

```go
package main

import "fmt"

var locales map[string]map[string]string

func main() {
    locales = make(map[string]map[string]string, 2)
    en := make(map[string]string, 10)
    en["pea"] = "pea"
    en["bean"] = "bean"
    locales["en"] = en
    cn := make(map[string]string, 10)
    cn["pea"] = "豌豆"
    cn["bean"] = "毛豆"
    locales["zh-CN"] = cn
    lang := "zh-CN"
    fmt.Println(msg(lang, "pea"))
    fmt.Println(msg(lang, "bean"))
}

func msg(locale, key string) string {
    if v, ok := locales[locale]; ok {
        if v2, ok := v[key]; ok {
            return v2
        }
    }
    return ""
}
```

上面範例示範了不同 locale 的文字翻譯，實現了中文和英文對於同一個 key 顯示不同語言的實現，上面實現了中文的文字訊息，如果想切換到英文版本，只需要把 lang 設定為 en 即可。

有些時候僅是 key-value 替換是不能滿足需要的，例如"I am 30 years old"，中文表達是"我今年 30 歲了"，而此處的 30 是一個變數，該怎麼辦呢？這個時候，我們可以結合`fmt.Printf`函式來實現，請看下面的程式碼：

```go
en["how old"] ="I am %d years old"
cn["how old"] ="我今年%d 歲了"

fmt.Printf(msg(lang, "how old"), 30)
```

上面的範例程式碼僅用以示範內部的實現方案，而實際資料是儲存在 JSON 裡面的，所以我們可以透過`json.Unmarshal`來為相應的 map 填充資料。

## 本地化日期和時間

因為時區的關係，同一時刻，在不同的地區，表示是不一樣的，而且因為 Locale 的關係，時間格式也不盡相同，例如中文環境下可能顯示：`2012 年 10 月 24 日 星期三 23 時 11 分 13 秒 CST`，而在英文環境下可能顯示:`Wed Oct 24 23:11:13 CST 2012`。這裡面我們需要解決兩點:

1. 時區問題
2. 格式問題

$GOROOT/lib/time 套件中的 timeinfo.zip 含有 locale 對應的時區的定義，為了獲得對應於當前 locale 的時間，我們應首先使用`time.LoadLocation(name string)`取得相應於地區的 locale，比如`Asia/Shanghai`或`America/Chicago`對應的時區資訊，然後再利用此資訊與呼叫`time.Now`獲得的 Time 物件協作來獲得最終的時間。詳細的請看下面的例子(該例子採用上面例子的一些變數):

```go
en["time_zone"]="America/Chicago"
cn["time_zone"]="Asia/Shanghai"

loc,_:=time.LoadLocation(msg(lang,"time_zone"))
t:=time.Now()
t = t.In(loc)
fmt.Println(t.Format(time.RFC3339))
```

我們可以透過類似處理文字格式的方式來解決時間格式的問題，舉例如下:

```go
en["date_format"]="%Y-%m-%d %H:%M:%S"
cn["date_format"]="%Y 年%m 月%d 日 %H 時%M 分%S 秒"

fmt.Println(date(msg(lang,"date_format"),t))

func date(fomate string,t time.Time) string{
    year, month, day = t.Date()
    hour, min, sec = t.Clock()
    //解析相應的%Y %m %d %H %M %S 然後回傳資訊
    //%Y 替換成 2012

    //%m 替換成 10

    //%d 替換成 24

}
```

## 本地化貨幣值

各個地區的貨幣表示也不一樣，處理方式也與日期差不多，細節請看下面程式碼:

```go
en["money"] ="USD %d"
cn["money"] ="￥%d 元"

fmt.Println(money_format(msg(lang,"date_format"),100))

func money_format(fomate string,money int64) string{
    return fmt.Sprintf(fomate,money)
}
```

## 本地化檢視和資源

我們可能會根據 Locale 的不同來展示檢視，這些檢視包含不同的圖片、css、js 等各種靜態資源。那麼應如何來處理這些資訊呢？首先我們應按 locale 來組織檔案資訊，請看下面的檔案目錄安排：

```markup
views
|--en  //英文範本
    |--images     //儲存圖片資訊
    |--js         //儲存 JS 檔案
    |--css        //儲存 css 檔案
    index.tpl     //使用者首頁
    login.tpl     //登陸首頁
|--zh-CN //中文範本
    |--images
    |--js
    |--css
    index.tpl
    login.tpl
```

有了這個目錄結構後我們就可以在渲染的地方這樣來實現程式碼：

```go
s1, _ := template.ParseFiles("views/"+lang+"/index.tpl")
VV.Lang=lang
s1.Execute(os.Stdout, VV)
```

而對於裡面的 index.tpl 裡面的資源設定如下：

```markup
// js 檔案
<script type="text/javascript" src="views/{{.Lang}}/js/jquery/jquery-1.8.0.min.js"></script>
// css 檔案
<link href="views/{{.Lang}}/css/bootstrap-responsive.min.css" rel="stylesheet">
// 圖片檔案
<img src="views/{{.Lang}}/images/btn.png">
```

採用這種方式來本地化檢視以及資源時，我們就可以很容易的進行擴充套件了。

## 總結

本小節介紹了如何使用及儲存本地資源，有時需要透過轉換函式來實現，有時透過 lang 來設定，但是最終都是透過 key-value 的方式來儲存 Locale 對應的資料，在需要時取出相應於 Locale 的資訊後，如果是文字資訊就直接輸出，如果是時間日期或者貨幣，則需要先透過`fmt.Printf`或其他格式化函式來處理，而對於不同 Locale 的檢視和資源則是最簡單的，只要在路徑裡面增加 lang 就可以實現了。

## links

* [目錄](https://github.com/doggy8088/build-web-application-with-golang-zhtw/tree/4cbbaa31bdfc3678915eb91f23db2f1bad554a20/preface.md)
* 上一節: [設定預設地區](https://willh.gitbook.io/build-web-application-with-golang-zhtw/10.0/10.1)
* 下一節: [國際化站點](https://willh.gitbook.io/build-web-application-with-golang-zhtw/10.0/10.3)
