Internationalization (i18n) in Next.js
Internationalization in Next.js
Internationalization (i18n) is the process of designing your app to support multiple languages and regions. Next.js App Router supports i18n through routing patterns and translation libraries.
Routing Strategy: Locale in URL
app/
[locale]/
page.tsx → /en, /ua, /de
about/page.tsx → /en/about, /ua/about
blog/
[slug]/page.tsx → /en/blog/hello, /ua/blog/hello
layout.tsxMiddleware for Locale Detection
tsx
// middleware.ts
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
const locales = ["en", "ua"];
const defaultLocale = "en";
function getLocale(request: NextRequest): string {
// Check cookie
const cookieLocale = request.cookies.get("locale")?.value;
if (cookieLocale && locales.includes(cookieLocale)) return cookieLocale;
// Check Accept-Language header
const acceptLanguage = request.headers.get("accept-language") || "";
for (const locale of locales) {
if (acceptLanguage.includes(locale)) return locale;
}
return defaultLocale;
}
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
// Check if pathname already has a locale
const hasLocale = locales.some(
locale => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
);
if (hasLocale) return NextResponse.next();
// Redirect to localized path
const locale = getLocale(request);
return NextResponse.redirect(
new URL(`/${locale}${pathname}`, request.url)
);
}
export const config = {
matcher: ["/((?!api|_next|favicon.ico).*)"],
};Translation Files (JSON)
json
// messages/en.json
{
"nav": {
"home": "Home",
"about": "About Us",
"contact": "Contact"
},
"hero": {
"title": "Welcome to our platform",
"description": "The best tool for developers"
}
}
// messages/ua.json
{
"nav": {
"home": "Головна",
"about": "Про нас",
"contact": "Контакти"
},
"hero": {
"title": "Ласкаво просимо на нашу платформу",
"description": "Найкращий інструмент для розробників"
}
}Using next-intl (Popular Library)
tsx
// app/[locale]/layout.tsx
import { NextIntlClientProvider } from "next-intl";
import { getMessages } from "next-intl/server";
export default async function LocaleLayout({
children,
params,
}: {
children: React.ReactNode;
params: Promise<{ locale: string }>;
}) {
const { locale } = await params;
const messages = await getMessages();
return (
<html lang={locale}>
<body>
<NextIntlClientProvider messages={messages}>
{children}
</NextIntlClientProvider>
</body>
</html>
);
}In Server Components
tsx
import { useTranslations } from "next-intl";
export default function HomePage() {
const t = useTranslations("hero");
return (
<div>
<h1>{t("title")}</h1>
<p>{t("description")}</p>
</div>
);
}In Client Components
tsx
"use client";
import { useTranslations } from "next-intl";
export function Navbar() {
const t = useTranslations("nav");
return (
<nav>
<a href="/">{t("home")}</a>
<a href="/about">{t("about")}</a>
<a href="/contact">{t("contact")}</a>
</nav>
);
}Language Switcher
tsx
"use client";
import { usePathname, useRouter } from "next/navigation";
export function LanguageSwitcher() {
const pathname = usePathname();
const router = useRouter();
const switchLocale = (newLocale: string) => {
// Replace current locale in path
const segments = pathname.split("/");
segments[1] = newLocale;
router.push(segments.join("/"));
};
return (
<div>
<button onClick={() => switchLocale("en")}>EN</button>
<button onClick={() => switchLocale("ua")}>UA</button>
</div>
);
}Key Considerations
| Aspect | Approach |
|---|---|
| Routing | [locale] dynamic segment |
| Detection | Middleware (cookie, Accept-Language header) |
| Translations | JSON files + library (next-intl, react-i18next) |
| SEO | <html lang>, alternate hreflang tags |
| Date/Number formatting | Intl API or library |
| RTL support | CSS logical properties, dir attribute |
Important:
Next.js i18n with App Router uses a [locale] segment in the URL, Middleware for automatic locale detection and redirection, and translation libraries like next-intl. Store translations in JSON files, use useTranslations() in components, and ensure proper SEO with lang attributes and hreflang tags.
Short Answer
Interview readyPremium
A concise answer to help you respond confidently on this topic during an interview.