はじめに
こんにちは!
DMM WEBCAMP mentor Advent Calendar 2022 7日目 を担当します @Keichan_15 です!
今年のAdvent Calendarでは、ありがたいことに 1日目 を担当させて頂きました。初執筆ながら多くの皆様にお読み頂けて感無量です、。ありがとうございます!
ちなみに実を言いますと、まだもう1記事執筆予定なのです…。早くポケモンやりたい…ニャオハ…
少々前置きが長くなりましたが、主に本記事では、 Rails で I18n 導入後に 「translation missing: ja.」系のエラー が出た時の対処法について紹介していきたいと思います!
ちなみに、今回の記事は1日目で紹介した I18n に非常に関連する内容となっておりますので、まだ読んでないよ!って方は是非とも事前にお読み頂けますと、より理解が深まるかと思います!
今回はどのようなパターンを想定した内容かと言いますと、
- Let's Rails開発!!
- ある程度実装進んできたからちょっとここで 日本語化 導入しよーっと!
- おい! 本来見たいエラーよりも先に対訳用意しろって言われてる!でも対訳いちいち全部入れるの面倒だよ! とりあえず今出てるエラーを見せてくれ…!
とお困りの方への記事となっております!
特に実装途中で日本語化を行った方、見たいエラーよりも先行して出てくるこのエラーに悩まされることが多いのではないでしょうか。
自身も「全部翻訳入れるの面倒や…。コピー&ペーストするのも面倒や…。」という 究極の面倒くさがり 人間なので、今回は 2STEP に分けてこの問題を解決していきます!
よろしくお願い致します!
※ 2STEPへ歩み出す前に(裏ルートがあります)
実はこのエラーですが、有効な解決策が既に存在しており、エラー文をコピってそれとなく記事を漁ってみると、
その他、関連記事が発見できると思います。以下に参考記事をご紹介します。
参考
出ているエラーの対訳をその都度 ja.yml
に転記したり、コピペの時間が十分にあったり、という方には本記事はあまり参考にならないかもしれません…。
何せ、「とりあえず目の前のエラーだけ見せてくれ!早よ!」って方に向けた内容です。時間は有限ですから…1秒も無駄に出来ないんです僕の人生。。
前提環境
- Ruby 3.1.2
- Rails 6.1.7
- yarn 1.22.19
本記事では、簡単なRailsアプリケーションを用いながら説明していきます。
前提として、
- ユーザー情報を保持する Users テーブル
- 投稿した記事の内容を保持する Lists テーブル
を作成しています。ER図は以下の通りです。
注意① Users テーブルについて
今回 User モデルの作成には devise を使用して実装しています。deviseについてザックリ説明すると、「Railsで作成したWebアプリケーションに簡単に認証機能(サインイン・サインアウト)を実装できるgem」になります。
具体的な使用方法については執筆の尺の関係上、割愛させて頂きます!(ゴメンネ )
注意② I18n について
ここでの I18n は configファイルへの追記やja.ymlを作成している状態を前提 とします。
導入方法については執筆の尺の関係上、今回は触れませんのでご了承ください!(ゴメンネ )
注意③ enum について
これも 導入されていることを前提 とします。
導入方法については…(以下略)(ゴメンネ )
現状
状況としては、記事の投稿をしたのに投稿内容が反映されない場面を想定して頂きたいです。
記事作成フォーム に 記事のタイトル・内容 を記入して 投稿
ボタンを押しても…
なぜでしょう。投稿内容が一覧に表示されていません!
原因を調査するため 我々はアマゾンの奥地へと… etc. save!
メソッドを用いて確認します。こちらも詳細は割愛しますが、ザックリ説明すると 保存が出来なかった場合にのみ例外を発生させます。 なんて便利な子なの…。
class ListsController < ApplicationController
def new
@list = List.new
end
def index
@lists = List.all
end
def create
@list = List.new(list_params)
@list.save! ← ココ!!
redirect_to lists_path
end
private
def list_params
params.require(:list).permit(:title, :body)
end
end
よし!これで原因を確認できるぞ!と思ったら…
translation missing: ja.activerecord.errors.messages.record_invalid
?? 見たいエラーはそれじゃないんだよなあ…。
ここで I18n 大好きマンの方は薄々お気付きかと思いますが、このエラーの意味を翻訳サイトに突っ込むと「翻訳無いで!ja.activerecord.errors.messages.record_invalidの翻訳が!早よ用意セエ!」って出てくると思います。優秀な翻訳サイトです、それ。
いやでもね、configには設定してるし、ここだけ翻訳一旦あればええんですよってのが今回のパターン。
module ご自身のアプリケーション名
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 6.1
# Configuration for the application, engines, and railties goes here.
#
# These settings can be overridden in specific environments using the files
# in config/environments, which are processed later.
#
# config.time_zone = "Central Time (US & Canada)"
# config.eager_load_paths << Rails.root.join("extras")
config.i18n.default_locale = :ja
end
ja:
enums:
order:
payment_method:
credit_card: "クレジットカード"
transfer: "銀行振込"
そうなんです。今回別に enum
を使用している所だけ日本語化出来たら良いんです。
日本語化の方色々直せヨ!と、先行して主張するオラオラ系エラー上司に負けちゃってるんです。本当に見たい、バリバリエリート系エラー部下くんの悲しき現実。これが縦社会です。
エリートが花開く未来を共に見届けるため、簡単 2STEP の旅に出て上司からの圧力を跳ね飛ばそう!という内容でございます!
STEP① コメントアウト、便利やなあ~
まず、STEP① で行う内容は config/application.rb から日本語化の設定コードをコメントアウトする になります!
module ご自身のアプリケーション名
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 6.1
# Configuration for the application, engines, and railties goes here.
#
# These settings can be overridden in specific environments using the files
# in config/environments, which are processed later.
#
# config.time_zone = "Central Time (US & Canada)"
# config.eager_load_paths << Rails.root.join("extras")
config.i18n.default_locale = :ja
end
Railsにて I18n を使用し日本語化を行っている場合、
config.i18n.default_locale = :ja
この1行を追加していると思います。多分してます。この1行を、
# config.i18n.default_locale = :ja
このように コメントアウト してあげましょう!
この1行は翻訳のデフォルト言語を日本語に設定するための記述ですが、このように変更することで 一時的に日本語化の設定を無効 にしています。
なんて簡単な STEP1 なのでしょうか!!
STEP② コメントアウト、正義やなあ~
STEP② もこれまたコメントアウトになります。 ja.ymlの中身をコメントアウト しましょう!
ja:
enums:
order:
payment_method:
credit_card: "クレジットカード"
transfer: "銀行振込"
こちらの config/locales/ja.yml
の ja:
以下の記述を、
ja:
# enums:
# order:
# payment_method:
# credit_card: "クレジットカード"
# transfer: "銀行振込"
このように コメントアウト してあげましょう!
ここでのポイントですが config/locales/ja.yml
の 中身全てをコメントアウトしてしまうとエラーが出てしまう という点です。
こんな感じに…。
ymlファイルの中身が全て空っぽの状態でもエラーが出てしまいます。一行だけ残すのがミソってやつです。
以上、2STEPが完了しましたら 必ず rails s
し直してください!画面リロードだけでは変更が反映されない場合が考えられます。ここは面倒くさがり屋さんでもやりましょうね…。
いよいよ待望のエラーとご対面…!?
果たして今回の2STEPを乗り越えると、待望のエラーが出てくれるのか!?
期待と共に再度 投稿
ボタンを押下すると…
やったー!見たいエラーが出てきてくれたよ~!
ってエラーの先に出てくるのがまたエラーかよ…という話ではあるんですけどね(笑)
ちなみにこのエラーですが、Listsテーブルの外部キー(FK)に設定しているuser_idに対して、ログイン中のユーザーのid(current_user.id)を入れてあげることで解決します。
今回の記事内容から若干反れるため、ご興味ある方は以下の「解決方法」を覗いてみてくださいね!
解決方法
★完成コード
class ListsController < ApplicationController
def new
@list = List.new
end
def index
@lists = List.all
end
def create
@list = List.new(list_params)
@list.user_id = current_user.id
@list.save
redirect_to lists_path
end
private
def list_params
params.require(:list).permit(:title, :body)
end
end
解説
〇現状起きている問題
まずは、現在発生しているエラーについて確認していきましょう!
画面が真っ赤真っ赤で逃げたしたくなりますが、エラーには問題解決への糸口がギュッと詰まっていますので見て損はありません!仲良くなればこっちのもんです。
着目すべき箇所は上から2行目の Validation failed: User must exist
の表示です。
ここから読み取れるのは Userモデルにデータが入っておらず、保存時にバリデーションで弾かれてしまっている という点です。
イメージとしては、記事のタイトル・本文は問題無くデータが渡っているのですが、 「誰が」その記事を投稿したのか という情報が不足してしまっています。
ここで冒頭に紹介したER図を確認してみましょう。
記事のデータを保持するテーブルは Lists テーブルでしたね。では Lists テーブルの中で 誰がその記事を投稿したのか の 「誰が」 に該当するデータが入るのはどのカラムになるでしょうか?
そうです、 user_id
ですね!
この user_id
には記事を投稿したユーザーのidが入ります。このカラムがあることによって、「誰が」の情報が補完され、記事のデータが作成されるようになります!
今や日常生活と切って離せないSNSですが、誰がそのツイートをしたのか、誰がその写真をアップしたのか など、いわゆる Who?
の情報は極めて重要ですよね!
では実際にコードに修正を加えていきましょう!
★問題の修正
まずは修正前のコードを確認します。
class ListsController < ApplicationController
def new
@list = List.new
end
def index
@lists = List.all
end
def create
@list = List.new(list_params)
@list.save!
redirect_to lists_path
end
private
def list_params
params.require(:list).permit(:title, :body)
end
end
まず最初に、 create
アクション内で変更した save!
メソッドを通常の save
メソッドに戻します。
def create
@list = List.new(list_params)
@list.save ← save! から save に変更!
redirect_to lists_path
end
次に、「誰が」の情報を追加するために @list = List.new(list_params)
の1行下から記述を追加していきます。
今回追加する対象となるカラムは user_id
でした。このカラムに、記事を投稿したユーザーのidを代入します。ここでの「記事を投稿したユーザー」は、今何をしているユーザーと同じ と捉えることができるでしょうか?
今回は前提としてdeviseを使用し、ユーザーはログイン済である状況を想定します。この条件を踏まえて別の捉え方をすると 今ログインしているユーザー と一緒である、とも取れますよね。この別視点での考え方が非常に重要です。
また、deviseでは「ログインしているユーザーの情報」を取得する手段として、 current_〇〇 と呼ばれるヘルパーメソッド(〇〇はdeviseを使用して作成したモデル名が入る)が使用できましたね。個人的にこのヘルパーメソッドについて分かりやすいなという記事がありましたので、「解決方法」のすぐ下に記載しております。是非こちらも確認してみてください!
代入すべきカラム、使用すべきヘルパーメソッドが確認できたところでコードを修正していきます。
def create
@list = List.new(list_params)
@list.user_id = current_user.id ← この1行を追加!
@list.save
redirect_to lists_path
end
@list.user_id = current_user.id
Lists テーブル内の user_id
カラムであることを示すため、代入すべきカラムの記述は @list.user_id
になります。
そして、そのカラムに current_user.id
と記述することで、今ログインしているユーザーのidを代入することができます。
このように記述することで Lists
テーブルの user_id
カラムに適切なデータが渡り、記事の投稿が出来ました!めでたし、めでたし。
◎まとめ
- まずはエラーをしっかり見よう!
- 別の視点から物事を捉えてみよう!
- コードいっぱい書こう!楽しい~~!
余談
Validation failed: (Model name) must exist
で検索すると、大体 optional: true
を付記して外部キーの nil
を許可しようといった記事がヒットします。例えば以下の記事などです。
この方法、正直DB設計上においてFKが nil
になるパターンってほとんど無いような気がしますし、根本的な解決になってないよねって個人的に思います。これ検索時に上の方でヒットするのでこれが正解なんだなって思っちゃうんですよね…。
特に学び始めの方は、モデルファイルにお互いアソシエーションを繋ぐ記述 (belongs_to, has_many)
がなぜ必要なの…?って所や、外部キーってそもそもどんなデータが入るの…?みたいな所をしっかり抑えておくべきなのかな~と思いました。
★解決方法の説明内で紹介した記事
おわりに
いかがでしたでしょうか。
今回は実装途中段階で日本語化を行うと、時々遭遇する可能性のある :ja is not a valid locale
のエラーについて見てきました。
冒頭でも若干紹介したように、今回の方法以外にもこのエラーの解決方法は存在します。ご自身の実装の進捗度合いと相談しつつ、適切な方法を取り入れて頂いて素敵なWebアプリケーション開発を!
また、この記事をお読み頂き少しでも同じエラーでお悩みの方の助けとなれば幸いです。
最後までお読み頂き、ありがとうございました~~!