Framework API Comparison
Fluenti provides a consistent API across Vue, React, and SolidJS. This page highlights what is identical and what differs due to framework-specific patterns.
Provider Setup
Section titled “Provider Setup”| Vue | React | Solid | |
|---|---|---|---|
| Init function | createFluenti(options) | — | — |
| Provider | app.use(i18n) plugin | <I18nProvider> component | <I18nProvider> component |
| Access hook | useI18n() composable | useI18n() hook | useI18n() hook |
Vue uses a plugin-based approach (app.use()), while React and Solid use a context provider component wrapping the tree.
useI18n() Return Values
Section titled “useI18n() Return Values”All three frameworks return the same core translation functions with identical signatures:
| Property | Vue | React | Solid | Identical? |
|---|---|---|---|---|
t(id, values?) | LocalizedString | LocalizedString | LocalizedString | ✅ |
t`Hello ${name}` | ✅ | ✅ | ✅ | ✅ |
d(value, style?) | LocalizedString | LocalizedString | LocalizedString | ✅ |
n(value, style?) | LocalizedString | LocalizedString | LocalizedString | ✅ |
format(message, values?) | LocalizedString | LocalizedString | LocalizedString | ✅ |
setLocale(locale) | Promise<void> | Promise<void> | Promise<void> | ✅ |
loadMessages(locale, msgs) | ✅ | ✅ | ✅ | ✅ |
getLocales() | string[] | string[] | string[] | ✅ |
preloadLocale(locale) | Promise<void> | Promise<void> | Promise<void> | ✅ |
te(key, locale?) | boolean | boolean | boolean | ✅ |
tm(key, locale?) | CompiledMessage? | CompiledMessage? | CompiledMessage? | ✅ |
Reactive state types
Section titled “Reactive state types”These properties expose the same data but use framework-native reactivity types:
| Property | Vue | React | Solid |
|---|---|---|---|
locale | Ref<string> | string | Accessor<string> |
isLoading | Ref<boolean> | boolean | Accessor<boolean> |
loadedLocales | Ref<ReadonlySet<string>> | ReadonlySet<string> | Accessor<Set<string>> |
- Vue: Use
.valueto read (e.g.,locale.value) - React: Plain values that trigger re-renders via
useState - Solid: Call as functions (e.g.,
locale())
Component Props
Section titled “Component Props”<Trans>, <Plural>, and <Select> share the same props across all frameworks:
<Trans>
Section titled “<Trans>”| Prop | Type | Required | Notes |
|---|---|---|---|
id | string | No | Override auto-generated hash ID |
context | string | No | Message context for disambiguation |
comment | string | No | Translator-facing note |
tag | string | No | Wrapper element. Default: no wrapper (Fragment) |
children | — | Yes | Content to translate |
render | (node) => node | No | React only — custom render wrapper |
<Plural>
Section titled “<Plural>”| Prop | Type | Required | Notes |
|---|---|---|---|
value | number | Yes | Numeric value for plural selection |
other | string | Yes | Required fallback form |
zero / one / two / few / many | string | No | CLDR plural category forms |
offset | number | No | Offset before selecting form |
tag | string | No | Wrapper element. Default: Fragment |
<Select>
Section titled “<Select>”| Prop | Type | Required | Notes |
|---|---|---|---|
value | string | Yes | Value to match against cases |
other | string | Yes | Required fallback case |
options | Record<string, string> | No | Named case options |
tag | string | No | Wrapper element. Default: Fragment |
Reactivity Model
Section titled “Reactivity Model”Each framework uses its native reactivity system internally:
| Aspect | Vue | React | Solid |
|---|---|---|---|
| State | ref(), shallowReactive() | useState(), useRef() | createSignal() |
| Tracking | Automatic dependency tracking via Proxy | Re-render on state change | Fine-grained signal tracking |
| Memoization | Automatic | useMemo(), useCallback() | Not needed (fine-grained) |
| Race protection | Not needed (synchronous reactive graph) | localeRequestRef counter | Not needed (synchronous signals) |
Lazy Loading Configuration
Section titled “Lazy Loading Configuration”| Vue | React | Solid | |
|---|---|---|---|
| Loader prop | chunkLoader | loadMessages | chunkLoader |
| Enable flag | lazyLocaleLoading: true | Automatic (when loadMessages provided) | lazyLocaleLoading: true |
| Loader signature | (locale) => Promise<Messages> | (locale) => Promise<Messages> | (locale) => Promise<Messages> |
Vue and Solid use chunkLoader + explicit lazyLocaleLoading flag. React infers lazy loading automatically when loadMessages is provided.
Vite Plugin
Section titled “Vite Plugin”Each framework has its own Vite plugin that wraps @fluenti/vite-plugin:
// Vueimport fluentiVue from '@fluenti/vue/vite-plugin'
// Reactimport fluentiReact from '@fluenti/react/vite-plugin'
// Solidimport fluentiSolid from '@fluenti/solid/vite-plugin'All three accept the same FluentiPluginOptions (optional config field). The framework plugin handles scope transforms, virtual module generation, and component compilation automatically.
Fluenti vs Other i18n Libraries
Section titled “Fluenti vs Other i18n Libraries”Feature Comparison
Section titled “Feature Comparison”| Feature | Fluenti | vue-i18n | react-i18next | next-intl | LinguiJS |
|---|---|---|---|---|---|
| Compile-time transforms | Yes | No | No | No | Yes |
| Runtime parser needed | No | Yes | Yes | Yes | No |
| ICU MessageFormat | Full | Partial | Via plugin | Full | Full |
| Plural/Select components | <Plural>, <Select> | $tc() | <Trans> | useTranslations | <Plural> |
| SSR support | Built-in | Built-in | Via next-i18next | Built-in | Manual |
| Code splitting | Built-in (static/dynamic) | Manual | Manual namespace loading | Per-locale JSON | Manual |
| TypeScript | Full (branded types, message ID narrowing) | Partial | Partial | Full | Partial |
| PO catalog format | Native | No | No | No | Native |
| JSON catalog format | Yes | Yes | Yes | Yes | Yes |
| Message extraction CLI | Built-in | No | i18next-parser | No | Built-in |
| Multi-framework | Vue, React, Solid, Next.js, Nuxt, SolidStart | Vue only | React only | Next.js only | React, Vue (experimental) |
Bundle Size
Section titled “Bundle Size”Because Fluenti compiles messages at build time, the runtime does not include a parser or compiler. This reduces the client-side footprint:
| Library | Runtime size (min+gzip) | Includes parser? |
|---|---|---|
@fluenti/core | ~3 kB | No |
vue-i18n | ~14 kB | Yes |
react-i18next + i18next | ~13 kB | Yes |
next-intl | ~12 kB | Yes |
@lingui/react + @lingui/core | ~5 kB | No |
Sizes are approximate and vary by version. Fluenti and Lingui both use compile-time transforms to keep runtime size minimal.
Runtime Overhead
Section titled “Runtime Overhead”Compile-time libraries (Fluenti, Lingui) perform message resolution via simple object lookups at runtime. Runtime libraries (vue-i18n, react-i18next, next-intl) parse ICU syntax on the fly or on first access, which adds CPU overhead on initial render and locale switches.
| Aspect | Compile-time (Fluenti) | Runtime (vue-i18n, react-i18next) |
|---|---|---|
| First render | Hash lookup | Parse + interpolate |
| Locale switch | Swap compiled catalog | Re-parse all visible messages |
| Memory | Pre-compiled functions | Parser cache + AST cache |
| Cold start (SSR) | Minimal | Parser initialization |
When to Choose Fluenti
Section titled “When to Choose Fluenti”Fluenti is a good fit when:
- You need a single i18n solution across Vue, React, and Solid projects
- Bundle size and runtime performance are priorities
- You use PO-based translation workflows (gettext tooling, Weblate, Crowdin)
- You want type-safe message IDs with compile-time checking
- Your app uses SSR and needs built-in hydration support
Consider alternatives when:
- You only use one framework and are already invested in its ecosystem library (e.g., vue-i18n for Vue-only projects)
- You need runtime message loading from a CMS without a build step
- Your team is already productive with react-i18next and migration cost outweighs the benefits
Migration Paths
Section titled “Migration Paths”If you are migrating from an existing i18n library, see the dedicated migration guides:
- From vue-i18n: Migration from vue-i18n covers the
vue-i18n-compatbridge layer for incremental migration - From react-i18next: Replace
useTranslation()withuseI18n(),<Trans>with Fluenti’s<Trans>, and convert JSON namespaces to PO catalogs or flat JSON - From next-intl: Swap
useTranslations()foruseI18n()and adopt@fluenti/nextwithwithFluenti()innext.config.ts - From LinguiJS: The APIs are similar — replace
<Trans>anduseLingui()with Fluenti equivalents; PO catalogs are compatible with minimal changes