copy_tuner_client 1.1.5 → 1.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/CLAUDE.md +32 -0
- data/README.md +42 -0
- data/lib/copy_tuner_client/cache.rb +4 -0
- data/lib/copy_tuner_client/configuration.rb +34 -2
- data/lib/copy_tuner_client/copyray.rb +4 -0
- data/lib/copy_tuner_client/i18n_backend.rb +12 -0
- data/lib/copy_tuner_client/translation_log.rb +5 -0
- data/lib/copy_tuner_client/version.rb +1 -1
- data/skills/copy-tuner/SKILL.md +39 -15
- data/skills/copy-tuner-to-locales-cleanup/SKILL.md +165 -0
- data/skills/copy-tuner-to-locales-cleanup/references/example-touchpoints.md +111 -0
- data/skills/copy-tuner-to-locales-cleanup/references/verification-final.md +73 -0
- data/skills/copy-tuner-to-locales-migrate-prefix/SKILL.md +265 -0
- data/skills/copy-tuner-to-locales-migrate-prefix/references/example-touchpoints.md +126 -0
- data/skills/copy-tuner-to-locales-migrate-prefix/references/export-and-split.md +126 -0
- data/skills/copy-tuner-to-locales-migrate-prefix/references/local-first-regexp.md +72 -0
- data/skills/copy-tuner-to-locales-migrate-prefix/references/verification-per-prefix.md +63 -0
- data/skills/copy-tuner-to-locales-migrate-prefix/scripts/migrate_prefix.rb +279 -0
- data/spec/copy_tuner_client/cache_spec.rb +21 -0
- data/spec/copy_tuner_client/configuration_spec.rb +53 -0
- data/spec/copy_tuner_client/copyray_spec.rb +11 -0
- data/spec/copy_tuner_client/helper_extension_spec.rb +6 -0
- data/spec/copy_tuner_client/i18n_backend_spec.rb +81 -0
- data/spec/copy_tuner_client/translation_log_spec.rb +51 -0
- metadata +12 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 70c29e81f0f1972e5b8833b0ab7a9f9b079186f055e7d988b207043930144708
|
|
4
|
+
data.tar.gz: 3277c0fc92c99421c183f19356ea49f8830df54c8bfdf2913c1433a4e30e6268
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: f7ca85e2ebd4d5f8634edc0a2b6c4ac1450d35c49e4bd81740c0bf31166ee8937c1cf29a4af4bda9ba5c1bf30d1985282635c9092a8ee2c6b931a0fcdb78c9f2
|
|
7
|
+
data.tar.gz: 680d69dcfee71fb565c08a948a41fae83946fd407045e50e4169266fdb83ef366dbb0c4a16326206ef1e7dae646e3d7a375bf98c88e3d5d8217bb1d755acbff4
|
data/CLAUDE.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
CopyTuner の Ruby クライアント gem。Rails アプリの I18n を CopyTuner サーバと同期する。
|
|
4
|
+
|
|
5
|
+
## コマンド
|
|
6
|
+
- テスト: `bundle exec rspec`(単一ファイル: `bundle exec rspec spec/copy_tuner_client/cache_spec.rb`)
|
|
7
|
+
- 特定の Rails バージョンでテスト: `BUNDLE_GEMFILE=gemfiles/8.0.gemfile bundle exec rspec`
|
|
8
|
+
- Lint: `bundle exec rubocop`(`sgcop` を継承)
|
|
9
|
+
- フロントエンドビルド: `yarn build`(開発: `yarn dev`)
|
|
10
|
+
- gem リリース: `bundle exec rake build|install|release`
|
|
11
|
+
|
|
12
|
+
## アーキテクチャ
|
|
13
|
+
`Configuration#apply`(lib/copy_tuner_client/configuration.rb)が全コンポーネントを組み立てる起点:
|
|
14
|
+
- `Client` — CopyTuner サーバ / S3 との HTTP 通信
|
|
15
|
+
- `Cache` — Mutex で保護された blurb ストア。Hash のように振る舞う。アップロードキューを管理
|
|
16
|
+
- `I18nBackend` — デフォルトの I18n backend を置き換える(`I18n.backend = ...`)。lookup で Cache を参照
|
|
17
|
+
- `Poller` / `ProcessGuard` — バックグラウンド同期スレッド
|
|
18
|
+
- Rack middleware `RequestSync` / `CopyrayMiddleware` — 開発環境でのリクエスト毎同期とオーバーレイ
|
|
19
|
+
Rails 統合は engine.rb のイニシャライザ経由(ヘルパー/SimpleForm フック、アセット precompile)。
|
|
20
|
+
|
|
21
|
+
## Gotchas
|
|
22
|
+
- **フロントエンドは `src/*.ts` を編集する。`app/assets/*` は Vite のビルド成果物なので直接編集しない**
|
|
23
|
+
(vite.config.ts が `src/main.ts` → `app/assets/javascripts/copytuner.js` を出力)。
|
|
24
|
+
- キー除外の 2 オプションは混同しやすい:
|
|
25
|
+
- `exclude_key_regexp` — locale 付きキー対象・アップロード時に作用
|
|
26
|
+
- `local_first_key_regexp` — locale を除いたキー対象・lookup 時に作用(ローカル YAML 優先)
|
|
27
|
+
local_first キーのアップロード抑止は `Cache#[]=` に集約されている。
|
|
28
|
+
- **アップロード抑止の新ルールは `Cache#[]=` に足す。`I18nBackend` の書き込み経路(`lookup` / `default` / `store_item`)ごとに個別ガードを足さない**
|
|
29
|
+
(理由: cache への書き込みは全経路が最終的に `Cache#[]=` を通る単一の関門。経路ごとにガードを足すと付け忘れの穴が生まれ、同じチェックが分散して保守負担になる。実際 local_first の抑止は当初 `default` 個別に足したが穴が残り、`Cache#[]=` への集約に作り直した)。
|
|
30
|
+
|
|
31
|
+
## Claude Code スキル
|
|
32
|
+
`skills/copy-tuner/` に i18n キー操作支援スキルがある(SKILL.md 参照)。
|
data/README.md
CHANGED
|
@@ -35,6 +35,48 @@ bundle exec rake copy_tuner:export
|
|
|
35
35
|
|
|
36
36
|
これで、`config/locales/copy_tuner.yml` に翻訳ファイルが作成されます。
|
|
37
37
|
|
|
38
|
+
## 特定のキーをローカル YAML 優先にする(段階移行)
|
|
39
|
+
|
|
40
|
+
`config.local_first_key_regexp` を設定すると、**locale を除いたキー**(例 `views.foo.bar`)がその正規表現にマッチした場合、CopyTuner サーバのキャッシュをスキップして、ローカルの `config/locales/*.yml`(`I18n::Backend::Simple`)を優先的に参照します。
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
CopyTunerClient.configure do |config|
|
|
44
|
+
# ...
|
|
45
|
+
# views.* で始まるキーはローカル YAML を優先する
|
|
46
|
+
config.local_first_key_regexp = /\Aviews\./
|
|
47
|
+
end
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
CopyTuner で一元管理している翻訳を、`views.*` のような単位で段階的にローカル YAML へ移行するためのオプションです。
|
|
51
|
+
|
|
52
|
+
- マッチしたキーは CopyTuner キャッシュを一切参照せず、ローカル YAML のみを引きます(完全分離)。
|
|
53
|
+
- ローカル YAML にも存在しない場合は未訳(`nil` / MissingTranslation)となります。CopyTuner へのフォールバックや新規キーのアップロードは行いません。これにより移行漏れを未訳として検知できます。
|
|
54
|
+
- マッチしたキーには、ビューヘルパー(`t` / `translate`)および SimpleForm のラベルで CopyRay オーバーレイマーカー(`<!--COPYRAY key-->`)を注入しません。これらのキーは CopyTuner 上で編集できないため、編集可能だと誤認させないためです。
|
|
55
|
+
|
|
56
|
+
`exclude_key_regexp` との違い:
|
|
57
|
+
|
|
58
|
+
| オプション | 対象 | 作用するタイミング |
|
|
59
|
+
| --- | --- | --- |
|
|
60
|
+
| `exclude_key_regexp` | locale 付きキー(例 `ja.views.foo`) | アップロード時(CopyTuner への送信を抑止) |
|
|
61
|
+
| `local_first_key_regexp` | locale を除いたキー(例 `views.foo`) | 読み込み時(lookup の優先順位) |
|
|
62
|
+
|
|
63
|
+
### `exclude_key_regexp` は非推奨です
|
|
64
|
+
|
|
65
|
+
`exclude_key_regexp` は **非推奨**です(将来のリリースで削除予定)。設定すると deprecation 警告が出ます。代わりに `local_first_key_regexp` を使ってください。
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
# Before(非推奨)
|
|
69
|
+
config.exclude_key_regexp = /\Aja\.views\./
|
|
70
|
+
|
|
71
|
+
# After: locale プレフィックス(ja.)を外して指定する
|
|
72
|
+
config.local_first_key_regexp = /\Aviews\./
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
移行時の注意:
|
|
76
|
+
|
|
77
|
+
- 対象キーの形式が異なります。`exclude_key_regexp` は **locale 付き**(`ja.views.foo`)、`local_first_key_regexp` は **locale を除いた**形式(`views.foo`)でマッチします。正規表現から locale プレフィックスを外してください。
|
|
78
|
+
- 挙動も少し変わります。`exclude_key_regexp` はアップロードを抑止するだけで lookup 時は CopyTuner キャッシュを参照し続けますが、`local_first_key_regexp` は lookup 時に CopyTuner キャッシュをスキップしてローカル YAML を優先します(完全分離)。ローカル管理へ移行する用途では `local_first_key_regexp` のほうが適切です。
|
|
79
|
+
|
|
38
80
|
## Claude Code スキル
|
|
39
81
|
|
|
40
82
|
`skills/copy-tuner/` に Claude Code 向けのスキルが含まれています。
|
|
@@ -21,6 +21,7 @@ module CopyTunerClient
|
|
|
21
21
|
@logger = options[:logger]
|
|
22
22
|
@mutex = Mutex.new
|
|
23
23
|
@exclude_key_regexp = options[:exclude_key_regexp]
|
|
24
|
+
@local_first_key_regexp = options[:local_first_key_regexp]
|
|
24
25
|
@upload_disabled = options[:upload_disabled]
|
|
25
26
|
@ignored_keys = options.fetch(:ignored_keys, [])
|
|
26
27
|
@ignored_key_handler = options.fetch(:ignored_key_handler, -> (e) { raise e })
|
|
@@ -51,6 +52,9 @@ module CopyTunerClient
|
|
|
51
52
|
|
|
52
53
|
# NOTE: config/locales以下のファイルに除外キーが残っていた場合の対応
|
|
53
54
|
key_without_locale = key.split('.')[1..].join('.')
|
|
55
|
+
# NOTE: local_first_key_regexp にマッチするキーは copy_tuner と完全分離するためアップロードしない
|
|
56
|
+
return if @local_first_key_regexp && key_without_locale.match?(@local_first_key_regexp)
|
|
57
|
+
|
|
54
58
|
if @ignored_keys.include?(key_without_locale)
|
|
55
59
|
@ignored_key_handler.call(IgnoredKey.new("Ignored key: #{key_without_locale}"))
|
|
56
60
|
end
|
|
@@ -18,7 +18,7 @@ module CopyTunerClient
|
|
|
18
18
|
proxy_port proxy_user secure polling_delay sync_interval
|
|
19
19
|
sync_interval_staging sync_ignore_path_regex logger
|
|
20
20
|
framework middleware disable_middleware disable_test_translation
|
|
21
|
-
ca_file exclude_key_regexp s3_host locales ignored_keys ignored_key_handler
|
|
21
|
+
ca_file exclude_key_regexp local_first_key_regexp s3_host locales ignored_keys ignored_key_handler
|
|
22
22
|
download_cache_dir].freeze
|
|
23
23
|
|
|
24
24
|
# @return [String] The API key for your project, found on the project edit form.
|
|
@@ -114,7 +114,14 @@ module CopyTunerClient
|
|
|
114
114
|
attr_accessor :poller
|
|
115
115
|
|
|
116
116
|
# @return [Regexp] Regular expression to exclude keys.
|
|
117
|
-
|
|
117
|
+
# @deprecated Use {#local_first_key_regexp} instead.
|
|
118
|
+
attr_reader :exclude_key_regexp
|
|
119
|
+
|
|
120
|
+
# @return [Regexp] Keys (without locale) matching this regexp bypass the
|
|
121
|
+
# copy_tuner cache and are looked up from local config/locales
|
|
122
|
+
# (I18n::Backend::Simple) first. Used for gradual migration from
|
|
123
|
+
# copy_tuner to local YAML.
|
|
124
|
+
attr_accessor :local_first_key_regexp
|
|
118
125
|
|
|
119
126
|
# @return [String] The S3 host to connect to (defaults to +copy-tuner-us.s3.amazonaws.com+).
|
|
120
127
|
attr_accessor :s3_host
|
|
@@ -163,6 +170,7 @@ module CopyTunerClient
|
|
|
163
170
|
self.html_escape = true
|
|
164
171
|
self.ignored_keys = []
|
|
165
172
|
self.ignored_key_handler = ->(e) { raise e }
|
|
173
|
+
self.local_first_key_regexp = nil
|
|
166
174
|
self.project_id = nil
|
|
167
175
|
self.download_cache_dir = Pathname.new(Dir.pwd).join('tmp', 'cache', 'copy_tuner_client')
|
|
168
176
|
|
|
@@ -314,6 +322,18 @@ module CopyTunerClient
|
|
|
314
322
|
@api_key = api_key
|
|
315
323
|
end
|
|
316
324
|
|
|
325
|
+
# @deprecated Use {#local_first_key_regexp} instead.
|
|
326
|
+
def exclude_key_regexp=(value)
|
|
327
|
+
unless value.nil?
|
|
328
|
+
ActiveSupport::Deprecation.new.warn(
|
|
329
|
+
'exclude_key_regexp is deprecated and will be removed in a future release. ' \
|
|
330
|
+
'Use local_first_key_regexp instead (note: it matches keys WITHOUT the locale prefix, ' \
|
|
331
|
+
'e.g. /\Aviews\./ instead of /\Aja\.views\./).'
|
|
332
|
+
)
|
|
333
|
+
end
|
|
334
|
+
@exclude_key_regexp = value
|
|
335
|
+
end
|
|
336
|
+
|
|
317
337
|
# Sync interval for Rack Middleware
|
|
318
338
|
def sync_interval
|
|
319
339
|
if environment_name == 'staging'
|
|
@@ -336,6 +356,18 @@ module CopyTunerClient
|
|
|
336
356
|
URI::Generic.build(scheme: self.protocol, host: self.host, port: self.port.to_i, path:).to_s
|
|
337
357
|
end
|
|
338
358
|
|
|
359
|
+
# locale を除いたキーが local_first_key_regexp にマッチするかを返す。
|
|
360
|
+
# マッチするキーはローカル config/locales(CopyTuner 管理外)で管理されるため、
|
|
361
|
+
# オーバーレイマーカー注入やキャッシュ参照をスキップする必要がある。
|
|
362
|
+
#
|
|
363
|
+
# @param key_without_locale [String, Symbol, nil] locale prefix を除いたキー(例: "views.foo.bar")
|
|
364
|
+
# @return [Boolean]
|
|
365
|
+
def local_first_key?(key_without_locale)
|
|
366
|
+
return false if local_first_key_regexp.nil? || key_without_locale.nil?
|
|
367
|
+
|
|
368
|
+
key_without_locale.to_s.match?(local_first_key_regexp)
|
|
369
|
+
end
|
|
370
|
+
|
|
339
371
|
private
|
|
340
372
|
|
|
341
373
|
def default_port
|
|
@@ -7,6 +7,10 @@ module CopyTunerClient
|
|
|
7
7
|
def self.augment_template(source, key)
|
|
8
8
|
return source if source.blank? || !source.is_a?(String)
|
|
9
9
|
|
|
10
|
+
# NOTE: local_first(CopyTuner 管理外でローカル config/locales 優先)のキーには
|
|
11
|
+
# オーバーレイマーカーを出さない。編集できないキーを編集可能だと誤認させないため。
|
|
12
|
+
return source if CopyTunerClient.configuration.local_first_key?(key)
|
|
13
|
+
|
|
10
14
|
escape = CopyTunerClient.configuration.html_escape && !source.html_safe?
|
|
11
15
|
augmented = "<!--COPYRAY #{key}-->#{escape ? ERB::Util.html_escape(source) : source}"
|
|
12
16
|
augmented.html_safe
|
|
@@ -73,6 +73,13 @@ module CopyTunerClient
|
|
|
73
73
|
CopyTunerClient::configuration.ignored_key_handler.call(IgnoredKey.new("Ignored key: #{key_without_locale}"))
|
|
74
74
|
end
|
|
75
75
|
|
|
76
|
+
# NOTE: local_first_key_regexp にマッチするキーは copy_tuner キャッシュをスキップし、
|
|
77
|
+
# ローカル config/locales(I18n::Backend::Simple)を優先する。段階的にローカルへ移行するための仕組み。
|
|
78
|
+
# ローカルに無い場合は nil(未訳)のまま返し、copy_tuner へのフォールバックも空キー登録も行わない(完全分離)。
|
|
79
|
+
if local_first_key?(key_without_locale)
|
|
80
|
+
return super
|
|
81
|
+
end
|
|
82
|
+
|
|
76
83
|
# NOTE: ハッシュ化した場合に削除されるキーに対応するため、最初に完全一致をチェック(旧クライアントの動作を維持)
|
|
77
84
|
# 例: `en.test.key` が `en.test.key.conflict` のように別のキーで上書きされている場合の対応
|
|
78
85
|
exact_match = cache[key_with_locale]
|
|
@@ -134,6 +141,10 @@ module CopyTunerClient
|
|
|
134
141
|
cache.wait_for_download
|
|
135
142
|
end
|
|
136
143
|
|
|
144
|
+
def local_first_key?(key_without_locale)
|
|
145
|
+
CopyTunerClient.configuration.local_first_key?(key_without_locale)
|
|
146
|
+
end
|
|
147
|
+
|
|
137
148
|
def default(locale, object, subject, options = {})
|
|
138
149
|
content = super(locale, object, subject, options)
|
|
139
150
|
return content if !object.is_a?(String) && !object.is_a?(Symbol)
|
|
@@ -141,6 +152,7 @@ module CopyTunerClient
|
|
|
141
152
|
if content.respond_to?(:to_str)
|
|
142
153
|
parts = I18n.normalize_keys(locale, object, options[:scope], options[:separator])
|
|
143
154
|
# NOTE: ActionView::Helpers::TranslationHelper#translate wraps default String in an Array
|
|
155
|
+
# NOTE: local_first キーのアップロード抑止は Cache#[]= 側に集約している
|
|
144
156
|
if subject.is_a?(String) || (subject.is_a?(Array) && subject.size == 1 && subject.first.is_a?(String))
|
|
145
157
|
key = parts.join('.')
|
|
146
158
|
cache[key] = content.to_str
|
|
@@ -13,6 +13,11 @@ module CopyTunerClient
|
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
def self.add(key, result)
|
|
16
|
+
# local_first(CopyTuner 管理外でローカル config/locales 優先)のキーは記録しない。
|
|
17
|
+
# ここに集約することで、Copyray オーバーレイの JSON(window.CopyTuner.data)にも
|
|
18
|
+
# local_first キーが混入しない。key は I18n.normalize_keys(nil, ...) 由来の locale なし形式。
|
|
19
|
+
return if CopyTunerClient.configuration.local_first_key?(key)
|
|
20
|
+
|
|
16
21
|
translations[key] = result if initialized? && !translations.key?(key)
|
|
17
22
|
end
|
|
18
23
|
|
data/skills/copy-tuner/SKILL.md
CHANGED
|
@@ -1,34 +1,58 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: copy-tuner
|
|
3
|
-
description:
|
|
3
|
+
description: "このプロジェクトの i18n キー(翻訳)の参照・追加・更新・削除を行うスキル。i18n / 翻訳 / ロケール / `t` / `tt` / 翻訳キーに関わる操作のとき、および `config/locales` ディレクトリや `.yml` 翻訳ファイルを読む・参照する・編集するときは必ず使用すること。翻訳は原則 copy_tuner サーバで管理されており(一部キーのみ config/locales 管理)、素朴に config/locales を読むだけでは大半の翻訳が見つからず誤った判断をするため、必ずこのスキルの手順に従う。"
|
|
4
4
|
license: MIT
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# copy_tuner スキル
|
|
8
8
|
|
|
9
|
-
このプロジェクトの i18n
|
|
10
|
-
|
|
9
|
+
このプロジェクトの i18n は原則 **copy_tuner** サーバで管理し、MCP ツールで操作する。
|
|
10
|
+
例外として `local_first_key_regexp` にマッチするキーだけは `config/locales/*.yml` で管理される。
|
|
11
|
+
**どちらの管理かを最初に判別する。**
|
|
11
12
|
|
|
12
|
-
##
|
|
13
|
+
## キーの管理場所の判別
|
|
13
14
|
|
|
14
|
-
|
|
15
|
+
1. `config/initializers/copy_tuner.rb` の `local_first_key_regexp` を確認する(未設定なら全キー copy_tuner)。
|
|
16
|
+
2. 対象キーから **locale を除いた形**(`ja.views.foo` → `views.foo`)がその正規表現にマッチするか見る。
|
|
17
|
+
|
|
18
|
+
| 判別 | 管理場所 | 操作 |
|
|
19
|
+
|---|---|---|
|
|
20
|
+
| マッチする | config/locales(copy_tuner と完全分離) | `config/locales/*.yml` を Read / Edit |
|
|
21
|
+
| マッチしない・未設定 | copy_tuner | MCP ツール(下記) |
|
|
22
|
+
|
|
23
|
+
**config/locales 管理キーの落とし穴:**
|
|
24
|
+
- copy_tuner には存在しないので、MCP ツールで探しても見つからない。`config/locales` を見ること。
|
|
25
|
+
- copy_tuner に登録しても lookup でバイパスされ無効。必ず `.yml` 側に書く。
|
|
26
|
+
- ja が無ければ未訳(`nil`)。copy_tuner にフォールバックしない(移行漏れを顕在化させる設計)。
|
|
27
|
+
- prefix 単位の一括移行は別作業。必要なら `skills/copy-tuner-to-locales-migrate-prefix/` を使う。
|
|
28
|
+
|
|
29
|
+
## copy_tuner MCP ツール
|
|
30
|
+
|
|
31
|
+
| やりたいこと | ツール |
|
|
15
32
|
|---|---|
|
|
16
|
-
|
|
|
33
|
+
| キー名(一部)から翻訳を調べる | `search_key`(引数は英語キーワード) |
|
|
17
34
|
| 画面テキストからキーを逆引き | `search_translations` |
|
|
18
35
|
| 新しいキーを登録する | `create_i18n_key` |
|
|
19
|
-
| 既存キーの翻訳を更新する | `get_edit_url
|
|
36
|
+
| 既存キーの翻訳を更新する | `get_edit_url`(ブラウザ編集) |
|
|
20
37
|
| 使用中ロケールを確認 | `get_locales` |
|
|
21
38
|
|
|
22
|
-
|
|
39
|
+
## 新規キー登録
|
|
23
40
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
1. `search_key` で機能名・画面名を英語キーワードで検索し、命名パターンを把握する(例: `search_key("move_out")` で退去関連のキー名の構造を確認する)
|
|
27
|
-
2. パターンに合わせたキー名を決める
|
|
28
|
-
3. ja は必須。他に必要なロケールは `get_locales` で確認し、日本語以外は文脈に応じて翻訳して登録する
|
|
29
|
-
4. コード内で `t("登録したキー名")` として使用する
|
|
41
|
+
1. `search_key` で関連キーを検索し、既存の命名パターンに合わせる(例: `search_key("move_out")`)。
|
|
42
|
+
2. ja は必須。他は `get_locales` で確認し、存在するロケール分を登録する。
|
|
30
43
|
|
|
31
44
|
## 不要なキーの処理
|
|
32
45
|
|
|
33
|
-
|
|
46
|
+
削除せず `config/initializers/copy_tuner.rb` の `ignored_keys` に追加する。
|
|
34
47
|
一定期間どこからも参照されていないことを確認してから手動削除する。
|
|
48
|
+
|
|
49
|
+
## `t` と `tt` の使い分け
|
|
50
|
+
|
|
51
|
+
**HTML 要素の属性値には `tt` を使う**(それ以外の通常出力は `t`)。`tt` は copy_tuner_client gem の
|
|
52
|
+
`HelperExtension` が提供するエイリアス。`t` は CopyTuner の都合で HTML コメント (`<!-- ... -->`) を
|
|
53
|
+
埋め込むため、属性値に入ると表示崩れや動作不良の原因になる。
|
|
54
|
+
|
|
55
|
+
```erb
|
|
56
|
+
<div title="<%= tt('views.tooltips.help_text') %>">...</div>
|
|
57
|
+
<h1><%= t('views.simulation.show.title') %></h1>
|
|
58
|
+
```
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: copy-tuner-to-locales-cleanup
|
|
3
|
+
description: >-
|
|
4
|
+
copy_tuner(CopyTuner / copy_tuner_client)の全 i18n キーを config/locales へ prefix 単位で移行し終えた後、
|
|
5
|
+
gem・初期化子・CI・deploy・ドキュメント・MCP 設定を一括撤去して copy_tuner 依存を完全に取り除くスキル。
|
|
6
|
+
prefix 単位の移行は copy-tuner-to-locales-migrate-prefix スキルで先に完了させておくこと。
|
|
7
|
+
disable-model-invocation: true
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
# copy_tuner 撤去スキル(クリーンアップ)
|
|
11
|
+
|
|
12
|
+
`copy-tuner-to-locales-migrate-prefix` スキルで**全 prefix を config/locales へ移行し終えた後**に、
|
|
13
|
+
1 回だけ実行する。`local_first_key_regexp` が全キーを覆っている(= もう CopyTuner から引かれるキーが無い)
|
|
14
|
+
ことを確認してから、gem・初期化子・CI・deploy・ドキュメント・MCP 設定をまとめて撤去する。
|
|
15
|
+
|
|
16
|
+
このスキルは**特定のリポジトリに依存しない**。固有値を覚えず、毎回リポジトリを探索して touchpoint を発見する。
|
|
17
|
+
種別ごとの典型的な在処は `references/example-touchpoints.md` を参照。
|
|
18
|
+
|
|
19
|
+
## なぜ完了判定を関門にするのか
|
|
20
|
+
|
|
21
|
+
`local_first_key_regexp` でカバーできていない prefix が 1 つでも残ったまま gem を抜くと、その prefix のキーが
|
|
22
|
+
config/locales に無く、**一斉に未訳化**する。だから「全キーが regexp にマッチしている」ことを gem 撤去前の
|
|
23
|
+
**関門**にする。判定には gem 自身の `local_first_key?` を使い、export した全キーを 1 件ずつ通す。
|
|
24
|
+
|
|
25
|
+
## ワークフロー
|
|
26
|
+
|
|
27
|
+
### 1. 完了判定(関門)
|
|
28
|
+
|
|
29
|
+
CopyTuner の全キーが `local_first_key_regexp` にマッチする(= 全 prefix 移行済み)ことを確認する。
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
# 全件 export(一時ファイルへ。読み取り専用検証だが export は書き込むので捨て場へ)
|
|
33
|
+
bundle exec rake copy_tuner:export[tmp/copy_tuner_check.yml]
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
export YAML から locale を除いた全キーを取り出し、**gem の判定そのもの**で 100% マッチを確認する:
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
bin/rails runner '
|
|
40
|
+
require "yaml"
|
|
41
|
+
data = YAML.load_file("tmp/copy_tuner_check.yml")
|
|
42
|
+
locale = data.keys.first # 例: "ja"
|
|
43
|
+
keys = []
|
|
44
|
+
walk = ->(h, prefix) do
|
|
45
|
+
h.each do |k, v|
|
|
46
|
+
key = prefix.empty? ? k.to_s : "#{prefix}.#{k}"
|
|
47
|
+
v.is_a?(Hash) ? walk.call(v, key) : keys << key
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
walk.call(data[locale], "")
|
|
51
|
+
unmatched = keys.reject { |k| CopyTunerClient.configuration.local_first_key?(k) }
|
|
52
|
+
if unmatched.empty?
|
|
53
|
+
puts "OK: 全 #{keys.size} キーが local_first_key_regexp にマッチ。cleanup へ進める。"
|
|
54
|
+
else
|
|
55
|
+
puts "STOP: 未マッチ #{unmatched.size} 件(未移行 prefix が残存)。migrate-prefix へ戻る。"
|
|
56
|
+
puts unmatched.first(30)
|
|
57
|
+
end
|
|
58
|
+
'
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
`STOP` が出たら **cleanup を中止し、`copy-tuner-to-locales-migrate-prefix` で残 prefix を移行してから出直す**。
|
|
62
|
+
`OK` のときだけ次へ進む。
|
|
63
|
+
|
|
64
|
+
> NOTE: ここで `export` を実行できるのは gem がまだ入っているから。完了判定は gem 撤去より**前**に行う。
|
|
65
|
+
|
|
66
|
+
> 補助目印: migrate は prefix を移すたびにオリジナル(`0000_original_*.yml`)から該当サブツリーを削除するので、
|
|
67
|
+
> 全移行完了時点で `0000_original_*.yml` はほぼ空(残るのは非表現値の隔離 `0005_rails_non_blurb.yml` 等のみ)に
|
|
68
|
+
> なっているはず。`local_first_key?` のマッチ判定が主の関門で、ファイルが空かどうかは副次的な目視確認。
|
|
69
|
+
> 食い違うとき(regexp は全マッチなのにオリジナルに blurb 化できるキーが残っている等)は削除漏れを疑う。
|
|
70
|
+
|
|
71
|
+
### 2. 最終不正キーチェック
|
|
72
|
+
|
|
73
|
+
gem を抜く前の最終ゲートとして、全キーに不正がないことを確認する。
|
|
74
|
+
|
|
75
|
+
```bash
|
|
76
|
+
bundle exec rake copy_tuner:detect_conflict_keys
|
|
77
|
+
bundle exec rake copy_tuner:detect_html_incompatible_keys
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
両方が `All success` を出すこと。列挙された場合はそのキーが config/locales で壊れている可能性があるので、
|
|
81
|
+
migrate-prefix 側で配置を直してから戻る。
|
|
82
|
+
|
|
83
|
+
### 3. `tt` ヘルパーをアプリ側へ退避
|
|
84
|
+
|
|
85
|
+
copy_tuner_client は `ActionView` の `translate` をフックする際に **`tt` という独自エイリアス**を生やす
|
|
86
|
+
(copyray コメント注入なしの純粋な翻訳。シグネチャは `tt(key, **options)`)。アプリのビューが `tt(...)` を
|
|
87
|
+
使っていると、gem 撤去で `tt` が消えて `NoMethodError`(テンプレートで未定義ヘルパー)になる。**gem を抜く前に**
|
|
88
|
+
アプリ側へ移しておく。
|
|
89
|
+
|
|
90
|
+
1. 利用箇所を洗い出す(残っていれば移行対象):
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
git grep -nw tt -- app lib | grep -v 'attr\|http\|setting' # 単語境界で tt のみ。誤検出は目視で除外
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
2. アプリのヘルパー(`app/helpers/application_helper.rb` 等。全ビューから見える場所)に、**非推奨警告付きで
|
|
97
|
+
`t` に委譲するだけ**の `tt` を定義する。中身は `t` を呼ぶだけでよい(gem 撤去後は `translate` に copyray
|
|
98
|
+
コメントが付かないので、`tt` と `t` は実質同義になる):
|
|
99
|
+
|
|
100
|
+
```ruby
|
|
101
|
+
# NOTE: copy_tuner_client が生やしていた tt ヘルパーの後方互換。gem 撤去で未定義になるため退避した。
|
|
102
|
+
# copyray コメント注入は廃止済みなので t と同義。新規コードでは t を使い、既存呼び出しは順次 t へ置換する。
|
|
103
|
+
def tt(key, **options)
|
|
104
|
+
ActiveSupport::Deprecation.new.warn('tt は非推奨です。t を使ってください。')
|
|
105
|
+
t(key, **options)
|
|
106
|
+
end
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
> NOTE: `tt` はビューヘルパーなので、コントローラ等から呼んでいないか手順 1 で必ず確認する。ビュー専用なら
|
|
110
|
+
> `ApplicationHelper` でよいが、別 namespace のヘルパーでしか使われていない場合はそのスコープに合わせる。
|
|
111
|
+
|
|
112
|
+
3. (任意・推奨)`tt(` を `t(` へ機械置換して呼び出し自体を消すと、非推奨ヘルパーを残さずに済む。置換する場合は
|
|
113
|
+
引数がそのまま通る(`tt(key, **options)` → `t(key, **options)`)。置換後は手順 1 の grep が 0 件になること。
|
|
114
|
+
置換しない場合は手順 2 のヘルパーを残し、警告ログで段階的に潰す。
|
|
115
|
+
|
|
116
|
+
### 4. 初期化子と gem を撤去
|
|
117
|
+
|
|
118
|
+
1. 初期化子(`config/initializers/copy_tuner.rb` 等)を削除(`local_first_key_regexp` ごと消える)。
|
|
119
|
+
2. `Gemfile` から copy_tuner 系 gem(`copy_tuner_client`, `copy_tuner_client-mcp` 等)を削除。
|
|
120
|
+
3. `bundle install` で `Gemfile.lock` を再生成。
|
|
121
|
+
|
|
122
|
+
これで I18n バックエンドが gem 製(`CopyTunerClient::I18nBackend`)から Rails 標準に戻り、`t()` への
|
|
123
|
+
copyray コメント注入も消える。あわせて `config/environments/*.rb` の `i18n.fallbacks` 等が gem に依存して
|
|
124
|
+
いないか確認する(標準バックエンドでも挙動が変わらないこと)。
|
|
125
|
+
|
|
126
|
+
### 5. CI を撤去
|
|
127
|
+
|
|
128
|
+
リポジトリ探索で見つけた CI の copy_tuner 痕跡を削除する。典型的には:
|
|
129
|
+
|
|
130
|
+
- copy_tuner 専用の deploy ワークフローファイル(main push で翻訳をデプロイする専用ファイル)… 丸ごと削除。
|
|
131
|
+
- AI エージェント用ワークフローの `mcp__copy-tuner__*` allowedTools 許可 … 削除。
|
|
132
|
+
- CI の「翻訳を export するステップ」… migrate-prefix の初回で削除済みのはず。**まだ残っていれば**ここで削除する
|
|
133
|
+
(`git grep copy_tuner .github/` で確認)。
|
|
134
|
+
|
|
135
|
+
### 6. deploy / 起動スクリプトを撤去
|
|
136
|
+
|
|
137
|
+
- deploy フックの `rake copy_tuner:deploy`(production 起動時に実行されるブロック)… 削除。
|
|
138
|
+
- コンテナ起動スクリプトの `rake copy_tuner:export` フォールバック … **必ず削除**。
|
|
139
|
+
gem 撤去後はこのタスク自体が消えており、残すとコンテナ起動が `Don't know how to build task` で落ちる。取りこぼし注意。
|
|
140
|
+
|
|
141
|
+
### 7. ドキュメント / スキル / MCP を撤去・方針最終化
|
|
142
|
+
|
|
143
|
+
方針ドキュメントが「copy_tuner で管理」や「移行中」のまま残ると、将来の作業者や AI が矛盾した行動を取る。
|
|
144
|
+
コードと方針を必ず揃える。
|
|
145
|
+
|
|
146
|
+
- i18n 方針ドキュメント(`CLAUDE.md`・`doc/` 配下等)を **config/locales 管理に
|
|
147
|
+
最終化**する。migrate-prefix が残した「移行中」の中間記述を、「config/locales で管理。copy_tuner は廃止。
|
|
148
|
+
新規キーは config/locales へ追加。複数形化不要」に確定する。**モデル名・カラム名の例外規定も撤廃**する
|
|
149
|
+
(全キーがローカル化されたので例外は不要)。
|
|
150
|
+
- copy_tuner 系スキル(`.claude/skills/` 配下の copy_tuner 操作スキル)を無効化または削除する。
|
|
151
|
+
- `README.md` の copy_tuner リンク・補助ドキュメント(「copy_tuner サーバで i18n 管理」記述を持つコマンド定義等)
|
|
152
|
+
を修正する。
|
|
153
|
+
- copy-tuner MCP server の接続設定(`.mcp.json` 等)を除去する。
|
|
154
|
+
|
|
155
|
+
### 8. 最終検証
|
|
156
|
+
|
|
157
|
+
`references/verification-final.md` の手順で、痕跡が残っていないか・gem が外れているか・テストが外部 API
|
|
158
|
+
接続なしで通るか・未訳が出ないかを確認する。
|
|
159
|
+
|
|
160
|
+
## 完了の目安
|
|
161
|
+
|
|
162
|
+
- `git grep -i 'copy.tuner\|copytuner\|CopyTunerClient'`(vendor/tmp 除外)でアプリ側のヒットが無い。
|
|
163
|
+
- 全翻訳キーが `config/locales` で解決し、`translation missing` が出ない。
|
|
164
|
+
- テスト・Linter が通り、外部 API 接続なしで CI が完結する。
|
|
165
|
+
- 方針ドキュメントが config/locales 管理に最終化され、「移行中」やモデル名・カラム名の例外記述が残っていない。
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# 典型的な touchpoint(種別ごと)
|
|
2
|
+
|
|
3
|
+
copy_tuner(`copy_tuner_client` gem)を使う Rails アプリで、移行・撤去のときに触ることになる touchpoint を
|
|
4
|
+
種別ごとに整理したもの。**探索クエリを投げるとこういう種類の結果が返る**というサンプルとして読む。値やパスは
|
|
5
|
+
プロジェクトごとに違うので、これを暗記せず、SKILL.md 手順 1 の grep を必ず自分で走らせて発見すること。
|
|
6
|
+
|
|
7
|
+
各 touchpoint に、どちらのスキルで触るかの印を付けた:
|
|
8
|
+
|
|
9
|
+
- **[migrate]** = `copy-tuner-to-locales-migrate-prefix`(このスキル)で触る。prefix 移行のたびに編集。
|
|
10
|
+
- **[cleanup]** = `copy-tuner-to-locales-cleanup`(全 prefix 完了後)で触る。gem・CI・deploy・docs・MCP 撤去。
|
|
11
|
+
|
|
12
|
+
## 手順1の grep が見つける touchpoint(種別ごと)
|
|
13
|
+
|
|
14
|
+
### gem 依存 — [cleanup]
|
|
15
|
+
|
|
16
|
+
```ruby
|
|
17
|
+
# Gemfile
|
|
18
|
+
gem 'copy_tuner_client' # 全環境
|
|
19
|
+
gem 'copy_tuner_client-mcp', github: '<org>/copy_tuner_client-mcp',
|
|
20
|
+
require: false # development, test
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
> 段階移行の前提として、`copy_tuner_client` は **PR #110(local_first_key_regexp)入りバージョン**へ
|
|
24
|
+
> 上げておく必要がある(migrate スキル手順 1 で確認)。
|
|
25
|
+
|
|
26
|
+
### 初期化子 — [migrate](local_first_key_regexp を積み上げる中心)
|
|
27
|
+
|
|
28
|
+
`config/initializers/copy_tuner.rb`(パスはプロジェクトにより異なる):
|
|
29
|
+
|
|
30
|
+
```ruby
|
|
31
|
+
CopyTunerClient.configure do |config|
|
|
32
|
+
config.api_key = Rails.application.credentials.fetch(:copytuner_api_key) { ENV.fetch('COPYTUNER_API_KEY', 'dummy') }
|
|
33
|
+
config.project_id = <プロジェクトの project_id>
|
|
34
|
+
config.host = '<copy_tuner サーバの host>'
|
|
35
|
+
config.html_escape = true
|
|
36
|
+
config.locales = [:ja]
|
|
37
|
+
config.ignored_keys = %w[]
|
|
38
|
+
# config.local_first_key_regexp = Regexp.union(/\Adevise\./, /\Aviews\./, ...)
|
|
39
|
+
end
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
→ `html_escape` 運用・運用ロケールという前提が分割・非表現値の判断に効く。initializer 全体の削除は [cleanup]。
|
|
43
|
+
|
|
44
|
+
### 既存 locales — [migrate](先頭固定リネーム → 後ろへ配置 → オリジナルから削除)
|
|
45
|
+
|
|
46
|
+
migrate スキルで既存 locales を `0000_original_` へ先頭固定リネームし、移行分を `0010_` 以降に置く
|
|
47
|
+
(ロード順の後勝ちで export 側を正とする)。prefix を移行するたびにオリジナルから該当サブツリーを削除するため、
|
|
48
|
+
全 prefix 完了後の cleanup 時点では **`0000_original_*.yml` はほぼ空**になっている。
|
|
49
|
+
|
|
50
|
+
- `0000_original_*.yml`(既存・先頭固定) … 未移行 prefix の残り。全移行完了時点ではほぼ空。
|
|
51
|
+
- `0010_` 以降の移行分 … prefix 単位で配置した YAML。Rails 標準フォーマットの非表現値(`date.abbr_day_names`
|
|
52
|
+
の配列・`date.order` のシンボル・`number.*.precision` の数値等)も migrate のスクリプトがこの中へ取り込んでいる。
|
|
53
|
+
|
|
54
|
+
→ cleanup では空になったオリジナルの扱い(削除するか空のまま残すか)は任意。非表現値は移行分(`0010_` 以降)に
|
|
55
|
+
取り込み済みで、gem 撤去後も Rails 標準フォーマットとして必要なので**消さない**。
|
|
56
|
+
|
|
57
|
+
### CI — [migrate](Export ステップのみ初回で削除) / [cleanup](残り)
|
|
58
|
+
|
|
59
|
+
- **CI の翻訳 export ステップ**(テストワークフロー内で `bin/rake copy_tuner:export` を走らせる類)…
|
|
60
|
+
「翻訳 DL 失敗でテストがコケないように」の保険。**[migrate] の初回で削除**(test が本番同等の
|
|
61
|
+
`cache.download` 挙動になる)。`disable_test_translation` は入れない。
|
|
62
|
+
- **copy_tuner 専用の deploy ワークフロー**(main push で翻訳をデプロイする専用ファイル)…
|
|
63
|
+
**[cleanup] で丸ごと削除**。
|
|
64
|
+
- **AI エージェント用ワークフローの `mcp__copy-tuner__*` allowedTools 許可** … **[cleanup] で削除**。
|
|
65
|
+
|
|
66
|
+
### deploy / 起動スクリプト — [cleanup]
|
|
67
|
+
|
|
68
|
+
- **deploy フック** … production 起動時に `bundle exec rake copy_tuner:deploy` を実行するブロック。削除。
|
|
69
|
+
- **コンテナ起動スクリプト** … 起動時に `rake copy_tuner:export` を走らせるフォールバック。**gem 撤去後は
|
|
70
|
+
このタスク自体が消えて起動が `Don't know how to build task` で落ちる**ため、必ず削除する。取りこぼし注意。
|
|
71
|
+
|
|
72
|
+
### 環境設定 — [cleanup] で確認
|
|
73
|
+
|
|
74
|
+
`config/environments/production.rb` の `config.i18n.fallbacks = true` 等。標準バックエンドでも有効なので確認のみ。
|
|
75
|
+
|
|
76
|
+
### ドキュメント / スキル / MCP — [migrate](中間状態更新) / [cleanup](最終化・撤去)
|
|
77
|
+
|
|
78
|
+
- **i18n 方針ドキュメント**(`CLAUDE.md`・`doc/` 配下等)… 「copy_tuner サーバで i18n データを管理 /
|
|
79
|
+
config/locales 配下は利用しない / 新規キー登録は基本禁止」等の記述。**[migrate] で中間状態に更新**、
|
|
80
|
+
**[cleanup] で最終化**。上記を参照している他のドキュメント(`CLAUDE.md` 等)も連動。
|
|
81
|
+
- **copy_tuner MCP 操作スキル**(`.claude/skills/` 配下)… **[cleanup] で無効化/削除**。
|
|
82
|
+
- **補助ドキュメント** … 「多言語対応: copy_tuner サーバで i18n データを管理」のような記述を持つコマンド定義等。
|
|
83
|
+
**[cleanup] で修正**。
|
|
84
|
+
- **`README.md`** … CopyTuner プロジェクトへのリンク。**[cleanup] で削除/修正**。
|
|
85
|
+
- **`.mcp.json`** … copy-tuner MCP server 接続設定。**[cleanup] で削除**。
|
|
86
|
+
|
|
87
|
+
## 規模感
|
|
88
|
+
|
|
89
|
+
copy_tuner 側のキー数・export YAML の行数はプロジェクト次第だが、export YAML のトップには Rails i18n 慣習どおりの
|
|
90
|
+
セクションが並ぶ: `activemodel.attributes` / `activerecord.{attributes,enums,models,errors}` / `views` / `text` /
|
|
91
|
+
`helpers` / `date`/`time`/`datetime`/`number` / `devise` / `good_job` / `ice_cube` /
|
|
92
|
+
`restrict_dependent_destroy` 等。これらのトップセクションが prefix 移行の基本粒度。`views` が最大になりやすいので
|
|
93
|
+
最後に回す。
|
|
94
|
+
|
|
95
|
+
## i18n 方針ドキュメント中間状態テンプレ([migrate] 手順 10 で使う)
|
|
96
|
+
|
|
97
|
+
移行中はこのような記述に置き換える。`<列挙>` は現在 `local_first_key_regexp` にマッチしている prefix に
|
|
98
|
+
更新する(prefix を増やすたびに更新)。
|
|
99
|
+
|
|
100
|
+
```markdown
|
|
101
|
+
### 国際化(i18n)
|
|
102
|
+
|
|
103
|
+
- **copy_tuner から config/locales へ段階移行中**
|
|
104
|
+
- 以下の prefix は config/locales 管理へ移行済み(`local_first_key_regexp` にマッチ): `<列挙: devise, ice_cube, views, ...>`
|
|
105
|
+
- 上記以外の prefix はまだ copy_tuner サーバで管理
|
|
106
|
+
- **新規キーの追加先**: 移行済み prefix のキーは config/locales へ。未移行 prefix のキーは従来どおり copy_tuner へ
|
|
107
|
+
- 複数形化対応は不要(日本語環境)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
> [cleanup] で全 prefix 完了後、この中間記述は「config/locales 管理。copy_tuner 廃止。新規キーは
|
|
111
|
+
> config/locales へ。複数形化不要」に最終化する(モデル名・カラム名の例外規定も撤廃)。
|