Routing: Internationalization
A simple i18n pattern puts the language first: /en/courses, /hi/courses, /es/courses. The locale folder is a dynamic segment [locale] (or fixed folders en, hi).
Locale as a URL segment
A simple i18n pattern puts the language first: /en/courses, /hi/courses, /es/courses. The locale folder is a dynamic segment [locale] (or fixed folders en, hi).
Root redirects can send / to /en or detect Accept-Language in middleware/proxy and rewrite or redirect.
Real-life example: Locale routing is airport gates labeled EN, HI, ES — same flight (page), different language channel.
app/
[locale]/
layout.tsx # lang attribute, locale nav
page.tsx # /en /hi
courses/
page.tsx # /en/courses /hi/coursesBasic i18n routing pattern
Validate the locale. If someone visits /xx/courses with an unknown code, redirect to a default language. Pass locale into dictionaries or next-intl / similar libraries later — start with folders and a simple messages map.
Real-life example: Validating locale is checking the passport language stamp before handing out the correct guidebook.
import { notFound } from "next/navigation";
import type { ReactNode } from "react";
const locales = ["en", "hi", "es"] as const;
type Props = {
children: ReactNode;
params: Promise<{ locale: string }>;
};
export default async function LocaleLayout({ children, params }: Props) {
const { locale } = await params;
if (!locales.includes(locale as (typeof locales)[number])) {
notFound();
}
return (
<div lang={locale}>
<header>Rishtaara · {locale.toUpperCase()}</header>
{children}
</div>
);
}import { NextResponse } from "next/server";
import type { NextRequest } from "next/server";
const locales = ["en", "hi", "es"];
const defaultLocale = "en";
export function middleware(request: NextRequest) {
const { pathname } = request.nextUrl;
const hasLocale = locales.some(
(l) => pathname === `/${l}` || pathname.startsWith(`/${l}/`)
);
if (hasLocale) return NextResponse.next();
// /courses → /en/courses
const url = request.nextUrl.clone();
url.pathname = `/${defaultLocale}${pathname}`;
return NextResponse.redirect(url);
}
export const config = {
matcher: ["/((?!_next|api|favicon.ico).*)"],
};const messages = {
en: { hello: "Hello learner" },
hi: { hello: "नमस्ते learner" },
es: { hello: "Hola learner" },
} as const;
export function t(locale: keyof typeof messages, key: "hello") {
return messages[locale][key];
}