copy_tuner_client 2.1.2 → 2.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +5 -0
- data/.rubocop.yml +46 -1
- data/CHANGELOG.md +2 -235
- data/CLAUDE.md +1 -0
- data/Gemfile +1 -0
- data/README.md +42 -5
- data/copy_tuner_client.gemspec +3 -3
- data/gemfiles/8.0.gemfile +3 -3
- data/gemfiles/8.1.gemfile +3 -3
- data/gemfiles/main.gemfile +3 -3
- data/lib/copy_tuner_client/cache.rb +34 -27
- data/lib/copy_tuner_client/client.rb +35 -23
- data/lib/copy_tuner_client/configuration.rb +87 -40
- data/lib/copy_tuner_client/copyray/rewriter.rb +1 -1
- data/lib/copy_tuner_client/copyray.rb +1 -0
- data/lib/copy_tuner_client/copyray_middleware.rb +31 -24
- data/lib/copy_tuner_client/dotted_hash.rb +7 -7
- data/lib/copy_tuner_client/engine.rb +4 -6
- data/lib/copy_tuner_client/helper_extension.rb +58 -54
- data/lib/copy_tuner_client/i18n_backend.rb +12 -14
- data/lib/copy_tuner_client/i18n_compat.rb +2 -1
- data/lib/copy_tuner_client/poller.rb +3 -4
- data/lib/copy_tuner_client/prefixed_logger.rb +13 -12
- data/lib/copy_tuner_client/process_guard.rb +29 -26
- data/lib/copy_tuner_client/queue_with_timeout.rb +29 -19
- data/lib/copy_tuner_client/rails.rb +9 -8
- data/lib/copy_tuner_client/request_sync.rb +26 -24
- data/lib/copy_tuner_client/simple_form_extention.rb +10 -6
- data/lib/copy_tuner_client/translation_log.rb +10 -5
- data/lib/copy_tuner_client/version.rb +1 -1
- data/lib/copy_tuner_client.rb +4 -8
- data/lib/tasks/copy_tuner_client_tasks.rake +14 -16
- data/skills/copy-tuner-to-locales-cleanup/SKILL.md +5 -5
- data/skills/copy-tuner-to-locales-migrate-prefix/SKILL.md +69 -37
- data/skills/copy-tuner-to-locales-migrate-prefix/references/example-touchpoints.md +31 -10
- data/skills/copy-tuner-to-locales-migrate-prefix/scripts/migrate_prefix.rb +158 -13
- data/skills/copy-tuner-to-t-migrate/scripts/migrate_tt.rb +19 -17
- data/spec/copy_tuner_client/cache_spec.rb +63 -57
- data/spec/copy_tuner_client/client_spec.rb +82 -81
- data/spec/copy_tuner_client/configuration_spec.rb +380 -206
- data/spec/copy_tuner_client/copyray/marker_spec.rb +2 -1
- data/spec/copy_tuner_client/copyray/rewriter_spec.rb +15 -10
- data/spec/copy_tuner_client/copyray_middleware_spec.rb +9 -6
- data/spec/copy_tuner_client/copyray_spec.rb +8 -8
- data/spec/copy_tuner_client/dotted_hash_spec.rb +33 -33
- data/spec/copy_tuner_client/helper_extension_spec.rb +47 -44
- data/spec/copy_tuner_client/i18n_backend_spec.rb +144 -143
- data/spec/copy_tuner_client/poller_spec.rb +25 -24
- data/spec/copy_tuner_client/prefixed_logger_spec.rb +15 -12
- data/spec/copy_tuner_client/process_guard_spec.rb +27 -27
- data/spec/copy_tuner_client/request_sync_spec.rb +51 -58
- data/spec/copy_tuner_client/translation_log_spec.rb +38 -24
- data/spec/copy_tuner_client/warden_integration_spec.rb +95 -0
- data/spec/copy_tuner_client_spec.rb +5 -6
- data/spec/support/client_spec_helpers.rb +1 -1
- data/spec/support/defines_constants.rb +3 -34
- data/spec/support/fake_client.rb +1 -3
- data/spec/support/fake_copy_tuner_app.rb +54 -62
- data/spec/support/fake_html_safe_string.rb +2 -3
- data/spec/support/fake_logger.rb +22 -21
- data/spec/support/fake_passenger.rb +4 -6
- data/spec/support/fake_unicorn.rb +2 -4
- data/spec/support/middleware_stack.rb +38 -4
- data/spec/support/writing_cache.rb +4 -4
- metadata +2 -1
|
@@ -64,9 +64,10 @@ bin/rails runner '
|
|
|
64
64
|
> NOTE: ここで `export` を実行できるのは gem がまだ入っているから。完了判定は gem 撤去より**前**に行う。
|
|
65
65
|
|
|
66
66
|
> 補助目印: migrate は prefix を移すたびにオリジナル(`0000_original_*.yml`)から該当サブツリーを削除するので、
|
|
67
|
-
>
|
|
68
|
-
>
|
|
69
|
-
>
|
|
67
|
+
> **全移行を経た場合**は `0000_original_*.yml` がほぼ空になっているはず(非表現値も `migrate_prefix.rb` が
|
|
68
|
+
> 移行分側の `--out` へ再適用済みで、オリジナル側に隔離ファイルとして残ることはない)。`local_first_key?`
|
|
69
|
+
> のマッチ判定が主の関門で、ファイルが空かどうかは副次的な目視確認。食い違うとき(regexp は全マッチなのに
|
|
70
|
+
> オリジナルに blurb 化できるキーが残っている等)は削除漏れを疑う。
|
|
70
71
|
|
|
71
72
|
### 2. 最終不正キーチェック
|
|
72
73
|
|
|
@@ -129,8 +130,7 @@ copyray コメント注入も消える。あわせて `config/environments/*.rb`
|
|
|
129
130
|
|
|
130
131
|
- copy_tuner 専用の deploy ワークフローファイル(main push で翻訳をデプロイする専用ファイル)… 丸ごと削除。
|
|
131
132
|
- AI エージェント用ワークフローの `mcp__copy-tuner__*` allowedTools 許可 … 削除。
|
|
132
|
-
- CI の「翻訳を export するステップ」…
|
|
133
|
-
(`git grep copy_tuner .github/` で確認)。
|
|
133
|
+
- CI の「翻訳を export するステップ」… ここで削除する(`git grep copy_tuner .github/` で確認)。
|
|
134
134
|
|
|
135
135
|
### 6. deploy / 起動スクリプトを撤去
|
|
136
136
|
|
|
@@ -2,24 +2,33 @@
|
|
|
2
2
|
name: copy-tuner-to-locales-migrate-prefix
|
|
3
3
|
description: >-
|
|
4
4
|
copy_tuner(CopyTuner / copy_tuner_client)で集中管理している i18n データを、prefix(正規表現)単位で
|
|
5
|
-
Rails 標準の config/locales(YAML
|
|
6
|
-
|
|
7
|
-
copy-tuner-to-locales-cleanup
|
|
5
|
+
Rails 標準の config/locales(YAML)管理へ移すスキル。gem の local_first_key_regexp を使うので、
|
|
6
|
+
gem を残したまま特定 prefix だけをローカル管理にできる(部分ローカル化)。1 回の実行で 1 prefix。
|
|
7
|
+
全 prefix を移して gem ごと撤去したい場合は繰り返し、完了後 copy-tuner-to-locales-cleanup スキルへ進む。
|
|
8
|
+
対象 prefix はスキル引数で指定でき、未指定なら export を俯瞰して選定する。
|
|
8
9
|
disable-model-invocation: true
|
|
9
10
|
---
|
|
10
11
|
|
|
11
|
-
# copy_tuner → config/locales
|
|
12
|
+
# copy_tuner → config/locales prefix 単位ローカル化スキル
|
|
12
13
|
|
|
13
|
-
copy_tuner(`copy_tuner_client` gem)で集中管理している i18n データを、**prefix
|
|
14
|
-
Rails 標準の `config/locales` 配下の YAML
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
copy_tuner(`copy_tuner_client` gem)で集中管理している i18n データを、**prefix(正規表現)単位で**
|
|
15
|
+
Rails 標準の `config/locales` 配下の YAML 管理へ移すためのワークフロー。**1 回の実行で 1 prefix だけ**扱う。
|
|
16
|
+
|
|
17
|
+
使い方は 2 つある:
|
|
18
|
+
|
|
19
|
+
- **部分ローカル化** — 特定 prefix だけを恒久的に `config/locales` 管理にする。**gem は残したまま**で、
|
|
20
|
+
CopyTuner 管理と locales 管理の二層が**定常状態**になる。
|
|
21
|
+
- **全移行** — 上記を全 prefix ぶん繰り返す。全 prefix の移行が完了したら
|
|
22
|
+
`copy-tuner-to-locales-cleanup` スキルで gem・CI・deploy・docs・MCP をまとめて撤去する。
|
|
23
|
+
|
|
24
|
+
**どちらの用途でもこのスキルがやること(手順 0〜10)は同一**で、差は「何回回すか」と「最後に cleanup へ
|
|
25
|
+
進むか」だけ。
|
|
17
26
|
|
|
18
27
|
このスキルは**特定のリポジトリに依存しない**。project_id・ファイルパス・CI 構成はプロジェクトごとに異なるので、
|
|
19
28
|
固有値を覚えるのではなく**毎サイクル、自分が編集する箇所(initializer の regexp・config/locales)を探索して
|
|
20
29
|
見つけ直す**(手順 2)。種別ごとの典型例は `references/example-touchpoints.md` を参照。
|
|
21
30
|
|
|
22
|
-
## なぜ prefix
|
|
31
|
+
## なぜ prefix 単位で切るのか
|
|
23
32
|
|
|
24
33
|
一発で全 i18n をローカル化すると、移行漏れ(ローカル YAML に書き忘れたキー)が**一斉に未訳化**して事故になる。
|
|
25
34
|
prefix 単位なら、移した範囲だけが影響を受け、移行漏れはその範囲の未訳として小さく顕在化する。安全な prefix から
|
|
@@ -38,11 +47,21 @@ prefix 単位なら、移した範囲だけが影響を受け、移行漏れは
|
|
|
38
47
|
- マッチしないキーは従来どおり CopyTuner キャッシュ優先 → 無ければローカル、という動作のまま。
|
|
39
48
|
- regexp は**単一**(配列非対応)。複数 prefix は `Regexp.union` で 1 本に積み上げる。
|
|
40
49
|
|
|
41
|
-
gem を残したまま regexp に prefix
|
|
42
|
-
|
|
50
|
+
gem を残したまま regexp に prefix を足していくだけなので、CopyTuner と config/locales は安全に共存する。
|
|
51
|
+
部分ローカル化ならこの共存が定常状態、全移行なら移行途中の状態としてそのまま成り立つ。
|
|
43
52
|
|
|
44
53
|
## ワークフロー(1 サイクル = 1 prefix)
|
|
45
54
|
|
|
55
|
+
### 0. 対象 prefix の受け取り(引数)
|
|
56
|
+
|
|
57
|
+
スキル引数で対象 prefix を渡せる(例: `devise` / `activerecord.attributes` / `views.users`)。
|
|
58
|
+
|
|
59
|
+
- **引数あり** … それを今回の対象とし、**手順 4 の選定はスキップ**する。ただし**手順 3 の全件 export は
|
|
60
|
+
実行する**(手順 6 の `--export` 入力として必須なので省けない)。
|
|
61
|
+
- 引数の prefix が export に**存在しなければ**、その旨をユーザーに報告して中断する(手順 6 のスクリプトも
|
|
62
|
+
同じ条件で異常終了するが、手順 3 の時点で気づけるほうが早い)。
|
|
63
|
+
- **引数なし** … 従来どおり手順 4 の基準で 1 つ選び、**選定結果をユーザーに提示してから**手順 5 へ進む。
|
|
64
|
+
|
|
46
65
|
### 1. gem 前提確認
|
|
47
66
|
|
|
48
67
|
`local_first_key_regexp` が使えるバージョンの `copy_tuner_client` が入っているか確認する。
|
|
@@ -65,14 +84,13 @@ grep 結果はセッションをまたいで残らない(複数セッション
|
|
|
65
84
|
git grep -nI 'local_first_key_regexp' -- ':!vendor' ':!tmp' ':!node_modules'
|
|
66
85
|
ls config/locales
|
|
67
86
|
|
|
68
|
-
#
|
|
69
|
-
git grep -nI
|
|
87
|
+
# 手順 9 用: i18n 方針ドキュメント
|
|
88
|
+
git grep -nI 'CopyTuner' -- 'doc/' 'CLAUDE.md'
|
|
70
89
|
```
|
|
71
90
|
|
|
72
91
|
- 手順 6・7 で毎回触る **initializer の `local_first_key_regexp`** の位置と、**`config/locales/`** の採番慣習
|
|
73
92
|
(例: `00_`・`10_`)を確認する。
|
|
74
|
-
- 手順 9(
|
|
75
|
-
ここで場所だけ押さえる。
|
|
93
|
+
- 手順 9(i18n 方針ドキュメントの更新)で触る箇所も、ここで場所だけ押さえる。
|
|
76
94
|
|
|
77
95
|
#### 2-1. (初回のみ)既存 locales を `0000_original_` プレフィックスへリネーム
|
|
78
96
|
|
|
@@ -101,8 +119,12 @@ cleanup は自前で touchpoint を grep し直す。**このスキルでそれ
|
|
|
101
119
|
|
|
102
120
|
### 3. 残 prefix の把握
|
|
103
121
|
|
|
104
|
-
全件を export
|
|
105
|
-
|
|
122
|
+
全件を export して俯瞰し、ローカル化済み(現在の `local_first_key_regexp` がマッチする)prefix と
|
|
123
|
+
copy_tuner 管理のまま残っている prefix を一覧化する。export は一時ファイルへ書く(`tmp/` 等の捨て場)。
|
|
124
|
+
|
|
125
|
+
対象 prefix が引数で指定されている場合、この一覧化は**対象 prefix が export に存在することの確認と規模把握**の
|
|
126
|
+
ためになる(選定は不要)。いずれの場合も `rake copy_tuner:export` の**実行自体は必須**で、出力は手順 6 の
|
|
127
|
+
`--export` 入力になる。
|
|
106
128
|
|
|
107
129
|
```bash
|
|
108
130
|
bundle exec rake copy_tuner:export[tmp/copy_tuner_all.yml]
|
|
@@ -114,7 +136,10 @@ bundle exec rake copy_tuner:export[tmp/copy_tuner_all.yml]
|
|
|
114
136
|
|
|
115
137
|
### 4. 対象 prefix の選定
|
|
116
138
|
|
|
117
|
-
|
|
139
|
+
**引数で prefix が指定されている場合はこの手順をスキップ**し、手順 5 へ進む。
|
|
140
|
+
|
|
141
|
+
残 prefix から **1 つ**選ぶ。以下の順序は**安全度の目安**で、影響が小さく構造が安定したものから始め、最後に
|
|
142
|
+
大物(`views`)を回す:
|
|
118
143
|
|
|
119
144
|
1. **gem 由来(最安全・先行)**: `devise` / `good_job` / `ice_cube` / `restrict_dependent_destroy` 等。
|
|
120
145
|
値が安定しアプリ実装に依存しにくい。
|
|
@@ -123,8 +148,8 @@ bundle exec rake copy_tuner:export[tmp/copy_tuner_all.yml]
|
|
|
123
148
|
3. **バリデーションメッセージ**: `activerecord.errors` / `activemodel.errors`。テストで検知しやすい。
|
|
124
149
|
4. **モデル名・カラム名**: `activerecord.models` / `activerecord.attributes` / `activemodel.attributes` /
|
|
125
150
|
`activerecord.enums`。プロジェクトの i18n 方針で「新規キー登録の例外」とされていることが多い
|
|
126
|
-
(プロジェクトの i18n
|
|
127
|
-
|
|
151
|
+
(プロジェクトの i18n 方針ドキュメントでそう規定されていることが多い)。**全 prefix を移行する場合は
|
|
152
|
+
この prefix も対象に含める**(例外規定の撤廃は cleanup で行う)。
|
|
128
153
|
5. **画面テキスト(最大・最後)**: `views` / `text`。量が多く画面影響が大きいので最後に回し、画面確認の比重を
|
|
129
154
|
上げる。1 回が大きすぎるなら `views.<controller>.` の 2 階層目で更に刻んでよい(regexp を `\Aviews\.users\.`
|
|
130
155
|
のように書ける)。
|
|
@@ -197,6 +222,15 @@ prefix 内で `date.formats`(文字列・export 勝ち)と `date.order`(
|
|
|
197
222
|
> 束ねている。スクリプトが中断した場合は `--out` のファイルだけが残る(オリジナルは無傷)ので、原因を直して
|
|
198
223
|
> 再実行するか `--out` を消してやり直す。
|
|
199
224
|
|
|
225
|
+
> NOTE: スクリプトは「対象 prefix の削除で実際に内容が変わったファイルだけ」を書き戻す(変更が無ければ
|
|
226
|
+
> `File.write` をスキップする)。とはいえ `--originals-glob` の指定ミス等で意図しないファイルが対象に
|
|
227
|
+
> 入っていないとも限らないため、実行後は必ず `git diff --stat config/locales/` で「対象 prefix を含む
|
|
228
|
+
> はずのファイルだけに差分が出ているか」を確認する。対象 prefix と無関係なはずのファイルに差分が出て
|
|
229
|
+
> いたら `git checkout -- <file>` で復元し、原因(`--originals-glob` や `--prefix` の指定)を見直す。
|
|
230
|
+
|
|
231
|
+
> NOTE: 書き戻されたファイルでは**コメント・空行が失われる**(YAML 標準ライブラリはこれらを保持しない。
|
|
232
|
+
> 値・エイリアス参照は保たれる)。`git diff` を見て惜しいコメントがあれば手で戻すこと。
|
|
233
|
+
|
|
200
234
|
### 7. local_first_key_regexp に prefix を追加
|
|
201
235
|
|
|
202
236
|
initializer(`config/initializers/copy_tuner.rb` 等)の `CopyTunerClient.configure` ブロックで、
|
|
@@ -233,33 +267,31 @@ config.local_first_key_regexp = Regexp.union(
|
|
|
233
267
|
> `bin/rails runner 'p CopyTunerClient.configuration.local_first_key?("<prefix>.foo")'` が `true`、隣接キー
|
|
234
268
|
> (`reviews.*` 等)が `false` になることを確認しておくとよい。
|
|
235
269
|
|
|
236
|
-
### 9.
|
|
270
|
+
### 9. i18n 方針ドキュメントを更新
|
|
237
271
|
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
だけになり、**本番と同じ挙動**になる。未移行 prefix も引き続きサーバから解決できるので、移行途中に消しても安全。
|
|
272
|
+
i18n 方針ドキュメント(`doc/` 等)が「copy_tuner で管理/config/locales は使わない」のまま残ると、
|
|
273
|
+
他の作業者や AI が「新規キーを copy_tuner と locales のどちらに足すか」を誤判断する。用途に応じて書き分ける
|
|
274
|
+
(テンプレ文は `references/example-touchpoints.md` にある):
|
|
242
275
|
|
|
243
|
-
|
|
244
|
-
|
|
276
|
+
- **部分ローカル化** … 「移行中」ではなく**恒久的な二層管理**として書く。「以下の prefix は config/locales
|
|
277
|
+
管理」「それ以外は copy_tuner 管理」「新規キーの追加先はどちら」の 3 点を明記する。
|
|
278
|
+
- **全移行** … 「copy_tuner から config/locales へ段階移行中」+現在ローカル化済みの prefix を列挙する
|
|
279
|
+
(最終形への書き換えは cleanup で行う)。
|
|
245
280
|
|
|
246
|
-
|
|
281
|
+
いずれも prefix を増やすたびに列挙を更新する。
|
|
247
282
|
|
|
248
|
-
|
|
249
|
-
他の作業者や AI が「新規キーを copy_tuner と locales のどちらに足すか」を誤判断する。**移行中であることと、
|
|
250
|
-
現在ローカル化済みの prefix を明記する**。中間状態テンプレ文は `references/example-touchpoints.md` にある。
|
|
251
|
-
prefix を増やすたびに、列挙も更新する。
|
|
283
|
+
### 10. 結果を報告
|
|
252
284
|
|
|
253
|
-
|
|
285
|
+
今回ローカル化した prefix と、現在の `local_first_key_regexp` を報告して 1 サイクル終了。加えて:
|
|
254
286
|
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
`copy-tuner-to-locales-cleanup` スキルへ進む。
|
|
287
|
+
- **部分ローカル化** … これで完了。残りの prefix は copy_tuner 管理のままが定常状態なので、次サイクルは不要。
|
|
288
|
+
- **全移行** … copy_tuner 管理のまま残っている prefix の一覧も報告する。残りがあれば次サイクルでこのスキルを
|
|
289
|
+
再実行する。全 prefix がローカル化済みになったら `copy-tuner-to-locales-cleanup` スキルへ進む。
|
|
258
290
|
|
|
259
291
|
## 1 サイクル完了の目安
|
|
260
292
|
|
|
261
293
|
- 手順 6 のスクリプトが**移行漏れゼロを確認して正常終了**し、`--out`(`0010_` 以降)に対象 prefix が配置され、
|
|
262
294
|
`0000_original_*.yml` から該当サブツリーが削除されている(非表現値は `--out` 側に保持済み)。
|
|
263
295
|
- `local_first_key_regexp` に対象 prefix が `\A` アンカー付きで追加されている(手順 7)。
|
|
264
|
-
-
|
|
296
|
+
- i18n 方針ドキュメントの「config/locales 管理の prefix」が更新されている(手順 9)。
|
|
265
297
|
- (任意)rspec/画面で `translation missing` が出ないことをユーザー判断で確認(手順 8)。
|
|
@@ -61,7 +61,8 @@ end
|
|
|
61
61
|
|
|
62
62
|
→ オリジナルを `0000_original_` で先頭固定し、移行分は `0010_` 以降に置く。重複キーはロード順の**後勝ちで
|
|
63
63
|
export 側が勝つ**(手作業マージ不要)。prefix を移行するたびにオリジナルから該当サブツリーを削除し、残存=
|
|
64
|
-
未移行 prefix
|
|
64
|
+
未移行 prefix の進捗マーカーにする(**全 prefix を移行する場合は最終的にオリジナルが空になる**)。
|
|
65
|
+
部分ローカル化では、対象外 prefix がオリジナルに残り続けるのが**定常状態**であり、空にならなくてよい。
|
|
65
66
|
|
|
66
67
|
→ Rails 標準フォーマットの **配列**(`date.abbr_day_names` 等)・`date.order` の `:year` 等の**シンボル配列**・
|
|
67
68
|
`number.*.precision` 等の**非表現値**は copy_tuner で表現できず export に出てこないため、`date`/`number` を移行
|
|
@@ -69,10 +70,10 @@ export 側が勝つ**(手作業マージ不要)。prefix を移行するた
|
|
|
69
70
|
移行分(`0010_` 以降)の中へ**非表現値ごと取り込む**。別ファイルへの隔離は不要(詳細は
|
|
70
71
|
`references/export-and-split.md`)。
|
|
71
72
|
|
|
72
|
-
### CI — [
|
|
73
|
+
### CI — [cleanup]
|
|
73
74
|
|
|
74
75
|
- **CI の翻訳 export ステップ**(テストワークフロー内で `bin/rake copy_tuner:export` を走らせる類)…
|
|
75
|
-
「翻訳 DL 失敗でテストがコケないように」の保険。**[
|
|
76
|
+
「翻訳 DL 失敗でテストがコケないように」の保険。**[cleanup] で削除**(test が本番同等の
|
|
76
77
|
`cache.download` 挙動になる)。`disable_test_translation` は入れない。
|
|
77
78
|
- **copy_tuner 専用の deploy ワークフロー**(main push で翻訳をデプロイする専用ファイル)…
|
|
78
79
|
**[cleanup] で丸ごと削除**。
|
|
@@ -88,10 +89,11 @@ export 側が勝つ**(手作業マージ不要)。prefix を移行するた
|
|
|
88
89
|
|
|
89
90
|
`config/environments/production.rb` の `config.i18n.fallbacks = true` 等。標準バックエンドでも有効なので確認のみ。
|
|
90
91
|
|
|
91
|
-
### ドキュメント / スキル / MCP — [migrate]
|
|
92
|
+
### ドキュメント / スキル / MCP — [migrate](用途別に更新) / [cleanup](最終化・撤去)
|
|
92
93
|
|
|
93
94
|
- **i18n 方針ドキュメント**(`CLAUDE.md`・`doc/` 配下等)… 「copy_tuner サーバで i18n データを管理 /
|
|
94
|
-
config/locales 配下は利用しない / 新規キー登録は基本禁止」等の記述。**[migrate]
|
|
95
|
+
config/locales 配下は利用しない / 新規キー登録は基本禁止」等の記述。**[migrate] で更新**(部分ローカル化なら
|
|
96
|
+
恒久的な二層管理として、全移行なら段階移行中の中間状態として。テンプレは後掲)。全移行の場合のみ
|
|
95
97
|
**[cleanup] で最終化**。上記を参照している他のドキュメント(`CLAUDE.md` 等)も連動。
|
|
96
98
|
- **copy_tuner MCP 操作スキル**(`.claude/skills/` 配下)… **[cleanup] で無効化/削除**。
|
|
97
99
|
- **補助ドキュメント** … 「多言語対応: copy_tuner サーバで i18n データを管理」のような記述を持つコマンド定義等。
|
|
@@ -107,10 +109,28 @@ copy_tuner 側のキー数・export YAML の行数はプロジェクト次第だ
|
|
|
107
109
|
`restrict_dependent_destroy` 等。これらのトップセクションが prefix 移行の基本粒度。`views` が最大になりやすいので
|
|
108
110
|
最後に回す。
|
|
109
111
|
|
|
110
|
-
## i18n
|
|
112
|
+
## i18n 方針ドキュメントテンプレ([migrate] 手順 9 で使う)
|
|
111
113
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
+
用途に応じて 2 版ある。`<列挙>` は現在 `local_first_key_regexp` にマッチしている prefix に更新する
|
|
115
|
+
(prefix を増やすたびに更新)。
|
|
116
|
+
|
|
117
|
+
### 部分ローカル化版
|
|
118
|
+
|
|
119
|
+
「移行中」ではなく、この状態が**恒久的に続く二層管理**であることを明記する。
|
|
120
|
+
|
|
121
|
+
```markdown
|
|
122
|
+
### 国際化(i18n)
|
|
123
|
+
|
|
124
|
+
- **一部の prefix は config/locales(YAML)管理、それ以外は copy_tuner サーバ管理**
|
|
125
|
+
- config/locales 管理の prefix(`local_first_key_regexp` にマッチ): `<列挙>`
|
|
126
|
+
- 上記以外の prefix は copy_tuner サーバで管理
|
|
127
|
+
- **新規キーの追加先**: 上記 prefix のキーは config/locales へ。それ以外は copy_tuner へ
|
|
128
|
+
- 複数形化対応は不要(日本語環境)
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
### 全移行版
|
|
132
|
+
|
|
133
|
+
「段階移行中」であることと、いずれ cleanup で最終化される中間状態であることを明記する。
|
|
114
134
|
|
|
115
135
|
```markdown
|
|
116
136
|
### 国際化(i18n)
|
|
@@ -122,5 +142,6 @@ copy_tuner 側のキー数・export YAML の行数はプロジェクト次第だ
|
|
|
122
142
|
- 複数形化対応は不要(日本語環境)
|
|
123
143
|
```
|
|
124
144
|
|
|
125
|
-
> [cleanup] で全 prefix 完了後、この中間記述は「config/locales 管理。
|
|
126
|
-
> config/locales
|
|
145
|
+
> 上記**全移行版にのみ係る注記**: [cleanup] で全 prefix 完了後、この中間記述は「config/locales 管理。
|
|
146
|
+
> copy_tuner 廃止。新規キーは config/locales へ。複数形化不要」に最終化する(モデル名・カラム名の例外規定も
|
|
147
|
+
> 撤廃)。部分ローカル化版は cleanup を経由しないため、この最終化は適用されない。
|
|
@@ -8,6 +8,10 @@
|
|
|
8
8
|
# bin/rails runner .claude/skills/copy-tuner-to-locales-migrate-prefix/scripts/migrate_prefix.rb \
|
|
9
9
|
# -- --prefix date --export tmp/copy_tuner_all.yml --out config/locales/0010_date.yml
|
|
10
10
|
#
|
|
11
|
+
# このスクリプト自身の自己検証(gem メンテナ向け。Rails も bundler も不要):
|
|
12
|
+
#
|
|
13
|
+
# ruby skills/copy-tuner-to-locales-migrate-prefix/scripts/migrate_prefix.rb --self-test
|
|
14
|
+
#
|
|
11
15
|
# What it does, in one pass:
|
|
12
16
|
# (1) Place : extract the target prefix subtree from the 0000_original_*.yml
|
|
13
17
|
# originals (load-order deep_merge), deep_merge the export subtree on top
|
|
@@ -29,6 +33,127 @@
|
|
|
29
33
|
require 'optparse'
|
|
30
34
|
require 'yaml'
|
|
31
35
|
|
|
36
|
+
# ---- self-test ----
|
|
37
|
+
# 引数パースより前に分岐する。このスクリプトはトップレベルで即実行する造りなので、`--self-test` の
|
|
38
|
+
# ときは本体へ進ませずテストだけ走らせてトップレベル return で抜ける。
|
|
39
|
+
if ARGV.delete('--self-test')
|
|
40
|
+
require 'minitest/autorun'
|
|
41
|
+
require 'tmpdir'
|
|
42
|
+
require 'fileutils'
|
|
43
|
+
require 'rbconfig'
|
|
44
|
+
|
|
45
|
+
# 本体はトップレベルで exit するため load では検証できない。fixture を tmpdir に作り、i18n だけを
|
|
46
|
+
# 直接設定する薄いランナー経由でサブプロセス実行する(スクリプトが触るのは I18n.load_path と
|
|
47
|
+
# Backend::Simple だけなので Rails は不要)。
|
|
48
|
+
class MigratePrefixSelfTest < Minitest::Test
|
|
49
|
+
SCRIPT = File.expand_path(__FILE__)
|
|
50
|
+
# 対象 prefix(date)を持つファイル。これだけが書き戻し対象になるのが期待挙動。
|
|
51
|
+
WITH_PREFIX = "ja:\n date:\n formats:\n default: \"%Y\"\n"
|
|
52
|
+
WITHOUT_PREFIX = "ja:\n greeting: \"hello\"\n"
|
|
53
|
+
# 片方だけが対象 prefix を持つ 2 ファイル構成。「1 ファイルだけ書き戻る」ことの検証に使う。
|
|
54
|
+
MIXED_FILES = { '0000_original_a.yml' => WITH_PREFIX, '0000_original_b.yml' => WITHOUT_PREFIX }.freeze
|
|
55
|
+
EXPORT_JA = "ja:\n date:\n formats:\n default: \"%Y/%m/%d\"\n"
|
|
56
|
+
EXPORT_JA_EN = "#{EXPORT_JA}en:\n date:\n formats:\n default: \"%m/%d/%Y\"\n".freeze
|
|
57
|
+
# to_yaml の既定 line_width(80)を確実に超える長さにして折り返しを誘発する。
|
|
58
|
+
LONG_VALUE = (['word'] * 60).join(' ').freeze
|
|
59
|
+
|
|
60
|
+
def test_untouched_file_keeps_bytes
|
|
61
|
+
# コメント・空行・ダブルクォートが 1 バイトも変わらないことを見る。
|
|
62
|
+
untouched = <<~YAML
|
|
63
|
+
ja:
|
|
64
|
+
# これは greeting のコメント
|
|
65
|
+
greeting: "hello"
|
|
66
|
+
|
|
67
|
+
farewell: "bye"
|
|
68
|
+
YAML
|
|
69
|
+
migrate({ '0000_original_a.yml' => WITH_PREFIX, '0000_original_b.yml' => untouched }) do |out, dir|
|
|
70
|
+
# binread は ASCII-8BIT を返すため、UTF-8 の期待値を b で揃えてバイト列として比較する。
|
|
71
|
+
assert_equal(untouched.b, read(dir, '0000_original_b.yml'), "無関係なファイルが書き換わった:\n#{out}")
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def test_missing_locale_root_is_not_added
|
|
76
|
+
migrate(MIXED_FILES, locales: %w[ja en], export: EXPORT_JA_EN) do |_out, dir|
|
|
77
|
+
MIXED_FILES.each_key do |name|
|
|
78
|
+
refute_includes(read(dir, name), 'en:', "#{name} に元ファイルに無い en ルートが新設された")
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def test_prefix_is_pruned_from_matching_file
|
|
84
|
+
migrate({ '0000_original_a.yml' => "#{WITH_PREFIX} greeting: \"hello\"\n" }) do |_out, dir|
|
|
85
|
+
assert_equal({ 'ja' => { 'greeting' => 'hello' } }, load_yaml(dir, '0000_original_a.yml'))
|
|
86
|
+
assert_equal(
|
|
87
|
+
{ 'ja' => { 'date' => { 'formats' => { 'default' => '%Y/%m/%d' } } } },
|
|
88
|
+
load_yaml(dir, '0010_migrated.yml')
|
|
89
|
+
)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def test_long_value_is_not_wrapped
|
|
94
|
+
migrate({ '0000_original_a.yml' => "#{WITH_PREFIX} greeting: \"#{LONG_VALUE}\"\n" }) do |_out, dir|
|
|
95
|
+
body = read(dir, '0000_original_a.yml')
|
|
96
|
+
assert_includes(body, "greeting: #{LONG_VALUE}\n", "長い値が折り返された:\n#{body}")
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def test_summary_counts_changed_files
|
|
101
|
+
migrate(MIXED_FILES) do |out, _dir|
|
|
102
|
+
assert_includes(out, 'オリジナル 2 ファイル中 1 ファイル')
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
private
|
|
107
|
+
|
|
108
|
+
# fixture を tmpdir に配置してサブプロセス実行し、標準出力と locales ディレクトリを yield する。
|
|
109
|
+
def migrate(files, locales: %w[ja], export: EXPORT_JA)
|
|
110
|
+
Dir.mktmpdir do |dir|
|
|
111
|
+
locales_dir = File.join(dir, 'config/locales')
|
|
112
|
+
FileUtils.mkdir_p(locales_dir)
|
|
113
|
+
FileUtils.mkdir_p(File.join(dir, 'tmp'))
|
|
114
|
+
files.each { |name, body| File.write(File.join(locales_dir, name), body) }
|
|
115
|
+
File.write(File.join(dir, 'tmp/export.yml'), export)
|
|
116
|
+
write_runner(dir, locales)
|
|
117
|
+
|
|
118
|
+
ok, out = spawn_script(dir, locales)
|
|
119
|
+
assert(ok, "スクリプトが異常終了した:\n#{out}")
|
|
120
|
+
yield(out, locales_dir)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def write_runner(dir, locales)
|
|
125
|
+
File.write(File.join(dir, 'runner.rb'), <<~RUBY)
|
|
126
|
+
require 'i18n'
|
|
127
|
+
I18n.load_path = Dir['config/locales/*.yml']
|
|
128
|
+
I18n.available_locales = #{locales.map(&:to_sym).inspect}
|
|
129
|
+
I18n.default_locale = #{locales.first.to_sym.inspect}
|
|
130
|
+
load #{SCRIPT.inspect}
|
|
131
|
+
RUBY
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
def spawn_script(dir, locales)
|
|
135
|
+
args = [
|
|
136
|
+
'--prefix', 'date', '--locales', locales.join(','),
|
|
137
|
+
'--export', 'tmp/export.yml', '--out', 'config/locales/0010_migrated.yml'
|
|
138
|
+
]
|
|
139
|
+
Dir.chdir(dir) do
|
|
140
|
+
read_io, write_io = IO.pipe
|
|
141
|
+
pid = spawn(RbConfig.ruby, File.join(dir, 'runner.rb'), '--', *args, out: write_io, err: write_io)
|
|
142
|
+
write_io.close
|
|
143
|
+
out = read_io.read
|
|
144
|
+
_, status = Process.waitpid2(pid)
|
|
145
|
+
[status.success?, out]
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
|
|
149
|
+
def read(dir, name) = File.binread(File.join(dir, name))
|
|
150
|
+
|
|
151
|
+
def load_yaml(dir, name) = YAML.safe_load_file(File.join(dir, name))
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
return
|
|
155
|
+
end
|
|
156
|
+
|
|
32
157
|
# NOTE: `bin/rails runner` は Kernel#abort が投げる SystemExit を握りつぶし終了コードが 0 になる
|
|
33
158
|
# (実機確認済み)。中断を呼び出し側へ確実に伝えるため、abort ではなく warn + exit(1) を使う。
|
|
34
159
|
def die(message)
|
|
@@ -40,12 +165,16 @@ end
|
|
|
40
165
|
options = {}
|
|
41
166
|
parser =
|
|
42
167
|
OptionParser.new do |opts|
|
|
43
|
-
opts.banner =
|
|
168
|
+
opts.banner =
|
|
169
|
+
'Usage: bin/rails runner migrate_prefix.rb -- --prefix PREFIX --export PATH --out PATH ' \
|
|
170
|
+
'[--regexp PATTERN] [--originals-glob GLOB]'
|
|
44
171
|
opts.on('--prefix PREFIX', 'ドット区切りの対象 prefix(例: date / activerecord.attributes)') { |v| options[:prefix] = v }
|
|
45
172
|
opts.on('--export PATH', '全件 export YAML(手順3で出力)') { |v| options[:export] = v }
|
|
46
173
|
opts.on('--out PATH', '移行分の出力先(例: config/locales/0010_date.yml)') { |v| options[:out] = v }
|
|
47
174
|
opts.on('--regexp PATTERN', 'leaf キー検証用の正規表現(省略時は prefix から \A<prefix>\. を生成)') { |v| options[:regexp] = v }
|
|
48
|
-
opts.on('--originals-glob GLOB', 'オリジナルファイルの glob(既定: config/locales/0000_original_*.yml)')
|
|
175
|
+
opts.on('--originals-glob GLOB', 'オリジナルファイルの glob(既定: config/locales/0000_original_*.yml)') do |v|
|
|
176
|
+
options[:originals_glob] = v
|
|
177
|
+
end
|
|
49
178
|
opts.on('--locales LIST', 'カンマ区切りの対象 locale(既定: I18n.available_locales。検証用の上書き)') { |v| options[:locales] = v }
|
|
50
179
|
end
|
|
51
180
|
# `bin/rails runner script -- ...` の `--` 以降だけを渡したいが、runner が剥がさない環境もあるため両対応。
|
|
@@ -156,7 +285,12 @@ end
|
|
|
156
285
|
# locale ルートを持つ raw Hash(`{ "ja" => {...}, "en" => {...} }`)から、対象 prefix を全 locale で刈った
|
|
157
286
|
# 新しい Hash を返す(非破壊)。移行漏れ検証のシミュレーションと実削除の両方で使う。
|
|
158
287
|
def prune_prefix_all_locales(raw, locales, keys)
|
|
159
|
-
locales.reduce(raw)
|
|
288
|
+
locales.reduce(raw) do |acc, locale|
|
|
289
|
+
# 元ファイルに無い locale ルートを新設すると、ja だけのファイルに `en: {}` が追記されてしまう。
|
|
290
|
+
next acc unless acc.key?(locale)
|
|
291
|
+
|
|
292
|
+
acc.merge(locale => prune_prefix(acc[locale], keys))
|
|
293
|
+
end
|
|
160
294
|
end
|
|
161
295
|
|
|
162
296
|
# ---- (1) 配置 ----
|
|
@@ -186,9 +320,7 @@ LOCALES.each do |locale|
|
|
|
186
320
|
end
|
|
187
321
|
|
|
188
322
|
# 全 locale でスキップ=当該 prefix がどこにも存在しない(prefix の typo)。
|
|
189
|
-
if merged_by_locale.empty?
|
|
190
|
-
die("prefix '#{PREFIX}' のキーが export にもオリジナルにも見つからない。prefix を確認すること。")
|
|
191
|
-
end
|
|
323
|
+
die("prefix '#{PREFIX}' のキーが export にもオリジナルにも見つからない。prefix を確認すること。") if merged_by_locale.empty?
|
|
192
324
|
|
|
193
325
|
# ---- (2) 静的ガード(書き出し前にできる検証を先に。中途半端な --out を残さない)----
|
|
194
326
|
# --out が次回起動時に Rails の i18n glob でロードされる場所か(採番ミス・配置ミスの検出)。
|
|
@@ -206,7 +338,10 @@ merged_by_locale.each do |locale, merged|
|
|
|
206
338
|
non_matching = leaf_entries(merged).keys.grep_v(regexp)
|
|
207
339
|
next if non_matching.empty?
|
|
208
340
|
|
|
209
|
-
die(
|
|
341
|
+
die(
|
|
342
|
+
"locale #{locale}: regexp #{regexp.inspect} にマッチしない leaf キーがある(regexp/prefix 不一致):\n " \
|
|
343
|
+
"#{non_matching.join("\n ")}"
|
|
344
|
+
)
|
|
210
345
|
end
|
|
211
346
|
|
|
212
347
|
# 書き出し(ここまでの検証を通過してから)。merged_by_locale は { "ja" => {...}, "en" => {...} } 形なので
|
|
@@ -217,7 +352,9 @@ puts "配置: #{out_path} (locale #{merged_by_locale.keys.join(',')} / leaf
|
|
|
217
352
|
|
|
218
353
|
# YAML ラウンドトリップ(to_yaml → 再読込で各 locale の merged と一致するか)。
|
|
219
354
|
merged_by_locale.each do |locale, merged|
|
|
220
|
-
|
|
355
|
+
next if load_locale_tree(out_path, locale) == merged
|
|
356
|
+
|
|
357
|
+
die("YAML ラウンドトリップ不一致。書き出し結果が壊れている: #{out_path} (locale #{locale})")
|
|
221
358
|
end
|
|
222
359
|
|
|
223
360
|
# ---- (3) 移行漏れ検証(削除をメモリ上でシミュレート)----
|
|
@@ -270,10 +407,18 @@ unless leaks.empty?
|
|
|
270
407
|
die('中断。--out の内容・採番・regexp を確認すること。')
|
|
271
408
|
end
|
|
272
409
|
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
410
|
+
changed =
|
|
411
|
+
original_files.count do |f|
|
|
412
|
+
raw = YAML.safe_load_file(f, permitted_classes: [Symbol], aliases: true) || {}
|
|
413
|
+
pruned = prune_prefix_all_locales(raw, LOCALES, prefix_keys)
|
|
414
|
+
# 対象 prefix を含まないファイルを to_yaml で書き戻すと、クォート・アンカー名・折り返し・コメントの
|
|
415
|
+
# 無関係な整形差分が出る。実質的な変更があるファイルだけ書き戻す。
|
|
416
|
+
next false if pruned == raw
|
|
417
|
+
|
|
418
|
+
# 折り返しだけは line_width で無効化できる(アンカー名・クォートは Psych の仕様で制御できない)。
|
|
419
|
+
File.write(f, pruned.to_yaml(line_width: -1))
|
|
420
|
+
true
|
|
421
|
+
end
|
|
277
422
|
|
|
278
|
-
puts "削除: prefix '#{PREFIX}' をオリジナル #{original_files.size} ファイルから刈り取った。"
|
|
423
|
+
puts "削除: prefix '#{PREFIX}' をオリジナル #{original_files.size} ファイル中 #{changed} ファイルから刈り取った。"
|
|
279
424
|
puts '完了。手順7(local_first_key_regexp 追加)が未済なら次に実施すること。'
|
|
@@ -86,7 +86,7 @@ RELATIVE_KEY_RE = /\btt\b\s*(?:\(\s*)?['":]\s*\./
|
|
|
86
86
|
# Rewriter(大文字 ⟦CT: 固定)の除去網をすり抜け画面に残る。I18n.t 化では直らない。
|
|
87
87
|
# 正しい修正は method 名を第一引数・表示テキストを第二引数へ分離すること。
|
|
88
88
|
LABEL_FIRST_ARG_RE = /
|
|
89
|
-
(?: (?<![\w.])\w+\.label\b | \blabel_tag\b | (?<![\w.])label(
|
|
89
|
+
(?: (?<![\w.])\w+\.label\b | \blabel_tag\b | (?<![\w.])label(?!\w) )
|
|
90
90
|
\s*\(?\s*
|
|
91
91
|
tt\b (?=\s*[("':@])
|
|
92
92
|
/x
|
|
@@ -110,14 +110,14 @@ def scan_file(path, in_app:)
|
|
|
110
110
|
next unless line =~ TT_CALL
|
|
111
111
|
|
|
112
112
|
entry = { file: rel, lineno: idx + 1, code: line.rstrip }
|
|
113
|
-
yield(classify_line(line, entry, in_app:
|
|
113
|
+
yield(classify_line(line, entry, in_app:))
|
|
114
114
|
end
|
|
115
115
|
rescue StandardError
|
|
116
116
|
nil
|
|
117
117
|
end
|
|
118
118
|
|
|
119
119
|
# ヒット 1 行を分類し、[:safe | :suspicious | :other, entry] を返す。
|
|
120
|
-
def classify_line(line, entry, in_app:)
|
|
120
|
+
def classify_line(line, entry, in_app:) # rubocop:disable Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
|
|
121
121
|
# tt の定義(def/alias)は呼び出しではない。t( へ置換すると Rails の t を壊すので絶対に自動変換しない。
|
|
122
122
|
if line.match?(TT_DEFINITION)
|
|
123
123
|
entry[:reason] = 'tt の定義(def/alias)。自動変換禁止。呼び出しを全て t へ移した後に手動で削除する'
|
|
@@ -143,15 +143,16 @@ def classify_line(line, entry, in_app:)
|
|
|
143
143
|
|
|
144
144
|
entry[:kind] = :string_manipulation
|
|
145
145
|
entry[:relative_key] = line.match?(RELATIVE_KEY_RE)
|
|
146
|
-
entry[:hint] =
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
146
|
+
entry[:hint] =
|
|
147
|
+
if entry[:relative_key]
|
|
148
|
+
'I18n.t へ。相対キー(.foo)は絶対キーへ書き換えが必要'
|
|
149
|
+
else
|
|
150
|
+
'I18n.t(絶対キー) へ置換を検討'
|
|
151
|
+
end
|
|
151
152
|
[:suspicious, entry]
|
|
152
153
|
end
|
|
153
154
|
|
|
154
|
-
buckets = { safe
|
|
155
|
+
buckets = { safe:, suspicious:, other: }
|
|
155
156
|
Dir.glob(ALL_GLOB).each do |path|
|
|
156
157
|
# vendor / node_modules / tmp 等のノイズを除外
|
|
157
158
|
next if relative(path).start_with?('vendor/', 'node_modules/', 'tmp/', '.git/')
|
|
@@ -163,7 +164,7 @@ end
|
|
|
163
164
|
|
|
164
165
|
if options[:json]
|
|
165
166
|
require 'json'
|
|
166
|
-
puts JSON.pretty_generate(safe
|
|
167
|
+
puts JSON.pretty_generate(safe:, suspicious:, other:)
|
|
167
168
|
exit(0)
|
|
168
169
|
end
|
|
169
170
|
|
|
@@ -172,13 +173,14 @@ def print_section(title, entries)
|
|
|
172
173
|
entries.each do |e|
|
|
173
174
|
# hint があれば必ず kind も付く(classify_line で対で設定)ので素直に出す。
|
|
174
175
|
# suspicious 内のサブ種別(label_arg / string_manipulation)を可視化する。
|
|
175
|
-
suffix =
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
176
|
+
suffix =
|
|
177
|
+
if e[:hint]
|
|
178
|
+
" # [#{e[:kind]}] #{e[:hint]}"
|
|
179
|
+
elsif e[:reason]
|
|
180
|
+
" # #{e[:reason]}"
|
|
181
|
+
else
|
|
182
|
+
''
|
|
183
|
+
end
|
|
182
184
|
puts "#{e[:file]}:#{e[:lineno]}: #{e[:code].strip}#{suffix}"
|
|
183
185
|
end
|
|
184
186
|
end
|