@bleedingdev/modern-js-i18n-runtime-extensions 3.9.0-ultramodern.6 → 3.9.0-ultramodern.7
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/index.js
CHANGED
|
@@ -9,7 +9,7 @@ var __webpack_modules__ = {
|
|
|
9
9
|
"./localeRedirectPolicy" (module) {
|
|
10
10
|
module.exports = require("./localeRedirectPolicy.js");
|
|
11
11
|
},
|
|
12
|
-
"./localisedUrls" (module) {
|
|
12
|
+
"./localisedUrls/index" (module) {
|
|
13
13
|
module.exports = require("./localisedUrls/index.js");
|
|
14
14
|
}
|
|
15
15
|
};
|
|
@@ -72,7 +72,7 @@ var __webpack_exports__ = {};
|
|
|
72
72
|
var __rspack_reexport = {};
|
|
73
73
|
for(const __rspack_import_key in _localeRedirectPolicy__rspack_import_2)if ("default" !== __rspack_import_key) __rspack_reexport[__rspack_import_key] = ()=>_localeRedirectPolicy__rspack_import_2[__rspack_import_key];
|
|
74
74
|
__webpack_require__.d(__webpack_exports__, __rspack_reexport);
|
|
75
|
-
var _localisedUrls_index__rspack_import_3 = __webpack_require__("./localisedUrls");
|
|
75
|
+
var _localisedUrls_index__rspack_import_3 = __webpack_require__("./localisedUrls/index");
|
|
76
76
|
var __rspack_reexport = {};
|
|
77
77
|
for(const __rspack_import_key in _localisedUrls_index__rspack_import_3)if ("default" !== __rspack_import_key) __rspack_reexport[__rspack_import_key] = ()=>_localisedUrls_index__rspack_import_3[__rspack_import_key];
|
|
78
78
|
__webpack_require__.d(__webpack_exports__, __rspack_reexport);
|
package/package.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"modern.js",
|
|
18
18
|
"i18n"
|
|
19
19
|
],
|
|
20
|
-
"version": "3.9.0-ultramodern.
|
|
20
|
+
"version": "3.9.0-ultramodern.7",
|
|
21
21
|
"types": "./dist/types/index.d.ts",
|
|
22
22
|
"main": "./dist/cjs/index.js",
|
|
23
23
|
"module": "./dist/esm/index.mjs",
|
|
@@ -55,15 +55,15 @@
|
|
|
55
55
|
"node": ">=26.7.0"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.9.0-ultramodern.
|
|
58
|
+
"@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.9.0-ultramodern.7",
|
|
59
59
|
"i18next-browser-languagedetector": "^8.2.1"
|
|
60
60
|
},
|
|
61
61
|
"peerDependencies": {
|
|
62
|
-
"@modern-js/runtime": "3.9.0-ultramodern.
|
|
62
|
+
"@modern-js/runtime": "3.9.0-ultramodern.7",
|
|
63
63
|
"react": "^19.2.8"
|
|
64
64
|
},
|
|
65
65
|
"devDependencies": {
|
|
66
|
-
"@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.9.0-ultramodern.
|
|
66
|
+
"@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.9.0-ultramodern.7",
|
|
67
67
|
"@rslib/core": "1.0.0",
|
|
68
68
|
"@scripts/rstest-config": "2.66.0",
|
|
69
69
|
"@types/node": "^26.4.1",
|
|
@@ -80,7 +80,6 @@ describe('latest language synchronization coordinator', () => {
|
|
|
80
80
|
|
|
81
81
|
binding.request('cs');
|
|
82
82
|
binding.request('de');
|
|
83
|
-
expect(pending.map(change => change.language)).toEqual(['cs', 'de']);
|
|
84
83
|
|
|
85
84
|
pending[1].resolve();
|
|
86
85
|
await pending[1].promise;
|
|
@@ -91,7 +90,6 @@ describe('latest language synchronization coordinator', () => {
|
|
|
91
90
|
await pending[0].promise;
|
|
92
91
|
await flush();
|
|
93
92
|
expect(target.language).toBe('cs');
|
|
94
|
-
expect(pending.map(change => change.language)).toEqual(['cs', 'de', 'de']);
|
|
95
93
|
|
|
96
94
|
pending[2].resolve();
|
|
97
95
|
await pending[2].promise;
|
|
@@ -100,81 +98,6 @@ describe('latest language synchronization coordinator', () => {
|
|
|
100
98
|
expect(commits.at(-1)).toBe('de');
|
|
101
99
|
});
|
|
102
100
|
|
|
103
|
-
test('repairs a shared singleton after the starting mount leaves', async () => {
|
|
104
|
-
const target = { language: 'en' };
|
|
105
|
-
const { changeLanguage, pending } = deferredChanger(target);
|
|
106
|
-
const commits: string[] = [];
|
|
107
|
-
const first = createLatestLanguageSyncBinding<Target>();
|
|
108
|
-
const second = createLatestLanguageSyncBinding<Target>();
|
|
109
|
-
for (const binding of [first, second]) {
|
|
110
|
-
binding.updateCallbacks({
|
|
111
|
-
changeLanguage,
|
|
112
|
-
commitLanguage: (_target, language) => commits.push(language),
|
|
113
|
-
readLanguage: current => current.language,
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
first.activate(target);
|
|
118
|
-
first.request('cs');
|
|
119
|
-
first.deactivate();
|
|
120
|
-
second.activate(target);
|
|
121
|
-
second.request('de');
|
|
122
|
-
|
|
123
|
-
pending[1].resolve();
|
|
124
|
-
await pending[1].promise;
|
|
125
|
-
await flush();
|
|
126
|
-
pending[0].resolve();
|
|
127
|
-
await pending[0].promise;
|
|
128
|
-
await flush();
|
|
129
|
-
|
|
130
|
-
expect(pending.map(change => change.language)).toEqual(['cs', 'de', 'de']);
|
|
131
|
-
pending[2].resolve();
|
|
132
|
-
await pending[2].promise;
|
|
133
|
-
await flush();
|
|
134
|
-
expect(target.language).toBe('de');
|
|
135
|
-
expect(commits.at(-1)).toBe('de');
|
|
136
|
-
});
|
|
137
|
-
|
|
138
|
-
test('supersedes a hung same-language attempt and repairs it after a later route', async () => {
|
|
139
|
-
rstest.useFakeTimers();
|
|
140
|
-
const target = { language: 'en' };
|
|
141
|
-
const { changeLanguage, pending } = deferredChanger(target);
|
|
142
|
-
const commits: string[] = [];
|
|
143
|
-
const binding = createLatestLanguageSyncBinding<Target>({
|
|
144
|
-
attemptTimeoutMs: 100,
|
|
145
|
-
retryDelayMs: () => 10,
|
|
146
|
-
});
|
|
147
|
-
binding.updateCallbacks({
|
|
148
|
-
changeLanguage,
|
|
149
|
-
commitLanguage: (_target, language) => commits.push(language),
|
|
150
|
-
readLanguage: current => current.language,
|
|
151
|
-
});
|
|
152
|
-
binding.activate(target);
|
|
153
|
-
binding.request('cs');
|
|
154
|
-
|
|
155
|
-
await rstest.advanceTimersByTimeAsync(110);
|
|
156
|
-
expect(pending.map(change => change.language)).toEqual(['cs', 'cs']);
|
|
157
|
-
pending[1].resolve();
|
|
158
|
-
await pending[1].promise;
|
|
159
|
-
await flush();
|
|
160
|
-
expect(commits.at(-1)).toBe('cs');
|
|
161
|
-
|
|
162
|
-
binding.request('de');
|
|
163
|
-
pending[2].resolve();
|
|
164
|
-
await pending[2].promise;
|
|
165
|
-
await flush();
|
|
166
|
-
expect(target.language).toBe('de');
|
|
167
|
-
|
|
168
|
-
pending[0].resolve();
|
|
169
|
-
await pending[0].promise;
|
|
170
|
-
await flush();
|
|
171
|
-
expect(pending.at(-1)?.language).toBe('de');
|
|
172
|
-
pending.at(-1)?.resolve();
|
|
173
|
-
await pending.at(-1)?.promise;
|
|
174
|
-
await flush();
|
|
175
|
-
expect(target.language).toBe('de');
|
|
176
|
-
});
|
|
177
|
-
|
|
178
101
|
test('bounds permanent failures and reports the final consequence once', async () => {
|
|
179
102
|
rstest.useFakeTimers();
|
|
180
103
|
const target = { language: 'en' };
|
|
@@ -83,17 +83,6 @@ describe('locale redirect resource policy', () => {
|
|
|
83
83
|
expect(
|
|
84
84
|
shouldIgnoreRedirect('/store/static/app.js', '/store/*', ignore),
|
|
85
85
|
).toBe(true);
|
|
86
|
-
expect(seen).toEqual(['/health', '/products']);
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
test('preserves startsWith mount removal and empty remaining path behavior', () => {
|
|
90
|
-
expect(shouldIgnoreRedirect('/storefront', '/store/*', ['/front'])).toBe(
|
|
91
|
-
true,
|
|
92
|
-
);
|
|
93
|
-
expect(shouldIgnoreRedirect('/store', '/store/*', ['/'])).toBe(true);
|
|
94
|
-
expect(shouldIgnoreRedirect('/health', '', ['/health'])).toBe(true);
|
|
95
|
-
expect(shouldIgnoreRedirect('/health', '/', ['/health'])).toBe(true);
|
|
96
|
-
expect(shouldIgnoreRedirect('/health', '/other/*', ['/health'])).toBe(true);
|
|
97
86
|
});
|
|
98
87
|
});
|
|
99
88
|
|
|
@@ -121,19 +110,6 @@ describe('locale redirect URL policy', () => {
|
|
|
121
110
|
).toBe('cs');
|
|
122
111
|
});
|
|
123
112
|
|
|
124
|
-
test('retains case-sensitive language detection and does not search later segments', () => {
|
|
125
|
-
for (const pathname of [
|
|
126
|
-
'/store',
|
|
127
|
-
'/store/CS/products',
|
|
128
|
-
'/store/fr/products',
|
|
129
|
-
'/store/products/cs',
|
|
130
|
-
]) {
|
|
131
|
-
expect(
|
|
132
|
-
getLanguageFromPath(request(pathname), '/store/*', languages),
|
|
133
|
-
).toBeNull();
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
|
|
137
113
|
test('switches localized dynamic paths while preserving mount, query and hash', () => {
|
|
138
114
|
expect(
|
|
139
115
|
buildLocalizedUrl(
|
|
@@ -170,25 +146,6 @@ describe('locale redirect URL policy', () => {
|
|
|
170
146
|
).toBe('/store/cs');
|
|
171
147
|
});
|
|
172
148
|
|
|
173
|
-
test('preserves the existing nonmatching and partial mount behavior', () => {
|
|
174
|
-
expect(
|
|
175
|
-
buildLocalizedUrl(
|
|
176
|
-
request('https://example.com/products'),
|
|
177
|
-
'/store/*',
|
|
178
|
-
'cs',
|
|
179
|
-
languages,
|
|
180
|
-
),
|
|
181
|
-
).toBe('/store/cs/products');
|
|
182
|
-
expect(
|
|
183
|
-
buildLocalizedUrl(
|
|
184
|
-
request('https://example.com/storefront'),
|
|
185
|
-
'/store/*',
|
|
186
|
-
'cs',
|
|
187
|
-
languages,
|
|
188
|
-
),
|
|
189
|
-
).toBe('/store/cs/front');
|
|
190
|
-
});
|
|
191
|
-
|
|
192
149
|
test('returns a non-cacheable temporary redirect with language negotiation headers', async () => {
|
|
193
150
|
const response = createLocaleRedirectResponse(
|
|
194
151
|
'/cs/produkty?sort=price#details',
|
|
@@ -7,7 +7,6 @@ import {
|
|
|
7
7
|
matchPathPattern,
|
|
8
8
|
resolveCanonicalLocalisedPath,
|
|
9
9
|
resolveLocalisedPath,
|
|
10
|
-
resolveLocalisedUrlsConfig,
|
|
11
10
|
validateLocalisedUrls,
|
|
12
11
|
} from '../src/localisedUrls/index';
|
|
13
12
|
|
|
@@ -24,19 +23,6 @@ const createRoute = (
|
|
|
24
23
|
children,
|
|
25
24
|
});
|
|
26
25
|
|
|
27
|
-
describe('resolveLocalisedUrlsConfig', () => {
|
|
28
|
-
test('enables the feature only for a non-empty map', () => {
|
|
29
|
-
const map = { '/about': { en: '/about', cs: '/o-nas' } };
|
|
30
|
-
const disabled = { enabled: false, map: {} };
|
|
31
|
-
|
|
32
|
-
expect(resolveLocalisedUrlsConfig(map)).toEqual({ enabled: true, map });
|
|
33
|
-
expect(resolveLocalisedUrlsConfig(undefined)).toEqual(disabled);
|
|
34
|
-
expect(resolveLocalisedUrlsConfig(false)).toEqual(disabled);
|
|
35
|
-
expect(resolveLocalisedUrlsConfig(true)).toEqual(disabled);
|
|
36
|
-
expect(resolveLocalisedUrlsConfig({})).toEqual(disabled);
|
|
37
|
-
});
|
|
38
|
-
});
|
|
39
|
-
|
|
40
26
|
describe('localisedUrls', () => {
|
|
41
27
|
test('leaves framework-internal Module Federation routes unlocalised', () => {
|
|
42
28
|
const internalRoute = createRoute('_mf', [
|
|
@@ -415,23 +401,6 @@ describe('localisedUrls', () => {
|
|
|
415
401
|
).toBe('/shoes/red');
|
|
416
402
|
});
|
|
417
403
|
|
|
418
|
-
test('a specific localised source outranks a broad canonical source', () => {
|
|
419
|
-
const localisedUrls = {
|
|
420
|
-
'/:section/:slug': {
|
|
421
|
-
en: '/generic/:section/:slug',
|
|
422
|
-
cs: '/obecne/:section/:slug',
|
|
423
|
-
},
|
|
424
|
-
'/products/:id': {
|
|
425
|
-
en: '/fixed/:id',
|
|
426
|
-
cs: '/produkty/:id',
|
|
427
|
-
},
|
|
428
|
-
};
|
|
429
|
-
|
|
430
|
-
expect(
|
|
431
|
-
resolveCanonicalLocalisedPath('/fixed/red', ['en', 'cs'], localisedUrls),
|
|
432
|
-
).toBe('/products/red');
|
|
433
|
-
});
|
|
434
|
-
|
|
435
404
|
test('missing required target params never collapse into empty segments', () => {
|
|
436
405
|
expect(() =>
|
|
437
406
|
resolveLocalisedPath('/products/red-shoe', 'cs', ['en', 'cs'], {
|
package/tests/paths.test.ts
CHANGED
|
@@ -1,189 +1,24 @@
|
|
|
1
1
|
import { describe, expect, test } from '@rstest/core';
|
|
2
|
-
import
|
|
3
|
-
import { buildLocalizedUrl, canonicalPath, localizePath } from '../src/paths';
|
|
2
|
+
import { canonicalPath, localizePath } from '../src/paths';
|
|
4
3
|
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
en: '/products/:slug',
|
|
10
|
-
cs: '/produkty/:slug',
|
|
11
|
-
},
|
|
12
|
-
'/files/*': {
|
|
13
|
-
en: '/files/*',
|
|
14
|
-
cs: '/soubory/*',
|
|
4
|
+
const config = {
|
|
5
|
+
languages: ['en', 'cs'],
|
|
6
|
+
localisedUrls: {
|
|
7
|
+
'/products/:slug': { en: '/products/:slug', cs: '/produkty/:slug' },
|
|
15
8
|
},
|
|
16
|
-
'/app/products/:slug': {
|
|
17
|
-
en: '/app/products/:slug',
|
|
18
|
-
cs: '/app/produkty/:slug',
|
|
19
|
-
},
|
|
20
|
-
} satisfies LocalisedUrlsMap;
|
|
21
|
-
|
|
22
|
-
const pathsConfig = {
|
|
23
|
-
languages,
|
|
24
|
-
localisedUrls,
|
|
25
9
|
};
|
|
26
10
|
|
|
27
|
-
describe('
|
|
28
|
-
|
|
29
|
-
{
|
|
30
|
-
name: 'adds the target locale prefix and localises canonical segments',
|
|
31
|
-
target: '/products/red-shoe',
|
|
32
|
-
language: 'cs',
|
|
33
|
-
expected: '/cs/produkty/red-shoe',
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
name: 'strips a case-insensitive source locale before relocalising',
|
|
37
|
-
target: '/CS/produkty/red%20shoe?tag=boots#details',
|
|
38
|
-
language: 'en',
|
|
39
|
-
expected: '/en/products/red%20shoe?tag=boots#details',
|
|
40
|
-
},
|
|
41
|
-
{
|
|
42
|
-
name: 'preserves repeated query keys and the fragment',
|
|
43
|
-
target: '/products/red-shoe?tag=boots&tag=sale#details',
|
|
44
|
-
language: 'cs',
|
|
45
|
-
expected: '/cs/produkty/red-shoe?tag=boots&tag=sale#details',
|
|
46
|
-
},
|
|
47
|
-
{
|
|
48
|
-
name: 'keeps a base-path segment inside the localised route map',
|
|
49
|
-
target: '/app/products/red-shoe?ref=nav#details',
|
|
50
|
-
language: 'cs',
|
|
51
|
-
expected: '/cs/app/produkty/red-shoe?ref=nav#details',
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
name: 'keeps the default locale explicitly prefixed',
|
|
55
|
-
target: '/products/red-shoe',
|
|
56
|
-
language: 'en',
|
|
57
|
-
expected: '/en/products/red-shoe',
|
|
58
|
-
},
|
|
59
|
-
{
|
|
60
|
-
name: 'does not double-prefix an already-localised URL',
|
|
61
|
-
target: '/cs/produkty/red-shoe?tag=boots#details',
|
|
62
|
-
language: 'cs',
|
|
63
|
-
expected: '/cs/produkty/red-shoe?tag=boots#details',
|
|
64
|
-
},
|
|
65
|
-
];
|
|
66
|
-
|
|
67
|
-
for (const scenario of rewriteScenarios) {
|
|
68
|
-
test(scenario.name, () => {
|
|
69
|
-
expect(
|
|
70
|
-
buildLocalizedUrl(
|
|
71
|
-
scenario.target,
|
|
72
|
-
scenario.language,
|
|
73
|
-
languages,
|
|
74
|
-
localisedUrls,
|
|
75
|
-
),
|
|
76
|
-
).toBe(scenario.expected);
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
test('canonical and localized path helpers strip and add prefixes exactly', () => {
|
|
81
|
-
expect(
|
|
82
|
-
canonicalPath('/CS/produkty/red-shoe?tag=boots#details', pathsConfig),
|
|
83
|
-
).toBe('/products/red-shoe?tag=boots#details');
|
|
84
|
-
expect(
|
|
85
|
-
localizePath('/products/red-shoe?tag=boots#details', 'cs', pathsConfig),
|
|
86
|
-
).toBe('/cs/produkty/red-shoe?tag=boots#details');
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
test('splat params preserve separators and percent-encode each segment', () => {
|
|
90
|
-
expect(
|
|
91
|
-
buildLocalizedUrl(
|
|
92
|
-
'/files/resume%20drafts/Q1%20deck.pdf',
|
|
93
|
-
'cs',
|
|
94
|
-
languages,
|
|
95
|
-
localisedUrls,
|
|
96
|
-
),
|
|
97
|
-
).toBe('/cs/soubory/resume%20drafts/Q1%20deck.pdf');
|
|
98
|
-
});
|
|
99
|
-
test('explicit maps work without plugin registration and keep suffixes verbatim', () => {
|
|
100
|
-
const config = {
|
|
101
|
-
languages,
|
|
102
|
-
localisedUrls: {
|
|
103
|
-
'/docs/:slug?': { en: '/docs/:slug?', cs: '/dokumenty/:slug?' },
|
|
104
|
-
},
|
|
105
|
-
};
|
|
106
|
-
expect(localizePath('/docs?x=%2f&x=2#literal?query', 'cs', config)).toBe(
|
|
107
|
-
'/cs/dokumenty?x=%2f&x=2#literal?query',
|
|
108
|
-
);
|
|
109
|
-
expect(canonicalPath('/cs/dokumenty/%E0%A4%A', config)).toBe(
|
|
110
|
-
'/dokumenty/%E0%A4%A',
|
|
111
|
-
);
|
|
112
|
-
expect(localizePath('/[literal]?x=1#x', 'cs', { languages })).toBe(
|
|
113
|
-
'/cs/[literal]?x=1#x',
|
|
114
|
-
);
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
describe('migrated public mapped helper contract', () => {
|
|
119
|
-
const localisedUrls = {
|
|
120
|
-
'/terms-of-service': {
|
|
121
|
-
en: '/terms-of-service',
|
|
122
|
-
cs: '/podminky-pouzivani',
|
|
123
|
-
},
|
|
124
|
-
'/products': {
|
|
125
|
-
en: '/products',
|
|
126
|
-
cs: '/produkty',
|
|
127
|
-
},
|
|
128
|
-
'/products/:slug': {
|
|
129
|
-
en: '/products/:slug',
|
|
130
|
-
cs: '/produkty/:slug',
|
|
131
|
-
},
|
|
132
|
-
// Canonical key that matches no language pattern.
|
|
133
|
-
'/talks/:slug': {
|
|
134
|
-
en: '/lectures/:slug',
|
|
135
|
-
cs: '/prednasky/:slug',
|
|
136
|
-
},
|
|
137
|
-
};
|
|
138
|
-
|
|
139
|
-
const languages = ['en', 'cs'];
|
|
140
|
-
const pathsConfig = { languages, localisedUrls };
|
|
141
|
-
|
|
142
|
-
test('query and hash survive localized slug mapping', () => {
|
|
143
|
-
expect(
|
|
144
|
-
buildLocalizedUrl(
|
|
145
|
-
'/products/bota?tag=x#detail',
|
|
146
|
-
'cs',
|
|
147
|
-
languages,
|
|
148
|
-
localisedUrls,
|
|
149
|
-
),
|
|
150
|
-
).toBe('/cs/produkty/bota?tag=x#detail');
|
|
151
|
-
});
|
|
152
|
-
|
|
153
|
-
test('localizes canonical keys that match no language pattern', () => {
|
|
11
|
+
describe('@modern-js/i18n-runtime-extensions/paths', () => {
|
|
12
|
+
test('localises the pathname while leaving query and hash verbatim', () => {
|
|
154
13
|
expect(
|
|
155
|
-
|
|
156
|
-
'/
|
|
14
|
+
localizePath(
|
|
15
|
+
'/products/red-shoe?tag=boots&tag=sale#details',
|
|
157
16
|
'cs',
|
|
158
|
-
|
|
159
|
-
localisedUrls,
|
|
17
|
+
config,
|
|
160
18
|
),
|
|
161
|
-
).toBe('/cs/
|
|
162
|
-
expect(
|
|
163
|
-
buildLocalizedUrl('/talks/ai-slop', 'en', languages, localisedUrls),
|
|
164
|
-
).toBe('/en/lectures/ai-slop');
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
test('re-localizes already-localized paths', () => {
|
|
19
|
+
).toBe('/cs/produkty/red-shoe?tag=boots&tag=sale#details');
|
|
168
20
|
expect(
|
|
169
|
-
|
|
170
|
-
).toBe('/
|
|
171
|
-
});
|
|
172
|
-
test('localizePath maps canonical paths per language', () => {
|
|
173
|
-
expect(localizePath('/products/bota', 'cs', pathsConfig)).toBe(
|
|
174
|
-
'/cs/produkty/bota',
|
|
175
|
-
);
|
|
176
|
-
expect(localizePath('/talks/x', 'en', pathsConfig)).toBe('/en/lectures/x');
|
|
177
|
-
});
|
|
178
|
-
|
|
179
|
-
test('canonicalPath strips language and reverse-maps localized slugs', () => {
|
|
180
|
-
expect(canonicalPath('/cs/produkty/bota', pathsConfig)).toBe(
|
|
181
|
-
'/products/bota',
|
|
182
|
-
);
|
|
183
|
-
expect(canonicalPath('/en/lectures/x?q=1#h', pathsConfig)).toBe(
|
|
184
|
-
'/talks/x?q=1#h',
|
|
185
|
-
);
|
|
186
|
-
expect(canonicalPath('/cs', pathsConfig)).toBe('/');
|
|
187
|
-
expect(canonicalPath('/en/products', pathsConfig)).toBe('/products');
|
|
21
|
+
canonicalPath('/CS/produkty/red-shoe?tag=boots#details', config),
|
|
22
|
+
).toBe('/products/red-shoe?tag=boots#details');
|
|
188
23
|
});
|
|
189
24
|
});
|