I asked Claude Code to translate the same 92 missing strings nine times. Three times it had only the JSON files. Six times it also had ten tools I wrote for exactly this job.

I expected the first group to break my files. It broke nothing. And in the second group, two sessions out of six never called a single one of my tools.

Why I ran this

I maintain a JetBrains plugin for translation keys. It exposes ten tools to AI agents through the IDE's MCP server: list the missing translations, write them, check the files, rename a key, and so on.

My assumption was simple. An agent that edits locale files by hand will rewrite values it should leave alone and lose a {{name}} on the way. The tools exist to prevent that. I had never measured it, so I did.

The setup

A small React app with i18next, five namespaces, 54 keys in English.

Locale Keys present Keys missing
en (reference) 54 0
fr 44 10
es 26 28
it 0 54

That makes 92 values to write and 70 existing values that must not move. The missing ones include {{name}} placeholders, _one / _other plural pairs, <b> tags and a line break.

The prompt is the same every time, and it never mentions a tool:

This React app uses i18next; its translation files are in public/locales. Translate the keys missing in fr, es and it from the English reference. 

Each run is a fresh headless session on a fresh copy of the project:

claude -p "$PROMPT" \ --model sonnet \ --setting-sources project \ --strict-mcp-config \ --permission-mode acceptEdits \ --settings '{"sandbox":{"enabled":true,"autoAllowBashIfSandboxed":true}}' \ --allowedTools Read Edit Write Glob Grep \ --no-session-persistence \ --output-format stream-json --verbose 

This is Claude Code 2.1.319 with Sonnet 5.5. --setting-sources project keeps my personal settings out of the session. The runs with tools add --mcp-config with the IDE's server, and the ten tool names to the allowed list.

After each run, a script compares every file with the starting point: values written, existing values changed, placeholders and tags against the English text. Time, cost and turns come from the session's own report.

Without the tools: it just worked

Run Written Placeholders or tags lost Existing values changed Time Cost Turns
1 92 / 92 0 1 57 s $0.20 33
2 92 / 92 0 0 57 s $0.19 8
3 92 / 92 0 0 85 s $0.27 30

Every placeholder survived. Every tag survived. The plurals came in pairs. My assumption was wrong.

The agent read the files, then wrote again, in full, the eleven that needed a change. In one run it generated them with a Python script instead.

The one changed value is in run 1. The French file had login.title set to "Login". The agent replaced it, and said so in its summary:

Existing fr value changed: fr/auth.json had login.title set to "Login", which was left in English. I changed it to "Se connecter".

It is probably right about the French. But I asked for the missing keys. In a real project, "Login" may be the word the product team chose, and one modified line in a diff of 205 added lines is easy to miss.

With the tools: less than half the time

In four of the six sessions that had the MCP server, the agent used the tools.

Run Written Placeholders or tags lost Existing values changed Time Cost Turns
1 92 / 92 0 0 25 s $0.14 9
2 92 / 92 0 0 22 s $0.13 9
5 92 / 92 0 0 25 s $0.13 10
6 92 / 92 0 0 35 s $0.15 15

On average that is 27 seconds against 66, and $0.14 against $0.22.

The reason is easy to see in the transcript. The agent never opens a JSON file. It asks for the list of what is missing, once per locale:

{"module":"project","sourceLocale":"en","locale":"fr","total":10,"items":[ {"key":"dashboard:welcome","source":"Welcome back, {{name}}!","pluralFromOther":false}, {"key":"pagination.page","source":"Page {{current}} of {{total}}","pluralFromOther":false} ]} 

(I kept two of the ten items.) Then it sends the translations back, once per locale, and the plugin writes them. That is about 4,000 output tokens instead of about 10,000.

The write tool is also stricter than a file edit. I called it by hand to see what it refuses:

{"written":[],"rejected":{"auth:login.title":"Already filled and not stale: only a stale value is replaced"}} {"written":[],"rejected":{"dashboard:welcome":"Placeholders differ from the source: Welcome back, {{name}}!"}} 

The first line is the "Login" rewrite from above. Through the tool, it cannot happen. The second is a French value I sent without its {{name}}.

Two sessions out of six ignored the tools

In runs 3 and 4 the server was connected and the ten tools were in the session's tool list. The agent wrote the files itself anyway.

Run Tools called Existing values changed Time Cost
3 none 1 70 s $0.23
4 none 1 67 s $0.24

Both made the same "Login" rewrite, once as "Se connecter" and once as "Connexion".

As far as I can tell, this comes from how Claude Code handles MCP tools. It does not load their definitions up front. The four sessions that used my tools each ran a tool search that fetches them by name. The two other sessions never searched.

One line fixed it

I added a CLAUDE.md with a single line to the project:

For translation files (public/locales), use the i18n_* MCP tools instead of editing the JSON files. 

Three more runs, same prompt. All three used the tools: 26 to 30 seconds, $0.13 each, nothing existing touched. Three runs prove little, but it went from four out of six to three out of three.

What 119 tools cost

The JetBrains MCP server in my IDE exposes 119 tools: database, debugger, terminal, git and the rest. Their definitions weigh about 236,000 characters. My ten tools are 9,146 of them.

I expected that to eat the context window. It does not. The first turn of a session holds 20,556 tokens without the server and 22,879 with it. That is 2,323 tokens for 119 tools.

It is the same mechanism as the section above. The tools are cheap because they are loaded on demand, and they get ignored because they are loaded on demand.

Where the agent alone did better

Two things, and both are on my list now.

Terminology. The existing French file keeps the word "trustee" in English. The existing Spanish file says "Panel" for Dashboard. The agent that read the files reused both, in all three runs. Through the tools, the agent only sees the English text of what is missing. Over the seven sessions that used the tools, six translated "trustee" on their own, as "fiduciaires" or "personnes de confiance", and three wrote "Panel de control" where the file already said "Panel". Nothing was refused, because the placeholders were intact.

Key order. In an empty file, the tools write the keys in alphabetical order: errors before login. The English file has login first. The agent alone copied the order of the reference.

Limits

  • Twelve sessions, one model, a 54-key project. This is one afternoon of measurements, not a benchmark of Claude Code.
  • In four of the five sessions that edited files, my sandbox refused two or three shell commands, which cost them a few turns. The one session without any refusal took 57 seconds and $0.20, so the gap remains.
  • Small files help the agent alone. Reading and rewriting whole files grows with their size, while the tool returns only what is missing. I expect the gap to widen on a large project. I have not measured it.
  • I did not grade the translations.
  • The tools need a JetBrains IDE open on the project.

Try it

The plugin is I18n Support Plus, free and open source (MIT), for JetBrains IDEs. The tools need version 2025.2.6 or later.

1- In the IDE: Settings › Tools › MCP Server, check Enable MCP Server.
2- Connect Claude Code:

 claude mcp add --transport http jetbrains http://127.0.0.1:64342/stream 

3- Add the line above to your CLAUDE.md, then ask for the missing translations.

The documentation covers Codex, Cursor and the other clients. The code is on GitHub.

One question to finish. If you have MCP servers connected to your agent, have you ever counted how often it calls them? Run the same prompt three times and count. I would like to see your ratio.

I build tools that cut the daily friction of front-end work. Let's talk on LinkedIn.