DEV Community

Tekeu Franck
Tekeu Franck

Posted on

How to Set Up Localization in Next.js App Router Using next-intl

Problem

You want your application to support multiple languages (internationalization), allowing users to view content in their preferred language.

This guide shows how to implement localization in a Next.js App Router project using the next-intl library.

Step 1: Install the Library

Prerequisites

  • Make sure you have Node.js and npm installed on your system.
  • A Nextjs app with app router initialized.

Installation

Run the following command in the terminal:

npm install next-intl
Enter fullscreen mode Exit fullscreen mode

Step 2: Configure Localization Routing

Create a folder at the root of your project named i18n, then add a file called routing.ts.

This file defines the supported locales and the default language.

i18n/routing.ts

import {defineRouting} from 'next-intl/routing';

export const routing = defineRouting({
  // A list of all locales that are supported
  locales: ['fr', 'en'],

  // Used when no locale matches
  defaultLocale: 'fr'
});
Enter fullscreen mode Exit fullscreen mode

Step 3: Create Translation Files

At the root of your project, create a folder named messages.

Inside this folder, create one JSON file per supported language:

  • en.json (English)
  • fr.json (French)

Each file must have the same structure and keys. Only the values should differ based on the language.

⚠️ Note: JSON files must not contain trailing commas, or your app will throw errors.

messages/en.json

{
    "homepage": {
        "greetings": "Welcome",
        "navbar": {
            "appname": "Beautiful Day",
            "home": "Home",
            "services": "Services",
            "rentals": "Rentals",
            "contact": "Contact",
            "about": "About"
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

messages/fr.json

{
    "homepage": {
        "greetings": "Bienvenue",
        "navbar": {
            "appname": "Beautiful Day",
            "home": "Accueil",
            "services": "Services",
            "rentals": "Locations",
            "contact": "Contact",
            "about": "À Propos"
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Configure Request-Based Translations

Create a file named request.ts inside the i18n folder.

This file loads the correct translation messages based on the current locale.

This ensures that:

  • The correct language is loaded per request.
  • Unsupported locales fall back to the default locale.

i18n/request.ts

import {getRequestConfig} from 'next-intl/server';
import {hasLocale} from 'next-intl';
import {routing} from './routing';

export default getRequestConfig(async ({requestLocale}) => {
  // Typically corresponds to the `[locale]` segment
  const requested = await requestLocale;
  const locale = hasLocale(routing.locales, requested)
    ? requested
    : routing.defaultLocale;

  return {
    locale,
    messages: (await import(`../messages/${locale}.json`)).default

  };
});
Enter fullscreen mode Exit fullscreen mode

Step 5: Set Up Dynamic Locale Routing

Create a dynamic folder named [locale] inside your app directory.

Move your layout.tsx and page.tsx into this folder.

This enables locale-based routes such as:

  • /en
  • /fr
  • /en/services
// Folder structure
-app
   |--[locale]
          |--layout.tsx
          |--page.tsx
Enter fullscreen mode Exit fullscreen mode

Step 6: Wrap Your App with NextIntlClientProvider

Update your layout.tsx to wrap your application with NextIntlClientProvider.

This makes translations available throughout your app.

Note: In some setups, you may also need to pass messages to the provider depending on how your configuration is structured.

app/[locale]/layout.tsx

import { NextIntlClientProvider, hasLocale } from "next-intl";
import { setRequestLocale } from "next-intl/server";

import { notFound } from "next/navigation";
import { routing } from "@/i18n/routing";


type Props = {
  children: React.ReactNode;
  params: Promise<{ locale: string }>;
};

export default async function LocaleLayout({ children, params }: Props) {
  const { locale } = await params;
  // Setup locale for the rest of the app.
  setRequestLocale(locale);

  // Validate locale
  if (!hasLocale(routing.locales, locale)) {
    notFound();
  }

  return (
    <NextIntlClientProvider locale={locale}>
      {/* UI that depends on locale */}
      {children}
    </NextIntlClientProvider>
  );
}

Enter fullscreen mode Exit fullscreen mode

Step 7: Use next-intl Navigation Helpers

Since your app is now locale-aware, you should use navigation utilities provided by next-intl.

These helpers ensure that routing automatically includes the correct locale.

This prevents issues where navigation might ignore the current language.

i18n/navigation.ts

import {createNavigation} from 'next-intl/navigation';
import {routing} from './routing';

// Lightweight wrappers around Next.js' navigation
// APIs that consider the routing configuration
export const {Link, redirect, usePathname, useRouter, getPathname} =
  createNavigation(routing);
Enter fullscreen mode Exit fullscreen mode

Step 8: Use Translations in Components

You can access translations using the useTranslations hook.

The argument passed to useTranslations defines the namespace (top-level key).

app/[locale]/page.tsx

import { useTranslations } from "next-intl";

export default function HomePage() {
    const t = useTranslations("homepage");

    return (
        <div>
        <p>{t("navbar.appname")}</p>
        <p>{t("greetings")}</p>
        <ul>
        <li>{t("navbar.home")}</li>
        <li>{t("navbar.services")}</li>
        <li>{t("navbar.rentals")}</li>
        <li>{t("navbar.contact")}</li>
        <li>{t("navbar.about")}</li>

        </ul>
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode

For example:

const t = useTranslations("homepage");
Enter fullscreen mode Exit fullscreen mode

This means all keys will be resolved relative to homepage.

Step 9: Configure Locale Detection with proxy.ts

Create a proxy.ts file at the root of your project.

This enables automatic locale detection and routing.

import createMiddleware from 'next-intl/middleware';
import {routing} from './i18n/routing';

export default createMiddleware(routing);

export const config = {
  // Match all pathnames except for
  // - … if they start with `/api`, `/trpc`, `/_next` or `/_vercel`
  // - … the ones containing a dot (e.g. `favicon.ico`)
  matcher: '/((?!api|trpc|_next|_vercel|.*\\..*).*)'
};
Enter fullscreen mode Exit fullscreen mode

This ensures that:

  • Users visiting / are redirected to a default locale (e.g., /fr)
  • Locale-based routing works automatically

Conclusion

In this guide, we walked through how to set up localization in a Next.js App Router project using the next-intl library. You learned how to configure routing, structure translation files, load messages dynamically, and use translations within your components.

At this stage, you can switch languages by manually navigating to different locale routes (e.g., /en, /fr). For a better user experience, consider implementing a language switcher that allows users to change languages seamlessly using a button.

With this foundation in place, you can further enhance your application by adding features such as automatic locale detection, persistent language preferences, and SEO optimization for multi-language content.

Localization is a key step toward building scalable and user-friendly applications, especially for a global audience.

Top comments (0)