Skip to content
lingua logoLinguai18n
Framework-neutral locale catalogs, typed translations, and explicit plural messages.
Version
v2.2.0
Size
2.4 KB gzip
Dependencies
Zero dependencies
BrowserNode ≥22SSRDeno
createCatalogTranslatorcreateTranslationStorecreateTranslatorhydrateTranslationStoreLinguaError View all 11 exports

Why Lingua?

Lingua separates immutable translation from mutable locale state. Use one catalog per locale, then select static or stateful API from whether locale can change.

ts
// Before
const message = catalogs[locale]?.inbox?.[count === 1 ? 'one' : 'other'] ?? 'inbox';

// After
const output = i18n.translate('inbox', { count });
FeatureLinguai18nextFormatJS
Bundle size2.4 KBVaries by selected modulesVaries by selected modules
Zero runtime dependencies
Explicit plural catalog nodesConvention/config dependentICU-message dependent
Declared lazy locale catalogsPlugin/config dependentApplication-defined
Immutable locale snapshotsApplication-definedApplication-defined

Use Lingua when you need a compact TypeScript runtime with explicit catalog structure, deterministic fallback, and framework-neutral subscriptions.

Consider i18next or FormatJS when you need their plugin ecosystems, message extraction pipelines, or framework-specific integrations.

Installation

sh
pnpm add @vielzeug/lingua
sh
npm install @vielzeug/lingua
sh
yarn add @vielzeug/lingua

Quick Start

Create locale store with static catalogs, then dispose it when owner ends.

ts
import { createTranslationStore } from '@vielzeug/lingua';

const i18n = createTranslationStore({
  catalogs: {
    de: { inbox: { plural: { one: 'Eine Nachricht', other: '{count} Nachrichten' } } },
    en: { inbox: { plural: { one: 'One message', other: '{count} messages' } } },
  },
  locale: 'en',
});

try {
  console.log(i18n.translate('inbox', { count: 3 }));
  await i18n.setLocale('de');
  console.log(i18n.translate('inbox', { count: 1 }));
} finally {
  i18n.dispose();
}

Features

  • createCatalogTranslator() compiles one immutable fixed-locale catalog.
  • createTranslator() compiles immutable locale-keyed catalogs.
  • createTranslationStore() manages locale changes and declared catalogs.
  • translate() renders text and plural messages through explicit catalog nodes.
  • translateDynamic() makes runtime-key lookup explicit.
  • load() deduplicates lazy catalog loading per locale.
  • getSnapshot() and subscribe() expose immutable translator revisions.
  • serialize() and hydrateTranslationStore() transfer resolved SSR catalogs.
  • createFormatter() and validateCatalog() remain isolated subpath tools.

Documentation

See Also

  • Ripple adapts Lingua snapshots into reactive application state.
  • Courier can fetch locale catalogs before passing them to Lingua loaders.
  • Wayfinder can drive locale selection from route state.