2025-09-05 14:59:21 +08:00

405 lines
15 KiB
JavaScript

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 }
);
}
});