Fetch loader
With an i18n instance from the Quick start and a Comvi project with
Content Deploy enabled as on Connect to Comvi, fetch each
locale’s JSON over HTTP with @comvi/plugin-fetch-loader. The root entry reads the public
CDN and is the smaller one; the /api entry also talks to the platform API.
Install
Section titled “Install”npm install @comvi/plugin-fetch-loaderpnpm add @comvi/plugin-fetch-loaderyarn add @comvi/plugin-fetch-loaderbun add @comvi/plugin-fetch-loaderLoad from the CDN
Section titled “Load from the CDN”import { createRoot } from 'react-dom/client';import { createI18n, I18nProvider } from '@comvi/react';import { cdnLoader } from '@comvi/plugin-fetch-loader';import App from './App';
const i18n = createI18n({ locale: 'en', fallbackLocale: 'en',}).use(cdnLoader({ cdnUrl: 'https://cdn.comvi.io/<project-token>' }));
createRoot(document.getElementById('root')!).render( <I18nProvider i18n={i18n} fallback={<p>Loading…</p>}> <App /> </I18nProvider>,);.use(cdnLoader()) adds HTTP loading to the i18n instance. fallback
paints until the first catalog is in. Omitting cdnUrl throws at composition time with
[@comvi/plugin-fetch-loader] cdnUrl is required. On Vue the same .use() goes on the
wrapper createI18n returns; on Nuxt it goes in the comvi.i18n.ts factory.
The CDN endpoint URL is under Settings → Content Deploy in your Comvi project, once Content
Deploy is enabled and saved; pass it as cdnUrl. The URL format is on
CDN Deployment.
cdnLoader(options) returns an installer: it takes the instance, composes the loader and
plugin capabilities onto it, queues cdnLoaderPlugin(options) and returns the instance widened
with registerLoader, reloadMessages and the rest of the loader surface. The plugin itself
runs inside init(): it registers a load function for (locale, namespace) pairs, and core
then loads the active namespaces through it, for the active locale and for each locale in
fallbackLocale. setLocale() loads the target locale only. Compose the loader before
init(); a plugin queued afterwards does not run until the next init().
A catalog with ICU messages ({count, plural, …}) needs compiler: icuCompiler on the
instance; see Pluralize with ICU.
Bundled fallback
Section titled “Bundled fallback”import { createI18n } from '@comvi/core';import { cdnLoader } from '@comvi/plugin-fetch-loader';
export const i18n = createI18n({ locale: 'en', fallbackLocale: 'en' }).use( cdnLoader({ cdnUrl: 'https://cdn.comvi.io/<project-token>', fallback: { en: () => import('./locales/en.json'), de: () => import('./locales/de.json'), 'en:admin': () => import('./locales/en/admin.json'), }, }),);
await i18n.init();When a network load fails, the loader serves the matching import: the exact
'locale:namespace' key first, then the 'locale' shorthand for the default namespace. A
fallback that resolves reports through onLoadSuccess, not onLoadError, because from the
instance’s point of view the catalog arrived.
CDN mode vs API mode
Section titled “CDN mode vs API mode”import { createI18n } from '@comvi/core';import { fetchLoader } from '@comvi/plugin-fetch-loader/api';
export const i18n = createI18n({ locale: 'en', apiKey: process.env.COMVI_API_KEY,}).use( fetchLoader({ cdnUrl: 'https://cdn.comvi.io/<project-token>', baseUrl: process.env.COMVI_API_URL, }),);
await i18n.init();fetchLoader() from the /api entry reads the CDN, and switches to the platform API when the
instance carries an apiKey: requests go out with Authorization: Bearer <apiKey>, one
request carries every active namespace, and draft translations and private projects are
reachable. The key needs project read and translation read access. cdnLoader() never builds
an API URL, so an apiKey changes nothing there. The /api entry ships the API request path
either way, which is why the root entry is smaller
(Bundle size).
Options
Section titled “Options”| Option | Default | Entry | Behaviour |
|---|---|---|---|
cdnUrl |
required | both | The CDN base URL from Settings → Content Deploy. |
baseUrl |
https://api.comvi.io |
/api |
The API origin. Precedence: baseUrl, then NEXT_PUBLIC_COMVI_API_URL, then the preset. The variable is read from process.env on the server; the name follows Next’s convention, and only Next can inline it into a browser bundle. Read only when the instance has an apiKey. Unrelated to Nuxt’s useLocaleHead({ baseUrl }). |
fallback |
none | both | Import map served when a network load fails. |
onLoadError |
none | both | (locale, namespace, error) when a pair fails and no fallback produced data. The instance’s loadError event fires too. |
onLoadSuccess |
none | both | (locale, namespace) when a pair loads, including from fallback. |
timeout |
10000 |
both | Per-request budget in milliseconds, covering the body. |
retry |
true |
/api |
Retries the project-info request after a 429, 5xx or transport failure, twice at most. Timeouts and translation loads, CDN or API, are never retried; the root entry has no retry at all. |
loadOnInit |
true |
both | Whether the plugin loads at install time. Inside init() the instance’s own load is the only one, so the flag matters only when the factory runs by hand. Pass ns: [] to createI18n to start with no request. |
cdnLayout |
{ rootNamespace: defaultNs } |
both | Which namespace lives at {cdnUrl}/{locale}.json. |
cache |
none | both | { revalidate, tags } forwarded to fetch() as Next.js’s next option, which Next’s patched fetch honours; Nitro ignores it. revalidate: 0 always refetches, false caches indefinitely. |
Both callbacks are read once, at install time, and neither fires after the plugin’s cleanup.
Naming an /api-only option on the root entry is a compile error. Concurrent requests for one
locale:namespace are deduplicated, clearMessages() aborts the loads it covers, and
destroy() aborts everything in flight.
A failed pair emits loadError and calls your onLoadError. When other namespaces for the
same locale loaded, the miss is reported through onError and t() keeps rendering from the
cache, walking fallbackLocale for the keys that never arrived. When every requested
namespace fails, the load rejects, so await i18n.init() or setLocale() throws. A failed
load of a fallbackLocale catalog is reported the same way and never rejects.
CDN layout
Section titled “CDN layout”import { createI18n } from '@comvi/core';import { cdnLoader } from '@comvi/plugin-fetch-loader';
export const i18n = createI18n({ locale: 'en', defaultNs: 'storefront', ns: ['storefront', 'default'],}).use( cdnLoader({ cdnUrl: 'https://cdn.comvi.io/<project-token>', cdnLayout: { rootNamespace: 'default' }, }),);
await i18n.init();| Requested namespace | URL |
|---|---|
default |
{cdnUrl}/en.json |
storefront |
{cdnUrl}/storefront/en.json |
By default the instance’s defaultNs loads from {cdnUrl}/{locale}.json and every other
namespace from {cdnUrl}/{namespace}/{locale}.json. Set rootNamespace when the namespace
stored at the CDN root differs from your defaultNs, or rootNamespace: false to put every
namespace in its own folder. API mode always names the namespaces it requests.
Read next
Section titled “Read next”- Connect to Comvi: the CDN URL, the status filter and the first wording change.
- Load translations on demand: import maps, custom loaders and namespaces.
- Handle missing keys and errors:
loadError,onLoadErrorandonError.