npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@fluenti/solid

v0.6.3

Published

SolidJS compile-time i18n — Trans/Plural/Select components, I18nProvider, useI18n

Readme

@fluenti/solid

npm bundle size license

Compile-time i18n for SolidJS -- reactive by design, zero runtime overhead.

Fluenti compiles your messages at build time and pairs them with Solid's fine-grained reactivity. When the locale changes, only the text nodes that depend on it re-render. No virtual DOM diffing, no wasted work.

Features

  • Compile-time transforms -- messages are resolved during the build; the runtime ships precompiled functions, not an ICU parser.
  • Signal-driven locale -- locale() is a Solid signal; any computation that reads it re-runs automatically.
  • <Trans>, <Plural>, <Select>, <DateTime>, <NumberFormat> -- declarative components that map directly to ICU MessageFormat.
  • t() / d() / n() / msg() -- imperative API for strings, dates, numbers, and lazy message definitions.
  • Code splitting -- load locale chunks on demand with a single chunkLoader option.
  • SSR-ready -- first-class SolidStart support with per-request isolation.

Quick Start

1. Install

pnpm add @fluenti/core @fluenti/solid
pnpm add -D @fluenti/cli

2. Configure Vite

// vite.config.ts
import solidPlugin from 'vite-plugin-solid'
import fluentiSolid from '@fluenti/solid/vite-plugin'

export default {
  plugins: [solidPlugin(), fluentiSolid()],
}

3. Wrap your app

// index.tsx
import { render } from 'solid-js/web'
import { I18nProvider } from '@fluenti/solid'
import en from './locales/compiled/en'
import ja from './locales/compiled/ja'
import App from './App'

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

4. Translate

import { t, useI18n, Trans, Plural, Select } from '@fluenti/solid'

function Demo(props) {
  const { d, n, setLocale } = useI18n()
  const name = 'World'

  return (
    <div>
      {/* Tagged template literal */}
      <h1>{t`Hello, ${name}!`}</h1>

      <Trans>Read the <a href="/docs">documentation</a></Trans>

      <Plural value={props.count} one="# item" other="# items" />

      <Select value={props.gender} male="He" female="She" other="They" />

      <p>{d(new Date(), 'long')}</p>
      <p>{n(1234.5, 'currency')}</p>

      <button onClick={() => setLocale('ja')}>日本語</button>
    </div>
  )
}

@fluenti/solid/components remains available as an explicit subpath when you want runtime-only component imports or a stricter bundle boundary.

What the compiler does

Write natural-language JSX. The Vite plugin extracts messages, generates deterministic IDs, and replaces the source with precompiled lookups -- all at build time.

// You write:
<Plural value={count} one="# item" other="# items" />

// The compiler emits (conceptually):
t('abc123', { count })   // hash-based lookup, no ICU parsing at runtime

API Reference

useI18n()

Returns the reactive i18n context. Works inside any component that is a descendant of <I18nProvider>, or after a top-level createFluenti() call.

const { t, d, n, format, locale, setLocale, isLoading } = useI18n()

| Method | Signature | Description | |--------|-----------|-------------| | t | (id: string \| MessageDescriptor, values?) => string or t`Hello ${name}` | Dual-mode: function call for catalog lookup, or tagged template literal | | d | (value: Date \| number, style?) => string | Format a date using named presets or Intl defaults | | n | (value: number, style?) => string | Format a number using named presets or Intl defaults | | format | (message: string, values?) => string | Format an ICU message string directly (no catalog lookup) | | locale | Accessor<string> | Reactive signal for the current locale | | setLocale | (locale: string) => Promise<void> | Change locale (async when lazy locale loading is enabled) | | loadMessages | (locale: string, messages) => void | Merge additional messages into a locale catalog at runtime | | getLocales | () => string[] | List all locales that have loaded messages | | preloadLocale | (locale: string) => void | Preload a locale chunk in the background without switching | | isLoading | Accessor<boolean> | Whether a locale chunk is currently being loaded | | loadedLocales | Accessor<Set<string>> | Set of locales whose messages have been loaded |

createFluenti(config)

Module-level singleton alternative to <I18nProvider>. Call once at startup; useI18n() will find it automatically.

import { createFluenti } from '@fluenti/solid'

const i18n = createFluenti({
  locale: 'en',
  fallbackLocale: 'en',
  messages: { en, ja },

  // Optional: post-translation transform, locale change callback, custom formatters
  transform: (result, id, locale) => result,
  onLocaleChange: (newLocale, prevLocale) => { /* ... */ },
  formatters: { /* custom ICU function formatters */ },
})

