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/vue-i18n-compat

v0.6.3

Published

Progressive migration bridge between vue-i18n and Fluenti — share locale, translate across both libraries

Downloads

2,309

Readme

@fluenti/vue-i18n-compat

npm bundle size license

Drop-in migration from vue-i18n to Fluenti. Keep your existing $t() calls, swap one plugin, and adopt compile-time i18n at your own pace.


Why?

Rewriting every translation call in a large Vue app is impractical. This package lets you:

  • Keep existing code working -- $t(), $tc(), $te(), and the Composition API all behave the same way they always have.
  • Add Fluenti incrementally -- new features use ICU MessageFormat and compile-time transforms; legacy messages stay in vue-i18n format until you are ready to move them.
  • Share locale state -- switching language in one library automatically updates the other, so users never see mixed-language UI.
  • Remove it when you are done -- once every message is migrated, swap @fluenti/vue-i18n-compat for @fluenti/vue and delete vue-i18n.

Quick Start

1. Install

pnpm add @fluenti/vue-i18n-compat @fluenti/core @fluenti/vue
# vue ^3.5 and vue-i18n ^9 || ^10 are peer dependencies

2. Swap the Plugin

Before (vue-i18n only):

import { createApp } from 'vue'
import { createI18n } from 'vue-i18n'

const i18n = createI18n({
  legacy: false,
  locale: 'en',
  messages: {
    en: { greeting: 'Hello!', farewell: 'Goodbye, {name}!' },
    ja: { greeting: 'こんにちは!', farewell: 'さようなら、{name}!' },
  },
})

const app = createApp(App)
app.use(i18n)
app.mount('#app')

After (bridge installed, existing code unchanged):

import { createApp } from 'vue'
import { createI18n } from 'vue-i18n'
import { createFluenti } from '@fluenti/vue'
import { createFluentBridge } from '@fluenti/vue-i18n-compat'

// Your existing vue-i18n setup -- nothing changes here
const i18n = createI18n({
  legacy: false,
  locale: 'en',
  messages: {
    en: { greeting: 'Hello!', farewell: 'Goodbye, {name}!' },
    ja: { greeting: 'こんにちは!', farewell: 'さようなら、{name}!' },
  },
})

// Fluenti setup (empty for now -- you will add messages here over time)
const fluenti = createFluenti({
  locale: 'en',
  fallbackLocale: 'en',
  messages: { en: {}, ja: {} },
})

// Bridge: one plugin to rule them both
const bridge = createFluentBridge({ vueI18n: i18n, fluenti })

const app = createApp(App)
app.use(bridge)   // replaces app.use(i18n)
app.mount('#app')

3. Existing Code Works

Every $t('greeting') in your templates and every useI18n() in your <script setup> blocks keeps working. No changes required.

<template>
  <!-- These calls hit vue-i18n, just like before -->
  <h1>{{ $t('greeting') }}</h1>
  <p>{{ $t('farewell', { name: 'Alice' }) }}</p>
</template>

4. Gradually Migrate

Move messages one at a time. When a key exists in both libraries, the bridge resolves it from Fluenti first (configurable).

// Before: vue-i18n format
{ greeting: 'Hello!' }

// After: ICU MessageFormat in Fluenti
{ greeting: 'Hello!' }  // simple strings are identical

// Before: vue-i18n plural (pipe syntax)
{ items: 'no items | one item | {count} items' }

// After: ICU plural in Fluenti
{ items: '{count, plural, =0 {no items} one {one item} other {# items}}' }

Once a key is in Fluenti, delete it from the vue-i18n messages object. When every key has been moved, replace the bridge with @fluenti/vue directly:

- import { createFluentBridge } from '@fluenti/vue-i18n-compat'
+ import { createFluenti } from '@fluenti/vue'

- app.use(bridge)
+ app.use(fluenti)

Usage

Composition API

<script setup>
import { useI18n } from '@fluenti/vue-i18n-compat'

const { t, tc, te, locale, setLocale, availableLocales } = useI18n()
</script>

