I've set up i18n in Next.js App Router projects more times than I'd like to admit. It's always the same: middleware config, a pile of JSON files, wiring up the provider, copy-pasting keys between locales, discovering at 11pm that common.heor.title returns undefined in production.
The last time it happened I was mass-translating 40+ keys across 5 languages by hand. I stopped halfway through and thought: why am I still doing this manually?
So I built i1n, an open source CLI (MIT) that pushes your translation keys and gets them back translated with full TypeScript types. Free tier, no credit card, no 14-day trial tricks.
Let me walk you through the setup.
Prerequisites
- A Next.js project (App Router or Pages Router)
- Node.js 18+
- ~5 minutes
Step 1: Install and init
npm install -g i1n
i1n init
It'll ask for an API key (you can grab one at i1n.ai, Google or GitHub login). After that it detects your framework and sets up the locale directory automatically.
Step 2: Write your source strings
locales/en_us/common.json:
{
"hero": {
"title": "Welcome to our app",
"subtitle": "The best tool for {task}",
"cta": "Get Started"
},
"nav": {
"home": "Home",
"about": "About",
"pricing": "Pricing"
}
}
Nothing new here â same JSON structure you're probably used to.
Step 3: Push and translate
i1n push --translate es,fr,de,ja
One command. It pushes your keys, translates to 4 languages, and generates i1n.d.ts with autocomplete for every key. Variables like {task} are left untouched in every language.
Step 4: Use it
import { t } from 'i1n';
export function Hero() {
return (
<section>
<h1>{t('hero.title')}</h1>
<p>{t('hero.subtitle', { task: 'building products' })}</p>
<button>{t('hero.cta')}</button>
</section>
);
}
If you typo a key, TypeScript yells at you before it ever hits production. No more t('hera.titl') silently rendering nothing.
If you're already on i18next or next-intl, there's a Bridge Mode that wraps your existing setup with type safety. One line, no migration. Details in the docs.
Bonus: MCP Server
This part is honestly the most fun. If you use Cursor, Claude Code, or Windsurf:
claude mcp add i1n -- npx i1n mcp
Now you can just tell your AI agent "internationalize this component" and it actually does it. Reads the file, extracts the strings, pushes them, translates everything, rewrites the code with t() calls. What used to take me an hour is like 30 seconds now.
What does your i18n setup look like? I've been going back and forth between next-intl and i18next with the App Router and I'm curious what other people landed on.
Top comments (0)