import { computed } from "vue"; import { createI18n } from "vue-i18n"; import { defineNuxtPlugin, useRoute, addRouteMiddleware, defineNuxtRouteMiddleware, useNuxtApp, useRouter } from "#imports"; import { localeCodes, vueI18nConfigs, isSSG, localeLoaders, parallelPlugin, normalizedLocales } from "#build/i18n.options.mjs"; import { loadVueI18nOptions, loadInitialMessages, loadLocale } from "../messages.js"; import { loadAndSetLocale, detectLocale, detectRedirect, navigate, injectNuxtHelpers, extendBaseUrl, _setLocale, mergeLocaleMessage } from "../utils.js"; import { getBrowserLocale as _getBrowserLocale, getLocaleCookie as _getLocaleCookie, setLocaleCookie as _setLocaleCookie, detectBrowserLanguage, getI18nCookie, runtimeDetectBrowserLanguage, getHost } from "../internal.js"; import { getComposer, getLocale, setLocale } from "../routing/utils.js"; import { extendI18n, createLocaleFromRouteGetter } from "../routing/extends/index.js"; export default defineNuxtPlugin({ name: "i18n:plugin", parallel: parallelPlugin, async setup(nuxt) { const route = useRoute(); const { vueApp: app } = nuxt; const nuxtContext = nuxt; const host = getHost(); const { configLocales, defaultLocale, multiDomainLocales, strategy } = nuxtContext.$config.public.i18n; const hasDefaultForDomains = configLocales.some( (l) => typeof l !== "string" && Array.isArray(l.defaultForDomains) ); let defaultLocaleDomain; if (defaultLocale) { defaultLocaleDomain = defaultLocale; } else if (hasDefaultForDomains) { const findDefaultLocale = configLocales.find( (l) => typeof l === "string" || !Array.isArray(l.defaultForDomains) ? false : l.defaultForDomains.includes(host ?? "") ); defaultLocaleDomain = findDefaultLocale?.code ?? ""; } else { defaultLocaleDomain = ""; } if (multiDomainLocales && (strategy === "prefix_except_default" || strategy === "prefix_and_default")) { const router = useRouter(); router.getRoutes().forEach((route2) => { if (route2.name?.toString().includes("___default")) { const routeNameLocale = route2.name.toString().split("___")[1]; if (routeNameLocale !== defaultLocaleDomain) { router.removeRoute(route2.name); } else { const newRouteName = route2.name.toString().replace("___default", ""); route2.name = newRouteName; } } }); } const runtimeI18n = { ...nuxtContext.$config.public.i18n, defaultLocale: defaultLocaleDomain }; runtimeI18n.baseUrl = extendBaseUrl(); const _detectBrowserLanguage = runtimeDetectBrowserLanguage(); __DEBUG__ && console.log("isSSG", isSSG); __DEBUG__ && console.log("useCookie on setup", _detectBrowserLanguage && _detectBrowserLanguage.useCookie); __DEBUG__ && console.log("defaultLocale on setup", runtimeI18n.defaultLocale); const vueI18nOptions = await loadVueI18nOptions(vueI18nConfigs, useNuxtApp()); vueI18nOptions.messages = vueI18nOptions.messages || {}; vueI18nOptions.fallbackLocale = vueI18nOptions.fallbackLocale ?? false; const getLocaleFromRoute = createLocaleFromRouteGetter(); const getDefaultLocale = (locale) => locale || vueI18nOptions.locale || "en-US"; const localeCookie = getI18nCookie(); let initialLocale = detectLocale( route, getLocaleFromRoute, getDefaultLocale(runtimeI18n.defaultLocale), { ssg: isSSG && runtimeI18n.strategy === "no_prefix" ? "ssg_ignore" : "normal", callType: "setup", firstAccess: true, localeCookie: _getLocaleCookie(localeCookie, _detectBrowserLanguage, runtimeI18n.defaultLocale) }, runtimeI18n ); __DEBUG__ && console.log("first detect initial locale", initialLocale); vueI18nOptions.messages = await loadInitialMessages(vueI18nOptions.messages, localeLoaders, { localeCodes, initialLocale, lazy: runtimeI18n.lazy, defaultLocale: runtimeI18n.defaultLocale, fallbackLocale: vueI18nOptions.fallbackLocale }); initialLocale = getDefaultLocale(initialLocale); __DEBUG__ && console.log("final initial locale:", initialLocale); const i18n = createI18n({ ...vueI18nOptions, locale: initialLocale }); let notInitialSetup = true; const isInitialLocaleSetup = (locale) => initialLocale !== locale && notInitialSetup; let ssgModeInitialSetup = true; const isSSGModeInitialSetup = () => isSSG && ssgModeInitialSetup; if (isSSGModeInitialSetup() && runtimeI18n.strategy === "no_prefix" && import.meta.client) { const initialLocaleCookie = localeCookie.value; nuxt.hook("app:mounted", () => { __DEBUG__ && console.log("hook app:mounted"); const detected = detectBrowserLanguage( route, { ssg: "ssg_setup", callType: "setup", firstAccess: true, localeCookie: initialLocaleCookie }, initialLocale ); __DEBUG__ && console.log("app:mounted: detectBrowserLanguage (locale, reason, from) -", Object.values(detected)); _setLocale(i18n, detected.locale); ssgModeInitialSetup = false; }); } extendI18n(i18n, { locales: runtimeI18n.configLocales, localeCodes, baseUrl: runtimeI18n.baseUrl, context: nuxtContext, hooks: { onExtendComposer(composer) { composer.strategy = runtimeI18n.strategy; composer.localeProperties = computed( () => normalizedLocales.find((l) => l.code === composer.locale.value) || { code: composer.locale.value } ); composer.setLocale = async (locale) => { const localeSetup = isInitialLocaleSetup(locale); const modified = await loadAndSetLocale(locale, i18n, runtimeI18n, localeSetup); if (modified && localeSetup) { notInitialSetup = false; } const redirectPath = await nuxtContext.runWithContext( () => detectRedirect({ route: { to: route }, targetLocale: locale, routeLocaleGetter: getLocaleFromRoute }) ); __DEBUG__ && console.log("redirectPath on setLocale", redirectPath); await nuxtContext.runWithContext( async () => await navigate( { nuxtApp: nuxtContext, i18n, redirectPath, locale, route }, { enableNavigate: true } ) ); }; composer.loadLocaleMessages = async (locale) => { const setter = (locale2, message) => mergeLocaleMessage(i18n, locale2, message); await loadLocale(locale, localeLoaders, setter); }; composer.differentDomains = runtimeI18n.differentDomains; composer.defaultLocale = runtimeI18n.defaultLocale; composer.getBrowserLocale = () => _getBrowserLocale(); composer.getLocaleCookie = () => _getLocaleCookie(localeCookie, _detectBrowserLanguage, runtimeI18n.defaultLocale); composer.setLocaleCookie = (locale) => _setLocaleCookie(localeCookie, locale, _detectBrowserLanguage); composer.onBeforeLanguageSwitch = (oldLocale, newLocale, initialSetup, context) => nuxt.callHook("i18n:beforeLocaleSwitch", { oldLocale, newLocale, initialSetup, context }); composer.onLanguageSwitched = (oldLocale, newLocale) => nuxt.callHook("i18n:localeSwitched", { oldLocale, newLocale }); composer.finalizePendingLocaleChange = async () => { if (!i18n.__pendingLocale) { return; } setLocale(i18n, i18n.__pendingLocale); if (i18n.__resolvePendingLocalePromise) { await i18n.__resolvePendingLocalePromise(); } i18n.__pendingLocale = void 0; }; composer.waitForPendingLocaleChange = async () => { if (i18n.__pendingLocale && i18n.__pendingLocalePromise) { await i18n.__pendingLocalePromise; } }; }, onExtendExportedGlobal(g) { return { strategy: { get() { return g.strategy; } }, localeProperties: { get() { return g.localeProperties.value; } }, setLocale: { get() { return async (locale) => Reflect.apply(g.setLocale, g, [locale]); } }, differentDomains: { get() { return g.differentDomains; } }, defaultLocale: { get() { return g.defaultLocale; } }, getBrowserLocale: { get() { return () => Reflect.apply(g.getBrowserLocale, g, []); } }, getLocaleCookie: { get() { return () => Reflect.apply(g.getLocaleCookie, g, []); } }, setLocaleCookie: { get() { return (locale) => Reflect.apply(g.setLocaleCookie, g, [locale]); } }, onBeforeLanguageSwitch: { get() { return (oldLocale, newLocale, initialSetup, context) => Reflect.apply(g.onBeforeLanguageSwitch, g, [oldLocale, newLocale, initialSetup, context]); } }, onLanguageSwitched: { get() { return (oldLocale, newLocale) => Reflect.apply(g.onLanguageSwitched, g, [oldLocale, newLocale]); } }, finalizePendingLocaleChange: { get() { return () => Reflect.apply(g.finalizePendingLocaleChange, g, []); } }, waitForPendingLocaleChange: { get() { return () => Reflect.apply(g.waitForPendingLocaleChange, g, []); } } }; }, onExtendVueI18n(composer) { return { strategy: { get() { return composer.strategy; } }, localeProperties: { get() { return composer.localeProperties.value; } }, setLocale: { get() { return async (locale) => Reflect.apply(composer.setLocale, composer, [locale]); } }, loadLocaleMessages: { get() { return async (locale) => Reflect.apply(composer.loadLocaleMessages, composer, [locale]); } }, differentDomains: { get() { return composer.differentDomains; } }, defaultLocale: { get() { return composer.defaultLocale; } }, getBrowserLocale: { get() { return () => Reflect.apply(composer.getBrowserLocale, composer, []); } }, getLocaleCookie: { get() { return () => Reflect.apply(composer.getLocaleCookie, composer, []); } }, setLocaleCookie: { get() { return (locale) => Reflect.apply(composer.setLocaleCookie, composer, [locale]); } }, onBeforeLanguageSwitch: { get() { return (oldLocale, newLocale, initialSetup, context) => Reflect.apply(composer.onBeforeLanguageSwitch, composer, [ oldLocale, newLocale, initialSetup, context ]); } }, onLanguageSwitched: { get() { return (oldLocale, newLocale) => Reflect.apply(composer.onLanguageSwitched, composer, [oldLocale, newLocale]); } }, finalizePendingLocaleChange: { get() { return () => Reflect.apply(composer.finalizePendingLocaleChange, composer, []); } }, waitForPendingLocaleChange: { get() { return () => Reflect.apply(composer.waitForPendingLocaleChange, composer, []); } } }; } } }); const pluginOptions = { __composerExtend: (c) => { const g = getComposer(i18n); c.strategy = g.strategy; c.localeProperties = computed(() => g.localeProperties.value); c.setLocale = g.setLocale; c.differentDomains = g.differentDomains; c.getBrowserLocale = g.getBrowserLocale; c.getLocaleCookie = g.getLocaleCookie; c.setLocaleCookie = g.setLocaleCookie; c.onBeforeLanguageSwitch = g.onBeforeLanguageSwitch; c.onLanguageSwitched = g.onLanguageSwitched; c.finalizePendingLocaleChange = g.finalizePendingLocaleChange; c.waitForPendingLocaleChange = g.waitForPendingLocaleChange; return () => { }; } }; app.use(i18n, pluginOptions); injectNuxtHelpers(nuxtContext, i18n); let routeChangeCount = 0; addRouteMiddleware( "locale-changing", defineNuxtRouteMiddleware(async (to, from) => { __DEBUG__ && console.log("locale-changing middleware", to, from); const locale = detectLocale( to, getLocaleFromRoute, () => { return getLocale(i18n) || getDefaultLocale(runtimeI18n.defaultLocale); }, { ssg: isSSGModeInitialSetup() && runtimeI18n.strategy === "no_prefix" ? "ssg_ignore" : "normal", callType: "routing", firstAccess: routeChangeCount === 0, localeCookie: _getLocaleCookie(localeCookie, _detectBrowserLanguage, runtimeI18n.defaultLocale) }, runtimeI18n ); __DEBUG__ && console.log("detect locale", locale); const localeSetup = isInitialLocaleSetup(locale); __DEBUG__ && console.log("localeSetup", localeSetup); const modified = await loadAndSetLocale(locale, i18n, runtimeI18n, localeSetup); if (modified && localeSetup) { notInitialSetup = false; } const redirectPath = await nuxtContext.runWithContext( () => detectRedirect({ route: { to, from }, targetLocale: locale, routeLocaleGetter: runtimeI18n.strategy === "no_prefix" ? () => locale : getLocaleFromRoute, calledWithRouting: true }) ); __DEBUG__ && console.log("redirectPath on locale-changing middleware", redirectPath); routeChangeCount++; return await nuxtContext.runWithContext( async () => navigate({ nuxtApp: nuxtContext, i18n, redirectPath, locale, route: to }) ); }), { global: true } ); } });