Skip to content

Commit ec95af9

Browse files
committed
feat: route resolution enhancement
1 parent 98695df commit ec95af9

File tree

7 files changed

+81
-10
lines changed

7 files changed

+81
-10
lines changed

src/constants.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ export const DEFAULT_OPTIONS = {
2929
localeDetector: '',
3030
typedPages: true,
3131
typedOptionsAndMessages: false,
32-
alternateLinkCanonicalQueries: true
32+
alternateLinkCanonicalQueries: true,
33+
routeResolutionEnhancement: false as false | 'explicit' | 'implicit'
3334
},
3435
bundle: {
3536
compositionOnly: true,

src/gen.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ declare global {
196196

197197
// prettier-ignore
198198
return `// Generated by @nuxtjs/i18n
199-
import type { ${i18nType} } from 'vue-i18n'
199+
import type { ${i18nType}, Locale } from 'vue-i18n'
200200
import type { ComposerCustomProperties } from '${relative(
201201
join(nuxt.options.buildDir, 'types'),
202202
resolve(runtimeDir, 'types.ts')
@@ -228,6 +228,21 @@ declare module '#app' {
228228
229229
${typedRouterAugmentations}
230230
231+
declare module 'vue-router' {
232+
interface Router {
233+
resolve<Name extends keyof RouteMap = keyof RouteMap>(
234+
to: RouteLocationAsRelativeTyped<RouteMap, Name>,
235+
currentLocation?: RouteLocationNormalizedLoaded,
236+
options?: { locale?: Locale | boolean }
237+
): RouteLocationResolved<Name>
238+
resolve(
239+
to: RouteLocationAsString | RouteLocationAsRelative | RouteLocationAsPath,
240+
currentLocation?: RouteLocationNormalizedLoaded,
241+
options?: { locale?: Locale | boolean }
242+
): RouteLocationResolved
243+
}
244+
}
245+
231246
${(options.autoDeclare && globalTranslationTypes) || ''}
232247
233248
export {}`

src/prepare/runtime.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ export function prepareRuntime(ctx: I18nNuxtContext, nuxt: Nuxt) {
99
const { options, resolver } = ctx
1010
// for core plugin
1111
addPlugin(resolver.resolve('./runtime/plugins/i18n'))
12+
addPlugin(resolver.resolve('./runtime/plugins/route-resolution-enhancement'))
1213
addPlugin(resolver.resolve('./runtime/plugins/route-locale-detect'))
1314
addPlugin(resolver.resolve('./runtime/plugins/ssg-detect'))
1415
addPlugin(resolver.resolve('./runtime/plugins/switch-locale-path-ssr'))

src/runtime/components/NuxtLinkLocale.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/* eslint-disable @typescript-eslint/no-unsafe-assignment */
22
import { isObject } from '@intlify/shared'
3-
import { useLocalePath, type Locale } from '#i18n'
3+
import { useLocaleRoute, type Locale } from '#i18n'
44
import { defineComponent, computed, h } from 'vue'
55
import { defineNuxtLink } from '#imports'
66
import { hasProtocol } from 'ufo'
@@ -28,7 +28,7 @@ export default defineComponent<NuxtLinkLocaleProps>({
2828
}
2929
},
3030
setup(props, { slots }) {
31-
const localePath = useLocalePath()
31+
const localeRoute = useLocaleRoute()
3232

3333
// From https://github.com/nuxt/nuxt/blob/main/packages/nuxt/src/app/components/nuxt-link.ts#L57
3434
const checkPropConflicts = (
@@ -43,7 +43,7 @@ export default defineComponent<NuxtLinkLocaleProps>({
4343

4444
const resolvedPath = computed(() => {
4545
const destination = props.to ?? props.href
46-
return (destination != null ? localePath(destination, props.locale) : destination) as string
46+
return destination != null ? localeRoute(destination, props.locale) : destination
4747
})
4848

4949
// Resolving link type
@@ -77,6 +77,7 @@ export default defineComponent<NuxtLinkLocaleProps>({
7777
}
7878

7979
if (!isExternal.value) {
80+
// @ts-expect-error type needs to expanded to allow route objects/paths as NuxtLinkProps
8081
_props.to = resolvedPath.value
8182
}
8283

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { defineNuxtPlugin } from '#imports'
2+
import type { Locale } from 'vue-i18n'
3+
import { resolveRoute } from '../routing/routing'
4+
import { useNuxtApp } from 'nuxt/app'
5+
import { useRouter } from 'vue-router'
6+
import type { I18nPublicRuntimeConfig } from '#internal-i18n-types'
7+
8+
type ResolverParams = Parameters<import('#vue-router').Router['resolve']>
9+
10+
export default defineNuxtPlugin({
11+
name: 'i18n:route-resolution-enhancement',
12+
dependsOn: ['i18n:plugin'],
13+
setup() {
14+
const nuxt = useNuxtApp()
15+
const runtimeI18n = nuxt.$config.public.i18n as I18nPublicRuntimeConfig
16+
17+
if (!runtimeI18n.experimental.routeResolutionEnhancement) return
18+
19+
const router = useRouter()
20+
const implicit = runtimeI18n.experimental.routeResolutionEnhancement === 'implicit'
21+
22+
const originalResolve = router.resolve.bind(router)
23+
router.resolve = (
24+
to: ResolverParams[0],
25+
currentLocation: ResolverParams[1],
26+
options?: { locale?: Locale | boolean }
27+
) => {
28+
/**
29+
* disable enhancement
30+
* - explicit mode without `locale`
31+
* - implicit mode with `locale: false`
32+
*/
33+
if ((!implicit && options?.locale == null) || options?.locale === false) {
34+
return originalResolve(to, currentLocation)
35+
}
36+
37+
// resolve to string | undefined
38+
const _locale = (typeof options?.locale === 'string' && options?.locale) || undefined
39+
console.log(_locale)
40+
return resolveRoute(nuxt._nuxtI18n, to, _locale ?? nuxt._nuxtI18n.getLocale())
41+
}
42+
}
43+
})

src/runtime/routing/routing.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -54,21 +54,21 @@ function normalizeRawLocation(route: RouteLocationRaw): RouteLike {
5454
}
5555

5656
/**
57-
* Try resolving route and throw on failure
57+
* Resolve route, throws on failure
5858
*/
59-
function resolveRoute(ctx: ComposableContext, route: RouteLocationRaw, locale: Locale) {
59+
export function resolveRoute(ctx: ComposableContext, route: RouteLocationRaw, locale: Locale) {
6060
const normalized = normalizeRawLocation(route)
61-
const resolved = ctx.router.resolve(ctx.resolveLocalizedRouteObject(normalized, locale))
61+
const resolved = ctx.router.resolve(ctx.resolveLocalizedRouteObject(normalized, locale), undefined, { locale: false })
6262
if (resolved.name) {
6363
return resolved
6464
}
6565

6666
// if unable to resolve route try resolving route based on original input
67-
return ctx.router.resolve(route)
67+
return ctx.router.resolve(route, undefined, { locale: false })
6868
}
6969

7070
/**
71-
* Try resolving route and return undefined on failure
71+
* Resolve route, returns undefined on failure
7272
*/
7373
function tryResolveRoute(ctx: ComposableContext, route: RouteLocationRaw, locale: Locale = ctx.getLocale()) {
7474
try {

src/types.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,16 @@ export interface ExperimentalFeatures {
8686
* @default true
8787
*/
8888
alternateLinkCanonicalQueries?: boolean
89+
90+
/**
91+
* Enhance Vue Router's route resolution with localization
92+
*
93+
* @defaultValue `false`
94+
*
95+
* @remark `'explicit'` - resolve localized routes when passing `{ locale: Locale | true }` as third argument to router.resolve.
96+
* @remark `'implicit'` - resolve localized routes by default
97+
*/
98+
routeResolutionEnhancement?: false | 'explicit' | 'implicit'
8999
}
90100

91101
export interface BundleOptions

0 commit comments

Comments
 (0)