Internationalization
It allows websites to offer content in many languages, automatically directing users to their preferred language version based on their settings or location.
It allows developers to provide exact and accurate translations instead of relying on potentially inaccurate browser translations.

There are many libraries we can use to implement internationalization in our next.js projects, but the simplest one I’ve ever worked with is next-intl, so in this tutorial, I’ll show you how to set up internationalization in your project.
In this tutorial, I’ll be using next-intl with the Next.js app router.

Install next-intl Library
Assuming you already have your next.js project setup, install the library.
npm install next-intl
Add the Dynamic Segment in the app/ Directory
The dynamic segment will help to show the webpage according to the language included in the path, for example: /es/about or en/about.

Here’s a file structure we’re going to be using:

Create the messages Directory
It’s recommended to place this directory in the root directory, this directory will contain the JSON files we’re going to use for the translations.
In this tutorial, I’ll be using three translations, so I’m going to create the following files:
- en.json — for English
- es.json — for Spanish
- fr.json — for French


Setup the next-intl Plugin
It’s important to pay attention to the following steps since a small error can prevent the website from working.
First update the next.config.js file and add the following code:
const createNextIntlPlugin = require('next-intl/plugin');
const withNextIntl = createNextIntlPlugin();
/** @type {import('next').NextConfig} */
const nextConfig = {};
module.exports = withNextIntl(nextConfig);Add the src/i18n.ts file, and make sure that the locales array contains the languages you want to implement.
import {notFound} from 'next/navigation';
import {getRequestConfig} from 'next-intl/server';
// Can be imported from a shared config
const locales = ['en', 'es', 'fr'];
export default getRequestConfig(async ({locale}) => {
// Validate that the incoming `locale` parameter is valid
if (!locales.includes(locale as any)) notFound();
return {
messages: (await import(`../messages/${locale}.json`)).default
};
});Add the src/middleware.ts again make sure to update the locales array and the matcher.
import createMiddleware from 'next-intl/middleware';
export default createMiddleware({
// A list of all locales that are supported
locales: ['en', 'es', 'fr'],
// Used when no locale matches
defaultLocale: 'en'
});
export const config = {
// Match only internationalized pathnames
matcher: ['/', '/(es|en|fr)/:path*']
};
In the [locale] dynamic segment add a layout.tsx file, and add the Intl client provider, wrap it around the children.
import {NextIntlClientProvider} from 'next-intl';
import {getMessages} from 'next-intl/server';
export default async function LocaleLayout({
children,
params: {locale}
}: {
children: React.ReactNode;
params: {locale: string};
}) {
// Providing all messages to the client
// side is the easiest way to get started
const messages = await getMessages();
return (
<html lang={locale}>
<body>
<NextIntlClientProvider messages={messages}>
{children}
</NextIntlClientProvider>
</body>
</html>
);
}Use the translation on the pages
import {useTranslations} from 'next-intl';
export default function Index() {
const t = useTranslations('Index');
return <h1>{t('title')}</h1>;
}You need to follow the regular process to add other pages, as you do when using the app router, for example, to add an About page:
Create an About Directory

And use the translations:

Congrats just like that you have a website in multiple languages.
Language Switch
Since we’re using Intl with i18n Routing, we have the option to allow the user to set their language of preference, in this example, I’ll use a use a switch located in the navbar.
We need to add a component to the navbar but first, we need to add and edit some files to make it work.
Add the config and navigation file:
src/config.ts
import { Pathnames } from "next-intl/navigation";
export const locales = ["en", "es", "fr"] as const;
export const pathnames = {
"/": "/",
"/pathnames": {
en: "/pathnames",
es: "/nombres-de-ruta",
fr: "/noms-de-chemin",
},
} satisfies Pathnames<typeof locales>;
export const localePrefix = undefined;
export type AppPathnames = keyof typeof pathnames;import { createLocalizedPathnamesNavigation } from "next-intl/navigation";
import { locales, pathnames, localePrefix } from "./config";
export const { Link, redirect, usePathname, useRouter } =
createLocalizedPathnamesNavigation({
locales,
pathnames,
localePrefix,
});
Add the Locale switcher key the messages files.


Now we can set up the switch component.
"use client";
import clsx from "clsx";
import { ChangeEvent, ReactNode, useTransition } from "react";
import { useRouter, usePathname } from "@/navigation";
import { useLocale, useTranslations } from "next-intl";
import { locales } from "@/config";
type Props = {
children: ReactNode;
defaultValue: string;
label: string;
};
export default function LocaleSwitcherSelect({}: Props) {
const router = useRouter();
const [isPending, startTransition] = useTransition();
const pathname = usePathname();
const t = useTranslations("LocaleSwitcher");
const locale = useLocale();
function onSelectChange(event: ChangeEvent<HTMLSelectElement>) {
const nextLocale = event.target.value;
startTransition(() => {
router.replace(pathname, { locale: nextLocale });
});
}
return (
<label
className={clsx(
"relative",
isPending && "transition-opacity [&:disabled]:opacity-30"
)}
>
<p className="sr-only">{t("label")}</p>
<select
className="inline-flex text-md bg-dark-blue py-1 pr-2 border border-gray rounded text-gray"
defaultValue={locale}
disabled={isPending}
onChange={onSelectChange}
>
{locales.map((cur) => (
<option key={cur} value={cur}>
{t("locale", { locale: cur })}
</option>
))}
</select>
</label>
);
}This is a client-side component that will allow us to select the language we want, in this example besides the libraries previously installed, I’m using clsx to conditionally apply the className, if you don’t have it in your project, you need to install it.
npm i clsxAnd that’s it, congrats, now you have a website in multiple languages. 🎉
Conclusion
Integrating Next-Intl for internationalization (i18n) routing in your Next.js project gives you full control when catering to a global audience. It allows you to offer content in multiple languages, ensuring precise and accurate translations, and directing users to their preferred language versions based on their settings or location. Furthermore, you can add a switch so that your users can select their language of choice.
This not only enhances user experience but also improves your website’s accessibility and search engine optimization across different language markets.
Here you can find the YouTube video.