UNPKG

@nuxtjs/i18n

Version:

Internationalization for Nuxt

249 lines (248 loc) 10.4 kB
import { isEqual } from "ufo"; import { isArray, isFunction, isString } from "@intlify/shared"; import { navigateTo, useNuxtApp, useRouter, useRuntimeConfig, useState } from "#imports"; import { NUXT_I18N_MODULE_ID, isSSG, localeCodes, localeLoaders, normalizedLocales, vueI18nConfigs } from "#build/i18n.options.mjs"; import { getComposer } from "./compatibility.js"; import { getDomainFromLocale, getHost, getLocaleDomain } from "./domain.js"; import { detectBrowserLanguage, runtimeDetectBrowserLanguage } from "./internal.js"; import { loadAndSetLocaleMessages, loadLocale, loadVueI18nOptions, makeFallbackLocaleCodes } from "./messages.js"; import { localePath, switchLocalePath } from "./routing/routing.js"; import { createLogger } from "#nuxt-i18n/logger"; import { unref } from "vue"; export function formatMessage(message) { return `[${NUXT_I18N_MODULE_ID}]: ${message}`; } export function initCommonComposableOptions(i18n) { return { i18n: i18n ?? useNuxtApp().$i18n, router: useRouter(), runtimeConfig: useRuntimeConfig(), metaState: useState("nuxt-i18n-meta", () => ({})) }; } export async function loadAndSetLocale(newLocale, initial = false) { const logger = /* @__PURE__ */ createLogger("loadAndSetLocale"); const nuxtApp = useNuxtApp(); const { differentDomains, skipSettingLocaleOnNavigate } = nuxtApp.$config.public.i18n; const opts = runtimeDetectBrowserLanguage(); const oldLocale = unref(nuxtApp.$i18n.locale); const localeCodes2 = unref(nuxtApp.$i18n.localeCodes); function syncCookie(locale = oldLocale) { if (opts === false || !opts.useCookie) return; if (skipSettingLocaleOnNavigate) return; nuxtApp.$i18n.setLocaleCookie(locale); } const localeOverride = await nuxtApp.$i18n.onBeforeLanguageSwitch(oldLocale, newLocale, initial, nuxtApp); if (localeOverride && localeCodes2.includes(localeOverride)) { if (oldLocale === localeOverride) { syncCookie(); return false; } newLocale = localeOverride; } __DEBUG__ && logger.log({ newLocale, oldLocale, initial }); if (!newLocale) { syncCookie(); return false; } if (!initial && differentDomains) { syncCookie(); return false; } if (oldLocale === newLocale) { syncCookie(); return false; } const i18nFallbackLocales = unref(nuxtApp.$i18n.fallbackLocale); const setter = nuxtApp.$i18n.mergeLocaleMessage.bind(nuxtApp.$i18n); if (i18nFallbackLocales) { const fallbackLocales = makeFallbackLocaleCodes(i18nFallbackLocales, [newLocale]); await Promise.all(fallbackLocales.map((locale) => loadLocale(locale, localeLoaders, setter, nuxtApp))); } await loadLocale(newLocale, localeLoaders, setter, nuxtApp); if (skipSettingLocaleOnNavigate) { return false; } syncCookie(newLocale); nuxtApp._vueI18n.__setLocale(newLocale); await nuxtApp.$i18n.onLanguageSwitched(oldLocale, newLocale); return true; } export function detectLocale(route, routeLocale, currentLocale, localeCookie) { const nuxtApp = useNuxtApp(); const { strategy, defaultLocale, differentDomains, multiDomainLocales } = nuxtApp.$config.public.i18n; const _detectBrowserLanguage = runtimeDetectBrowserLanguage(); const logger = /* @__PURE__ */ createLogger("detectLocale"); const detectedBrowser = detectBrowserLanguage(route, localeCookie, currentLocale); __DEBUG__ && logger.log({ detectBrowserLanguage: detectedBrowser }); if (detectedBrowser.locale && detectedBrowser.from != null && localeCodes.includes(detectedBrowser.locale)) { return detectedBrowser.locale; } let detected = ""; __DEBUG__ && logger.log("1/3", { detected, strategy }); if (differentDomains || multiDomainLocales) { detected ||= getLocaleDomain(normalizedLocales, strategy, route); } else if (strategy !== "no_prefix") { detected ||= routeLocale; } __DEBUG__ && logger.log("2/3", { detected, detectBrowserLanguage: _detectBrowserLanguage }); const cookieLocale = (localeCodes.includes(detectedBrowser.locale) || localeCookie && localeCodes.includes(localeCookie)) && _detectBrowserLanguage && _detectBrowserLanguage.useCookie && localeCookie; detected ||= cookieLocale || currentLocale || defaultLocale || ""; __DEBUG__ && logger.log("3/3", { detected, cookieLocale, defaultLocale, localeCookie }); return detected; } export function detectRedirect({ to, from, locale, routeLocale }, inMiddleware = false) { if (routeLocale === locale || useNuxtApp().$i18n.strategy === "no_prefix") { return ""; } const common = initCommonComposableOptions(); const logger = /* @__PURE__ */ createLogger("detectRedirect"); __DEBUG__ && logger.log({ to, from }); __DEBUG__ && logger.log({ locale, routeLocale, inMiddleware }); let redirectPath = switchLocalePath(common, locale, to); if (inMiddleware && !redirectPath) { redirectPath = localePath(common, to.fullPath, locale); } if (isEqual(redirectPath, to.fullPath) || from && isEqual(redirectPath, from.fullPath)) { return ""; } return redirectPath; } const useRedirectState = () => useState(NUXT_I18N_MODULE_ID + ":redirect", () => ""); export async function navigate({ nuxt, locale, route, redirectPath }, enableNavigate = false) { const { rootRedirect, differentDomains, multiDomainLocales, skipSettingLocaleOnNavigate, locales, strategy } = nuxt.$config.public.i18n; const logger = /* @__PURE__ */ createLogger("navigate"); __DEBUG__ && logger.log("options", { rootRedirect, differentDomains, skipSettingLocaleOnNavigate, enableNavigate, isSSG }); if (route.path === "/" && rootRedirect) { let redirectCode = 302; if (isString(rootRedirect)) { redirectPath = "/" + rootRedirect; } else { redirectPath = "/" + rootRedirect.path; redirectCode = rootRedirect.statusCode; } redirectPath = nuxt.$localePath(redirectPath, locale); __DEBUG__ && logger.log("rootRedirect mode", { redirectPath, redirectCode }); return navigateTo(redirectPath, { redirectCode }); } if (import.meta.client && skipSettingLocaleOnNavigate) { nuxt._vueI18n.__pendingLocale = locale; nuxt._vueI18n.__pendingLocalePromise = new Promise((resolve) => { nuxt._vueI18n.__resolvePendingLocalePromise = () => resolve(); }); if (!enableNavigate) { return; } } if (multiDomainLocales && strategy === "prefix_except_default") { const host = getHost(); const currentDomain = locales.find((locale2) => { if (isString(locale2)) return; return locale2.defaultForDomains?.find((domain) => domain === host); }); const defaultLocaleForDomain = !isString(currentDomain) ? currentDomain?.code : void 0; if (route.path.startsWith(`/${defaultLocaleForDomain}`)) { return navigateTo(route.path.replace(`/${defaultLocaleForDomain}`, "")); } if (!route.path.startsWith(`/${locale}`) && locale !== defaultLocaleForDomain) { const oldLocale = nuxt._vueI18n.__localeFromRoute(route.path); if (oldLocale !== "") { return navigateTo(`/${locale + route.path.replace(`/${oldLocale}`, "")}`); } return navigateTo(`/${locale + (route.path === "/" ? "" : route.path)}`); } if (redirectPath && route.path !== redirectPath) { return navigateTo(redirectPath); } return; } if (differentDomains) { const state = useRedirectState(); __DEBUG__ && logger.log("redirect", { state: state.value, redirectPath }); if (state.value && state.value !== redirectPath) { if (import.meta.client) { state.value = ""; window.location.assign(redirectPath); } if (import.meta.server) { __DEBUG__ && logger.log("differentDomains servermode", { redirectPath }); state.value = redirectPath; } } } else if (redirectPath) { return navigateTo(redirectPath); } } export function prefixable(currentLocale, defaultLocale, strategy) { return ( // strategy has no prefixes strategy !== "no_prefix" && // strategy should not prefix default locale !(currentLocale === defaultLocale && (strategy === "prefix_and_default" || strategy === "prefix_except_default")) ); } export function extendBaseUrl(ctx) { const logger = /* @__PURE__ */ createLogger("extendBaseUrl"); const { baseUrl, defaultLocale, differentDomains } = ctx.$config.public.i18n; if (isFunction(baseUrl)) { return () => { const baseUrlResult = baseUrl(ctx); __DEBUG__ && logger.log("using localeLoader function -", { baseUrlResult }); return baseUrlResult; }; } const localeCode = isFunction(defaultLocale) ? defaultLocale() : defaultLocale; return () => { if (differentDomains && localeCode) { const domain = getDomainFromLocale(localeCode); if (domain) { __DEBUG__ && logger.log("using differentDomains -", { domain }); return domain; } } __DEBUG__ && logger.log("using runtimeConfig -", { baseUrl }); return baseUrl ?? ""; }; } function uniqueKeys(...objects) { const keySet = /* @__PURE__ */ new Set(); for (const obj of objects) { for (const key of Object.keys(obj)) { keySet.add(key); } } return Array.from(keySet); } export function createNuxtI18nDev() { const nuxtApp = useNuxtApp(); const composer = getComposer(nuxtApp._vueI18n); async function resetI18nProperties(locale) { const opts = await loadVueI18nOptions(vueI18nConfigs, nuxtApp); const messageLocales = uniqueKeys(opts.messages || {}, composer.messages.value); for (const k of messageLocales) { if (locale && k !== locale) continue; const current = opts.messages?.[k] || {}; await loadAndSetLocaleMessages(k, localeLoaders, { [k]: current }, nuxtApp); composer.setLocaleMessage(k, current); } if (locale != null) return; const numberFormatLocales = uniqueKeys(opts.numberFormats || {}, composer.numberFormats.value); for (const k of numberFormatLocales) { composer.setNumberFormat(k, opts.numberFormats?.[k] || {}); } const datetimeFormatsLocales = uniqueKeys(opts.datetimeFormats || {}, composer.datetimeFormats.value); for (const k of datetimeFormatsLocales) { composer.setDateTimeFormat(k, opts.datetimeFormats?.[k] || {}); } } return { resetI18nProperties }; } export function toArray(value) { return isArray(value) ? value : [value]; }