vite-vue-internationalization
v1.1.3
Published
A Vite plugin for typed Vue SFC translations with SFC custom blocks and inline locale chunks
Readme
vite-vue-internationalization
A typed internationalization plugin for Vite that lets Vue SFCs own their translations directly.
VVI is the short name for vite-vue-internationalization.
Documentation
Links
It supports <locale> custom blocks, global dictionaries, Volar type completion, and optional locale-specific chunk output.
VVI targets Vite 8 and newer. Builds run on Vite's Rolldown-based pipeline and require Node.js ^20.19.0 || >=22.12.0.
<template>
<h1>{{ $locale.sfc.title }}</h1>
<p>{{ $l.sfc.count({ n }) }}</p>
</template>
<script setup lang="ts">
const n = 3;
</script>
<locale locale="ja-JP" lang="yaml">
title: りんご
count: "{n} 個のりんご"
</locale>
<locale locale="en-US" lang="yaml">
title: Apple
count: "one apple | {n} apples"
</locale>In <script setup>, injected $locale and $l bindings are computed refs. Use .value in script code, while templates keep the direct $locale.sfc.title / $l.sfc.count({ n }) form:
<script setup lang="ts">
const title = $locale.value.sfc.title;
const countText = $l.value.sfc.count({ n: 3 });
</script>For component-local messages, prefer a <locale> block or top-level defineInternationalization() in the same SFC. For app-wide messages in SFCs that do not own local messages, configure sfcTransform: "all" instead of adding empty dictionaries just to force injection. Plain .ts modules should use useLocale() / useLocalizer() from virtual:vite-vue-internationalization.
Features
- Write translations as YAML or JSON in Vue SFC
<locale>blocks. - Get typed
$localeand$lcompletions in templates and TypeScript. - Read app-wide global dictionaries through the same API.
- Opt into injecting
$localeand$lfor every SFC when global dictionary access is needed outside locale-owning components. - Choose between Vue I18n-compatible syntax (
vue) and ICU message syntax (icu). - Choose either the default
virtualbuild strategy orinline-chunksfor locale-specific output chunks. - Share the same configuration between the Vite plugin and Vue Language Tools / Volar.
Minimal Setup
// vite.config.ts
import vue from '@vitejs/plugin-vue';
import { defineConfig } from 'vite';
import { vueInternationalization } from 'vite-vue-internationalization';
export default defineConfig({
plugins: [
vueInternationalization(),
vue(),
],
});// tsconfig.json
{
"vueCompilerOptions": {
"plugins": [
{
"name": "vite-vue-internationalization/volar",
"primaryLocale": "ja-JP"
}
]
}
}Set sfcTransform: "all" when SFCs without <locale> blocks or defineInternationalization() still need $locale.env or $l.env global dictionary access:
{
"vueCompilerOptions": {
"plugins": [
{
"name": "vite-vue-internationalization/volar",
"primaryLocale": "ja-JP",
"sfcTransform": "all"
}
]
}
}Vite transform output keeps global env bindings as broad runtime dictionary types to avoid duplicating large global type literals in every transformed SFC. Vue Language Tools / Volar uses detailed global dictionary types by default for editor completion and vue-tsc; set globalType: "runtime" in the Volar plugin config when the global dictionary is too large for type checking.
// src/env.d.ts
/// <reference types="vite-vue-internationalization/virtual" />// src/main.ts
import { createApp } from 'vue';
import { createInternationalization } from 'virtual:vite-vue-internationalization';
import App from './App.vue';
const app = createApp(App);
const internationalization = createInternationalization();
app.use(internationalization);
await internationalization.ready;
app.mount('#app');You may import virtual:vite-vue-internationalization from app .ts modules, not only from Vue SFCs. This is the supported way to share createInternationalization(), currentLocale, primaryLocale, and other generated runtime exports across app code.
Plain TypeScript Modules
Plain .ts modules do not receive implicit $locale or $l bindings. Use useLocale() and useLocalizer() from the virtual module instead:
import { useLocale, useLocalizer } from 'virtual:vite-vue-internationalization';
export function useAppMessages() {
const $locale = useLocale(import.meta.url);
const $l = useLocalizer(import.meta.url);
return {
appName: () => $locale.value.env.appName,
greeting: (name: string) => $l.value.env.greeting({ name }),
};
}Call these helpers after app.use(createInternationalization()), such as inside Vue setup code or functions called from it. In plain .ts modules, sfc points at the module id passed to useLocale() / useLocalizer(); for app-wide dictionaries, prefer env.
Inline Chunks
The default virtual strategy keeps locale payloads in virtual modules and lets Vite split them with dynamic import() calls.
Use buildStrategy: "inline-chunks" when you want build-time locale-specific JavaScript chunks. This strategy duplicates localizable chunks per locale and replaces static $locale / $l references with locale-specific string literals, accessed subtrees, or message formatting expressions.
// vite.config.ts
export default defineConfig({
plugins: [
vueInternationalization({
primaryLocale: 'ja-JP',
buildStrategy: 'inline-chunks',
}),
vue(),
],
});The generated HTML entry script is replaced with a small *.i18n-loader.js file. Existing script attributes such as nonce, crossorigin, and referrerpolicy are preserved. If the original script has integrity, it is replaced with integrity for the generated loader, and the loader verifies the selected locale chunk with modulepreload and per-locale chunk integrity before importing it.
Static references such as $locale.sfc.title and $l.sfc.count({ n }) are fully inlined. Dynamic subtree lookups such as $locale.env.labels[key] keep a runtime lookup against the resolved locale-specific subtree, but bare locale helper objects are not expanded into full dictionaries. Missing values fall back to the primary locale, then to the key string.
Documentation Pages
- English:
- Japanese:
Examples
- Vue syntax example on StackBlitz
- ICU syntax example on StackBlitz
examples/cloudflare-worker-ssr: Vue SSR to an HTML string in a Cloudflare Workers-style Vite build.examples/nuxt: Nuxt app using VVI throughvite.pluginsand a Nuxt plugin.
To view the documentation locally:
pnpm docs:dev