i18nによる日本語化対応
i18nとrails-i18n
Railsでは様々な言語に対応するために、デフォルトでi18nというgemが導入されている。
以下Railsガイド Rails 国際化 (i18n) APIより引用
RubyのI18n (国際化・多言語化を意味する internationalization を短縮したもの) gemはRuby on Rails 2.2以降からRailsに同梱されています。このgemは、アプリケーションの文言を英語以外の 別の1つの言語に翻訳 する機能や 多言語サポート 機能を簡単かつ拡張可能な方式で導入するためのフレームワークを提供します。
つまり、導入のためのフレームワークであり、これだけでは日本語化対応ができない。
翻訳するためのファイルを用意したり、読み込むようにパスを設定したりする必要があるということ。
rails-i18nというgemがあり、これには様々な言語の翻訳ファイルが入っている。
rails-i18nをインストールし、アプリケーションのデフォルト言語を日本語に設定することで必要最低限(バリデーションエラー時のメッセージや、月や曜日など)の日本語を使用できるようになる。
ただし、必要最低限の日本語対応となるので、個別のアプリケーションのモデルやビューなどで使用している用語の日本語を設定したい場合は、別途YAML形式の翻訳ファイルを作成する必要がある。(拡張子は.yml 日本語のファイルなのでファイル名はja.ymlにする)
日本語化
まず、Railsのデフォルトの言語を日本語に設定する必要がある。
config/application.rb
module AppName class Application < Rails::Application #このクラス内に書く config.i18n.default_locale = :ja end end
次にGemfileに
gem 'rails-i18n'
を追加しbundle install
する。
このgemを導入することによって、Railsを日本語で使う場合のデフォルトのロケールファイル
rails-i18n/ja.yml at master · svenfuchs/rails-i18n · GitHub
をダウンロードしてローカル(config/locales以下にja.ymlとして)に配置しなくても、gem内部にある日本語の国際化設定ファイルを参照するようになる。
ViewとModelの日本語化
config/application.rb
module AppName class Application < Rails::Application config.i18n.default_locale = :ja # 読み込む対象のファイルを増やす config.i18n.load_path += Dir[Rails.root.join('config/locales/**/*.{rb,yml}').to_s] end end
と記載して、i18n対応ファイルをlocales以下に配置することで、多言語対応させたviewを表示させることができる。
必要最低限の日本語対応に加え、モデルに関連する内容のみを記載するファイル、viewの表示の内容のみを記載するファイルなどと、i18nの定義ファイルを複数に分けて設定できる。
※この設定の変更を反映させるためには、サーバーの再起動が必要。
開発の初期段階から、作成したモデル名やカラムの翻訳ファイルを作成しておくとよい。
ActiveRecordとViewで翻訳ファイルを分けて作成すると管理しやすい。
(例)
locales以下にviews,activerecordディレクトリを作り、そこに配置する。
config/locales/views/ja.yml
config/locales/activerecord/ja.yml
翻訳ファイルはアプリケーションが大きくなるほど単一のファイルでの管理が難しくなる。その場合はviewの翻訳ファイルさらに細かく分け、コントローラごとに分けるなどする。(可読性はもちろん、後述するlazy-lookup記法を使う為の配慮)
翻訳ファイルの書き方例
config/locales/views/ja.yml
ja: defaults: login: 'ログイン' logout: 'ログアウト' users: new: title: 'ユーザー登録' user_sessions: new: title: 'ログイン'
config/locales/activerecord/ja.yml
ja: activerecord: models: user: 'ユーザー' post: '投稿' attributes: user: email: 'メールアドレス' password: 'パスワード'
tメソッド
viewで翻訳ファイルに記載した用語を使用する場合は、tメソッドを使う。(tはtranslateのt)
上の翻訳ファイルの書き方例の場合
<%= t('defaults.login') %>
でログイン
と表示できる。
<%= t('users.new.title') %>
でユーザー登録
と表示できる。
lazy-lookup記法
<%= t('users.new.title') %>
と書く場合、app/views/users/new.htmlのようにビューテンプレートの階層と翻訳ファイルの階層構造が同じ場合は
<%= t('.title') %>
のように省略して書くことができる。この書き方がlazy-lookup記法。
参考サイト
Rails 国際化 (i18n) API - Railsガイド
[初学者]Railsのi18nによる日本語化対応 - Qiita
sorceryを使用して、ユーザー登録・ログイン機能を作成
sorceryとは
Railsに認証機能を実装するためのライブラリ。
インストール
Gemfileに
gem 'sorcery'
と書きターミナルでbundle install
を実行。
初期設定
$ bundle exec rails g sorcery:install
これで必要最低限なファイルが作成される。
- config/initialize/sorcery.rb
- db/migrate/20xxxxxxxx_sorcery_core.rb
- app/models/users.rb
など
マイグレーションファイル(db/migrate/20xxxxxxxx_sorcery_core.rb)には、必要最低限のカラム(eメール、パスワード等)がすでに定義されている。ここに適宜必要なカラム(例えば氏名など)を追加しデータベースに反映させる。
bundle exec rails db:migrate
または
rake db:migrate
を実行する。
(ちなみに、bundle execはBundlerが管理するGemを利用できる状態でコマンドを実行するということ)
コントローラを設定
$ bundle exec rails g sorcery:install
を実行した事で、app/models/users.rbは作成されているが、usersコントローラは自分で作成する。
$ rails g controller users
を実行。
app/controllers/users_controller.rb
class UsersController < ApplicationController # ... private def user_params params.require(:user).permit(:email, :password, :password_confirmation) end end
また
$ rails g controller UserSessions new create destroy
でuser_sessions.controller.rbを作る。
app/controllers/user_sessions.controller.rb
class UserSessionsController < ApplicationController def create @user = login(params[:email], params[:password]) if @user redirect_back_or_to(:users, notice: 'Login successful') else flash.now[:alert] = 'Login failed' render :new end end def destroy logout redirect_to(:users, notice: 'Logged out!') end end
リダイレクト先やフラッシュなどは仕様に合わせて変更すれば良い。
usersコントローラでユーザー情報、user_sessionsコントローラでログイン/ログアウトに関する情報を扱う。
ルーティングを設定
config/routes.rb
root 'users#index' resources :users get 'login', to: 'user_sessions#new' post 'login', to: 'user_sessions#create' delete 'logout', to: 'user_sessions#destroy'
例えば、users_contoroller.rbでnewアクションとcreateアクションしか設定しないのであれば、
resources :users, only %i[new create]
という書き方もできる。
※リソースベースのルーティングを設定すると、URL用のヘルパーを使用することができるようになる。(login_pathのようなやつ)
モデル
app/models/users.rb
class User < ActiveRecord::Base authenticates_with_sorcery! validates :password, length: { minimum: 3 }, if: -> { new_record? || changes[:crypted_password] } validates :password, confirmation: true, if: -> { new_record? || changes[:crypted_password] } validates :password_confirmation, presence: true, if: -> { new_record? || changes[:crypted_password] } validates :email, uniqueness: true end
ログインフォーム
入力用のフォーム(パスワードがデータベースで暗号化される前にパスワードを保持しておくためのもの)
app/views/users/_form.html.erb
<div class="field"> <%= form.label :password %><br /> <%= form.password_field :password %> </div> <div class="field"> <%= form.label :password_confirmation %><br /> <%= form.password_field :password_confirmation %> </div>
ログイン用のフォーム
app/views/user_sessions/new.html.erb
<h1>Login</h1> <%= render 'form' %> <%= link_to 'Back', users_path %>
app/views/user_sessions/_form.html.erb
<%= form_with url: login_path, method: :post do |f| %> <div class="field"> <%= f.label :email %><br /> <%= f.text_field :email %> </div> <div class="field"> <%= f.label :password %><br /> <%= f.password_field :password %> </div> <div class="actions"> <%= f.submit "Login" %> </div> <% end %>
あとは、app/views/layouts/application.html.erbにナビゲーションリンクとフラッシュメッセージを表示するためのビューを用意する(もちろんフラッシュ用のパーシャルを作っても良い)。そこにログアウト用のリンクを作る。
注意:logoutのリンクでmethod: :deleteを指定し忘れないようにする。
そして、ユーザー登録用のビューをapp/views/users/new/html.erbとして作成する。
ログインしていないユーザーの権限を制限
sorceryには便利なメソッドが用意されている。
require_login(sorceryが用意しているので自分で定義しなくて良い)
ログインしていない状態だとログインページへリダイレクトさせる
# app/controllers/application_controller.rb # 全てのアクションが呼び出される前にrequire_loginメソッドを呼び出す before_action :require_login # app/controllers/users_controller.rb # users_controllerのindex,new,createアクションが呼び出されるときは、require_loginを呼び出さない skip_before_action :require_login, only: [:index, :new, :create] # app/controllers/user_sessions_controller.rb skip_before_action :require_login, only: [:new, :create]
参考サイト
Simple Password Authentication · Sorcery/sorcery Wiki · GitHub
Gitコマンドのまとめ
Gitとは
簡単にいうと、ソースコードのバージョン管理をするためのツール。
Gitコマンド
$ git init
これでGitリポジトリが作成され、カレントディレクトリに.gitディレクトリが作られる。
- Gitの状態を確認する
$ git status
- git add
ファイルを追加する場合。ファイルを複数追加する場合は、スペースで区切る。
$ git add ファイル名
特定のディレクトリ以下のファイルを全て追加
$ git add ディレクトリのパス/*
全ての変更をまとめて追加
$ git add -A
もしくは
$ git add .
- git commit
インデックスに追加された変更内容をコミットする
$ git commit -m 'コミットメッセージ'
チーム開発の場合、変更内容がわかりやすいコミットメッセージを書く必要がある。
- 過去のコミット一覧を表示
$ git log
コミットしたユーザー名・メールアドレス、コミット日時、コミットIDが表示される。
終了する場合はターミナルにqと打つ。
- 過去のコミット内容・差分を表示
$ git show
複数名で手分けして開発を進める際ブランチを作り、コミットしたいものはマージすることで反映される。
- ブランチを作成
$ git branch ブランチ名
ブランチを切り替えるには
$ git branch ブランチ名
ブランチの作成とそのブランチへの切り替えを同時に実行できるコマンドが以下
$ git checkout-b ブランチ名
- マージ
現在いるブランチに別のブランチをマージ
$ git merge ブランチ名
- ブランチの削除
マージされていないブランチは削除されないコマンド
$ git branch -d ブランチ名
マージされていないブランチも削除できるコマンド
$ git branch -D ブランチ名
- git resetコマンド
主に3つのオプションがあり、それぞれ取り消しの範囲が違う
コミットのみ取り消す場合
$ git reset --soft コミットID
コミットとインデックスの変更を取り消す。作業ツリーにはコードに変更が残された状態になる。
$ git reset --mixed コミットID
変更を全て取り消す。一度取り消すと元に戻せない(リモートリポジトリにコミットがある場合を除く)
$ git reset --hard コミットID
※作業中の変更の取り消し
git reset --hard コミットIDを使えば良い。
ただし、Untrackedな状態のファイルはGitの操作の対象とならないため消せない。git reset時に変更を戻したい場合は、一度git add ファイル名 -N
でGit管理下におく必要がある。
参考サイト
rails consoleとテーブル Rails入門②
8月2日はほぼ復習で終わり、8月3日はRUNTEQのイベント「Vimハラ」に参加した。
8月4日、今日はRails入門②「rails consoleを制する者はRailsを制する」と入門フェーズ総復習をした。rails consoleを使って複数のモデルを作り、紐付ける事を学んだ。 学んだことをまとめてみる。
課題ではusersテーブルとtasksテーブル、categoriesテーブル、tasksテーブルとcategoriesテーブルを繋ぐ中間テーブルのtask_categoriesテーブルを作った。そこから、rails consoleを起動させて、user情報のレコード、そのuserのtask、categoryを作り、taskとcategoryを紐付ける事をした。
モデル作成時に外部キーを持たせるには
rails g model モデル名B(単数形) キー1:データ型 キー2:データ型 ... 紐付けたいモデル名(単数形)A:references
のようにターミナルに打ち込む。
こうして作ったモデルBのマイグレーションファイルに、外部キー制約を自動で作ってくれて、Rails6系だとNOT NULL制約を自動でつけてくれる。
Bに紐付いたAを作る
まずアソシエーションを定義する。
app/models/モデル名A.rbに、has_many :Bs(複数形), dependent: :destroy
を追加。
rails consoleを使い、Aのテーブルからidが例えば1のAを探し、aに代入(インスタンスを作る)
a.Bs.create(〇〇: □□)
で作れる。B.create(〇〇: □□, A_id: 1)としても作れるが、上の方法の方がわかりやすい。
中間テーブル
モデル同士を一対一で紐付けたい場合、例えばAとBだとBにA_idを持たせれば良いが、モデルに多対多の関係がある場合は、中間テーブルを作れば良い。(課題ではtaskとcategoryに中間テーブルを持たせた。userとtaskは一対多。)
has_many :〇〇, through: :中間テーブル名
これを中間テーブルで繋がったそれぞれのテーブルのapp/models/テーブル名.rbに入れる。〇〇には他方のモデルの複数形を入れる。
こうすると、rails consoleで情報を取得した時、関係するテーブルの情報も取得できる。これをしないと外部キーのidしか取得できず、同時に扱いたい場合に手数が多くなる。(例えば、タスクとそのカテゴリを同時に表示させたい場合など。task.categoriesで一発でカテゴリ名を表示できる。カテゴリに関しても同様。category.tasksでそのカテゴリのタスクを表示できる。)
バグ修正 Rails入門②
8月1日は、Rails入門②の課題「バグと友達になろう」とHTTP演習をした。
復習のため学習内容をまとめてみる。
まずエラー修正課題だが、解決できていなかったエラーを書き出してみると、
1 NoNameError undefined local variable or method 'about_url'
2 NoMethodError undefined method 'log_in'
3 Failure パスワードが空欄でログインできないという旨の内容
解決の為に試みた事
3はソースコードを確認するとparams.require(:user).permit(:name, :email, :password_confirmation)
となっており、ストロングパラメーターに:password
が抜けていることが原因だとわかった。慣れていない為、関係していそうなコントローラーやビューのコードとしばらくにらめっこしてようやく発見。
1はまずroutes.rbを見てみるとget 'static_pages/about'
はあるが、アクションを呼び出すto: 'static_pages#about'
がない。
これを記載しなくてもページは表示できるんだっけ?いやできなかったはず……つまり、ここは間違いだな。しかし、get 'static_pages/about'
なわけだから、このままだと/static_pages/aboutというURLになるはず。about_urlというヘルパーメソッドで指定できない。routes.rbを書き換えるべきか、テストコードをabout_static_pages_urlにするべきなのか……。
また、about.rbはあるがこれではページの表示はできないのでは?(後から振り返ればここを修正しようとしてファイルの名前をabout.erbに変更していたが、これは間違いで正しくはabout.html.erbだった。確認大事。)
さらにstatic_pages.controller.rbにaboutアクションがないぞ? →追加する
よし、これで動くはず →動かず(前述の事が原因)
2は、log_inなんてメソッドどこにも定義されてないぞ?よしlog_inメソッドをわざわざ定義しなくてもsession[:id]をuser.idに入れればいいのかな? →テスト通らず(実は見つけられなかっただけでhelpersフォルダ内のsession_helper.rbに定義されている。また、session[:user_id]とするべきだった。)
以上こんな方法でバグ修正していては全く効率的ではなく無駄に時間がかかる上、見落としも起きてしまうだろう。
解答の動画があるのだが、エラーを特定していく過程がとてもスマートで効率的で感動した!目からウロコが落ちた。ので、その方法を自分なりに言語化して復習しようというのが今回の本題!
エラー修正の心得
- テスト実行時のエラーメッセージとソースコードだけ見て進めない
- ブラウザの検証ツールを使う
- binding.irb
- grep -r 検索したいワード ディレクトリ とターミナルでコマンドを打つと、指定したディレクトリ以下にあるファイルで検索したいワードを全体検索
例えば、3のログインできないケース。
まず実際にブラウザでログインを試す。→より詳しいエラーメッセージが表示される。 (実際にブラウザで試すメリット!)
それによるとパスワードが空欄、パスワードが規定文字数以下 とのこと。
パスワードが正しく送信できているかを確認する必要がある。ここでブラウザの検証ツール(ディベロッパツール)の出番だ。
ネットワークタブでフォームから送信された内容を確認できる。(その他にも様々なことを確認できるので使わない手はない)
viewのフォームからの送信は問題ないとわかる。つまり送信された情報がsaveできていないということ。
saveを実行する手前で一旦処理を止め検証する。ソースコードをエディタなどで開き、saveをする行の直前にbinding.irb
と加える。それからまたブラウザでログインしてみると、saveする直前で処理が止まり、その時点での@userを出力させたりできる。
こうして実際にアプリケーションの挙動を探っていく事で、どうすればいいかわからない場合でもエラーの原因を探ることができる。
メモ
2021-7-31
現在RUNTEQに通い始めて19日目。
カリキュラムのRails入門②の「バグと友達になろう」に取り組んでいる最中。バグの修正課題。
昨日は課題となるコードをGithubでリポジトリをクローンし、環境構築でrbenvでRubyのバージョン、nodenvでNode.jsのバージョンをそれぞれ指定し、Yarnをインストールをしたりした。
参考にしたサイト
肝心のバグ修正だが、rails test:systemを実行し見つけたErrorとFailureが計4つ。
1つ目は比較的簡単に見つかった。
NoMethodError: undefined method `authenticate' for nil:NilClass
とエラーが出た箇所の変数にぼっち演算子がついておらず、nilの場合エラーになるということ。
その他のエラーがなかなか手強く色々試すもバグ修正できなかった。
エラーが出ている箇所はわかるものの、どう変更を加えればいいかわからない状態。
pryなどのデバッグツールを使ってみようと思うが……理解が浅く使いこなせるだろうか?
明日は講師に質問してみようと思う。
ブログを書き始めた理由
現在RUNTEQというプログラミングスクールに通っており、日々の学習の記録と復習として学んだ内容をまとめる事で理解を深めるために書き始めました。