Internationalization (i18n)
Build applications that speak your users' language
When to Use This Skill
Use this skill when:
-
Implementing multi-language support
-
Formatting dates/numbers for different locales
-
Handling RTL languages
-
Managing translation files
Critical Patterns
Pattern 1: Basic Translation
When: Displaying translated text
Good:
// useTranslations hook import { useTranslations } from 'next-intl';
export default function HomePage() { const t = useTranslations('homepage');
return ( <div> <h1>{t('title')}</h1> <p>{t('subtitle')}</p> </div> ); }
// Translation file // messages/en.json { "homepage": { "title": "Welcome to Our Store", "subtitle": "Find the best products" } }
Why: Centralized translations make it easy to support multiple languages.
Pattern 2: Variables in Translations
When: Including dynamic values
Good:
{ "welcome": "Welcome, {username}!", "itemCount": "You have {count} items" }
<p>{t('welcome', { username: 'Alice' })}</p> <p>{t('itemCount', { count: 5 })}</p>
Why: ICU syntax allows flexible variable interpolation.
Pattern 3: Pluralization
When: Handling singular/plural forms
Good:
{ "items": "{count, plural, =0 {No items} =1 {1 item} other {# items}}" }
<p>{t('items', { count: 0 })}</p> // "No items" <p>{t('items', { count: 1 })}</p> // "1 item" <p>{t('items', { count: 5 })}</p> // "5 items"
Why: Different languages have different pluralization rules.
Pattern 4: Date/Number Formatting
When: Displaying locale-specific formats
Good:
import { useFormatter } from 'next-intl';
export default function EventDate({ date, amount }) { const format = useFormatter();
return ( <div> {/* Date: "January 15, 2024" (en), "15 janvier 2024" (fr) */} <p>{format.dateTime(date, { dateStyle: 'long' })}</p>
{/* Currency: "$99.99" (en-US), "99,99 €" (de-DE) */}
<p>{format.number(amount, {
style: 'currency',
currency: 'USD'
})}</p>
</div>
); }
Why: Date and number formats vary by locale.
Pattern 5: RTL Support
When: Supporting Arabic, Hebrew, etc.
Good:
/* Use logical properties / .card { margin-inline-start: 1rem; / Left in LTR, right in RTL */ padding-inline-end: 2rem; }
/* Manual RTL handling */ [dir='rtl'] .arrow { transform: scaleX(-1); }
// Set dir attribute <html lang={locale} dir={locale === 'ar' ? 'rtl' : 'ltr'}>
Why: RTL languages need flipped layouts.
Best Practices
Do's:
-
✅ Extract all user-facing text to translation files
-
✅ Use ICU syntax for complex messages
-
✅ Test with long German text (30% longer)
-
✅ Test RTL languages
-
✅ Format dates/numbers with locale-aware formatters
-
✅ Provide context in translation keys
Don'ts:
-
❌ Don't hardcode strings
-
❌ Don't concatenate translations
-
❌ Don't use locale for business logic
-
❌ Don't forget text expansion
Code Examples
Example 1: Basic Translation with Variables
// messages/en.json { "welcome": "Welcome, {username}!", "cartItems": "You have {count, plural, =0 {no items} =1 {1 item} other {# items}} in your cart" }
// Component import { useTranslations } from 'next-intl';
export default function Header({ username, cartCount }: HeaderProps) { const t = useTranslations();
return ( <header> <h1>{t('welcome', { username })}</h1> <p>{t('cartItems', { count: cartCount })}</p> </header> ); }
Example 2: Locale-Aware Date and Number Formatting
import { useFormatter } from 'next-intl';
export default function ProductCard({ product }: ProductCardProps) { const format = useFormatter();
return ( <div> <h3>{product.name}</h3> <p>{format.number(product.price, { style: 'currency', currency: 'USD' })}</p> <time>{format.dateTime(product.releaseDate, { year: 'numeric', month: 'long', day: 'numeric' })}</time> </div> ); }
For comprehensive examples and detailed implementations, see the references/ folder.
Progressive Disclosure
For detailed implementations:
-
next-intl Setup - Installation, middleware, layouts
-
ICU Patterns - Variables, pluralization, rich text, formatting
References
-
next-intl Setup
-
ICU Patterns
-
next-intl Documentation
-
ICU Message Format
Maintained by dsmj-ai-toolkit