Lingo.dev の CLI は、設定した config/localesローカライゼーションエンジン を通じて Rails の YAML ファイルを翻訳します。Rails には i18n API が標準で組み込まれており、アプリ内の翻訳可能なテキストはロケールごとの YAML ファイルに保存されます。Lingo.dev はランタイム依存を追加することなく、既存のパイプラインに自然に組み込めます。
このガイドでは、Rails アプリを最初から最後までローカライズする手順を紹介します。CLI の設定、ロケールごとの YAML ファイル構成、リクエスト時のロケール切り替え、そして CI での翻訳自動化までを扱います。
デモリポジトリ
一緒に試すなら、lingodotdev/ruby-on-rails-localization-example をクローンまたはフォークしてください。実際に動く Rails アプリで、config/locales YAML ファイル、コミット済みの .lingo/config.json、さらに翻訳もすでに入っているので、設定とその出力結果を並べて確認できます。
Rails のローカライズの仕組み#
Rails は config/locales/ 配下の YAML ファイルから翻訳を読み込みます。各ファイルはルートにロケールコードを持ち、I18n.t を使ってコードから参照する検索パスに対応した、ネスト構造のキーを含みます。
| レイヤー | 内容 | ファイル例 |
|---|---|---|
| UI 文言 | ボタン、ラベル、フラッシュメッセージ | config/locales/en.yml |
| メーラー文面 | ActionMailer の件名と本文 | config/locales/mailers.en.yml |
| モデルのエラー | バリデーションメッセージと属性名 | config/locales/activerecord.en.yml |
Rails の YAML ファイルは、先頭のキーが en:、es:、fr: のようなロケールコードになっています。Rails はファイル名ではなく、このルートキーをもとに翻訳を管理します。config/locales/ 配下のすべてのファイルを読み込み、それぞれの内容を宣言されたルートキーの下に格納します。つまり、es.yml のルートが en: のままでも無視されるわけではなく、en 名前空間にマージされます。その結果、スペイン語には翻訳がまったく入らず、英語の翻訳だけが気づかないうちに上書きされてしまいます。
つまり、この形式を翻訳するには値だけでなく、そのキー自体も書き換える必要があります。yaml-root-key 形式はまさにそのためのものです。ルートキー配下のツリーをたどり、文字列の値だけを翻訳し、ターゲットロケールをルートにしたファイルを書き出します。ネストしたキー、%{name} の補間トークン、CLDR の複数形カテゴリ(zero/one/two/few/many/other)は構造なので、そのまま保持されます。コメントや YAML アンカーも同様です。
前提条件#
ローカライゼーションエンジンを作成する
CLI を実行するたびに、コンテンツはローカライゼーションエンジンを通ります。これは、どの LLM モデル、用語集、ブランドボイス、ルールを適用するかを決める設定です。Lingo.dev dashboardで作成し、API keyを生成してください。
Ruby と Rails を確認する
このガイドは Rails 7.2 以上を対象としており、Ruby 3.1 以上が必要です。バージョンを確認してください。
ruby -v
rails -vNode.js を確認する
CLI の利用には Node.js 22 以上が必要です。
node -vRails i18n を設定する
このガイドでは、アプリがすでに config/locales/*.yml に翻訳を保存していることを前提としています。ビューやコントローラーに文字列がハードコードされている場合は、まずそれらを t() 呼び出しに切り出してください。たとえば、次のようなコードを置き換えます。
<h1>Welcome</h1>次のようにします。
<h1><%= t(".welcome") %></h1>その後、対応するキーを config/locales/en.yml に追加してください。移行手順の全体については、Rails の internationalization guide を参照してください。
翻訳ファイルを整理する#
Rails は *.yml 配下にある config/locales/ ファイルをすべて自動で読み込みます。ソースロケールは翻訳済みファイルと並べて配置し、ディレクトリ全体を信頼できる単一の情報源にしましょう。
config/locales/
en.yml # Source locale
es.yml # Generated by Lingo.dev
fr.yml
de.yml一般的な en.yml には、プレーンな文字列、ネストされた名前空間、%{name} の補間、複数形が混在しています。
en:
hello: "Hello"
home:
welcome: "Welcome, %{name}!"
cta: "Get started"
notifications:
unread:
zero: "No unread notifications"
one: "1 unread notification"
other: "%{count} unread notifications"
errors:
messages:
blank: "can't be blank"CLI を設定する#
CLI をインストールし、認証を済ませて、プロジェクトを自分のエンジンにリンクします。
npm install -g @lingo.dev/cli
lingo login
lingo init
lingo linklingo init と lingo link を実行すると、使用するロケールとエンジンの .lingo/config.json、orgId を含む engineId が作成されます。これを .lingo/lock.json と一緒にコミットしておけば、どのマシンでも、どの CI 実行でも同じ状態を共有できます。
files[] エントリでソースロケールファイルを指定し、形式を明示的に設定します。
{
"orgId": "org_...",
"engineId": "eng_...",
"sourceLocale": "en",
"targetLocales": ["es", "fr", "de"],
"files": [{ "pattern": "config/locales/en.yml", "format": "yaml-root-key" }]
}ここでは "format": "yaml-root-key" は必須です。.yml のパスだけでは、ルートキーがロケールなのか通常の設定なのか判別できないため、CLI は推測しません。format を省略すると、そのファイルは汎用の yaml として扱われ、値だけが翻訳され、ルートは en: のまま残ります。これが前述のサイレントフェイルです。
出力先はソースパスから導かれるため、config/locales/en.yml から config/locales/es.yml、config/locales/fr.yml、config/locales/de.yml が生成されます。
Rails は en.yml に加えて、devise.en.yml、mailers.en.yml、activerecord.en.yml のような用途別ファイルも読み込みます。これらも対象にするには、glob を使った 2 つ目のエントリを追加します。
{
"files": [
{ "pattern": "config/locales/en.yml", "format": "yaml-root-key" },
{ "pattern": "config/locales/*.en.yml", "format": "yaml-root-key" }
]
}この 2 つのパターンは重なりません。1 つ目は en.yml にだけ一致し、2 つ目は .en.yml で終わるファイルにだけ一致します。そのため、es.yml や devise.es.yml のような翻訳済みファイルがソースとして拾われることはありません。devise.en.yml から devise.es.yml が生成されます。
複数ロケール向けに Rails を設定する#
利用可能なロケールと、デフォルトで使うロケールを Rails に指定します。config/application.rb では次のように設定します。
module YourApp
class Application < Rails::Application
config.i18n.available_locales = [:en, :es, :fr, :de]
config.i18n.default_locale = :en
config.i18n.fallbacks = [:en]
end
endURL パラメータまたは ApplicationController ヘッダーをもとに、Accept-Language でリクエストごとのロケールを選択します。
class ApplicationController < ActionController::Base
around_action :switch_locale
private
def switch_locale(&action)
locale = params[:locale] || http_accept_locale || I18n.default_locale
I18n.with_locale(locale, &action)
end
def http_accept_locale
header = request.headers["Accept-Language"].to_s
header.scan(/[a-z]{2}/).find { |l| I18n.available_locales.map(&:to_s).include?(l) }
end
def default_url_options
{ locale: I18n.locale }
end
endビューで翻訳を表示する#
ERB テンプレートでは t と l ヘルパーを使います。キーの先頭にドットを付けると現在のビューパス基準で解決されるため、翻訳キーをそのテンプレートの近くに保てます。
<h1><%= t(".welcome", name: @user_name) %></h1>
<p><%= t("notifications.unread", count: @unread_count) %></p>
<%= link_to t(".cta"), signup_path, class: "btn-primary" %>レイアウトにロケール切り替えを追加します。
<nav>
<% I18n.available_locales.each do |locale| %>
<%= link_to locale.upcase, url_for(locale: locale) %>
<% end %>
</nav>ローカルで翻訳する#
lingo push --waitpush はソースファイルをアップロードし、ローカライゼーションエンジンで処理して、翻訳済みファイルを書き戻します。--wait を付けると、出力がそろうまでコマンドは待機したままになります。これは明示的に指定しておく価値があります。というのも、今後のリリースでデフォルトが変わり、push は実行を送信したらすぐ終了し、結果は lingo pull で取得する形になるためです。
2 回目以降は差分だけが翻訳されます。push はソースをハッシュ化して .lingo/lock.json と比較するため、変更のない項目にはコストがかかりません。プロジェクトでの初回実行時、またはロケール追加後は、コーパス全体の処理が必要です。
lingo push --backfill-missingglob を渡せば、実行対象をファイルの一部に絞り込めます。
lingo push "config/locales/**"別の場所で生成された出力、たとえば別マシンや CI の結果を取得するには、lingo pull を実行します。
初回の翻訳実行後は、新しい YAML ファイルを読み込ませるために Rails サーバーを再起動してください。
bin/rails serverスペイン語の表示を確認するには、/es にアクセスしてください。
複数形#
Rails は CLDR plural categories、つまり zero、one、two、few、many、other を使います。count: に I18n.t 引数を渡すと、Rails が対応するキーを選択します。
t("notifications.unread", count: 0) # => "No unread notifications"
t("notifications.unread", count: 1) # => "1 unread notification"
t("notifications.unread", count: 12) # => "12 unread notifications"CLI は各複数形バリエーションをその場で翻訳します。ターゲットロケールで英語の one/other より多くのカテゴリが必要な場合は、ソースの en.yml で定義しておいてください。
CI で自動化#
Lingo.dev GitHub App は、プッシュやプルリクエストのたびにサーバー側で翻訳を実行します。ランナーは不要で、API キーをリポジトリに保存する必要もありません。使用するエンジンはコミット済みの .lingo/config.json から解決されるため、コミットするファイルには orgId と engineId が含まれている必要があります。
CLI を自分のパイプラインで動かしたいなら、GitHub Actions、GitLab CI、Bitbucket Pipelines 向けの CI/CD ワークフロー を参照してください。LINGO_API_KEY をシークレットとして渡し、lingo push --wait をほかのビルドステップと同じように実行します。
デプロイ前に検証する#
未翻訳のコンテンツを本番環境に出さないために、lingo check をデプロイゲートとして使います。未翻訳の項目が 1 つでも残っていれば非ゼロのステータスで終了し、何も書き込みません。
lingo checkこれを、アセットのプリコンパイルやコンテナビルドの前に独立した CI ステップとして追加します。
- name: Verify translations
run: lingo check
- name: Precompile assets
run: bundle exec rails assets:precompile