next-intl-app-router by liuchiawei/agent-skills
npx skills add https://github.com/liuchiawei/agent-skills --skill next-intl-app-router使用 基于前缀的语言路由(例如 /en/about、/ja/about)来设置和使用 next-intl。在任何 Next.js App Router 项目中使用此技能。
示例代码: 可复制粘贴的示例位于此技能的 examples/ 文件夹中。查看 examples/README.md 了解每个文件在项目中的位置。
保持以下结构:
├── messages/
│ ├── en.json
│ ├── ja.json
│ └── ...
├── next.config.ts
└── src/
├── i18n/
│ ├── request.ts
│ ├── routing.ts
│ └── navigation.ts
├── proxy.ts # Next.js 16+ (原为 middleware.ts)
└── app/
├── layout.tsx # 根布局,此处不包装 NextIntlClientProvider
└── [locale]/
├── layout.tsx
├── page.tsx
└── ...
根布局不使用 包装;只有 才这样做。
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
NextIntlClientProviderapp/[locale]/layout.tsx连接插件(默认路径 ./i18n/request.ts):
// next.config.ts
import type { NextConfig } from "next";
import createNextIntlPlugin from "next-intl/plugin";
const nextConfig: NextConfig = {
/* ... */
};
const withNextIntl = createNextIntlPlugin();
export default withNextIntl(nextConfig);
自定义路径:createNextIntlPlugin('./src/i18n/request.ts')。
在 src/i18n/routing.ts 中的中心配置:
import { defineRouting } from "next-intl/routing";
export const routing = defineRouting({
locales: ["en", "ja", "zh-CN", "zh-TW"],
defaultLocale: "en",
});
src/i18n/request.ts:从 [locale] 段解析语言并加载消息。
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,
};
});
Next.js 16 使用 proxy.ts 替代 middleware.ts。API 相同:
// src/proxy.ts
import createMiddleware from "next-intl/middleware";
import { routing } from "./i18n/routing";
export const proxy = createMiddleware(routing);
export const config = {
matcher: "/((?!api|trpc|_next|_vercel|.*\\..*).*)",
};
匹配器:匹配所有路径名,除了 /api、/trpc、/_next、/_vercel 以及包含点号(例如 favicon.ico)的路径。
使用项目导航包装器,以便链接保持当前语言:
// src/i18n/navigation.ts
import { createNavigation } from "next-intl/navigation";
import { routing } from "./routing";
export const { Link, redirect, usePathname, useRouter, getPathname } =
createNavigation(routing);
在组件中:从 @/i18n/navigation 导入 Link(及其他),而不是从 next/navigation 或 next/link 导入,以获取支持语言的 URL。示例:examples/Nav-client.tsx, examples/BackToHomeButton.tsx。
app/[locale]/layout.tsx 必须(完整文件:examples/app-locale-layout.tsx):
hasLocale 验证 locale → 如果无效则 notFound()。setRequestLocale(locale)。NextIntlClientProvider 和 getMessages() 包装子组件。// app/[locale]/layout.tsx
import { NextIntlClientProvider, hasLocale } from "next-intl";
import { setRequestLocale } from "next-intl/server";
import { notFound } from "next/navigation";
import { routing } from "@/i18n/routing";
import { getMessages } from "next-intl/server";
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 (
<NextIntlClientProvider messages={messages}>
{children}
</NextIntlClientProvider>
);
}
[locale] 下的页面对于静态渲染,[locale] 下使用 next-intl 的每个页面都必须调用 setRequestLocale(locale)(如果需要,使用 use(params))。示例:app-locale-page.tsx, app-locale-about-page.tsx。(如果需要,使用 use(params))。布局已经设置了它;使用语言渲染服务器组件的页面也应该设置它。
// app/[locale]/page.tsx
import { use } from "react";
import { setRequestLocale } from "next-intl/server";
export default function IndexPage({
params,
}: {
params: Promise<{ locale: string }>;
}) {
const { locale } = use(params);
setRequestLocale(locale);
return <TokyoPage />;
}
// app/[locale]/about/page.tsx
import { use } from "react";
import { setRequestLocale } from "next-intl/server";
import AboutContainer from "./components/AboutContainer";
export default function AboutPage({
params,
}: {
params: Promise<{ locale: string }>;
}) {
const { locale } = use(params);
setRequestLocale(locale);
return <AboutContainer />;
}
在该布局/页面中,在任何 next-intl API 调用之前调用 setRequestLocale。
客户端组件: useTranslations(namespace):
"use client";
import { useTranslations } from "next-intl";
import { Link } from "@/i18n/navigation";
export default function BackToHomeButton() {
const t = useTranslations("BackToHomeButton");
return (
<Link href="/">
<span>{t("buttonText")}</span>
</Link>
);
}
"use client";
import { useTranslations } from "next-intl";
import { Link } from "@/i18n/navigation";
export default function Nav() {
const t = useTranslations("Navigation");
return <Link href="/about">{t("links.about")}</Link>;
}
服务器组件: 使用 next-intl/server 中的 getTranslations(根据需要与语言/命名空间一起使用 await)。
messages/ 下每种语言一个 JSON 文件。嵌套键映射到命名空间和键:
{
"HomePage": {
"title": "Hello world!"
},
"LandingPage": {
"title": "Tokyo Sounds",
"navbar": {
"home": "Home",
"about": "About"
}
},
"BackToHomeButton": {
"buttonText": "Back to Home",
"tooltip": "Return to the main page"
}
}
useTranslations("LandingPage") → t("title"), t("navbar.about")。"selectColor": "Select {color} color" → t("selectColor", { color: "Blue" })。next.config.ts:createNextIntlPlugin() 包装配置。src/i18n/routing.ts:包含 locales 和 defaultLocale 的 defineRouting。src/i18n/request.ts:getRequestConfig + hasLocale + 动态 messages/${locale}.json。src/proxy.ts(或 middleware.ts):createMiddleware(routing) 和匹配器。src/i18n/navigation.ts:createNavigation(routing) 并重新导出 Link 等。app/[locale]/layout.tsx:hasLocale → notFound、setRequestLocale、generateStaticParams、NextIntlClientProvider + getMessages()。app/[locale]/**/page.tsx:使用静态渲染时调用 setRequestLocale(locale)。useTranslations("Namespace");链接使用来自 @/i18n/navigation 的 Link。每周安装数
112
代码库
GitHub 星标数
1
首次出现
2026年2月4日
安全审计
安装于
codex102
opencode102
gemini-cli101
github-copilot100
amp90
kimi-cli90
Setup and usage of next-intl with prefix-based locale routing (e.g. /en/about, /ja/about). Use this skill in any Next.js App Router project.
Example code: Copy-paste examples live in this skill's examples/ folder. See examples/README.md for where each file goes in your project.
Keep this structure:
├── messages/
│ ├── en.json
│ ├── ja.json
│ └── ...
├── next.config.ts
└── src/
├── i18n/
│ ├── request.ts
│ ├── routing.ts
│ └── navigation.ts
├── proxy.ts # Next.js 16+ (was middleware.ts)
└── app/
├── layout.tsx # Root layout, no NextIntlClientProvider here
└── [locale]/
├── layout.tsx
├── page.tsx
└── ...
Root layout does not wrap with NextIntlClientProvider; only app/[locale]/layout.tsx does.
Wire the plugin (default path ./i18n/request.ts):
// next.config.ts
import type { NextConfig } from "next";
import createNextIntlPlugin from "next-intl/plugin";
const nextConfig: NextConfig = {
/* ... */
};
const withNextIntl = createNextIntlPlugin();
export default withNextIntl(nextConfig);
Custom path: createNextIntlPlugin('./src/i18n/request.ts').
Central config in src/i18n/routing.ts:
import { defineRouting } from "next-intl/routing";
export const routing = defineRouting({
locales: ["en", "ja", "zh-CN", "zh-TW"],
defaultLocale: "en",
});
src/i18n/request.ts: resolve locale from the [locale] segment and load messages.
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,
};
});
Next.js 16 uses proxy.ts instead of middleware.ts. Same API:
// src/proxy.ts
import createMiddleware from "next-intl/middleware";
import { routing } from "./i18n/routing";
export const proxy = createMiddleware(routing);
export const config = {
matcher: "/((?!api|trpc|_next|_vercel|.*\\..*).*)",
};
Matcher: all pathnames except /api, /trpc, /_next, /_vercel, and paths containing a dot (e.g. favicon.ico).
Use project navigation wrappers so links keep the current locale:
// src/i18n/navigation.ts
import { createNavigation } from "next-intl/navigation";
import { routing } from "./routing";
export const { Link, redirect, usePathname, useRouter, getPathname } =
createNavigation(routing);
In components: import Link (and others) from @/i18n/navigation, not from next/navigation or next/link, for locale-aware URLs. Example: examples/Nav-client.tsx, examples/BackToHomeButton.tsx.
app/[locale]/layout.tsx must (full file: examples/app-locale-layout.tsx):
locale with hasLocale → notFound() if invalid.setRequestLocale(locale) for static rendering.NextIntlClientProvider and getMessages().// app/[locale]/layout.tsx
import { NextIntlClientProvider, hasLocale } from "next-intl";
import { setRequestLocale } from "next-intl/server";
import { notFound } from "next/navigation";
import { routing } from "@/i18n/routing";
import { getMessages } from "next-intl/server";
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 (
<NextIntlClientProvider messages={messages}>
{children}
</NextIntlClientProvider>
);
}
[locale]For static rendering, every page under [locale] that uses next-intl must call setRequestLocale(locale) (and use use(params) if needed). Examples: app-locale-page.tsx, app-locale-about-page.tsx. (and use use(params) if needed). Layout already sets it; pages that render server components using locale should set it too.
// app/[locale]/page.tsx
import { use } from "react";
import { setRequestLocale } from "next-intl/server";
export default function IndexPage({
params,
}: {
params: Promise<{ locale: string }>;
}) {
const { locale } = use(params);
setRequestLocale(locale);
return <TokyoPage />;
}
// app/[locale]/about/page.tsx
import { use } from "react";
import { setRequestLocale } from "next-intl/server";
import AboutContainer from "./components/AboutContainer";
export default function AboutPage({
params,
}: {
params: Promise<{ locale: string }>;
}) {
const { locale } = use(params);
setRequestLocale(locale);
return <AboutContainer />;
}
Call setRequestLocale before any next-intl APIs in that layout/page.
Client components: useTranslations(namespace):
"use client";
import { useTranslations } from "next-intl";
import { Link } from "@/i18n/navigation";
export default function BackToHomeButton() {
const t = useTranslations("BackToHomeButton");
return (
<Link href="/">
<span>{t("buttonText")}</span>
</Link>
);
}
"use client";
import { useTranslations } from "next-intl";
import { Link } from "@/i18n/navigation";
export default function Nav() {
const t = useTranslations("Navigation");
return <Link href="/about">{t("links.about")}</Link>;
}
Server components: use getTranslations from next-intl/server (await with locale/namespace as needed).
One JSON file per locale under messages/. Nested keys map to namespaces and keys:
{
"HomePage": {
"title": "Hello world!"
},
"LandingPage": {
"title": "Tokyo Sounds",
"navbar": {
"home": "Home",
"about": "About"
}
},
"BackToHomeButton": {
"buttonText": "Back to Home",
"tooltip": "Return to the main page"
}
}
useTranslations("LandingPage") → t("title"), t("navbar.about")."selectColor": "Select {color} color" → t("selectColor", { color: "Blue" }).next.config.ts: createNextIntlPlugin() wraps config.src/i18n/routing.ts: defineRouting with locales and defaultLocale.src/i18n/request.ts: getRequestConfig + hasLocale + dynamic messages/${locale}.json.src/proxy.ts (or ): and matcher.Weekly Installs
112
Repository
GitHub Stars
1
First Seen
Feb 4, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
codex102
opencode102
gemini-cli101
github-copilot100
amp90
kimi-cli90
Genkit JS 开发指南:AI 应用构建、错误排查与最佳实践
6,100 周安装
AI代码安全指南 - Project CodeGuard软件安全技能,防止常见漏洞
104 周安装
Excel财务建模规范与xlsx文件处理指南 - 零错误公式与行业标准格式
104 周安装
MyVibe Publish:一键发布网页内容到MyVibe平台,支持HTML/ZIP/目录/URL
104 周安装
OnchainKit:Coinbase 官方 React 区块链组件库,快速构建链上应用
104 周安装
Ralph Wiggum 循环模式:Claude Code 自动化迭代开发工具,实现代码重构与测试驱动开发
104 周安装
DigitalOcean管理技能:监控、告警、项目组织与正常运行时间检查
104 周安装
middleware.tscreateMiddleware(routing)src/i18n/navigation.ts: createNavigation(routing) and re-export Link, etc.app/[locale]/layout.tsx: hasLocale → notFound, setRequestLocale, generateStaticParams, NextIntlClientProvider + getMessages().app/[locale]/**/page.tsx: setRequestLocale(locale) when using static rendering.useTranslations("Namespace"); links use Link from @/i18n/navigation.