# @comvi/solid

API reference for @comvi/solid: I18nProvider, useI18n, useI18nInstance, useI18nLoader and useI18nPlugins, the nine reactive primitives, and <T>.

Canonical: https://comvi.io/docs/i18n/api/solid

The SolidJS binding (peer `solid-js ^1.8`): a provider, a `useI18n()` hook whose reactive
members are accessors, low-level primitives and a `<T>` component.

## Exports

- Values: `I18nProvider`, `T`, `useI18n`, `useI18nInstance`, `useI18nLoader`,
`useI18nPlugins`, `createLocaleSignal`, `createDefaultNamespaceSignal`,
`createDefaultNamespaceState`, `createDefaultParamsSignal`, `createDefaultParamsState`,
`createLoadingSignal`, `createInitializingSignal`, `createInitializedSignal`,
`createCacheRevisionSignal`, `createI18n`, `loader`, `attachLoader`, `plugins`,
`definePlugin`, `icuCompiler`, `devtools`.
- Types: `I18nProviderProps`, `UseI18nReturn`, `UseI18nLoaderReturn`, `UseI18nPluginsReturn`,
`TProps`, `ComponentMap`, `DevtoolsOptions`, `I18n`, every `@comvi/core` type.

`I18n` is a type only; `@comvi/core/tags` is not re-exported. `TagComponentConfig` comes from
`@comvi/core/rich-text`.

`src/i18n.ts`

```ts

export const i18n = createI18n({ locale: 'en', fallbackLocale: 'en' })
.use(loader({ en: () => import('./locales/en.json') }))
.use(plugins());
```

## `<I18nProvider>`

```ts
const I18nProvider: ParentComponent<I18nProviderProps>
```

| Prop | Type | Default | Meaning |
| --- | --- | --- | --- |
| `i18n` | `AnyI18nInstance` | required | The i18n instance; any default-params type |
| `children` | `JSX.Element` | required | The subtree |
| `autoInit` | `boolean` | `true` | Call `init()` on mount |
| `fallback` | `JSX.Element` | none | Rendered instead of `children` until `isInitialized`; also what `renderToString` paints. `undefined` does not gate; `false` or `null` gates blank |
| `ssrLocale` | `string` | none | Written onto the instance in the render phase, so SSR needs a per-request instance. It paints on the first render when that locale's catalogs are already available; otherwise the switch lands when they load |
| `onError` | `(error: Error) => void` | none | Called when auto-initialization fails, after the instance's own `onError` |

`src/App.tsx`

```tsx

export function App() {
return (
    <I18nProvider i18n={i18n} fallback={<p>Loading</p>}>
      <h1>Hello</h1>

);
}
```

## `useI18n(ns?)`

```ts
function useI18n<DefaultNS extends string | undefined = undefined, D extends DefaultTranslationParams = {}>(ns?: DefaultNS): UseI18nReturn<DefaultNS, D>
```

Reactive members are accessors: `locale()`, `isLoading()`. `t()` in JSX tracks the locale and
the cache revision. A bound namespace adds its bare keys to `t`'s key union.

| Returned | Type | Meaning |
| --- | --- | --- |
| `t` | `NamespaceBoundTranslateFn<DefaultNS, D, string>` | Translate, reactive |
| `tRaw` | `NamespaceBoundTranslateFn<DefaultNS, D, TranslationResult>` | Rich-text half |
| `locale` | `Accessor<string>` | Current locale |
| `isLoading`, `isInitializing`, `isInitialized` | `Accessor<boolean>` | Loading flags |
| `cacheRevision` | `Accessor<number>` | Cache and config revision |
| `defaultParams` | `Accessor<DefaultParamsSnapshot<D>>` | Shallow snapshot |
| `dir` | `() => 'ltr' \| 'rtl'` | Text direction |
| `setLocale` | `(locale: string) => Promise<void>` | `setLocale` |
| `addMessages`, `setFallbackLocale`, `setDefaultParams`, `clearMessages`, `hasLocale`, `hasTranslation`, `on`, `reportError` | as on the instance | Bound functions |
| `getLoadedLocales`, `getActiveNamespaces`, `getDefaultNamespace`, `getMessagesCache` | `() => …` | Snapshots |
| `formatNumber`, `formatDate`, `formatCurrency`, `formatRelativeTime` | bound, trailing `locale?` | Reactive: read the locale signal at call time, like `t()`; a trailing `locale` overrides it |

`src/Greeting.tsx`

```tsx

export function Greeting() {
const { t, locale, setLocale } = useI18n();
return (
    <button onClick={() => void setLocale(locale() === 'en' ? 'de' : 'en')}>
      {t('greeting', { name: 'Alice' })}
    </button>
);
}
```

## `useI18nLoader()`, `useI18nPlugins()`

```ts
function useI18nLoader(): UseI18nLoaderReturn // AcquiredLoaderApi
function useI18nPlugins(): UseI18nPluginsReturn // AcquiredPluginsApi
```

