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