- 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 ): Validate locale with hasLocale → notFound() if invalid. Call setRequestLocale(locale) for static rendering. 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 : defineRouting with locales and defaultLocale . src/i18n/request.ts : getRequestConfig + hasLocale + dynamic messages/${locale}.json . src/proxy.ts (or middleware.ts ): createMiddleware(routing) and matcher. src/i18n/navigation.ts : createNavigation(routing) and re-export Link , 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 use Link from @/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
next-intl-app-router
安装
npx skills add https://github.com/liuchiawei/agent-skills --skill next-intl-app-router