@burojs/i18n-core
v0.2.0
Published
Buro: hierarchical i18n with fallback chain over i18next
Downloads
181
Readme
@burojs/i18n-core
Hierarchical i18n for buro, with a per-resource fallback chain over i18next.
Translation happens at render time, so switching language updates the whole UI live — no reload, no refetch.
Install
pnpm add @burojs/i18n-coreUsage
import { createI18n, createI18nextAdapter } from '@burojs/i18n-core';
const i18n = createI18n({
adapter: createI18nextAdapter({
defaultLocale: 'ru',
locales: ['ru', 'en'],
resources: { ru: ruBundle, en: enBundle },
fallbackLocale: 'en',
}),
});Key resolution
A key is resolved against the most specific namespace first and falls back outward, so a resource can override a shared string without copying the rest:
resources.<resource>.fields.<field>.label → fields.<field>.label → <the key itself>resolveKey implements that chain and is exported for callers that need to know which key won.
Locale registry
Most apps assemble their resources from more than one place: the framework
ships a base dictionary, the app adds its own chrome strings, a domain module
adds its enum labels. Printing that as one literal object means adding a
language is a code change. createLocaleRegistry instead lets each source
register a contribution — the registry merges them:
import {
createLocaleRegistry,
localeContributionsFromGlob,
createI18nFromRegistry,
} from '@burojs/i18n-core';
import { en as frameworkEn } from '@burojs/react';
const registry = createLocaleRegistry();
registry.register({ locale: 'en', priority: 0, source: '@burojs/react', bundle: frameworkEn });
// One file per locale, globbed by the bundler — dropping in `es.ts` is the
// entire mechanism for adding Spanish. Nothing here lists locale codes.
const glob = import.meta.glob('./locales/*.ts', { eager: true });
for (const c of localeContributionsFromGlob(glob, { source: 'app', priority: 10 })) {
registry.register(c);
}
const i18n = await createI18nFromRegistry(registry, {
defaultLocale: 'en',
fallbackLocale: 'en', // or a per-locale chain: { fr: ['en'], default: ['en'] }
});A key collision within one (locale, namespace) resolves by the higher
DECLARED priority, never by which contribution happened to register last.
localeContributionsFromGlob is bundler-agnostic (same reasoning as
@burojs/core's docsFromGlob): it accepts whatever flat path → module
map import.meta.glob or an equivalent walk produces, and the caller — not
this package — is the one that actually calls the bundler API.
Main exports
createI18n/I18nFacade— the facade the React layer consumes.createI18nextAdapter— the default backend; swap it to use another i18n runtime.createResourceI18n/ResourceI18n— a resource-scoped view of the facade.resolveKey— the fallback-chain resolver, usable standalone.createLocaleRegistry/LocaleRegistry— merges per-source locale contributions by declared priority.localeContributionsFromGlob— turns a bundler glob of per-locale files into contributions.createI18nFromRegistry— wires aLocaleRegistrystraight intocreateI18nextAdapter+createI18n.
License
MIT
