forked from nuxt-modules/i18n
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
…dules#2625) * refactor: integrate `localizeRoutes` from `vue-i18n-routing` * test: integrate `localizeRoutes` tests * refactor: integrate localizeRoutes from vue-i18n-routing (nuxt-modules#28)
- Loading branch information
1 parent
7270c9c
commit 69345ab
Showing
10 changed files
with
750 additions
and
64 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,186 @@ | ||
import { getNormalizedLocales } from './utils' | ||
|
||
import type { Locale } from 'vue-i18n' | ||
import type { NuxtPage } from '@nuxt/schema' | ||
import type { MarkRequired, MarkOptional } from 'ts-essentials' | ||
import type { NuxtI18nOptions } from './types' | ||
|
||
const join = (...args: (string | undefined)[]) => args.filter(Boolean).join('') | ||
|
||
/** | ||
* Options to compute route localizing | ||
* | ||
* @remarks | ||
* The route options that is compute the route to be localized on {@link localizeRoutes} | ||
* | ||
* @public | ||
*/ | ||
export declare interface ComputedRouteOptions { | ||
locales: readonly string[] | ||
paths: Record<string, string> | ||
} | ||
|
||
/** | ||
* Resolver for route localizing options | ||
* | ||
* @public | ||
*/ | ||
export declare type RouteOptionsResolver = (route: NuxtPage, localeCodes: string[]) => ComputedRouteOptions | undefined | ||
|
||
/** | ||
* Localize route path prefix judgment options used in {@link LocalizeRoutesPrefixable} | ||
* | ||
* @public | ||
*/ | ||
export interface PrefixLocalizedRouteOptions { | ||
/** | ||
* Current locale | ||
*/ | ||
locale: Locale | ||
/** | ||
* Default locale | ||
*/ | ||
defaultLocale?: Locale | undefined | ||
/** | ||
* The parent route of the route to be resolved | ||
*/ | ||
parent: NuxtPage | undefined | ||
/** | ||
* The path of route | ||
*/ | ||
path: string | ||
} | ||
function prefixLocalizedRoute( | ||
localizeOptions: PrefixLocalizedRouteOptions, | ||
options: LocalizeRoutesParams, | ||
extra = false | ||
): boolean { | ||
const isDefaultLocale = localizeOptions.locale === (options.defaultLocale ?? '') | ||
const isChildWithRelativePath = localizeOptions.parent != null && !localizeOptions.path.startsWith('/') | ||
|
||
// no need to add prefix if child's path is relative | ||
return ( | ||
!extra && | ||
!options.differentDomains && | ||
!isChildWithRelativePath && | ||
// skip default locale if strategy is 'prefix_except_default' | ||
!(isDefaultLocale && options.strategy === 'prefix_except_default') | ||
) | ||
} | ||
|
||
function adjustRoutePathForTrailingSlash(localized: LocalizedRoute, trailingSlash: boolean) { | ||
const isChildWithRelativePath = localized.parent != null && !localized.path.startsWith('/') | ||
return localized.path.replace(/\/+$/, '') + (trailingSlash ? '/' : '') || (isChildWithRelativePath ? '' : '/') | ||
} | ||
|
||
export type LocalizeRoutesParams = MarkRequired< | ||
NuxtI18nOptions, | ||
'strategy' | 'locales' | 'routesNameSeparator' | 'trailingSlash' | 'defaultLocaleRouteNameSuffix' | ||
> & { | ||
includeUnprefixedFallback?: boolean | ||
optionsResolver?: RouteOptionsResolver | ||
} | ||
|
||
type LocalizedRoute = NuxtPage & { locale: Locale; parent: NuxtPage | undefined } | ||
type LocalizeRouteParams = { | ||
/** | ||
* locales to use for localization | ||
*/ | ||
locales: string[] | ||
/** | ||
* parent route | ||
*/ | ||
parent?: NuxtPage | ||
/** | ||
* indicates whether this is a default route for 'prefix_and_default' strategy | ||
*/ | ||
extra?: boolean | ||
} | ||
|
||
/** | ||
* Localize routes | ||
* | ||
* @param routes - Some routes | ||
* @param options - An options | ||
* | ||
* @returns Localized routes | ||
* | ||
* @public | ||
*/ | ||
export function localizeRoutes(routes: NuxtPage[], options: LocalizeRoutesParams): NuxtPage[] { | ||
if (options.strategy === 'no_prefix') { | ||
return routes | ||
} | ||
|
||
function localizeRoute(route: NuxtPage, { locales = [], parent, extra = false }: LocalizeRouteParams): NuxtPage[] { | ||
// skip route localization | ||
if (route.redirect && !route.file) { | ||
return [route] | ||
} | ||
|
||
// resolve with route (page) options | ||
const routeOptions = options.optionsResolver?.(route, locales) | ||
if (options.optionsResolver != null && routeOptions == null) { | ||
return [route] | ||
} | ||
|
||
// component specific options | ||
const componentOptions: ComputedRouteOptions = { | ||
// filter locales to prevent child routes from being localized even though they are disabled in the configuration. | ||
locales: locales.filter(locale => (routeOptions?.locales ?? locales).includes(locale)), | ||
paths: {}, | ||
...routeOptions | ||
} | ||
|
||
const localizedRoutes: (LocalizedRoute | NuxtPage)[] = [] | ||
for (const locale of componentOptions.locales) { | ||
const localized: LocalizedRoute = { ...route, locale, parent } | ||
const isDefaultLocale = locale === options.defaultLocale | ||
const addDefaultTree = isDefaultLocale && options.strategy === 'prefix_and_default' && parent == null && !extra | ||
|
||
// localize route again for strategy `prefix_and_default` | ||
if (addDefaultTree && parent == null && !extra) { | ||
localizedRoutes.push(...localizeRoute(route, { locales: [locale], extra: true })) | ||
} | ||
|
||
const nameSegments = [localized.name, options.routesNameSeparator, locale] | ||
if (extra) { | ||
nameSegments.push(options.routesNameSeparator, options.defaultLocaleRouteNameSuffix) | ||
} | ||
|
||
// localize name if set | ||
localized.name &&= join(...nameSegments) | ||
|
||
// localize child routes if set | ||
localized.children &&= localized.children.flatMap(child => | ||
localizeRoute(child, { locales: [locale], parent: route, extra }) | ||
) | ||
|
||
// use custom path if found | ||
localized.path = componentOptions.paths?.[locale] ?? localized.path | ||
|
||
const localePrefixable = prefixLocalizedRoute(localized, options, extra) | ||
if (localePrefixable) { | ||
localized.path = join('/', locale, localized.path) | ||
|
||
if (isDefaultLocale && options.strategy === 'prefix' && options.includeUnprefixedFallback) { | ||
localizedRoutes.push({ ...route, locale, parent }) | ||
} | ||
} | ||
|
||
localized.path &&= adjustRoutePathForTrailingSlash(localized, options.trailingSlash) | ||
localizedRoutes.push(localized) | ||
} | ||
|
||
// remove properties used for localization process | ||
return localizedRoutes.flatMap((x: MarkOptional<LocalizedRoute, 'parent' | 'locale'>) => { | ||
delete x.parent | ||
delete x.locale | ||
return x | ||
}) | ||
} | ||
|
||
return routes.flatMap(route => | ||
localizeRoute(route, { locales: getNormalizedLocales(options.locales).map(x => x.code) }) | ||
) | ||
} |
Oops, something went wrong.