# Internationalization (i18n) 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. ## What's included **Config & types** - `SUPPORTED_LOCALES` — `['en', 'fr', 'de', 'hi', 'ar']` as a const tuple - `SupportedLocale` — union type derived from the above - `DEFAULT_LOCALE` — `'en'` - `LOCALE_LABELS` — display names per locale (e.g. `hi → 'हिन्दी'`) - `LOCALE_FLAGS` — emoji flags per locale - `RTL_LOCALES` — locales that need `dir="rtl"` (currently `['ar']`) - `isRTL(locale)` — returns boolean - `isSupportedLocale(locale)` — type guard **Formatting** - `formatPrice(amountUSD, locale, currency?, usdToInrRate?)` — auto-converts to INR for `hi` locale; uses `Intl.NumberFormat` with no decimals - `formatDate(date, locale, style?)` — locale-aware date; `style` defaults to `'medium'` - `formatTime(date, locale)` — short time string - `formatNumber(n, locale)` — locale-aware number (thousands separators, etc.) - `formatRelativeTime(date, locale)` — "2 hours ago" / "in 3 days" via `Intl.RelativeTimeFormat` **Message files** - `EN_MESSAGES`, `FR_MESSAGES` — typed message objects covering `nav`, `hero`, `blocks`, `purchase`, `dashboard`, `affiliate`, `errors`, `common` — use these as the content for `messages/en.json` and `messages/fr.json` **Config strings** - `I18N_CONFIG_FILE` — ready-to-paste content for `i18n.ts` at project root - `I18N_MIDDLEWARE` — ready-to-paste content for `middleware.ts` - `LANGUAGE_SWITCHER_CODE` — full source for a `LanguageSwitcher` client component (copy to `components/LanguageSwitcher.tsx`) ## Setup ### 1. Install dependencies ```bash npm install next-intl ``` ### 2. Create `i18n.ts` at project root Paste the contents of `I18N_CONFIG_FILE` exported from this block. ### 3. Update `next.config.js` ```js const withNextIntl = require('next-intl/plugin')('./i18n.ts') module.exports = withNextIntl({ /* your existing config */ }) ``` ### 4. Create message files ```bash mkdir messages ``` Then create `messages/en.json` with the contents of `EN_MESSAGES`, `messages/fr.json` with `FR_MESSAGES`, and so on. For `de`, `hi`, `ar` — translate from `EN_MESSAGES` or use your own strings; the type shape is `typeof EN_MESSAGES`. ### 5. Add middleware Paste `I18N_MIDDLEWARE` into your `middleware.ts`. If you already have middleware, merge the matcher config and wrap with `createMiddleware`. ### 6. Move `app/` to `app/[locale]/` ``` app/ [locale]/ layout.tsx page.tsx ... ``` next-intl handles the routing from here. ### 7. Add RTL support in `app/[locale]/layout.tsx` ```tsx import { isRTL } from '@/blocks/i18n' export default function LocaleLayout({ children, params: { locale } }) { return (
{children} ) } ``` ## Usage examples ```tsx // Translations in a server component import { useTranslations } from 'next-intl' export default function Nav() { const t = useTranslations('nav') return {t('blocks')} } ``` ```ts // Formatting import { formatPrice, formatDate, formatRelativeTime } from '@/blocks/i18n' formatPrice(19, 'en') // $19 formatPrice(19, 'hi') // ₹1,596 (auto-converts at 84x) formatDate(new Date(), 'fr') // 16 mars 2026 formatRelativeTime('2026-03-18', 'de') // gestern ``` ```tsx // Drop in the language switcher // 1. Copy LANGUAGE_SWITCHER_CODE to components/LanguageSwitcher.tsx // 2. Use it: import { LanguageSwitcher } from '@/components/LanguageSwitcher' ``` ## Notes - `formatPrice` uses a hardcoded `usdToInrRate` of `84` by default — pass your env var value at call time (`formatPrice(price, locale, 'USD', Number(process.env.USD_TO_INR_RATE))`) if you want it dynamic - `I18N_CONFIG_FILE` and `I18N_MIDDLEWARE` are exported as strings, not modules — copy-paste them into actual `.ts` files; do not import and re-export them - German (`de`) and Arabic (`ar`) message files are not provided — `EN_MESSAGES` defines the required shape; TypeScript will enforce it via `typeof EN_MESSAGES` - `localePrefix: 'as-needed'` means English URLs have no prefix (`/blocks`) while others do (`/fr/blocks`) — change to `'always'` in the middleware config if you want `/en/blocks` too