<template>
  <h1>{{ t('greeting') }}</h1>          <!-- vue-i18n key -->
  <p>{{ t('welcome') }}</p>              <!-- Fluenti key -->
  <p v-if="te('greeting')">Exists!</p>  <!-- checks both libraries -->
  <p>{{ tc('items', count) }}</p>        <!-- pipe-separated or ICU plurals -->

  <button @click="setLocale('ja')">日本語</button>
</template>

Options API

$t, $te, and $tc are overridden globally -- existing templates work without changes.

<template>
  <p>{{ $t('greeting') }}</p>
  <p>{{ $tc('items', 3) }}</p>
  <p v-if="$te('farewell')">{{ $t('farewell', { name: 'Bob' }) }}</p>
</template>

How It Works

┌────────────┐   locale sync   ┌─────────┐
│  vue-i18n  │◄───────────────►│ Fluenti │
└─────┬──────┘                 └────┬────┘
      │      ┌──────────────┐       │
      └─────►│    Bridge    │◄──────┘
             │ t / tc / te  │
             └──────────────┘

The bridge installs both libraries as a single Vue plugin. Locale state is synced bidirectionally -- switching language in one library automatically updates the other. Translation lookups fall through: if the primary library does not have a key, the bridge checks the other.

API Reference

createFluentBridge(options)

Creates the bridge plugin. Call app.use(bridge) instead of app.use(i18n).

| Option | Type | Default | Description | |--------|------|---------|-------------| | vueI18n | VueI18nInstance | required | vue-i18n instance from createI18n() | | fluenti | FluentiPlugin | required | Fluenti plugin from createFluenti() | | priority | 'fluenti-first' \| 'vue-i18n-first' | 'fluenti-first' | Which library to check first for translations |

useI18n()

Returns the BridgeContext. Must be called inside a component where the bridge plugin is installed.

BridgeContext

| Property / Method | Type | Description | |-------------------|------|-------------| | t(key, values?) | string | Translate -- checks both libraries per priority | | tc(key, count, values?) | string | Pluralized translation (pipe syntax or ICU) | | te(key, locale?) | boolean | Check if key exists in either library | | tm(key) | unknown | Get the raw message object | | d(value, style?) | string | Format a date | | n(value, style?) | string | Format a number | | format(message, values?) | string | Format an ICU message string directly | | locale | Ref<string> | Reactive locale (synced across both libraries) | | setLocale(locale) | Promise<void> | Change locale (syncs to both libraries) | | availableLocales | ComputedRef<string[]> | Merged locales from both libraries | | isLoading | Ref<boolean> | Whether Fluenti is loading a locale chunk | | fluenti | FluentiContext | Access the underlying Fluenti context | | vueI18n | VueI18nGlobal | Access the underlying vue-i18n global composer |

Compatibility Matrix

| vue-i18n feature | Supported | Notes | |------------------|-----------|-------| | $t() / t() | Yes | Bridged with fallthrough to both libraries | | $tc() / tc() | Yes | Pipe-separated plurals via vue-i18n; ICU plurals via Fluenti | | $te() / te() | Yes | Returns true if key exists in either library | | $tm() / tm() | Yes | Returns raw message from priority library | | $d() / d() | Yes | Delegates to Fluenti's date formatter | | $n() / n() | Yes | Delegates to Fluenti's number formatter | | useI18n() | Yes | Returns unified BridgeContext | | Composition API mode | Yes | Requires legacy: false in vue-i18n | | Legacy API mode | No | Use legacy: false -- the bridge requires the Composition API | | Component interpolation (<i18n-t>) | No | Use Fluenti's <Trans> component instead | | Custom directives (v-t) | No | Fluenti uses v-t as a compile-time node transform | | Per-component i18n blocks | No | Use Fluenti's file-based catalogs or defineMessages() |

Migration Checklist

  1. Install the bridge alongside your existing vue-i18n setup
  2. Verify your app works without any other changes
  3. Write new features using Fluenti messages (ICU MessageFormat)
  4. Move existing messages from vue-i18n format to ICU format, one file at a time
  5. Remove empty vue-i18n locale objects as they become unnecessary
  6. When all messages are migrated, replace the bridge with @fluenti/vue and uninstall vue-i18n

Documentation

Full documentation at fluenti.dev.

License

MIT