The config accepts all FluentiRuntimeConfigFull options from @fluenti/core, including transform, onLocaleChange, and formatters. See the core README for details.

Components

<Trans> -- Rich text

Render translated content containing inline JSX elements:

<Trans>Click <a href="/next">here</a> to continue</Trans>

| Prop | Type | Default | Description | |------|------|---------|-------------| | tag | string | 'span' | Wrapper element for multiple children |

<Plural> -- Plural forms

ICU plural rules as a component. Supports string props or rich-text JSX element props:

{/* String props */}
<Plural value={count} zero="No items" one="1 item" other="{count} items" />

{/* Rich text via JSX element props */}
<Plural
  value={count}
  zero={<>No <strong>items</strong> left</>}
  one={<><em>1</em> item remaining</>}
  other={<><strong>{count}</strong> items remaining</>}
/>

| Prop | Type | Default | Description | |------|------|---------|-------------| | value | number | -- | The count to pluralize on (required) | | zero | string \| JSX.Element | -- | Text for zero items | | one | string \| JSX.Element | -- | Singular form | | two | string \| JSX.Element | -- | Dual form | | few | string \| JSX.Element | -- | Few form | | many | string \| JSX.Element | -- | Many form | | other | string \| JSX.Element | '' | Default/fallback form |

<Select> -- Option selection

ICU select patterns as a component:

{/* String props */}
<Select value={gender} male="He" female="She" other="They" />

{/* Rich text via options + other */}
<Select
  value={gender}
  options={{
    male: <><strong>He</strong> liked this</>,
    female: <><strong>She</strong> liked this</>,
  }}
  other={<><em>They</em> liked this</>}
/>

| Prop | Type | Default | Description | |------|------|---------|-------------| | value | string | -- | The value to match (required) | | options | Record<string, string \| JSX.Element> | -- | Named options map | | other | string \| JSX.Element | '' | Fallback when no option matches |

<DateTime> -- Date formatting

import { DateTime } from '@fluenti/solid'

<DateTime value={new Date()} format="long" />

| Prop | Type | Default | Description | |------|------|---------|-------------| | value | Date \| number | -- | The date value to format (required) | | style | string | -- | Named date format style |

<NumberFormat> -- Number formatting

import { NumberFormat } from '@fluenti/solid'

<NumberFormat value={1234.56} format="currency" />

| Prop | Type | Default | Description | |------|------|---------|-------------| | value | number | -- | The number to format (required) | | style | string | -- | Named number format style |

Utilities

| Export | Description | |--------|-------------| | msg | Tag for lazy message definitions outside the component tree |

Code Splitting

Load locale messages on demand instead of bundling everything upfront:

  <I18nProvider
  locale="en"
  messages={{ en }}
  lazyLocaleLoading
  chunkLoader={(locale) => import(`./locales/compiled/${locale}.js`)}
>
  <App />
</I18nProvider>
const { setLocale, isLoading, preloadLocale } = useI18n()

// Preload on hover
onMount(() => preloadLocale('ja'))

// Switch locale -- instant if preloaded, async otherwise
await setLocale('ja')

SSR with SolidStart

Server-side i18n

Create a server-side i18n instance with per-request locale resolution:

// lib/i18n.server.ts
import { createServerI18n } from '@fluenti/solid/server'

export const { setLocale, getI18n } = createServerI18n({
  loadMessages: (locale) => import(`../locales/compiled/${locale}.js`),
  fallbackLocale: 'en',
  resolveLocale: () => {
    // Read locale from cookie, header, or URL
    const event = getRequestEvent()
    return event?.request.headers.get('accept-language')?.split(',')[0] ?? 'en'
  },
})

Hydration helper

The getSSRLocaleScript utility injects a tiny inline script that makes the server-detected locale available to the client before hydration, preventing a locale flash:

import { getSSRLocaleScript } from '@fluenti/solid/server'

SSR utilities re-exported from @fluenti/solid/server

| Export | Description | |--------|-------------| | createServerI18n | Create server-side i18n with lazy message loading | | detectLocale | Detect locale from headers, cookies, URL path, or query | | getSSRLocaleScript | Inline script for hydrating the locale on the client | | getHydratedLocale | Read the locale set by the SSR script on the client | | isRTL / getDirection | RTL detection helpers |

Documentation

Full docs at fluenti.dev.

License

MIT