vue-i18n-kit
i18n Kit

A reusable Vue 3 localization plugin that wraps vue-i18n and provides a ready-to-use integration layer — set up once, reuse across every project in your team.
Features
- One-liner setup — install the plugin in
main.tsand start using composables immediately - Two-method translation API —
t(key, vars?)for simple strings,tm(key, vars)for ICU-pluralized strings; both fromuseT() - ICU pluralization —
Intl.PluralRules-based with named CLDR categories (one,few,many, …); correct for every language including Arabic (6 forms) - Lazy loading — locale JSON files are fetched on demand; only the active locale is loaded at startup
- Namespace code splitting — split large locale files by feature and lazy-load namespaces on demand with
useNamespace() - Persist locale — selected language is automatically saved to
localStorageand restored on next visit - Fallback locale — missing translation keys transparently fall back to the configured fallback language
- Locale metadata — attach any custom data to a locale (
display,flag,author, …) and read it back throughuseLocaleanduseAvailableLocales - Date / number / currency formatting —
useFormatwrapsIntland always uses the active locale - Plugin service —
createVueI18nPluginreturns anI18nPluginwith a.serviceproperty — fully usable outside Vue components (router guards, Pinia stores, SSR entry points) - Locale change hook —
service.onLocaleChange(cb)fires after every successful locale switch; returns an unsubscribe function - TypeScript-first — all public APIs are fully typed, no
anyleaks into consumer code - Vite check plugin — checks all locale files for missing or extra keys at build time (import from
vue-i18n-kit/vite) - Vite inline plugin —
vueI18nInlinePluginbakes all locale JSON into the bundle as a static virtual module — zero HTTP requests at runtime - Vite namespace plugin —
vueI18nNamespacePluginscans split locale directories and generates a virtual module with per-namespace dynamic imports - In-context translation editor —
vueI18nDevPluginoverlays a pencil icon on every translated string during development; click to edit inline - CLI —
vue-i18n-kit init / add / check / merge / prunescaffolds, audits, and cleans locale files from the terminal - Dictionary merge —
vue-i18n-kit mergedeep-merges a shared/corporate base dictionary into project locale files - Dead key pruning —
vue-i18n-kit pruneremoves keys not referenced anywhere in the source code - Base dictionary (extends) —
extendsfield ini18n-kit.config.jsonloads a shared locale directory and merges it under project keys - Locked keys — declare immutable keys in the base config; editor shows them read-only, server rejects edits with 403, CLI tools never touch them
- Validation rules — configure
rulesini18n-kit.config.jsonto tune length thresholds, placeholder patterns, HTML/ICU warnings per project - Ignore lists —
ignore.prune,ignore.duplicates,ignore.unused,ignore.scanExcludewhitelists protect dynamic keys, brand names, and test files - Alphabetical merge —
mergenow always sorts output keys alphabetically (likesort); use--no-sortto opt out - TypeScript type generation —
vue-i18n-kit typesgenerates aTranslationKeyunion type from locale files;--watchmode regenerates on change - Stale translation detection — tracks when reference values change and flags translations in other locales as outdated; CLI
stalecommand + editor filter + "Mark as reviewed" button - XLIFF / PO export —
vue-i18n-kit exportgenerates industry-standard XLIFF 1.2 or Gettext PO files for professional translators; translator notes are included - XLIFF / PO import —
vue-i18n-kit importreads completed XLIFF or PO files back into locale JSON - Coverage report —
vue-i18n-kit statsprints fill rate per locale and namespace to the console;--format jsonfor CI pipelines;--format htmlfor a self-contained visual report - Machine translation — auto-translate missing keys via LibreTranslate or DeepL (500k chars/month free); ICU placeholders and plurals are encoded as opaque XML tags so they survive translation intact
- Locale Editor UI — browser-based editor with dashboard, inline editing, group operations, phantom key detection, plural preview, stale filter, XLIFF/PO export-import, and more
When you'd reach for this
A missing translation key doesn't break the build or throw a console error — it just shows the user a raw identifier instead of text, and the first person to notice is usually the user, not the developer. vue-i18n-kit checks every locale for completeness right at build time, before it ever gets that far.
- "1 file" and "5 files" are different forms, and not just in Russian — English has one plural form, Russian has three, Arabic has six — the grammatically correct form is picked automatically for each language instead of one string with a number dropped in.
- A translator doesn't need to read code to fix a typo — A typo in a button's text can be fixed by someone who's never opened a code editor — right in the browser, with a table of keys and a live preview, no repository access required.
- A new language shouldn't slow the site down for everyone else — A user in Germany shouldn't have to download a Japanese translation just because the site supports 12 languages — each locale loads separately, and only when it's actually needed.
- Translation is needed where there's no component to reach it from — An error message in an HTTP interceptor, text in a state store, a check inside a router guard — translation stays available in exactly the places where the usual way of reaching it from inside a component won't work.
- Different client releases are built from one codebase — Each client gets its own set of copy on top of one shared base — some strings are identical everywhere, some are overridden for a specific brand. The shared dictionary can be safely merged into each client's files: missing strings get added, while anything already overridden for a brand stays untouched instead of getting clobbered by mistake.
- Nothing should be missing right before a release — A developer adds a new screen and forgets to translate it into three of the five languages — that's catchable with one command that compares every locale against the reference and reports which keys are missing and which aren't used anywhere anymore.
Installation
| Peer dependency | Version | Required |
|---|---|---|
vue |
^3.3.0 |
yes |
vue-i18n |
^11.0.0 |
yes |
vite |
>=5.0.0 |
only for the Vite plugin |
Neither vue nor vue-i18n is bundled — they must be installed in the consuming project.
npm install vue-i18n-kit vue vue-i18n
Quick start
1. Create locale files
src/
└── locales/
├── en.json
└── ru.json
// locales/en.json
{
"buttons": {
"submit": "Submit",
"cancel": "Cancel"
},
"greeting": "Hello, {name}!",
"items": "{count, plural, one {# item} other {# items}}"
}
Tip: Run
vue-i18n-kit initto scaffold step 1 automatically (locale files, config, Vite plugin).
2. Register the plugin
// main.ts
import { createApp } from 'vue'
import { createVueI18nPlugin } from 'vue-i18n-kit'
import App from './App.vue'
const app = createApp(App)
app.use(
createVueI18nPlugin({
defaultLocale: 'en',
fallbackLocale: 'en',
locales: {
en: {
messages: () => import('./locales/en.json'),
meta: { display: 'English', flag: '🇬🇧' },
},
ru: {
messages: () => import('./locales/ru.json'),
meta: { display: 'Русский', flag: '🇷🇺' },
},
},
persistLocale: true,
}),
)
app.mount('#app')
3. Use composables in components
<script setup lang="ts">
import { useT, useLocale, useAvailableLocales } from 'vue-i18n-kit'
const { t, tm } = useT()
const { locale, setLocale, isLoading, localeMeta } = useLocale()
const { availableLocales } = useAvailableLocales()
</script>
<template>
<div>
<p>{{ t('greeting', { name: 'Alice' }) }}</p>
<p>{{ tm('items', { count: 5 }) }}</p>
<select :value="locale" @change="setLocale(($event.target as HTMLSelectElement).value)">
<option v-for="loc in availableLocales" :key="loc.code" :value="loc.code">
{{ loc.meta?.flag }} {{ loc.meta?.display ?? loc.code }}
</option>
</select>
<span v-if="isLoading">Loading…</span>
<button :disabled="isLoading">{{ t('buttons.submit') }}</button>
</div>
</template>
More examples
Dates, numbers, and currency adapt themselves to the language
formatDate/formatNumber/formatCurrency wrap the native Intl APIs, follow the active locale, and re-render themselves the moment it switches — no hand-rolled per-language formatting.
import { useFormat } from 'vue-i18n-kit'
const { formatDate, formatNumber, formatCurrency } = useFormat()
formatDate(new Date(), { dateStyle: 'long' }) // '28 марта 2026 г.' (ru)
formatNumber(1_234_567.89) // '1 234 567,89' (ru)
formatCurrency(1999.99, 'EUR') // '1 999,99 €' (ru)
// Switch the active locale and every formatter re-renders itself — no
// manual re-formatting, no separate en/ru number-formatting code paths.
Pluralization that actually handles Russian
One ICU template, and Intl.PluralRules picks the right form — one/few/many/other — instead of a hand-rolled if/else chain per number.
import { useT } from 'vue-i18n-kit'
const { tm } = useT()
// locale: "{points} {points, plural, one {рубль} few {рубля} many {рублей} other {рублей}}"
tm('balance', { points: 1 }) // → '1 рубль'
tm('balance', { points: 3 }) // → '3 рубля'
tm('balance', { points: 21 }) // → '21 рубль'
tm('balance', { points: 25 }) // → '25 рублей'
// Same template, four different real Russian plural forms — driven by
// Intl.PluralRules under the hood, not a hand-rolled if/else chain.
A forgotten translation breaks the build, not production
The Vite plugin diffs every locale against the reference one on every save and on build — a missing key surfaces in seconds, not after a user complaint.
import { vueI18nCheckPlugin } from 'vue-i18n-kit/vite'
export default defineConfig({
plugins: [
vue(),
vueI18nCheckPlugin({
localesDir: 'src/locales',
defaultLocale: 'en',
failOnMissing: true,
}),
],
})
// Runs on every locale-file save (HMR) and again at build start:
//
// [vue-i18n-kit] Incomplete translations detected (reference: "en"):
// Locale "ru":
// Missing keys (2):
// - buttons.cancel
// - profile.title
Documentation & links
- 📖 Full documentation: npm.vuecraft.ru/en/packages/vue-i18n-kit
- 🌐 VueCraft: vuecraft.ru/en
- 👤 Author: macrulez.ru/en
- 💻 GitHub: macrulezru/vue-i18n-kit
- 📦 NPM: vue-i18n-kit
- 🐛 Issues: github.com/macrulezru/vue-i18n-kit/issues
License
MIT
💖 Support the project
Open source takes time and effort. If this library saves you time or brings value, consider supporting further development.
Thank you for being part of this journey. ❤️
Related Packages
Lightweight Vue 3 i18n library with lazy-loading blocks and template caching
Enable component-scoped i18n with Option API when using vue-i18n in composition...
Per-page, all-locale translation sources merged into vue-i18n messages, plus a C...
Localization for Laravel applications, from the terminal: Lyngo wraps user-facin...
Version History
| Version | Released | Vue | License |
|---|---|---|---|
| 0.4.12 | ^3.3.0 | MIT | |
| 0.4.11 | ^3.3.0 | MIT | |
| 0.4.10 | ^3.3.0 | MIT | |
| 0.4.9 | ^3.3.0 | MIT | |
| 0.4.8 | ^3.3.0 | MIT | |
| 0.4.7 | ^3.3.0 | MIT | |
| 0.4.6 | ^3.3.0 | MIT | |
| 0.4.5 | ^3.3.0 | MIT | |
| 0.4.4 | ^3.3.0 | MIT | |
| 0.4.3 | ^3.3.0 | MIT | |
| 0.4.2 | ^3.3.0 | MIT | |
| 0.4.1 | ^3.3.0 | MIT | |
| 0.4.0 | ^3.3.0 | MIT | |
| 0.3.3 | ^3.3.0 | MIT | |
| 0.3.2 | ^3.3.0 | MIT | |
| 0.3.1 | ^3.3.0 | MIT | |
| 0.3.0 | ^3.3.0 | MIT | |
| 0.2.1 | ^3.3.0 | MIT | |
| 0.2.0 | ^3.3.0 | MIT | |
| 0.1.10 | ^3.3.0 | MIT | |
| 0.1.9 | ^3.3.0 | MIT | |
| 0.1.8 | ^3.3.0 | MIT | |
| 0.1.7 | ^3.3.0 | MIT | |
| 0.1.5 | ^3.3.0 | MIT | |
| 0.1.4 | ^3.3.0 | MIT | |
| 0.1.3 | ^3.3.0 | MIT | |
| 0.1.2 | ^3.3.0 | MIT | |
| 0.1.0 | ^3.3.0 | MIT |