@coherent.js/i18n

Internationalization for Coherent.js applications: translations with interpolation and pluralization, locale-aware formatters, and locale management.

pnpm add @coherent.js/i18n

Translations

Create a translator, add messages per locale, and translate with t(). Interpolation uses {{name}} placeholders by default:

import { createTranslator } from '@coherent.js/i18n';

const translator = createTranslator({ defaultLocale: 'en', fallbackLocale: 'en' });

translator.addTranslations('en', {
  common: {
    welcome: 'Welcome',
    hello: 'Hello, {{name}}!'
  }
});
translator.addTranslations('fr', {
  common: {
    welcome: 'Bienvenue',
    hello: 'Bonjour, {{name}} !'
  }
});

translator.t('common.hello', { name: 'Ada' }); // "Hello, Ada!"
translator.setLocale('fr');
translator.t('common.welcome');                // "Bienvenue"

Keys are dot-paths into the translation object. Missing keys fall back to fallbackLocale, then to the key itself (customizable via missingKeyHandler).

Scoped translators

createScopedTranslator(translator, namespace) prefixes every key, which keeps component code short:

import { createScopedTranslator } from '@coherent.js/i18n';

const t = createScopedTranslator(translator, 'common');
t('hello', { name: 'Ada' }); // same as translator.t('common.hello', ...)

Formatters

Locale-aware date, number, currency, and list formatting built on Intl:

import { createFormatters } from '@coherent.js/i18n';

const fmt = createFormatters('en-US');

fmt.date.format(new Date());   // "1/15/2026"
fmt.number.format(12345.678);  // "12,345.678"
fmt.currency.format(1999.99);  // "$1,999.99"

Individual formatter classes (DateFormatter, NumberFormatter, CurrencyFormatter, ListFormatter) are also exported.

Locale management

import {
  createLocaleManager,
  detectLocale,
  normalizeLocale,
  isRTL,
  getLocaleDirection
} from '@coherent.js/i18n';

const locales = createLocaleManager({ defaultLocale: 'en-US' });
locales.getLocale();          // current locale

detectLocale();               // best-guess locale for the environment
normalizeLocale('EN_us');     // "en-US"
isRTL('ar');                  // true
getLocaleDirection('he');     // "rtl"

Using translations in components

Components are plain functions, so pass the translator (or a scoped t) like any other dependency:

export function Greeting({ t, name }) {
  return { p: { text: t('hello', { name }) } };
}

See also