Lingua 3.0 Migration
Lingua 3.0 simplifies the constructor surface to two entry points and adds explicit failure defaults.
Summary of changes
| Before | After |
|---|---|
createCatalogTranslator(catalog, { locale }) | createTranslator(catalog, { locale }) |
createTranslator(catalogs, { locale, fallback }) | Removed — use createI18n for multi-locale |
createTranslationStore({ catalogs, locale }) | createI18n({ catalogs, locale, loadCatalog? }) |
hydrateTranslationStore(state) | createI18n({ state }) |
TranslationStore / TranslationSnapshot | I18n / I18nSnapshot |
segments() / segmentsDynamic() | parts() / partsDynamic() |
onMissingKey / onMissingValue options | missing: 'throw' | 'key' | handler |
@vielzeug/lingua/format (createFormatter) | Retained as an optional standalone Intl facade |
TranslationState version 3 | version 4 |
LinguaMissingResourceError (2.x) | LinguaMissingCatalogError (unchanged from 2.x) |
| — | LinguaMissingKeyError, LinguaMissingValueError (new) |
Replace constructor calls
// Before — fixed catalog
const translator = createCatalogTranslator(catalog, { locale: 'fr' });
// After
const translator = createTranslator(catalog, { locale: 'fr' });// Before — locale-keyed catalogs
const translator = createTranslator({ en: enCatalog, fr: frCatalog }, { locale: 'fr', fallback: 'en' });
// After — eager catalogs preserve synchronous translation and fallback
const i18n = createI18n({
catalogs: { en: enCatalog, fr: frCatalog },
locale: 'fr',
fallback: 'en',
});
i18n.translate('key');// Before — mutable store
const i18n = createTranslationStore({ catalogs: { en: enCatalog, fr: async () => frCatalog }, locale: 'en' });
// After
const i18n = createI18n({
catalogs: { en: enCatalog },
locale: 'en',
loadCatalog: (locale) => import(`./locales/${locale}`).then((m) => m.default),
});// Before — hydration
const client = hydrateTranslationStore(server.serialize());
// After
const client = createI18n({ state: server.serialize() });Replace segments with parts
segments() returned mixed primitive arrays. parts() returns a typed discriminated union.
// Before
const segs = translator.segments('error', { values: { retry } });
// ['Try ', { href: '/retry' }, '.']
// After
const parts = translator.parts('error', { values: { retry } });
// [
// { type: 'text', value: 'Try ' },
// { type: 'value', value: { href: '/retry' } },
// { type: 'text', value: '.' },
// ]Replace missing handlers
// Before
createTranslator(catalog, { onMissingKey: (key) => `[${key}]`, onMissingValue: (name) => `<${name}>` });
// After
createTranslator(catalog, { missing: ({ key, name }) => (name ? `<${name}>` : `[${key}]`) });Keep or replace the format subpath
@vielzeug/lingua/format remains available when a shared, cached Intl facade improves application code. Direct Intl usage remains appropriate for isolated formatting.
// Before
import { createFormatter } from '@vielzeug/lingua/format';
const fmt = createFormatter(() => i18n.locale);
fmt.currency(9.99, 'USD');
// After
new Intl.NumberFormat(i18n.locale, { style: 'currency', currency: 'USD' }).format(9.99);Bump state version
TranslationState version changed from 3 to 4. Re-serialize server state before hydrating clients with the new API.
Review the Usage Guide and API Reference for the full catalog, translator, and i18n contracts.
Lingua 3.1 Migration
Lingua 3.1 resets serialized-state versioning and narrows catalogKeys() to catalogs only.
State version is 1
TranslationState is versioned 1 — numbering restarts instead of continuing the i18nit counter. createI18n({ state }) rejects any other version with LinguaInvalidStateError. Re-serialize server state with the new build before hydrating.
Catalog keys from an instance
catalogKeys() no longer accepts an I18n instance. Pass the current locale catalog explicitly:
// Before
const allKeys = catalogKeys(i18n);
// After
const state = i18n.serialize();
const allKeys = catalogKeys(state.catalogs[state.locale]);Review the Usage Guide and API Reference for the full catalog, translator, and i18n contracts.