Plugin Service
createVueI18nPlugin returns an I18nPlugin object — it satisfies Vue's Plugin interface (so app.use(plugin) works unchanged) and exposes a .service property that is usable anywhere in the application, including outside Vue component setup(). For the options passed into createVueI18nPlugin itself, see Plugin Setup.
import { createVueI18nPlugin } from 'vue-i18n-kit'
export const i18nPlugin = createVueI18nPlugin({
defaultLocale: 'en',
locales: {
en: { messages: () => import('./locales/en.json'), meta: { display: 'English' } },
ru: { messages: () => import('./locales/ru.json'), meta: { display: 'Русский' } },
},
})// router/index.ts — outside setup()
import { i18nPlugin } from '@/i18n'
router.beforeEach(async (to) => {
const lang = to.params.lang as string
if (lang) await i18nPlugin.service.setLocale(lang)
})service API
locale
Ref<string> — currently active locale — the same ref instance as useLocale().locale.
isLoading
Ref<boolean> — true while a locale file is being fetched.
setLocale
(lang: string) => Promise<void> — switch locale. Lazy-loads if needed. Throws if lang is not registered.
availableLocales
ComputedRef<LocaleInfo[]> — all registered locales with their metadata. Same computed instance on every access.
onLocaleChange
(cb: (lang: string) => void) => () => void — subscribe to locale switches from anywhere — useful for syncing external state that cannot be driven by Vue reactivity. Returns an unsubscribe function.
// Update <html lang> on every switch
i18nPlugin.service.onLocaleChange((lang) => {
document.documentElement.lang = lang
})
// Unsubscribe when no longer needed
const unsubscribe = i18nPlugin.service.onLocaleChange((lang) => {
analytics.track('locale_changed', { lang })
})
unsubscribe()loadNamespace
(ns: string) => Promise<void> — loads one namespace for the active locale. See Namespace Loading for the composable equivalent and the namespace concept in general.
isNamespaceLoaded
(ns: string) => boolean — checks whether a namespace is already loaded, without triggering a fetch.
service vs composables — when to use which
- Vue component
setup()—useLocale(),useT(),useAvailableLocales()— reactive, template-friendly. - Router guards, Pinia stores, utility modules —
plugin.service— nogetCurrentInstance()needed. - SSR entry points, server middleware —
plugin.service— inject the plugin instance from your plugin file.
SSR note
service stores state in a closure created when createVueI18nPlugin is called. In SSR the plugin must be created per request, not at module level:
// ✅ Correct — one plugin instance per Nuxt request
export default defineNuxtPlugin((nuxtApp) => {
const plugin = createVueI18nPlugin({ ... })
nuxtApp.vueApp.use(plugin)
})// ❌ Wrong — shared across all SSR requests
const plugin = createVueI18nPlugin({ ... }) // module level
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.vueApp.use(plugin) // plugin.service.locale is shared — requests contaminate each other
})Error handling
Unknown locale
setLocale throws a descriptive error if the requested locale is not registered:
try {
await setLocale('de')
} catch (err) {
// [vue-i18n-kit] Locale "de" is not registered. Available locales: en, ru
console.error(err.message)
}Failed network request
If the async loader function rejects, setLocale resets isLoading to false and re-throws the original error. isLoading.value is guaranteed to be false after the catch block.
Plugin not installed
Calling any composable before app.use(createVueI18nPlugin(...)) throws immediately:
[vue-i18n-kit] Plugin not installed. Call app.use(createVueI18nPlugin(...)) before using composables.