SPA開発 7. Userモデル開発 #04
2019年11月17日に更新

【Rails】エラーメッセージの日本語化【i18nとja.ymlのセッティング】

今回達成すること

今回は、バリデーションエラーメッセージの日本語化(i18n)の実装を行い、同時にapplication.rbセッティングも行います。

具体的に以下の手順で実装していきます。

  • ユーザーモデルの「password」カラムにバリデーションを設定
  • passwordハッシュ化の設定
  • エラーメッセージの日本語化(i18n)の実装
  • application.rbのセッティング
    1. TimeZoneの変更
    2. Zeitwerkの設定

最終的なapplication.rbとuser.rbはこのようになります。

config/application.rb
require_relative 'boot'

require "rails"
# Pick the frameworks you want:
require "active_model/railtie"
require "active_job/railtie"
require "active_record/railtie"
require "active_storage/engine"
require "action_controller/railtie"
require "action_mailer/railtie"
require "action_view/railtie"
require "action_cable/engine"
# require "sprockets/railtie"
require "rails/test_unit/railtie"

# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)

module Myapp8
  class Application < Rails::Application
    # Initialize configuration defaults for originally generated Rails version.
    config.load_defaults 6.0

    # 表示TimeZone
    config.time_zone = 'Tokyo'

    # DB保存時間をlocal(Tokyo)にする
    config.active_record.default_timezone = :local

    # i18n
    config.i18n.default_locale = :ja

    # Zeitwerk $LOAD_PATHにPathを追加しない(Zeitwerk有効時false推奨)
    config.add_autoload_paths_to_load_path = false

    # autoload path => ActiveSupport::Dependencies.autoload_paths
    config.autoload_paths += %W(#{config.root}/lib/validator)

    # Settings in config/environments/* take precedence over those specified here.
    # Application configuration can go into files in config/initializers
    # -- all .rb files in that directory are automatically loaded after loading
    # the framework and any gems in your application.

    # Only loads a smaller set of middleware suitable for API only apps.
    # Middleware like session, flash, cookies can be added back manually.
    # Skip views, helpers and assets when generating a new resource.
    config.api_only = true
  end
end

app/models/user.rb
class User < ApplicationRecord
  before_validation :downcase_email

  # password hashing
  has_secure_password

  # validates
  validates :email, presence: true, email: { allow_blank: true }
  VALID_PASSWORD_REGEX = /\A[\w\-]+\z/
  validates :password, presence: true,
                       length: { minimum: 8 },
                       format: {
                         with: VALID_PASSWORD_REGEX,
                         message: :invalid_password
                       },
                       allow_blank: true

  # methods
  # 自分以外のactiveなユーザーがいればtrueを返す
  def activated?(email)
    users = new_record? ? User.all : User.where.not(id: self.id)
    users.find_by(email: email, activated: true).present?
  end

  private
    # validatesにemail小文字化
    def downcase_email
      self.email = email.downcase if self.email
    end
end

それでは実装に移りましょう。

passwordカラムのバリデーション

「password」カラムには、このようなバリデーションを設定していきます。

  • 入力必須
  • 8文字以上
  • 書式は英数字、ハイフン、アンダーバーのみ利用可能

user.rbに下記を追加してください。

app/models/user.rb
class User < ApplicationRecord
	...

  # validates
  validates :email, presence: true, email: { allow_blank: true }
  
  # 追記
  VALID_PASSWORD_REGEX = /\A[\w\-]+\z/
  validates :password, presence: true,
                       length: { minimum: 8 },
                       format: {
                         with: VALID_PASSWORD_REGEX,
                         message: :invalid_password
                       },
                       allow_blank: true
	...
end

入力必須

  • presence: true

    trueにすると入力必須となります。

8文字以上

  • length: { minimum: 8 }

    文字列の長さの最小を指定するには、lengthにminimumのパラメータを渡します。

    ちなみに、最大の長さを制限したいとき => { maximum: 20 }

    文字列の長さを完全一致で制限したいとき => { is: 8 }

    8文字以上、20文字以下はこのように指定します。 => { minimum: 8, maximum: 20 }

今回は gem ‘bcrypt’ の設定により、最大72文字までの制限がついていますのでmaximumは指定しません。

書式は英数字、ハイフン、アンダーバーのみ利用可能

Rubyの正規表現を利用します。

  • /\A[\w\-]+\z/

    これを分解すると。。。

    \A => 文字列の先頭一致

    [ ] => [ ]内のいずれか1文字

    \w => 英数字とアンダーバー

    - => ハイフン

    + => 1回以上の繰り返し

    \z => 文字列の文末一致

まとめると。。。

「先頭から文末まで英数字かアンダーバーかハイフンを1文字以上繰り返す」文字列に制限しています。

パスワードは制限をかけすぎると第三者に読み解かれるリスクがありますので、許容範囲を広めにしています。

  • allow_blank: true

    nilもしくは空白時に検証をスキップします。

    これはまさにRailsチュートリアルの手法を採用しています。

    新規ユーザー登録時に空のパスワードが有効になってしまうのかと心配になるかもしれませんが、安心してください。6.3.3で説明したように、has_secure_passwordでは (追加したバリデーションとは別に) オブジェクト生成時に存在性を検証するようになっているため、空のパスワード (nil) が新規ユーザー登録時に有効になることはありません。

    第10章 ユーザーの更新・表示・削除 - Railsチュートリアル

以上でユーザーモデルのバリデーションは完了です。

passwordハッシュ化の設定を行う

passwordハッシュ化とは、passwordをランダムな文字列に変換してデータベースに保存することを言います。

ちなみに、暗号化は復元可能、ハッシュ化は復元不可を意味します。

ハッシュ化されたpassword

2019-11-15 09-49-54

Gem ‘bcrypt’

パスワードを安全なハッシュに変換するには、Gem bcryptを使います。

インストールがまだの方は、Gemfilegem 'bcrypt'を追記し、$ bundle install コマンドを実行してください。

Railsのhas_secure_passwordメソッド

ハッシュ化されたパスワードをデータベースに保存するには、Railsのhas_secure_passwordメソッドを使います。

使い方は、user.rbhas_secure_passwordを追記するだけです。

app/models/user.rb
class User < ApplicationRecord
  before_validation :downcase_email

  # 追記
  has_secure_password
	...
end

has_secure_passwordメソッドは、次のような機能を持ちます。

  • ハッシュ化したパスワードを、データベース内のpassword_digestという属性に保存できるようになる。

  • passwordpassword_confirmation(パスワード確認フォーム)が使えるようになる。

    また、双方が一致するかどうかのバリデーションも追加される。

  • authenticateメソッドが使えるようになる。

    引数の文字列がパスワードと一致するとUserオブジェクトを、間違っているとfalse返すメソッド

この機能が使える条件として、モデル内にpassword_digestというカラムが含まれている必要があります。

参考

第6章 ユーザーのモデルを作成する - Railsチュートリアル

passwordハッシュ化のまとめ

あれこれ言いましたが、passwordのハッシュ化は

  1. Gem bcryptをインストールして、
  2. モデルにhas_secure_passwordを追記する

だけで実現します。

エラーメッセージの日本語化(i18n)の実装

さあ、メイントピックのお時間です。

バリデーションエラーを日本語化していきましょう。

i18nとは?

翻訳や多言語化をサポートするためのRubyのGemです。

Rails2.2以降からデフォルトで組み込まれています。

Rails 国際化 (i18n) API - Rails ガイド

現状の確認

現在の状況を整理します。

ターミナルからrails consoleにログインしユーザーを作成してみます。

myapp $ rails c
irb(main):001:0> user = User.new(email:"user@example.com", password:"pass")
irb(main):002:0> user.save
irb(main):003:0> user.errors.full_messages
=> ["Password is too short (minimum is 8 characters)"]
  • user.errors.full_messages

    user変数に保存されているエラーメッセージを確認することができます。

結果、["Password is too short (minimum is 8 characters)"]

パスワードが短いよ!と英語でエラーメッセージが表示されました。

エラーメッセージは英語のままですね。このエラーメッセージを日本語化していきます。

rails consoleからはexitで抜けておいてください。

1. 日本語化の基となるファイルを作成する

Railsの多言語化対応は、ymlファイルで管理します。

config/locales ディレクトリ直下に、ja.ymlを作成しましょう。

myapp $ touch config/locales/ja.yml

2. ja.ymlファイルをコピペする

下記のgithubからja.ymlの内容をコピペします。

https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/ja.yml

「Row」ボタンをクリックして全選択するとコピーが簡単に行えますよ。

2019-11-15 11-07-54

config/locales/ja.yml
ja:
  activerecord:
    errors:
      messages:
        record_invalid: 'バリデーションに失敗しました: %{errors}'
        restrict_dependent_destroy:
          has_one: "%{record}が存在しているので削除できません"
          has_many: "%{record}が存在しているので削除できません"
  date:
  ...

3. application.rbに日本語ファイルを利用する設定を行う

Railsのデフォルトは、en.ymlファイルを参照しています。

myapp $ rails runner "puts I18n.default_locale"

en

$ rails runnerは、Railsの環境下で、引数に渡したRubyコードを実行するコマンドです。$ rails r と省略することができます。

これをja.ymlファイルを参照するように変更します。

application.rbを編集します。

config/application.rb
module Myapp8
  class Application < Rails::Application
    ...
    # 追記
    # i18n
    config.i18n.default_locale = :ja

もう一度 $ rails r で確認してみましょう。

myapp $ rails r "puts I18n.default_locale"

ja

OK!!これでi18nの初期設定は完了です。

rails consoleで確認してみる

もう一度エラーを吐き出すユーザーを作成してみます。

myapp $ rails c
irb(main):001:0> user = User.new(email:"user@example.com", password:"pass")
irb(main):002:0> user.save
irb(main):003:0> user.errors.full_messages
=> ["Passwordは8文字以上で入力してください"]

OK。エラーが日本語になりました!ただ、ここの「password」は「パスワード」と表示したい。。。

これはja.ymlファイルを編集することで実現します。

ja.ymlの編集方法

ja.ymlファイルは自分のアプリに合わせて編集していく必要があります。

activerecord:以下にモデル名、カラム名の日本語読みを追加します。

config/locales/ja.yml
ja:
  activerecord:
    models:
      user: ユーザー
    attributes:
      user:
        email: メールアドレス
        password: パスワード
        activated: アクティブプラグ
        admin: 管理者プラグ
    errors:
    ...

ja.ymlの注意点

Railsはymlファイルの改行インデントで日本語化のパスを参照しています。

例えば、カラム名の場合。

attributes: => モデル名 => カラム名 の順に改行とインデントを入れる必要があります。

attributes:         # attributes:の直下に
  user:            # モデル名を指定し
    email: メールアドレス   # カラム名を指定する

日本語化の確認

ちゃんと設定できたか確認してみましょう。

rails consoleにログインしましょう。

既にconsoleにいる場合は、再読み込みコマンドreload!を実行してください。

irb(main):028:0> User.model_name.human # モデル名日本語化の確認
=> "ユーザー"

irb(main):029:0> User.human_attribute_name :email # カラム名日本語化の確認
=> "メールアドレス"

irb(main):029:0> User.human_attribute_name :password
=> "パスワード"

irb(main):031:0> User.human_attribute_name :activated
=> "アクティブプラグ"

irb(main):032:0> User.human_attribute_name :admin
=> "管理者プラグ"

上のように日本語化できていれば成功です。

エラーの出るユーザーを保存しようとすると、 全てのエラーメッセージが日本語表示となりました。

irb(main):007:0> user.errors.full_messages
=> ["パスワードは8文字以上で入力してください"]

ja.ymlに独自のエラーメッセージを追加する

上で設定したpasswordの書式エラー「invalid_password」のメッセージを追加します。

app/models/user.rb
format: {
 with: VALID_PASSWORD_REGEX,
 message: :invalid_password # ← このエラーメッセージを追加する
},

ja.ymlの116行目の「errors:」以下の「messages:」直下に追加します。

config/locales/ja.yml(116行目以下)
  errors:
    format: "%{attribute}%{message}"
    messages:
      invalid_password: は半角英数字、ハイフン、アンダーバーが使えます # ← 追記

正しく設定てきたか確認してみます。

rails consoleにログインしてpassword書式エラーのユーザーを作成してみましょう。

myapp $ rails c
irb(main):001:0> user = User.new(email:"user@example.com", password:"pass~./word")
irb(main):002:0> user.save
irb(main):003:0> user.errors.full_messages
=> ["パスワードは半角英数字、ハイフン、アンダーバーが使えます"]

OK!ちゃんとja.ymlが読み込まれていますね。

invalidエラーメッセージを変えておく

ja.yml130行目のinvalidエラーメッセージは、主に書式が正しくない場合に利用します。

config/locales/ja.yml(130行目あたり)
  errors:
    format: "%{attribute}%{message}"
    messages:
      ...
      invalid: は不正な値です

「は不正な値です」はユーザーからすれば、次にどういう行動を取れば正解か?が分かりにくいエラーメッセージです。

そこでよりエラーが理解できる「の書式が正しくありません」に変更します。

config/locales/ja.yml
  errors:
    format: "%{attribute}%{message}"
    messages:
      ...
      invalid: の書式が正しくありません # 変更

こいつも確認してみましょう。

emailの書式エラーメッセージとして設定していますので、正しくないemailで保存してみます。

myapp $ rails c
irb(main):001:0> user = User.new(email:"test.com", password:"password")
irb(main):002:0> user.save
irb(main):003:0> user.errors.full_messages
=> ["メールアドレスの書式が正しくありません"]

OK!エラーメッセージが更新されました。

あれ?読み込まれないとなった場合は、reload!を実行するか、consoleから抜けてもう一度試してみてください。

最後にapplication.rbのセッティング

最後は、TimeZoneとZeitwerkの設定を行いましょう。

TimeZoneを日本時間にする

RailsのTimeZoneには2種類あります。双方ともデフォルトでは世界標準時間のUTCが使用されています。

  1. 表示時間
  2. データベースを読み書きする時間

今回は、日本だけの利用を前提とするので1、2共に日本時間を採用します。

application.rbを開いてください。

config/application.rb
module Myapp8
  class Application < Rails::Application
    ...
    # 追記
    # 表示TimeZone
    config.time_zone = 'Tokyo'
    
    # DB読み書きをlocal(Tokyo)にする
    config.active_record.default_timezone = :local

下記コマンドで確認できます。

myapp $ rails r "puts Time.zone.name"

Tokyo

myapp $ rails r "puts Rails.application.config.active_record.default_timezone"

local

Zeitwerkの設定

Zeitwerk(ツァイトベルク)はRails 6 から組み込まれたオートロードシステムです。

Zeitwerkを有効にしている場合、$LOAD_PATH(Rubyライブラリをロードするときの検索パスが入った配列)にオートロードパスを追加する必要がありません。

Rails 6 のデフォルトでは、$LOAD_PATHにオートロードパスを追加する設定になっていますが、Zeitwerk有効時は追加しない設定が推奨されています。

具体的には、config.add_autoload_paths_to_load_pathfalseにすることで$LOAD_PATHにパスを追加しない設定となります。

config/application.rb
module Myapp8
  class Application < Rails::Application
    ...
    # 追記
    # Zeitwerk $LOAD_PATHにPathを追加しない(Zeitwerk有効時false推奨)
    config.add_autoload_paths_to_load_path = false

参考

Rails アプリケーションを設定する - Rails ガイド

Zeitwerkのコマンド集

Zeitwerkを有効にする
config/application.rb
module Myapp8
  class Application < Rails::Application
    
    config.load_defaults 6.0 # 6.0を指定すると自動的に有効になる
Zeitwerkが有効になっているか確認する
Rails console
$ rails c
> Rails.autoloaders.zeitwerk_enabled?
=> true
Zeitwerkを無効にする
config/application.rb
module Myapp8
  class Application < Rails::Application

    config.autoloader = :classic # クラシックモードを指定する

まとめ

今回は以下の設定を行っていきました。

  • ユーザーモデルのpasswordカラムにバリデーションを設定
  • passwordハッシュ化のセッティング
  • i18nのデフォルト言語を日本語に変更
  • ja.ymlファイルを新たに追加
  • バリデーションエラーを日本語化
  • passwordに独自のエラーメッセージを設定
  • Rails TimeZoneの変更
  • $LOAD_PATHにオートロードパスを追加しないよう設定

いやぁ〜長かったですね。

本当にお疲れ様でした。

コミットしとく

ここまでの変更を一旦コミットしておきましょう。

myapp $ git add -A
myapp $ git commit -m "add_user_validator"

さて、次回は?

さて次回は、ユーザーモデルにデモデータを入れ込むために、seedファイルを作成していきます。

お楽しみに!

現在、カテゴリー「Rails apiとNuxt.jsでSPA開発」のデモアプリを構築中です。記事になるまでもう少々のお時間が必要です。ブログの更新が止まって申し訳ありません。デモアプリの進捗状況は こちらの記事 で随時お伝えしてまいります。
スポンサー広告
次の記事はこちらです
SPA開発
今日のTweet
スポンサー広告