LocalePack
ChromeFirefoxEdgeOperaSafariCWS Listing
Vue.jsReact
Next.jsi18nextReact Native
Guides
Home/Guides/Next.js i18n comparison
March 7, 2026

Next.js i18n: next-intl vs react-i18next vs i18next

Three libraries dominate Next.js localization: next-intl, react-i18next, and i18next. Each uses JSON locale files — the only difference is how that JSON is loaded, typed, and delivered to components. This guide covers the practical differences so you can pick one and start shipping.

At a glance

All three libraries read locale JSON files. The differences are in App Router compatibility, TypeScript ergonomics, and how much configuration they need out of the box.

Featurenext-intlreact-i18nexti18next
App Router (RSC) supportNative — works in Server Components without a providerYes, with extra setup — hooks only work in Client ComponentsYes — use the core API directly in server code
Pages Router supportYes (limited — App Router is the focus)First-classFirst-class
TypeScript safetyType-safe keys from your locale file with one type declarationAvailable via i18next-typescript plugin; more setup requiredSame as react-i18next
Plural formatICU message syntaxKey suffixes (_one, _other, _many…)Key suffixes (_one, _other, _many…)
Variable interpolationICU — {name}Mustache — {{name}}Mustache — {{name}}
Locale routing middlewareBuilt in — one function callWrite your own or use next-i18n-routerWrite your own
Namespace / code splittingSingle file per locale (all keys loaded together)Multiple namespaces — load only what a page needsMultiple namespaces — load only what a page needs
Ecosystem / pluginsNext.js-focusedLarge — HTTP backend, caching, language detection, formattersSame ecosystem as react-i18next
Bundle size (approx.)~14 kB gzipped~8 kB + i18next ~15 kB = ~23 kB gzipped~15 kB gzipped
Non-React usage (API routes, email)Limited — designed for React renderingUse the i18next core directlyYes — primary use case

next-intl

npm install next-intl

next-intl is built specifically for Next.js. It is the only library in this comparison that works natively in React Server Components without a provider wrapper — messages are loaded on the server and passed to client components only when needed.

File format

Messages live in one JSON file per locale, typically at messages/en.json. Keys can be nested to any depth:

// messages/en.json
{
  "nav": {
    "home": "Home",
    "about": "About"
  },
  "home": {
    "title": "Welcome, {name}",
    "items": "{count, plural, one {# item} other {# items}}"
  }
}

Usage in components

In a Server Component, call getTranslations(). In a Client Component, use the useTranslations() hook:

// app/[locale]/page.tsx — Server Component
import { getTranslations } from "next-intl/server";

export default async function HomePage() {
  const t = await getTranslations("home");
  return <h1>{t("title", { name: "Alice" })}</h1>;
}

// components/Nav.tsx — Client Component
"use client";
import { useTranslations } from "next-intl";

export function Nav() {
  const t = useTranslations("nav");
  return <nav>{t("home")}</nav>;
}

Middleware for locale routing

next-intl handles locale routing through Next.js middleware. A minimal setup redirects /about to /en/about automatically:

// middleware.ts
import createMiddleware from "next-intl/middleware";
import { routing } from "./i18n/routing";

export default createMiddleware(routing);

export const config = {
  matcher: ["/((?!api|_next|.*\..*).*)"],
};

// i18n/routing.ts
import { defineRouting } from "next-intl/routing";

export const routing = defineRouting({
  locales: ["en", "de", "fr", "ja"],
  defaultLocale: "en",
});
next-intl uses ICU message syntax for pluralization and variable substitution — the same standard used by the Intl browser API. This means plural rules are correct for all languages without extra configuration.

TypeScript safety

next-intl can generate types from your default locale file so that t("typo") is a TypeScript error at build time. Add one type declaration:

// global.d.ts — wire up type-safe messages
import en from "@/messages/en.json";

declare global {
  type IntlMessages = typeof en;
}

react-i18next

npm install react-i18next i18next

react-i18next is the React binding layer for i18next — the most widely used i18n library in the JavaScript ecosystem. It has the largest community, the most plugins, and the longest track record. If your team already uses it in a non-Next.js project, carrying it into a Next.js app is straightforward.

