Topic 18 of 47
i18n
Overview
Internationalization (i18n) in Next.js enables building multilingual apps using URL-based locale routing. The App Router approach uses middleware for locale detection and a library like next-intl for translations.
Syntax
typescript
// middleware.ts — locale routing
import createMiddleware from "next-intl/middleware";
export default createMiddleware({
locales: ["en", "hi", "fr"],
defaultLocale: "en",
});
// app/[locale]/layout.tsx — locale layout
import { NextIntlClientProvider } from "next-intl";
import { getMessages } from "next-intl/server";
export default async function LocaleLayout({ children, params }) {
const messages = await getMessages();
return (
<html lang={params.locale}>
<body>
<NextIntlClientProvider messages={messages}>
{children}
</NextIntlClientProvider>
</body>
</html>
);
}
// Usage in component
import { useTranslations } from "next-intl";
function HomePage() {
const t = useTranslations("Home");
return <h1>{t("title")}</h1>; // "Welcome" or "स्वागत" based on locale
}Common Pitfalls
- RTL (right-to-left) languages (Arabic, Hebrew) need dir='rtl' on the html element and CSS logical properties.
- Number and date formatting should use the Intl API with the current locale — not hardcoded formats.
- Interview tip: SEO for i18n requires hreflang tags and separate sitemaps per locale — next-intl generates these automatically.
Real-World Example
Language switcher with URL-based locale switching
example
typescript
// messages/en.json
// { "Home": { "title": "Welcome", "cta": "Get Started" } }
// messages/hi.json
// { "Home": { "title": "स्वागत है", "cta": "शुरू करें" } }
"use client";
import { useLocale } from "next-intl";
import { usePathname, useRouter } from "next/navigation";
export function LanguageSwitcher() {
const locale = useLocale();
const pathname = usePathname();
const router = useRouter();
const languages = [
{ code: "en", label: "English" },
{ code: "hi", label: "हिंदी" },
{ code: "fr", label: "Français" },
];
const switchLocale = (newLocale: string) => {
// Replace locale segment in URL
const newPath = pathname.replace(`/${locale}`, `/${newLocale}`);
router.push(newPath);
};
return (
<select value={locale} onChange={e => switchLocale(e.target.value)}>
{languages.map(lang => (
<option key={lang.code} value={lang.code}>{lang.label}</option>
))}
</select>
);
}