このプロジェクトの i18n キー(翻訳)の参照・追加・更新・削除を行うスキル。i18n / 翻訳 / ロケール / `t` / `tt` / 翻訳キーに関わる操作のとき、および `config/locales` ディレクトリや `.yml` 翻訳ファイルを読む・参照する・編集するときは必ず使用すること。翻訳は原則 copy_tuner サーバで管理されており(一部キーのみ config/locales 管理)、素朴に config/locales を読むだけでは大半の翻訳が見つからず誤った判断をするため、必ずこのスキルの手順に従う。
copy-tuner-to-locales-migrate-prefix
copy_tuner(CopyTuner / copy_tuner_client)で集中管理している i18n データを、prefix(正規表現)単位で Rails 標準の config/locales(YAML)管理へ移すスキル。
gem の local_first_key_regexp を使うので、 gem を残したまま特定 prefix だけをローカル管理にできる(部分ローカル化)。1 回の実行で 1 prefix。全 prefix を移して gem ごと撤去したい場合は繰り返し、完了後 copy-tuner-to-locales-cleanup スキルへ進む。
対象 prefix はスキル引数で指定でき、未指定なら export を俯瞰して選定する。
含まれるファイル(6)
- SKILL.md21.7 KB
- references/example-touchpoints.md9.2 KB
- references/export-and-split.md9.4 KB
- references/local-first-regexp.md3.4 KB
- references/verification-per-prefix.md2.9 KB
- scripts/migrate_prefix.rb20.6 KB
SKILL.md(原文)
インストールする前に、エージェントに与えられる指示の中身を確認できます。
copy_tuner → config/locales prefix 単位ローカル化スキル
copy_tuner(copy_tuner_client gem)で集中管理している i18n データを、prefix(正規表現)単位で
Rails 標準の config/locales 配下の YAML 管理へ移すためのワークフロー。1 回の実行で 1 prefix だけ扱う。
使い方は 2 つある:
- 部分ローカル化 — 特定 prefix だけを恒久的に
config/locales管理にする。gem は残したままで、 CopyTuner 管理と locales 管理の二層が定常状態になる。 - 全移行 — 上記を全 prefix ぶん繰り返す。全 prefix の移行が完了したら
copy-tuner-to-locales-cleanupスキルで gem・CI・deploy・docs・MCP をまとめて撤去する。
どちらの用途でもこのスキルがやること(手順 0〜10)は同一で、差は「何回回すか」と「最後に cleanup へ 進むか」だけ。
このスキルは特定のリポジトリに依存しない。project_id・ファイルパス・CI 構成はプロジェクトごとに異なるので、
固有値を覚えるのではなく毎サイクル、自分が編集する箇所(initializer の regexp・config/locales)を探索して
見つけ直す(手順 2)。種別ごとの典型例は references/example-touchpoints.md を参照。
なぜ prefix 単位で切るのか
一発で全 i18n をローカル化すると、移行漏れ(ローカル YAML に書き忘れたキー)が一斉に未訳化して事故になる。 prefix 単位なら、移した範囲だけが影響を受け、移行漏れはその範囲の未訳として小さく顕在化する。安全な prefix から 順に潰していける。
前提となる gem 機能(local_first_key_regexp)
copy-tuner-ruby-client #110 で追加された
local_first_key_regexp を使う。挙動の要点(references/local-first-regexp.md に詳細):
- locale を除いたキー(
views.foo等)が regexp にマッチすると、I18nBackend#lookupは CopyTuner キャッシュもアップロードキューも一切見ず、Rails 標準バックエンド(I18n::Backend::Simple)に委譲して ローカル YAML だけを引く。これを完全分離と呼ぶ。 - 完全分離なので、マッチキーがローカル YAML に無ければ即
nil(未訳)。CopyTuner へフォールバックしない。 これが「移行漏れを未訳として顕在化させる」仕組み。 - マッチしないキーは従来どおり CopyTuner キャッシュ優先 → 無ければローカル、という動作のまま。
- regexp は単一(配列非対応)。複数 prefix は
Regexp.unionで 1 本に積み上げる。
gem を残したまま regexp に prefix を足していくだけなので、CopyTuner と config/locales は安全に共存する。 部分ローカル化ならこの共存が定常状態、全移行なら移行途中の状態としてそのまま成り立つ。
ワークフロー(1 サイクル = 1 prefix)
0. 対象 prefix の受け取り(引数)
スキル引数で対象 prefix を渡せる(例: devise / activerecord.attributes / views.users)。
- 引数あり … それを今回の対象とし、手順 4 の選定はスキップする。ただし手順 3 の全件 export は
実行する(手順 6 の
--export入力として必須なので省けない)。 - 引数の prefix が export に存在しなければ、その旨をユーザーに報告して中断する(手順 6 のスクリプトも 同じ条件で異常終了するが、手順 3 の時点で気づけるほうが早い)。
- 引数なし … 従来どおり手順 4 の基準で 1 つ選び、選定結果をユーザーに提示してから手順 5 へ進む。
1. gem 前提確認
local_first_key_regexp が使えるバージョンの copy_tuner_client が入っているか確認する。
bin/rails runner 'p CopyTunerClient.configuration.respond_to?(:local_first_key_regexp)'
true でなければこのスキルは使えない。gem を PR #110 が入ったバージョンへ上げてから(Gemfile 更新 →
bundle update copy_tuner_client)出直す。バージョンアップはこのスキルの前提条件であり、ここで止める。
2. 編集対象の場所を確認(毎サイクル)
このスキルが実際に編集するのは次の箇所だけなので、毎サイクル開始時にこれらの場所を軽く確認する。
grep 結果はセッションをまたいで残らない(複数セッションに分割して進める前提)ため、「初回に把握したはず」に
頼らず毎回見つけ直す。vendor/・tmp/・node_modules/・Gemfile.lock は除外する。
# 毎サイクル触る: initializer の local_first_key_regexp と config/locales の採番慣習
git grep -nI 'local_first_key_regexp' -- ':!vendor' ':!tmp' ':!node_modules'
ls config/locales
# 手順 9 用: i18n 方針ドキュメント
git grep -nI 'CopyTuner' -- 'doc/' 'CLAUDE.md'
- 手順 6・7 で毎回触る initializer の
local_first_key_regexpの位置と、config/locales/の採番慣習 (例:00_・10_)を確認する。 - 手順 9(i18n 方針ドキュメントの更新)で触る箇所も、ここで場所だけ押さえる。
2-1. (初回のみ)既存 locales を 0000_original_ プレフィックスへリネーム
このスキルの分割方針(手順 6)は、既存 config/locales をロード最先頭に固定し、移行で足すファイルを後ろに
置いて Rails i18n の後勝ちで上書きすることを前提にする。そのため初回サイクルの最初に一度だけ、既存の
locales ファイルを 0000_original_ プレフィックスへリネームする。
# 例(既存ファイル名に合わせて読み替える):
git mv config/locales/00_ja.yml config/locales/0000_original_ja.yml
git mv config/locales/00_devise.ja.yml config/locales/0000_original_devise.ja.yml
git mv config/locales/10_app.yml config/locales/0000_original_app.yml
- 初回判定:
ls config/localesに0000_original_で始まるファイルが既にあれば、このリネームは済んでいる のでスキップする(2 サイクル目以降は常にスキップ)。 0000_は数値プレフィックスで必ず最先頭ロードになる。以降 migrate で足すファイルは0010_以降に置き (手順 6)、ロード順で後勝ち=オリジナルを上書きする。Regexp.unionの積み上げ(手順 7)と合わせ、 「重複キーは export 側を正とする」を手作業マージなしで構造的に保証するのがこの固定の狙い。- このリネームはファイルの中身を一切変えない(純粋にロード順を確定するだけ)。リネーム後に rspec を流し、
translation missingが増えていないことだけ確認する。
gem・CI の deploy ワークフロー・deploy フック・MCP 設定の撤去は copy-tuner-to-locales-cleanup の仕事で、
cleanup は自前で touchpoint を grep し直す。このスキルでそれらを棚卸し・編集する必要はない。種別ごとの
典型的な在処は references/example-touchpoints.md を参照。
3. 残 prefix の把握
全件を export して俯瞰し、ローカル化済み(現在の local_first_key_regexp がマッチする)prefix と
copy_tuner 管理のまま残っている prefix を一覧化する。export は一時ファイルへ書く(tmp/ 等の捨て場)。
対象 prefix が引数で指定されている場合、この一覧化は対象 prefix が export に存在することの確認と規模把握の
ためになる(選定は不要)。いずれの場合も rake copy_tuner:export の実行自体は必須で、出力は手順 6 の
--export 入力になる。
bundle exec rake copy_tuner:export[tmp/copy_tuner_all.yml]
このタスクは内部で CopyTunerClient.cache.sync を呼び、サーバから最新を取得してから全 blurb を書き出す
(全件取得の唯一確実な手段。詳細は references/export-and-split.md)。出力 YAML のトップセクションを眺めて、
どの prefix が残っているかを確認する。
4. 対象 prefix の選定
引数で prefix が指定されている場合はこの手順をスキップし、手順 5 へ進む。
残 prefix から 1 つ選ぶ。以下の順序は安全度の目安で、影響が小さく構造が安定したものから始め、最後に
大物(views)を回す:
- gem 由来(最安全・先行):
devise/good_job/ice_cube/restrict_dependent_destroy等。 値が安定しアプリ実装に依存しにくい。 - Rails 標準フォーマット:
date/time/datetime/number/helpers/text。 ただし非表現値(配列・シンボル・数値)の注意あり(手順 6)。 - バリデーションメッセージ:
activerecord.errors/activemodel.errors。テストで検知しやすい。 - モデル名・カラム名:
activerecord.models/activerecord.attributes/activemodel.attributes/activerecord.enums。プロジェクトの i18n 方針で「新規キー登録の例外」とされていることが多い (プロジェクトの i18n 方針ドキュメントでそう規定されていることが多い)。全 prefix を移行する場合は この prefix も対象に含める(例外規定の撤廃は cleanup で行う)。 - 画面テキスト(最大・最後):
views/text。量が多く画面影響が大きいので最後に回し、画面確認の比重を 上げる。1 回が大きすぎるならviews.<controller>.の 2 階層目で更に刻んでよい(regexp を\Aviews\.users\.のように書ける)。
5. 不正キーの事前チェック(関門)
export 済み YAML を config/locales に持ち込む前に、不正キーがないことを確認する。
bundle exec rake copy_tuner:detect_conflict_keys
bundle exec rake copy_tuner:detect_html_incompatible_keys
detect_conflict_keys… キー衝突。fooという値キーとfoo.barというネストキーが同居すると YAML へ 正しく展開できない。detect_html_incompatible_keys…html_escape有効環境で.html慣習と矛盾する値など。
これらのタスクは全キー対象で prefix 絞り込み引数は無い。出力のうち今回の対象 prefix に該当する行だけを
関門にする(対象外 prefix の不正キーは、その prefix を移す回まで保留してよい)。該当があればそのまま
config/locales に持ち込むと壊れるので、一覧をユーザーに報告し、copy_tuner の管理画面側で修正してもらってから
進む。(タスクが見つからない場合は bundle exec rake -T copy_tuner で確認。)
6. 対象 prefix を config/locales へ配置(スクリプトで一気通貫)
配置・移行漏れ検証・オリジナルからの削除は決定論的なので、手作業でなく scripts/migrate_prefix.rb を
bin/rails runner で実行する。1 本のスクリプトで配置 → 静的ガード → 移行漏れ検証 → オリジナル削除まで
一気通貫で行い、漏れが 1 件でもあればオリジナルを一切変更せず中断する。
bin/rails runner .claude/skills/copy-tuner-to-locales-migrate-prefix/scripts/migrate_prefix.rb \
-- --prefix date --export tmp/copy_tuner_all.yml --out config/locales/0010_date.yml
--prefix… 今回移行する prefix(ドット区切り。date/activerecord.attributes等)。--export… 手順 3 で出した全件 export YAML。--out… 移行分の配置先。オリジナル(0000_original_*.yml)より後にロードされる採番(0010_以降)。 採番慣習は手順 2 で確認したものに合わせる。--regexp… 省略時は prefix から/\A<prefix>\./を自動生成(手順 7 のlocal_first_key_regexpと整合)。 2 階層目で刻む(views.users等)ときだけ明示する。--locales… 省略時はI18n.available_locales(default_localeを先頭)を対象 locale にする。--locales ja,enのように明示すると上書きできる(fixture を使った検証用)。スクリプトは対象 locale すべてを横断して配置・ 検証・削除する(単一ロケール運用でも、将来 locale を足しても自動追従する)。
スクリプトの動作(詳細・設計判断は references/export-and-split.md):
配置・検証・削除は対象 locale(--locales/既定は I18n.available_locales)ごとに独立して行い、--out には
全 locale のサブツリー(ja: / en: …)を書き出す。あるロケールに当該 prefix のキーが無ければ warn して
そのロケールはスキップする。
- 配置: オリジナル全ファイルから対象 prefix サブツリーを抽出し(ファイル名昇順=ロード順で deep merge)、
export サブツリーをその上に deep merge(String blurb は export 勝ち)、最後にオリジナル由来の非表現値
(配列・シンボル・数値・真偽値)を再適用して
--outへ書き出す(deep_merge(deep_merge(orig, exp), non_blurb))。 「export を正とする」を満たしつつ、非表現値は orig 値が必ず勝つ(export 側に壊れた非表現値が出ても置換 されない。後述)。 - 静的ガード: YAML ラウンドトリップ一致・
--outがconfig/locales配下の.yml(次回起動の i18n glob で ロードされる場所)・全 leaf キーが regexp にマッチ、を確認(採番ミス・regexp 不一致・YAML 崩れの早期検出)。 - 移行漏れ検証: オリジナル削除をメモリ上でシミュレートし、削除後ツリーを素の
I18n::Backend::Simpleに 載せて対象 prefix の全キーを実 lookup。translation missingになるキーがあれば漏れ。 - ゲート: 漏れゼロのときだけオリジナルから該当サブツリーを削除(空親も刈る)。漏れがあれば中断。
非表現値が必ず orig 値で残る: copy_tuner は flat な文字列 blurb しか持てないため、配列・シンボル・数値・
真偽値(date.order の :year、date.*_names、number.*.precision/*.significant 等)は export に出てこない
/壊れて(文字列化して)出てくる可能性がある。スクリプトは export を上書きした後に、オリジナル由来の
非表現値(非 String leaf)を再適用するので、export 側に壊れた値が出ても orig の正値が置換勝ちする。同じ
prefix 内で date.formats(文字列・export 勝ち)と date.order(シンボル配列・orig 勝ち)が住み分く。現スキルが
以前使っていた 0005_rails_non_blurb.yml への別隔離は不要(このスクリプトに吸収済み)。
NOTE: 削除は手順 8 ではなくこのスクリプト内で完結する(移行漏れ検証を通った場合のみ)。オリジナルを 残したまま検証しても、オリジナルが漏れを埋めてしまい未訳検知が無意味になるため、検証と削除を同一スクリプトに 束ねている。スクリプトが中断した場合は
--outのファイルだけが残る(オリジナルは無傷)ので、原因を直して 再実行するか--outを消してやり直す。
NOTE: スクリプトは「対象 prefix の削除で実際に内容が変わったファイルだけ」を書き戻す(変更が無ければ
File.writeをスキップする)。とはいえ--originals-globの指定ミス等で意図しないファイルが対象に 入っていないとも限らないため、実行後は必ずgit diff --stat config/locales/で「対象 prefix を含む はずのファイルだけに差分が出ているか」を確認する。対象 prefix と無関係なはずのファイルに差分が出て いたらgit checkout -- <file>で復元し、原因(--originals-globや--prefixの指定)を見直す。
NOTE: 書き戻されたファイルではコメント・空行が失われる(YAML 標準ライブラリはこれらを保持しない。 値・エイリアス参照は保たれる)。
git diffを見て惜しいコメントがあれば手で戻すこと。
7. local_first_key_regexp に prefix を追加
initializer(config/initializers/copy_tuner.rb 等)の CopyTunerClient.configure ブロックで、
local_first_key_regexp を Regexp.union で組み直し、今回の prefix を 1 本足す。
config.local_first_key_regexp = Regexp.union(
/\Adevise\./,
/\Aice_cube\./,
/\Aviews\./, # ← 今回追加した prefix
)
必ず \A でアンカーする(views が reviews に部分マッチする事故を防ぐ。キーは locale 除去後なので
\A 起点で良い)。詳細・なぜ単一 Regexp なのかは references/local-first-regexp.md。
8. 検証(任意・ユーザー判断)
手順 6 のスクリプトが移行漏れ(削除後に未訳化するキー)がゼロであることを機械的に確認した上で削除まで
済ませている(メモリ上で削除をシミュレートし、素の I18n::Backend::Simple で対象 prefix の全キーを実 lookup)。
そのためスキルとしての rspec/画面確認は必須にしない。すべて git 管理下なので、問題があれば戻せる。
ただしスクリプト内の検証は静的キー集合とロード経路までしか見ない。次の実行時コンテキスト依存の範囲は カバーしないので、必要に応じてユーザー判断で確認する:
- lazy lookup
t('.key')… 実際の controller/view コンテキストでフルキーが決まる。 - 動的キー
t("views.#{type}.title")… 静的に列挙できないキー。 _htmlの表示崩れ・補間%{...}… lookup 成功と表示の正しさは別。
確認したい場合は rspec(docker compose up -d db → bundle exec rspec)や主要画面で translation missing が
出ないか見る。i18n 参照パターンの確認例は references/verification-per-prefix.md。
NOTE: regexp 追加(手順 7)後にスクリプトの regexp 引数と
local_first_key_regexpがずれていないか、bin/rails runner 'p CopyTunerClient.configuration.local_first_key?("<prefix>.foo")'がtrue、隣接キー (reviews.*等)がfalseになることを確認しておくとよい。
9. i18n 方針ドキュメントを更新
i18n 方針ドキュメント(doc/ 等)が「copy_tuner で管理/config/locales は使わない」のまま残ると、
他の作業者や AI が「新規キーを copy_tuner と locales のどちらに足すか」を誤判断する。用途に応じて書き分ける
(テンプレ文は references/example-touchpoints.md にある):
- 部分ローカル化 … 「移行中」ではなく恒久的な二層管理として書く。「以下の prefix は config/locales 管理」「それ以外は copy_tuner 管理」「新規キーの追加先はどちら」の 3 点を明記する。
- 全移行 … 「copy_tuner から config/locales へ段階移行中」+現在ローカル化済みの prefix を列挙する (最終形への書き換えは cleanup で行う)。
いずれも prefix を増やすたびに列挙を更新する。
10. 結果を報告
今回ローカル化した prefix と、現在の local_first_key_regexp を報告して 1 サイクル終了。加えて:
- 部分ローカル化 … これで完了。残りの prefix は copy_tuner 管理のままが定常状態なので、次サイクルは不要。
- 全移行 … copy_tuner 管理のまま残っている prefix の一覧も報告する。残りがあれば次サイクルでこのスキルを
再実行する。全 prefix がローカル化済みになったら
copy-tuner-to-locales-cleanupスキルへ進む。
1 サイクル完了の目安
- 手順 6 のスクリプトが移行漏れゼロを確認して正常終了し、
--out(0010_以降)に対象 prefix が配置され、0000_original_*.ymlから該当サブツリーが削除されている(非表現値は--out側に保持済み)。 local_first_key_regexpに対象 prefix が\Aアンカー付きで追加されている(手順 7)。- i18n 方針ドキュメントの「config/locales 管理の prefix」が更新されている(手順 9)。
- (任意)rspec/画面で
translation missingが出ないことをユーザー判断で確認(手順 8)。
レビュー
まだレビューはありません。使ってみた感想をお寄せください。
同じリポジトリのスキル
概要と使いどころ
copy_tuner(CopyTuner / copy_tuner_client)の全 i18n キーを config/locales へ prefix 単位で移行し終えた後、 gem・初期化子・CI・deploy・ドキュメント・MCP 設定を一括撤去して copy_tuner 依存を完全に取り除くスキル。 prefix 単位の移行は copy-tuner-to-locales-migrate-prefix スキルで先に完了させておくこと。
copy_tuner_client v2.0.0 への移行で、削除された独自ヘルパー tt の呼び出しを Rails 標準の t(translate)へ 置換するスキル。tt は PR #122 で存在理由を失い gem から削除されたため、残った tt(...) は NoMethodError に なる。大半は機械的に tt( → t( で済むが、訳文を文字列加工している箇所(→ I18n.t へ)と label 系ヘルパーの 第一引数に渡している箇所(→ 引数構造を分離)だけは別扱いで、開発環境のマーカートークン残留バグを避けるため 1 件ずつ確認する。対象は app 配下の .rb / .haml / .erb。