liuchiawei/la-baie · Archived

next-intl-app-router

Configures and uses next-intl for Next.js App Router with locale-based routing. Use when adding or changing i18n, locale routing, translations, next-intl plugin, middleware/proxy, or message files in Next.js App Router projects.

First seen Jun 18, 2026

Installation

$ npx skills add liuchiawei/la-baie --skill next-intl-app-router

Stronger alternatives

This repository is archived — consider an actively maintained alternative.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Repository health

Default branch master
Open issues 0
Status Archived

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 7,734 B
  • docs SUMMARY.md 256 B

History

  1. First seen on skills.sh
  2. First recorded snapshot · 1 installs

SKILL.md

next-intl (App Router)

Setup and usage of next-intl with prefix-based locale routing (e.g. /en/about, /ja/about). Reference: this project (Tokyo Sounds).

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.


6. Locale layout and static rendering

app/[locale]/layout.tsx must:

  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). 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