i18n-swarm · CI localization gate
Catch the hardcoded string before the PR merges
A CI check that fails the pull request the moment a new hardcoded UI string lands, so the localization regression never reaches production. Works with next-intl, react-i18next and vue-i18n.
The gate's verdict is only as good as the engine behind it, so I ran that engine against 8 unmodified open-source Next.js App Router apps with no i18n (Vercel Commerce, Vercel Platforms, Magic UI, two shadcn dashboards, a starter, a portfolio template, leerob.io). Each is a clean clone: baseline build, full pass, fresh install, build again to verify. Not a mockup. Full table and per-app diffs: results.md.
The gate
Localization rots the day after you finish it. Someone adds a button, ships the label hardcoded, and your other locales quietly fall behind. check runs on the diff, not the whole tree, so it fails only on the string the PR just added and prints the keyed fix as a ready-to-apply patch.
npx i18n-swarm check # working tree vs HEAD npx i18n-swarm check <base>..<head> # a PR range npx i18n-swarm check --files=a.tsx,b.vue # explicit files
Brand names, code identifiers, decorative alt text and config-only strings are demoted to non-blocking notes, so the gate fires on real copy (Sign In, Save) and not on variant="primary".
What the keyed fix looks like
app/error.tsx · vercel/commerce "use client"; + import { useTranslations } from 'next-intl'; export default function Error({ reset }) { + const t = useTranslations(); return ( - <h2>Oh no!</h2> + <h2>{t('error.oh_no')}</h2>
Every edit is re-parsed after it is written. A bad edit reverts instead of mangling the file, which is why the corruption count is 0 across all 1,447 edits.
It does not translate. Every ja key is seeded with the English fallback, because machine-translating Japanese is how you ship a homograph bug no compiler catches, so that stays human.
Prose, interpolated counts and sentences with inline links are not guessed at. They go to a review queue, not a fail.
vs i18next-cli
The honest answer: i18next-cli is the official, broader, better-maintained tool, and if you are on react-i18next and Locize you should use it. i18n-swarm's edge is two narrower things. After it rewrites, it runs your real next build and reverts any edit that won't re-parse, where i18next-cli calls itself "not an automated compiler". And its gate is diff-scoped, so it fails a PR on the one string you just added instead of on every hardcoded string already in the tree.
Common questions
How do I stop hardcoded strings from reaching production?
Run a check on the pull request's diff and fail the job when a new user-facing string lands without a key. i18n-swarm check exits non-zero on a new hardcoded UI string and prints the keyed fix. Make it a required status check and the regression can't merge.
How do I fail CI when a hardcoded string is added in a Next.js or React app?
npx i18n-swarm check <base>..<head> against the PR range. It diffs only what changed and exits 1 if a new string is real copy instead of a key. Brand names and code identifiers are demoted to non-blocking notes.
How do I detect missing or untranslated i18n keys in CI?
It catches the upstream cause: a new string that was never keyed, which is how locales fall behind. It isn't a key-diff between en.json and ja.json; it stops the un-keyed string that would have created the gap. Translation quality stays human.
Does it work with next-intl, react-i18next and vue-i18n?
Yes. The same classifier drives the retrofit (next-intl for the Next.js App Router, react-i18next for plain React or Vite, vue-i18n for Vue 3) and the gate. The gate only needs git and the diff.
How is it different from i18next-cli?
i18next-cli is official and broader. i18n-swarm runs your real build and reverts edits that won't re-parse, and its gate is diff-scoped, so it fails the PR on the one string you just added rather than the whole tree.
Raw per-app diffs: results.md · commerce · platforms · magicui · leerob · adm-dashboard · kiran-dashboard · ts-starter · dillion-portfolio
Which would you use, the one-time retrofit or the recurring drift gate? Email me which one, one word is plenty. I read every reply.
Email: retrofit or the gateThe CLI is npx i18n-swarm ./your-app. Published on npm (v0.1.0, MIT). Run it on a branch and read the diff before you trust it.