Skip to content

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.

ts
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: 'Русский' } },
  },
})
ts
// 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.

ts
// 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 — no getCurrentInstance() 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:

ts
// ✅ Correct — one plugin instance per Nuxt request
export default defineNuxtPlugin((nuxtApp) => {
  const plugin = createVueI18nPlugin({ ... })
  nuxtApp.vueApp.use(plugin)
})
ts
// ❌ 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:

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