VinniZP avatar

i18n-patterns

Internationalization patterns for Lingx. Type-safe translations with Lingx SDK, ICU MessageFormat, k

提供方 VinniZP|开源

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

DocumentPurpose
key-naming.mdKey naming conventions
type-safety.mdTKey, tKey(), tKeyUnsafe()
icu-format.mdICU MessageFormat patterns
extraction.mdKey extraction and CLI

Core Concepts

Translation Functions

FunctionUse CaseType Safety
t('key')Static string literal keysFull
td(dynamicKey)Dynamic keys from variablesVia tKey
tKey('key')Create type-safe key referenceFull
tKeyUnsafe('key')Escape hatch for dynamic keysNone

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 });