Plugin System
A plugin is a function that receives the i18n instance and wires extra behaviour into it — a loader, a locale detector, a translation post-processor, an event listener, or any combination. The official plugins (FetchLoader, LocaleDetector, InContextEditorPlugin) are built on the same contract you’d use for your own.
Quick Example
Section titled “Quick Example”import { createI18n } from "@comvi/core";import type { I18nPluginFactory } from "@comvi/core";
const Logger: I18nPluginFactory<{ prefix?: string }> = (options = {}) => function LoggerPlugin(i18n) { const prefix = options.prefix ?? "[i18n]"; const unsubscribe = i18n.on("localeChanged", ({ from, to }) => { console.log(`${prefix} ${from} → ${to}`); });
return unsubscribe; // cleanup runs on i18n.destroy() };
const i18n = createI18n({ locale: "en" }) .use(Logger({ prefix: "[app]" }));
await i18n.init();Most configurable plugins use two layers: a factory that takes options and returns a plugin function. The plugin function runs during init() and may return a cleanup function that runs on destroy().
Lifecycle
Section titled “Lifecycle”use() only queues the plugin — nothing runs until init().
- Plugin functions run in registration order (FIFO). Each gets its own timeout.
- After all plugins, the registered locale detector (if any) runs and may switch the locale.
- Initial namespaces are loaded last, using whatever loader the plugins registered.
- On
destroy(), cleanup functions run in reverse order (LIFO) and are awaited.
Registration Options
Section titled “Registration Options”i18n.use(plugin, options) accepts an optional second argument:
| Option | Default | Behaviour |
|---|---|---|
required | true | A throw or timeout from the plugin function aborts init(). Set to false to report the error and continue. |
timeout | 10000 ms | Per-plugin-function budget. Exceeding it rejects with a timeout error. |
onError | — | Called before the default error pipeline if the plugin function fails. Throws inside onError are swallowed. |
i18n.use(MyPlugin({ apiKey: "..." }), { required: false, timeout: 5000, onError: (err) => analytics.captureException(err),});A factory that throws synchronously (e.g. on invalid options) fails before use() even sees a plugin function — PluginOptions do not apply to that path.