Hooks
The module calls two runtime (app) hooks. i18n:register adds translations that are not in your locales/ files. i18n:beforeLocaleSwitch runs before the page switches to another locale, and can send it to another locale or stop it.
i18n:register
'i18n:register': (
register: (translations: unknown, locale?: string) => void,
locale: string,
) => HookResult
translations into the page chunk of the route being loaded, for locale (default: the hook's locale). Keys already there are overridden.The hook is typed through RuntimeNuxtHooks, so nuxtApp.hook('i18n:register', …) needs no cast.
When it fires
The loader plugin (i18n-plugin-loader) calls it:
- once on startup, when every plugin has run (
app:created) and before the app renders; - in its
router.beforeEach, after the target page's translations are loaded, when the path changes — on every navigation underno_prefix.
Every callback and every register call is awaited before the app renders or the navigation goes on, so the page renders with the merged keys. With hooks: false the hook is never called.
Example
// plugins/i18n-extra.ts
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('i18n:register', async (register, locale) => {
const extra = await $fetch<Record<string, unknown>>(`/api/cms-strings/${locale}`)
register(extra, locale)
})
})
This is a runtime hook: register it with nuxtApp.hook in a plugin, not with nuxt.hook in nuxt.config or a module's setup. A module adds such a plugin with addPlugin.
i18n:beforeLocaleSwitch
'i18n:beforeLocaleSwitch': (ctx: { from: string; to: string; cancel: boolean }) => HookResult
true to stay: the page, its dictionary and the stored preference do not change.// plugins/confirm-locale.client.ts
export default defineNuxtPlugin((nuxtApp) => {
nuxtApp.hook('i18n:beforeLocaleSwitch', async (ctx) => {
if (hasUnsavedChanges() && !confirm('Leave without saving?')) ctx.cancel = true
// a regional locale the app does not have yet
if (ctx.to === 'de-at') ctx.to = 'de'
})
})
It is called once per switch, on the client, after the first load:
- for a navigation into another locale:
$switchLocale,<i18n-switcher>, a link orrouter.pushto a page of another locale (prefix strategies). A cancel aborts the navigation, as a guard returningfalse; anothertoredirects to the same page in that locale, and that navigation is not asked again; - under
no_prefixand in hash mode, by$switchLocalebefore the dictionary loads (there the dictionary moves first, and no guard could undo it).
$switchLocale resolves false when the switch is cancelled, and true when the page switched, also to a locale the hook chose. A switch to a locale on another domain (locales[].baseUrl) leaves the app and is not asked. With nobody subscribed the hook costs one lookup per switch, and with hooks: false it is never called.
After a switch
There is no hook after a switch: watch the locale. A watch of $getLocale() fires once the new locale is applied, its dictionary loaded and in place:
// plugins/locale-analytics.client.ts
export default defineNuxtPlugin((nuxtApp) => {
watch(
() => nuxtApp.$getLocale(),
(locale) => track('locale', locale),
)
})