Skip to content
fluenti

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:

PriorityAPIWhereFramework
1v-t directiveVue templatesVue only
2import { t } …; t`…`<script setup> / JSX / supported server scopesVue, React, Solid, Next
3<Trans> / <Plural> / <Select>Templates (all frameworks)All
4msg tagged templateOutside components (routes, stores)All
5useI18n().t() / await getI18n()Runtime lookup / escape hatchAll

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/react authoring surface still requires withFluenti(). Without it, only the explicit server runtime APIs remain supported.

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.

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>

The Vite plugin rewrites direct-import t into the runtime useI18n() binding at build time:

// Input
const greeting = t`Hello ${name}`
// Output
const greeting = t('Hello {name}', { name: name })

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.

ExpressionICU PlaceholderValue
${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.

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.

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.

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 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.

PropWhen used
value(required) The numeric value to pluralize on
zeroExact zero match
oneCLDR “one” category (e.g., 1 in English)
twoCLDR “two” category (e.g., Arabic dual)
fewCLDR “few” category (e.g., Russian 2-4)
manyCLDR “many” category (e.g., Arabic 11-99)
other(required) Fallback for all other values
offsetICU plural offset before category selection

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.

You can also write ICU plural patterns directly in t() calls:

{count, plural,
=0 {No items}
one {# item}
other {# items}
}

Exact matches (=0, =1, =2) are checked before CLDR categories:

{count, plural, =0 {Nobody} =1 {Just you} one {# person} other {# people}}
{count, plural, offset:1
=0 {Nobody}
=1 {Just {name}}
one {# other and {name}}
other {# others and {name}}
}
LanguageCategories
Englishone, other
Chineseother (no plural forms)
Arabiczero, one, two, few, many, other
Russianone, few, many, other
Frenchone, other

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>

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.

SituationBest API
Vue template textv-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 APIt() function

All frameworks provide two utility functions via useI18n() for working with the catalog directly:

  • te(key, locale?) — Check if a translation key exists. Returns boolean.
  • tm(key, locale?) — Get the raw compiled message without interpolation. Returns CompiledMessage | 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>

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.