# @fluenti/solid

> SolidJS bindings for Fluenti — compile-time `t`, runtime-capable components, and signal-based locale switching.

@fluenti/solid is the Solid runtime layer for Fluenti. It supports:

- compile-time authoring with `import { t } from '@fluenti/solid'`
- runtime lookup and imperative APIs through `useI18n()`
- signal-driven locale reactivity
- runtime-capable components that keep working without the build plugin

## Install

```bash
pnpm add @fluenti/core @fluenti/solid @fluenti/vite-plugin
```

## Public Exports

- `createFluentiContext`
- `I18nProvider`
- `useI18n`
- `t`
- `Trans`
- `Plural`
- `Select`
- `DateTime`
- `NumberFormat`
- `msg`

Types:

- `FluentiContext`
- `FluentiConfig`
- `FluentiTransProps`
- `FluentiPluralProps`
- `FluentiSelectProps`

## Context model

### `createFluentiContext(config)`

Creates an isolated reactive context backed by Solid signals. This is the factory used internally by `<I18nProvider>` and can also be used directly for advanced setups.

### `I18nProvider`

Creates and provides the nearest Solid context boundary for its subtree.

### `useI18n()`

Returns the context from the nearest `<I18nProvider>`. Throws if no provider is found.

This is the public runtime API for translations, locale switching, and formatting.

## Example

```tsx
import { render } from 'solid-js/web'
import { I18nProvider, useI18n, t } from '@fluenti/solid'

function Greeting() {
  const name = 'Fluenti'
  const headline = t`Hello ${name}`
  const { setLocale, preloadLocale } = useI18n()

  return (
    <>
      <h1>{headline}</h1>
      <button onMouseEnter={() => preloadLocale('ja')} onClick={() => void setLocale('ja')}>
        日本語
      </button>
    </>
  )
}

render(() => (
  <I18nProvider locale="en" messages={{ en, ja }}>
    <Greeting />
  </I18nProvider>
), document.getElementById('root')!)
```

## `I18nConfig`

`I18nConfig` extends the core config with Solid-specific lazy-loading support:

```ts
{
  locale: 'en',
  messages: { en },
  fallbackLocale: 'en',
  fallbackChain: { ja: ['en'] },
  lazyLocaleLoading: true,
  chunkLoader: (locale) => import(`./locales/compiled/${locale}.js`),
  dateFormats: { short: { dateStyle: 'medium' } },
  numberFormats: { currency: { style: 'currency', currency: 'USD' } },
}
```

Fields added on top of the core runtime:

- `lazyLocaleLoading?: boolean`
- `chunkLoader?: (locale) => Promise<Messages | { default: Messages }>`
- `fallbackChain?`
- `dateFormats?`
- `numberFormats?`

## `useI18n()` return shape

- `locale(): string`
- `setLocale(locale): Promise<void>`
- `t(idOrDescriptor, values?)`
- tagged-template `t\`\``
- `d(value, style?)`
- `n(value, style?)`
- `format(message, values?)`
- `loadMessages(locale, messages)`
- `getLocales()`
- `isLoading()`
- `loadedLocales()`
- `te(key, locale?)`
- `tm(key, locale?)`
- `preloadLocale(locale)`

Any reactive computation that reads `t()` re-runs when `locale()` changes.

## Compile-time `t`

Imported `t` is compile-time only:

```ts
import { t } from '@fluenti/solid'

const title = t`Hello ${name}`                                   // ✅ Preferred
const label = t({ message: 'Hello {name}', context: 'home.hero' }, { name }) // ✅ Preferred
```

Use `useI18n().t()` for runtime lookup by ID or other imperative scenarios.

If imported `t` runs at runtime without the build plugin, it throws a clear error.

## Runtime-capable components

These components continue to work without the build plugin:

- `Trans`
- `Plural`
- `Select`
- `DateTime`
- `NumberFormat`

Cross-framework contract highlights:

- `Plural` supports `id`, `context`, `comment`, and `offset`
- `zero` maps to exact `=0`
- `#` uses the adjusted count
- `Select` supports `options`, which take precedence over direct case props

## Lazy locale loading

```tsx
<I18nProvider
  locale="en"
  messages={{ en }}
  lazyLocaleLoading={true}
  chunkLoader={(locale) => import(`./locales/compiled/${locale}.js`)}
>
  <App />
</I18nProvider>
```

Behavior:

- default locale can be loaded eagerly
- additional locales load on demand
- `setLocale()` becomes async when a chunk must be fetched
- `preloadLocale()` fetches a locale without switching

## Build plugin relationship

`@fluenti/solid` is the runtime layer. `@fluenti/vite-plugin` provides compile-time transforms for:

- direct-import `t`
- JSX component optimizations

The component layer remains runtime-correct without the plugin.
