Internationalization in Next.js – Multilingual Routing, Dynamic Localization, and Static Rendering

Next.js supports internationalization by enabling dynamic routing and localized content for multiple languages. This article explains how to detect the user’s locale, redirect based on language preferences, load translation dictionaries, and generate static pages for each locale.

InternationalizationLocaleTranslationgenerateStaticParams

~2 min read • Updated Oct 28, 2025

Terminology


  • Locale: An identifier for language and regional formatting preferences
  • en-US: English (United States)
  • nl-NL: Dutch (Netherlands)
  • nl: Dutch (generic)

Internationalized Routing


To select the correct locale, use the browser’s Accept-Language header. Libraries like @formatjs/intl-localematcher and Negotiator help determine the preferred language:

let headers = { 'accept-language': 'en-US,en;q=0.5' }
let languages = new Negotiator({ headers }).languages()
let locales = ['en-US', 'nl-NL', 'nl']
let defaultLocale = 'en-US'

match(languages, locales, defaultLocale) // -> 'en-US'

Locale-Based Redirects


In proxy.js, redirect users based on their locale:

export function proxy(request) {
  const { pathname } = request.nextUrl
  const pathnameHasLocale = locales.some(
    (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
  )

  if (pathnameHasLocale) return

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

Folder Structure in app/


To support multiple languages, nest files under app/[lang]. This allows the router to pass the lang parameter to layouts and pages:

export default async function Page({ params }) {
  const { lang } = await params
  return ...
}

Loading Translations


Use separate dictionaries for each language:

// dictionaries/en.json
{ "products": { "cart": "Add to Cart" } }

// dictionaries/nl.json
{ "products": { "cart": "Toevoegen aan Winkelwagen" } }

Define a getDictionary function to load translations:

export const getDictionary = async (locale: 'en' | 'nl') =>
  dictionaries[locale]()

In your page:

const dict = await getDictionary(lang)
return <button>{dict.products.cart}</button>

Static Rendering for Locales


Use generateStaticParams to generate static pages for each language:

export async function generateStaticParams() {
  return [{ lang: 'en-US' }, { lang: 'de' }]
}

In the layout:

<html lang={(await params).lang}>
  <body>{children}</body>
</html>

Conclusion


Internationalization in Next.js enables multilingual experiences through dynamic routing, translation loading, and static generation. With proper folder structure and locale detection, you can build scalable, global-ready applications.


Written & researched by Dr. Shahin Siami