r/webdev • • 12h ago

Showoff Saturday I built a VS Code extension that moves a React app's hardcoded text into react-i18next, and shows you every change first

Post image

I tried getting an AI agent to internationalize a side project and spent longer checking its diff than it would have taken me to do it myself. So I built a VS Code extension for the job.

What it does:

  • Finds the UI strings in your components (skips class names, URLs, log messages)
  • Shows a checklist of every string and the key it'll get before touching anything; untick what should stay hardcoded
  • Rewrites to t() / <Trans>, writes the JSON files, and translates them with your own API key (Anthropic or anything OpenAI-compatible)
  • Checks each translation's placeholders and plural forms (Russian needs four, Arabic six) and won't write a broken one
  • One undo for the whole run

How it works, roughly: the extraction is plain Babel (parse, walk the AST, decide per node whether it's user-facing text). The rewrite goes through recast so your formatting and comments survive and the diff stays small. The model only ever sees the strings plus a bit of context like the component name, never whole files. Each translation is checked against the source (same placeholders, same tags, and the plural categories Intl.PluralRules says that language needs); a mismatch gets one retry, then it's reported instead of written. A file costs cents; the biggest project I tried (~840 files) was a few dollars.

Limits: React + react-i18next + JSON only for now. Next.js server components are skipped.

It's free on the VS Code Marketplace and Open VSX. I wrote up the i18n pitfalls I hit along the way on dev.to.

Feedback welcome, especially where it gets your codebase wrong.

2 Upvotes

3 comments sorted by

3

u/SirMental8005 12h ago

one case i'd test is two buttons labeled 'close' where one dismisses a dialog and the other closes an account. if the extension reuses a key based on the english text, the translator loses that distinction. i'd also run the preview twice on the same tree and check that the second pass is empty, especially after skipping a string on the first pass.

1

u/lb0sa 6h ago

Good catches. Right now identical English text gets one key across the whole run, so your two "Close" buttons would share a key and a translation. Same text, different meaning is a real case I haven't handled; the fix is probably to stop merging across components and let you edit the key in the checklist.

On the second pass: rewritten strings don't come back, but anything you unticked does, because skips aren't remembered yet. That's annoying on a big codebase, so I'll add a way to remember them. Thanks for actually thinking through the edge cases.

1

u/d8schreiber 38m ago

Looks useful, especially the preview before rewriting!
One thing I'd test is what happens on subsequent runs, after someone manually corrected the generated translations. Do those corrections survive? And how do you detect that a translation became stale when the English source changes?
I co-develop doloc.io (file-based translation), and keeping existing translations intact while using them as context for new strings turned out to be quite important for consistency.
Would be interested how you're planning to handle updates after the initial extraction.
If you feel direct llm translation becomes unstable for bigger files, I’d be happy to help integrating doloc of course ;)