Gettextの使い方メモ
はいコンバンハ。
Gettext
っていうモジュールがあるんですが、POTとかPOファイルとか、なんかよくわかんなかったので書いておきます。
できること
いわゆるi18n、アプリケーションの国際化ができます。Phoenixアプリ前提にしてしまいますが、例えばHTMLに
<h1><%= MyApp.Gettext.gettext "welcome!" %></h1>
と書いて、jaロケールのPOファイルには
msgid "welcome!" msgstr "ようこそおいでくださいました"
enロケールのPOファイルには
msgid "welcome!" msgstr "Welcome to My Page!"
と書いておくと、ロケールに応じていい感じに歓迎できるというわけです。ロケールの指定は、Gettext.put_locale/2
で動的に設定したり、config/config.exs`などに
config :my_app, MyApp.Gettext, default_locale: "ja"
と書いて行います。
わかんなかったこと
POファイルの作り方で混乱しました。Phoenixアプリでは、priv
の下にgettext
という謎ディレクトリが最初から作られています。
./priv └── gettext ├── en │ └── LC_MESSAGES │ └── errors.po └── errors.pot
ここにはPOTファイルとPOファイルがあります。POTはPortable Object Templateの略なので、テンプレートです。どういうこっちゃと言うと、おもむろに
mix gettext.merge priv/gettext --locale ja
と打ってみると、このテンプレートをもとにjaロケール用のPOファイルが作られるのです。
./priv └── gettext ├── en │ └── LC_MESSAGES │ └── errors.po ├── errors.pot └── ja └── LC_MESSAGES └── errors.po
まず大前提として、このテンプレートはコードから抽出された結果が吐き出される場所であるということを覚えておきましょう。ファイル名を変えたり内容でファイル分けたり、それなりに手で編集することもあるとは思いますが、日本語や英語をガリガリ書くのは別の場所です。
試しにUser
モデルに
def changeset(model, params \\ %{}) do ... |> validate_length(:password, min: 8, message: MyApp.Gettext.gettext("password too short", count: 8)) ... end
と書いて抽出してみます。抽出タスクは
mix gettext.extract
です。これをやると、default.pot
が作られます。default
という名前が気に入らない場合はdgettext("errors", "password too short")
のようにdgettext
でドメインを指定しましょう。
./priv └── gettext ├── default.pot ├── en │ └── LC_MESSAGES │ └── errors.po ├── errors.pot └── ja └── LC_MESSAGES └── errors.po
# default.pot ## This file is a PO Template file. ## ## `msgid`s here are often extracted from source code. ## Add new translations manually only if they're dynamic ## translations that can't be statically extracted. ## ## Run `mix gettext.extract` to bring this file up to ## date. Leave `msgstr`s empty as changing them here as no ## effect: edit them in PO (`.po`) files instead. msgid "" msgstr "" "Language: INSERT LANGUAGE HERE\n" #: web/models/user.ex:31 msgid "password too short" msgstr ""
User
モデルの中で使っているものが抽出されています。これでコードの変更がテンプレートファイルに反映されたので、各言語用のPOファイルへさらに反映させます。ここで使うのが先ほどのgettext.merge
タスクです。既に翻訳がいくつか書かれているところへ、新しい項目を放り込んでいくので”マージ”というわけです。
mix gettext.merge priv/gettext
en
とja
の下に、default.pot
からdefault.po
が作られています。
./priv └── gettext ├── default.pot ├── en │ └── LC_MESSAGES │ ├── default.po │ └── errors.po ├── errors.pot └── ja └── LC_MESSAGES ├── default.po └── errors.po
ようやく翻訳を書くところへきました。ja/LC_MESSAGES/default.po
に、"password too short"に対応する日本語を書きます。
## `msgid`s in this file come from POT (.pot) files. ## ## Do not add, change, or remove `msgid`s manually here as ## they're tied to the ones in the corresponding POT file ## (with the same domain). ## ## Use `mix gettext.extract --merge` or `mix gettext.merge` ## to merge POT files into PO files. msgid "" msgstr "" "Language: ja\n" #: web/models/user.ex:31 msgid "password too short" msgstr "%{count}文字以上じゃないとダメっていう規則なんですよ。すません。"
まとめ
- コード内で
gettext "foobar"
を使う mix gettext.extract
でテンプレートファイルを更新mix gettext.merge priv/gettext
で各言語ファイルに反映- 翻訳を書く
追記
コメントでphoenix-locale_jaを教えていただきました。Ectoのバリデーションエラーだけ翻訳を用意して頂いているようです。