KilimcininKorOglu/claude-code-mods/tree/main/plugins/i18n-watch
i18n-watch
在呼叫翻譯鍵的編輯後,命名 JSON、Laravel PHP、YAML 或 gettext 語言環境檔案缺少的鍵以及缺少它們的語言。
關於這個 mod
#i18n-watch
此模型將 t('checkout.total') 新增至元件中,且金鑰不存在於區域設定檔中,或僅以英文存在。然後,該頁面會顯示原始金鑰,或土耳其語頁面上的英語,除非使用者註意到,否則沒有人會注意到。當編輯使用一個或多個語言環境檔案缺少的翻譯鍵時,此模式會告訴模型。此註釋隨編輯結果一起提供,因此模型會在同一輪中添加關鍵影格。預設情況下,什麼都不停止;在 deny 模式下,當金鑰遺失時,提交、推送和合併都會停止。
它的作用
1。此模組掛鉤編輯和寫入工具。成功呼叫原始檔後(.ts、.tsx、.js、.jsx、.mjs、.cjs、.vue、.svelte、QXZ .rb、.erb、.haml、.slim),它讀取翻譯呼叫新增的編輯:new_string中old_string沒有的那些,或是每次呼叫Write。
2。讀取的呼叫為 t、$t、i18n.t、__、trans、trans_choice、@lang、_、gettext、QXZ以及引號的第一個參數,也在之後this.、vm.、i18n.、$i18n.、I18n. 和 i18n.global.。變數參數、模板文字和 Rails 惰性鍵 (t('.title')) 將被跳過。
3。它读取会话目录的这些目录下的语言环境文件,最多 4 级别深度和 200 文件,跳过 node_modules 和 2 MB 上的任何文件:locales、lang、i18n、translations、locale、 t('checkout.total')Q、resources/lang、src/locales、src/i18n、public/locales。會話目錄是會話啟動的目錄,在啟動時讀取一次,因為 Bash cd 會移動會話自己的目錄。編輯的檔案是根據會話啟動的 git 儲存庫命名的,因此在 apps/web 中開啟的會話將 apps/api 的檔案命名為 apps/api/x.ts。在 git 儲存庫之外,檔案是根據會話目錄命名的。該根也會在會話開始時讀取一次。
|格式|範例路徑 |按鍵|
|---|---|---|
| JSON(i18next、vue-i18n、Laravel)| locales/tr.json、locales/tr/checkout.json、lang/tr.json |嵌套鍵作為點路徑;item_one 也定義了 item |
| PHP 陣列 (Laravel) | lang/tr/messages.php | messages.key,嵌套陣列作為點路徑 |
| YAML(Rails、Symfony)| config/locales/tr.yml、translations/messages.tr.yaml |虛線路徑; Rails 頂部鍵 (tr:) 被遺漏 |
|取得文字 | locale/tr/LC_MESSAGES/django.po |每個msgid |
語言來自目錄(tr/、en-US/)或來自檔案名稱(tr.json、messages.tr.yaml)。命名空間檔案中的鍵也算 ns.key 和 denyQ。4。當一種語言缺少某個鍵,或當沒有一種語言擁有該鍵時,該鍵就遺失了。每個鍵都以調用它的行命名,因此你可以打開它。模型在編輯結果後讀取此註解:
i18n-watch: this edit uses translation keys the locale files lack: checkout.total:42 (missing in tr, de) · checkout.vat:58 (missing in every locale). Add them to each locale file.
最多命名 10 鍵,其餘的都算在內。僅報告此編輯添加的按鍵;先前報告的一個關鍵留在了調查結果中,沒有再次提及。 5。同時,一行到達了文字記錄,因此你可以看到模型被告知的內容。該行保存檔案及其密鑰,無需指令。該文件之所以命名是因為模型看到了編輯,而你沒有:
i18n-watch: keys srcQXZKQZXDAQ.vue uses that the locale files lack: checkout.total:42 (missing in tr, de) · checkout.vat:58 (missing in every locale)
註解和線條是獨立的通道:模型從不讀取線條,你也從不讀取註解。
6。當 側邊欄 打開時,這些鍵會作為其流中的條目而轉到那裡:首先是文件,然後每個鍵一行,鍵為紅色,其行號微弱,every locale 為紅色,一些區域設定列表為黃色。成績單保持乾淨。該條目會一直保留,直到新的條目將其從窗格中推出為止。在側邊欄關閉或未安裝該模組的情況下,腳本行將如上所示。
7。發現從來都不是被記住的答案。它持有的密鑰是一個聲明,每個措施都會再次從磁碟讀取來源檔案並刪除它不再調用的密鑰。因此,一個發現有兩種方式,在每次編輯和寫入之後、在每個主循環結束時以及在受保護的 git 命令之前進行測量:
- 每個地點都獲得了鑰匙;
- 程式碼停止呼叫它們,因為編輯刪除了字串,將其替換為另一個字串或將其移動到另一個檔案。一個文件消失了,它的發現也就結束了。
該條目已清除,綠色條目表示它是兩個中的哪一個:
i18n-watch: every locale now has the keys srcQXZKQZXDCQ.vue lacked: checkout.total · checkout.vat
i18n-watch: index.php no longer uses: Unauthorized Access
當側邊欄關閉時,相同的文字是轉錄行。該模型沒有讀取任何內容:該發現由其自己的工作完成,因此註釋只會重複它剛剛所做的事情。存在但無法讀取的文件會保留其發現,因為未讀的文件無法證明任何內容。
8。在每個主循環結束時再次測量模型未關閉的發現,剩下的內容作為一個註釋到達模型並帶有下一個提示:
.tsxQ
每回合一個音符,而不是每個提示一個音符。如果沒有這個,結果就會在編輯時說一次,然後站在窗格中,而模型卻忘記了它。你沒有讀到任何新內容:該窗格已經包含相同的發現。9。在 deny 模式下,mod 也會停止 git commit、git push 和 git merge,而檔案仍會使用語言環境檔案缺少的金鑰;具有 --dry-run、--helpQ 或-hBQX 的指令不會停止。 git commit 僅對自己的檔案負責:mod 讀取索引 (git diff --cached --name-only -z),並在沒有開啟的檔案時執行提交,並用一行告訴你還有多少個檔案仍然存在。 push 和 merge 沒有可供讀取的索引,因此每個發現都在那裡。沒有旁路;只有你用 /i18n-watch mode note 關閉門。 note 模式是預設模式,不會停止任何操作,但它仍然會測量 git 命令的結果,因此確定的結果不會保留在窗格中。
語言環境檔案在需要它們的輪次的第一次編輯時被讀取,並且在編輯或寫入語言環境檔案之後再次被讀取。沒有這些目錄的專案什麼都得不到。每個會話將跳過並記錄一次無法讀取或解析的區域設定檔。
在即時檢查中,模型將 t('cart.total') 新增至具有 locales/en.json 和 locales/tr.json 的專案檔案中,閱讀編輯後的註釋,並逐字引用。
指令
QXZKQZXDDQ on or off, the mode, and the files still missing keys
QXZKQZXDEQ on | off on by default
QXZKQZXDFQ mode note note only; the default
QXZKQZXDGQ mode deny a commit, a push and a merge also stop while a key is missing
安裝
claude plugin marketplace add KilimcininKorOgluQXZKQZXDHQ
claude plugin install i18n-watch@kilimcininkoroglu-mods
函數鉤子是搶先體驗的。 Claude Code 2.1.288 以及後來預設載入它們,所以沒有什麼可以打開的。
安裝後
1。重新啟動Claude Code。
它可以達到什麼
在 Claude Code 2.1.283 上使用 claude plugin validate 進行驗證:
❯ .QXZKQZXDIQ.ts hooks: session.start, command.run{command=i18n-watch}, turn.start, tool.call{tool=Bash}, turn.complete, prompt.submit, tool.call{tool=Edit}, tool.call{tool=Write}
❯ .QXZKQZXDJQ.ts calls: $.command.register, $.fs.exists (via isDir, usedNow), $.fs.list (via walkLocales), $.fs.read (via loadCatalog, usedNow), $.fs.stat (via isDir), $.process.run (via shownRootOf, stagedPaths), $.session.cwd, $.sidebar.clear (via dropEntry), $.sidebar.set (via toPerson), $.store.get (via readSettings), $.store.set (via runCommand, setMode), $.ui.log (via catalogOf, gate, toPerson)
到達L2,它執行git來讀取索引。
1. Reads: the text of each Edit and Write call; the Bash command text; each reported source file again; the locale directories under the session directory and their files
2. Runs: git rev-parse --show-toplevel once at the session's start, to name files against the repository root; git rev-parse --show-toplevel and git diff --cached --name-only -z, at a guarded command in deny mode, to read which files the commit holds
3. Sends: a note to the model after an edit that uses missing keys, one more with the next prompt while a finding stands, and one line to the transcript; nothing leaves the machine
4. Persists: in $.store, the onQXZKQZXDKQ setting and the mode; the locale keys live in memory for one turn
5. Hostile input: locale files are only parsed as data (JSON.parse and line regexes), never run; PHP files are not executed
限制
- 僅讀取會話目錄下的語言環境目錄。當會話在儲存庫根目錄啟動時,不會看到區域設定位於
apps/web/src/locales的 monorepo。 - Laravel PHP 檔案是逐行讀取的,而不是執行:在一行上打開和關閉的數組,看不到計算出的鍵和
included 數組。 - YAML 透過縮排讀取。不遵循錨點、別名和流映射 (
{a: b})。 - 不檢查執行階段建置的金鑰(
t(name)、t(`a.${b}`))。 - 語言程式碼是兩個字母,帶有可選區域或腳本(
tr、.tsQ、zh-Hant);無法識別fil等三字母程式碼。 - 不檢查僅編輯移動的按鍵(也在
old_string中),也不檢查透過 Bash 進行的編輯。 deny模式沒有旁路。當無法修復問題時,可以使用/i18n-watch mode note關閉門。- 門讀取命令文字。透過隱藏
git commit的腳本或別名的提交不會停止。 git commit -a、-am和--之後帶有路徑規範的提交不會縮小到索引,因為它們提交索引尚未儲存的檔案。每一個公開的發現都代表著這些。- 在命令執行之前讀取索引。檔案在讀取和執行之間發生變化的提交(同時另一個進程暫存)是根據讀取時索引所保存的內容來衡量的。- 查找結果是透過文件進行的呼叫而不是區域設定文件自己的使用來關閉的。即使語言環境檔案仍然缺少程式碼停止呼叫的鍵,也會從查找結果中刪除該鍵,因為沒有任何東西再呼叫它。
發展
make install # eslint, typescript-eslint, typescript
make lint # complexity limit 10, the build fails above it
make typecheck # needs .claudeQXZKQZXDLQ/ from QXZKQZXDMQ
make validate
make test # claude plugin test
安裝
請先查看作者 README,確認 marketplace 與外掛名稱;指令可能隨儲存庫結構而變動。
claude plugin marketplace add KilimcininKorOglu/claude-code-mods claude plugin install i18n-watch
原文 / README
i18n-watch
The model adds t('checkout.total') to a component, and the key exists in no locale file, or in English only. The page then shows the raw key, or English on the Turkish page, and nobody notices until a user does. This mod tells the model when an edit uses translation keys that one or more locale files lack. The note comes with the Edit's result, so the model adds the keys in the same turn. By default nothing is stopped; in deny mode a commit, a push and a merge stop while a key is missing.
What it does
-
The mod hooks the Edit and Write tools. After a successful call on a source file (
.ts,.tsx,.js,.jsx,.mjs,.cjs,.vue,.svelte,.astro,.php,.py,.rb,.erb,.haml,.slim), it reads the translation calls the edit added: those innew_stringthatold_stringdoes not have, or every call of a Write. -
The calls read are
t,$t,i18n.t,__,trans,trans_choice,@lang,_,gettextandngettextwith a quoted first argument, also afterthis.,vm.,i18n.,$i18n.,I18n.andi18n.global.. A variable argument, a template literal and a Rails lazy key (t('.title')) are skipped. -
It reads the locale files under these directories of the session directory, at most 4 levels deep and 200 files, skipping
node_modulesand any file over 2 MB:locales,lang,i18n,translations,locale,config/locales,resources/lang,src/locales,src/i18n,public/locales. The session directory is the one the session started in, read once at its start, because a Bashcdmoves the session's own directory. The edited file is named against the git repository the session started in, so a session opened inapps/webnames a file ofapps/apiasapps/api/x.ts. Outside a git repository the file is named against the session directory. That root is read once at the session's start too.| Format | Example path | Keys | |---|---|---| | JSON (i18next, vue-i18n, Laravel) |
locales/tr.json,locales/tr/checkout.json,lang/tr.json| nested keys as dotted paths;item_onealso definesitem| | PHP array (Laravel) |lang/tr/messages.php|messages.key, nested arrays as dotted paths | | YAML (Rails, Symfony) |config/locales/tr.yml,translations/messages.tr.yaml| dotted paths; a Rails top key (tr:) is left out | | gettext |locale/tr/LC_MESSAGES/django.po| eachmsgid|The language comes from a directory (
tr/,en-US/) or from the file name (tr.json,messages.tr.yaml). A key in a namespace file also counts asns.keyandns:key. -
A key is missing when a language lacks it, or when no language has it. Each key is named with the line it is called on, so you can open it. The model reads this note after the Edit's result:
i18n-watch: this edit uses translation keys the locale files lack: checkout.total:42 (missing in tr, de) · checkout.vat:58 (missing in every locale). Add them to each locale file.At most 10 keys are named, the rest counted. Only the keys this edit added are reported; a key reported before stays in the finding without being said again.
-
At the same moment one line reaches the transcript, so you see what the model was told. The line holds the file and its keys, without the instruction. The file is named because the model saw the edit and you did not:
i18n-watch: keys src/Cart.vue uses that the locale files lack: checkout.total:42 (missing in tr, de) · checkout.vat:58 (missing in every locale)The note and the line are separate channels: the model never reads the line, and you never read the note.
-
While the sidebar is open, those keys go there instead, as an entry in its stream: the file first, then one line per key, with the key red, its line number faint,
every localered and a list of some locales yellow. The transcript stays clean. The entry stays until newer ones push it off the pane. With the sidebar closed, or without that mod installed, the transcript line is written as above. -
A finding is never a remembered answer. The keys it holds are a claim, and every measure reads the source file from disk again and drops the keys it no longer calls. So a finding closes two ways, measured after each Edit and Write, at the end of each main-loop turn, and before a guarded git command:
- every locale gained the keys;
- the code stopped calling them, because the edit deleted the string, replaced it with another one or moved it to another file. A file that is gone closes its finding too.
The entry is cleared and a green one says which of the two it was:
i18n-watch: every locale now has the keys src/Cart.vue lacked: checkout.total · checkout.vat i18n-watch: index.php no longer uses: Unauthorized AccessWith the sidebar closed the same text is one transcript line. The model reads nothing of this: the finding closed by its own work, so a note would only repeat what it just did. A file that is there and cannot be read keeps its finding, because an unread file proves nothing.
-
A finding the model did not close is measured again at the end of each main-loop turn, and what is left reaches the model as one note with its next prompt:
i18n-watch: 1 file(s) still use translation keys the locale files lack: index.php (Unauthorized Access:42). Add the keys to every locale file, or take the calls out.One note per turn, not one per prompt. Without this the finding would be said once, at the edit, and then stand in the pane while the model forgot it. You read nothing new: the pane already carries the same finding.
-
In
denymode the mod also stopsgit commit,git pushandgit mergewhile a file still uses keys the locale files lack; a command with--dry-run,--helpor-his not stopped. Agit commitanswers for its own files alone: the mod reads the index (git diff --cached --name-only -z) and lets the commit run when it holds none of the open files, with one line to you naming how many still stand. Apushand amergehold no index to read, so every finding stands there. There is no bypass; only you turn the gate off, with/i18n-watch mode note.notemode is the default and stops nothing, but it measures the findings at a git command all the same, so a settled one does not stay in the pane.
The locale files are read at the first edit of a turn that needs them, and again after an Edit or Write of a locale file. A project without these directories gets nothing. A locale file that cannot be read or parsed is skipped and logged once per session.
In the live check the model added t('cart.total') to a file of a project with locales/en.json and locales/tr.json, read the note after the Edit, and quoted it word for word.
Command
/i18n-watch on or off, the mode, and the files still missing keys
/i18n-watch on | off on by default
/i18n-watch mode note note only; the default
/i18n-watch mode deny a commit, a push and a merge also stop while a key is missing
Install
claude plugin marketplace add KilimcininKorOglu/claude-code-mods
claude plugin install i18n-watch@kilimcininkoroglu-mods
Function hooks are early access. Claude Code 2.1.288 and later load them by default, so there is nothing to switch on.
After installing
- Restart Claude Code.
What it can reach
Validated with claude plugin validate on Claude Code 2.1.283:
❯ ./register.ts hooks: session.start, command.run{command=i18n-watch}, turn.start, tool.call{tool=Bash}, turn.complete, prompt.submit, tool.call{tool=Edit}, tool.call{tool=Write}
❯ ./register.ts calls: $.command.register, $.fs.exists (via isDir, usedNow), $.fs.list (via walkLocales), $.fs.read (via loadCatalog, usedNow), $.fs.stat (via isDir), $.process.run (via shownRootOf, stagedPaths), $.session.cwd, $.sidebar.clear (via dropEntry), $.sidebar.set (via toPerson), $.store.get (via readSettings), $.store.set (via runCommand, setMode), $.ui.log (via catalogOf, gate, toPerson)
Reach L2, it runs git to read the index.
1. Reads: the text of each Edit and Write call; the Bash command text; each reported source file again; the locale directories under the session directory and their files
2. Runs: git rev-parse --show-toplevel once at the session's start, to name files against the repository root; git rev-parse --show-toplevel and git diff --cached --name-only -z, at a guarded command in deny mode, to read which files the commit holds
3. Sends: a note to the model after an edit that uses missing keys, one more with the next prompt while a finding stands, and one line to the transcript; nothing leaves the machine
4. Persists: in $.store, the on/off setting and the mode; the locale keys live in memory for one turn
5. Hostile input: locale files are only parsed as data (JSON.parse and line regexes), never run; PHP files are not executed
Limits
- Only the locale directories under the session directory are read. A monorepo whose locales sit in
apps/web/src/localesis not seen when the session starts at the repository root. - A Laravel PHP file is read line by line, not run: an array opened and closed on one line, a computed key and an
included array are not seen. - YAML is read by indent. Anchors, aliases and flow mappings (
{a: b}) are not followed. - A key built at run time (
t(name),t(`a.${b}`)) is not checked. - A language code is two letters with an optional region or script (
tr,pt_BR,zh-Hant); a three-letter code such asfilis not recognised. - A key the edit only moves (it was in
old_stringtoo) is not checked, and neither is an edit through Bash. - The
denymode has no bypass. When a finding cannot be fixed, you turn the gate off with/i18n-watch mode note. - The gate reads the command text. A commit through a script or an alias that hides
git commitis not stopped. - A
git commit -a, a-amand a commit with a pathspec after--are not narrowed to the index, because they commit files the index does not hold yet. Every open finding stands for those. - The index is read before the command runs. A commit whose files change between the read and the run (another process staging meanwhile) is measured against what the index held at the read.
- A finding is closed by the calls the file makes, not by the locale files' own use. A key the code stopped calling is dropped from the finding even when the locale files still lack it, because nothing calls it any more.
Development
make install # eslint, typescript-eslint, typescript
make lint # complexity limit 10, the build fails above it
make typecheck # needs .claude/types/ from /plugin-types
make validate
make test # claude plugin test
