Using Translations
How to use translations in Server Components, Client Components, and with the Trans component.
This guide covers how to access and display translations in your components using next-intl.
Server Components
In React Server Components, use getTranslations from next-intl/server. This is an async function that returns a translation function.
import { getTranslations } from 'next-intl/server';async function SettingsPage() { const t = await getTranslations('settings'); return ( <div> <h1>{t('pageTitle')}</h1> <p>{t('pageDescription')}</p> </div> );}Multiple Namespaces
Load multiple namespaces by calling getTranslations multiple times:
async function DashboardPage() { const tCommon = await getTranslations('common'); const tBilling = await getTranslations('billing'); return ( <div> <h1>{tCommon('dashboardTabLabel')}</h1> <p>{tBilling('currentPlan')}</p> </div> );}Accessing Nested Keys
For deeply nested translations, you can either pass the full path to getTranslations or use dot notation in the key:
// Option 1: Namespace includes the pathconst t = await getTranslations('settings.personalSettings.name');const title = t('title');// Option 2: Access from root namespace with dot notationconst t = await getTranslations('settings');const title = t('personalSettings.name.title');Page Metadata
Use getTranslations in generateMetadata for translated page titles and descriptions:
import { getTranslations } from 'next-intl/server';import type { Metadata } from 'next';export async function generateMetadata(): Promise<Metadata> { const t = await getTranslations('marketing'); return { title: t('pageTitle'), description: t('pageDescription'), };}Client Components
In Client Components, use the useTranslations hook from next-intl:
'use client';import { useTranslations } from 'next-intl';function CreateOrganizationDialog() { const t = useTranslations('organizations'); return ( <Dialog> <DialogTitle>{t('createOrganizationDialogHeading')}</DialogTitle> <DialogDescription> {t('createOrganizationDialogDescription')} </DialogDescription> </Dialog> );}The hook accesses translations from the context provided by I18nClientProvider in the root layout.
The Trans Component
For translations that include JSX elements or rich formatting, use the Trans component from @kit/ui/trans:
import { Trans } from '@kit/ui/trans';// Basic usage<Trans i18nKey="auth.welcomeMessage" />// With interpolated values<Trans i18nKey="common.greeting" values={{ name: user.name }}/>// With components for rich text<Trans i18nKey="auth.termsAgreement" components={{ TermsLink: <a href="/terms" className="underline" />, PrivacyLink: <a href="/privacy" className="underline" />, }}/>Trans Component Props
| Prop | Type | Description |
|---|---|---|
i18nKey | string | Translation key with namespace prefix (e.g., 'auth.login.title') |
values | object | Values to interpolate into the translation |
components | object | React elements or functions for rich text |
defaults | ReactNode | Fallback content if translation not found |
ns | string | Override namespace (optional) |
How Components Work
The components prop accepts either React elements or render functions:
// React element - children are replaced with translation content<Trans i18nKey="auth.termsAgreement" components={{ TermsLink: <a href="/terms" className="underline" />, }}/>// Render function - full control over rendering<Trans i18nKey="common.highlight" components={{ bold: (chunks) => <strong className="font-bold">{chunks}</strong>, }}/>For the translation:
{ "termsAgreement": "By signing up, you agree to our <TermsLink>Terms</TermsLink>.", "highlight": "This is <bold>important</bold> text."}Translation Patterns
Flat Keys
Simple key-value pairs for straightforward translations:
{ "signUp": "Sign Up", "signIn": "Sign In", "forgotPassword": "Forgot Password?"}const t = useTranslations('auth');t('signUp'); // "Sign Up"Nested Keys
Organized hierarchical structure for related translations:
{ "routes": { "account": "Account", "members": "Members", "billing": "Billing" }, "roles": { "owner": { "label": "Owner" }, "member": { "label": "Member" } }}const t = useTranslations('common');t('routes.account'); // "Account"t('roles.owner.label'); // "Owner"Interpolation
Dynamic values are inserted using curly braces:
{ "greeting": "Hello, {name}!", "itemCount": "You have {count} items", "planRenewal": "Renews every {interval} at {price}"}t('greeting', { name: 'John' }); // "Hello, John!"t('itemCount', { count: 5 }); // "You have 5 items"t('planRenewal', { interval: 'month', price: '$9' });Pluralization
Use ICU message format for count-dependent text:
{ "invitations": "{count} {count, plural, one {invitation} other {invitations}} pending"}t('invitations', { count: 1 }); // "1 invitation pending"t('invitations', { count: 5 }); // "5 invitations pending"More complex plural rules:
{ "items": "{count, plural, =0 {No items} one {# item} other {# items}}"}Rich Text
For translations that need markup, use XML-like tags that map to components:
{ "termsAgreement": "By signing up, you agree to our <TermsLink>Terms of Service</TermsLink> and <PrivacyLink>Privacy Policy</PrivacyLink>."}<Trans i18nKey="auth.termsAgreement" components={{ TermsLink: <a href="/terms" className="underline" />, PrivacyLink: <a href="/privacy" className="underline" />, }}/>Getting Current Locale
Server-Side
import { getLocale } from 'next-intl/server';async function MyServerComponent() { const locale = await getLocale(); // locale = 'en', 'es', 'fr', etc.}Client-Side
'use client';import { useLocale } from 'next-intl';function MyClientComponent() { const locale = useLocale(); // locale = 'en', 'es', 'fr', etc.}Locale-Aware Navigation
Use navigation utilities from @kit/i18n/navigation for automatic locale handling:
import { Link, redirect, permanentRedirect, useRouter, usePathname} from '@kit/i18n/navigation';Link Component
The Link component automatically prefixes paths with the current locale:
// Current locale is 'es'<Link href="/settings">Settings</Link>// Renders: <a href="/es/settings">Settings</a>// For default locale 'en' (with localePrefix: 'as-needed')<Link href="/settings">Settings</Link>// Renders: <a href="/settings">Settings</a>Programmatic Navigation
'use client';import { useRouter, usePathname } from '@kit/i18n/navigation';function NavigationExample() { const router = useRouter(); const pathname = usePathname(); const handleClick = () => { router.push('/dashboard'); // Locale is handled automatically }; // pathname returns the path without locale prefix console.log(pathname); // '/settings' not '/es/settings'}Server-Side Redirects
import { redirect, permanentRedirect } from '@kit/i18n/navigation';async function ProtectedPage() { const session = await getSession(); if (!session) { redirect('/auth/sign-in'); // Redirects with correct locale }}Checking if Translation Exists
Use the has method to check if a translation key exists:
const t = useTranslations('common');if (t.has('newFeature.title')) { return <h1>{t('newFeature.title')}</h1>;}return <h1>Default Title</h1>;This is useful for conditional rendering based on translation availability.
Date and Time Formatting
next-intl provides locale-aware date and time formatting.
Client Components
'use client';import { useFormatter } from 'next-intl';function DateDisplay({ date }: { date: Date }) { const format = useFormatter(); return ( <div> {/* Full date */} <p>{format.dateTime(date, { dateStyle: 'full' })}</p> {/* Relative time */} <p>{format.relativeTime(date)}</p> {/* Custom format */} <p>{format.dateTime(date, { year: 'numeric', month: 'long', day: 'numeric' })}</p> </div> );}Server Components
import { getFormatter } from 'next-intl/server';async function ServerDateDisplay({ date }: { date: Date }) { const format = await getFormatter(); return <p>{format.dateTime(date, { dateStyle: 'long' })}</p>;}Number Formatting
Format numbers according to locale conventions:
'use client';import { useFormatter } from 'next-intl';function PriceDisplay({ amount, currency }: { amount: number; currency: string }) { const format = useFormatter(); return ( <span> {format.number(amount, { style: 'currency', currency: currency })} </span> );}Next: Managing Translations →