RailsなどのI18nのやり方

Railsのアプリケーションを多言語化する場合、keyと定義する必要があります。翻訳文を探すとき、keyを与えて、翻訳文が返されます。例えば、config/locales/ja.ymlファイルで

ja:
  view:
    welcome: 本サイトへようこそ

のように定義して、

view側で

I18n.t 'view.welcome'

を呼ぶと、ちゃんと”本サイトへようこそ”が返されます。

PhoenixとGettext

Phoenixの場合はrailsのやりかとと少し異なります。Phoenixのアプリケーションを英語化や多言語化するとき、デフォルトではGettexを使います。Gettextはrailsのi18nと違って、keyを自分で定義する必要がありません。もしrailsのようlに使いたいなら、linguistというパケッジを入れる必要があります。

Gettextの使い方

gettext()関数を入れる

まず、多言語化したいところにgettext関数を入れる

例えば、テンプレート側で

<%= "welcome" %>

があります。

このwelcomeを日本語化したい場合、以下のように修正する必要があります。

<%= gettext("welcome") %>

potファイルを生成する

次は、gettextのテンプレートファイルを自動作成します。以下のコマンドを実行すると、

$ mix gettext.extract

priv/gettext/default.potというテンプレートファイルが作成されます(potはPortable Object Template略)。

ファイルの中身は以下のような感じです。

#: web/views/page_view.ex:2
msgid "welcome"
msgstr ""

poファイルを生成する

次は、テンプレートファイルからPOファイルを生成します。以下のコマンドを実行すると、

$ mix gettext.merge priv/gettext

初めて日本語のPOファイルを作りたい場合は

$ mix gettext.merge priv/gettext --locale ja

を実行する必要があります。

extractとmergeを同時に実行することもできます

$ mix gettext.extract --merge

上のコマンドを実行すると、/priv/gettext/ja/LC_MESSAGES/default.poなどのファイルが生成されます。 ファイルの中身は以下のような感じです。

#: web/views/page_view.ex:2
msgid "welcome"
msgstr ""

poファイルを手動で修正

/priv/gettext/ja/LC_MESSAGES/default.poファイルを以下のように日本語の翻訳を追加、他の言語の場合は/priv/gettext/xxx/LC_MESSAGES/default.poを修正

#: web/views/page_view.ex:2
msgid "welcome"
msgstr "ようこそ"

plugを追加する

多国言語の翻訳ファイルを追加した後、localeの設定が必要です。ここではplugを利用してlocaleの設定を行います。

サポートする言語をconfig/config.exsに追加します。

config :myapp, Myapp.Gettext,
  locales: ~w(en ja)

web/controllers/locale.exというファイルを追加します。ファイルの中身は以下のようになります。

defmodule Myapp.Locale do
  import Plug.Conn

  def init(_default), do: nil

  def call(conn, default) do
    case extract_param_locale(conn) do
      nil ->
        locale = List.first(extract_locale(conn)) || default
        set_locale conn, locale
      locale ->
        set_locale conn, locale
    end
  end

  defp set_locale(conn, locale) when is_nil(locale) do
    conn
  end

  defp set_locale(conn, locale) do
    Gettext.put_locale(Myapp.Gettext, locale)
    conn |> put_session(:locale, locale)
  end

  defp extract_param_locale(conn) do
    case conn.params["locale"] do
      nil ->
        case get_session(conn, :locale) do
          nil ->
            nil
          locale ->
            locale_check locale
        end
      locale ->
        locale_check locale
    end
  end

  defp locale_check(locale) do
    if Enum.member?(Myapp.Gettext.supported_locales, locale) do
      locale
    else
      nil
    end
  end

  defp extract_locale(conn) do
    # Filter for only known locales
    extract_accept_language(conn)
    |> Enum.filter(fn locale -> Enum.member?(Myapp.Gettext.supported_locales, locale) end)
  end

  defp extract_accept_language(conn) do
    case conn |> get_req_header("accept-language") do
      [value|_] ->
        value
        |> String.split(",")
        |> Enum.map(&parse_language_option/1)
        |> Enum.sort(&(&1.quality > &2.quality))
        |> Enum.map(&(&1.tag))
      _ ->
        []
    end
  end

  defp parse_language_option(string) do
    captures = ~r/^(?<tag>[\w\-]+)(?:;q=(?<quality>[\d\.]+))?$/i
    |> Regex.named_captures(string)

    quality = case Float.parse(captures["quality"] || "1.0") do
                {val, _} -> val
                _ -> 1.0
              end

    %{tag: captures["tag"], quality: quality}
  end
end

サーバーを起動して、localhost:4000?locale=jaにアクセスすると、日本語が表示され、localhost:4000?locale=enにアクセスすると、英語が表示されます。