Implementing Multilingual Support in Next.js with next-intl
A guide to implementing efficient multilingual support in a Next.js App Router project using next-intl, including directory structure, routing, language switching, and best practices.
next-intl
is an internationalization (i18n) library designed specifically for the Next.js App Router. It is lightweight, easy to use, and deeply integrated with the App Router.
1. Installation
Install next-intl
using pnpm
.
2. Configuration Overview
Recommended Directory Structure
messages/
: Stores translation files such asen.json
andzh.json
src/i18n/
: Contains language-related configuration and helper functions
Steps to Set Up Multilingual Support
-
Integrate the
next-intl
plugin innext.config.js
import type { NextConfig } from "next"; import createNextIntlPlugin from "next-intl/plugin"; const nextConfig: NextConfig = {}; const withNextIntl = createNextIntlPlugin(); export default withNextIntl(nextConfig);
-
Use TypeScript files like
en.ts
andzh.ts
instead of JSON for translation content to improve flexibility -
Create a
getI18n
function to dynamically load translation objects// src/i18n/index.ts import { en } from "./en"; import { zh } from "./zh"; const locales = { zh, en }; export function getI18n(locale: string) { return locales[locale as keyof typeof locales] || locales["en"]; }
-
Define supported locales and routing settings in
routing.ts
// src/i18n/routing.ts import { defineRouting } from "next-intl/routing"; export const routing = defineRouting({ // A list of all locales that are supported locales: ["en", "zh"], // Used when no locale matches defaultLocale: "en", localeDetection: false, });
-
Load language messages dynamically in
request.ts
based on the incoming locale// src/i18n/request.ts import { hasLocale } from "next-intl"; import { getRequestConfig } from "next-intl/server"; 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, }; });
-
Wrap navigation methods like
Link
innavigation.ts
to automatically handle locale prefixes// src/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);
Locale-Aware Layout
In src/app/[locale]/layout.tsx
, set the <html lang="xx">
attribute according to the current locale and wrap the app with NextIntlClientProvider
.
// src/app/[locale]/layout.tsx
import { routing } from "@/i18n/routing";
import { NextIntlClientProvider, hasLocale } from "next-intl";
import { notFound } from "next/navigation";
export default async function LocaleLayout({
children,
params,
}: {
children: React.ReactNode;
params: Promise<{ locale: string }>;
}) {
const { locale } = await params;
if (!hasLocale(routing.locales, locale)) {
notFound();
}
return (
<html lang={locale}>
<body
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
<NextIntlClientProvider>{children}</NextIntlClientProvider>
</body>
</html>
);
}
3. Using Translations
-
Use
useTranslations
in components to access localized stringsimport {useTranslations} from 'next-intl'; function About() { const t = useTranslations('About'); return <h1>{t('title')}</h1>; }
-
For complex content structures, use
getI18n(locale)
to load translation objectsimport { useLocale } from "next-intl"; export default function Header() { const locale = useLocale(); const t = getI18n(locale); } return ( <div> {t.services.items.map((service, index) => ( <Card key={index}> {service.description} </Card> ))} </div> )
-
The custom
Link
component automatically handles language-prefixed routesimport { Link } from "@/i18n/navigation"; <Link href={posts[0].url} className="inline-flex items-center px-6 py-3 rounded-lg bg-blue-600 text-white font-medium hover:bg-blue-700 transition-colors" > Read More → </Link>
With this setup, next-intl
provides a clean and scalable solution for adding multilingual support to Next.js applications.