File format

Locale files are organized by namespace — one JSON file per feature area or page. The default namespace is translation:

// public/locales/en/translation.json
{
  "nav_home": "Home",
  "nav_about": "About"
}

// public/locales/en/checkout.json
{
  "title": "Checkout",
  "items_one": "{{count}} item",
  "items_other": "{{count}} items"
}

Keys are flat by convention, though nesting is supported. Plurals use a key-suffix pattern (_one, _other) rather than ICU syntax.

Usage in components

"use client";
import { useTranslation } from "react-i18next";

export function Nav() {
  const { t } = useTranslation(); // default namespace
  return <nav>{t("nav_home")}</nav>;
}

// Loading a specific namespace
export function CheckoutSummary({ count }: { count: number }) {
  const { t } = useTranslation("checkout");
  return <p>{t("items", { count })}</p>;
}

App Router setup

react-i18next works with the App Router, but Server Components cannot use hooks. The recommended approach is a server-side helper that calls the i18next core API directly:

// i18n.ts — shared config
import i18next from "i18next";
import { initReactI18next } from "react-i18next/initReactI18next";

i18next.use(initReactI18next).init({
  lng: "en",
  fallbackLng: "en",
  resources: {
    en: { translation: require("./public/locales/en/translation.json") },
    de: { translation: require("./public/locales/de/translation.json") },
  },
});

export default i18next;

// In a Server Component — use the core API directly
import i18n from "@/i18n";
const t = i18n.getFixedT("de");
return <h1>{t("nav_home")}</h1>;
For App Router projects starting fresh, next-intl requires less boilerplate and has better first-class RSC support. react-i18next shines in projects migrating from Pages Router or already sharing an i18next configuration across React Native or non-Next.js apps.

The Trans component for rich content

react-i18next’s Trans component handles translations that embed React elements — links, bold text, or icons inside a sentence:

// en/translation.json
{
  "tos_prompt": "By signing up you agree to our <1>Terms of Service</1>."
}

// Component
import { Trans } from "react-i18next";

<Trans
  i18nKey="tos_prompt"
  components={[<span />, <Link href="/terms" />]}
/>

next-intl offers a similar rich() API, but the Trans component is more expressive for complex markup.

i18next (without react-i18next)

npm install i18next i18next-resources-to-backend

i18next is the core library that react-i18next wraps. You can use it directly in Next.js Server Components and API routes without any React binding layer. This is useful when you need i18n in server-only code — middleware, API handlers, email templates, or PDF generation — without bringing in React hooks.

// lib/i18n-server.ts
import i18next from "i18next";
import resourcesToBackend from "i18next-resources-to-backend";

const i18n = i18next.createInstance();

await i18n
  .use(
    resourcesToBackend(
      (language: string, namespace: string) =>
        import(`../public/locales/${language}/${namespace}.json`),
    ),
  )
  .init({
    lng: "en",
    fallbackLng: "en",
    defaultNS: "translation",
  });

export default i18n;

The file format is identical to react-i18next. The same JSON locale files work with both. Switching from standalone i18next to react-i18next later requires no locale file changes.

Pluralization: ICU vs key suffixes

The biggest difference in how you write locale files is pluralization syntax.

next-intl: ICU message format

// messages/en.json
{
  "items": "{count, plural, =0 {No items} one {# item} other {# items}}"
}

// messages/pl.json — Polish has four plural forms
{
  "items": "{count, plural, one {# element} few {# elementy} many {# elementów} other {# elementu}}"
}

All plural forms live in one key. ICU rules handle every language automatically — no special configuration for Polish, Arabic, or Russian plural forms.

react-i18next / i18next: key suffix pattern

// public/locales/en/translation.json
{
  "items_zero": "No items",
  "items_one": "{{count}} item",
  "items_other": "{{count}} items"
}

// public/locales/pl/translation.json — Polish
{
  "items_one": "{{count}} element",
  "items_few": "{{count}} elementy",
  "items_many": "{{count}} elementów",
  "items_other": "{{count}} elementu"
}

