Back to list
ngxtm

nextjs-internationalization-i18n

by ngxtm

0🍴 0📅 Jan 23, 2026

SKILL.md


name: Next.js Internationalization (i18n) description: Best practices for multi-language handling, locale routing, and detection middleware. metadata: labels: [nextjs, i18n, middleware, routing] triggers: files: ['middleware.ts', 'app/[lang]/**', 'messages/*.json'] keywords: [i18n, locale, translation, next-intl, redirect]

Internationalization (i18n)

Priority: P2 (MEDIUM)

Use Sub-path Routing (/en, /de) and Server Components for translations.

Principles

  1. Sub-path Routing: Use URL segments (e.g., app/[lang]/page.tsx) to manage locales.
    • Why: SEO friendly, sharable, and cacheable.
  2. Server-Side Translation: Load dictionary files (en.json) in Server Components.
    • Why: Reduces client bundle size. No huge JSON blobs sent to browser.
  3. Middleware Detection: Use middleware.ts to detect Accept-Language headers and redirect users to their preferred locale.
  4. Type Safety: Use robust typing for translation keys to prevent broken text UI.

Implementation Pattern

1. Directory Structure

app/
├── [lang]/             # Dynamic Locale Segment
│   ├── layout.tsx      # <html lang={params.lang}>
│   └── page.tsx
└── api/
messages/               # Translation Dictionaries
├── en.json
└── es.json
middleware.ts           # Locale Redirection

2. Middleware (middleware.ts)

Redirect root traffic (/) to localized traffic (/en).

import { NextResponse } from 'next/server';
import type { NextRequest } from 'next/server';
import { match } from '@formatjs/intl-localematcher';
import Negotiator from 'negotiator';

const LOCALES = ['en', 'es', 'fr'];
const DEFAULT = 'en';

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

  // 1. Check if path already has locale
  const pathnameHasLocale = LOCALES.some(
    (locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`,
  );
  if (pathnameHasLocale) return;

  // 2. Detect locale (Header matching)
  const headers = {
    'accept-language': request.headers.get('accept-language') || '',
  };
  const languages = new Negotiator({ headers }).languages();
  const locale = match(languages, LOCALES, DEFAULT);

  // 3. Redirect
  request.nextUrl.pathname = `/${locale}${pathname}`;
  return NextResponse.redirect(request.nextUrl);
}

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

3. Server Component Usage

// app/[lang]/page.tsx

// 1. Dynamic Params ensure static generation works for each language
export async function generateStaticParams() {
  return [{ lang: 'en' }, { lang: 'es' }];
}

export default async function Page({ params: { lang } }) {
  // 2. Load Dictionary On Demand
  const dict = await getDictionary(lang);

  return <h1>{dict.home.title}</h1>;
}

Redirect Handling Strategy

When handling redirects (Authentication, Legacy URLs) in an i18n app:

  • Always preserve locale: Use redirect(/${lang}/login) instead of just /login.
  • Server Actions: Return redirect(...) from actions.
  • Next Config: Use next.config.js for legacy SEO 301s (e.g., old-site /about-us -> /en/about).

Score

Total Score

50/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

0/10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

Reviews

💬

Reviews coming soon