@bleedingdev/modern-js-plugin-i18n 3.2.0-ultramodern.120 → 3.2.0-ultramodern.121
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/runtime/Link.js +33 -21
- package/dist/cjs/runtime/i18n/backend/defaults.node.js +42 -8
- package/dist/cjs/runtime/localizedPaths.js +1 -4
- package/dist/cjs/runtime/routerAdapter.js +2 -2
- package/dist/cjs/runtime/utils.js +2 -9
- package/dist/cjs/server/index.js +1 -9
- package/dist/cjs/shared/localisedUrls.js +107 -27
- package/dist/esm/runtime/Link.mjs +33 -21
- package/dist/esm/runtime/i18n/backend/defaults.node.mjs +24 -3
- package/dist/esm/runtime/localizedPaths.mjs +2 -5
- package/dist/esm/runtime/routerAdapter.mjs +3 -3
- package/dist/esm/runtime/utils.mjs +3 -10
- package/dist/esm/server/index.mjs +2 -10
- package/dist/esm/shared/localisedUrls.mjs +99 -28
- package/dist/esm-node/runtime/Link.mjs +33 -21
- package/dist/esm-node/runtime/i18n/backend/defaults.node.mjs +24 -3
- package/dist/esm-node/runtime/localizedPaths.mjs +2 -5
- package/dist/esm-node/runtime/routerAdapter.mjs +3 -3
- package/dist/esm-node/runtime/utils.mjs +3 -10
- package/dist/esm-node/server/index.mjs +2 -10
- package/dist/esm-node/shared/localisedUrls.mjs +99 -28
- package/dist/types/runtime/Link.d.ts +10 -0
- package/dist/types/runtime/i18n/backend/defaults.node.d.ts +3 -2
- package/dist/types/runtime/utils.d.ts +2 -2
- package/dist/types/shared/localisedUrls.d.ts +15 -0
- package/dist/types/shared/type.d.ts +7 -5
- package/package.json +16 -12
- package/rstest.config.mts +6 -1
- package/src/runtime/Link.tsx +28 -12
- package/src/runtime/i18n/backend/defaults.node.ts +40 -2
- package/src/runtime/localizedPaths.ts +6 -17
- package/src/runtime/routerAdapter.tsx +4 -5
- package/src/runtime/utils.ts +11 -23
- package/src/server/index.ts +7 -17
- package/src/shared/localisedUrls.ts +212 -42
- package/src/shared/type.ts +7 -5
- package/tests/backendDefaults.test.ts +51 -0
- package/tests/i18nUtils.test.ts +10 -3
- package/tests/link.test.tsx +51 -1
- package/tests/localisedUrls.test.ts +224 -0
- package/tests/routerAdapter.test.tsx +12 -8
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
export declare const resolveDefaultLocalesDir: (cwd?: string) => string;
|
|
1
2
|
export declare const DEFAULT_I18NEXT_BACKEND_OPTIONS: {
|
|
2
|
-
loadPath: string;
|
|
3
|
-
addPath: string;
|
|
3
|
+
readonly loadPath: string;
|
|
4
|
+
readonly addPath: string;
|
|
4
5
|
};
|
|
5
6
|
export declare function convertBackendOptions<T extends {
|
|
6
7
|
loadPath?: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type TInternalRuntimeContext } from '@modern-js/runtime/context';
|
|
2
|
-
import type {
|
|
2
|
+
import type { LocalisedUrlsOption } from '../shared/localisedUrls';
|
|
3
3
|
export declare const getPathname: (context: TInternalRuntimeContext) => string;
|
|
4
4
|
export declare const getEntryPath: () => string;
|
|
5
5
|
/**
|
|
@@ -26,7 +26,7 @@ export declare const splitUrlTarget: (target: string) => {
|
|
|
26
26
|
* @param languages - Array of supported languages
|
|
27
27
|
* @returns The localized URL path with search and hash re-appended verbatim
|
|
28
28
|
*/
|
|
29
|
-
export declare const buildLocalizedUrl: (target: string, language: string, languages: string[], localisedUrls?:
|
|
29
|
+
export declare const buildLocalizedUrl: (target: string, language: string, languages: string[], localisedUrls?: LocalisedUrlsOption) => string;
|
|
30
30
|
export declare const detectLanguageFromPath: (pathname: string, languages: string[], localePathRedirect: boolean) => {
|
|
31
31
|
detected: boolean;
|
|
32
32
|
language?: string;
|
|
@@ -7,6 +7,19 @@ export interface ResolvedLocalisedUrlsConfig {
|
|
|
7
7
|
map: LocalisedUrlsMap;
|
|
8
8
|
}
|
|
9
9
|
export declare const normalisePathPattern: (path: string) => string;
|
|
10
|
+
/**
|
|
11
|
+
* Normalise a concrete request pathname: slash cleanup only. Unlike
|
|
12
|
+
* {@link normalisePathPattern} it must not rewrite literal `[x]` segments to
|
|
13
|
+
* `:x` params — pathnames are values, not patterns.
|
|
14
|
+
*/
|
|
15
|
+
export declare const normalisePathname: (pathname: string) => string;
|
|
16
|
+
/**
|
|
17
|
+
* Localised URLs are strictly opt-in: only an explicit, non-empty map enables
|
|
18
|
+
* route expansion and validation. `true`, `false`, an empty map and absence
|
|
19
|
+
* all resolve to disabled, so upstream-style configs (`localePathRedirect` +
|
|
20
|
+
* `languages` without a map) keep plain locale-prefix behavior instead of
|
|
21
|
+
* failing the build for every route missing from a map they never wrote.
|
|
22
|
+
*/
|
|
10
23
|
export declare const resolveLocalisedUrlsConfig: (option: LocalisedUrlsOption | undefined) => ResolvedLocalisedUrlsConfig;
|
|
11
24
|
export declare const validateLocalisedUrls: (routes: (NestedRouteForCli | PageRoute)[], languages: string[], localisedUrls: LocalisedUrlsMap) => void;
|
|
12
25
|
export declare const applyLocalisedUrlsToRoutes: (routes: (NestedRouteForCli | PageRoute)[], languages: string[], localisedUrls: LocalisedUrlsMap) => (NestedRouteForCli | PageRoute)[];
|
|
@@ -19,3 +32,5 @@ export declare const resolveLocalisedPath: (pathname: string, targetLanguage: st
|
|
|
19
32
|
* against every language variant and rebuilt from the canonical map key.
|
|
20
33
|
*/
|
|
21
34
|
export declare const resolveCanonicalLocalisedPath: (pathname: string, languages: string[], localisedUrls: LocalisedUrlsMap) => string;
|
|
35
|
+
export declare const localiseTargetPathname: (pathname: string, language: string, languages: string[], localisedUrls?: LocalisedUrlsOption) => string;
|
|
36
|
+
export declare const canonicalTargetPathname: (pathname: string, languages: string[], localisedUrls?: LocalisedUrlsOption) => string;
|
|
@@ -10,12 +10,14 @@ export interface BaseLocaleDetectionOptions {
|
|
|
10
10
|
/**
|
|
11
11
|
* Enables localised pathnames in addition to the locale prefix.
|
|
12
12
|
*
|
|
13
|
-
* -
|
|
14
|
-
*
|
|
13
|
+
* - non-empty object: map canonical route paths to every configured
|
|
14
|
+
* language; route generation then validates that every localisable route
|
|
15
|
+
* path has entries for all configured languages.
|
|
16
|
+
* - absent / `false` / `true` / empty object: keep only locale-prefix
|
|
17
|
+
* behavior (`/en/about`).
|
|
15
18
|
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
* configured languages.
|
|
19
|
+
* Strictly opt-in: without a map, `localePathRedirect` + `languages` behave
|
|
20
|
+
* exactly like upstream Modern.js.
|
|
19
21
|
*/
|
|
20
22
|
localisedUrls?: LocalisedUrlsOption;
|
|
21
23
|
}
|
package/package.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"modern",
|
|
18
18
|
"modern.js"
|
|
19
19
|
],
|
|
20
|
-
"version": "3.2.0-ultramodern.
|
|
20
|
+
"version": "3.2.0-ultramodern.121",
|
|
21
21
|
"engines": {
|
|
22
22
|
"node": ">=20"
|
|
23
23
|
},
|
|
@@ -87,23 +87,26 @@
|
|
|
87
87
|
"i18next-fs-backend": "^2.6.6",
|
|
88
88
|
"i18next-http-backend": "^4.0.0",
|
|
89
89
|
"i18next-http-middleware": "^3.9.7",
|
|
90
|
-
"
|
|
91
|
-
"@modern-js/
|
|
92
|
-
"@modern-js/server-runtime": "npm:@bleedingdev/modern-js-server-runtime@3.2.0-ultramodern.
|
|
93
|
-
"@modern-js/
|
|
94
|
-
"@modern-js/
|
|
95
|
-
"@modern-js/
|
|
96
|
-
"@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.2.0-ultramodern.120"
|
|
90
|
+
"@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.2.0-ultramodern.121",
|
|
91
|
+
"@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.2.0-ultramodern.121",
|
|
92
|
+
"@modern-js/server-runtime": "npm:@bleedingdev/modern-js-server-runtime@3.2.0-ultramodern.121",
|
|
93
|
+
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.2.0-ultramodern.121",
|
|
94
|
+
"@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.2.0-ultramodern.121",
|
|
95
|
+
"@modern-js/types": "npm:@bleedingdev/modern-js-types@3.2.0-ultramodern.121"
|
|
97
96
|
},
|
|
98
97
|
"peerDependencies": {
|
|
99
|
-
"@modern-js/runtime": "3.2.0-ultramodern.
|
|
98
|
+
"@modern-js/runtime": "3.2.0-ultramodern.121",
|
|
100
99
|
"i18next": ">=25.7.4",
|
|
101
100
|
"react": "^19.2.7",
|
|
102
|
-
"react-dom": "^19.2.7"
|
|
101
|
+
"react-dom": "^19.2.7",
|
|
102
|
+
"react-i18next": "^17.0.0"
|
|
103
103
|
},
|
|
104
104
|
"peerDependenciesMeta": {
|
|
105
105
|
"i18next": {
|
|
106
106
|
"optional": true
|
|
107
|
+
},
|
|
108
|
+
"react-i18next": {
|
|
109
|
+
"optional": true
|
|
107
110
|
}
|
|
108
111
|
},
|
|
109
112
|
"devDependencies": {
|
|
@@ -115,9 +118,10 @@
|
|
|
115
118
|
"jest": "^30.4.2",
|
|
116
119
|
"react": "^19.2.7",
|
|
117
120
|
"react-dom": "^19.2.7",
|
|
121
|
+
"react-i18next": "17.0.8",
|
|
118
122
|
"ts-jest": "^29.4.11",
|
|
119
|
-
"@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.2.0-ultramodern.
|
|
120
|
-
"@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.2.0-ultramodern.
|
|
123
|
+
"@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.2.0-ultramodern.121",
|
|
124
|
+
"@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.2.0-ultramodern.121"
|
|
121
125
|
},
|
|
122
126
|
"sideEffects": false,
|
|
123
127
|
"publishConfig": {
|
package/rstest.config.mts
CHANGED
|
@@ -26,7 +26,12 @@ export default {
|
|
|
26
26
|
withTestPreset({
|
|
27
27
|
name: 'plugin-i18n-node',
|
|
28
28
|
testEnvironment: 'node',
|
|
29
|
-
include: [
|
|
29
|
+
include: [
|
|
30
|
+
'tests/i18nUtils.test.ts',
|
|
31
|
+
'tests/localisedUrls.test.ts',
|
|
32
|
+
'tests/linkTypes.test.ts',
|
|
33
|
+
'tests/backendDefaults.test.ts',
|
|
34
|
+
],
|
|
30
35
|
extends: commonConfig,
|
|
31
36
|
}),
|
|
32
37
|
withTestPreset({
|
package/src/runtime/Link.tsx
CHANGED
|
@@ -119,7 +119,17 @@ export interface LinkBaseProps extends AnchorRest {
|
|
|
119
119
|
search?: string | Record<string, unknown>;
|
|
120
120
|
hashScrollIntoView?: boolean | ScrollIntoViewOptions;
|
|
121
121
|
replace?: boolean;
|
|
122
|
+
/**
|
|
123
|
+
* Prefetching behavior, forwarded to the underlying router link:
|
|
124
|
+
* react-router gets it verbatim (Modern.js `PrefetchLink` supports it),
|
|
125
|
+
* TanStack receives it as its native `preload` prop (`'none'` -> `false`).
|
|
126
|
+
* Stripped from plain `<a>` fallbacks (external / no-router targets).
|
|
127
|
+
*/
|
|
122
128
|
prefetch?: 'intent' | 'render' | 'viewport' | 'none';
|
|
129
|
+
/**
|
|
130
|
+
* Native preload value of the underlying router link. When set, it wins
|
|
131
|
+
* over `prefetch` on the TanStack branch.
|
|
132
|
+
*/
|
|
123
133
|
preload?: unknown;
|
|
124
134
|
activeOptions?: LinkActiveOptions;
|
|
125
135
|
/** Extra anchor props applied when the link is active. */
|
|
@@ -215,6 +225,8 @@ export const Link = <TTo extends string = string>(
|
|
|
215
225
|
hashScrollIntoView,
|
|
216
226
|
activeOptions,
|
|
217
227
|
activeProps,
|
|
228
|
+
prefetch,
|
|
229
|
+
preload,
|
|
218
230
|
...rest
|
|
219
231
|
} = props as LinkBaseProps & { to: string; params?: LinkParams };
|
|
220
232
|
|
|
@@ -320,12 +332,7 @@ export const Link = <TTo extends string = string>(
|
|
|
320
332
|
|
|
321
333
|
// External targets and same-page anchors are vanilla links.
|
|
322
334
|
if (!target) {
|
|
323
|
-
const {
|
|
324
|
-
prefetch: _prefetch,
|
|
325
|
-
preload: _preload,
|
|
326
|
-
replace: _replace,
|
|
327
|
-
...anchorProps
|
|
328
|
-
} = rest;
|
|
335
|
+
const { replace: _replace, ...anchorProps } = rest;
|
|
329
336
|
|
|
330
337
|
return (
|
|
331
338
|
<a href={to} {...anchorProps}>
|
|
@@ -337,12 +344,7 @@ export const Link = <TTo extends string = string>(
|
|
|
337
344
|
const { Link: RouterLink, hasRouter, framework } = adapter;
|
|
338
345
|
|
|
339
346
|
if (!hasRouter || !RouterLink) {
|
|
340
|
-
const {
|
|
341
|
-
prefetch: _prefetch,
|
|
342
|
-
preload: _preload,
|
|
343
|
-
replace: _replace,
|
|
344
|
-
...anchorProps
|
|
345
|
-
} = rest;
|
|
347
|
+
const { replace: _replace, ...anchorProps } = rest;
|
|
346
348
|
const {
|
|
347
349
|
className: activeClassName,
|
|
348
350
|
style: activeStyle,
|
|
@@ -378,6 +380,17 @@ export const Link = <TTo extends string = string>(
|
|
|
378
380
|
};
|
|
379
381
|
|
|
380
382
|
if (framework === 'tanstack') {
|
|
383
|
+
// TanStack's prop is `preload`; map our react-router-flavored `prefetch`
|
|
384
|
+
// onto it (`'none'` -> `false`). An explicit native `preload` wins.
|
|
385
|
+
const tanstackPreload =
|
|
386
|
+
preload !== undefined
|
|
387
|
+
? preload
|
|
388
|
+
: prefetch === undefined
|
|
389
|
+
? undefined
|
|
390
|
+
: prefetch === 'none'
|
|
391
|
+
? false
|
|
392
|
+
: prefetch;
|
|
393
|
+
|
|
381
394
|
// Pass hash/search natively: string-concatenated targets silently break
|
|
382
395
|
// TanStack navigation.
|
|
383
396
|
return (
|
|
@@ -386,6 +399,7 @@ export const Link = <TTo extends string = string>(
|
|
|
386
399
|
{...(target.searchObject ? { search: target.searchObject } : {})}
|
|
387
400
|
{...(target.hash ? { hash: target.hash } : {})}
|
|
388
401
|
{...(hashScrollIntoView === undefined ? {} : { hashScrollIntoView })}
|
|
402
|
+
{...(tanstackPreload === undefined ? {} : { preload: tanstackPreload })}
|
|
389
403
|
{...rest}
|
|
390
404
|
{...activeRest}
|
|
391
405
|
{...activeAttributes}
|
|
@@ -400,6 +414,8 @@ export const Link = <TTo extends string = string>(
|
|
|
400
414
|
return (
|
|
401
415
|
<RouterLink
|
|
402
416
|
to={target.href}
|
|
417
|
+
{...(prefetch === undefined ? {} : { prefetch })}
|
|
418
|
+
{...(preload === undefined ? {} : { preload })}
|
|
403
419
|
{...rest}
|
|
404
420
|
{...activeRest}
|
|
405
421
|
{...activeAttributes}
|
|
@@ -1,6 +1,44 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import nodePath from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Conventional locales roots, in the same priority order as the CLI plugin's
|
|
6
|
+
* `detectLocalesDirectory` auto-detection (project-root `./locales` first —
|
|
7
|
+
* the upstream convention — then the scaffold's `./config/public/locales`).
|
|
8
|
+
* The fs-backend default must read from the same directory whose existence
|
|
9
|
+
* enabled the backend in the first place.
|
|
10
|
+
*/
|
|
11
|
+
const CONVENTIONAL_LOCALES_DIRS = [
|
|
12
|
+
'./locales',
|
|
13
|
+
'./config/public/locales',
|
|
14
|
+
] as const;
|
|
15
|
+
|
|
16
|
+
const isDirectory = (dirPath: string): boolean => {
|
|
17
|
+
try {
|
|
18
|
+
return fs.statSync(dirPath).isDirectory();
|
|
19
|
+
} catch {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
export const resolveDefaultLocalesDir = (
|
|
25
|
+
cwd: string = process.cwd(),
|
|
26
|
+
): string => {
|
|
27
|
+
for (const dir of CONVENTIONAL_LOCALES_DIRS) {
|
|
28
|
+
if (isDirectory(nodePath.resolve(cwd, dir))) {
|
|
29
|
+
return dir;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return CONVENTIONAL_LOCALES_DIRS[0];
|
|
33
|
+
};
|
|
34
|
+
|
|
1
35
|
export const DEFAULT_I18NEXT_BACKEND_OPTIONS = {
|
|
2
|
-
loadPath:
|
|
3
|
-
|
|
36
|
+
get loadPath(): string {
|
|
37
|
+
return `${resolveDefaultLocalesDir()}/{{lng}}/{{ns}}.json`;
|
|
38
|
+
},
|
|
39
|
+
get addPath(): string {
|
|
40
|
+
return `${resolveDefaultLocalesDir()}/{{lng}}/{{ns}}.json`;
|
|
41
|
+
},
|
|
4
42
|
};
|
|
5
43
|
|
|
6
44
|
function convertPath(path: string | undefined): string | undefined {
|
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
import { useMemo } from 'react';
|
|
2
2
|
import type { LocalisedUrlsOption } from '../shared/localisedUrls';
|
|
3
|
-
import {
|
|
4
|
-
resolveCanonicalLocalisedPath,
|
|
5
|
-
resolveLocalisedUrlsConfig,
|
|
6
|
-
} from '../shared/localisedUrls';
|
|
3
|
+
import { canonicalTargetPathname } from '../shared/localisedUrls';
|
|
7
4
|
import { useModernI18n } from './context';
|
|
8
5
|
import { useI18nRouterAdapter } from './routerAdapter';
|
|
9
6
|
import { buildLocalizedUrl, splitUrlTarget } from './utils';
|
|
@@ -35,19 +32,11 @@ export const canonicalPath = (
|
|
|
35
32
|
config: LocalizedPathsConfig,
|
|
36
33
|
): string => {
|
|
37
34
|
const { pathname, search, hash } = splitUrlTarget(target);
|
|
38
|
-
const
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const localisedUrlsConfig = resolveLocalisedUrlsConfig(config.localisedUrls);
|
|
44
|
-
const resolvedPath = localisedUrlsConfig.enabled
|
|
45
|
-
? resolveCanonicalLocalisedPath(
|
|
46
|
-
pathWithoutLanguage,
|
|
47
|
-
config.languages,
|
|
48
|
-
localisedUrlsConfig.map,
|
|
49
|
-
)
|
|
50
|
-
: pathWithoutLanguage;
|
|
35
|
+
const resolvedPath = canonicalTargetPathname(
|
|
36
|
+
pathname,
|
|
37
|
+
config.languages,
|
|
38
|
+
config.localisedUrls,
|
|
39
|
+
);
|
|
51
40
|
|
|
52
41
|
return `${resolvedPath}${search}${hash}`;
|
|
53
42
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { isBrowser, RuntimeContext } from '@modern-js/runtime';
|
|
2
2
|
import {
|
|
3
|
+
getRouterRuntimeState,
|
|
3
4
|
InternalRuntimeContext,
|
|
4
5
|
type TInternalRuntimeContext,
|
|
5
6
|
type TRuntimeContext,
|
|
@@ -131,9 +132,8 @@ const getRouterFramework = (
|
|
|
131
132
|
inReactRouter: boolean,
|
|
132
133
|
): I18nRouterFramework | undefined => {
|
|
133
134
|
const framework =
|
|
134
|
-
internalContext
|
|
135
|
-
|
|
136
|
-
runtimeContext.routerFramework;
|
|
135
|
+
getRouterRuntimeState(internalContext)?.framework ||
|
|
136
|
+
getRouterRuntimeState(runtimeContext)?.framework;
|
|
137
137
|
|
|
138
138
|
if (framework) {
|
|
139
139
|
return framework;
|
|
@@ -167,8 +167,7 @@ const getRouterInstance = (
|
|
|
167
167
|
return contextRouter;
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
-
const router =
|
|
171
|
-
internalContext.routerInstance || internalContext.routerRuntime?.instance;
|
|
170
|
+
const router = getRouterRuntimeState(internalContext)?.instance;
|
|
172
171
|
if (!router || typeof router !== 'object') {
|
|
173
172
|
return null;
|
|
174
173
|
}
|
package/src/runtime/utils.ts
CHANGED
|
@@ -3,11 +3,8 @@ import {
|
|
|
3
3
|
getGlobalBasename,
|
|
4
4
|
type TInternalRuntimeContext,
|
|
5
5
|
} from '@modern-js/runtime/context';
|
|
6
|
-
import type {
|
|
7
|
-
import {
|
|
8
|
-
resolveLocalisedPath,
|
|
9
|
-
resolveLocalisedUrlsConfig,
|
|
10
|
-
} from '../shared/localisedUrls';
|
|
6
|
+
import type { LocalisedUrlsOption } from '../shared/localisedUrls';
|
|
7
|
+
import { localiseTargetPathname } from '../shared/localisedUrls';
|
|
11
8
|
|
|
12
9
|
export const getPathname = (context: TInternalRuntimeContext): string => {
|
|
13
10
|
if (isBrowser()) {
|
|
@@ -74,26 +71,17 @@ export const buildLocalizedUrl = (
|
|
|
74
71
|
target: string,
|
|
75
72
|
language: string,
|
|
76
73
|
languages: string[],
|
|
77
|
-
localisedUrls?:
|
|
74
|
+
localisedUrls?: LocalisedUrlsOption,
|
|
78
75
|
): string => {
|
|
79
76
|
const { pathname, search, hash } = splitUrlTarget(target);
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
pathWithoutLanguage,
|
|
89
|
-
language,
|
|
90
|
-
languages,
|
|
91
|
-
localisedUrlsConfig.map,
|
|
92
|
-
)
|
|
93
|
-
: pathWithoutLanguage;
|
|
94
|
-
const resolvedSegments = resolvedPath.split('/').filter(Boolean);
|
|
95
|
-
|
|
96
|
-
return `/${[language, ...resolvedSegments].join('/')}${search}${hash}`;
|
|
77
|
+
const localizedPathname = localiseTargetPathname(
|
|
78
|
+
pathname,
|
|
79
|
+
language,
|
|
80
|
+
languages,
|
|
81
|
+
localisedUrls,
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
return `${localizedPathname}${search}${hash}`;
|
|
97
85
|
};
|
|
98
86
|
|
|
99
87
|
export const detectLanguageFromPath = (
|
package/src/server/index.ts
CHANGED
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
} from '../runtime/i18n/detection/config.js';
|
|
10
10
|
import type { LanguageDetectorOptions } from '../runtime/i18n/instance';
|
|
11
11
|
import {
|
|
12
|
-
|
|
12
|
+
localiseTargetPathname,
|
|
13
13
|
resolveLocalisedUrlsConfig,
|
|
14
14
|
} from '../shared/localisedUrls.js';
|
|
15
15
|
import type { LocaleDetectionOptions } from '../shared/type';
|
|
@@ -314,22 +314,12 @@ const buildLocalizedUrl = (
|
|
|
314
314
|
? pathname.slice(basePath.length)
|
|
315
315
|
: pathname;
|
|
316
316
|
|
|
317
|
-
const
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
const resolvedPath = localisedUrlsConfig.enabled
|
|
324
|
-
? resolveLocalisedPath(
|
|
325
|
-
pathWithoutLanguage,
|
|
326
|
-
language,
|
|
327
|
-
languages,
|
|
328
|
-
localisedUrlsConfig.map,
|
|
329
|
-
)
|
|
330
|
-
: pathWithoutLanguage;
|
|
331
|
-
const resolvedSegments = resolvedPath.split('/').filter(Boolean);
|
|
332
|
-
const newPathname = `/${[language, ...resolvedSegments].join('/')}`;
|
|
317
|
+
const newPathname = localiseTargetPathname(
|
|
318
|
+
remainingPath,
|
|
319
|
+
language,
|
|
320
|
+
languages,
|
|
321
|
+
localisedUrls,
|
|
322
|
+
);
|
|
333
323
|
// Handle root path case to avoid double slashes like //en
|
|
334
324
|
const suffix = `${url.search}${url.hash}`;
|
|
335
325
|
const localizedUrl =
|