Using Translations
How to use translations in Server Components, Client Components, and with the Trans component.
This guide covers how to use translations in your application using the next-intl library.
Server Components
In React Server Components, use getTranslations from next-intl/server:
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> );}Accessing Nested Keys
For deeply nested translations, pass the full namespace path:
// Access settings.personalSettings.name.titleconst t = await getTranslations('settings.personalSettings.name');const title = t('title');// Or access from rootconst t = await getTranslations('settings');const title = t('personalSettings.name.title');Page Metadata
Use getTranslations in generateMetadata:
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:
'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 Trans Component
For more complex scenarios with JSX interpolation, use the Trans component from @kit/ui/trans:
import { Trans } from '@kit/ui/trans';// Basic usage<Trans i18nKey="auth.welcomeMessage" />// With values<Trans i18nKey="common.greeting" values={{ name: user.name }}/>// With components for rich text<Trans i18nKey="terms.agreement" components={{ TermsLink: <a href="/terms" className="underline" />, PrivacyLink: <a href="/privacy" className="underline" />, }}/>The translation key uses dot notation: namespace.key.
Translation Patterns
Flat Keys
Simple key-value pairs:
{ "signUp": "Sign Up", "signIn": "Sign In", "forgotPassword": "Forgot Password?"}Usage:
const t = useTranslations('auth');t('signUp'); // "Sign Up"Nested Keys
Organized hierarchical structure:
{ "routes": { "account": "Account", "members": "Members", "billing": "Billing" }, "roles": { "owner": { "label": "Owner" }, "member": { "label": "Member" } }}Usage:
const t = useTranslations('common');t('routes.account'); // "Account"t('roles.owner.label'); // "Owner"Interpolation
Dynamic values in translations:
{ "greeting": "Hello, {name}!", "itemCount": "You have {count} items", "planRenewal": "Renews every {interval} at {price}"}Usage:
t('greeting', { name: 'John' }); // "Hello, John!"t('itemCount', { count: 5 }); // "You have 5 items"Pluralization
ICU message format for plurals:
{ "invitations": "{count} {count, plural, one {invitation} other {invitations}} pending"}Usage:
t('invitations', { count: 1 }); // "1 invitation pending"t('invitations', { count: 5 }); // "5 invitations pending"Rich Text
For translations with markup:
{ "termsAgreement": "By signing up, you agree to our <TermsLink>Terms of Service</TermsLink> and <PrivacyLink>Privacy Policy</PrivacyLink>."}Usage with Trans:
<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, useRouter, usePathname } from '@kit/i18n/navigation';// Link automatically prefixes with current locale<Link href="/settings">Settings</Link>// Redirect with localeredirect('/dashboard');// Client-side navigationconst router = useRouter();router.push('/settings');// Get current pathname (without locale prefix)const pathname = usePathname();Error Handling
Missing translations fall back to the key name and log a warning:
// If 'auth.unknownKey' doesn't exist:t('unknownKey'); // Returns "unknownKey" and logs warningConfigure fallback behavior in apps/web/i18n/request.ts:
getMessageFallback(info) { console.log(`Missing i18n key: ${info.key}`); return info.key; // Return the key as fallback}Next: Managing Translations →