Skip to content

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.

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().

use() only queues the plugin — nothing runs until init().

  1. Plugin functions run in registration order (FIFO). Each gets its own timeout.
  2. After all plugins, the registered locale detector (if any) runs and may switch the locale.
  3. Initial namespaces are loaded last, using whatever loader the plugins registered.
  4. On destroy(), cleanup functions run in reverse order (LIFO) and are awaited.

i18n.use(plugin, options) accepts an optional second argument:

OptionDefaultBehaviour
requiredtrueA throw or timeout from the plugin function aborts init(). Set to false to report the error and continue.
timeout10000 msPer-plugin-function budget. Exceeding it rejects with a timeout error.
onErrorCalled 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.