Next.js Curriculum
MediumEnterprise Architecture100% Free30 min read

Internationalization (i18n): App Router Routing

The 30-Second Interview Answer

Unlike Pages Router which featured built-in i18n configuration in next.config.js, App Router relies on subpath routing via dynamic route segments (app/[lang]/...) orchestrated by proxy.ts or middleware.ts. The proxy negotiates language preferences from Accept-Language headers or cookies and rewrites/redirects to the appropriate locale.

Learning Objectives

Implement localized subpath routing using the [lang] dynamic segment convention.

Detect and negotiate user locales using Accept-Language headers and negotiator/formatjs.

Persist language preferences across user sessions via cookies.

Generate localized static routes with generateStaticParams for all supported locales.

Prerequisites & Background Knowledge
HTTP Accept-Language headerSubpath routing strategyi18n localization concepts

Why Interviewers Care

Required for all global applications expanding into international markets across North America, Europe, Asia, and LATAM.

The Challenge

How does internationalization (i18n) work in Next.js App Router without next.config.js i18n support? Implement locale negotiation in Middleware and define a localized [lang] layout.

Code Architecture & Implementation

1// middleware.ts
2import { NextRequest, NextResponse } from 'next/server';
3
4const locales = ['en', 'es', 'fr', 'de'];
5const defaultLocale = 'en';
6
7function getLocale(request: NextRequest): string {
8 // 1. Check user cookie first
9 const cookieLocale = request.cookies.get('NEXT_LOCALE')?.value;
10 if (cookieLocale && locales.includes(cookieLocale)) return cookieLocale;
11
12 // 2. Parse Accept-Language header
13 const acceptLang = request.headers.get('accept-language') || '';
14 const preferred = acceptLang.split(',')[0].split('-')[0].toLowerCase();
15
16 return locales.includes(preferred) ? preferred : defaultLocale;
17}
18
19export function middleware(request: NextRequest) {
20 const { pathname } = request.nextUrl;
21
22 // Check if pathname already starts with a supported locale
23 const pathnameHasLocale = locales.some(
24 (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
25 );
26
27 if (pathnameHasLocale) return NextResponse.next();
28
29 // Redirect to localized URL: /products -> /en/products
30 const locale = getLocale(request);
31 request.nextUrl.pathname = `/${locale}${pathname}`;
32 return NextResponse.redirect(request.nextUrl);
33}

Architectural Explanation

App Router delegates internationalization to folder-based routing: app/[lang]/page.tsx. Every route is nested inside the [lang] dynamic segment. When users arrive at /about, Middleware detects their browser language (e.g. Spanish) and redirects them to /es/about. Component dictionaries (e.g. getDictionary(lang)) load strictly server-side JSON translation files, meaning only the translations for the active language are loaded without shipping huge multilingual JSON bundles to the browser.

Common Mistakes & Anti-Patterns

Attempting to use `i18n` in next.config.js in App Router and wondering why it has no effect.

Shipping translation dictionaries for ALL languages in the client JavaScript bundle.

Not including `hreflang` alternate links in metadata, harming international SEO rankings.

Interview Follow-Up Traps

The interviewer asks: "How do you avoid shipping unused language strings to the browser?" Keep translation dictionaries inside Server Components; only pass the translated strings as props to Client Components.

Frequently Asked Questions

Which libraries simplify i18n in Next.js App Router?

Popular libraries include `next-intl`, `react-intl`, and `lingui`.

Set a `NEXT_LOCALE` cookie and navigate to the corresponding localized subpath URL.

Return arrays pairing locales with slugs: `[{ lang: "en", slug: "about" }, { lang: "es", slug: "about" }]`.