Skip to content

Commit 4f4ad4e

Browse files
authored
fix: preserve relative child paths during route localization (#3783)
1 parent 05c40a7 commit 4f4ad4e

2 files changed

Lines changed: 111 additions & 6 deletions

File tree

src/kit/gen.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -43,12 +43,20 @@ export type LocalizeRouteParams = {
4343

4444
function handlePathNesting(localizedPath: string, parentLocalizedPath: string = '') {
4545
if (!parentLocalizedPath) return localizedPath
46-
// handle parent/index
47-
const path = localizedPath.replace(parentLocalizedPath + '/', '')
48-
if (path === parentLocalizedPath) {
49-
return localizedPath === parentLocalizedPath ? '' : localizedPath
46+
47+
if (localizedPath[0] !== '/') {
48+
return localizedPath
49+
}
50+
51+
const index = localizedPath.indexOf(parentLocalizedPath)
52+
if (index >= 0) {
53+
// parent path is found, slice from index + parent length + slash, for example:
54+
// * parent='/foo', child='/foo/bar' => 'bar'
55+
// * parent='bar', child='/foo/bar/:id()' => ':id()'
56+
return localizedPath.slice(localizedPath.indexOf(parentLocalizedPath) + parentLocalizedPath.length + 1)
5057
}
51-
return path
58+
59+
return localizedPath
5260
}
5361

5462
function createHandleTrailingSlash(ctx: RouteContext): RouteContext['handleTrailingSlash'] {
@@ -143,7 +151,7 @@ export type RouteContext = {
143151
locale: string,
144152
opts: LocalizeRouteParams
145153
) => LocalizableRoute[]
146-
localizeRouteName: (name: LocalizableRoute, locale: string, isDefault: boolean) => string
154+
localizeRouteName: (name: LocalizableRoute, locale: string, isDefault: boolean) => string | undefined
147155
handleTrailingSlash: (localizedPath: string, hasParent: boolean) => string
148156
localizers: { enabled: (data: LocalizerData) => boolean; localizer: LocalizerFn }[]
149157
}
@@ -167,6 +175,7 @@ function createLocalizeRouteName(opts: {
167175
const separator = opts.routesNameSeparator || '___'
168176
const defaultSuffix = opts.defaultLocaleRouteNameSuffix || 'default'
169177
return (route, locale, isDefault) => {
178+
if (route.name == null) return
170179
// prettier-ignore
171180
return !isDefault
172181
? route.name + separator + locale

test/pages/custom_route.test.ts

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -299,6 +299,102 @@ test('#1649', () => {
299299
expect(localizedPages).toMatchSnapshot()
300300
})
301301

302+
test('#3781', () => {
303+
const pages = [
304+
{
305+
name: 'index',
306+
path: '/',
307+
file: '/path/to/3781/pages/index.vue',
308+
children: []
309+
},
310+
{
311+
path: '/customer',
312+
file: '/path/to/3781/pages/customer.vue',
313+
children: [
314+
{
315+
path: 'my-orders',
316+
file: '/path/to/3781/pages/customer/my-orders.vue',
317+
children: [
318+
{
319+
name: 'customer-my-orders',
320+
path: 'index',
321+
file: '/path/to/3781/pages/customer/my-orders/index.vue',
322+
children: []
323+
},
324+
{
325+
name: 'customer-my-orders-id',
326+
path: ':id()',
327+
file: '/path/to/3781/pages/customer/my-orders/[id].vue',
328+
children: []
329+
}
330+
]
331+
}
332+
]
333+
}
334+
]
335+
336+
const options = getNuxtOptions({
337+
customer: {
338+
en: '/customer-account'
339+
},
340+
'customer-my-orders': {
341+
en: '/customer-account/my-orders'
342+
},
343+
'customer-my-orders-id': {
344+
en: '/customer-account/my-orders/[id]'
345+
}
346+
})
347+
348+
options.locales = [{ code: 'en' }]
349+
options.defaultLocale = 'en'
350+
options.customRoutes = 'config'
351+
352+
vi.spyOn(fs, 'readFileSync').mockReturnValue('')
353+
354+
const ctx = new NuxtPageAnalyzeContext(options.pages)
355+
analyzeNuxtPages(ctx, 'pages', pages)
356+
const localizedPages = localizeRoutes(pages, {
357+
...options,
358+
includeUnprefixedFallback: false,
359+
optionsResolver: getRouteOptionsResolver(ctx, options.defaultLocale!, options.customRoutes!)
360+
} as Parameters<typeof localizeRoutes>[1])
361+
362+
expect(localizedPages).toMatchInlineSnapshot(`
363+
[
364+
{
365+
"children": [],
366+
"file": "/path/to/3781/pages/index.vue",
367+
"name": "index___en",
368+
"path": "/",
369+
},
370+
{
371+
"children": [
372+
{
373+
"children": [
374+
{
375+
"children": [],
376+
"file": "/path/to/3781/pages/customer/my-orders/index.vue",
377+
"name": "customer-my-orders___en",
378+
"path": "",
379+
},
380+
{
381+
"children": [],
382+
"file": "/path/to/3781/pages/customer/my-orders/[id].vue",
383+
"name": "customer-my-orders-id___en",
384+
"path": ":id()",
385+
},
386+
],
387+
"file": "/path/to/3781/pages/customer/my-orders.vue",
388+
"path": "my-orders",
389+
},
390+
],
391+
"file": "/path/to/3781/pages/customer.vue",
392+
"path": "/customer-account",
393+
},
394+
]
395+
`)
396+
})
397+
302398
test('pages config using route name', () => {
303399
const pages = [
304400
{

0 commit comments

Comments
 (0)