Ruby
hanami

hanami のバリデーションメッセージを日本語化する

More than 1 year has passed since last update.

はじめに

先日行われた RubyKaigi でもセッションが設けられていましたが hanami はドメイン駆動設計を意識した Ruby の Web アプリケーションフレームワークです。

るびまで紹介されているのを見て「そういえば昔 Lotus とかあったけど、どうなったのかな?」と思って調べたところ Lotus が名前を改めたのが hanami だったと知った経緯などもあり、いい機会なのでチュートリアルを進めてみました。

殆どは先述のるびまでまとめられた内容が詳しいのですが、仮に実案件で使うとして個人的に絶対に外せないポイントがバリデーションメッセージの日本語化でした。

この記事ではそのあたりについて、自分で今理解している範囲でまとめていきます。

ステップ1. コントローラーにおける Params に依存しないメッセージの日本語化

チュートリアルを最後まで行うと、コントローラーには params のバリデーションが定義されています。

# apps/web/controllers/books/create.rb
module Web::Controllers::Books
  class Create
    # 略
    params do
      required(:book).schema do
        required(:title).filled(:str?)
        required(:author).filled(:str?)
      end
    end
    # 略
  end
end

実際にサーバを起動し、ブラウザからタイトルを空にした状態でリクエストを投げると Title must be filled のようなメッセージが得られると思います。

まずは後半の must be filled の箇所を日本語にします。

コードを書き換える前に hanami のバリデーションについて軽く説明すると、それ自体モデルやコントローラーには依存せず使えるライブラリになっています。

https://github.com/hanami/validations

そして、その中身で使われているのは dry-validation という Gem です。

https://github.com/dry-rb/dry-validation

だとすればメッセージもそこで使われているものがデフォルトになっているはず、と思いロケールファイルを探してきました。

これを config 以下に config/messages.yml として保存し、試しに値を上書きしてみます(ロケールとして en を指定したままですが、ここについては後述します)。

en:
  errors:
    filled?: "みたしとらん!"

次に、先ほどのコントローラーを開いてこのロケールファイルが使われるようにします。

# 必要な箇所だけ抜粋
params do
  configure do
    config.messages = :yaml
    config.messages_file = 'config/messages.yml'
  end

  required(:book).schema do
    required(:title).filled(:str?)
    required(:author).filled(:str?)
  end
end

config.messages についてはデフォルトで :yaml が指定されているので省略可能ですが、分かりやすさ重点で残しておきました。

この状態で再度リクエストを投げると、エラーメッセージが「Titleみたしとらん!」のように変更されると思います(されない場合、一度サーバを再起動してください)。

ステップ2. Params の値を日本語化

次に TitleAuthor といった値も日本語化していきます。こちらはネームスペース等がさっぱり分からなかったので i18n Gem を使って行う事にしました。Gemfile に gem 'i18n' を追加して bundle install を実行したら作業開始です。

まず p などで確認してもらうと分かりますが params.error_messages を取っている箇所で params.errors を表示させると次のような値が得られます。

{:book=>{:title=>["みたしとらん!"], :author=>["みたしとらん!"]}}

この構造であればロケールファイルを作って翻訳させる事ができそうです。 config/locales ディレクトリを作成し config/locales/ja.yml として次のような値を設定してみます。

ja:
  book:
    title: "タイトル"
    author: "著者"

このロケールファイルを使うための設定を行わないといけないので config/initializers/locale.rb を作成して起動時に i18n Gem がセットアップさせるよう変更します。

# config/initializers/locale.rb
require "i18n"

I18n.load_path = Dir[Hanami.root.join("config/locales/*.yml")]
I18n.available_locales = :ja
I18n.default_locale = :ja
I18n.backend.load_translations

これはサーバ起動時にしか読み込まれないと思うので、ここまで書いたらサーバを再起動してください。

次にテンプレート側に変更を加えます。本筋に関係ないので省きましたが haml テンプレートも問題なく使えます。

- unless params.valid?
  .errors
    %h3 There was a problem with your submission
    %ul
      - params.errors.each do |model, attrs|
        - attrs.each do |attr, messages|
          - messages.each do |message|
            %li="#{I18n.t("#{model}.#{attr}")}#{message}"

少し分かりにくいかもしれませんが

{:book=>{:title=>["みたしとらん!"], :author=>["みたしとらん!"]}}

の内容をもとにエラーメッセージが日本語で表示されるようになっています。動作させてみた結果はこんな感じでした。

スクリーンショット 2017-09-22 16.45.06.png

文言の是非はともかく、意図した動作をしている事はお分かり頂けたかと思います。

課題

実際に動作させていて気がついた問題点がいくつかありました。

hanami アプリケーション自体にロケーリングは存在しない?

全体、あるいは apps 単位でコンフィグを書けそうな気がしたのですが、ドキュメントなど見つける事ができませんでした。

実はステップ1でロケールファイルを en のままにしていたのは他の言語に変更する方法が分からなかったためです。

それなら、そこも i18n を使うように設定すればいいんじゃないか?と感じた方もいらっしゃると思いますが…

config.messages = :i18n が受け付けられない

コントローラーの変更で config.messages = :yaml と書いていましたが hanami のバリデーションでは :i18n も指定可能とされています。

しかし、少なくともコントローラーに直接記述した限りでは何故か例外が発生してしまいました。必要な設定を忘れているなどの問題かもしれませんが、今のところ動作に至っていません。

validation クラスの切り出し

単純に validation クラスだけを作成してしまうと newcreate アクションの両方で同じテンプレートを使っているため、テンプレートの都合のためにロジック側を修正する必要が生じてしまい良さそうには思えません。

るびまで紹介されている Hanami::Interactor を使って切り出せば良さそうですが、何となく歯切れの悪い説明がされていて気になります(きれいに役割の分担ができているので問題なさそうに見えるのですが、どのあたりで議論があり、何がまとめきれていないのかは把握していません)。

最後に

実際に hanami を今すぐプロダクトで使いたい!と思った場合、上のような問題が発生するたびに自力で解決していくしか無さそうで、まだリスクがありそうに感じたというのが正直なところです。

まずは時間をかけてドキュメントやソースコードを追っていく事や機能面での妥協が許される会社内の案件や自分で使うツールなどで試しに採用して、考え方を理解していくのが良さそうに思いました。