typescriptadvanced

Internationalization (i18n) Routing

Implement locale-based routing with middleware detection and dictionary loading.

typescript
// middleware.ts
import { NextRequest, NextResponse } from 'next/server';

const locales = ['en', 'de', 'fr', 'es'];
const defaultLocale = 'en';

function getLocale(request: NextRequest): string {
  const acceptLanguage = request.headers.get('accept-language') || '';
  const preferred = acceptLanguage.split(',')[0]?.split('-')[0];
  return locales.includes(preferred) ? preferred : defaultLocale;
}

export function middleware(request: NextRequest) {
  const { pathname } = request.nextUrl;

  // Check if pathname has a locale
  const hasLocale = locales.some(
    (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`
  );

  if (hasLocale) return NextResponse.next();

  // Redirect to locale-prefixed path
  const locale = getLocale(request);
  const url = request.nextUrl.clone();
  url.pathname = `/${locale}${pathname}`;
  return NextResponse.redirect(url);
}

export const config = {
  matcher: ['/((?!api|_next/static|_next/image|favicon.ico).*)'],
};

// app/[locale]/layout.tsx
import { getDictionary } from '@/lib/dictionaries';

export async function generateStaticParams() {
  return locales.map((locale) => ({ locale }));
}

export default async function LocaleLayout({
  children,
  params,
}: {
  children: React.ReactNode;
  params: Promise<{ locale: string }>;
}) {
  const { locale } = await params;
  const dict = await getDictionary(locale);

  return (
    <html lang={locale}>
      <body>{children}</body>
    </html>
  );
}

// lib/dictionaries.ts
const dictionaries: Record<string, () => Promise<Record<string, string>>> = {
  en: () => import('@/dictionaries/en.json').then((m) => m.default),
  de: () => import('@/dictionaries/de.json').then((m) => m.default),
  fr: () => import('@/dictionaries/fr.json').then((m) => m.default),
};

export async function getDictionary(locale: string) {
  return dictionaries[locale]?.() || dictionaries.en();
}

Use Cases

  • multi-language websites
  • locale detection
  • content translation

Tags

Related Snippets

Similar patterns you can reuse in the same workflow.