Technology · Next.js

Next.js Internationalization

Implement i18n in Next.js with routing, translations, locale detection, and multi-language support.

TL;DR
  1. 01Use locale-prefixed routing like /en, /es, /fr in the app directory.
  2. 02Organize translation files by locale and reference them in components.
  3. 03Detect locale from URL, user preference, or browser language.

Locale-Based Routing

  • Create a locale folder structure to organize routes by language.
    app/
      [locale]/
        layout.tsx
        page.tsx
        about/
          page.tsx
  • This creates URLs like /en, /es, /en/about, /es/about, etc.
  • Access the locale parameter in your layouts and pages.
    export default function Layout({ 
      children, 
      params: { locale } 
    }: {
      children: React.ReactNode;
      params: { locale: string };
    }) {
      return <html lang={locale}>{children}</html>;
    }
  • Use middleware to redirect to the user's preferred locale.
  • Generate static params for all supported locales.

Translation Files

  • Create JSON or JSON5 files to store translations by locale.
    locales/
      en.json
      es.json
      fr.json
  • Structure translations hierarchically for nested content.
    {
      "common": {
        "welcome": "Welcome",
        "goodbye": "Goodbye"
      },
      "home": {
        "title": "Home Page",
        "description": "Welcome to our site"
      }
    }
  • Load translations based on the current locale in your component.
    import translations from "@/locales/en.json";
    const message = translations.common.welcome;
  • Use a translation library like next-intl for complex needs.

Using the next-intl Library

  • Install and configure next-intl for a complete i18n solution.
    npm install next-intl
  • Create a configuration file for supported locales and messages.
    import { getRequestConfig } from "next-intl/server";
    
    export default getRequestConfig(async ({ locale }) => ({
      messages: (await import(`./messages/${locale}.json`)).default
    }));
  • Use the useTranslations hook to access translations in components.
    "use client";
    import { useTranslations } from "next-intl";
    
    export default function HomePage() {
      const t = useTranslations("home");
      return <h1>{t("title")}</h1>;
    }
  • next-intl handles formatting, pluralization, and date localization.

Locale Detection

  • Detect the user's preferred locale from the Accept-Language header.
    import { getLocale } from "next-intl/server";
    
    export const getServerSideProps = async ({ req }) => {
      const acceptLanguage = req.headers["accept-language"];
      const preferredLocale = acceptLanguage?.split(",")[0].split("-")[0];
      // Use preferredLocale to redirect or set session
    };
  • Store locale preference in a cookie or user profile.
    const setLocale = (locale: string) => {
      document.cookie = `NEXT_LOCALE=${locale}`;
      router.push(`/${locale}`);
    };
  • Allow users to manually select their language.
    <select onChange={(e) => setLocale(e.target.value)}>
      <option value="en">English</option>
      <option value="es">Español</option>
      <option value="fr">Français</option>
    </select>
  • Respect browser language preference on first visit.

SEO and Hreflang

  • Set the lang attribute on the HTML element per locale.
    <html lang={locale}>
  • Add hreflang links to alternate language versions for SEO.
    export async function generateMetadata({ params }) {
      const alternates = {
        languages: {
          en: `https://example.com/en/page`,
          es: `https://example.com/es/page`,
          fr: `https://example.com/fr/page`
        }
      };
      return { alternates };
    }
  • Generate sitemaps for each locale to help search engines index them.
  • Use canonical URLs to prevent duplicate content issues.

Tip: Use next-intl library for complex i18n needs like pluralization, date formatting, and locale-aware routing, as it handles many edge cases automatically.

Warning: Always set the HTML lang attribute correctly for each locale, since screen readers and search engines rely on it for language detection.

Next.js Dynamic RoutesNext.js Middleware