`useI18nLoader()` gives `addActiveNamespace`, `addActiveNamespaces`, `removeActiveNamespace`,
`removeActiveNamespaces`, `reloadMessages`, `onLoadError`; `useI18nPlugins()` gives
`onMissingKey`. Each throws, in development and production, when the instance lacks the
capability. Cached per instance.

`src/LazyNamespace.tsx`

```tsx

export function LazyNamespace() {
const { t } = useI18n('dashboard');
const { addActiveNamespace } = useI18nLoader();

onMount(() => void addActiveNamespace('dashboard'));

return <h1>{t('page.title')}</h1>;
}
```

## `useI18nInstance()`

```ts
function useI18nInstance(): WrapperI18nInstance
```

The i18n instance itself, not a signal. Throws outside `<I18nProvider>`.

## `<T>`

```ts
const T: Component<TProps>

type TProps = TBaseProps & Record<string, unknown>
type ComponentMap = Record<string, ComponentHandler>
// ComponentHandler and ComponentConfigTarget are the shape of one ComponentMap entry; neither is exported
type ComponentHandler =
| string
| ((props: { children?: JSX.Element }) => JSX.Element)
| (Omit<TagComponentConfig, 'props'> & { props?: Record<string, any> } & (
      | { tag: ComponentConfigTarget; component?: ComponentConfigTarget }
      | { component: ComponentConfigTarget; tag?: ComponentConfigTarget }
    ))
type ComponentConfigTarget = string | ((props: any) => JSX.Element)
```

| Prop | Type | Meaning |
| --- | --- | --- |
| `i18nKey` | `keyof TranslationKeys \| PermissiveKey` | Required |
| `params` | `TranslationParams` | Interpolation params; any other prop is merged in |
| `ns`, `locale`, `fallback` | `string` | Overrides; with `locale` set the component does not recompute on a locale change |
| `markers` | `boolean` | `false` turns the in-context editor's markers off for this element when they cause a problem; it is then not editable in context |
| `components` | `ComponentMap` | Tag name to a tag name, a component, or a `{ tag \| component, props }` config |
| `children` | `JSX.Element` | Rendered when the key is missing, no `fallback` was given and `onMissingKey` returned nothing |

A handler that throws is reported as `reportError(error, { source: 'translation', tagName })`
and the tag renders its children, on the server and the client alike. A non-callable opaque
handler renders its children silently.

`src/Rich.tsx`

```tsx

export function Rich() {
return (
    <>
      <T
        i18nKey="rich.message"
        components={{
          bold: 'strong',
          highlight: ({ children }) => <mark>{children}</mark>,
          link: { tag: 'a', props: { href: '/about' } },
        }}
      />
      <T i18nKey="optional.feature">Default content</T>
    </>
);
}
```

## Reactive primitives

Each takes an i18n instance and runs inside a reactive context. A `Signal` reads; a `State`
returns `[accessor, setter]` and the setter writes through to the instance.

```ts
function createLocaleSignal(i18n: WrapperI18nInstance): Accessor<string>
function createDefaultNamespaceSignal(i18n: WrapperI18nInstance): Accessor<string>
function createDefaultNamespaceState(i18n: WrapperI18nInstance): [Accessor<string>, (namespace: string) => void]
function createDefaultParamsSignal<D>(i18n: WrapperI18nInstance<D>): Accessor<DefaultParamsSnapshot<D>>
function createDefaultParamsState<D>(i18n: WrapperI18nInstance<D>): [Accessor<DefaultParamsSnapshot<D>>, (params: SetDefaultParamsArg<D>) => void]
function createLoadingSignal(i18n: WrapperI18nInstance): Accessor<boolean>
function createInitializingSignal(i18n: WrapperI18nInstance): Accessor<boolean>
function createInitializedSignal(i18n: WrapperI18nInstance): Accessor<boolean>
function createCacheRevisionSignal(i18n: WrapperI18nInstance): Accessor<number>
```

| Primitive | Tracks |
| --- | --- |
| `createLocaleSignal` | `localeChanged` |
| `createDefaultNamespaceSignal`, `createDefaultNamespaceState` | `defaultNamespaceChanged`; the setter calls `setDefaultNamespace` |
| `createDefaultParamsSignal`, `createDefaultParamsState` | `configChanged`; the setter calls `setDefaultParams` |
| `createLoadingSignal` | `loadingStateChanged` (`isLoading`) |
| `createInitializingSignal` | `loadingStateChanged` (`isInitializing`) |
| `createInitializedSignal` | `initialized` and `destroyed` |
| `createCacheRevisionSignal` | every revision event except `localeChanged` and `loadingStateChanged` |

## Read next

- [Solid](/docs/i18n/solid): setup, loading, plurals, tags and typing for a Solid app.
- [Type-check translation keys](/docs/i18n/type-safety): generated keys, autocomplete and compile-time checks.
- [`@comvi/core`](/docs/i18n/api/core): the i18n instance, the capabilities and every type.
