Translating Content
Fluenti offers multiple ways to translate content. Direct-import t is a compile-time API, while component APIs such as Trans, Plural, and Select remain runtime-correct even without a build plugin. Choose the right API for each situation:
| Priority | API | Where | Framework |
|---|---|---|---|
| 1 | v-t directive | Vue templates | Vue only |
| 2 | import { t } …; t`…` | <script setup> / JSX / supported server scopes | Vue, React, Solid, Next |
| 3 | <Trans> / <Plural> / <Select> | Templates (all frameworks) | All |
| 4 | msg tagged template | Outside components (routes, stores) | All |
| 5 | useI18n().t() / await getI18n() | Runtime lookup / escape hatch | All |
Without Build Plugin
Section titled “Without Build Plugin”Fluenti draws a hard line between its compile-time authoring API and its runtime-capable components:
import { t }requires the Fluenti build plugin or loader. If it reaches runtime untransformed, it throws a clear error.<Trans>,<Plural>,<Select>,<DateTime>, and<NumberFormat>continue to work without the build plugin in React, Vue, and Solid.- Build-time transforms only optimize these components by precomputing IDs and extraction metadata. They do not change the rendered result.
- In Next.js server files, the unified
@fluenti/reactauthoring surface still requireswithFluenti(). Without it, only the explicit server runtime APIs remain supported.
v-t Directive (Vue)
Section titled “v-t Directive (Vue)”The v-t directive is a compile-time nodeTransform — it rewrites your template during Vue’s SFC compilation, producing zero runtime overhead:
<template> <h1 v-t>Welcome back, {{ name }}!</h1> <p v-t>Read the <a href="/terms">terms</a> and <strong>conditions</strong></p> <img v-t.alt src="/hero.jpg" alt="Welcome banner" /></template>For the full v-t reference (interpolation, explicit IDs, pluralization, attributes), see v-t Directive.
t() Function
Section titled “t() Function”The t() function looks up messages from the compiled catalog by hash. For most translation-only usage, prefer import { t } directly. Use useI18n() when you also need d(), n(), locale, setLocale, or other runtime features.
<script setup>import { t } from '@fluenti/vue'</script>
<template> <p>{{ t('Welcome to Fluenti') }}</p> <p>{{ t('Hello, {name}!', { name: userName }) }}</p></template>import { t } from '@fluenti/react'
function Greeting() { const name = 'World' return ( <div> <p>{t('Welcome to Fluenti')}</p> <p>{t('Hello, {name}!', { name })}</p> </div> )}Prefer t`…` tagged template over t() — see the Tagged Templates section below.
import { t } from '@fluenti/solid'
function Greeting() { return ( <div> <p>{t('Welcome to Fluenti')}</p> <p>{t('Hello, {name}!', { name: 'World' })}</p> </div> )}Compile-Time Transform
Section titled “Compile-Time Transform”The Vite plugin rewrites direct-import t into the runtime useI18n() binding at build time:
// Inputconst greeting = t`Hello ${name}`
// Outputconst greeting = t('Hello {name}', { name: name })Tagged Templates
Section titled “Tagged Templates”Direct-import t is Fluenti’s main compile-time translation API. The Vite plugin and Next loader use AST scope analysis to detect it inside supported scopes and optimize it at build time.
<script setup>import { t } from '@fluenti/vue'
const pageTitle = t`Welcome to Fluenti`</script>
<template> <h1>{{ pageTitle }}</h1></template>Compile-time authoring path for <script setup>. The transformed expression produces a plain string value. Use useI18n().t() inside a computed or template expression when you need reactive script-driven text.
import { t } from '@fluenti/react'
function Dashboard() { const name = 'World' const count = 5
return ( <div> <h1>{t`Welcome to Fluenti`}</h1> <p>{t`Hello ${name}, you have ${count} items`}</p> </div> )}Returns a plain value — React’s re-render cycle handles reactivity. Works in both Client Components and Server Components (with withFluenti()).
import { t } from '@fluenti/solid'
function Dashboard() { return ( <div> <h1>{t`Welcome to Fluenti`}</h1> <p>{t`Hello ${name()}, you have ${count()} items`}</p> </div> )}Use direct-import t inline in JSX or another reactive scope. It does not return a createMemo.
Variable Naming
Section titled “Variable Naming”| Expression | ICU Placeholder | Value |
|---|---|---|
${name} | {name} | unref(name) |
${user.name} | {name} | unref(user.name) |
${user.profile.displayName} | {displayName} | unref(user.profile.displayName) |
${getName()} | {0} | getName() |
${a + b} | {0} | a + b |
Rules: Simple identifier uses the name as placeholder. Property access uses the last segment. Everything else gets positional {0}, {1}, etc.
Lazy Messages with msg“
Section titled “Lazy Messages with msg“”Use msg for translations that need to be defined outside of components — in constants, stores, or route definitions.
import { msg } from '@fluenti/core'
const ROLES = { admin: msg`Administrator`, user: msg`Regular User`,}msg returns a MessageDescriptor — it records the message text without translating it. Resolve it at render time with t():
<template> <span>{{ t(ROLES.admin) }}</span></template>Use cases: route meta, store constants, config objects, enum-like maps.
When translations contain HTML elements or framework components, use <Trans> with its default slot (Vue) or children (React/Solid). You write native HTML inside the component — no indexed placeholders.
<template> <Trans>Read the <router-link to="/terms">terms</router-link> and <strong>conditions</strong></Trans></template>Trans, Plural, and Select are globally registered by the Vue plugin.
import { Trans } from '@fluenti/react'
<Trans> Read the <a href="/terms">terms</a> and <strong>conditions</strong></Trans><Trans> Read the <A href="/terms">terms</A> and <strong>conditions</strong></Trans>The <Trans> component extracts the text content of its default slot (including HTML tags) as the source message. During compilation, translators see the tags as placeholders, and at runtime the translated message is rendered with the original elements restored.
Trans works both with and without the build plugin. The plugin only removes runtime extraction work by precomputing the message ID and component list.
Framework main entries re-export the runtime-capable components. The /components subpath remains available when you want an explicit component-only boundary.
Plural
Section titled “Plural”Fluenti uses Intl.PluralRules for locale-aware pluralization, supporting all CLDR plural categories. The <Plural> component is the simplest way to handle plurals. Pass the numeric value and provide a string for each plural category your source language needs. Use # as a placeholder for the numeric value.
<template> <Plural :value="count" one="# item" other="# items" /></template><Plural value={count} one="# item" other="# items" /><Plural value={count()} one="# item" other="# items" />Plural is runtime-capable without the build plugin. Fluenti constructs a synthetic ICU plural message, looks it up through the catalog, and falls back to the source forms if needed.
| Prop | When used |
|---|---|
value | (required) The numeric value to pluralize on |
zero | Exact zero match |
one | CLDR “one” category (e.g., 1 in English) |
two | CLDR “two” category (e.g., Arabic dual) |
few | CLDR “few” category (e.g., Russian 2-4) |
many | CLDR “many” category (e.g., Arabic 11-99) |
other | (required) Fallback for all other values |
offset | ICU plural offset before category selection |
Catalog extraction
Section titled “Catalog extraction”Plural forms are auto-extracted to catalogs by fluenti extract. You only write the source-language forms — Fluenti generates the correct ICU {count, plural, ...} pattern in the catalog.
ICU MessageFormat syntax
Section titled “ICU MessageFormat syntax”You can also write ICU plural patterns directly in t() calls:
{count, plural, =0 {No items} one {# item} other {# items}}Exact matches
Section titled “Exact matches”Exact matches (=0, =1, =2) are checked before CLDR categories:
{count, plural, =0 {Nobody} =1 {Just you} one {# person} other {# people}}Offset
Section titled “Offset”{count, plural, offset:1 =0 {Nobody} =1 {Just {name}} one {# other and {name}} other {# others and {name}}}Language-specific plural rules
Section titled “Language-specific plural rules”| Language | Categories |
|---|---|
| English | one, other |
| Chinese | other (no plural forms) |
| Arabic | zero, one, two, few, many, other |
| Russian | one, few, many, other |
| French | one, other |
Select
Section titled “Select”ICU select patterns for choosing text based on a string value (e.g., gender, status).
<template> <Select :value="gender" male="He liked this" female="She liked this" other="They liked this" /></template><Select value={gender} male="He liked this" female="She liked this" other="They liked this" /><Select value={gender()} male="He liked this" female="She liked this" other="They liked this" />For programmatic usage, pass an options object instead of individual props:
<Select value={role} options={{ admin: 'Full access', editor: 'Can edit', other: 'Unknown' }} />Select is also runtime-capable without the build plugin. Fluenti constructs a synthetic ICU select message, performs normal catalog lookup, and reconstructs any rich content.
Props: value (required), other (required), options (preferred object form), plus any string keys as individual props. options takes precedence over direct case props.
When to Use What
Section titled “When to Use What”| Situation | Best API |
|---|---|
| Vue template text | v-t directive |
Reactive value in <script setup> | t`…` tagged template |
| JSX expression (React/Solid) | t`…` tagged template |
| Rich text with embedded HTML | <Trans> component |
| Plural forms | <Plural> component |
| Outside component lifecycle (routes, stores) | msg tagged template |
| Dynamic message IDs from API | t() function |
Checking and Inspecting Messages
Section titled “Checking and Inspecting Messages”All frameworks provide two utility functions via useI18n() for working with the catalog directly:
te(key, locale?)— Check if a translation key exists. Returnsboolean.tm(key, locale?)— Get the raw compiled message without interpolation. ReturnsCompiledMessage | undefined.
These are useful for conditional rendering, debugging, and validating dynamic keys before translation.
<script setup>import { useI18n } from '@fluenti/vue'const { te, t } = useI18n()</script>
<template> <span v-if="te('premium.badge')">{{ t('premium.badge') }}</span> <span v-else>Free</span></template>import { useI18n } from '@fluenti/react'
function Badge() { const { te, t } = useI18n() return te('premium.badge') ? <span>{t('premium.badge')}</span> : <span>Free</span>}import { useI18n } from '@fluenti/solid'
function Badge() { const { te, t } = useI18n() return te('premium.badge') ? <span>{t('premium.badge')}</span> : <span>Free</span>}Both functions accept an optional locale parameter to check a specific locale instead of the current one:
te('greeting', 'ja') // Does Japanese have this key?tm('greeting', 'ja') // What's the raw Japanese message?For full API details, see the framework-specific reference pages: @fluenti/vue, @fluenti/react, @fluenti/solid.