SolidJS i18n Integration
Comvi’s SolidJS integration uses signals and context providers for fine-grained reactivity. Translations update only the DOM nodes that depend on them, with no unnecessary re-renders.
Installation
Section titled “Installation”pnpm add @comvi/solid @comvi/core @comvi/plugin-fetch-loader-
Create the i18n instance
src/i18n.ts import { createI18n } from '@comvi/core';import { FetchLoader } from '@comvi/plugin-fetch-loader';export const i18n = createI18n({locale: 'en',fallbackLocale: 'en',}).use(FetchLoader({cdnUrl: 'https://cdn.comvi.io/your-distribution-id',})); -
Wrap your app with the provider
src/index.tsx import { render } from 'solid-js/web';import { I18nProvider } from '@comvi/solid';import { i18n } from './i18n';import App from './App';await i18n.init();render(() => (<I18nProvider i18n={i18n}><App /></I18nProvider>),document.getElementById('root')!,); -
Use in components
src/components/Greeting.tsx import { useI18n } from '@comvi/solid';function Greeting() {const { t } = useI18n();return (<div><h1>{t('hello.world')}</h1><p>{t('welcome.message', { name: 'Alice' })}</p></div>);}
useI18n() Hook
Section titled “useI18n() Hook”The primary hook for accessing translations and locale state. All returned values are reactive signals.
import { useI18n } from '@comvi/solid';
function MyComponent() { const { t, locale, isLoading, setLocale } = useI18n();
return ( <Show when={!isLoading()} fallback={<p>Loading...</p>}> <h1>{t('page.title')}</h1> <p>Current locale: {locale()}</p> </Show> );}Returned values:
| Property | Type | Description |
|---|---|---|
t | (key, params?) => string | Translate a key with optional parameters |
locale | Accessor<string> | Current language (reactive signal) |
isLoading | Accessor<boolean> | Whether translations are being loaded |
setLocale | (lang: string) => Promise<void> | Switch to a different locale |
Changing Language
Section titled “Changing Language”Use the setLocale function returned from useI18n(). Because Solid uses signals, all translated strings update automatically:
import { useI18n } from '@comvi/solid';
function LanguageSwitcher() { const { locale, setLocale } = useI18n();
return ( <select value={locale()} onChange={(e) => setLocale(e.target.value)} > <option value="en">English</option> <option value="de">Deutsch</option> <option value="fr">Francais</option> </select> );}The <T> Component
Section titled “The <T> Component”For translations containing HTML or nested components, use <T> for safe interpolation:
import { T } from '@comvi/solid';
function LegalNotice() { return ( // Translation: "Read our <link>terms of service</link>" <T i18nKey="legal.tos" components={{ link: (props) => <a href="/terms">{props.children}</a>, }} /> );}Namespaces
Section titled “Namespaces”Load translations from a specific namespace:
function Dashboard() { const { t } = useI18n('dashboard');
return <h1>{t('page.title')}</h1>;}Reactive Patterns
Section titled “Reactive Patterns”SolidJS signals make Comvi translations fine-grained reactive. Here are common patterns.
Derived Translations
Section titled “Derived Translations”Use createMemo when you need a derived translation value:
import { createMemo } from 'solid-js';import { useI18n } from '@comvi/solid';
function Greeting(props: { name: string }) { const { t } = useI18n();
const greeting = createMemo(() => t('greeting', { name: props.name }) );
return <p>{greeting()}</p>;}Side Effects on Language Change
Section titled “Side Effects on Language Change”Run logic whenever the locale changes with createEffect:
import { createEffect } from 'solid-js';import { useI18n } from '@comvi/solid';
function DocumentLang() { const { locale } = useI18n();
createEffect(() => { document.documentElement.lang = locale(); });
return null;}Conditional Loading
Section titled “Conditional Loading”Show loading states while translations are fetched:
import { Switch, Match } from 'solid-js';import { useI18n } from '@comvi/solid';
function Page() { const { t, isLoading } = useI18n();
return ( <Switch> <Match when={isLoading()}> <div class="skeleton">Loading...</div> </Match> <Match when={!isLoading()}> <h1>{t('page.title')}</h1> </Match> </Switch> );}Lazy Loading
Section titled “Lazy Loading”Combine the fetch loader with lazy loading to fetch translations on demand:
import { createI18n } from '@comvi/core';import { FetchLoader } from '@comvi/plugin-fetch-loader';
export const i18n = createI18n({ locale: 'en', fallbackLocale: 'en',}) .use(FetchLoader({ cdnUrl: 'https://cdn.comvi.io/your-distribution-id', // Only load translations when needed (default behavior) }));Translations are fetched when the user switches locales. Already-loaded locales are served from cache.
SSR with SolidStart
Section titled “SSR with SolidStart”For server-side rendering with SolidStart, initialize translations in the route’s data function so they are available before rendering.
Route Data Loading
Section titled “Route Data Loading”import { createAsync } from '@solidjs/router';import { i18n } from '../i18n';
export const route = { load: () => { return i18n.init(); },};Root Layout
Section titled “Root Layout”Set the HTML lang attribute in your root layout:
import { useI18n, I18nProvider } from '@comvi/solid';import { i18n } from './i18n';
export default function Root() { return ( <I18nProvider i18n={i18n}> <Html /> </I18nProvider> );}
function Html() { const { locale } = useI18n();
return ( <html lang={locale()}> <head> <meta charset="utf-8" /> </head> <body> {/* your app */} </body> </html> );}TypeScript
Section titled “TypeScript”Enable type-safe translations with auto-generated types. The Comvi CLI emits a .d.ts that augments the TranslationKeys interface in @comvi/core — importing it once anywhere in your project gives t() autocomplete and parameter validation:
import { createI18n } from '@comvi/core';// Side-effect import — augments @comvi/core's TranslationKeys interfaceimport './types/i18n';
export const i18n = createI18n({ locale: 'en', fallbackLocale: 'en',});Now t('home.title') autocompletes keys and t('greeting', { name }) validates parameters at compile time. See Type-Safe Translations for the full setup.