diff --git a/docs/content/docs/3.options/2.routing.md b/docs/content/docs/3.options/2.routing.md index 7d5a89d17..66d86ac47 100644 --- a/docs/content/docs/3.options/2.routing.md +++ b/docs/content/docs/3.options/2.routing.md @@ -112,6 +112,14 @@ Internal suffix added to generated route names for default locale, if strategy i Internal separator used for generated route names for each locale. You shouldn't need to change this. +## `routesNameSuffix` + +- type: `string` +- default: `'locale'` + +Internal suffix used for generated route names for each locale. You shouldn't need to change this. + + ## `rootRedirect` - type: `string` or `object` or `null` diff --git a/specs/basic_usage.spec.ts b/specs/basic_usage.spec.ts index 16f169dfc..8118c07ed 100644 --- a/specs/basic_usage.spec.ts +++ b/specs/basic_usage.spec.ts @@ -108,8 +108,10 @@ describe('basic usage', async () => { "leaveGuards": { "Set(0)": [], }, - "meta": {}, - "name": "nuxt-context-extension___en", + "meta": { + "locale": true, + }, + "name": "nuxt-context-extension", "path": "/nuxt-context-extension", "props": { "default": false, @@ -119,8 +121,10 @@ describe('basic usage', async () => { }, }, ], - "meta": {}, - "name": "nuxt-context-extension___en", + "meta": { + "locale": true, + }, + "name": "nuxt-context-extension", "params": {}, "path": "/nuxt-context-extension", "query": {}, diff --git a/specs/different_domains/different_domains_multi_locales_prefix_except_default.spec.ts b/specs/different_domains/different_domains_multi_locales_prefix_except_default.spec.ts index ef5571eeb..27823d283 100644 --- a/specs/different_domains/different_domains_multi_locales_prefix_except_default.spec.ts +++ b/specs/different_domains/different_domains_multi_locales_prefix_except_default.spec.ts @@ -88,7 +88,7 @@ test('pass ` to props', async () => { `http://nuxt-app.localhost` ) expect(dom.querySelector('#switch-locale-path-usages .switch-to-no a').getAttribute('href')).toEqual( - `http://nuxt-app.localhost/no` + `http://nuxt-app.localhost` // fix undefined locale ) expect(dom.querySelector('#switch-locale-path-usages .switch-to-fr a').getAttribute('href')).toEqual( `http://fr.nuxt-app.localhost` diff --git a/specs/fixtures/routing/nuxt.config.ts b/specs/fixtures/routing/nuxt.config.ts index b19c0e5e3..df1d230a7 100644 --- a/specs/fixtures/routing/nuxt.config.ts +++ b/specs/fixtures/routing/nuxt.config.ts @@ -1,3 +1,5 @@ +import { STRATEGIES } from '../../../src/constants' + export default defineNuxtConfig({ // devtools: { enabled: true }, modules: ['@nuxtjs/i18n'], diff --git a/specs/routing/routing-tests.ts b/specs/routing/routing-tests.ts index bbf701956..80ffdbcfc 100644 --- a/specs/routing/routing-tests.ts +++ b/specs/routing/routing-tests.ts @@ -43,9 +43,11 @@ export async function localePathTests(strategy: Strategies) { expect(await getText(page, '#locale-path .query-foo-string')).toEqual(prefixPath('?foo=1')) expect(await getText(page, '#locale-path .query-foo-string-about')).toEqual(prefixPath('/about?foo=1')) expect(await getText(page, '#locale-path .query-foo-test-string')).toEqual(prefixPath('/about?foo=1&test=2')) - if (strategy === 'no_prefix') { - // TODO: fix localePath escapes paths for `no_prefix` strategy + + if (strategy === 'no_prefix' || strategy === 'prefix_except_default' || strategy === 'prefix_and_default') { + // TODO: fix localePath escapes paths for `no_prefix` and `prefix_except_default` and `prefix_and_default` strategy // unexpectedly resolves to /path/as%20a%20test?foo=bar+sentence + // problem connected with router.resolve expect(await getText(page, '#locale-path .query-foo-path-param')).not.toEqual( prefixPath('/path/as a test?foo=bar+sentence') ) @@ -54,6 +56,7 @@ export async function localePathTests(strategy: Strategies) { prefixPath('/path/as a test?foo=bar+sentence') ) } + expect(await getText(page, '#locale-path .query-foo-path-param-escaped')).toEqual( prefixPath('/path/as%20a%20test?foo=bar+sentence') ) @@ -135,42 +138,42 @@ export async function localeLocationTests() { expect(JSON.parse(await getText(page, '#locale-location .index'))).include({ fullPath: '/en', path: '/en', - name: 'index___en', + name: 'index___locale', href: '/en' }) expect(JSON.parse(await getText(page, '#locale-location .index-name-ja'))).include({ fullPath: '/ja', path: '/ja', - name: 'index___ja', + name: 'index___locale', href: '/ja' }) expect(JSON.parse(await getText(page, '#locale-location .about-name'))).include({ fullPath: '/en/about', path: '/en/about', - name: 'about___en', + name: 'about___locale', href: '/en/about' }) expect(JSON.parse(await getText(page, '#locale-location .about-ja'))).include({ fullPath: '/ja/about', path: '/ja/about', - name: 'about___ja', + name: 'about___locale', href: '/ja/about' }) expect(JSON.parse(await getText(page, '#locale-location .about-name-ja'))).include({ fullPath: '/ja/about', path: '/ja/about', - name: 'about___ja', + name: 'about___locale', href: '/ja/about' }) expect(JSON.parse(await getText(page, '#locale-location .path-match-ja'))).include({ fullPath: '/ja/:pathMatch(.*)*', path: '/ja/:pathMatch(.*)*', - name: 'pathMatch___ja', + name: 'pathMatch___locale', href: '/ja/:pathMatch(.*)*' }) @@ -178,14 +181,14 @@ export async function localeLocationTests() { expect(JSON.parse(await getText(page, '#locale-location .path-match-name'))).include({ fullPath: '/en', path: '/en', - name: 'pathMatch___en', + name: 'pathMatch___locale', href: '/en' }) expect(JSON.parse(await getText(page, '#locale-location .path-match-name-ja'))).include({ fullPath: '/ja', path: '/ja', - name: 'pathMatch___ja', + name: 'pathMatch___locale', href: '/ja' }) @@ -193,7 +196,7 @@ export async function localeLocationTests() { expect(JSON.parse(await getText(page, '#locale-location .about-object-ja'))).include({ fullPath: '/ja/about', path: '/ja/about', - name: 'about___ja', + name: 'about___locale', href: '/ja/about' }) @@ -201,7 +204,7 @@ export async function localeLocationTests() { expect(JSON.parse(await getText(page, '#locale-location .undefined-path-ja'))).include({ fullPath: '/ja/vue-i18n', path: '/ja/vue-i18n', - name: 'pathMatch___ja', + name: 'pathMatch___locale', href: '/ja/vue-i18n' }) @@ -215,42 +218,42 @@ export async function localeRouteTests() { expect(JSON.parse(await getText(page, '#locale-route .index'))).include({ fullPath: '/en', path: '/en', - name: 'index___en', + name: 'index___locale', href: '/en' }) expect(JSON.parse(await getText(page, '#locale-route .index-name-ja'))).include({ fullPath: '/ja', path: '/ja', - name: 'index___ja', + name: 'index___locale', href: '/ja' }) expect(JSON.parse(await getText(page, '#locale-route .about-name'))).include({ fullPath: '/en/about', path: '/en/about', - name: 'about___en', + name: 'about___locale', href: '/en/about' }) expect(JSON.parse(await getText(page, '#locale-route .about-ja'))).include({ fullPath: '/ja/about', path: '/ja/about', - name: 'about___ja', + name: 'about___locale', href: '/ja/about' }) expect(JSON.parse(await getText(page, '#locale-route .about-name-ja'))).include({ fullPath: '/ja/about', path: '/ja/about', - name: 'about___ja', + name: 'about___locale', href: '/ja/about' }) expect(JSON.parse(await getText(page, '#locale-route .path-match-ja'))).include({ fullPath: '/ja/:pathMatch(.*)*', path: '/ja/:pathMatch(.*)*', - name: 'pathMatch___ja', + name: 'pathMatch___locale', href: '/ja/:pathMatch(.*)*' }) @@ -258,14 +261,14 @@ export async function localeRouteTests() { expect(JSON.parse(await getText(page, '#locale-route .path-match-name'))).include({ fullPath: '/en', path: '/en', - name: 'pathMatch___en', + name: 'pathMatch___locale', href: '/en' }) expect(JSON.parse(await getText(page, '#locale-route .path-match-name-ja'))).include({ fullPath: '/ja', path: '/ja', - name: 'pathMatch___ja', + name: 'pathMatch___locale', href: '/ja' }) @@ -273,7 +276,7 @@ export async function localeRouteTests() { expect(JSON.parse(await getText(page, '#locale-route .about-object-ja'))).include({ fullPath: '/ja/about', path: '/ja/about', - name: 'about___ja', + name: 'about___locale', href: '/ja/about' }) @@ -281,7 +284,7 @@ export async function localeRouteTests() { expect(JSON.parse(await getText(page, '#locale-route .undefined-path-ja'))).include({ fullPath: '/ja/vue-i18n', path: '/ja/vue-i18n', - name: 'pathMatch___ja', + name: 'pathMatch___locale', href: '/ja/vue-i18n' }) diff --git a/src/constants.ts b/src/constants.ts index 7434f02de..b6855b84a 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -52,6 +52,7 @@ export const DEFAULT_OPTIONS = { defaultLocale: '', defaultDirection: 'ltr', routesNameSeparator: '___', + routesNameSuffix: 'locale', trailingSlash: false, defaultLocaleRouteNameSuffix: 'default', strategy: STRATEGY_PREFIX_EXCEPT_DEFAULT, diff --git a/src/module.ts b/src/module.ts index 14523c25c..d53ef793c 100644 --- a/src/module.ts +++ b/src/module.ts @@ -41,7 +41,7 @@ import { applyLayerOptions, checkLayerOptions, resolveLayerVueI18nConfigInfo } f import { generateTemplateNuxtI18nOptions } from './template' import type { HookResult } from '@nuxt/schema' -import type { NuxtI18nOptions } from './types' +import type { CustomRoutePages, NuxtI18nOptions } from './types' export * from './types' @@ -148,11 +148,13 @@ export default defineNuxtModule({ lazy: options.lazy, rootRedirect: options.rootRedirect, routesNameSeparator: options.routesNameSeparator, + routesNameSuffix: options.routesNameSuffix, defaultLocaleRouteNameSuffix: options.defaultLocaleRouteNameSuffix, skipSettingLocaleOnNavigate: options.skipSettingLocaleOnNavigate, differentDomains: options.differentDomains, trailingSlash: options.trailingSlash, configLocales: options.locales, + customPages: options.pages, locales: options.locales.reduce( (obj, locale) => { if (typeof locale === 'string') { @@ -440,6 +442,12 @@ export interface ModulePublicRuntimeConfig { * @internal */ routesNameSeparator: Required['routesNameSeparator'] + /** + * Overwritten at build time, used to pass generated options to runtime + * + * @internal + */ + routesNameSuffix: Required['routesNameSuffix'] /** * Overwritten at build time, used to pass generated options to runtime * @@ -452,6 +460,12 @@ export interface ModulePublicRuntimeConfig { * @internal */ trailingSlash: Required['trailingSlash'] + /** + * Overwritten at build time, used to pass generated options to runtime + * + * @internal + */ + customPages: CustomRoutePages } } export interface ModuleHooks { diff --git a/src/routing.ts b/src/routing.ts index a67f0370b..b804ffa93 100644 --- a/src/routing.ts +++ b/src/routing.ts @@ -6,8 +6,6 @@ import type { NuxtPage } from '@nuxt/schema' import type { MarkRequired, MarkOptional } from 'ts-essentials' import type { NuxtI18nOptions, PrefixLocalizedRouteOptions, RouteOptionsResolver } from './types' -const join = (...args: (string | undefined)[]) => args.filter(Boolean).join('') - /** * Options to compute route localizing * @@ -22,11 +20,11 @@ export declare interface ComputedRouteOptions { } export function prefixLocalizedRoute( + isDefaultLocale: boolean, localizeOptions: PrefixLocalizedRouteOptions, options: LocalizeRoutesParams, extra = false ): boolean { - const isDefaultLocale = localizeOptions.locale === (localizeOptions.defaultLocale ?? '') const isChildWithRelativePath = localizeOptions.parent != null && !localizeOptions.path.startsWith('/') // no need to add prefix if child's path is relative @@ -45,7 +43,7 @@ function adjustRoutePathForTrailingSlash(localized: LocalizedRoute, trailingSlas export type LocalizeRoutesParams = MarkRequired< NuxtI18nOptions, - 'strategy' | 'locales' | 'routesNameSeparator' | 'trailingSlash' | 'defaultLocaleRouteNameSuffix' + 'strategy' | 'locales' | 'routesNameSeparator' | 'routesNameSuffix' | 'trailingSlash' | 'defaultLocaleRouteNameSuffix' > & { includeUnprefixedFallback?: boolean optionsResolver?: RouteOptionsResolver @@ -86,7 +84,7 @@ export function localizeRoutes(routes: NuxtPage[], options: LocalizeRoutesParams return routes } - let defaultLocales = [options.defaultLocale ?? ''] + let defaultLocales = options.defaultLocale ? [options.defaultLocale] : [] if (options.differentDomains) { const domainDefaults = options.locales .filter(locale => (isObject(locale) ? locale.domainDefault : false)) @@ -117,54 +115,111 @@ export function localizeRoutes(routes: NuxtPage[], options: LocalizeRoutesParams ...routeOptions } + route.meta = { ...route.meta, ...{ locale: true } } + const localizedRoutes: (LocalizedRoute | NuxtPage)[] = [] - for (const locale of componentOptions.locales) { - const localized: LocalizedRoute = { ...route, locale, parent } - const isDefaultLocale = defaultLocales.includes(locale) - const addDefaultTree = isDefaultLocale && options.strategy === 'prefix_and_default' && parent == null && !extra + const defaultLocale = defaultLocales[0] - // localize route again for strategy `prefix_and_default` - if (addDefaultTree && parent == null && !extra) { - localizedRoutes.push(...localizeRoute(route, { locales: [locale], extra: true })) - } + let nonDefaultLocales = componentOptions.locales - const nameSegments = [localized.name, options.routesNameSeparator, locale] - if (extra) { - nameSegments.push(options.routesNameSeparator, options.defaultLocaleRouteNameSuffix) - } + if (options.strategy !== 'prefix' && options.strategy !== 'prefix_and_default') { + nonDefaultLocales = componentOptions.locales.filter(l => l !== defaultLocale) + } - // localize name if set - localized.name &&= join(...nameSegments) + let localeRegex = nonDefaultLocales.join('|') - // use custom path if found - localized.path = componentOptions.paths?.[locale] ?? localized.path + // Adding a route for the default locale + if ((options.strategy !== 'prefix' || options.includeUnprefixedFallback) && !parentLocalized) { + const defaultLocalized: LocalizedRoute = { ...route, locale: defaultLocale, parent } + defaultLocalized.meta = { ...defaultLocalized.meta, ...{ locale: true } } + localizedRoutes.push(defaultLocalized) + } - const localePrefixable = prefixLocalizedRoute( - { defaultLocale: isDefaultLocale ? locale : options.defaultLocale, ...localized }, - options, - extra - ) - if (localePrefixable) { - localized.path = join('/', locale, localized.path) + let detectBrowserLanguage = false + if (options.detectBrowserLanguage !== false) { + detectBrowserLanguage = !!options.detectBrowserLanguage?.useCookie + } - if (isDefaultLocale && options.strategy === 'prefix' && options.includeUnprefixedFallback) { - localizedRoutes.push({ ...route, locale, parent }) - } + if (options.strategy === 'prefix' && !parentLocalized && !detectBrowserLanguage) { + const redirectLocalized: LocalizedRoute = { + ...route, + locale: `/`, + name: 'index', + redirect: `/${defaultLocale}`, + meta: { ...route.meta, ...{ locale: true } }, + parent } + localizedRoutes.push(redirectLocalized) + } - localized.path &&= adjustRoutePathForTrailingSlash(localized, options.trailingSlash) + // Adding a combined route for all non-default locales + const combinedLocalized: LocalizedRoute = { ...route, locale: `/:locale(${localeRegex})`, parent } + let routePath = combinedLocalized.path + if (parentLocalized != null && parentLocalized.path.startsWith('/:locale')) { + routePath = routePath.replace(`${parentLocalized.path}/`, '') + } + if (!routePath.startsWith('/')) { + routePath = `/${routePath}` + } - // remove parent path from child route - if (parentLocalized != null) { - localized.path = localized.path.replace(parentLocalized.path + '/', '') - } + if (extra && parentLocalized != null) { + combinedLocalized.path = parentLocalized.path + routePath.replace(/\/:locale\(.+\)/, '') + } else if (parentLocalized != null) { + combinedLocalized.path = `${parentLocalized.path}${routePath}` + } else { + combinedLocalized.path = `/:locale(${localeRegex})${routePath}` + } - // localize child routes if set - localized.children &&= localized.children.flatMap(child => - localizeRoute(child, { locales: [locale], parent: route, parentLocalized: localized, extra }) + if (combinedLocalized.name) { + combinedLocalized.name = combinedLocalized.name.replace( + `${options.routesNameSeparator}${options.routesNameSuffix}`, + '' ) + combinedLocalized.name += `${options.routesNameSeparator}${options.routesNameSuffix}` + } + combinedLocalized.meta = { ...combinedLocalized.meta, ...{ locale: true } } + + combinedLocalized.path &&= adjustRoutePathForTrailingSlash(combinedLocalized, options.trailingSlash) + + combinedLocalized.children &&= combinedLocalized.children.flatMap(child => + localizeRoute(child, { locales: [...nonDefaultLocales], parent: route, parentLocalized: combinedLocalized }) + ) + + for (const locale of componentOptions.locales) { + if (componentOptions.paths?.[locale] !== undefined) { + if (!componentOptions.paths?.[locale]) { + nonDefaultLocales = nonDefaultLocales.filter(l => l !== locale) + localeRegex = nonDefaultLocales.join('|') + if (!parentLocalized) { + combinedLocalized.path = `/:locale(${localeRegex})${routePath}` + } + continue + } + const subLocalized: LocalizedRoute = { ...route, locale: locale, parent } + + let prefix = `/:locale(${locale})` + if (options.strategy !== 'prefix' && locale === defaultLocale) { + prefix = '' + subLocalized.name = `${route.name}${options.routesNameSeparator}${locale}` + } else { + subLocalized.name = `${route.name}${options.routesNameSeparator}${options.routesNameSuffix}${options.routesNameSeparator}${locale}` + } + subLocalized.path = `${prefix}${componentOptions.paths[locale]}` + subLocalized.meta = { ...subLocalized.meta, ...{ locale: true } } + + if (combinedLocalized.children) { + const localizedChildren = combinedLocalized.children.flatMap(child => + localizeRoute(child, { locales: [locale], parent: route, parentLocalized: subLocalized, extra: true }) + ) + localizedRoutes.push(...localizedChildren) + } + + localizedRoutes.push(subLocalized) + } + } - localizedRoutes.push(localized) + if (nonDefaultLocales.length) { + localizedRoutes.push(combinedLocalized) } // remove properties used for localization process diff --git a/src/runtime/internal.ts b/src/runtime/internal.ts index 220091a5c..83d2f5856 100644 --- a/src/runtime/internal.ts +++ b/src/runtime/internal.ts @@ -319,11 +319,15 @@ export function detectBrowserLanguage( return { locale: '', stat: false, reason: 'not_found_match' } } -export function getHost() { - let host: string | undefined +export function getHostName() { + let hostName: string | undefined + + // Check if the code is running on the client-side if (import.meta.client) { - host = window.location.host - } else if (import.meta.server) { + hostName = window.location.hostname // Get the hostname without the port + } + // Check if the code is running on the server-side + else if (import.meta.server) { const header = useRequestHeaders(['x-forwarded-host', 'host']) let detectedHost: string | undefined @@ -333,9 +337,14 @@ export function getHost() { detectedHost = header['host'] } - host = isArray(detectedHost) ? detectedHost[0] : detectedHost + // If the value is an array, take the first element, otherwise use as is + detectedHost = isArray(detectedHost) ? detectedHost[0] : detectedHost + + // If detectedHost includes a port, extract only the part before the colon + hostName = detectedHost ? detectedHost.split(':')[0] : undefined } - return host + + return hostName } export function getLocaleDomain( @@ -343,7 +352,7 @@ export function getLocaleDomain( strategy: string, route: string | RouteLocationNormalized | RouteLocationNormalizedLoaded ): string { - let host = getHost() || '' + let host = getHostName() || '' if (host) { __DEBUG__ && console.log( diff --git a/src/runtime/plugins/i18n.ts b/src/runtime/plugins/i18n.ts index 7e28f921a..2f1652f16 100644 --- a/src/runtime/plugins/i18n.ts +++ b/src/runtime/plugins/i18n.ts @@ -68,7 +68,7 @@ export default defineNuxtPlugin({ vueI18nOptions.messages = vueI18nOptions.messages || {} vueI18nOptions.fallbackLocale = vueI18nOptions.fallbackLocale ?? false - const getLocaleFromRoute = createLocaleFromRouteGetter() + const getLocaleFromRoute = createLocaleFromRouteGetter(runtimeI18n.defaultLocale, runtimeI18n.strategy) const getDefaultLocale = (defaultLocale: string) => defaultLocale || vueI18nOptions.locale || 'en-US' const localeCookie = getI18nCookie() @@ -172,13 +172,29 @@ export default defineNuxtPlugin({ notInitialSetup = false } + // don't use routeToObject. it's make memory leak + const subRoute = { + name: route.name, + params: route.params, + meta: route.meta, + matched: route.matched, + path: route.path, + fullPath: route.fullPath, + hash: route.hash, + query: route.query, + redirectedFrom: route.redirectedFrom + } + + const oldLocale = getLocaleFromRoute(subRoute) + const redirectPath = await nuxtContext.runWithContext(() => detectRedirect({ - route: { to: route }, + route: { to: subRoute }, targetLocale: locale, - routeLocaleGetter: getLocaleFromRoute + oldLocale: oldLocale }) ) + __DEBUG__ && console.log('redirectPath on setLocale', redirectPath) await nuxtContext.runWithContext( @@ -456,11 +472,26 @@ export default defineNuxtPlugin({ notInitialSetup = false } + // don't use routeToObject. is's make memory leak + const subRoute = { + name: to.name, + params: to.params, + meta: to.meta, + matched: to.matched, + path: to.path, + fullPath: to.fullPath, + hash: to.hash, + query: to.query, + redirectedFrom: to.redirectedFrom + } + + const oldLocale = getLocaleFromRoute(subRoute) + const redirectPath = await nuxtContext.runWithContext(() => detectRedirect({ - route: { to, from }, + route: { to: subRoute, from }, targetLocale: locale, - routeLocaleGetter: runtimeI18n.strategy === 'no_prefix' ? () => locale : getLocaleFromRoute, + oldLocale: runtimeI18n.strategy === 'no_prefix' ? locale : oldLocale, calledWithRouting: true }) ) diff --git a/src/runtime/routing/compatibles/routing.ts b/src/runtime/routing/compatibles/routing.ts index ff0dda506..b20b8affe 100644 --- a/src/runtime/routing/compatibles/routing.ts +++ b/src/runtime/routing/compatibles/routing.ts @@ -1,14 +1,19 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { isString, assign } from '@intlify/shared' import { hasProtocol, parsePath, parseQuery, withTrailingSlash, withoutTrailingSlash } from 'ufo' -import { DEFAULT_DYNAMIC_PARAMS_KEY } from '#build/i18n.options.mjs' +import { DEFAULT_DYNAMIC_PARAMS_KEY, normalizedLocales } from '#build/i18n.options.mjs' import { unref } from '#imports' import { resolve, routeToObject } from './utils' import { getLocale, getLocaleRouteName, getRouteName } from '../utils' import { extendPrefixable, extendSwitchLocalePathIntercepter, type CommonComposableOptions } from '../../utils' -import type { Strategies, PrefixableOptions, SwitchLocalePathIntercepter } from '#build/i18n.options.mjs' +import type { + Strategies, + PrefixableOptions, + SwitchLocalePathIntercepter, + CustomRoutePages +} from '#build/i18n.options.mjs' import type { Locale } from 'vue-i18n' import type { RouteLocation, @@ -17,7 +22,8 @@ import type { RouteLocationPathRaw, RouteLocationNamedRaw, RouteLocationNormalizedLoaded, - RouteLocationNormalized + RouteLocationNormalized, + LocationQuery } from 'vue-router' const RESOLVED_PREFIXED = new Set(['prefix_and_default', 'prefix_except_default']) @@ -41,6 +47,7 @@ export const DefaultPrefixable = prefixable * @remarks * Base name is name of the route without locale suffix and other metadata added by nuxt i18n module + * @param common * @param givenRoute - A route. * * @returns The route base name. if cannot get, `undefined` is returned. @@ -63,6 +70,7 @@ export function getRouteBaseName(common: CommonComposableOptions, givenRoute?: R * @remarks * If locale is not specified, uses current locale. * + * @param common * @param route - A route. * @param locale - A locale, optional. * @@ -131,8 +139,21 @@ export function localeLocation( export function resolveRoute(common: CommonComposableOptions, route: RouteLocationRaw, locale: Locale | undefined) { const { router, i18n } = common const _locale = locale || getLocale(i18n) - const { routesNameSeparator, defaultLocale, defaultLocaleRouteNameSuffix, strategy, trailingSlash } = + if (!_locale || _locale === 'undefined') { + return null + } + const { strategy, routesNameSeparator, routesNameSuffix, trailingSlash, customPages } = common.runtimeConfig.public.i18n + let { defaultLocale } = common.runtimeConfig.public.i18n + + const lang = [...normalizedLocales].find(locale => locale.code === _locale) + if (lang?.domain) { + const defaultCode = lang?.code + if (defaultCode && !!locale && strategy !== 'prefix') { + defaultLocale = defaultCode.toString() + } + } + const prefixable = extendPrefixable(common.runtimeConfig) // if route parameter is a string, check if it's a path or name of route. let _route: RouteLocationPathRaw | RouteLocationNamedRaw @@ -150,30 +171,39 @@ export function resolveRoute(common: CommonComposableOptions, route: RouteLocati _route = route } - let localizedRoute = assign({} as RouteLocationPathRaw | RouteLocationNamedRaw, _route) + let localizedRoute = assign( + {} as (RouteLocationPathRaw & { params: any; name?: string }) | RouteLocationNamedRaw, + _route + ) const isRouteLocationPathRaw = (val: RouteLocationPathRaw | RouteLocationNamedRaw): val is RouteLocationPathRaw => 'path' in val && !!val.path && !('name' in val) if (isRouteLocationPathRaw(localizedRoute)) { const resolvedRoute = resolve(common, localizedRoute, strategy, _locale) - // @ts-ignore const resolvedRouteName = getRouteBaseName(common, resolvedRoute) if (isString(resolvedRouteName)) { localizedRoute = { - name: getLocaleRouteName(resolvedRouteName, _locale, { + name: getLocaleRouteName( + resolvedRouteName, + _locale, defaultLocale, - strategy, routesNameSeparator, - defaultLocaleRouteNameSuffix - }), + routesNameSuffix, + strategy + ), // @ts-ignore - params: resolvedRoute.params, + params: { ...resolvedRoute.params }, query: resolvedRoute.query, hash: resolvedRoute.hash } as RouteLocationNamedRaw + if ((defaultLocale !== _locale && strategy !== 'no_prefix') || strategy === 'prefix') { + // @ts-ignore + localizedRoute.params = { ...localizedRoute.params, ...{ locale: _locale } } + } + // @ts-expect-error localizedRoute.state = (resolvedRoute as ResolveV4).state } else { @@ -185,22 +215,96 @@ export function resolveRoute(common: CommonComposableOptions, route: RouteLocati localizedRoute.path = trailingSlash ? withTrailingSlash(localizedRoute.path, true) : withoutTrailingSlash(localizedRoute.path, true) + + localizedRoute.params = { ...localizedRoute.params } + if ((defaultLocale !== _locale && strategy !== 'no_prefix') || strategy === 'prefix') { + // @ts-ignore + localizedRoute.params = { ...resolvedRoute.params, ...{ locale: _locale } } + } } } else { if (!localizedRoute.name && !('path' in localizedRoute)) { localizedRoute.name = getRouteBaseName(common, router.currentRoute.value) } - localizedRoute.name = getLocaleRouteName(localizedRoute.name, _locale, { + localizedRoute.name = getLocaleRouteName( + localizedRoute.name, + _locale, defaultLocale, - strategy, routesNameSeparator, - defaultLocaleRouteNameSuffix - }) + routesNameSuffix, + strategy + ) + + localizedRoute.params = { ...localizedRoute.params } + if ((defaultLocale !== _locale && strategy !== 'no_prefix') || strategy === 'prefix') { + localizedRoute.params = { ...localizedRoute.params, ...{ locale: _locale } } + } } try { - const resolvedRoute = router.resolve(localizedRoute) + if (localizedRoute.name) { + const routeName = localizedRoute.name.toString() + routesNameSeparator + _locale + const subLocalizedRoute = assign( + {} as (RouteLocationPathRaw & { params: any; name?: string }) | RouteLocationNamedRaw, + localizedRoute, + { + name: routeName + } + ) + + subLocalizedRoute.params = { ...subLocalizedRoute.params } + if ((defaultLocale !== _locale && strategy !== 'no_prefix') || strategy === 'prefix') { + subLocalizedRoute.params = { ...subLocalizedRoute.params, ...{ locale: _locale } } + } + + const resolvedRoute = router.resolve(subLocalizedRoute) + + if (resolvedRoute.name) { + return resolvedRoute + } + } + } catch (e) {} + + try { + let resolvedRoute = router.resolve(localizedRoute) + if (resolvedRoute.path) { + let checker = false + for (const i in resolvedRoute.matched) { + if ( + resolvedRoute.matched[i].meta && + resolvedRoute.matched[i].meta.locale && + checkLocale(resolvedRoute.path, resolvedRoute.matched[i].path) + ) { + checker = true + } + } + if (!checker && localizedRoute.name) { + localizedRoute.name = localizedRoute.name.toString().replace(`${routesNameSeparator}${routesNameSuffix}`, '') + resolvedRoute = router.resolve(localizedRoute) + } + + const parts = resolvedRoute.path.split('/') + const routePath = parts.slice(2).join('/') + + const result = findValueByPath([routePath, _locale], customPages) + if (result === null || result === false) { + return null + } + if (result && _locale) { + const localizedRoute = router.resolve({ path: '/' + _locale + result }) + // @ts-ignore + localizedRoute.params = { ..._route.params } + localizedRoute.query = (_route.query ?? {}) as LocationQuery + localizedRoute.hash = _route.hash ?? '' + + if ((defaultLocale !== _locale && strategy !== 'no_prefix') || strategy === 'prefix') { + localizedRoute.params = { ...localizedRoute.params, ...{ locale: _locale } } + } + + return router.resolve(localizedRoute) + } + } if (resolvedRoute.name) { return resolvedRoute } @@ -215,6 +319,30 @@ export function resolveRoute(common: CommonComposableOptions, route: RouteLocati } } +function checkLocale(path: string, pattern: string) { + const localePatternMatch = pattern.match(/:locale\(([^)]+)\)/) + if (!localePatternMatch) { + return true + } + + const allowedLocales = localePatternMatch[1].split('|') + + const localeRegex = new RegExp('^/' + allowedLocales.join('|') + '(?:/|$)') + + return localeRegex.test(path) +} + +function findValueByPath(pathArray: string[], pagesObject: CustomRoutePages): string | undefined | null | false { + let currentObject: any = pagesObject + for (const key of pathArray) { + if (currentObject[key] === undefined) { + return undefined + } + currentObject = currentObject[key] + } + return currentObject +} + export const DefaultSwitchLocalePathIntercepter: SwitchLocalePathIntercepter = (path: string) => path function getLocalizableMetaFromDynamicParams( @@ -255,6 +383,11 @@ export function switchLocalePath( const resolvedParams = getLocalizableMetaFromDynamicParams(common, route)[locale] const baseRoute = { ...routeCopy, name, params: { ...routeCopy.params, ...resolvedParams } } + + if (baseRoute.params) { + delete baseRoute.params.locale + } + const path = localePath(common, baseRoute, locale) // custom locale path with interceptor diff --git a/src/runtime/routing/extends/router.ts b/src/runtime/routing/extends/router.ts index 3f90a4b90..a6d823de1 100644 --- a/src/runtime/routing/extends/router.ts +++ b/src/runtime/routing/extends/router.ts @@ -1,15 +1,10 @@ import { isString, isObject } from '@intlify/shared' import { getLocalesRegex } from '../utils' -import { localeCodes } from '#build/i18n.options.mjs' +import { localeCodes, type Strategies } from '#build/i18n.options.mjs' import type { RouteLocationNormalized, RouteLocationNormalizedLoaded } from 'vue-router' -import { useRuntimeConfig } from 'nuxt/app' -export function createLocaleFromRouteGetter() { - const { routesNameSeparator, defaultLocaleRouteNameSuffix } = useRuntimeConfig().public.i18n - const localesPattern = `(${localeCodes.join('|')})` - const defaultSuffixPattern = `(?:${routesNameSeparator}${defaultLocaleRouteNameSuffix})?` - const regexpName = new RegExp(`${routesNameSeparator}${localesPattern}${defaultSuffixPattern}$`, 'i') +export function createLocaleFromRouteGetter(defaultLocale: string, strategy: Strategies) { const regexpPath = getLocalesRegex(localeCodes) /** @@ -20,12 +15,8 @@ export function createLocaleFromRouteGetter() { const getLocaleFromRoute = (route: RouteLocationNormalizedLoaded | RouteLocationNormalized | string): string => { // extract from route name if (isObject(route)) { - if (route.name) { - const name = isString(route.name) ? route.name : route.name.toString() - const matches = name.match(regexpName) - if (matches && matches.length > 1) { - return matches[1] - } + if (route.params.locale) { + return route.params.locale.toString() } else if (route.path) { // Extract from path const matches = route.path.match(regexpPath) @@ -33,6 +24,9 @@ export function createLocaleFromRouteGetter() { return matches[1] } } + if (route.name && route.meta && route.meta.locale && strategy !== 'prefix') { + return defaultLocale // #1888 + } } else if (isString(route)) { const matches = route.match(regexpPath) if (matches && matches.length > 1) { diff --git a/src/runtime/routing/utils.ts b/src/runtime/routing/utils.ts index 0eb6b1a12..2969c5a53 100644 --- a/src/runtime/routing/utils.ts +++ b/src/runtime/routing/utils.ts @@ -3,7 +3,7 @@ import { isString, isSymbol, isFunction } from '@intlify/shared' import { isRef, unref } from '#imports' -import type { LocaleObject, Strategies, BaseUrlResolveHandler } from '#build/i18n.options.mjs' +import type { LocaleObject, BaseUrlResolveHandler } from '#build/i18n.options.mjs' import type { Composer, I18n, Locale, VueI18n } from 'vue-i18n' export const inBrowser = typeof window !== 'undefined' @@ -97,16 +97,16 @@ export function getRouteName(routeName?: string | symbol | null) { export function getLocaleRouteName( routeName: symbol | string | null | undefined, locale: Locale, - { - defaultLocale, - strategy, - routesNameSeparator, - defaultLocaleRouteNameSuffix - }: { defaultLocale: string; strategy: Strategies; routesNameSeparator: string; defaultLocaleRouteNameSuffix: string } + defaultLocale: string, + routesNameSeparator: string, + routesNameSuffix: string, + strategy: string ) { - let name = getRouteName(routeName) + (strategy === 'no_prefix' ? '' : routesNameSeparator + locale) - if (locale === defaultLocale && strategy === 'prefix_and_default') { - name += routesNameSeparator + defaultLocaleRouteNameSuffix + let name = getRouteName(routeName) + .replace(`${routesNameSeparator}${locale}`, '') + .replace(`${routesNameSeparator}${routesNameSuffix}`, '') + if ((locale !== defaultLocale && strategy !== 'no_prefix') || strategy === 'prefix') { + name += `${routesNameSeparator}${routesNameSuffix}` } return name } diff --git a/src/runtime/utils.ts b/src/runtime/utils.ts index 8a1f84da7..35136b4ee 100644 --- a/src/runtime/utils.ts +++ b/src/runtime/utils.ts @@ -263,7 +263,7 @@ export function detectLocale( export function detectRedirect({ route, targetLocale, - routeLocaleGetter, + oldLocale, calledWithRouting = false }: { route: { @@ -271,7 +271,7 @@ export function detectRedirect({ from?: RouteLocationNormalized | RouteLocationNormalizedLoaded } targetLocale: Locale - routeLocaleGetter: ReturnType + oldLocale: string calledWithRouting?: boolean }): string { const nuxtApp = useNuxtApp() @@ -279,7 +279,7 @@ export function detectRedirect({ const { strategy, differentDomains } = common.runtimeConfig.public.i18n __DEBUG__ && console.log('detectRedirect: targetLocale -> ', targetLocale) __DEBUG__ && console.log('detectRedirect: route -> ', route) - __DEBUG__ && console.log('detectRedirect: calledWithRouting -> ', calledWithRouting, routeLocaleGetter(route.to)) + __DEBUG__ && console.log('detectRedirect: calledWithRouting -> ', calledWithRouting, oldLocale) let redirectPath = '' const { fullPath: toFullPath } = route.to @@ -296,7 +296,7 @@ export function detectRedirect({ !isStaticGenerate && !differentDomains && (calledWithRouting || strategy !== 'no_prefix') && - routeLocaleGetter(route.to) !== targetLocale + oldLocale !== targetLocale ) { // the current route could be 404 in which case attempt to find matching route using the full path const routePath = nuxtApp.$switchLocalePath(targetLocale) || nuxtApp.$localePath(toFullPath, targetLocale) @@ -311,7 +311,7 @@ export function detectRedirect({ } } - if ((differentDomains || (isSSG && import.meta.client)) && routeLocaleGetter(route.to) !== targetLocale) { + if ((differentDomains || (isSSG && import.meta.client)) && oldLocale !== targetLocale) { /** * `$router.currentRoute` does not yet reflect the `to` value, * when the Router middleware handler is executed. diff --git a/src/types.ts b/src/types.ts index d32d4c857..3898740ba 100644 --- a/src/types.ts +++ b/src/types.ts @@ -186,6 +186,13 @@ export type NuxtI18nOptions< * @defaultValue '___' */ routesNameSeparator?: string + /** + * Suffix added to the generated route names for each locale to distinguish them based on language or regional settings. + * This suffix is appended to the base route name to create a unique name for each localized version of a route. + * + * @defaultValue 'locale' + */ + routesNameSuffix?: string /** * Internal suffix added to generated route names for default locale * diff --git a/test/pages/__snapshots__/custom_route.test.ts.snap b/test/pages/__snapshots__/custom_route.test.ts.snap index 5d28989aa..d949aa97e 100644 --- a/test/pages/__snapshots__/custom_route.test.ts.snap +++ b/test/pages/__snapshots__/custom_route.test.ts.snap @@ -7,90 +7,190 @@ exports[`#1649 1`] = ` { "children": [], "file": "/path/to/1649/pages/account/addresses.vue", - "name": "account-addresses___en", + "meta": { + "locale": true, + }, + "name": "account-addresses", "path": "addresses", }, { "children": [], "file": "/path/to/1649/pages/account/index.vue", - "name": "account___en", + "meta": { + "locale": true, + }, + "name": "account", "path": "", }, { "children": [], "file": "/path/to/1649/pages/account/profile.vue", - "name": "account-profile___en", + "meta": { + "locale": true, + }, + "name": "account-profile", "path": "profile", }, ], "file": "/path/to/1649/pages/account.vue", + "meta": { + "locale": true, + }, "path": "/account", }, + { + "children": [], + "file": "/path/to/1649/pages/account/addresses.vue", + "meta": { + "locale": true, + }, + "name": "account-addresses___fr___locale", + "path": "/:locale(fr)/compte/adresses", + }, + { + "children": [], + "file": "/path/to/1649/pages/account/addresses.vue", + "meta": { + "locale": true, + }, + "name": "account-addresses___locale", + "path": "/:locale(fr)/compte/account/addresses", + }, + { + "children": [], + "file": "/path/to/1649/pages/account/index.vue", + "meta": { + "locale": true, + }, + "name": "account___locale", + "path": "/:locale(fr)/compte/account", + }, + { + "children": [], + "file": "/path/to/1649/pages/account/profile.vue", + "meta": { + "locale": true, + }, + "name": "account-profile___fr___locale", + "path": "/:locale(fr)/compte/profil", + }, + { + "children": [], + "file": "/path/to/1649/pages/account/profile.vue", + "meta": { + "locale": true, + }, + "name": "account-profile___locale", + "path": "/:locale(fr)/compte/account/profile", + }, { "children": [ { "children": [], "file": "/path/to/1649/pages/account/addresses.vue", - "name": "account-addresses___ja", + "meta": { + "locale": true, + }, + "name": "account-addresses", "path": "addresses", }, { "children": [], "file": "/path/to/1649/pages/account/index.vue", - "name": "account___ja", + "meta": { + "locale": true, + }, + "name": "account", "path": "", }, { "children": [], "file": "/path/to/1649/pages/account/profile.vue", - "name": "account-profile___ja", + "meta": { + "locale": true, + }, + "name": "account-profile", "path": "profile", }, ], "file": "/path/to/1649/pages/account.vue", - "path": "/ja/account", + "meta": { + "locale": true, + }, + "name": "undefined___locale___fr", + "path": "/:locale(fr)/compte", }, { "children": [ { "children": [], "file": "/path/to/1649/pages/account/addresses.vue", - "name": "account-addresses___fr", - "path": "adresses", + "meta": { + "locale": true, + }, + "name": "account-addresses___locale___fr", + "path": "/:locale(fr)/compte/adresses", + }, + { + "children": [], + "file": "/path/to/1649/pages/account/addresses.vue", + "meta": { + "locale": true, + }, + "name": "account-addresses___locale", + "path": "/:locale(ja|fr)/account/addresses", }, { "children": [], "file": "/path/to/1649/pages/account/index.vue", - "name": "account___fr", - "path": "", + "meta": { + "locale": true, + }, + "name": "account___locale", + "path": "/:locale(ja|fr)/account", + }, + { + "children": [], + "file": "/path/to/1649/pages/account/profile.vue", + "meta": { + "locale": true, + }, + "name": "account-profile___locale___fr", + "path": "/:locale(fr)/compte/profil", }, { "children": [], "file": "/path/to/1649/pages/account/profile.vue", - "name": "account-profile___fr", - "path": "profil", + "meta": { + "locale": true, + }, + "name": "account-profile___locale", + "path": "/:locale(ja|fr)/account/profile", }, ], "file": "/path/to/1649/pages/account.vue", - "path": "/fr/compte", + "meta": { + "locale": true, + }, + "path": "/:locale(ja|fr)/account", }, { "children": [], "file": "/path/to/1649/pages/index.vue", - "name": "index___en", + "meta": { + "locale": true, + }, + "name": "index", "path": "/", }, { "children": [], "file": "/path/to/1649/pages/index.vue", - "name": "index___ja", - "path": "/ja", - }, - { - "children": [], - "file": "/path/to/1649/pages/index.vue", - "name": "index___fr", - "path": "/fr", + "meta": { + "locale": true, + }, + "name": "index___locale", + "path": "/:locale(ja|fr)", }, ] `; @@ -100,20 +200,29 @@ exports[`Module configuration > dynamic parameters 1`] = ` { "children": [], "file": "/path/to/nuxt-app/pages/blog/[date]/[slug].vue", - "name": "blog-date-slug___en", + "meta": { + "locale": true, + }, + "name": "blog-date-slug", "path": "/blog/:date/:slug", }, { "children": [], "file": "/path/to/nuxt-app/pages/blog/[date]/[slug].vue", - "name": "blog-date-slug___ja", - "path": "/ja/blog/tech/:date()/:slug()", + "meta": { + "locale": true, + }, + "name": "blog-date-slug___locale___ja", + "path": "/:locale(ja)/blog/tech/:date()/:slug()", }, { "children": [], "file": "/path/to/nuxt-app/pages/blog/[date]/[slug].vue", - "name": "blog-date-slug___fr", - "path": "/fr/blog/:date/:slug", + "meta": { + "locale": true, + }, + "name": "blog-date-slug___locale", + "path": "/:locale(ja|fr)/blog/:date/:slug", }, ] `; @@ -123,17 +232,45 @@ exports[`Module configuration > simple 1`] = ` { "children": [], "file": "/path/to/nuxt-app/pages/about.vue", + "meta": { + "locale": true, + }, + "path": "/about", + }, + { + "children": [], + "file": "/path/to/nuxt-app/pages/about.vue", + "meta": { + "locale": true, + }, + "name": "undefined___en", "path": "/about-us", }, { "children": [], "file": "/path/to/nuxt-app/pages/about.vue", - "path": "/ja/about-us", + "meta": { + "locale": true, + }, + "name": "undefined___locale___ja", + "path": "/:locale(ja)/about-us", + }, + { + "children": [], + "file": "/path/to/nuxt-app/pages/about.vue", + "meta": { + "locale": true, + }, + "name": "undefined___locale___fr", + "path": "/:locale(fr)/a-propos", }, { "children": [], "file": "/path/to/nuxt-app/pages/about.vue", - "path": "/fr/a-propos", + "meta": { + "locale": true, + }, + "path": "/:locale(ja|fr)/about", }, ] `; @@ -143,107 +280,162 @@ exports[`Module configuration > the part of URL 1`] = ` { "children": [], "file": "/path/to/nuxt-app/pages/about.vue", + "meta": { + "locale": true, + }, "path": "/about", }, { "children": [], "file": "/path/to/nuxt-app/pages/about.vue", - "path": "/ja/about", + "meta": { + "locale": true, + }, + "name": "undefined___locale___fr", + "path": "/:locale(fr)/a-propos", }, { "children": [], "file": "/path/to/nuxt-app/pages/about.vue", - "path": "/fr/a-propos", + "meta": { + "locale": true, + }, + "path": "/:locale(ja|fr)/about", }, { "children": [], "file": "/path/to/nuxt-app/pages/services/coaching.vue", - "name": "services-coaching___en", + "meta": { + "locale": true, + }, + "name": "services-coaching", "path": "/services/coaching", }, { "children": [], "file": "/path/to/nuxt-app/pages/services/coaching.vue", - "name": "services-coaching___ja", - "path": "/ja/services/coaching", + "meta": { + "locale": true, + }, + "name": "services-coaching___locale___fr", + "path": "/:locale(fr)/offres/formation", }, { "children": [], "file": "/path/to/nuxt-app/pages/services/coaching.vue", - "name": "services-coaching___fr", - "path": "/fr/offres/formation", + "meta": { + "locale": true, + }, + "name": "services-coaching___locale", + "path": "/:locale(ja|fr)/services/coaching", }, { "children": [], "file": "/path/to/nuxt-app/pages/services/development/app.vue", - "name": "services-development-app___en", + "meta": { + "locale": true, + }, + "name": "services-development-app", "path": "/services/development/app", }, { "children": [], "file": "/path/to/nuxt-app/pages/services/development/app.vue", - "name": "services-development-app___ja", - "path": "/ja/services/development/app", + "meta": { + "locale": true, + }, + "name": "services-development-app___locale___fr", + "path": "/:locale(fr)/offres/developement/app", }, { "children": [], "file": "/path/to/nuxt-app/pages/services/development/app.vue", - "name": "services-development-app___fr", - "path": "/fr/offres/developement/app", + "meta": { + "locale": true, + }, + "name": "services-development-app___locale", + "path": "/:locale(ja|fr)/services/development/app", }, { "children": [], "file": "/path/to/nuxt-app/pages/services/development/index.vue", - "name": "services-development___en", + "meta": { + "locale": true, + }, + "name": "services-development", "path": "/services/development", }, { "children": [], "file": "/path/to/nuxt-app/pages/services/development/index.vue", - "name": "services-development___ja", - "path": "/ja/services/development", + "meta": { + "locale": true, + }, + "name": "services-development___locale___fr", + "path": "/:locale(fr)/offres/developement", }, { "children": [], "file": "/path/to/nuxt-app/pages/services/development/index.vue", - "name": "services-development___fr", - "path": "/fr/offres/developement", + "meta": { + "locale": true, + }, + "name": "services-development___locale", + "path": "/:locale(ja|fr)/services/development", }, { "children": [], "file": "/path/to/nuxt-app/pages/services/development/website.vue", - "name": "services-development-website___en", + "meta": { + "locale": true, + }, + "name": "services-development-website", "path": "/services/development/website", }, { "children": [], "file": "/path/to/nuxt-app/pages/services/development/website.vue", - "name": "services-development-website___ja", - "path": "/ja/services/development/website", + "meta": { + "locale": true, + }, + "name": "services-development-website___locale___fr", + "path": "/:locale(fr)/offres/developement/site-web", }, { "children": [], "file": "/path/to/nuxt-app/pages/services/development/website.vue", - "name": "services-development-website___fr", - "path": "/fr/offres/developement/site-web", + "meta": { + "locale": true, + }, + "name": "services-development-website___locale", + "path": "/:locale(ja|fr)/services/development/website", }, { "children": [], "file": "/path/to/nuxt-app/pages/services/index.vue", - "name": "services___en", + "meta": { + "locale": true, + }, + "name": "services", "path": "/services", }, { "children": [], "file": "/path/to/nuxt-app/pages/services/index.vue", - "name": "services___ja", - "path": "/ja/services", + "meta": { + "locale": true, + }, + "name": "services___locale___fr", + "path": "/:locale(fr)/offres", }, { "children": [], "file": "/path/to/nuxt-app/pages/services/index.vue", - "name": "services___fr", - "path": "/fr/offres", + "meta": { + "locale": true, + }, + "name": "services___locale", + "path": "/:locale(ja|fr)/services", }, ] `; @@ -252,15 +444,41 @@ exports[`Page components > JavaScript 1`] = ` [ { "children": [], + "meta": { + "locale": true, + }, + "path": "/about", + }, + { + "children": [], + "meta": { + "locale": true, + }, + "name": "undefined___en", "path": "/about-us", }, { "children": [], - "path": "/ja/about-ja", + "meta": { + "locale": true, + }, + "name": "undefined___locale___ja", + "path": "/:locale(ja)/about-ja", + }, + { + "children": [], + "meta": { + "locale": true, + }, + "name": "undefined___locale___fr", + "path": "/:locale(fr)/a-propos", }, { "children": [], - "path": "/fr/a-propos", + "meta": { + "locale": true, + }, + "path": "/:locale(ja|fr)/about", }, ] `; @@ -269,18 +487,43 @@ exports[`Page components > dynamic route 1`] = ` [ { "children": [], + "meta": { + "locale": true, + }, + "name": "articles-name", + "path": "/articles/:name", + }, + { + "children": [], + "meta": { + "locale": true, + }, "name": "articles-name___en", "path": "/articles/:name()", }, { "children": [], - "name": "articles-name___ja", - "path": "/ja/%E8%A8%98%E4%BA%8B/:name()", + "meta": { + "locale": true, + }, + "name": "articles-name___locale___ja", + "path": "/:locale(ja)/%E8%A8%98%E4%BA%8B/:name()", + }, + { + "children": [], + "meta": { + "locale": true, + }, + "name": "articles-name___locale___fr", + "path": "/:locale(fr)/articles/:name()", }, { "children": [], - "name": "articles-name___fr", - "path": "/fr/articles/:name()", + "meta": { + "locale": true, + }, + "name": "articles-name___locale", + "path": "/:locale(ja|fr)/articles/:name", }, ] `; @@ -289,15 +532,41 @@ exports[`Page components > simple 1`] = ` [ { "children": [], + "meta": { + "locale": true, + }, + "path": "/about", + }, + { + "children": [], + "meta": { + "locale": true, + }, + "name": "undefined___en", "path": "/about-us", }, { "children": [], - "path": "/ja/about-ja", + "meta": { + "locale": true, + }, + "name": "undefined___locale___ja", + "path": "/:locale(ja)/about-ja", + }, + { + "children": [], + "meta": { + "locale": true, + }, + "name": "undefined___locale___fr", + "path": "/:locale(fr)/a-propos", }, { "children": [], - "path": "/fr/a-propos", + "meta": { + "locale": true, + }, + "path": "/:locale(ja|fr)/about", }, ] `; @@ -306,15 +575,41 @@ exports[`Page components > with definePageMeta 1`] = ` [ { "children": [], + "meta": { + "locale": true, + }, + "path": "/about", + }, + { + "children": [], + "meta": { + "locale": true, + }, + "name": "undefined___en", "path": "/about-us", }, { "children": [], - "path": "/ja/about-ja", + "meta": { + "locale": true, + }, + "name": "undefined___locale___ja", + "path": "/:locale(ja)/about-ja", + }, + { + "children": [], + "meta": { + "locale": true, + }, + "name": "undefined___locale___fr", + "path": "/:locale(fr)/a-propos", }, { "children": [], - "path": "/fr/a-propos", + "meta": { + "locale": true, + }, + "path": "/:locale(ja|fr)/about", }, ] `; diff --git a/test/pages/__snapshots__/localize_routes.test.ts.snap b/test/pages/__snapshots__/localize_routes.test.ts.snap index 8f190912b..4aaefd0f6 100644 --- a/test/pages/__snapshots__/localize_routes.test.ts.snap +++ b/test/pages/__snapshots__/localize_routes.test.ts.snap @@ -16,20 +16,32 @@ exports[`localizeRoutes > Route options resolver: routing disable > should be di exports[`localizeRoutes > basic > should be localized routing 1`] = ` [ { - "name": "home___en", - "path": "/en", + "meta": { + "locale": true, + }, + "name": "home", + "path": "/", }, { - "name": "home___ja", - "path": "/ja", + "meta": { + "locale": true, + }, + "name": "home___locale", + "path": "/:locale(en|ja)", }, { - "name": "about___en", - "path": "/en/about", + "meta": { + "locale": true, + }, + "name": "about", + "path": "/about", }, { - "name": "about___ja", - "path": "/ja/about", + "meta": { + "locale": true, + }, + "name": "about___locale", + "path": "/:locale(en|ja)/about", }, ] `; @@ -39,30 +51,48 @@ exports[`localizeRoutes > has children > should be localized routing 1`] = ` { "children": [ { - "name": "user-profile___en", + "meta": { + "locale": true, + }, + "name": "user-profile", "path": "profile", }, { - "name": "user-posts___en", + "meta": { + "locale": true, + }, + "name": "user-posts", "path": "posts", }, ], - "name": "user___en", - "path": "/en/user/:id", + "meta": { + "locale": true, + }, + "name": "user", + "path": "/user/:id", }, { "children": [ { - "name": "user-profile___ja", - "path": "profile", + "meta": { + "locale": true, + }, + "name": "user-profile___locale", + "path": "/:locale(en|ja)/user/:id/profile", }, { - "name": "user-posts___ja", - "path": "posts", + "meta": { + "locale": true, + }, + "name": "user-posts___locale", + "path": "/:locale(en|ja)/user/:id/posts", }, ], - "name": "user___ja", - "path": "/ja/user/:id", + "meta": { + "locale": true, + }, + "name": "user___locale", + "path": "/:locale(en|ja)/user/:id", }, ] `; @@ -70,20 +100,32 @@ exports[`localizeRoutes > has children > should be localized routing 1`] = ` exports[`localizeRoutes > route name separator > should be localized routing 1`] = ` [ { - "name": "home__en", - "path": "/en", + "meta": { + "locale": true, + }, + "name": "home", + "path": "/", }, { - "name": "home__ja", - "path": "/ja", + "meta": { + "locale": true, + }, + "name": "home__locale", + "path": "/:locale(en|ja)", }, { - "name": "about__en", - "path": "/en/about", + "meta": { + "locale": true, + }, + "name": "about", + "path": "/about", }, { - "name": "about__ja", - "path": "/ja/about", + "meta": { + "locale": true, + }, + "name": "about__locale", + "path": "/:locale(en|ja)/about", }, ] `; @@ -104,28 +146,48 @@ exports[`localizeRoutes > strategy: "no_prefix" > should be localized routing 1` exports[`localizeRoutes > strategy: "prefix" > should be localized routing 1`] = ` [ { + "meta": { + "locale": true, + }, "name": "home", "path": "/", }, { - "name": "home___en", - "path": "/en", + "meta": { + "locale": true, + }, + "name": "index", + "path": "/", + "redirect": "/en", }, { - "name": "home___ja", - "path": "/ja", + "meta": { + "locale": true, + }, + "name": "home___locale", + "path": "/:locale(en|ja)", }, { + "meta": { + "locale": true, + }, "name": "about", "path": "/about", }, { - "name": "about___en", - "path": "/en/about", + "meta": { + "locale": true, + }, + "name": "index", + "path": "/about", + "redirect": "/en", }, { - "name": "about___ja", - "path": "/ja/about", + "meta": { + "locale": true, + }, + "name": "about___locale", + "path": "/:locale(en|ja)/about", }, ] `; @@ -133,70 +195,78 @@ exports[`localizeRoutes > strategy: "prefix" > should be localized routing 1`] = exports[`localizeRoutes > strategy: "prefix_and_default" > should be localized routing 1`] = ` [ { - "name": "home___en___default", + "meta": { + "locale": true, + }, + "name": "home", "path": "/", }, { - "name": "home___en", - "path": "/en", + "meta": { + "locale": true, + }, + "name": "home___locale", + "path": "/:locale(en|ja)", }, { - "name": "home___ja", - "path": "/ja", - }, - { - "name": "about___en___default", + "meta": { + "locale": true, + }, + "name": "about", "path": "/about", }, { - "name": "about___en", - "path": "/en/about", - }, - { - "name": "about___ja", - "path": "/ja/about", + "meta": { + "locale": true, + }, + "name": "about___locale", + "path": "/:locale(en|ja)/about", }, { "children": [ { - "name": "user-profile___en___default", + "meta": { + "locale": true, + }, + "name": "user-profile", "path": "profile", }, { - "name": "user-posts___en___default", + "meta": { + "locale": true, + }, + "name": "user-posts", "path": "posts", }, ], - "name": "user___en___default", + "meta": { + "locale": true, + }, + "name": "user", "path": "/user/:id", }, { "children": [ { - "name": "user-profile___en", - "path": "profile", - }, - { - "name": "user-posts___en", - "path": "posts", - }, - ], - "name": "user___en", - "path": "/en/user/:id", - }, - { - "children": [ - { - "name": "user-profile___ja", - "path": "profile", + "meta": { + "locale": true, + }, + "name": "user-profile___locale", + "path": "/:locale(en|ja)/user/:id/profile", }, { - "name": "user-posts___ja", - "path": "posts", + "meta": { + "locale": true, + }, + "name": "user-posts___locale", + "path": "/:locale(en|ja)/user/:id/posts", }, ], - "name": "user___ja", - "path": "/ja/user/:id", + "meta": { + "locale": true, + }, + "name": "user___locale", + "path": "/:locale(en|ja)/user/:id", }, ] `; @@ -204,20 +274,32 @@ exports[`localizeRoutes > strategy: "prefix_and_default" > should be localized r exports[`localizeRoutes > strategy: "prefix_except_default" > should be localized routing 1`] = ` [ { - "name": "home___en", + "meta": { + "locale": true, + }, + "name": "home", "path": "/", }, { - "name": "home___ja", - "path": "/ja", + "meta": { + "locale": true, + }, + "name": "home___locale", + "path": "/:locale(ja)", }, { - "name": "about___en", + "meta": { + "locale": true, + }, + "name": "about", "path": "/about", }, { - "name": "about___ja", - "path": "/ja/about", + "meta": { + "locale": true, + }, + "name": "about___locale", + "path": "/:locale(ja)/about", }, ] `; @@ -225,20 +307,32 @@ exports[`localizeRoutes > strategy: "prefix_except_default" > should be localize exports[`localizeRoutes > trailing slash > should be localized routing 1`] = ` [ { - "name": "home___en", - "path": "/en/", + "meta": { + "locale": true, + }, + "name": "home", + "path": "/", }, { - "name": "home___ja", - "path": "/ja/", + "meta": { + "locale": true, + }, + "name": "home___locale", + "path": "/:locale(en|ja)/", }, { - "name": "about___en", - "path": "/en/about/", + "meta": { + "locale": true, + }, + "name": "about", + "path": "/about", }, { - "name": "about___ja", - "path": "/ja/about/", + "meta": { + "locale": true, + }, + "name": "about___locale", + "path": "/:locale(en|ja)/about/", }, ] `; diff --git a/test/pages/ignore_route/__snapshots__/disable.test.ts.snap b/test/pages/ignore_route/__snapshots__/disable.test.ts.snap index 8088aba53..a1e52fe5a 100644 --- a/test/pages/ignore_route/__snapshots__/disable.test.ts.snap +++ b/test/pages/ignore_route/__snapshots__/disable.test.ts.snap @@ -79,20 +79,20 @@ exports[`Module configuration > simple 1`] = ` { "children": [], "file": "/path/to/nuxt-app/pages/index.vue", - "name": "index___en", + "meta": { + "locale": true, + }, + "name": "index", "path": "/", }, { "children": [], "file": "/path/to/nuxt-app/pages/index.vue", - "name": "index___ja", - "path": "/ja", - }, - { - "children": [], - "file": "/path/to/nuxt-app/pages/index.vue", - "name": "index___fr", - "path": "/fr", + "meta": { + "locale": true, + }, + "name": "index___locale", + "path": "/:locale(ja|fr)", }, ] `; diff --git a/test/pages/ignore_route/__snapshots__/pick.test.ts.snap b/test/pages/ignore_route/__snapshots__/pick.test.ts.snap index 8bb1eecd8..9b68eb2d8 100644 --- a/test/pages/ignore_route/__snapshots__/pick.test.ts.snap +++ b/test/pages/ignore_route/__snapshots__/pick.test.ts.snap @@ -5,59 +5,98 @@ exports[`Module configuration > nested complex route 1`] = ` { "children": [], "file": "/path/to/nuxt-app/pages/about.vue", + "meta": { + "locale": true, + }, "path": "/about", }, { "children": [], "file": "/path/to/nuxt-app/pages/about.vue", - "path": "/ja/about", + "meta": { + "locale": true, + }, + "path": "/:locale(ja)/about", }, { "children": [], "file": "/path/to/nuxt-app/pages/services/coaching.vue", - "name": "services-coaching___en", + "meta": { + "locale": true, + }, + "name": "services-coaching", "path": "/services/coaching", }, { "children": [], "file": "/path/to/nuxt-app/pages/services/coaching.vue", - "name": "services-coaching___ja", - "path": "/ja/services/coaching", + "meta": { + "locale": true, + }, + "name": "services-coaching___locale", + "path": "/:locale(ja)/services/coaching", }, { "children": [], "file": "/path/to/nuxt-app/pages/services/development/app.vue", - "name": "services-development-app___ja", - "path": "/ja/services/development/app", + "meta": { + "locale": true, + }, + "name": "services-development-app", + "path": "/services/development/app", + }, + { + "children": [], + "file": "/path/to/nuxt-app/pages/services/development/app.vue", + "meta": { + "locale": true, + }, + "name": "services-development-app___locale", + "path": "/:locale(ja)/services/development/app", }, { "children": [], "file": "/path/to/nuxt-app/pages/services/development/index.vue", - "name": "services-development___en", + "meta": { + "locale": true, + }, + "name": "services-development", "path": "/services/development", }, { "children": [], "file": "/path/to/nuxt-app/pages/services/development/index.vue", - "name": "services-development___ja", - "path": "/ja/services/development", + "meta": { + "locale": true, + }, + "name": "services-development___locale", + "path": "/:locale(ja)/services/development", }, { "children": [], "file": "/path/to/nuxt-app/pages/services/development/website.vue", - "name": "services-development-website___en", + "meta": { + "locale": true, + }, + "name": "services-development-website", "path": "/services/development/website", }, { "children": [], "file": "/path/to/nuxt-app/pages/services/development/website.vue", - "name": "services-development-website___ja", - "path": "/ja/services/development/website", + "meta": { + "locale": true, + }, + "name": "services-development-website___locale", + "path": "/:locale(ja)/services/development/website", }, { "children": [], "file": "/path/to/nuxt-app/pages/services/index.vue", - "name": "services___en", + "meta": { + "locale": true, + }, + "name": "services", "path": "/services", }, ] @@ -68,14 +107,20 @@ exports[`Module configuration > nested dynamic route 1`] = ` { "children": [], "file": "/path/to/nuxt-app/pages/[nested]/[route]/index.vue", - "name": "nested-route___ja", - "path": "/ja/:nested/:route", + "meta": { + "locale": true, + }, + "name": "nested-route", + "path": "/:nested/:route", }, { "children": [], "file": "/path/to/nuxt-app/pages/[nested]/[route]/index.vue", - "name": "nested-route___fr", - "path": "/fr/:nested/:route", + "meta": { + "locale": true, + }, + "name": "nested-route___locale", + "path": "/:locale(ja|fr)/:nested/:route", }, ] `; @@ -85,14 +130,20 @@ exports[`Module configuration > nested static route 1`] = ` { "children": [], "file": "/path/to/nuxt-app/pages/nested/route/index.vue", - "name": "nested-route___ja", - "path": "/ja/nested/route", + "meta": { + "locale": true, + }, + "name": "nested-route", + "path": "/nested/route", }, { "children": [], "file": "/path/to/nuxt-app/pages/nested/route/index.vue", - "name": "nested-route___fr", - "path": "/fr/nested/route", + "meta": { + "locale": true, + }, + "name": "nested-route___locale", + "path": "/:locale(ja|fr)/nested/route", }, ] `; @@ -104,11 +155,17 @@ exports[`Module configuration > simple 1`] = ` { "children": [], "file": "/path/to/nuxt-app/pages/about/index.vue", - "name": "about___en", + "meta": { + "locale": true, + }, + "name": "about", "path": "", }, ], "file": "/path/to/nuxt-app/pages/about.vue", + "meta": { + "locale": true, + }, "path": "/about", }, { @@ -116,30 +173,36 @@ exports[`Module configuration > simple 1`] = ` { "children": [], "file": "/path/to/nuxt-app/pages/about/index.vue", - "name": "about___fr", - "path": "", + "meta": { + "locale": true, + }, + "name": "about___locale", + "path": "/:locale(fr)/about", }, ], "file": "/path/to/nuxt-app/pages/about.vue", - "path": "/fr/about", + "meta": { + "locale": true, + }, + "path": "/:locale(fr)/about", }, { "children": [], "file": "/path/to/nuxt-app/pages/index.vue", - "name": "index___en", + "meta": { + "locale": true, + }, + "name": "index", "path": "/", }, { "children": [], "file": "/path/to/nuxt-app/pages/index.vue", - "name": "index___ja", - "path": "/ja", - }, - { - "children": [], - "file": "/path/to/nuxt-app/pages/index.vue", - "name": "index___fr", - "path": "/fr", + "meta": { + "locale": true, + }, + "name": "index___locale", + "path": "/:locale(ja|fr)", }, ] `; @@ -148,8 +211,19 @@ exports[`Page components > dynamic route 1`] = ` [ { "children": [], - "name": "articles-name___fr", - "path": "/fr/articles/:name", + "meta": { + "locale": true, + }, + "name": "articles-name", + "path": "/articles/:name", + }, + { + "children": [], + "meta": { + "locale": true, + }, + "name": "articles-name___locale", + "path": "/:locale(fr)/articles/:name", }, ] `; @@ -158,11 +232,17 @@ exports[`Page components > simple 1`] = ` [ { "children": [], - "path": "/fr/about", + "meta": { + "locale": true, + }, + "path": "/about", }, { "children": [], - "path": "/ja/about", + "meta": { + "locale": true, + }, + "path": "/:locale(fr|ja)/about", }, ] `; diff --git a/test/pages/localize_routes.test.ts b/test/pages/localize_routes.test.ts index f37c3d8d9..9ae05d52b 100644 --- a/test/pages/localize_routes.test.ts +++ b/test/pages/localize_routes.test.ts @@ -24,16 +24,22 @@ describe('localizeRoutes', function () { } ] const localeCodes = ['en', 'ja'] + const localizedRoutes = localizeRoutes(routes, { ...nuxtOptions, locales: localeCodes }) expect(localizedRoutes).toMatchSnapshot() expect(localizedRoutes.length).to.equal(4) - localeCodes.forEach(locale => { - routes.forEach(route => { - expect(localizedRoutes).to.deep.include({ - path: `/${locale}${route.path === '/' ? '' : route.path}`, - name: `${route.name}${nuxtOptions.routesNameSeparator}${locale}` - }) + routes.forEach(route => { + expect(localizedRoutes).to.deep.include({ + path: route.path, + name: route.name, + meta: { locale: true } + }) + + expect(localizedRoutes).to.deep.include({ + path: `/:locale(${localeCodes.join('|')})${route.path === '/' ? '' : route.path}`, + name: `${route.name}${nuxtOptions.routesNameSeparator}locale`, + meta: { locale: true } }) }) }) @@ -64,16 +70,16 @@ describe('localizeRoutes', function () { expect(localizedRoutes).toMatchSnapshot() expect(localizedRoutes.length).to.equal(2) - localeCodes.forEach(locale => { - routes.forEach(route => { - expect(localizedRoutes).to.deep.include({ - path: `/${locale}${route.path === '/' ? '' : route.path}`, - name: `${route.name}${nuxtOptions.routesNameSeparator}${locale}`, - children: children.map(child => ({ - path: child.path, - name: `${child.name}${nuxtOptions.routesNameSeparator}${locale}` - })) - }) + routes.forEach(route => { + expect(localizedRoutes).to.deep.include({ + path: `/:locale(${localeCodes.join('|')})${route.path === '/' ? '' : route.path}`, + name: `${route.name}${nuxtOptions.routesNameSeparator}locale`, + children: children.map(child => ({ + path: `/:locale(${localeCodes.join('|')})${route.path === '/' ? '' : route.path}/${child.path}`, + name: `${child.name}${nuxtOptions.routesNameSeparator}locale`, + meta: { locale: true } + })), + meta: { locale: true } }) }) }) @@ -96,12 +102,11 @@ describe('localizeRoutes', function () { expect(localizedRoutes).toMatchSnapshot() expect(localizedRoutes.length).to.equal(4) - localeCodes.forEach(locale => { - routes.forEach(route => { - expect(localizedRoutes).to.deep.include({ - path: `/${locale}${route.path === '/' ? '' : route.path}/`, - name: `${route.name}${nuxtOptions.routesNameSeparator}${locale}` - }) + routes.forEach(route => { + expect(localizedRoutes).to.deep.include({ + path: `/:locale(${localeCodes.join('|')})${route.path === '/' ? '' : route.path}/`, + name: `${route.name}${nuxtOptions.routesNameSeparator}locale`, + meta: { locale: true } }) }) }) @@ -128,12 +133,11 @@ describe('localizeRoutes', function () { expect(localizedRoutes).toMatchSnapshot() expect(localizedRoutes.length).to.equal(4) - localeCodes.forEach(locale => { - routes.forEach(route => { - expect(localizedRoutes).to.deep.include({ - path: `/${locale}${route.path === '/' ? '' : route.path}`, - name: `${route.name}${'__'}${locale}` - }) + routes.forEach(route => { + expect(localizedRoutes).to.deep.include({ + path: `/:locale(${localeCodes.join('|')})${route.path === '/' ? '' : route.path}`, + name: `${route.name}${'__'}locale`, + meta: { locale: true } }) }) }) diff --git a/test/pages/utils.ts b/test/pages/utils.ts index 39b5b8614..0e763b34e 100644 --- a/test/pages/utils.ts +++ b/test/pages/utils.ts @@ -10,7 +10,7 @@ export function getNuxtOptions( defaultLocale = 'en' ): MarkRequired< NuxtI18nOptions, - 'strategy' | 'defaultLocaleRouteNameSuffix' | 'trailingSlash' | 'routesNameSeparator' + 'strategy' | 'defaultLocaleRouteNameSuffix' | 'trailingSlash' | 'routesNameSeparator' | 'routesNameSuffix' > { return { customRoutes, @@ -20,6 +20,7 @@ export function getNuxtOptions( defaultLocaleRouteNameSuffix: 'default', trailingSlash: false, routesNameSeparator: '___', + routesNameSuffix: 'locale', locales: [ { code: 'en', iso: 'en-US', file: 'en.json', name: 'English' }, { code: 'ja', iso: 'ja-JP', file: 'ja.json', name: 'Japanses' }, diff --git a/test/routing-utils.test.ts b/test/routing-utils.test.ts index 0ee14620e..3f644dbdc 100644 --- a/test/routing-utils.test.ts +++ b/test/routing-utils.test.ts @@ -48,58 +48,32 @@ describe('adjustRouteDefinitionForTrailingSlash', function () { describe('getLocaleRouteName', () => { describe('strategy: prefix_and_default', () => { it('should be `route1___en___default`', () => { - assert.equal( - utils.getLocaleRouteName('route1', 'en', { - defaultLocale: 'en', - strategy: 'prefix_and_default', - routesNameSeparator: '___', - defaultLocaleRouteNameSuffix: 'default' - }), - 'route1___en___default' - ) + assert.equal(utils.getLocaleRouteName('route1', 'en', 'en', '___', 'locale', 'prefix_and_default'), 'route1') }) }) describe('strategy: prefix_except_default', () => { it('should be `route1___en`', () => { - assert.equal( - utils.getLocaleRouteName('route1', 'en', { - defaultLocale: 'en', - strategy: 'prefix_except_default', - routesNameSeparator: '___', - defaultLocaleRouteNameSuffix: 'default' - }), - 'route1___en' - ) + assert.equal(utils.getLocaleRouteName('route1', 'en', 'en', '___', 'locale', 'prefix_except_default'), 'route1') }) }) describe('strategy: no_prefix', () => { it('should be `route1`', () => { - assert.equal( - utils.getLocaleRouteName('route1', 'en', { - defaultLocale: 'en', - strategy: 'no_prefix', - routesNameSeparator: '___', - defaultLocaleRouteNameSuffix: 'default' - }), - 'route1' - ) + assert.equal(utils.getLocaleRouteName('route1', 'en', 'en', '___', 'locale', 'no_prefix'), 'route1') + }) + }) + + describe('strategy: prefix_regexp', () => { + it('should be `route1`', () => { + assert.equal(utils.getLocaleRouteName('route1', 'en', 'en', '___', 'locale', 'prefix_regexp'), 'route1') }) }) describe('irregular', () => { describe('route name is null', () => { it('should be ` (null)___en___default`', () => { - assert.equal( - utils.getLocaleRouteName(null, 'en', { - defaultLocale: 'en', - strategy: 'prefix_and_default', - routesNameSeparator: '___', - defaultLocaleRouteNameSuffix: 'default' - }), - '(null)___en___default' - ) + assert.equal(utils.getLocaleRouteName(null, 'en', 'en', '___', 'locale', 'prefix_regexp'), '(null)') }) }) }) diff --git a/vitest.config.ts b/vitest.config.ts index 9ec55bbbb..204d0afbb 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -7,6 +7,9 @@ export default defineConfig({ restoreMocks: true, testTimeout: 300000, retry: 1, + chaiConfig: { + truncateThreshold: 0 + }, server: { deps: { inline: [/@nuxt\/test-utils/]