10.2 ローカライズリソース

前の節ではどのようにしてLocaleを設定するかご紹介しました。Localeを設定したあとはどのようにしてLocaleに対応する情報を保存するかという問題を解決する必要があります。ここでの情報とは以下の内容を含みます:テキスト情報、時間と日時、通貨の値、画像、ファイルや動画といったリソース等です。ここではこれらの情報に対してご紹介していきたいと思います。Go言語ではこれらのフォーマットの情報をJSONに保存します。その後それぞれ適した方法によって表示します。(以下では日本語と英語の2つの言語を対比して例を挙げます。保存の形式はそれぞれen.jsonとja-JP.jsonです。)

ローカライズテキスト情報

この情報はWebアプリケーションを書く中で最も使われるもので、ローカライズリソースでも最も多い情報でもあります。ロケールの言語に合った方法でテキスト情報を表示したい場合、ひとつの方法としては必要となる言語に対応したmapを作成することでkey-valueの関係を維持するというものがあります。出力される前に最適なmapから対応するテキストを取り出します。以下は簡単な例です:

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["ja-JP"] = cn
    lang := "ja-JP"
    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関数を組み合わせることで実装することができます。下のコードをご覧ください:

en["how old"] ="I am %d years old"
cn["how old"] ="今年で%dになります"

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

上のコード例では内部の実装方法を使用しただけで、実際のデータはJSONの中に保存されています。そのため、json.Unmarshalを使って対応するmapにデータを追加することができます。

日付と日時のローカライズ

タイムゾーンの関係で、同一時刻においても異なる地域でその表示は異なってきます。またLocaleの関係で、時間のフォーマットも全く異なってきます。例えば日本語の環境下では:2013年 10月24日 水曜日 23時11分13秒 JSTとなり、英語の環境下ではWed Oct 24 23:11:13 CST 2012のように表示されます。ここでは2つの項目を解決しなければなりません。

  1. タイムゾーンの問題
  2. フォーマットの問題

$GOROOT/lib/timeパッケージのtimeinfo.zipにはlocaleに対応するタイムゾーンの定義が含まれています。対応する現在のlocaleの時間を取得するためには、まずtime.LoadLocation(name string)を使用して対応するタイムゾーンのlocaleを取得します。例えばAsia/ShanghaiまたはAmerica/Chicagoに対応するタイムゾーンデータです。その後、この情報を再利用し、time.Nowをコールすることにより得られるTimeオブジェクトとあわせて最終的な時間を取得します。詳細は以下の例をご覧ください(この例では上のいくつかの変数を採用しています):

en["time_zone"]="America/Chicago"
cn["time_zone"]="Asia/Tokyo"

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

本文形式を処理する似たような方法によって時間のフォーマットの問題を解決することができます。以下に例を挙げます:

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に置換されます
}

通過の値のローカライズ

各地域の通過の表示も異なります。処理方法も日時とあまり変わりありません。詳細は以下のコードをご覧ください:

en["money"] ="USD %d"
cn["money"] ="¥%d円"

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

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

ビューとリソースのローカライズ

Localeの違いによってビューを表示させる場合もあるかもしれません。これらのビューには画像、css、jsといった各種静的なリソースが含まれています。ではこれらの情報をどのようにして処理すべきでしょうか?まずlocaleによってファイル情報を構成しなければなりません。下のディレクトリの構成をご覧ください:

views
|--en  //英語のテンプレート
    |--images     //画像情報を保存
    |--js         //JSファイルを保存
    |--css        //cssファイルを保存
    index.tpl     //ユーザのトップページ
    login.tpl     //ログインのトップページ
|--ja-JP //日本語のテンプレート
    |--images
    |--js
    |--css
    index.tpl
    login.tpl

このディレクトリ構成があってはじめて以下のようなコードで実装することができます:

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

またindex.tplの中リソースの設定は以下のとおりです:

// jsファイル
<script type="text/javascript" src="views/{{.VV.Lang}}/js/jquery/jquery-1.8.0.min.js"></script>
// cssファイル
<link href="views/{{.VV.Lang}}/css/bootstrap-responsive.min.css" rel="stylesheet">
// 画像ファイル
<img src="views/{{.VV.Lang}}/images/btn.png">

このような方法を採用することでビューとリソースをローカライズすると、用意に拡張を行うことができます。

まとめ

この節ではどのようにしてローカライズリソースを使用し、保存するかご紹介しました。ある時は置換関数によって実装する必要があり、またある時はlangによって設定する必要があります。しかし最終的にはどれもkey-valueの方法によってLocaleに対応したデータを保存することになります。必要な時に対応するLocaleの情報を取り出して、もしそれがてkしうと情報であれば直接出力し、もし時間や日時または通過であった場合はfmtPrintfを使ったりその他のフォーマッタ関数によって処理する必要があります。異なるLocaleのビューとリソースに対しては最も簡単で、パスにlangを追加するだけで実装することができます。

results matching ""

    No results matching ""