405 lines
15 KiB
JavaScript
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 }
|
|
);
|
|
}
|
|
});
|