web-i18n-next-intl
Next.js App Routerで、型安全な国際化(i18n)を実現し、多言語対応のWebアプリケーション開発を効率的に進めることができるSkill。
📜 元の英語説明(参考)
Type-safe i18n for Next.js App Router
🇯🇵 日本人クリエイター向け解説
Next.js App Routerで、型安全な国際化(i18n)を実現し、多言語対応のWebアプリケーション開発を効率的に進めることができるSkill。
※ jpskill.com 編集部が日本のビジネス現場向けに補足した解説です。Skill本体の挙動とは独立した参考情報です。
下記のコマンドをコピーしてターミナル(Mac/Linux)または PowerShell(Windows)に貼り付けてください。 ダウンロード → 解凍 → 配置まで全自動。
mkdir -p ~/.claude/skills && cd ~/.claude/skills && curl -L -o web-i18n-next-intl.zip https://jpskill.com/download/10287.zip && unzip -o web-i18n-next-intl.zip && rm web-i18n-next-intl.zip
$d = "$env:USERPROFILE\.claude\skills"; ni -Force -ItemType Directory $d | Out-Null; iwr https://jpskill.com/download/10287.zip -OutFile "$d\web-i18n-next-intl.zip"; Expand-Archive "$d\web-i18n-next-intl.zip" -DestinationPath $d -Force; ri "$d\web-i18n-next-intl.zip"
完了後、Claude Code を再起動 → 普通に「動画プロンプト作って」のように話しかけるだけで自動発動します。
💾 手動でダウンロードしたい(コマンドが難しい人向け)
- 1. 下の青いボタンを押して
web-i18n-next-intl.zipをダウンロード - 2. ZIPファイルをダブルクリックで解凍 →
web-i18n-next-intlフォルダができる - 3. そのフォルダを
C:\Users\あなたの名前\.claude\skills\(Win)または~/.claude/skills/(Mac)へ移動 - 4. Claude Code を再起動
⚠️ ダウンロード・利用は自己責任でお願いします。当サイトは内容・動作・安全性について責任を負いません。
🎯 このSkillでできること
下記の説明文を読むと、このSkillがあなたに何をしてくれるかが分かります。Claudeにこの分野の依頼をすると、自動で発動します。
📦 インストール方法 (3ステップ)
- 1. 上の「ダウンロード」ボタンを押して .skill ファイルを取得
- 2. ファイル名の拡張子を .skill から .zip に変えて展開(macは自動展開可)
- 3. 展開してできたフォルダを、ホームフォルダの
.claude/skills/に置く- · macOS / Linux:
~/.claude/skills/ - · Windows:
%USERPROFILE%\.claude\skills\
- · macOS / Linux:
Claude Code を再起動すれば完了。「このSkillを使って…」と話しかけなくても、関連する依頼で自動的に呼び出されます。
詳しい使い方ガイドを見る →- 最終更新
- 2026-05-18
- 取得日時
- 2026-05-18
- 同梱ファイル
- 1
📖 Skill本文(日本語訳)
※ 原文(英語/中国語)を Gemini で日本語化したものです。Claude 自身は原文を読みます。誤訳がある場合は原文をご確認ください。
next-intl 国際化パターン
クイックガイド: Next.js App Router でタイプセーフな国際化を行うには、next-intl を使用します。メッセージには
useTranslations、日付/数値にはuseFormatter、ロケール検出には middleware を使用します。静的レンダリングにはsetRequestLocale(locale)を呼び出します。
<critical_requirements>
重要: この Skill を使用する前に
すべてのコードは CLAUDE.md のプロジェクト規約に従う必要があります (kebab-case, 名前付きエクスポート, インポート順,
import type, 名前付き定数)
(静的レンダリングのため、すべてのページ/レイアウトコンポーネントの先頭で setRequestLocale(locale) を呼び出す必要があります)
(使用する前に、routing.locales に対してロケールを検証する必要があります)
(クライアントサイドのフックを有効にするには、ルートレイアウトで NextIntlClientProvider を使用する必要があります)
(ロケールコードには名前付き定数を使用する必要があります - インラインのロケール文字列は使用しないでください)
</critical_requirements>
自動検出: next-intl, useTranslations, useFormatter, useLocale, NextIntlClientProvider, i18n routing, ロケール検出, ICU message format
使用する場面:
- Next.js App Router で国際化を実装する場合
- 補間と複数形化を使用してローカライズされたメッセージをレンダリングする場合
- ロケールごとに日付、数値、相対時間をフォーマットする場合
- ロケールベースのルーティングと middleware を設定する場合
- 複数のロケールに対して静的ページを生成する場合
カバーする主要なパターン:
- routing.ts, request.ts, middleware を使用したプロジェクトのセットアップ
- ICU 構文を使用したメッセージ用の useTranslations フック
- 日付、数値、リスト用の useFormatter フック
- generateStaticParams と setRequestLocale を使用した静的レンダリング
- タイプセーフな翻訳キーのための TypeScript 統合
使用しない場面:
- シンプルな単一ロケールアプリケーション (i18n の複雑さを避ける)
- Pages Router (異なる API - Pages Router のドキュメントを使用)
- Next.js 以外の React アプリケーション (代わりに react-intl を使用)
詳細なリソース:
- コード例については、examples/ (core.md, formatting.md, pluralization.md, markup.md) を参照してください。
- 意思決定フレームワークとアンチパターンについては、reference.md を参照してください。
<philosophy>
哲学
next-intl は、ICU message format をサポートした タイプセーフなロケール対応レンダリング の原則に従います。翻訳は名前空間付きの JSON オブジェクトとして編成され、Server Components 用にリクエストごとにロードされ、Client Components 用にコンテキストを介して提供されます。middleware はロケール検出を自動的に処理し、setRequestLocale はビルド時の静的レンダリングを可能にします。
コア原則:
- Server-first: パフォーマンス向上のため、Server Components で翻訳をロードします
- タイプセーフなキー: TypeScript の拡張により、コンパイル時に翻訳の欠落を検出します
- ICU 標準: 複数形化とフォーマットには業界標準の ICU message syntax を使用します
- 静的フレンドリー: 明示的なロケールパラメータを使用した静的生成をサポートします
</philosophy>
<patterns>
コアパターン
パターン 1: プロジェクトのセットアップ
標準的なファイル構造を使用して、App Router で next-intl をセットアップします。
ファイル構造
src/
i18n/
routing.ts # ロケール構成
request.ts # サーバー側のロケール解決
navigation.ts # ロケール対応の Link, useRouter
proxy.ts # ロケール検出とルーティング (Next.js 16 より前の middleware.ts)
app/
[locale]/
layout.tsx # NextIntlClientProvider を使用したルートレイアウト
page.tsx # ロケールセグメント内のページ
messages/
en.json # 英語の翻訳
de.json # ドイツ語の翻訳
注: Next.js 16 以降では、
middleware.tsはproxy.tsに名前が変更されました。Next.js 15 以前を使用している場合は、middleware.tsを使用してください。
構成ファイル
// src/i18n/routing.ts
import { defineRouting } from "next-intl/routing";
export const SUPPORTED_LOCALES = ["en", "de", "fr"] as const;
export const DEFAULT_LOCALE = "en";
export const routing = defineRouting({
locales: SUPPORTED_LOCALES,
defaultLocale: DEFAULT_LOCALE,
});
export type Locale = (typeof routing.locales)[number];
良い点: ロケール用の名前付き定数により、アプリ全体でタイプセーフな使用が可能になり、エクスポートされた Locale 型により、ロケールパラメータの型チェックが可能になります。
// src/i18n/request.ts
import { getRequestConfig } from "next-intl/server";
import { hasLocale } from "next-intl";
import { routing } from "./routing";
export default getRequestConfig(async ({ requestLocale }) => {
const requested = await requestLocale;
const locale = hasLocale(routing.locales, requested)
? requested
: routing.defaultLocale;
return {
locale,
messages: (await import(`../../messages/${locale}.json`)).default,
};
});
良い点: サポートされているリストに対してロケールを検証し、無効なロケールに対してデフォルトにフォールバックし、必要な翻訳ファイルのみを動的にインポートします。
// src/i18n/navigation.ts
import { createNavigation } from "next-intl/navigation";
import { routing } from "./routing";
export const { Link, redirect, usePathname, useRouter, getPathname } =
createNavigation(routing);
良い点: Next.js のナビゲーション API をロケール対応でラップし、Link はロケールプレフィックスを自動的に含めます。
// src/proxy.ts (Next.js 16+) or src/middleware.ts (Next.js 15 and earlier)
import createMiddleware from "next-intl/middleware";
import { routing } from "./i18n/routing";
export default createMiddleware(routing);
export const config = {
matcher: "/((?!api|_next|_vercel|.*\\..*).*)",
};
良い点: proxy/middleware は、URL、Cookie、および Accept-Language ヘッダーからロケールを検出します。matcher は、API ルートと静的ファイルを除外します。必要に応じて、API フレームワークのルートの追加の除外を追加します。
パターン 2: プロバイダーを使用したルートレイアウト
NextIntlClientProvider でアプリケーションをラップし、ロケールを検証します。
// src/app/[locale]/layout.tsx
import { NextIntlClientProvider, hasLocale } from "next-intl";
import { notFound } from "next/navigation";
import { getMessages, setRequestLocale } from "next-intl/server";
import { routing,
(原文がここで切り詰められています) 📜 原文 SKILL.md(Claudeが読む英語/中国語)を展開
next-intl Internationalization Patterns
Quick Guide: Use next-intl for type-safe internationalization in Next.js App Router.
useTranslationsfor messages,useFormatterfor dates/numbers, middleware for locale detection. CallsetRequestLocale(locale)for static rendering.
<critical_requirements>
CRITICAL: Before Using This Skill
All code must follow project conventions in CLAUDE.md (kebab-case, named exports, import ordering,
import type, named constants)
(You MUST call setRequestLocale(locale) at the top of ALL page/layout components for static rendering)
(You MUST validate locale against routing.locales before using it)
(You MUST use NextIntlClientProvider in the root layout to enable client-side hooks)
(You MUST use named constants for locale codes - NO inline locale strings)
</critical_requirements>
Auto-detection: next-intl, useTranslations, useFormatter, useLocale, NextIntlClientProvider, i18n routing, locale detection, ICU message format
When to use:
- Implementing internationalization in Next.js App Router
- Rendering localized messages with interpolation and pluralization
- Formatting dates, numbers, and relative time per locale
- Setting up locale-based routing and middleware
- Generating static pages for multiple locales
Key patterns covered:
- Project setup with routing.ts, request.ts, and middleware
- useTranslations hook for messages with ICU syntax
- useFormatter hook for dates, numbers, and lists
- Static rendering with generateStaticParams and setRequestLocale
- TypeScript integration for type-safe translation keys
When NOT to use:
- Simple single-locale applications (skip i18n complexity)
- Pages Router (different API - use Pages Router docs)
- Non-Next.js React applications (use react-intl instead)
Detailed Resources:
- For code examples, see examples/ (core.md, formatting.md, pluralization.md, markup.md)
- For decision frameworks and anti-patterns, see reference.md
<philosophy>
Philosophy
next-intl follows the principle of type-safe, locale-aware rendering with ICU message format support. Translations are organized as namespaced JSON objects, loaded per-request for Server Components and provided via context for Client Components. The middleware handles locale detection automatically, while setRequestLocale enables static rendering at build time.
Core principles:
- Server-first: Load translations in Server Components for better performance
- Type-safe keys: TypeScript augmentation catches missing translations at compile time
- ICU standard: Use industry-standard ICU message syntax for pluralization and formatting
- Static-friendly: Support static generation with explicit locale parameters
</philosophy>
<patterns>
Core Patterns
Pattern 1: Project Setup
Set up next-intl with the App Router using the standard file structure.
File Structure
src/
i18n/
routing.ts # Locale configuration
request.ts # Server-side locale resolution
navigation.ts # Locale-aware Link, useRouter
proxy.ts # Locale detection and routing (middleware.ts before Next.js 16)
app/
[locale]/
layout.tsx # Root layout with NextIntlClientProvider
page.tsx # Pages within locale segment
messages/
en.json # English translations
de.json # German translations
Note: In Next.js 16+,
middleware.tswas renamed toproxy.ts. If using Next.js 15 or earlier, usemiddleware.ts.
Configuration Files
// src/i18n/routing.ts
import { defineRouting } from "next-intl/routing";
export const SUPPORTED_LOCALES = ["en", "de", "fr"] as const;
export const DEFAULT_LOCALE = "en";
export const routing = defineRouting({
locales: SUPPORTED_LOCALES,
defaultLocale: DEFAULT_LOCALE,
});
export type Locale = (typeof routing.locales)[number];
Why good: named constants for locales enable type-safe usage throughout app, exported Locale type enables type checking of locale parameters
// src/i18n/request.ts
import { getRequestConfig } from "next-intl/server";
import { hasLocale } from "next-intl";
import { routing } from "./routing";
export default getRequestConfig(async ({ requestLocale }) => {
const requested = await requestLocale;
const locale = hasLocale(routing.locales, requested)
? requested
: routing.defaultLocale;
return {
locale,
messages: (await import(`../../messages/${locale}.json`)).default,
};
});
Why good: validates locale against supported list, falls back to default for invalid locales, dynamically imports only needed translation file
// src/i18n/navigation.ts
import { createNavigation } from "next-intl/navigation";
import { routing } from "./routing";
export const { Link, redirect, usePathname, useRouter, getPathname } =
createNavigation(routing);
Why good: wraps Next.js navigation APIs with locale awareness, Link automatically includes locale prefix
// src/proxy.ts (Next.js 16+) or src/middleware.ts (Next.js 15 and earlier)
import createMiddleware from "next-intl/middleware";
import { routing } from "./i18n/routing";
export default createMiddleware(routing);
export const config = {
matcher: "/((?!api|_next|_vercel|.*\\..*).*)",
};
Why good: proxy/middleware handles locale detection from URL, cookies, and Accept-Language header, matcher excludes API routes and static files. Add additional exclusions for your API framework routes as needed.
Pattern 2: Root Layout with Provider
Wrap the application with NextIntlClientProvider and validate the locale.
// src/app/[locale]/layout.tsx
import { NextIntlClientProvider, hasLocale } from "next-intl";
import { notFound } from "next/navigation";
import { getMessages, setRequestLocale } from "next-intl/server";
import { routing, type Locale } from "@/i18n/routing";
type Props = {
children: React.ReactNode;
params: Promise<{ locale: string }>;
};
export function generateStaticParams() {
return routing.locales.map((locale) => ({ locale }));
}
export default async function LocaleLayout({ children, params }: Props) {
const { locale } = await params;
if (!hasLocale(routing.locales, locale)) {
notFound();
}
setRequestLocale(locale);
const messages = await getMessages();
return (
<html lang={locale}>
<body>
<NextIntlClientProvider messages={messages}>
{children}
</NextIntlClientProvider>
</body>
</html>
);
}
Why good: validates locale and returns 404 for invalid locales, setRequestLocale enables static rendering, generateStaticParams pre-renders all locale variants, explicit messages prop ensures Client Components receive translations, html lang attribute improves accessibility
Note: In next-intl v4.0+,
NextIntlClientProviderauto-inherits messages from server config. Passingmessagesexplicitly is optional but recommended for clarity.
Pattern 3: useTranslations Hook
Use the useTranslations hook for rendering localized messages.
Basic Usage
// src/app/[locale]/about/page.tsx
import { useTranslations } from "next-intl";
import { setRequestLocale } from "next-intl/server";
type Props = {
params: Promise<{ locale: string }>;
};
export default async function AboutPage({ params }: Props) {
const { locale } = await params;
setRequestLocale(locale);
const t = useTranslations("About");
return (
<article>
<h1>{t("title")}</h1>
<p>{t("description")}</p>
</article>
);
}
// messages/en.json
{
"About": {
"title": "About Us",
"description": "Learn more about our company."
}
}
Why good: namespaced translations keep messages organized, setRequestLocale at top of component enables static rendering
With Interpolation
const t = useTranslations("Profile");
// Message: "Hello, {name}!"
t("greeting", { name: user.name }); // "Hello, Jane!"
// Message: "You have {count} unread messages"
t("unreadCount", { count: messages.length }); // "You have 5 unread messages"
Why good: named placeholders are explicit and refactorable, TypeScript can validate placeholder names with augmentation
Pattern 4: Pluralization with ICU Syntax
Use ICU plural syntax for count-based messages.
const t = useTranslations("Notifications");
// Renders correct plural form based on locale rules
t("itemCount", { count: items.length });
// messages/en.json
{
"Notifications": {
"itemCount": "{count, plural, =0 {No items} one {# item} other {# items}}"
}
}
Plural categories by language:
- English:
one,other - Russian:
one,few,many,other - Arabic:
zero,one,two,few,many,other
Why good: ICU plural syntax handles locale-specific plural rules automatically, # is replaced with the formatted count
Ordinal Pluralization
// Message: "It's your {year, selectordinal, one {#st} two {#nd} few {#rd} other {#th}} birthday!"
t("birthday", { year: 21 }); // "It's your 21st birthday!"
Pattern 5: Rich Text Formatting
Use t.rich() for messages containing markup.
const t = useTranslations("Legal");
const content = t.rich("terms", {
link: (chunks) => <a href="/terms">{chunks}</a>,
bold: (chunks) => <strong>{chunks}</strong>,
});
return <p>{content}</p>;
{
"Legal": {
"terms": "By signing up, you agree to our <link>Terms of Service</link> and <bold>Privacy Policy</bold>."
}
}
Why good: keeps translation strings complete and translatable, markup tags are defined by developers and can be React components
Pattern 6: useFormatter Hook
Use useFormatter for locale-aware formatting of dates, numbers, and lists.
Date and Time Formatting
import { useFormatter } from "next-intl";
function EventDate({ date }: { date: Date }) {
const format = useFormatter();
return (
<time dateTime={date.toISOString()}>
{format.dateTime(date, {
year: "numeric",
month: "long",
day: "numeric",
})}
</time>
);
}
Why good: uses Intl.DateTimeFormat under the hood, respects locale-specific date formats automatically
Relative Time with useNow
import { useFormatter, useNow } from "next-intl";
const UPDATE_INTERVAL_MS = 60000;
function RelativeTime({ date }: { date: Date }) {
const format = useFormatter();
const now = useNow({ updateInterval: UPDATE_INTERVAL_MS });
return <time>{format.relativeTime(date, now)}</time>;
}
Why good: useNow provides a reactive "now" value that updates on interval, relative time updates automatically
Number and Currency Formatting
import { useFormatter } from "next-intl";
function Price({ amount, currency }: { amount: number; currency: string }) {
const format = useFormatter();
return (
<span>
{format.number(amount, {
style: "currency",
currency,
})}
</span>
);
}
Why good: handles locale-specific number formatting (1,234.56 vs 1.234,56), currency symbols and positions vary by locale
Pattern 7: Static Rendering with generateStaticParams
Enable static generation for all locale variants.
// src/app/[locale]/blog/[slug]/page.tsx
import { setRequestLocale } from "next-intl/server";
import { routing } from "@/i18n/routing";
type Props = {
params: Promise<{ locale: string; slug: string }>;
};
export function generateStaticParams() {
const slugs = ["getting-started", "advanced-features", "faq"];
return routing.locales.flatMap((locale) =>
slugs.map((slug) => ({ locale, slug })),
);
}
export default async function BlogPost({ params }: Props) {
const { locale, slug } = await params;
setRequestLocale(locale);
// Component implementation
}
Why good: generates all combinations of locales and slugs at build time, setRequestLocale enables next-intl to work in static context
Pattern 8: Locale Switching
Implement a locale switcher component using next-intl navigation.
"use client";
import { useLocale } from "next-intl";
import { usePathname, useRouter } from "@/i18n/navigation";
import { routing, type Locale } from "@/i18n/routing";
const LOCALE_LABELS: Record<Locale, string> = {
en: "English",
de: "Deutsch",
fr: "Francais",
};
export function LocaleSwitcher() {
const locale = useLocale();
const router = useRouter();
const pathname = usePathname();
const handleChange = (newLocale: Locale) => {
router.replace(pathname, { locale: newLocale });
};
return (
<select
value={locale}
onChange={(e) => handleChange(e.target.value as Locale)}
aria-label="Select language"
>
{routing.locales.map((loc) => (
<option key={loc} value={loc}>
{LOCALE_LABELS[loc]}
</option>
))}
</select>
);
}
Why good: uses next-intl navigation APIs to preserve current path, aria-label provides accessibility, type-safe locale handling
Pattern 9: TypeScript Integration
Enable type-safe translation keys with TypeScript augmentation using the AppConfig interface (next-intl v4.0+).
Configuration
// src/i18n/types.ts (or global.d.ts)
import type en from "../../messages/en.json";
import { routing } from "./routing";
import type { formats } from "./request";
declare module "next-intl" {
interface AppConfig {
Locale: (typeof routing.locales)[number];
Messages: typeof en;
Formats: typeof formats;
}
}
// tsconfig.json (add to compilerOptions)
{
"compilerOptions": {
"allowArbitraryExtensions": true
}
}
Why good: typos in translation keys become compile-time errors, IDE autocomplete for translation keys, strictly-typed locales prevent invalid locale strings, Formats registration enables type-safe formatting
Optional: Type-Safe Message Arguments (Experimental)
For automatic type inference of ICU message arguments, configure createMessagesDeclaration:
// next.config.mjs
import { createNextIntlPlugin } from "next-intl/plugin";
const withNextIntl = createNextIntlPlugin({
experimental: {
createMessagesDeclaration: "./messages/en.json",
},
});
export default withNextIntl({});
This generates type declarations enabling autocomplete for message arguments like {name} or {count, plural, ...}.
Pattern 10: Server Actions and Metadata
Use async getTranslations for Server Actions and Metadata.
// src/app/[locale]/page.tsx
import { getTranslations, setRequestLocale } from "next-intl/server";
type Props = {
params: Promise<{ locale: string }>;
};
export async function generateMetadata({ params }: Props) {
const { locale } = await params;
const t = await getTranslations({ locale, namespace: "Metadata" });
return {
title: t("title"),
description: t("description"),
};
}
export default async function HomePage({ params }: Props) {
const { locale } = await params;
setRequestLocale(locale);
const t = await getTranslations("Home");
return <h1>{t("welcome")}</h1>;
}
Why good: getTranslations works in async contexts like generateMetadata, locale parameter is required for metadata since it runs outside component tree
</patterns>
<integration>
Integration Notes
- Server Components first: Load translations in Server Components for performance; use
useTranslations(sync) orgetTranslations(async) - Client Components: Wrap with
NextIntlClientProviderfor hooks access; locale switching and interactive features live here - Locale state: Managed entirely by next-intl - read with
useLocale(), never store separately
</integration>
<red_flags>
RED FLAGS
High Priority Issues:
- Missing
setRequestLocale(locale)in page/layout components -- breaks static rendering - Not awaiting
paramsin App Router -- params is a Promise in Next.js 15+, causes runtime errors - Missing
NextIntlClientProviderin root layout -- Client Components cannot access translations - Hardcoded locale strings -- use named constants from routing.ts
- Not validating locale against
routing.locales-- invalid locales cause cryptic errors - Using
middleware.tson Next.js 16+ -- must rename toproxy.ts
Medium Priority Issues:
- Missing
generateStaticParamsfor static routes -- forces dynamic rendering - Using
t()instead oft.rich()for messages with markup -- returns string, not ReactNode - Missing namespace in
useTranslations-- all keys become global, conflicts likely - Using
useTranslationsingenerateMetadata-- usegetTranslationsinstead
Gotchas & Edge Cases:
setRequestLocale(locale)must be called at the TOP of components, before any hooksgenerateMetadataruns outside the component tree -- requires explicit locale param togetTranslationst.rich()tag functions receivechunks(ReactNode[]), not a single elementuseNow()only updates on client -- SSR shows initial value until hydration- v4.0+ GDPR cookie changes: locale cookies now expire when browser closes and are only set when user switches locale
- Next.js 16:
proxy.tsruns on Node.js runtime, not Edge
For full anti-patterns with code examples, see reference.md.
</red_flags>
<critical_reminders>
CRITICAL REMINDERS
All code must follow project conventions in CLAUDE.md
(You MUST call setRequestLocale(locale) at the top of ALL page/layout components for static rendering)
(You MUST validate locale against routing.locales before using it)
(You MUST use NextIntlClientProvider in the root layout to enable client-side hooks)
(You MUST use named constants for locale codes - NO inline locale strings)
Failure to follow these rules will break static generation and cause runtime errors with invalid locales.
</critical_reminders>