Next.js internationalization: i18n config vs automatic translation (2026)
Next.js gives you two fundamentally different paths to a multilingual app: built-in i18n routing combined with a translation library, and DOM-based automatic translation with a single script tag. This guide explains both — what each one actually does, what it costs, and when to choose it.
Next.js i18n routing: what it gives you
Next.js ships with a built-in i18n routing layer configured in next.config.ts. It handles two things: URL routing (/fr/, /de/ prefixes) and browser locale detection via the Accept-Language header.
What it does not do: translate any content. The i18n routing config is purely infrastructure — it sets up the URL structure and locale detection, then hands off. For actual translated text in your components, you add a translation library on top: next-intl, react-i18next, or LinguiJS.
The routing config alone does mean that visiting yourapp.com/fr/dashboard works as a distinct URL — useful for SEO and shareable links — but without a translation library, the page still renders in English.
The full i18n stack for Next.js
A complete Next.js i18n setup with next-intl involves the following pieces:
- 1
next.config.tsi18n block declaring supported locales and the default. - 2Per-locale JSON message files:
messages/en.json,messages/fr.json, etc., checked into the repo and updated with every new UI string. - 3Type-safe message keys generated from the English source file so TypeScript catches missing or mistyped keys at compile time.
- 4Server Components load messages via
getTranslations(); Client Components are wrapped with<NextIntlClientProvider>. - 5RTL languages (Arabic, Hebrew) require separate CSS work on top:
dir="rtl"on the root element, logical property utilities, and icon mirroring.
A minimal next.config.ts i18n block:
And a matching messages/en.json:
Every key in en.json must be duplicated in every other locale file, translated, and kept in sync as the product evolves.
The cost at scale
The per-string overhead of wrapping in t() is small. The aggregate cost compounds quickly:
Content–code coupling
Every new UI string requires a new key in the source file and a corresponding entry in every target locale file. A new feature shipped Monday means the French and German files need to be updated before French and German users see anything other than a key fallback.
Pluralization rules per language
English has two plural forms. Arabic has six. Polish has four with complex rules. ICU message format handles this, but every plural string requires a separate definition per locale — the naive approach of a single string with a count breaks for many languages.
Interpolation for dynamic values
Strings like “Welcome, {name}!” or “You have {count}items” need interpolation syntax that varies by library, requires testing across all locales, and can produce grammatically incorrect output in languages where word order differs from English.
TypeScript overhead
Type-safe keys require a generated type file derived from the English source. This generation step must run in CI, adds to build time, and fails the build if the source locale file has structural issues — useful, but not free.
RTL is a separate project
next-intl and react-i18next translate text; they do not flip layout direction. Arabic and Hebrew support requires separate work: setting dir="rtl", auditing every component for physical CSS properties (margin-left → margin-inline-start), and testing the full layout in RTL mode.
When Next.js i18n config is the right call
There are specific contexts where the full library stack is justified or required:
Open-source apps with community translations
When translations are community-contributed on GitHub (as JSON or PO files), a standard library format is the correct contributor interface. Automatic translation doesn't serve this model.
Compliance-regulated content
Regulated industries (legal, medical, financial) may require that every UI string is explicitly reviewed and approved. An i18n library makes every string an auditable discrete artifact with a version history.
Heavy number, date, and currency formatting
Analytics dashboards and accounting tools that format numbers, dates, and currency throughout the UI benefit from the locale-aware Intl API integration that FormatJS and next-intl provide out of the box.
Offline-capable apps
PWAs with full offline support need bundled translation files at build time because they cannot call a translation API when offline. Library-based i18n is the only option here.
Automatic translation: how it works with Next.js
The alternative is a script tag in your root app/layout.tsx, placed inside the <body> element. No changes to next.config.ts, no locale routing configuration, no JSON files.
The script uses a MutationObserver to watch the DOM after Next.js renders. This handles:
- Initial Server Component HTML — the page content rendered on the server and streamed to the browser.
- Post-hydration content — Client Component state changes, data from
useEffect, lazy-loaded components. - Client-side navigation — Next.js route transitions trigger the observer for the newly rendered page content.
- RTL layout —
document.documentElement.diris set automatically when switching to Arabic, Hebrew, or another RTL language.
The approach works with both App Router and Pages Router. The rendering model (server or client) is irrelevant — only the final DOM output matters.
Setup comparison
The difference in setup complexity is significant. Here is the next-intl path versus the Lingvit script-tag path:
next-intl setup (~8 files, ~200 lines of configuration)
Lingvit setup (1 line)
What automatic translation does not cover
DOM-based translation has real limitations to understand before choosing it:
Server-side email strings
Transactional emails built from string constants in server code are never rendered to a browser DOM. They need separate translation handling — a multilingual email template system or per-locale email templates.
Strings never rendered to DOM
Hardcoded strings used only for logging, API responses consumed by non-browser clients, or content only ever processed server-side are not visible to the MutationObserver and will not be translated.
Complex ICU pluralization
Applications that need grammatically correct pluralization in Arabic (six plural forms) or Polish (four forms with rule exceptions) for dynamically counted values require ICU message format support — which is a library feature, not something DOM-level translation handles automatically.
Frequently asked questions
- Does Lingvit work with App Router server components?
- Yes. Server Components produce HTML that the browser receives and inserts into the DOM exactly like any other HTML. The Lingvit script picks up this content normally via the MutationObserver. There is no special Server Component handling needed.
- Can I use Lingvit alongside next-intl?
- Yes. If you have next-intl for some strings and want automatic translation for the rest, both can coexist. Lingvit will translate whatever text nodes are in the DOM at render time, including next-intl rendered strings if those are not yet translated in your locale files.
- What about SEO — does automatic translation create indexable language URLs?
- No, not as a general launch capability. Use Next.js locale routing and server-rendered metadata for independently indexable language pages; Lingvit handles supported runtime translation in the browser.
Related guides
Add languages to your Next.js app
Works with App Router and Pages Router. No next.config changes needed.