Translations & Dictionaries
The dictionary pattern
messages/en.json
{
"HomePage": {
"title": "Welcome",
"subtitle": "Learn Next.js, step by step"
}
}messages/de.json
{
"HomePage": {
"title": "Willkommen",
"subtitle": "Next.js Schritt für Schritt lernen"
}
}HomePage, Navbar, and so on) keeps large dictionaries navigable and avoids collisions between unrelated strings that happen to share a short key like title.Loading the right dictionary on the server
app/[locale]/page.tsx — loading a dictionary manually
async function getDictionary(locale: string) {
return import(`../../../messages/${locale}.json`).then((m) => m.default)
}
export default async function HomePage({
params,
}: {
params: { locale: string }
}) {
const dict = await getDictionary(params.locale)
return (
<div>
<h1>{dict.HomePage.title}</h1>
<p>{dict.HomePage.subtitle}</p>
</div>
)
}Using a library: next-intl's useTranslations
Hand-loading dictionaries works, but it's repetitive across every page and doesn't give Client Components an ergonomic way to reach the same strings. Libraries like next-intl wrap the dictionary pattern in a hook that works in both Server and Client Components.
A component using next-intl
import { useTranslations } from 'next-intl'
export default function HomePage() {
const t = useTranslations('HomePage')
return (
<div>
<h1>{t('title')}</h1>
<p>{t('subtitle')}</p>
</div>
)
}Approach | Server Components | Client Components | Setup effort |
|---|---|---|---|
Manual dictionary loading | Yes | Needs prop-drilling or context | Low, but repetitive |
next-intl ( | Yes | Yes, same hook | One-time provider/config setup |
A translation dictionary is typically one JSON file per locale, with keys namespaced by page or feature.
The dictionary for the current locale is loaded in a Server Component (or via
generateStaticParams) and threaded down to whatever needs it.Libraries like next-intl's
useTranslationshook wrap that lookup and work in both Server and Client Components.Beyond a handful of strings, keeping dictionaries in sync across locales is usually the harder problem — not the loading mechanism itself.