CodePark/ questions / i18n in React & Next.js
START PRACTICINGPRACTICE
Frontend question · Internationalization › i18n in React & Next.js

How do you set up locale-based routing and translations in the Next.js App Router?

app/
  [lang]/
    layout.tsx      // <html lang={lang} dir={...}>
    page.tsx        // const dict = await getDictionary(lang)
    pricing/page.tsx
dictionaries/
  en.json
  ar.json
middleware.ts       // proxy.ts in Next.js 16: redirect "/" to "/en" or "/ar"

The App Router has no built-in i18n setting. You build it with a dynamic route segment for the locale, plus middleware for the redirect. (The Pages Router had an i18n option in next.config.js, but the App Router doesn't use it.)

The steps:

  • Route segment: put every page under app/[lang]/. Each page reads lang from its params, so /ar/pricing gets lang = "ar".
  • Redirect: in middleware (renamed proxy in Next.js 16), check if the path already has a supported locale. If not, choose one from a cookie or Accept-Language, and redirect /pricing to /ar/pricing. Libraries like negotiator and @formatjs/intl-localematcher do the matching.
  • Messages: load a dictionary per locale in a Server Component, with await import("./dictionaries/ar.json"). Server Components send no translation JavaScript to the browser. Pass only the strings that Client Components need.
  • Layout: in app/[lang]/layout.tsx, render <html lang={lang} dir={lang === "ar" ? "rtl" : "ltr"}>.
  • Static pages: return every locale from generateStaticParams, so each language version is prerendered.
  • Metadata: translate titles in generateMetadata, and add alternates.languages for hreflang links.

Libraries like next-intl package all of this: middleware, message loading and formatting for both Server and Client Components.

  • The App Router has no built-in i18n config, so the locale becomes a [lang] route segment.
  • Middleware (proxy in Next.js 16) redirects paths without a locale, using a cookie or Accept-Language.
  • Server Components load the locale's dictionary, so translations don't add client JavaScript.
  • The root layout sets lang and dir on the <html> element.
  • generateStaticParams prerenders every locale.
  • generateMetadata translates titles and adds hreflang alternates.