Introduction
Internationalization is a fundamental requirement for modern web applications. Next.js has evolved its i18n support from an integrated routing system in Pages Router to a flexible middleware-based approach in App Router. This article covers routing strategies, locale detection, content management, and SEO optimization for multi-language Next.js applications.
The key concepts are locales, default locale, locale detection, and the trade-offs between sub-path routing and domain routing. The App Router approach offers more flexibility but requires more manual setup than its predecessor.
Routing Strategies — Sub-path vs Domain
The choice between sub-path and domain routing has significant implications for SEO and infrastructure.
| Criteria | Sub-path (/en/about) | Domain (en.example.com) |
|---|---|---|
| SEO authority | Shared domain authority | Independent per locale |
| Geo-targeting | Weaker signals | Stronger signals |
| Infrastructure | Single deployment | Multiple deployments |
| Analytics | Unified | Segmented |
For most projects, sub-path routing is recommended due to its simplicity and shared domain authority. Implementation in App Router uses middleware to handle locale prefix rewriting.
import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
const locales = ["en", "ja", "fr"];
const defaultLocale = "en";
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
const pathnameHasLocale = locales.some(
(locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
);
if (pathnameHasLocale) return;
const locale = request.cookies.get("NEXT_LOCALE")?.value || defaultLocale;
return NextResponse.redirect(new URL(`/${locale}${pathname}`, request.url));
}
export const config = {
matcher: ["/((?!api|_next|favicon.ico).*)"],
};
Locale Detection and Negotiation
Next.js determines the locale through a negotiation chain: cookie to Accept-Language header to default locale. The NEXT_LOCALE cookie convention allows users to persist their language preference.
function getLocale(request: NextRequest): string {
const cookieLocale = request.cookies.get("NEXT_LOCALE")?.value;
if (cookieLocale && locales.includes(cookieLocale)) return cookieLocale;
const acceptLanguage = request.headers.get("accept-language");
if (acceptLanguage) {
const preferred = acceptLanguage
.split(",")
.map((lang) => lang.split(";")[0].trim())
.find((lang) => locales.includes(lang));
if (preferred) return preferred;
}
return defaultLocale;
}
This negotiation logic ensures users always see content in their preferred language while allowing manual overrides.
Content Management with next-intl
next-intl is the recommended library for App Router i18n. It provides type-safe translation keys, namespace splitting, and efficient message loading.
// middleware.ts
import createMiddleware from "next-intl/middleware";
export default createMiddleware({ locales: ["en", "ja"], defaultLocale: "en" });
export const config = { matcher: ["/((?!api|_next).*)"] };
Translation files are organized by namespace for maintainability:
{
"navigation": { "home": "Home", "about": "About" },
"home": { "title": "Welcome", "description": "This is the home page" }
}
Components consume translations with the useTranslations hook:
import { useTranslations } from "next-intl";
function HomePage() {
const t = useTranslations("home");
return <h1>{t("title")}</h1>;
}
SEO for Multi-Language Sites
Cross-language SEO requires proper hreflang tags and canonical URLs. Each page should include self-referencing and alternate hreflang links.
export async function generateMetadata({ params }: Props) {
const { locale } = await params;
return {
alternates: {
languages: {
en: "/en/about",
ja: "/ja/about",
"x-default": "/en/about",
},
},
};
}
Common SEO pitfalls include duplicate content across locales, missing hreflang return links, and inconsistent locale URL patterns. Google’s guidelines require each language version to link back to all other language versions.
Conclusion
For most projects, sub-path routing with next-intl provides the best balance of simplicity, SEO, and developer experience. Invest in proper locale detection middleware, organize translation files by namespace, and implement complete hreflang metadata. The official Next.js i18n examples and next-intl documentation are excellent resources for production implementations.
