Skip to content

@comvi/svelte API Reference

@comvi/svelte provides Svelte store-based reactivity, context functions for setup, and a <T> component for safe rich-text interpolation. It supports both Svelte 4 and Svelte 5. It builds on top of @comvi/core and re-exports all core APIs.

Unlike React or SolidJS providers, Svelte uses its built-in setContext/getContext API. Call setI18nContext() in your root component.

Sets the i18n instance in Svelte context. Must be called in the root component (e.g., App.svelte or +layout.svelte).

interface SetI18nContextOptions {
autoInit?: boolean; // default true
}
<script>
import { setI18nContext } from '@comvi/svelte';
import { i18n } from './lib/i18n';
setI18nContext(i18n); // auto-inits in microtask
// setI18nContext(i18n, { autoInit: false }); // to disable
</script>
<slot />

Auto-init is a microtask (occurs before renders but not immediately). Pass { autoInit: false } to disable.

Gets the i18n instance from Svelte context. Throws if called outside of a component with context set.

<script>
import { getI18nContext } from '@comvi/svelte';
const i18n = getI18nContext();
// Direct access to the i18n instance
const unsub = i18n.on('localeChanged', ({ to }) => {
console.log('Language changed to:', to);
});
</script>

The primary function for translating strings and reading i18n state. Returns an object with Svelte readable stores and i18n methods.

<script>
import { useI18n } from '@comvi/svelte';
const { t, locale, setLocale, isLoading } = useI18n();
</script>
{#if $isLoading}
<p>Loading...</p>
{:else}
<h1>{$t('hello.world')}</h1>
<p>{$t('greeting', { name: 'Alice' })}</p>
<p>Current locale: {$locale}</p>
{/if}
useI18n(ns?: string): UseI18nReturn

Pass an optional namespace string to scope all $t() calls to that namespace.

interface UseI18nReturn {
t: Readable<(key: string, params?: TranslationParams) => string>;
tRaw: Readable<(key: string, params?: TranslationParams) => TranslationResult>;
locale: Readable<string>;
isLoading: Readable<boolean>;
isInitializing: Readable<boolean>;
isInitialized: Readable<boolean>;
cacheRevision: Readable<number>;
// methods (direct refs):
setLocale, addTranslations, addActiveNamespace, setFallbackLocale,
onMissingKey, onLoadError, clearTranslations, reloadTranslations,
hasLocale, hasTranslation, getLoadedLocales, getActiveNamespaces,
getDefaultNamespace, getTranslationCache, on, reportError,
formatNumber, formatDate, formatCurrency, formatRelativeTime,
dir: Readable<"ltr" | "rtl">
}

Use as $t('key'), $locale, $dir. Stores are memoized per i18n + namespace.

onMissingKey(cb) and onLoadError(cb) are bound straight to the core instance and return a cleanup function. The Svelte binding does not narrow the missing-key return type — the callback signature is (key: string, locale: string, namespace: string) => TranslationResult | void (matching the core instance method). Register them once (e.g. in your root layout) and call the cleanup in onDestroy.

<script>
const { t } = useI18n('dashboard');
</script>
<!-- Keys are scoped — no need for { ns: 'dashboard' } -->
<h1>{$t('page.title')}</h1>

In Svelte 5, use $derived for computed translations:

<script>
import { useI18n } from '@comvi/svelte';
const { t, locale } = useI18n();
let name = $state('Alice');
// Derived state updates when language or name changes
const greeting = $derived($t('greeting', { name }));
</script>
<p>{greeting}</p>

The <T> component renders translations containing rich content (HTML tags, styled elements) safely.

<script>
import { T } from '@comvi/svelte';
</script>

The <T> component accepts these props:

Prop Type Notes
i18nKey keyof TranslationKeys | PermissiveKey required
params TranslationParams interpolation params
ns string namespace
locale string override locale
fallback string fallback text if the key is missing (slotted children are used otherwise)
raw boolean forwards raw: true to post-processors that support it, such as the in-context editor marker injector
components ComponentMap tag map (HTML tag names or { tag, props } — no component functions)
allowedTags Set<string> override the default safe-tag allow-list used when emitting {@html}
<!-- Translation: "Hello, {name}!" -->
<T i18nKey="greeting" params={{ name: 'Alice' }} />
<!-- Translation: "Read our <link>terms of service</link> and <bold>privacy policy</bold>" -->
<T
i18nKey="legal.tos"
components={{
link: { tag: 'a', props: { href: '/terms', class: 'text-blue-600' } },
bold: 'strong',
}}
/>

Svelte’s ComponentMap supports two kinds of mappings:

<T
i18nKey="rich.message"
components={{
// 1. HTML tag name
bold: 'strong',
// 2. Tag with props
link: { tag: 'a', props: { href: '/about', class: 'underline' } },
icon: { tag: 'span', props: { class: 'icon icon-info' } },
}}
/>

For advanced use cases, @comvi/svelte exports low-level store creators that bridge the i18n event system to Svelte readable stores. All stores are memoized per i18n instance.

Store Creator Returns Tracks
createLanguageStore(i18n) Readable<string> localeChanged events
createLoadingStore(i18n) Readable<boolean> loadingStateChanged events
createInitializingStore(i18n) Readable<boolean> loadingStateChanged events
createInitializedStore(i18n) Readable<boolean> initialized / destroyed events
createCacheRevisionStore(i18n) Readable<number> namespaceLoaded / translationsCleared events
<script>
import { createLanguageStore, createLoadingStore } from '@comvi/svelte';
import { getI18nContext } from '@comvi/svelte';
const i18n = getI18nContext();
const locale = createLanguageStore(i18n);
const isLoading = createLoadingStore(i18n);
</script>
<p>Locale: {$locale}, Loading: {$isLoading}</p>

@comvi/svelte re-exports createI18n, I18n, and all types from @comvi/core. Import other core value exports from @comvi/core directly.

See the @comvi/core API Reference for the full list.