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
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'
});
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"
}
}
}
messages/fr.json
{
"homepage": {
"greetings": "Bienvenue",
"navbar": {
"appname": "Beautiful Day",
"home": "Accueil",
"services": "Services",
"rentals": "Locations",
"contact": "Contact",
"about": "À Propos"
}
}
}
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
};
});
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
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
messagesto 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>
);
}
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);
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>
)
}
For example:
const t = useTranslations("homepage");
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|.*\\..*).*)'
};
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)