Careeroza
Internationalization
Locale in the URL
Put the language in the first segment: /en/products, /hi/products. Everything goes under app/[locale]/ and middleware redirects /products to the best locale.
app/
└── [locale]/
├── layout.tsx <html lang={locale}>
├── page.tsx /en, /hi
└── products/page.tsx /en/products, /hi/products
dictionaries/
├── en.json
└── hi.jsonChoosing the Locale
Example
Middleware reads the Accept-Language header and picks the best supported locale (libraries like @formatjs/intl-localematcher do this more completely):
const SUPPORTED = ['en', 'hi', 'ta'] as const;
type Locale = (typeof SUPPORTED)[number];
const DEFAULT: Locale = 'en';
function pickLocale(acceptLanguage: string | null): Locale {
if (!acceptLanguage) return DEFAULT;
const ranked = acceptLanguage
.split(',')
.map((part) => {
const [tag, q] = part.trim().split(';q=');
return { lang: tag.toLowerCase().split('-')[0], q: q ? Number(q) : 1 };
})
.sort((a, b) => b.q - a.q);
const hit = ranked.find((r) => (SUPPORTED as readonly string[]).includes(r.lang));
return hit ? (hit.lang as Locale) : DEFAULT;
}
console.log(pickLocale('hi-IN,hi;q=0.9,en-US;q=0.8,en;q=0.7'));
console.log(pickLocale('fr-FR,fr;q=0.9,ta;q=0.5'));
console.log(pickLocale('de-DE'));
console.log(pickLocale(null));Output
hi ta en en
Dictionaries
// app/[locale]/dictionaries.ts
import 'server-only';
const dictionaries = {
en: () => import('@/dictionaries/en.json').then((m) => m.default),
hi: () => import('@/dictionaries/hi.json').then((m) => m.default),
};
export type Locale = keyof typeof dictionaries;
export const hasLocale = (l: string): l is Locale => l in dictionaries;
export const getDictionary = (locale: Locale) => dictionaries[locale]();// app/[locale]/page.tsx
import { notFound } from 'next/navigation';
import { getDictionary, hasLocale } from './dictionaries';
export default async function Home({ params }: { params: Promise<{ locale: string }> }) {
const { locale } = await params;
if (!hasLocale(locale)) notFound();
const t = await getDictionary(locale);
return <h1>{t.home.title}</h1>; // "Today's Deals" / "आज के ऑफ़र"
}
export function generateStaticParams() {
return [{ locale: 'en' }, { locale: 'hi' }];
}Tip: For bigger apps, the next-intl library adds pluralisation, number/date formatting and typed message keys on top of this pattern.