はじめに
先日行われた 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
のバリデーションについて軽く説明すると、それ自体モデルやコントローラーには依存せず使えるライブラリになっています。
そして、その中身で使われているのは dry-validation
という Gem です。
だとすればメッセージもそこで使われているものがデフォルトになっているはず、と思いロケールファイルを探してきました。
これを 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 の値を日本語化
次に Title
や Author
といった値も日本語化していきます。こちらはネームスペース等がさっぱり分からなかったので 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=>["みたしとらん!"]}}
の内容をもとにエラーメッセージが日本語で表示されるようになっています。動作させてみた結果はこんな感じでした。
文言の是非はともかく、意図した動作をしている事はお分かり頂けたかと思います。
課題
実際に動作させていて気がついた問題点がいくつかありました。
hanami アプリケーション自体にロケーリングは存在しない?
全体、あるいは apps
単位でコンフィグを書けそうな気がしたのですが、ドキュメントなど見つける事ができませんでした。
実はステップ1でロケールファイルを en
のままにしていたのは他の言語に変更する方法が分からなかったためです。
それなら、そこも i18n
を使うように設定すればいいんじゃないか?と感じた方もいらっしゃると思いますが…
config.messages = :i18n が受け付けられない
コントローラーの変更で config.messages = :yaml
と書いていましたが hanami
のバリデーションでは :i18n
も指定可能とされています。
しかし、少なくともコントローラーに直接記述した限りでは何故か例外が発生してしまいました。必要な設定を忘れているなどの問題かもしれませんが、今のところ動作に至っていません。
validation クラスの切り出し
単純に validation クラスだけを作成してしまうと new
と create
アクションの両方で同じテンプレートを使っているため、テンプレートの都合のためにロジック側を修正する必要が生じてしまい良さそうには思えません。
るびまで紹介されている Hanami::Interactor
を使って切り出せば良さそうですが、何となく歯切れの悪い説明がされていて気になります(きれいに役割の分担ができているので問題なさそうに見えるのですが、どのあたりで議論があり、何がまとめきれていないのかは把握していません)。
最後に
実際に hanami
を今すぐプロダクトで使いたい!と思った場合、上のような問題が発生するたびに自力で解決していくしか無さそうで、まだリスクがありそうに感じたというのが正直なところです。
まずは時間をかけてドキュメントやソースコードを追っていく事や機能面での妥協が許される会社内の案件や自分で使うツールなどで試しに採用して、考え方を理解していくのが良さそうに思いました。