
i18n-patterns
Internationalization patterns for Lingx. Type-safe translations with Lingx SDK, ICU MessageFormat, k
by VinniZP|Open Source
Lingx i18n Patterns
Type-safe internationalization patterns using the Lingx SDK.
Quick Reference
import { useTranslation, tKey, tKeyUnsafe, type TKey } from '@lingx/sdk-nextjs';
// In component
const { t, td } = useTranslation('namespace');
t('key.path') // Static key
td(tKey('key.path')) // Dynamic key, type-safe
td(tKeyUnsafe(`${var}.title`)) // Dynamic key, escape hatch
File Organization
apps/web/public/locales/
├── en.json # Root namespace (common strings)
├── ru.json
├── dashboard/ # Dashboard namespace
│ ├── en.json
│ └── ru.json
├── settings/ # Settings namespace
│ ├── en.json
│ └── ru.json
├── projects/ # Projects namespace
│ ├── en.json
│ └── ru.json
└── workbench/ # Workbench namespace
├── en.json
└── ru.json
Documentation
| Document | Purpose |
|---|---|
| key-naming.md | Key naming conventions |
| type-safety.md | TKey, tKey(), tKeyUnsafe() |
| icu-format.md | ICU MessageFormat patterns |
| extraction.md | Key extraction and CLI |
Core Concepts
Translation Functions
| Function | Use Case | Type Safety |
|---|---|---|
t('key') | Static string literal keys | Full |
td(dynamicKey) | Dynamic keys from variables | Via tKey |
tKey('key') | Create type-safe key reference | Full |
tKeyUnsafe('key') | Escape hatch for dynamic keys | None |
Namespace Usage
// Using namespace - looks in [namespace]/en.json
const { t } = useTranslation('settings');
t('title'); // Looks for "title" in settings/en.json
// Root namespace - looks in en.json
const { t } = useTranslation();
t('common.save'); // Looks for "common.save" in en.json
Basic Usage
'use client';
import { useTranslation } from '@lingx/sdk-nextjs';
export function SettingsPage() {
const { t } = useTranslation('settings');
return (
<div>
<h1>{t('title')}</h1>
<p>{t('description')}</p>
<button>{t('actions.save')}</button>
</div>
);
}
With Dynamic Keys
import { useTranslation, tKey, type TKey } from '@lingx/sdk-nextjs';
interface NavItem {
href: string;
labelKey: TKey; // Type-safe key reference
}
const navItems: NavItem[] = [
{ href: '/', labelKey: tKey('nav.home') },
{ href: '/projects', labelKey: tKey('nav.projects') },
];
function Nav() {
const { td } = useTranslation();
return (
<nav>
{navItems.map((item) => (
<a key={item.href} href={item.href}>
{td(item.labelKey)} {/* Use td() for dynamic keys */}
</a>
))}
</nav>
);
}
Decision Tree
What translation function to use?
Is the key a string literal in code?
└─ YES → Use t('key')
└─ NO → Is the key from a typed variable/prop?
└─ YES → Use td(tKey('key')) or td(prop.labelKey)
└─ NO → Use td(tKeyUnsafe(dynamicKey))
---
Where to put the key?
Is it used across multiple pages/namespaces?
└─ YES → Root en.json (common.*)
Is it specific to one feature/page?
└─ YES → [namespace]/en.json
Best Practices
DO
// ✅ Use namespace in useTranslation
const { t } = useTranslation('settings');
t('security.title');
// ✅ Use tKey for typed dynamic keys
const items = [{ labelKey: tKey('nav.home') }];
// ✅ Use ICU for plurals
t('items.count', { count: 5 });
// "items.count": "{count, plural, one {# item} other {# items}}"
DON'T
// ❌ Don't use variables directly in t()
t(variableName); // No type checking!
// ❌ Don't concatenate keys
t('section.' + name); // No type checking!
// ❌ Don't use template literals in t()
t(`${section}.title`); // No type checking!
Common Patterns
Conditional Text with ICU Select
// Translation file
{
"status": "{state, select, enabled {Enabled} disabled {Disabled} other {Unknown}}"
}
// Usage
t('status', { state: isEnabled ? 'enabled' : 'disabled' })
Pluralization
{
"items": "{count, plural, one {# item} other {# items}}"
}
t('items', { count: 1 }); // "1 item"
t('items', { count: 5 }); // "5 items"
With Variables
{
"greeting": "Hello, {name}!"
}
t('greeting', { name: user.name });