Skip to content

Internationalization (i18n)

Internationalization (i18n) lets the same Next.js app serve users in multiple languages and locales. This lesson covers locale-based routing in the App Router, message catalogs, and practical patterns for translated UI.

What Internationalization Means in Next.js

i18n covers locale routing (/en/about vs /fr/about), translated strings (message catalogs), and sometimes locale-aware formatting for dates, numbers, and currencies. The App Router does not ship a built-in i18n router the way the older Pages Router i18n config did; most teams use a library such as next-intl or build a lightweight locale segment themselves.

A common App Router pattern is a dynamic [locale] segment at the top of app/, so every page lives under /en/..., /de/..., and so on. Middleware can detect the preferred language from cookies or Accept-Language and redirect visitors to the right locale prefix.

// app/[locale]/layout.tsx
export default function LocaleLayout({
  children,
  params,
}: {
  children: React.ReactNode;
  params: { locale: string };
}) {
  return (
    <html lang={params.locale}>
      <body>{children}</body>
    </html>
  );
}

Placing a [locale] segment under app/ makes the active language part of every URL and available to layouts and pages via params.

Locale Segment and Message Lookup

app/
  [locale]/
    layout.tsx
    page.tsx
    about/
      page.tsx

// messages/en.json
{ "Home": { "title": "Welcome" } }

// messages/fr.json
{ "Home": { "title": "Bienvenue" } }
  • [locale] becomes the first URL segment for every localized route.
  • Message files (JSON or TypeScript) store translated strings keyed by namespace.
  • Middleware can set a default locale and rewrite unknown locales to a fallback.
  • Use the native lang attribute on <html> so assistive tech and search engines know the page language.

i18n Cheat Sheet

Core pieces of a Next.js App Router internationalization setup.

Piece Role
[locale] segment Puts the language in the URL path
Message catalogs Store translated UI strings per locale
Middleware Detect locale and redirect/rewrite
lang on <html> Exposes the active language to browsers and SEO
Libraries (next-intl) Handle routing, messages, and formatting helpers

Locale Detection with Middleware

Middleware can inspect cookies and the Accept-Language header, then redirect bare paths like /about to /en/about (or whatever default you choose). Keep detection logic fast — Edge Middleware should not call heavy databases just to choose a locale.

// middleware.ts (illustrative)
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";

const locales = ["en", "fr", "de"];

export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;
  const hasLocale = locales.some(
    (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
  );
  if (hasLocale) return NextResponse.next();

  const locale = "en";
  request.nextUrl.pathname = `/${locale}${pathname}`;
  return NextResponse.redirect(request.nextUrl);
}

This sketch always falls back to English; a real app would prefer a saved cookie or Accept-Language match first.

SEO for Multi-Language Sites

Each locale should expose unique URLs, accurate hreflang / alternates.languages metadata, and correctly translated titles and descriptions. The Metadata API supports language alternatives so search engines can cluster the same page across languages.

  • Give every locale its own URL path (or subdomain) rather than swapping language via client-only JS.
  • Use generateMetadata to set locale-specific titles and alternates.languages.
  • Keep content genuinely translated — machine translation alone with no review often hurts SEO and trust.

Common Mistakes

  • Hardcoding English strings throughout components instead of using message keys.
  • Forgetting to set <html lang={locale}>, leaving every page marked as the wrong language.
  • Relying only on client-side language switching without unique URLs for each locale.
  • Translating only the UI chrome while leaving SEO metadata (titles/descriptions) untranslated.

Key Takeaways

  • App Router i18n usually starts with a [locale] segment and message catalogs.
  • Middleware handles locale detection and redirects for users without a locale prefix.
  • Libraries like next-intl reduce boilerplate for routing, messages, and formatting.
  • SEO needs per-locale URLs, metadata, and hreflang / language alternates.
  • Always set the lang attribute on <html> from the active locale.

Pro Tip

Start with a small set of locales and a clear message-key naming convention (for example Namespace.key) before scaling — renaming keys later across a large catalog is far more painful than agreeing on conventions early.