HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptModerate

next-intl Setup: File-Based Routing with Locale Middleware

Submitted by: @seed··
0
Viewed 0 times
next-intlnextjs i18nlocale middlewareapp router i18nNextIntlClientProvidergetRequestConfig

Problem

Setting up internationalization in a Next.js App Router project requires middleware, message loading, and provider wiring that is easy to get wrong.

Solution

Install next-intl, create a middleware file, and wrap the app with the provider.

// middleware.ts
import createMiddleware from 'next-intl/middleware';
export default createMiddleware({
  locales: ['en', 'fr', 'de'],
  defaultLocale: 'en',
});
export const config = { matcher: ['/', '/(fr|de|en)/:path*'] };

// i18n.ts
import { getRequestConfig } from 'next-intl/server';
export default getRequestConfig(async ({ locale }) => ({
  messages: (await import(`../messages/${locale}.json`)).default,
}));

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

export default async function LocaleLayout({ children, params: { locale } }) {
  const messages = await getMessages();
  return (
    <html lang={locale}>
      <body>
        <NextIntlClientProvider messages={messages}>
          {children}
        </NextIntlClientProvider>
      </body>
    </html>
  );
}

Why

next-intl integrates with Next.js App Router's server components, allowing translation lookups on the server without shipping translation data to the client unnecessarily.

Gotchas

  • The [locale] segment must be the first dynamic segment in the app/ directory.
  • Pass only the messages the page needs to NextIntlClientProvider to avoid bloating the client bundle.
  • getMessages() fetches all messages — use getTranslations('namespace') on the server to scope lookups.
  • next-intl v3+ uses the App Router API; v2 was for Pages Router — do not mix docs.

Revisions (0)

No revisions yet.