備忘録、はじめました。

作業したこと忘れないようにメモっておきます。

go-i18nで必要なjsonをgoのソースコードから生成するgoi18n-parser

モチベーションとして、

  • 多言語対応をしたい
  • goのソースコードから自動で翻訳ファイルを作成したい
  • シンプルでいい(複雑な機能は要らない)

があり、最小限で作ってみたので紹介します。

github.com

goi18n-parser

goi18n-parser ではソースコードの解析に go/ast を使用しています。

go/astを使うと、goのソースコードを抽象構文木にして中身を解析することが出来ます。

今回解析したい内容は、

  • 翻訳関数の使われている部分

となります(この「翻訳関数」とは便宜的に書いてるだけです)。 goのソースコード内に翻訳関数が使われていて、その翻訳関数の引数が翻訳対象となるメッセージのIDとして記憶されていきます。

具体的には次のようなコードです:

    ...

    fmt.Println(SomeStruct.T("server_error_something"))

    return fmt.Error(T("server_error_invalid_user_session"))

    ...

上記の例だと翻訳関数は T になります。この Tgo/ast を使って見つけていきます。

今回作成したパッケージ goi18np を使って取得する場合は次のように書けます:

a := goi18np.Analyzer{}
a.AnalyzeFromFile("/path/to/code.go")
a.SaveJSON("/path/to/translate.json")

また、複数ファイルも同時に解析できます:

a.AnalyzeFromFiles([]string{"/path/to/code.go", "/path/to/code2.go"})

入力と出力の例を以下に紹介します。

package sample

import (
    "fmt"

    "github.com/nicksnyder/go-i18n/i18n"
)

type A struct {
    T i18n.TranslateFunc
}

func SampleCaller() {
    T, _ := i18n.Tfunc("eu-US")

    fmt.Println(T("sample_uniq_key"))
    for i := 0; i > 100; i++ {
        if i%10 == 0 {
            fmt.Println(T("sample_uniq_key_2"))
            x := T("sample_uniq_key_3")
            fmt.Println(x)
        }
    }

    T2, _ := i18n.Tfunc("en-US")
    a := A{T: T2}

    fmt.Println(a.T("sample_uniq_key_4"))

    fmt.Printf("%s,%s,%s", a.T("sample_uniq_key_4"), T("sample_uniq_key_5"), T("sample_uniq_key_2"))
    return
}

このソースコードを解析した結果が次のようになります:

[
  {
    "id": "sample_uniq_key",
    "translation": ""
  },
  {
    "id": "sample_uniq_key_2",
    "translation": ""
  },
  {
    "id": "sample_uniq_key_3",
    "translation": ""
  },
  {
    "id": "sample_uniq_key_4",
    "translation": ""
  },
  {
    "id": "sample_uniq_key_5",
    "translation": ""
  }
]

翻訳内容の部分を one または other をつけて、単数形と複数形の文章に分ける処理はしていません。 そこまでの自動化はせずに、必要になったときに人の手で追加する感じになると思います。

また、実際にjsonで保存するときは go1.8 で出てきた sort.Slice を使うなどして、ID順にソートして差分が少なくなるように工夫が必要かと思います。