276 lines
10 KiB
JavaScript
Raw Normal View History

2025-09-05 14:59:21 +08:00
import { isArray, isString, isObject } from "@intlify/shared";
import { hasProtocol } from "ufo";
import isHTTPS from "is-https";
import {
useRequestHeaders,
useRequestEvent,
useCookie as useNuxtCookie,
useRuntimeConfig,
useNuxtApp,
unref
} from "#imports";
import { NUXT_I18N_MODULE_ID, DEFAULT_COOKIE_KEY, isSSG, localeCodes, normalizedLocales } from "#build/i18n.options.mjs";
import { findBrowserLocale, getI18nTarget, getLocalesRegex } from "./routing/utils.js";
import { createLogger, initCommonComposableOptions } from "./utils.js";
export function formatMessage(message) {
return NUXT_I18N_MODULE_ID + " " + message;
}
export function callVueI18nInterfaces(i18n, name, ...args) {
const target = getI18nTarget(i18n);
const [obj, method] = [target, target[name]];
return Reflect.apply(method, obj, [...args]);
}
export function getVueI18nPropertyValue(i18n, name) {
const target = getI18nTarget(i18n);
return unref(target[name]);
}
export function defineGetter(obj, key, val) {
Object.defineProperty(obj, key, { get: () => val });
}
export function wrapComposable(fn, common = initCommonComposableOptions()) {
return (...args) => fn(common, ...args);
}
export function parseAcceptLanguage(input) {
return input.split(",").map((tag) => tag.split(";")[0]);
}
export function getBrowserLocale() {
let ret;
if (import.meta.client) {
if (navigator.languages) {
ret = findBrowserLocale(normalizedLocales, navigator.languages);
__DEBUG__ && console.log("getBrowserLocale (navigator.languages, ret) -", navigator.languages, ret);
}
} else if (import.meta.server) {
const header = useRequestHeaders(["accept-language"]);
__DEBUG__ && console.log("getBrowserLocale accept-language", header);
const accept = header["accept-language"];
if (accept) {
ret = findBrowserLocale(normalizedLocales, parseAcceptLanguage(accept));
__DEBUG__ && console.log("getBrowserLocale ret", ret);
}
}
return ret;
}
export function getI18nCookie() {
const detect = runtimeDetectBrowserLanguage();
const cookieKey = detect && detect.cookieKey || DEFAULT_COOKIE_KEY;
const date = /* @__PURE__ */ new Date();
const cookieOptions = {
expires: new Date(date.setDate(date.getDate() + 365)),
path: "/",
sameSite: detect && detect.cookieCrossOrigin ? "none" : "lax",
secure: detect && detect.cookieCrossOrigin || detect && detect.cookieSecure
};
if (detect && detect.cookieDomain) {
cookieOptions.domain = detect.cookieDomain;
}
return useNuxtCookie(cookieKey, cookieOptions);
}
export function getLocaleCookie(cookieRef, detect, defaultLocale) {
__DEBUG__ && console.log("getLocaleCookie", {
useCookie: detect && detect.useCookie,
cookieKey: detect && detect.cookieKey,
localeCodes
});
if (detect === false || !detect.useCookie) {
return;
}
const localeCode = cookieRef.value ?? void 0;
const env = import.meta.client ? "client" : "server";
if (localeCode == null) {
__DEBUG__ && console.log(`getLocaleCookie (${env}) - none`);
return;
}
if (localeCodes.includes(localeCode)) {
__DEBUG__ && console.log(`getLocaleCookie (${env}) - locale from cookie: `, localeCode);
return localeCode;
}
if (defaultLocale) {
__DEBUG__ && console.log(
`getLocaleCookie (${env}) - unknown locale cookie (${localeCode}), setting to defaultLocale (${defaultLocale})`
);
cookieRef.value = defaultLocale;
return defaultLocale;
}
__DEBUG__ && console.log(`getLocaleCookie (${env}) - unknown locale cookie (${localeCode}), unsetting cookie`);
cookieRef.value = void 0;
return;
}
export function setLocaleCookie(cookieRef, locale, detect) {
if (detect === false || !detect.useCookie) {
return;
}
cookieRef.value = locale;
}
export var DetectFailure = /* @__PURE__ */ ((DetectFailure2) => {
DetectFailure2["NOT_FOUND"] = "not_found_match";
DetectFailure2["FIRST_ACCESS"] = "first_access_only";
DetectFailure2["NO_REDIRECT_ROOT"] = "not_redirect_on_root";
DetectFailure2["NO_REDIRECT_NO_PREFIX"] = "not_redirect_on_no_prefix";
DetectFailure2["SSG_IGNORE"] = "detect_ignore_on_ssg";
return DetectFailure2;
})(DetectFailure || {});
var DetectFrom = /* @__PURE__ */ ((DetectFrom2) => {
DetectFrom2["COOKIE"] = "cookie";
DetectFrom2["NAVIGATOR_HEADER"] = "navigator_or_header";
DetectFrom2["FALLBACK"] = "fallback";
return DetectFrom2;
})(DetectFrom || {});
export const DefaultDetectBrowserLanguageFromResult = { locale: "" };
export function detectBrowserLanguage(route, detectLocaleContext, locale = "") {
const logger = createLogger("detectBrowserLanguage");
const _detect = runtimeDetectBrowserLanguage();
if (!_detect) {
return DefaultDetectBrowserLanguageFromResult;
}
const { strategy } = useRuntimeConfig().public.i18n;
const { ssg, callType, firstAccess, localeCookie } = detectLocaleContext;
__DEBUG__ && logger.log({ ssg, callType, firstAccess });
if (isSSG && strategy === "no_prefix" && (import.meta.server || ssg === "ssg_ignore")) {
return { locale: "", reason: "detect_ignore_on_ssg" /* SSG_IGNORE */ };
}
if (!firstAccess) {
return { locale: strategy === "no_prefix" ? locale : "", reason: "first_access_only" /* FIRST_ACCESS */ };
}
const { redirectOn, alwaysRedirect, useCookie, fallbackLocale } = _detect;
const path = isString(route) ? route : route.path;
__DEBUG__ && logger.log({ locale, path, strategy, alwaysRedirect, redirectOn });
if (strategy !== "no_prefix") {
if (redirectOn === "root" && path !== "/") {
__DEBUG__ && logger.log("not root", { path });
return { locale: "", reason: "not_redirect_on_root" /* NO_REDIRECT_ROOT */ };
}
__DEBUG__ && redirectOn === "no prefix" && logger.log("no prefix -", { path });
if (redirectOn === "no prefix" && !alwaysRedirect && path.match(getLocalesRegex(localeCodes))) {
return { locale: "", reason: "not_redirect_on_no_prefix" /* NO_REDIRECT_NO_PREFIX */ };
}
}
let from;
const cookieMatch = useCookie && localeCookie || void 0;
if (useCookie) {
from = "cookie" /* COOKIE */;
}
const browserMatch = getBrowserLocale();
if (!cookieMatch) {
from = "navigator_or_header" /* NAVIGATOR_HEADER */;
}
const matchedLocale = cookieMatch || browserMatch;
const resolved = matchedLocale || fallbackLocale || "";
if (!matchedLocale && fallbackLocale) {
from = "fallback" /* FALLBACK */;
}
__DEBUG__ && logger.log({ locale: resolved, cookieMatch, browserMatch, from });
return { locale: resolved, from };
}
export function getHost() {
let host;
if (import.meta.client) {
host = window.location.host;
} else if (import.meta.server) {
const header = useRequestHeaders(["x-forwarded-host", "host"]);
let detectedHost;
if ("x-forwarded-host" in header) {
detectedHost = header["x-forwarded-host"];
} else if ("host" in header) {
detectedHost = header["host"];
}
host = isArray(detectedHost) ? detectedHost[0] : detectedHost;
}
return host;
}
export function getLocaleDomain(locales, strategy, route) {
let host = getHost() || "";
if (host) {
__DEBUG__ && console.log(
`MultiDomainsMultiLocales: locating domain for host: `,
host,
strategy,
isObject(route) ? route.path : route
);
let matchingLocale;
const matchingLocales = locales.filter((locale) => {
if (locale && locale.domain) {
let domain = locale.domain;
if (hasProtocol(locale.domain)) {
domain = locale.domain.replace(/(http|https):\/\//, "");
}
return domain === host;
} else if (Array.isArray(locale?.domains)) {
return locale.domains.includes(host);
}
return false;
});
if (matchingLocales.length === 1) {
matchingLocale = matchingLocales[0];
__DEBUG__ && console.log(`MultiDomainsMultiLocales: found only one matching domain: `, host, matchingLocales[0].code);
} else if (matchingLocales.length > 1) {
if (strategy === "no_prefix") {
console.warn(
formatMessage(
"Multiple matching domains found! This is not supported for no_prefix strategy in combination with differentDomains!"
)
);
matchingLocale = matchingLocales[0];
} else {
if (route) {
const routePath = isObject(route) ? route.path : isString(route) ? route : "";
__DEBUG__ && console.log(`MultiDomainsMultiLocales: Check in matched domain for locale match in path: `, routePath, host);
if (routePath && routePath !== "") {
const matches = routePath.match(getLocalesRegex(matchingLocales.map((l) => l.code)));
if (matches && matches.length > 1) {
matchingLocale = matchingLocales.find((l) => l.code === matches[1]);
__DEBUG__ && console.log(
`MultiDomainsMultiLocales: Found matching locale from path. MatchingLocale is now`,
matchingLocale?.code
);
}
}
}
if (!matchingLocale) {
matchingLocale = matchingLocales.find(
(l) => Array.isArray(l.defaultForDomains) ? l.defaultForDomains.includes(host) : l.domainDefault
);
__DEBUG__ && console.log(
`MultiDomainsMultiLocales: matching locale not found - trying to get default for this domain. MatchingLocale is now`,
matchingLocale?.code
);
}
}
}
if (matchingLocale) {
return matchingLocale.code;
} else {
host = "";
}
}
return host;
}
export function getDomainFromLocale(localeCode) {
const runtimeConfig = useRuntimeConfig();
const nuxtApp = useNuxtApp();
const host = getHost();
const config = runtimeConfig.public.i18n;
const lang = normalizedLocales.find((locale) => locale.code === localeCode);
const domain = config?.locales?.[localeCode]?.domain || lang?.domain || config?.locales?.[localeCode]?.domains?.find((v) => v === host) || lang?.domains?.find((v) => v === host);
if (domain) {
if (hasProtocol(domain, { strict: true })) {
return domain;
}
let protocol;
if (import.meta.server) {
const {
node: { req }
} = useRequestEvent(nuxtApp);
protocol = req && isHTTPS(req) ? "https:" : "http:";
} else {
protocol = new URL(window.location.origin).protocol;
}
return protocol + "//" + domain;
}
console.warn(formatMessage("Could not find domain name for locale " + localeCode));
}
export const runtimeDetectBrowserLanguage = (opts = useRuntimeConfig().public.i18n) => {
if (opts?.detectBrowserLanguage === false) return false;
return opts?.detectBrowserLanguage;
};