How to implement locale-based routing in Next.js (Pages Router) v16

Configure routing with locale segments

Problem

When building a multilingual application, one fundamental decision shapes everything else: how will the application know which language to display? Without an explicit mechanism, the URL /about becomes ambiguous—it could represent content in any language. Users cannot share links to specific language versions, and search engines struggle to understand which version belongs to which audience. This ambiguity creates problems for both user experience and SEO, as there is no clear way to identify or bookmark content in a particular language.

Solution

Put a language identifier directly into the URL path by configuring Next.js's built-in i18n routing support. Declare the locales you want to support and a default locale in your Next.js configuration. Next.js will automatically handle the routing, making paths like /fr/about and /nl-NL/about available, while the default locale does not have a prefix. This makes every path unique to a specific language, removing the ambiguity for both users and search engines.

Steps

1. Add i18n configuration to next.config.js

Add the i18n config to your next.config.js file to declare which locales your application supports.

module.exports = {
  i18n: {
    locales: ["en-US", "fr", "nl-NL"],
    defaultLocale: "en-US",
  },
};

Locales are UTS Locale Identifiers, a standardized format for defining locales, generally made up of a language, region, and script separated by a dash. The defaultLocale is used when visiting a non-locale prefixed path.

2. Access locale information in pages

Use the useRouter() hook to access locale information in your page components.

import { useRouter } from "next/router";

export default function AboutPage() {
  const router = useRouter();
  const { locale, locales, defaultLocale } = router;

  return (
    <div>
      <h1>About Us</h1>
      <p>Current locale: {locale}</p>
    </div>
  );
}

The locale property contains the currently active locale, locales contains all configured locales, and defaultLocale contains the configured default locale.

3. Access locale in data fetching functions

When pre-rendering pages with getStaticProps or getServerSideProps, the locale information is provided in the context.

import { GetStaticProps } from "next";

export const getStaticProps: GetStaticProps = async (context) => {
  const { locale } = context;

  const messages = await import(`../messages/${locale}.json`);

  return {
    props: {
      messages: messages.default,
    },
  };
};

This allows you to load locale-specific data at build time or request time based on the active locale.

Use next/link with a locale prop to transition to a different locale.

import Link from "next/link";

export default function LanguageSwitcher() {
  return (
    <nav>
      <Link href="/about" locale="en-US">
        English
      </Link>
      <Link href="/about" locale="fr">
        Français
      </Link>
      <Link href="/about" locale="nl-NL">
        Nederlands
      </Link>
    </nav>
  );
}

If no locale prop is provided, the currently active locale is used during client-transitions. The locale prop allows users to switch languages while staying on the same logical page.

5. Generate static paths for all locales

When leveraging getStaticPaths, the configured locales are provided in the context parameter under locales and the configured defaultLocale under defaultLocale.

import { GetStaticPaths } from "next";

export const getStaticPaths: GetStaticPaths = async (context) => {
  const { locales } = context;

  const paths = locales.flatMap((locale) => [
    { params: { slug: "getting-started" }, locale },
    { params: { slug: "advanced" }, locale },
  ]);

  return {
    paths,
    fallback: false,
  };
};

This ensures that all locale versions of your dynamic pages are pre-rendered at build time.