i18n について
Railsアプリに国際化とローカライズを導入する開発者向けのI18nスキルです。ロケールファイルの整理、リクエストごとの言語選択、日付・数値・モデル・メールのローカライズを扱います。
主な機能
- 01ロケールファイルで翻訳を管理
- 02利用可能なロケールとフォールバックの設定
- 03遅延検索と補間の利用
- 04日付、数値、複数形のローカライズ
- 05モデル、検証エラー、メールの翻訳
- 06リクエストごとの安全な言語切り替え
活用シーン
- 01既存のRailsアプリに多言語対応を追加
- 02ロケールファイルの整理と分割
- 03ユーザー設定やURLによる言語選択
- 04フォームエラー、日付、メールのローカライズ
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ thinkoodle/ rails-skills --skill i18nインストール後、Claude Code で/i18nを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/locale-files.mdlocale-switching.mdlookups.mdmodel-translations.mdpluralization.mdtesting.md詳細
Rails I18n エキスパート
I18n フレームワークを使って、Rails アプリケーションを国際化およびローカライズします。ユーザーに表示される文字列はすべてロケールファイルに記述し、ハードコードしないでください。
基本方針
基本原則:
- すべての文字列をロケールファイルに記述する — ビュー、コントローラー、メーラー、モデルにユーザー向けテキストをハードコードしないでください
- あらゆる場所で遅延検索を使う — ビューやコントローラーでは、
books.index.titleではなく.titleを使います - 言語ではなく機能ごとに整理する — ロケールファイルは、1つの巨大なファイルにまとめず、ドメイン(モデル、ビュー、デフォルトなど)ごとに分割します
- YAML を基本にする — 日付形式に Ruby のラムダが必要な場合を除き、
.ymlファイルを使います - 開発/テスト環境ではエラーを明示する —
raise_on_missing_translations = trueを設定し、キーの不足を早期に検出します
このスキルを使う場面
- 既存の Rails アプリに I18n サポートを追加するとき
- YAML ロケールファイルを作成または編集するとき
t()/I18n.t()やl()/I18n.l()ヘルパーを使うとき- ロケール切り替え(URL、サブドメイン、ヘッダー、ユーザー設定)を設定するとき
- Active Record のモデル名、属性、エラーメッセージを翻訳するとき
- 日付、時刻、数値、通貨をローカライズするとき
- 英語以外のロケールの複数形ルールを設定するとき
- 大規模なアプリケーションでロケールファイルを整理するとき
- フォールバックや利用可能なロケールを設定するとき
手順
ステップ 1:既存の I18n 設定を確認する
まず、プロジェクトの現在の I18n 設定を確認してください — 慣習の不一致はキー検索の失敗につながります。
# Check existing locale files
find config/locales -name "*.yml" -o -name "*.rb" | sort
# Check I18n config
grep -r "i18n" config/application.rb config/initializers/ config/environments/
# Check available locales
grep -r "available_locales" config/
# Check for existing translation usage
rg "I18n\.t\b|\ t[\(\ ][\'\"\.]" --type ruby --type erb -l
# Check for hardcoded strings in views (potential I18n candidates)
rg -l ">[A-Z][a-z]+" app/views/ --type erb
既存の慣習に合わせてください。プロジェクトでフラットなキーを使っている場合は、ネストしたキーを導入しないでください。機能ごとに整理している場合は、それに従ってください。
ステップ 2:I18n を適切に設定する
config/application.rb の最小限の設定:
# config/application.rb
config.i18n.available_locales = [:en, :es, :fr]
config.i18n.default_locale = :en
config.i18n.fallbacks = true # Falls back to default_locale
config.i18n.load_path += Dir[Rails.root.join("config", "locales", "**", "*.{rb,yml}")]
テスト/開発環境では、翻訳の不足を検出します:
# config/environments/test.rb
config.i18n.raise_on_missing_translations = true
# config/environments/development.rb
config.i18n.raise_on_missing_translations = true
available_locales を設定してください — 設定しないと、任意のロケール文字列が受け入れられ、ファイルシステムのトラバーサル攻撃や予期しないフォールバック動作につながる可能性があります。
ステップ 3:翻訳ヘルパーを正しく使う
基本的な検索
# In views (translate helper auto-available)
t("hello") # Simple key
t("messages.welcome") # Nested key
t(:welcome, scope: :messages) # Same thing, symbol + scope
# In controllers/models/services
I18n.t("messages.welcome")
# With default fallback
t("missing.key", default: "Fallback text")
t("missing.key", default: [:other_key, "Final fallback"])
遅延検索(こちらを優先)
遅延検索は、ビューのパスまたはコントローラーのアクションに基づいて自動的にスコープを設定します。
# config/locales/en.yml
en:
books:
index:
title: "All Books"
empty: "No books found"
show:
title: "Book Details"
create:
success: "Book created!"
failure: "Could not create book."
<%# app/views/books/index.html.erb %>
<h1><%= t(".title") %></h1> <%# Resolves to books.index.title %>
<p><%= t(".empty") %></p> <%# Resolves to books.index.empty %>
# app/controllers/books_controller.rb
class BooksController < ApplicationController
def create
if @book.save
redirect_to @book, notice: t(".success") # books.create.success
else
flash.now[:alert] = t(".failure") # books.create.failure
render :new, status: :unprocessable_entity
end
end
end
ビューやコントローラーでは、遅延検索(.key)を優先してください — 翻訳キーを DRY に保ち、ファイル構造に対応させられます。共有キーやグローバルキーを参照する場合に限り、完全なパスを使います。
文字列の補間
en:
greeting: "Hello, %{name}!"
item_count: "You have %{count} items in %{location}"
t("greeting", name: current_user.name)
t("item_count", count: 5, location: "your cart")
scope や default を補間変数名に使わないでください。これらは I18n で予約されており、I18n::ReservedInterpolationKey が発生します。
複数形
en:
notifications:
zero: "No notifications" # optional for English
one: "1 notification"
other: "%{count} notifications"
t("notifications", count: 0) # => "No notifications"
t("notifications", count: 1) # => "1 notification"
t("notifications", count: 42) # => "42 notifications"
:count 変数は特別な意味を持ちます — 複数形を選択し、さらに文字列への補間も行います。
英語で必要なのは one と other だけです。ほかの言語では異なる形式が必要です。
- アラビア語:
zero、one、two、few、many、other - ロシア語:
one、few、many、other - 日本語:
otherのみ
ロケール固有の複数形ルールには、rails-i18n gem を使います。
HTML セーフな翻訳
_html で終わるキーや html という名前のキーは、ビューで自動的に HTML セーフとして扱われます。
en:
welcome_html: "<strong>Welcome</strong> to %{app_name}"
help:
html: "Need <em>help</em>? <a href='%{url}'>Contact us</a>"
<%= t("welcome_html", app_name: "MyApp") %> <%# HTML not escaped %>
補間値は引き続きエスケープされるため、XSS に対して安全です。翻訳自体にマークアップが含まれる場合に限り、_html キーを使ってください。
ステップ 4:Active Record モデルを翻訳する
モデル名と属性
en:
activerecord:
models:
user:
one: "User"
other: "Users"
admin/post: "Admin Post" # Namespaced model
attributes:
user:
email: "Email address"
first_name: "First name"
user/role: # Nested attribute
admin: "Administrator"
User.model_name.human # => "User"
User.model_name.human(count: 2) # => "Users"
User.human_attribute_name(:email) # => "Email address"
バリデーションエラーメッセージ
エラーメッセージは次の順序で検索されます(最初に一致したものが使われます)。
activerecord.errors.models.MODEL.attributes.ATTRIBUTE.ERROR
activerecord.errors.models.MODEL.ERROR
activerecord.errors.messages.ERROR
errors.attributes.ATTRIBUTE.ERROR
errors.messages.ERROR
en:
activerecord:
errors:
models:
user:
attributes:
email:
blank: "is required — we need this to contact you"
taken: "is already registered"
name:
too_short: "must be at least %{count} characters"
# Applies to all attributes on User:
invalid: "has a problem"
# Applies to all models:
messages:
blank: "can't be empty"
# Global fallback for all models:
errors:
format: "%{attribute}: %{message}" # Customize full_message format
messages:
blank: "is required"
エラーメッセージで利用できる補間変数:model、attribute、value、count。
ステップ 5:日付、時刻、数値をローカライズする
日付/時刻の書式設定
en:
date:
formats:
default: "%Y-%m-%d"
short: "%b %d"
long: "%B %d, %Y"
time:
formats:
default: "%a, %d %b %Y %H:%M:%S %z"
short: "%d %b %H:%M"
long: "%B %d, %Y %H:%M"
l(Date.today) # Default format
l(Date.today, format: :short) # Short format
l(Time.current, format: :long) # Long format
日付/時刻には l() を使います — strftime は現在のロケールを無視するため、英語以外のユーザー向けに日付が正しく書式設定されません。
数値の書式設定
数値ヘルパー(number_to_currency、number_with_delimiter など)はロケールファイルを参照します。
en:
number:
format:
separator: "."
delimiter: ","
precision: 3
currency:
format:
unit: "$"
format: "%u%n" # $1,000.00
separator: "."
delimiter: ","
precision: 2
es:
number:
currency:
format:
unit: "€"
format: "%n %u" # 1.000,00 €
separator: ","
delimiter: "."
ステップ 6:リクエストごとにロケールを設定する
I18n.with_locale を使った around_action を使ってください — I18n.locale = を直接設定すると、スレッドサーバー(Puma)ではリクエスト間で値が漏れ、ほかのユーザーのロケールが表示される原因になります。
URL パスからの取得(推奨)
# config/routes.rb
scope "(:locale)", locale: /en|es|fr/ do
resources :books
# ... all routes
end
# app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
around_action :switch_locale
def switch_locale(&action)
locale = params[:locale] || I18n.default_locale
I18n.with_locale(locale, &action)
end
def default_url_options
{ locale: I18n.locale }
end
end
優先順位を組み合わせたチェーン
def resolve_locale
params[:locale].presence ||
current_user&.locale.presence ||
request.env["HTTP_ACCEPT_LANGUAGE"]&.scan(/^[a-z]{2}/)&.first&.then { |l|
l.to_sym if I18n.available_locales.include?(l.to_sym)
} ||
I18n.default_locale
end
ステップ 7:ロケールファイルを整理する
小規模なアプリ: ロケールごとに 1 つのファイル(config/locales/en.yml、es.yml)。
中規模/大規模なアプリ — 用途ごとに分割します:
config/locales/
defaults/en.yml # Date, time, number formats
models/en.yml # AR model names, attributes, errors
views/en.yml # View translations (lazy lookup keys)
mailers/en.yml # Mailer subjects and content
ネストされたディレクトリを読み込む: config.i18n.load_path += Dir[Rails.root.join("config", "locales", "**", "*.{rb,yml}")]
YAML のルール: 最上位のキーはロケールです。キーにはスネークケースを使用します。キーとして使う 'true'/'false'/'yes'/'no'/'on'/'off' は引用符で囲みます(そうしないと YAML はブール値として解析します)。ネストは 4 階層以内にします。
ステップ 8:Action Mailer の翻訳
メーラーの件名は mailer_scope.action_name.subject から自動的に解決されます:
en:
user_mailer:
welcome:
subject: "Welcome to %{app_name}!"
class UserMailer < ApplicationMailer
def welcome(user)
mail(to: user.email) # Subject auto-resolved
# Or with interpolation: mail(to: user.email, subject: default_i18n_subject(app_name: "MyApp"))
end
end
ステップ 9:フォールバック
# config/application.rb
config.i18n.fallbacks = true # Falls back to default_locale
config.i18n.fallbacks = { es: :en, fr: :en } # Or specific chains
開発/テスト環境では、フォールバックを OFF にし、 raise_on_missing_translations = true にします。
エージェントがよくする間違い
- 文字列をハードコーディングする —
t(".success")ではなく"Record saved"と記述する - YAML のネストを誤る — 最上位のロケールキーを忘れる、またはインデントを誤る
- 遅延ルックアップを使わない — ビュー/コントローラーで
t(".title")ではなくt("users.show.title")を使う available_localesを設定し忘れる — 設定しないと任意のロケール文字列が受け入れられます(セキュリティリスク)I18n.locale =を使う — スレッドサーバーではリクエストをまたいで設定が漏れるため、I18n.with_localeを使うcountを指定せずに複数形処理をする — 文字列ではなく生のハッシュが返される_htmlサフィックスを付け忘れる — 翻訳内の HTML がエスケープされます- YAML のブール値を引用符で囲まない —
true、false、yes、noはブール値として解析されるため、キーとして使う場合は引用符で囲む - サーバーを再起動し忘れる — 新しいロケールファイルを読み込むには再起動が必要です
クイックリファレンス
翻訳のルックアップ方法
| コンテキスト | 方法 | 遅延ルックアップ |
|---|---|---|
| ビュー | t(".key") または t("full.key") | ✅ はい |
| コントローラー | t(".key") または I18n.t("full.key") | ✅ はい |
| モデル | I18n.t("full.key") | ❌ いいえ |
| メーラー | I18n.t("full.key") | ❌ いいえ |
| サービス/ジョブ | I18n.t("full.key") | ❌ いいえ |
ロケールファイルの基本構成
en:
# View translations (lazy lookups)
controller_name:
action_name:
key: "value"
# Shared/global
shared:
save: "Save"
cancel: "Cancel"
# Active Record
activerecord:
models:
model_name: { one: "Singular", other: "Plural" }
attributes:
model_name:
attribute: "Label"
errors:
models:
model_name:
attributes:
attribute:
error_type: "message"
# Formats (or use rails-i18n gem)
date:
formats: { default: "%Y-%m-%d", short: "%b %d", long: "%B %d, %Y" }
time:
formats: { default: "%Y-%m-%d %H:%M" }
number:
currency:
format: { unit: "$" }
# Mailers
mailer_name:
action_name:
subject: "Subject line"
新しいロケールのチェックリスト
-
available_localesに追加する - 既存の構成に合わせてロケールファイルを作成する
- 日付/時刻/数値のデフォルト値と複数形ルールのために
rails-i18ngem を追加する -
raise_on_missing_translations = trueを設定してテストする - ロケール切り替え UI を追加する。URL ベースの場合は
default_url_optionsも更新する
詳しいパターン、例、注意点については、references/ ディレクトリを参照してください:
references/lookups.md— 翻訳のルックアップ方法、補間、遅延ルックアップreferences/locale-files.md— YAML のパターン、ファイルの整理、日付/時刻/数値のローカライズ、カスタムバックエンドreferences/pluralization.md— 言語ごとの複数形ルールreferences/model-translations.md— Active Record のモデル/属性/エラーの翻訳references/locale-switching.md— ロケール切り替えの方法とフォールバック設定references/testing.md— I18n のテスト、よく使われる gem(rails-i18n、i18n-tasks、mobility)