Skip to content

Features Overview

A scrollable showcase. Each section has the smallest example that captures the feature, then a link to the full guide. Most demos use the framework-agnostic @comvi/core; tag rendering, reactivity, and SSR show one framework binding where it matters.

All examples below assume this minimal instance. Translation values are added inline per section to keep each demo self-contained.

src/i18n.ts
import { createI18n } from '@comvi/core';
const i18n = createI18n({
locale: 'en',
fallbackLocale: 'en',
translation: {
en: { /* values shown per section below */ },
},
});
await i18n.init();

Look up a translation by key. Pass {name} placeholders to interpolate runtime parameters.

{ "greeting": "Hello, {name}!" }
i18n.t('greeting', { name: 'Alice' });
// → "Hello, Alice!"
i18n.t('missing.key', { fallback: 'Hi!' });
// → "Hi!"

Translation Function →

ICU plural, select, and selectordinal work for every language — including ones with complex rules like Ukrainian, Polish, and Arabic. Backed by Intl.PluralRules.

{
"cart.items": "{count, plural, =0 {Cart is empty} one {# item} other {# items}}"
}
i18n.t('cart.items', { count: 0 }); // → "Cart is empty"
i18n.t('cart.items', { count: 1 }); // → "1 item"
i18n.t('cart.items', { count: 5 }); // → "5 items"

Pluralization & ICU →

Translators write tags inside the sentence; your code maps them to real components. Word order stays in the translator’s hands — no string concatenation in your UI.

{ "consent": "I agree to the <tos>Terms of Service</tos>." }
React
import { T } from '@comvi/react';
<T
i18nKey="consent"
components={{ tos: <a href="/terms" /> }}
/>
// → "I agree to the <a href="/terms">Terms of Service</a>."

Tag Interpolation →

Split keys into logical groups — per page, per feature. Namespaces load on demand: only the strings the user actually needs.

admin/en.json
{ "title": "Admin Dashboard" }
await i18n.addActiveNamespace('admin');
i18n.t('title', { ns: 'admin' }); // → "Admin Dashboard"

Namespaces →

Change the active locale at runtime. Translations for the new locale load automatically.

await i18n.setLocaleAsync('de');
i18n.t('greeting', { name: 'Alice' });
// → "Hallo, Alice!"

setLocaleAsync() waits for namespace loading; i18n.locale = 'de' is fire-and-forget.

Language Switching →

Framework bindings expose locale, loading state, and the translation function as reactive primitives. Components re-render automatically when the locale changes or new translations load — no manual subscriptions.

React
import { useI18n } from '@comvi/react';
function Greeting() {
const { t, locale, setLocale } = useI18n();
return (
<>
<p>{t('greeting', { name: 'Alice' })}</p>
<button onClick={() => setLocale('de')}>Deutsch</button>
</>
);
}

The same pattern works in Vue (refs), Solid (Accessors), and Svelte (Readable stores).

Language Switching →

Plug in a loader for runtime fetch (CDN), keep them static at build time, or write a custom one. With the Fetch Loader, translators ship updates without redeploying the app.

import { FetchLoader } from '@comvi/plugin-fetch-loader';
createI18n({ locale: 'en' })
.use(FetchLoader({
cdnUrl: 'https://cdn.comvi.io/your-distribution-id',
}));

The Fetch Loader respects HTTP cache headers and deduplicates concurrent requests for the same locale/namespace.

Loading Translations →

Locale-aware formatting for numbers, dates, currency, and relative time — Intl.* under the hood. Switch locale and the formatters follow automatically.

i18n.formatNumber(1234567.89); // → "1,234,567.89"
i18n.formatCurrency(1234.5, 'USD'); // → "$1,234.50"
i18n.formatDate(new Date(), { dateStyle: 'long' }); // → "May 1, 2026"
i18n.formatRelativeTime(-3, 'day'); // → "3 days ago"

Formatting →

Generated types augment @comvi/core’s TranslationKeys interface. Typos and missing parameters become compile-time errors.

declare module '@comvi/core' {
interface TranslationKeys {
'greeting': { name: string };
}
}
i18n.t('greeting', { name: 'Alice' }); // ✅
i18n.t('greting', { name: 'Alice' }); // ❌ TS2345: not assignable
i18n.t('greeting'); // ❌ missing param 'name'

The Comvi CLI (comvi typegen) generates this declaration automatically from your translations.

Type Safety →

Hooks for missing keys, load failures, and broader runtime errors. Comvi runs in resilient mode by default — errors are reported, not thrown.

createI18n({
locale: 'en',
onMissingKey: ({ key, locale, namespace }) => {
console.warn(`Missing: ${key} (${locale}/${namespace})`);
return `[!${key}]`; // final fallback after locales are checked
},
onError: (error, ctx) => {
Sentry.captureException(error, { extra: ctx });
},
});

Error Handling →

Pre-load translations on the server, render real text, hydrate cleanly on the client. Built-in helpers for Next.js and Nuxt; React/Vue can pass SSR state explicitly.

Next.js — app/[locale]/layout.tsx
import { setRequestLocale, loadTranslations } from '@comvi/next/server';
export default async function Layout({ params }) {
const { locale } = await params;
setRequestLocale(locale);
await loadTranslations(locale);
return /* ...children with <I18nProvider>... */;
}

No FOUC, no hydration mismatch.

Server-Side Rendering →