@comvi/core
createI18n() returns the i18n instance: text with {param} interpolation, the cache, events
and default params. The capabilities attach with .use(installer) from a subpath,
/loader, /plugins and
/devtools; /icu is the compiler
option and /rich-text and /tags are the rich-text helpers.
Exports
Section titled “Exports”- Values:
createI18n,I18n,MessagesCache,flattenCatalog,createBoundTranslation,translationResultToString,subscribeToRevision,REVISION_EVENTS,missingCapability,hasLoaderApi,hasPluginApi,formatNumber,formatDate,formatCurrency,formatRelativeTime,getTextDirection,createElement,isVirtualNode. - Types:
I18nOptions,I18nEvent,I18nEventData,TranslationKeys,TranslationParams,TranslationResult,VirtualNode,ElementNode,TextNode,FragmentNode,MessageCompiler,SyntaxExtension,IcuSyntaxError,LocaleSource,RevisionEvent,RevisionEventSource,CapabilityName,I18nPlugin,I18nPluginFactory,PluginOptions,ErrorReportContext,I18nBaseOptions,TranslationValue,MessagesMap,FlattenedMessages,LoaderFn,LoaderResult,MissingKeyInfo,PostProcessFn,TagInterpolationOptions,TagCallbackParams,TranslationParamValue,DefaultTranslationParams,DefaultTranslationParamValue,SetDefaultParamsArg,DefaultParamsSnapshot,HasTranslationKeys,ExtractNamespaces, the instance shapes and the key helpers listed under Types.
createI18n(options)
Section titled “createI18n(options)”function createI18n<D extends DefaultTranslationParams = {}>(options: I18nOptions<D>): I18n<D>const I18n: new <D>(options: I18nOptions<D>) => I18n<D>createI18n(options) is new I18n(options). It throws when locale is missing or
messages is not an object; in development it also checks that every per-locale value is one.
import { createI18n } from '@comvi/core';
export const i18n = createI18n({ locale: 'en', fallbackLocale: 'en', messages: { en: { greeting: 'Hello, {name}!' } },});
await i18n.init();I18nOptions
Section titled “I18nOptions”| Option | Type | Default | Meaning |
|---|---|---|---|
locale |
string |
required | Initial active locale |
defaultNs |
string |
'default' |
Namespace for a call that passes no ns param |
ns |
string[] |
[defaultNs] |
Namespaces init() loads; [] skips initial loading. Needs the loader capability, otherwise init() warns once in development |
messages |
Record<string, Record<string, TranslationValue>> |
none | Inline catalogs keyed 'locale' or 'locale:namespace', flat or nested |
compiler |
MessageCompiler |
simple compiler | Pass icuCompiler for ICU plural, select and selectordinal |
fallbackLocale |
string | readonly string[] |
[] |
Locales consulted, in order, when a key is missing or empty; with a loader, init() and each namespace activation load them too |
missingParam |
'literal' | 'drop' |
'literal' |
Render an absent {name} as itself or as an empty string; null always renders empty |
defaultParams |
DefaultTranslationParams |
none | Non-nullish interpolation defaults merged under every call; locale, ns, fallback, markers, tagInterpolation are rejected |
postProcess |
PostProcessFn |
none | One post-processor, registered first |
onMissingKey |
(info: MissingKeyInfo) => TranslationResult | void |
none | Miss hook, consulted after the fallback chain when neither params.fallback nor a plugin callback supplied a value |
strict |
'dev' | 'off' |
'off' |
'dev' warns on every miss in development builds |
apiKey |
string |
none | Read back by plugins as i18n.apiKey |
collectContext |
boolean |
true |
false opts out of in-context editor context collection, even when the Chrome extension enables the editor |
tagInterpolation |
TagInterpolationOptions |
none | basicHtmlTags, strict, onTagWarning, extensions |
devMode |
boolean |
build flag | Defaults to the build flavour of the dist the bundler resolved: true from the development export condition, false from the default build. Plugins read it |
onError |
(error: Error, context?: ErrorReportContext) => void |
none | Global error sink |
exposeGlobal |
boolean |
none | Ignored by a bare instance, even one with .use(devtools()); init() warns once in development when true. Pass it to devtools({ exposeGlobal }) instead |
instanceId |
string |
none | Same: pass it to devtools({ instanceId }) instead |
.use(installer)
Section titled “.use(installer)”use<T extends this>(installer: (instance: this) => T): Ti18n.use(f) calls f(i18n) and returns the widened instance. The installer has to return
the instance it was given; anything else throws with code: 'E_USE_NOT_INSTALLER'. A raw
plugin goes through definePlugin(fn) or registerPlugin(fn) instead.
import { createI18n } from '@comvi/core';import { loader } from '@comvi/core/loader';import { plugins } from '@comvi/core/plugins';
export const i18n = createI18n({ locale: 'en' }) .use(loader({ uk: () => import('./locales/uk.json') })) .use(plugins());Properties
Section titled “Properties”| Property | Type | Meaning |
|---|---|---|
locale |
string (get/set) |
The setter is fire-and-forget; a rejected switch emits loadError with namespace: 'locale-change' |
isLoading |
boolean |
A load is in flight |
isInitializing |
boolean |
init() is running |
isInitialized |
boolean |
init() completed |
apiKey |
string | undefined |
Readonly, from the option |
collectContext |
boolean | undefined |
Readonly, from the option |
devMode |
boolean |
Readonly |
instanceId |
string | undefined |
Assigned by the devtools capability |
messagesCache |
MessagesCache |
Readonly, see MessagesCache |
defaultParams |
DefaultParamsSnapshot<D> |
Readonly shallow snapshot, copied on every read |
configRevision |
number |
Bumps on every configChanged |
localeRevision |
number |
For framework bindings: how many times the locale has changed, bumped before localeChanged listeners run |
isInitializing belongs to init() alone: a locale detector that calls setLocale()
mid-init flips isLoading, never isInitializing. The flag clears when init() settles,
whether it resolved or threw.
Methods
Section titled “Methods”init(): Promise<this>t(key, params?): stringtRaw(key, params?): TranslationResultaddMessages(messages: MessagesMap): voidgetMessages(locale?: string, namespace?: string): FlattenedMessageshasLocale(locale: string, namespace?: string): booleanhasTranslation(key: string, locale?: string, namespace?: string, checkFallbacks?: boolean): booleansetLocale(value: string): Promise<void>setFallbackLocale(fallback: string | readonly string[]): voidsetDefaultParams(params: SetDefaultParamsArg<D>): voidsetDefaultNamespace(namespace: string): voidgetDefaultNamespace(): stringgetActiveNamespaces(): string[]getFallbackLocales(): string[]getLoadedLocales(): string[]withMessages(catalogs: Readonly<Record<string, FlattenedMessages>>): I18nMessagesView<D>clearMessages(locale?: string, namespace?: string): voidon<E extends I18nEvent>(event: E, callback: (data: I18nEventData[E]) => void): () => voidreportError(error: unknown, context?: ErrorReportContext): voiddestroy(): Promise<void>| Method | Meaning |
|---|---|
init() |
Runs plugin init hooks and the locale detector (plugin capability), then loads the initial namespaces. Rejects after destroy() |
t(key, params?) |
Translate to a string; t(null) returns ''. One call signature: the key union is AcceptedCoreKey, params dispatch through ArgsFor |
tRaw(key, params?) |
Same lookup, returns string | Array<string | VirtualNode> |
addMessages(t) |
Merge catalogs keyed 'locale' or 'locale:namespace', flat or nested; every namespace carried is activated and emits namespaceLoaded |
getMessages(l?, ns?) |
Defaults to the current locale and default namespace |
hasLocale(l, ns?) |
Cache check, no fallback chain |
hasTranslation(key, l?, ns?, fb?) |
Key check; an empty value is not a translation; checkFallbacks (default false) walks the chain |
setLocale(l) |
Synchronous with nothing composed; the loader capability overrides it to load first. A rapid second call supersedes the first silently |
setFallbackLocale(fb) |
Emits configChanged with source 'fallbackLocale' |
setDefaultParams(p) |
Replaces defaults, emits configChanged with source 'defaultParams'; constructor-guaranteed keys cannot be removed |
setDefaultNamespace(ns) |
Emits defaultNamespaceChanged |
getDefaultNamespace(), getActiveNamespaces(), getFallbackLocales(), getLoadedLocales() |
Snapshots, not live |
withMessages(catalogs) |
A read view with three methods and nothing else, t, tRaw and hasTranslation, which read each key from catalogs first and from the cache otherwise: the merge addMessages(catalogs) would make, without making it. The view never writes the instance (no cache entry, no locale, no active namespace) and never emits namespaceLoaded or localeChanged, so a dropped view leaves nothing behind. Diagnostics are not publication: a missing key emits missingKey and a throwing post-processor reaches onError, as for any t(). A later addMessages() reaches the view through the cache for every key catalogs does not hold. catalogs is keyed 'locale:namespace' and holds flat catalogs, as flattenCatalog returns them |
clearMessages(l?, ns?) |
Clears content only, emits messagesCleared; deactivation is removeActiveNamespace() on the loader capability |
on(event, cb) |
Returns an unsubscribe function |
reportError(e, ctx?) |
Routes to onError; the same Error object is reported once per instance |
destroy() |
Idempotent. Removes discovery, runs plugin cleanups in LIFO order, emits destroyed, clears subscriptions, active namespaces, post-processors, cache, loader and detector |
A destroy() that lands while init() is awaiting makes init() resolve quietly with
isInitialized still false: no initialized event, no error report. That is what happens
when a component unmounts with a fire-and-forget init() in flight.
Lookup on a miss: emit missingKey, run the plugin onMissingKey callbacks, then return
params.fallback if set, else the first defined plugin result, else the constructor
onMissingKey result, else the key itself.
Events
Section titled “Events”| Event | Payload | Fires when |
|---|---|---|
initialized |
void |
init() finished |
destroyed |
void |
destroy() finished |
localeChanged |
{ from: string; to: string } |
the locale changed |
defaultNamespaceChanged |
{ from: string; to: string } |
the default namespace changed |
messagesCleared |
{ locale?: string; namespace?: string } |
clearMessages() ran |
loadingStateChanged |
{ isLoading: boolean; isInitializing: boolean } |
a loading flag flipped |
namespaceLoaded |
{ namespace: string; locale: string } |
a catalog landed |
missingKey |
{ key: string; locale: string; namespace: string } |
a lookup missed |
loadError |
{ locale: string; namespace: string; error: Error } |
a load failed |
configChanged |
{ source: 'fallbackLocale' | 'namespaceActivated' | 'namespaceDeactivated' | 'defaultParams' } |
config changed |
subscribeToRevision(i18n, cb) subscribes to the seven events in REVISION_EVENTS
(localeChanged, namespaceLoaded, loadingStateChanged, initialized, messagesCleared,
defaultNamespaceChanged, configChanged) and returns one unsubscribe.
Formatting functions
Section titled “Formatting functions”function formatNumber(i18n: LocaleSource, value: number, options?: Intl.NumberFormatOptions, locale?: string): stringfunction formatDate(i18n: LocaleSource, value: Date | number, options?: Intl.DateTimeFormatOptions, locale?: string): stringfunction formatCurrency(i18n: LocaleSource, value: number, currency: string, options?: Intl.NumberFormatOptions, locale?: string): stringfunction formatRelativeTime(i18n: LocaleSource, value: number, unit: Intl.RelativeTimeFormatUnit, options?: Intl.RelativeTimeFormatOptions, locale?: string): stringfunction getTextDirection(locale: string): 'ltr' | 'rtl'Free functions, not methods. LocaleSource is { locale: string }, so any i18n instance
qualifies as the first argument. getTextDirection takes a locale string. Formatters are
cached per locale and options.
Helpers
Section titled “Helpers”function flattenCatalog(nested: Record<string, any>): FlattenedMessagesfunction createBoundTranslation<D, NS>(i18n: { t; tRaw? }, ns?: NS): NamespaceBoundTranslateFn<NS, D, TranslationResult> // any object with the instance's t(), such as the instance or a withMessages() viewfunction translationResultToString(result: TranslationResult): stringfunction subscribeToRevision(i18n: RevisionEventSource, callback: (event: RevisionEvent) => void): () => voidfunction missingCapability(name: 'loader' | 'plugins'): Errorfunction hasLoaderApi(instance: WrapperI18nInstance): instance is WrapperI18nInstance & I18nLoaderApifunction hasPluginApi(instance: WrapperI18nInstance): instance is WrapperI18nInstance & I18nPluginApifunction createElement(tag: string, props?: Record<string, unknown>, children?: Array<VirtualNode | string>): ElementNodefunction isVirtualNode(value: unknown): value is VirtualNodeflattenCatalog is optional: the instance flattens nested catalogs itself. Non-string leaves
are coerced with String(), null and undefined leaves are dropped with a development
warning.
hasLoaderApi and hasPluginApi are runtime guards: each checks that every member of
the capability is a real function, so the development stand-ins attachPlugins installs for
loader members do not count.
MessagesCache
Section titled “MessagesCache”new MessagesCache(options?: { defaultNs?: string })get(locale: string, namespace?: string): FlattenedMessages | undefinedset(locale: string, namespace: string, translations: FlattenedMessages): voidhas(locale: string, namespace?: string): booleandelete(locale: string, namespace?: string): voidclear(): voidkeys(): IterableIterator<string> // 'locale:namespace'getLocales(): string[]getRevision(): numberclone(): Map<string, FlattenedMessages>getInternalMap(): ReadonlyMap<string, FlattenedMessages>get size(): number| Type | Meaning |
|---|---|
TranslationKeys |
Augment it to type t(); see Type-check translation keys |
InferKeys<T, NS?, DefaultNS?> |
Keys inferred from a JSON catalog object, prefixed NS: unless NS equals DefaultNS; params are never |
DefaultNsKeys, Namespaces, NamespacedKeys<NS>, NamespacedKeyParams<NS, K>, PermissiveKey |
Key helpers derived from TranslationKeys |
ParamsArg, NamespacedParamsArg, NamespaceBoundParamsArg, AcceptedKey, AcceptedCoreKey, ArgsFor |
The single call signature’s parts |
TranslateFn<D, R>, NamespaceBoundTranslateFn<NS, D, R> |
The t / tRaw shapes bindings return |
I18nCoreInstance<D> |
Lookup, cache and events surface |
I18nCoreExtraApi |
init, destroy, setLocale, instanceId, withMessages, the snapshot getters |
MessagesMap |
Record<string, Record<string, TranslationValue>>: catalogs keyed 'locale' or 'locale:namespace', the shape addMessages() and the messages option take. Re-exported by @comvi/react, @comvi/next and @comvi/nuxt |
I18nMessagesView<D> |
What withMessages() returns: t, tRaw and hasTranslation of I18nCoreInstance |
WrapperI18nInstance<D> |
I18nCoreInstance & I18nCoreExtraApi, what every binding requires and a bare instance satisfies |
AnyI18nInstance |
WrapperI18nInstance with setDefaultParams and init widened, what providers accept |
I18nLoaderApi, I18nPluginApi |
The two capability member sets |
PluginI18nInstance<D> |
WrapperI18nInstance & I18nLoaderApi & I18nPluginApi, what a plugin may rely on |
I18nInstance<D> |
A composed instance: I18nCoreInstance plus reloadMessages, setPluginData, getPluginData |
VirtualNode |
ElementNode | TextNode | FragmentNode; TranslationResult is string | Array<string | VirtualNode> |
TagCallback |
(params: { children: TranslationResult; name: string }) => VirtualNode | string |
IcuSyntaxError |
Error & { code: 'E_ICU_SYNTAX'; argumentType: string } |
ErrorReportContext |
{ source; pluginName?; tagName?; key?; locale?; namespace?; event? } with source one of plugin, plugin-cleanup, request-cleanup, init, translation, namespace-load, post-processor, event, setLocale, compile |
ComviQueue, ComviQueueEntry, ComviHook |
The window.__COMVI__ protocol, see /devtools |
Read next
Section titled “Read next”@comvi/core/loader:loader(map),attachLoaderand namespace activation.@comvi/core/plugins:plugins(),definePlugin()and the plugin members.- Handle missing keys and errors:
onError,reportErrorand the error codes.