소스 정보
- 저장소
- tomevault-io/skills-registry
- 최근 소스 활동
- 2026년 4월 28일 22:53
- 감지된 SKILL.md 언어
- 영어
- 스타
- 0
- 포크
- 0
설치 방법
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
소스 파일 검토
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
메뉴
기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.
설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.
Codex 또는 Claude로 설치 이 Prompt를 복사해 Codex, Claude 또는 다른 어시스턴트에 붙여 넣으면 Skill 페이지를 검토하고 설치를 진행할 수 있습니다.
직접 명령은 검토 Prompt를 거치지 않습니다. 실행하기 전에 소스를 확인하세요.
npx skills add https://github.com/tomevault-io/skills-registry --skill web-i18n-next-intl명령은 한 줄로 유지됩니다. 복사하기 전에 가로로 스크롤해 전체 내용을 확인하세요.
로컬 사본을 원하시나요? SkillsMP에서 현재 제공할 수 있는 파일을 다운로드하세요.
| Use when this capability is needed.
> Use when this capability is needed.
Review architecture and API design for the vfs-s3 project. Use when the user mentions @architect, asks to review an issue's design, discuss module boundaries, API shape, or architectural decisions for vfs-s3. Also trigger when the user wants to create an ADR (Architecture Decision Record) or evaluate a technical approach for the project. Intended for dispatch from Codex automation or Claude routines; GitHub trigger phrase: @vfs-s3-bot please prepare design doc Use when this capability is needed.
SOC 직업 분류 기준
SKILL.md 표시 중
| name | web-i18n-next-intl |
| description | Type-safe i18n for Next.js App Router Use when this capability is needed. |
| metadata | {"author":"agents-inc"} |
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>
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:
Key patterns covered:
When NOT to use:
Detailed Resources:
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:
Set up next-intl with the App Router using the standard 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.
// 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.
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();
(
);
}
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.
Use the useTranslations hook for rendering localized messages.
// 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
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
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:
one, otherone, few, many, otherzero, one, two, few, many, otherWhy good: ICU plural syntax handles locale-specific plural rules automatically, # is replaced with the formatted count
// Message: "It's your {year, selectordinal, one {#st} two {#nd} few {#rd} other {#th}} birthday!"
t("birthday", { year: 21 }); // "It's your 21st birthday!"
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
Use useFormatter for locale-aware formatting of dates, numbers, and lists.
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
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
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
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
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} =>
{LOCALE_LABELS[loc]}
))}
);
}
Why good: uses next-intl navigation APIs to preserve current path, aria-label provides accessibility, type-safe locale handling
Enable type-safe translation keys with TypeScript augmentation using the AppConfig interface (next-intl v4.0+).
// 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
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, ...}.
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
useTranslations (sync) or getTranslations (async)NextIntlClientProvider for hooks access; locale switching and interactive features live hereuseLocale(), never store separately<red_flags>
High Priority Issues:
setRequestLocale(locale) in page/layout components -- breaks static renderingparams in App Router -- params is a Promise in Next.js 15+, causes runtime errorsNextIntlClientProvider in root layout -- Client Components cannot access translationsrouting.locales -- invalid locales cause cryptic errorsmiddleware.ts on Next.js 16+ -- must rename to proxy.tsMedium Priority Issues:
generateStaticParams for static routes -- forces dynamic renderingt() instead of t.rich() for messages with markup -- returns string, not ReactNodeuseTranslations -- all keys become global, conflicts likelyuseTranslations in generateMetadata -- use getTranslations insteadGotchas & Edge Cases:
setRequestLocale(locale) must be called at the TOP of components, before any hooksgenerateMetadata runs outside the component tree -- requires explicit locale param to getTranslationst.rich() tag functions receive chunks (ReactNode[]), not a single elementuseNow() only updates on client -- SSR shows initial value until hydrationproxy.ts runs on Node.js runtime, not EdgeFor full anti-patterns with code examples, see reference.md.
</red_flags>
<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>
Converted and distributed by TomeVault — claim your Tome and manage your conversions.