
Next Intl App Router
- 467 installs
- 2 repo stars
- Updated February 17, 2026
- liuchiawei/agent-skills
next-intl-app-router is an agent skill that configures next-intl locale routing, middleware, and message loading for developers adding i18n to Next.js App Router applications.
About
next-intl-app-router is a liuchiawei/agent-skills frontend skill for setting up next-intl with prefix-based locale routing such as /en/about and /ja/about in Next.js App Router projects. It walks through nine implementation areas: next.config.ts plugin wrapping, defineRouting in src/i18n/routing.ts, getRequestConfig in src/i18n/request.ts, middleware or Next.js 16 proxy.ts, createNavigation helpers, locale layout with NextIntlClientProvider, static rendering via setRequestLocale, translated pages, and JSON message files. The examples folder maps eleven copy-paste files—including routing.ts, request.ts, proxy.ts, and app/[locale]/layout.tsx—to project paths. Developers reach for next-intl-app-router when adding locales, fixing middleware matchers, loading messages per locale, or migrating App Router apps to next-intl instead of ad hoc string tables. The checklist ensures root layouts do not wrap NextIntlClientProvider globally, client components use useTranslations with namespaced keys, and locale-aware Link helpers replace default next/link imports for correct prefix handling.
- next-intl-app-router
- AI & Agent Building
- AI-coding skill
Next Intl App Router by the numbers
- 467 all-time installs (skills.sh)
- +18 installs in the week ending Aug 2, 2026 (Skillselion tracking)
- Ranked #1,847 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 2, 2026 (Skillselion catalog sync)
npx skills add https://github.com/liuchiawei/agent-skills --skill next-intl-app-routerAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 467 |
|---|---|
| repo stars | ★ 2 |
| Last updated | February 17, 2026 |
| Repository | liuchiawei/agent-skills ↗ |
How do you add next-intl to Next.js App Router?
Helps with ai & agent building tasks.
Who is it for?
Next.js App Router developers introducing or refactoring next-intl locale routing, middleware, and translation message files.
Skip if: Pages Router-only apps, non-Next React projects, or teams not using the next-intl library.
When should I use this skill?
The user edits i18n routing, next-intl middleware, locale layouts, or message JSON in a Next.js App Router repo.
What you get
next.config plugin setup, i18n routing and request files, middleware matcher, locale layouts, and per-locale message JSON.
- i18n routing configuration
- locale middleware setup
- translation message structure
By the numbers
- Documents 9 numbered next-intl setup sections for App Router projects
- Maps 11 copy-paste example files to standard project paths
Files
next-intl (App Router)
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.
File layout
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.
---
1. Next config
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').
---
2. Routing config
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",
});---
3. Request config
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,
};
});---
4. Proxy / middleware (Next.js 16)
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).
---
5. Navigation helpers
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.
---
6. Locale layout and static rendering
app/[locale]/layout.tsx must (full file: examples/app-locale-layout.tsx):
1. Validate locale with hasLocale → notFound() if invalid. 2. Call setRequestLocale(locale) for static rendering. 3. Wrap children with 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>
);
}---
7. Pages under [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.
---
8. Using translations
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).
---
9. Messages format
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").- Interpolation:
"selectColor": "Select {color} color"→t("selectColor", { color: "Blue" }).
---
Checklist
- [ ]
next.config.ts:createNextIntlPlugin()wraps config. - [ ]
src/i18n/routing.ts:defineRoutingwithlocalesanddefaultLocale. - [ ]
src/i18n/request.ts:getRequestConfig+hasLocale+ dynamicmessages/${locale}.json. - [ ]
src/proxy.ts(ormiddleware.ts):createMiddleware(routing)and matcher. - [ ]
src/i18n/navigation.ts:createNavigation(routing)and re-exportLink, etc. - [ ]
app/[locale]/layout.tsx:hasLocale→notFound,setRequestLocale,generateStaticParams,NextIntlClientProvider+getMessages(). - [ ] Each
app/[locale]/**/page.tsx:setRequestLocale(locale)when using static rendering. - [ ] Client components:
useTranslations("Namespace"); links useLinkfrom@/i18n/navigation.
---
Reference
- Copy-paste examples: examples/ — standalone files for use in any project.
- Extended config (localePrefix, pathnames, etc.): reference.md
- Official: next-intl App Router, Routing setup
// Place at: 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 />;
}
// Place at: 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>
);
}
// Place at: 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 <YourHomeComponent />;
}
"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>
);
}
import { createNavigation } from "next-intl/navigation";
import { routing } from "./routing";
export const { Link, redirect, usePathname, useRouter, getPathname } =
createNavigation(routing);
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,
};
});
import { defineRouting } from "next-intl/routing";
export const routing = defineRouting({
locales: ["en", "ja", "zh-CN", "zh-TW"],
defaultLocale: "en",
});
{
"HomePage": {
"title": "Hello world!"
},
"LandingPage": {
"title": "App Name",
"navbar": {
"home": "Home",
"about": "About"
}
},
"BackToHomeButton": {
"buttonText": "Back to Home",
"tooltip": "Return to the main page"
},
"Navigation": {
"links": {
"home": "Home",
"about": "About"
}
}
}
"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>;
}
import type { NextConfig } from "next";
import createNextIntlPlugin from "next-intl/plugin";
const nextConfig: NextConfig = {
// your app config
};
const withNextIntl = createNextIntlPlugin();
export default withNextIntl(nextConfig);
// Next.js 16: proxy.ts (was middleware.ts in older Next)
import createMiddleware from "next-intl/middleware";
import { routing } from "./i18n/routing";
export const proxy = createMiddleware(routing);
export const config = {
matcher: "/((?!api|trpc|_next|_vercel|.*\\..*).*)",
};
Example files
Copy these into your project as needed. Path mapping:
| This file | Copy to (in your project) |
|---|---|
next.config.ts | next.config.ts (merge with existing) |
i18n/routing.ts | src/i18n/routing.ts |
i18n/request.ts | src/i18n/request.ts |
i18n/navigation.ts | src/i18n/navigation.ts |
proxy.ts | src/proxy.ts (Next 16) or src/middleware.ts |
app-locale-layout.tsx | src/app/[locale]/layout.tsx |
app-locale-page.tsx | src/app/[locale]/page.tsx |
app-locale-about-page.tsx | src/app/[locale]/about/page.tsx |
BackToHomeButton.tsx | Any client component (e.g. src/components/BackToHomeButton.tsx) |
Nav-client.tsx | Any client nav component |
messages-en.json | messages/en.json (add ja.json, etc. per locale) |
Adjust @/i18n/routing and @/i18n/navigation if your alias differs.
next-intl App Router — Reference
Example files (in this skill)
Full copy-paste examples live in the examples/ folder. Mapping to your project:
| Purpose | Example file | Your project path |
|---|---|---|
| Plugin | examples/next.config.ts | next.config.ts |
| Routing config | examples/i18n/routing.ts | src/i18n/routing.ts |
| Request / messages | examples/i18n/request.ts | src/i18n/request.ts |
| Navigation | examples/i18n/navigation.ts | src/i18n/navigation.ts |
| Middleware / proxy | examples/proxy.ts | src/proxy.ts or src/middleware.ts |
| Locale layout + provider | examples/app-locale-layout.tsx | src/app/[locale]/layout.tsx |
| Index page | examples/app-locale-page.tsx | src/app/[locale]/page.tsx |
| About page | examples/app-locale-about-page.tsx | src/app/[locale]/about/page.tsx |
| Client component (Link + t) | examples/BackToHomeButton.tsx, examples/Nav-client.tsx | Any client component |
| Messages | examples/messages-en.json | messages/en.json (+ one per locale) |
See examples/README.md for copy-to-path mapping.
defineRouting options
defineRouting({
locales: ["en", "ja", "zh-CN", "zh-TW"],
defaultLocale: "en",
localePrefix: "as-needed", // or "always" | "never"
pathnames: { "/about": "/about", "/users": "/users" }, // optional
});- localePrefix:
"always"(e.g./en/about),"as-needed"(default locale can omit prefix),"never"(no prefix in URL).
getRequestConfig return shape
return {
locale: "en",
messages: { ... },
timeZone: "Asia/Tokyo", // optional
now: new Date(), // optional
defaultTranslationValues: {}, // optional
};Server vs client APIs
| Context | Hook / API | Import from |
|---|---|---|
| Client | useTranslations(namespace) | next-intl |
| Client | Link, useRouter, usePathname, redirect | @/i18n/navigation |
| Server | getTranslations({ locale, namespace }) | next-intl/server |
| Server | getMessages(), setRequestLocale(locale) | next-intl/server |
| Server | hasLocale(routing.locales, locale) | next-intl |
Links
Related skills
FAQ
Which files does next-intl-app-router expect in a Next.js App Router project?
next-intl-app-router standardizes next.config.ts with createNextIntlPlugin, src/i18n/routing.ts, src/i18n/request.ts, middleware or proxy.ts, src/i18n/navigation.ts, and app/[locale]/layout.tsx with NextIntlClientProvider plus locale message JSON.
Does next-intl-app-router include copy-paste examples?
next-intl-app-router ships an examples folder mapping eleven files—routing, request, navigation, proxy, locale layouts, pages, and messages/en.json—to exact paths inside a Next.js App Router repository.