May 26, 2024

Md. Saad

Internationalization (often abbreviated as i18n) in the context of websites refers to designing and developing a website to be easily adapted to various languages and regions without requiring engineering changes. This involves creating a framework that supports multiple languages, date formats, currency formats, and other locale-specific elements. By internationalizing a website, we can make it accessible to a global audience, which can help expand your reach and improve user experience for visitors from different regions.
Internationalization (i18n) in Next.js applications involves translating your app's content and routing based on the user's preferred language. In this tutorial, we will discuss the step-by-step guide on how to set up i18n with Next.js app router.
To follow along with this guide and code, you should have the following:
The first and foremost step to begin with is creating a Next.js App. Use the following command to create a next.js app.
npx create-next-app app-name
As it is not an article on creating the next.js app, we will not discuss it in detail. If you want to read it in detail follow the link.
Let’s install the following dependencies using the dev tools
npm install negotiator
npm install @formatjs/intl-localematcherMiddleware allows you to run code before a request is completed. Then, based on the incoming request, you can modify the response by rewriting, redirecting, modifying the request or response headers, or responding directly. We will use Middleware to automatically detect user language preferences and also user’s manually used languages.
Now create a middleware.js at the root of the project and add the following codes:
// middleware.js
import {match} from "@formatjs/intl-localematcher";
import Negotiator from "negotiator";
import {NextResponse} from "next/server";
const defaultLocale = "en";
const locales = ["en", "de", "cs"];
function getLocale(request) {
const acceptedLanguage = request.headers.get("accept-language") ?? undefined;
const headers = {"accept-language": acceptedLanguage};
const languages = new Negotiator({headers}).languages();
return match(languages, locales, defaultLocale); // en,de or cs
}
export function middleware(request) {
// get the pathname from request url
const pathname = request.nextUrl.pathname;
const pathNameIsMissingLocale = locales.every(
(locale) => !pathname.startsWith(`/${locale}`) && !pathname.startsWith(`/${locale}/`)
);
if (pathNameIsMissingLocale) {
// detect user's preference & redirect with a locale with a path eg: /en/about
const locale = getLocale(request);
return NextResponse.redirect(new URL(`/${locale}/${pathname}`, request.url));
}
return NextResponse.next();
}
export const config = {
matcher: ["/((?!_next).*)"],
};These codes will prioritise user language preference from the browser setting and show the site in that language. However, users can manually go to the also.
We can then create a getDictionary function to load the translations for the requested locale.
app/[lang]/dictionaries/dictionaries.jsx
import "server-only";
// We also get the default import for cleaner types
const dictionaries = {
en: () => import("./en.json").then((module) => module.default),
de: () => import("./de.json").then((module) => module.default),
cs: () => import("./cs.json").then((module) => module.default),
};
export const getDictionary = async (locale) => dictionaries[locale]();// dictionaries/en.json
{
"welcome": "Welcome",
"contact": "Contact Us"
}
// dictionaries/nl.json
{
"welcome": "Vítejte",
"contact": "Neem contact met ons op"
}
// dictionaries/de.json
{
"welcome": "Willkommen",
"contact": "Kontact os"
}Finally, we have to configure the app router.
import "../globals.css";
import {Inter} from "next/font/google";
const inter = Inter({subsets: ["latin"]});
export const metadata = {
title: "Create Next App",
description: "Generated by create next app",
};
export async function generateStaticParams() {
return i18n.locales.map((locale) => ({lang: locale}));
}
export default function RootLayout({children, params}) {
return (
<html lang={params.lang}>
<body className={inter.className}>{children}</body>
</html>
);
}// app/[lang]/page.jsx
import {getDictionary} from "./dictionaries/dictionaries";
export default async function IndexPage({params: {lang}}) {
const dictionary = await getDictionary(lang);
return (
<div>
<div className="max-w-[1400px] py-2 flex justify-center px-5 mx-auto text-center">
<div className="bg-slate-200 shadow-md py-5 px-5 w-1/3 rounded-md">
<h1 className="font-bold">Current locale: {lang}</h1>
<p>This text is rendered on the server: {dictionary.welcome}</p>
</div>
</div>
</div>
);
}// app/[lang]/contact/page.js
import {getDictionary} from "../dictionaries/dictionaries";
export default async function Contact({params: {lang}}) {
const dictionary = await getDictionary(lang);
return (
<div>
<div className="max-w-[1400px] py-2 flex justify-center px-5 mx-auto text-center">
<div className="bg-slate-200 shadow-md py-5 px-5 w-1/3 rounded-md">
<h1 className="font-bold font-2xl">Current locale: {lang}</h1>
<p>This text is rendered on the server: {dictionary.contact}</p>
</div>
</div>
</div>
);
}This will create a simple contact page with the given languages.
npm run devIf your browser's preferred language is set to English then The site will be run on localhost:3000/en by default and go to the contact page using the localhost:3000/en/contact. Moreover, whenever we change the language to the preferred language it will be redirected to the translated version.
Congratulations, we have successfully added i18n to our next.js site. For more details, you can visit the official next.js
Want to create a multilingual site? Contact Staticmania. Our dedicated team will guide you to your journey.