skills/hubeiqiao/skills/next-intl-setRequestLocale-ssg-fix

next-intl-setRequestLocale-ssg-fix

Installation
SKILL.md

next-intl setRequestLocale SSG Fix

Problem

When using next-intl v4+ with Next.js App Router and static generation (generateStaticParams), locale pages build successfully but render text in the default locale (typically English) instead of the expected locale. The build output shows no errors — pages are generated for all locales — but at runtime, all locale variants show identical (default) content.

Context / Trigger Conditions

  • Using next-intl v4.x with Next.js 14/15 App Router
  • Pages use generateStaticParams() to pre-render locale variants
  • [locale] dynamic segment in the app directory
  • Symptom 1: Build succeeds, locale pages are listed in build output (e.g., /zh-CN, /zh-TW)
  • Symptom 2: Page <title> may show correct locale (if generateMetadata passes locale explicitly via getTranslations({ locale }))
  • Symptom 3: Client component text (from useTranslations()) shows default locale
  • Symptom 4: getMessages() in layout returns default locale messages
  • No error messages — the fallback is silent

Especially likely when:

  • Using a custom middleware (not createMiddleware from next-intl/middleware)
  • The middleware handles auth (e.g., Supabase) alongside locale detection

Root Cause

During static site generation (SSG), there is no HTTP request context. The requestLocale parameter in getRequestConfig resolves to undefined because no middleware sets the x-next-intl-locale header, and there's no live request to extract the locale from.

When requestLocale is undefined, the getRequestConfig function typically falls back to the default locale:

// i18n/request.ts
export default getRequestConfig(async ({ requestLocale }) => {
  let locale = await requestLocale;
  // requestLocale is undefined during SSG!
  if (!locale || !locales.includes(locale)) {
    locale = 'en'; // Always falls back to default
  }
  // ...
});

This means getMessages() always returns default locale messages during SSG.

Solution

Call setRequestLocale(locale) in every server component (layout and page) that uses next-intl server functions, before calling getMessages(), getTranslations(), or any other next-intl/server function.

Fix the layout:

// app/[locale]/layout.tsx
import { getMessages, setRequestLocale } from 'next-intl/server';

export default async function LocaleLayout({ children, params }) {
  const { locale } = params;

  // CRITICAL: Set locale for static rendering context
  setRequestLocale(locale);

  const messages = await getMessages(); // Now returns correct locale messages
  // ...
}

Fix every page:

// app/[locale]/page.tsx
import { setRequestLocale } from 'next-intl/server';

export default function LocalePage({ params }) {
  setRequestLocale(params.locale);
  return <PageClient />;
}
// app/[locale]/about/page.tsx
import { getTranslations, setRequestLocale } from 'next-intl/server';

export default async function LocaleAboutPage({ params }) {
  const { locale } = params;
  setRequestLocale(locale);
  const t = await getTranslations({ locale, namespace: 'about.meta' });
  // ...
}

Rules:

  1. Call setRequestLocale() before any getMessages() or getTranslations() call
  2. Call it in every layout and page under [locale]/, not just the layout
  3. The locale value comes from params.locale (the route segment)

Verification

  1. Run npm run build — locale pages should still be listed with (SSG)
  2. Run npm run dev and navigate to a locale page (e.g., /zh-CN)
  3. Verify client component text renders in the correct locale
  4. Verify useTranslations() returns translated strings, not default locale

Why This Is Misleading

  • Build succeeds with no warnings about wrong locale
  • generateMetadata works if it passes locale explicitly: getTranslations({ locale }) — this bypasses the request context entirely
  • Only getMessages() (no locale param) is affected, because it reads from request context
  • The NextIntlClientProvider receives English messages but is told locale="zh-CN", creating a mismatch where the provider has the right locale but wrong messages

Notes

  • setRequestLocale is described by next-intl as a "stopgap" until Next.js natively supports accessing route parameters in all Server Components
  • This is especially important when NOT using createMiddleware from next-intl/middleware (e.g., when using custom Supabase auth middleware)
  • Even when using next-intl's middleware, setRequestLocale is still recommended for static rendering support
  • The function is synchronous — no await needed

References

Weekly Installs
1
First Seen
7 days ago