i18n Page Setup
Adds internationalization support to Next.js pages using next-intl.
When to Use
- •Creating a new page with user-facing text
- •Adding translations to an existing page
- •Converting hardcoded strings to i18n
- •Adding a new locale
Supported Locales
| Locale | Language | File |
|---|---|---|
en | English | sites/arolariu.ro/messages/en.json |
ro | Romanian | sites/arolariu.ro/messages/ro.json |
fr | French | sites/arolariu.ro/messages/fr.json |
Message Structure
Messages are organized by namespace (usually matching the page/feature):
json
{
"Namespace": {
"__metadata__": {
"title": "Page Title for SEO",
"description": "Page description for SEO and social sharing"
},
"title": "Display Title",
"subtitle": "Subtitle text",
"actions": {
"submit": "Submit",
"cancel": "Cancel"
},
"errors": {
"required": "This field is required",
"invalid": "Invalid input"
}
}
}
Conventions:
- •
__metadata__— Reserved for SEO metadata (used bygenerateMetadata()) - •Nest by feature:
actions.submit,errors.required - •Use descriptive keys:
invoiceCountnotcount - •Keep keys in camelCase
Server Component Pattern
Use getTranslations() for Server Components:
tsx
// page.tsx (Server Component)
import {getTranslations} from "next-intl/server";
import {createMetadata} from "@/metadata";
export async function generateMetadata(): Promise<Metadata> {
const t = await getTranslations("Namespace.__metadata__");
return createMetadata({
title: t("title"),
description: t("description"),
});
}
export default async function Page(): Promise<React.JSX.Element> {
const t = await getTranslations("Namespace");
return (
<main>
<h1>{t("title")}</h1>
<p>{t("subtitle")}</p>
</main>
);
}
Client Component Pattern
Use useTranslations() hook for Client Components:
tsx
// island.tsx (Client Component)
"use client";
import {useTranslations} from "next-intl";
export default function RenderScreen(): React.JSX.Element {
const t = useTranslations("Namespace");
return (
<main>
<h1>{t("title")}</h1>
<button>{t("actions.submit")}</button>
</main>
);
}
Rich Text and Interpolation
tsx
// Interpolation
t("greeting", {name: "Alex"})
// Message: "Hello, {name}!"
// Pluralization
t("itemCount", {count: items.length})
// Message: "You have {count, plural, =0 {no items} one {# item} other {# items}}"
// Rich text (HTML tags)
t.rich("terms", {
link: (chunks) => <a href="/terms">{chunks}</a>,
})
// Message: "By continuing you agree to our <link>terms</link>"
Workflow
- •Define namespace matching the page or feature name
- •Add keys to
messages/en.jsonfirst (primary locale) - •Add translations to
messages/ro.jsonandmessages/fr.json - •Use in Server Components with
getTranslations() - •Use in Client Components with
useTranslations() - •Use in metadata with
generateMetadata()+createMetadata() - •Verify all 3 locales have matching keys
Checklist
- • Keys added to all 3 locale files (
en.json,ro.json,fr.json) - •
__metadata__namespace includestitleanddescription - • Server Components use
getTranslations() - • Client Components use
useTranslations() - • No hardcoded user-facing strings in JSX
- • Interpolation used for dynamic values (not string concatenation)
- • Keys follow camelCase naming
- • Nested structure matches feature organization
- •
npm run generate:i18nruns without errors