Each plural form is a separate key. More verbose, but easier to hand to translators who are unfamiliar with ICU syntax.

Both formats are supported by LocalePack. Upload your source locale file and the output preserves whichever plural format your library expects — ICU tokens stay intact for next-intl, key suffix structure stays intact for react-i18next.

Locale routing in the App Router

Both next-intl and react-i18next work with the app/[locale]/ dynamic segment. The locale is a route parameter; middleware detects the preferred locale and redirects accordingly.

// Recommended folder structure for App Router
app/
  [locale]/
    layout.tsx       ← wraps pages with locale context
    page.tsx
    about/
      page.tsx
  middleware.ts      ← detects and redirects to locale

messages/            ← locale files (next-intl convention)
  en.json
  de.json
  fr.json

// — OR —

public/
  locales/           ← locale files (react-i18next convention)
    en/
      translation.json
    de/
      translation.json

next-intl provides a middleware factory out of the box. With react-i18next you write the middleware yourself — typically a small function that reads Accept-Language, matches a supported locale, and rewrites the URL.

Which one should you pick?

next-intl

Best for: New App Router projects

  • –Native React Server Component support — no provider needed in server layouts
  • –Built-in middleware for locale routing with one function call
  • –Type-safe message keys with zero extra tooling
  • –ICU pluralization handles all language edge cases correctly out of the box
  • –Smallest configuration surface for a greenfield Next.js app

Not ideal for: Projects already running react-i18next, or codebases that share i18n config across React Native or non-Next.js apps

react-i18next

Best for: Migrations from Pages Router, or multi-platform codebases

  • –Largest ecosystem — plugins for HTTP backends, caching, language detection
  • –Trans component for complex markup embedded inside translations
  • –Same config works across React, React Native, and non-Next.js React apps
  • –Namespace-based code splitting keeps bundle size down for large apps
  • –Familiar API for teams already experienced with i18next

Not ideal for: App Router projects starting fresh — RSC integration requires more setup than next-intl

i18next (standalone)

Best for: Server-only code that needs i18n

  • –No React dependency — works in middleware, API routes, email templates
  • –Same locale file format as react-i18next — easy to unify across your codebase
  • –Initialize once, import and call anywhere on the server

Not ideal for: Component trees — use react-i18next or next-intl for anything rendered in React

Locale file format summary

All three libraries read plain JSON. The structural conventions differ:

LibraryDefault pathKey stylePlural format
next-intlmessages/{locale}.jsonNested objectsICU syntax
react-i18nextpublic/locales/{locale}/{ns}.jsonFlat or nestedKey suffixes (_one, _other)
i18nextConfigurableFlat or nestedKey suffixes (_one, _other)

Migrating between libraries

The most common migration path is from next-i18next (Pages Router) to next-intl (App Router). Locale JSON files are reusable — next-intl reads nested JSON natively, so flat key structures from next-i18next can be imported directly. The main changes are in the calling code, not the locale files:

Fromnext-i18next useTranslation()
Tonext-intl useTranslations() — same call signature, different import
Fromi18next {{count}} interpolation in locale files
Tonext-intl {count} ICU interpolation — update your locale strings
Fromnext-i18next serverSideTranslations() in getStaticProps
Tonext-intl getTranslations() — called directly inside the Server Component
Fromnext.config.js i18n block (Pages Router only)
Tonext-intl middleware + routing config — the next.config.js i18n key is unused in App Router

Translate your Next.js locale files with LocalePack

LocalePack translates the JSON locale files used by next-intl, react-i18next, and i18next. Upload your source locale file — flat or nested, ICU or key-suffix plural format — and download a ready-to-use ZIP with one file per target language. No platform setup, no subscription. Upload once, pay once, download.

Translate Next.js locale files →

Related guides

messages.json format explained (with placeholders)

The WebExtension messages.json format reference — different from Next.js locale JSON but useful context if you build both.

Validate messages.json before shipping

Validation scripts for required fields, missing keys across locales, and malformed placeholder syntax.

← Back to Guides
LocalePack
GuidesPrivacyTermsSupport

© 2025 LocalePack. All rights reserved.

This project was translated with LocalePack logoLocalePack