Content · Intermediate
Internationalization
Locale routing, RTL support, and currency/date/number formatting for Next.js 14+ using next-intl 3.x. Ships with message files for English, French, German, Hindi, and Arabic.
Read the Getting Access guideif you haven't yet.
1. Get the file
Sign in at marrowstack.dev, open the Internationalization block, and click Copy all files. The block ships as a set of files: lib/i18n.ts, middleware config, and messages/ JSON files for each locale.
2. Prerequisites
- Next.js 14 or 15, App Router
- TypeScript 5+ with strict mode
3. Install
- Copy the block files into your project. Place message files in
messages/at the project root. - Install peer dependencies:
npm install next-intl4. Environment
| Variable | Required | Default | Purpose |
|---|---|---|---|
| NEXT_PUBLIC_DEFAULT_LOCALE | no | — | Default locale. Defaults to en. Must be one of: en, fr, de, hi, ar |
5. Wire it in
import createMiddleware from 'next-intl/middleware'
import { routing } from '@/lib/i18n'
export default createMiddleware(routing)
export const config = {
matcher: ['/((?!api|_next|_vercel|.*\..*).*)']
}import { NextIntlClientProvider } from 'next-intl'
import { getMessages } from 'next-intl/server'
import { notFound } from 'next/navigation'
import { routing } from '@/lib/i18n'
export default async function LocaleLayout({
children,
params: { locale },
}: {
children: React.ReactNode
params: { locale: string }
}) {
if (!routing.locales.includes(locale as never)) notFound()
const messages = await getMessages()
return (
<html lang={locale} dir={locale === 'ar' ? 'rtl' : 'ltr'}>
<body>
<NextIntlClientProvider messages={messages}>
{children}
</NextIntlClientProvider>
</body>
</html>
)
}import { useTranslations } from 'next-intl'
export default function HomePage() {
const t = useTranslations('home')
return <h1>{t('title')}</h1>
}6. Adding a new locale
- Add the locale code to the
localesarray inlib/i18n.ts. - Create
messages/<locale>.jsonwith translations for all keys present inmessages/en.json. - Set the
dirattribute in your layout for RTL locales (Arabic, Hebrew, Urdu).
7. Verify it works
- Navigate to
/frand confirm the page renders in French. - Navigate to
/arand confirm the layout direction is RTL. - Call
formatCurrency(1234.5, 'USD', 'de')and confirm the German currency format.
8. Failure modes & fixes
MISSING_MESSAGE: 'home.title'
Cause: A key exists in one locale's message file but is absent in another.
Fix: Keep all message files in sync. Add the missing key to the failing locale's JSON file.
locale param missing from URL
Cause: The app layout is at app/layout.tsx instead of app/[locale]/layout.tsx.
Fix: Move your root layout into app/[locale]/layout.tsx as shown in the wiring example.
Middleware not running
Cause: The matcher in middleware.ts is excluding the locale paths.
Fix: Use the matcher shown above — it excludes API routes, static files, and Next.js internals only.