Internationalization
Multi-language support with next-intl
This project uses next-intl for internationalization with locale-based routing and automatic locale detection.
Overview
The i18n system provides:
- Locale-based routing - URLs like
/en/aboutand/it/about - Automatic detection - Browser language, cookies, headers
- Type-safe translations - Full TypeScript support
- ICU message format - Pluralization, variables, rich text
- Server and client - Works in all component types
Structure
src/i18n/
├── config.tsx # Locale configuration
├── init.ts # i18n initialization
├── navigation.ts # Localized navigation utilities
├── request.ts # Request-based locale detection
├── routing.ts # Routing configuration
├── seo.ts # SEO helpers
├── server.ts # Server-side utilities
├── rich-text.tsx # Rich text rendering
└── translate.tsx # Translation utilities
src/messages/
├── dictionaries/ # Translation files per locale
│ ├── en/
│ │ ├── common.json
│ │ ├── auth.json
│ │ └── ...
│ └── it/
│ └── ...
└── zod-errors/ # Zod validation messages
├── en.json
└── it.jsonQuick Links
Translations
Translation files, namespaces, and usage patterns
Routing
Locale-based routing and navigation
Supported Locales
Configure locales in src/i18n/config.tsx:
export const locales = ["en", "it"] as const;
export const defaultLocale = "en";Quick Start
Server Components
import { getTranslations } from "next-intl/server";
export default async function Page() {
const t = await getTranslations("common");
return <h1>{t("welcome")}</h1>;
}Client Components
"use client";
import { useTranslations } from "next-intl";
export function MyComponent() {
const t = useTranslations("common");
return <p>{t("greeting")}</p>;
}With Parameters
{
"greeting": "Hello, {name}!",
"items": "You have {count, plural, =0 {no items} =1 {one item} other {# items}}"
}t("greeting", { name: "John" }); // "Hello, John!"
t("items", { count: 5 }); // "You have 5 items"Adding a New Locale
Step 1: Update config
Add the locale to src/i18n/config.tsx
Step 2: Copy translation files
cp -r src/messages/dictionaries/en src/messages/dictionaries/esStep 3: Translate files
Update all JSON files in the new locale folder
Step 4: Add Zod errors
cp src/messages/zod-errors/en.json src/messages/zod-errors/es.jsonFeatures
Automatic Locale Detection
The middleware automatically detects locale from:
- URL path segment (highest priority)
- Cookie (
NEXT_LOCALE) - Accept-Language header
- Default locale (fallback)
Type-Safe Keys
Translation keys are fully typed when using the correct namespace:
const t = useTranslations("common");
t("welcome"); // ✅ Typed
t("nonexistent"); // ❌ TypeScript errorICU Message Format
Supports pluralization, gender, and more:
{
"followers": "{count, plural, =0 {No followers} =1 {1 follower} other {# followers}}",
"greeting": "Hello {name}!"
}Rich Text
Use RichText for formatted content with links:
{
"terms": "By signing up, you agree to our <link>Terms of Service</link>."
}import { RichText } from "@/i18n/rich-text";
<RichText
messageKey="common.terms"
components={{
link: (chunks) => <Link href="/terms">{chunks}</Link>,
}}
/>Best Practices
- Use namespaces - Organize translations by feature or page
- Keep keys consistent - Use the same structure across all locales
- Avoid string concatenation - Use full sentences with parameters
- Test all locales - Verify translations before deployment