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.title
const t = await getTranslations('settings.personalSettings.name');
const title = t('title');
// Or access from root
const 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 locale
redirect('/dashboard');
// Client-side navigation
const 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 warning

Configure 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 →