diff --git a/src/runtime/internal.ts b/src/runtime/internal.ts index 1a9b71b26..cd386c2d3 100644 --- a/src/runtime/internal.ts +++ b/src/runtime/internal.ts @@ -3,16 +3,23 @@ import { isArray, isString, isObject } from '@intlify/shared' import { hasProtocol } from 'ufo' import isHTTPS from 'is-https' -import { useRequestHeaders, useRequestEvent, useCookie as useNuxtCookie, useRuntimeConfig, useNuxtApp } from '#imports' +import { + useRequestHeaders, + useRequestEvent, + useCookie as useNuxtCookie, + useRuntimeConfig, + useNuxtApp, + useRouter +} from '#imports' import { NUXT_I18N_MODULE_ID, DEFAULT_COOKIE_KEY, isSSG, localeCodes, normalizedLocales } from '#build/i18n.options.mjs' -import { findBrowserLocale, getLocalesRegex } from './routing/utils' +import { findBrowserLocale, getLocalesRegex, getRouteName } from './routing/utils' import { initCommonComposableOptions, type CommonComposableOptions } from './utils' import { createLogger } from 'virtual:nuxt-i18n-logger' import type { Locale } from 'vue-i18n' import type { DetectBrowserLanguageOptions, LocaleObject } from '#build/i18n.options.mjs' import type { RouteLocationNormalized, RouteLocationNormalizedLoaded } from 'vue-router' -import type { CookieRef } from 'nuxt/app' +import type { CookieRef, NuxtApp } from 'nuxt/app' import type { ModulePublicRuntimeConfig } from '../module' export function formatMessage(message: string) { @@ -374,4 +381,66 @@ export const runtimeDetectBrowserLanguage = ( return opts?.detectBrowserLanguage } +/** + * Removes default routes depending on domain + */ +export function setupMultiDomainLocales(nuxtContext: NuxtApp, defaultLocaleDomain: string) { + const { multiDomainLocales, strategy, routesNameSeparator, defaultLocaleRouteNameSuffix } = + nuxtContext.$config.public.i18n + + // feature disabled + if (!multiDomainLocales) return + + // incompatible strategy + if (!(strategy === 'prefix_except_default' || strategy === 'prefix_and_default')) return + + const router = useRouter() + const defaultRouteSuffix = [routesNameSeparator, defaultLocaleRouteNameSuffix].join('') + + // remove or rename default routes if not applicable for domain + for (const route of router.getRoutes()) { + const routeName = getRouteName(route.name) + + if (!routeName.includes(defaultRouteSuffix)) continue + + const routeNameLocale = routeName.split(routesNameSeparator)[1] + if (routeNameLocale === defaultLocaleDomain) { + route.name = routeName.replace(defaultRouteSuffix, '') + continue + } + + // use `route.name` directly as `routeName` stringifies `Symbol` + // @ts-expect-error type mismatch + router.removeRoute(route.name) + } +} + +/** + * Returns default locale for the current domain, returns `defaultLocale` by default + */ +export function getDefaultLocaleForDomain(nuxtContext: NuxtApp) { + const { locales, defaultLocale, multiDomainLocales } = nuxtContext.$config.public.i18n + + let defaultLocaleDomain: string = defaultLocale || '' + + if (!multiDomainLocales) { + return defaultLocaleDomain + } + + const host = getHost() + const hasDefaultForDomains = locales.some( + (l): l is LocaleObject => typeof l !== 'string' && Array.isArray(l.defaultForDomains) + ) + + if (hasDefaultForDomains) { + const findDefaultLocale = locales.find((l): l is LocaleObject => + typeof l === 'string' || !Array.isArray(l.defaultForDomains) ? false : l.defaultForDomains.includes(host ?? '') + ) + + defaultLocaleDomain = findDefaultLocale?.code ?? '' + } + + return defaultLocaleDomain +} + /* eslint-enable @typescript-eslint/no-explicit-any */ diff --git a/src/runtime/plugins/i18n.ts b/src/runtime/plugins/i18n.ts index 71663dc2b..de2d43ded 100644 --- a/src/runtime/plugins/i18n.ts +++ b/src/runtime/plugins/i18n.ts @@ -1,13 +1,6 @@ import { computed, ref, watch } from 'vue' import { createI18n } from 'vue-i18n' -import { - defineNuxtPlugin, - useRoute, - addRouteMiddleware, - defineNuxtRouteMiddleware, - useNuxtApp, - useRouter -} from '#imports' +import { defineNuxtPlugin, useRoute, addRouteMiddleware, defineNuxtRouteMiddleware, useNuxtApp } from '#imports' import { localeCodes, vueI18nConfigs, @@ -26,7 +19,8 @@ import { detectBrowserLanguage, getI18nCookie, runtimeDetectBrowserLanguage, - getHost + getDefaultLocaleForDomain, + setupMultiDomainLocales } from '../internal' import { inBrowser, resolveBaseUrl } from '../routing/utils' import { extendI18n, createLocaleFromRouteGetter } from '../routing/extends' @@ -58,40 +52,9 @@ export default defineNuxtPlugin({ const route = useRoute() const { vueApp: app } = nuxt const nuxtContext = nuxt as unknown as NuxtApp - const host = getHost() - const { locales, defaultLocale, multiDomainLocales, strategy } = nuxtContext.$config.public.i18n - - const hasDefaultForDomains = locales.some( - (l): l is LocaleObject => typeof l !== 'string' && Array.isArray(l.defaultForDomains) - ) - - let defaultLocaleDomain: string - if (defaultLocale) { - defaultLocaleDomain = defaultLocale - } else if (hasDefaultForDomains) { - const findDefaultLocale = locales.find((l): l is LocaleObject => - 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(route => { - if (route.name?.toString().includes('___default')) { - const routeNameLocale = route.name.toString().split('___')[1] - if (routeNameLocale !== defaultLocaleDomain) { - router.removeRoute(route.name) - } else { - const newRouteName = route.name.toString().replace('___default', '') - route.name = newRouteName - } - } - }) - } + const defaultLocaleDomain = getDefaultLocaleForDomain(nuxtContext) + setupMultiDomainLocales(nuxtContext, defaultLocaleDomain) // Fresh copy per request to prevent reusing mutated options const runtimeI18n = { ...nuxtContext.$config.public.i18n, defaultLocale: defaultLocaleDomain }