@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
package/dist/cjs/runtime/Link.js
CHANGED
|
@@ -122,7 +122,7 @@ const mergeClassNames = (...values)=>{
|
|
|
122
122
|
return classNames.length > 0 ? classNames.join(' ') : void 0;
|
|
123
123
|
};
|
|
124
124
|
const Link = (props)=>{
|
|
125
|
-
const { to, params, children, hash: hashProp, search: searchProp, hashScrollIntoView, activeOptions, activeProps, ...rest } = props;
|
|
125
|
+
const { to, params, children, hash: hashProp, search: searchProp, hashScrollIntoView, activeOptions, activeProps, prefetch, preload, ...rest } = props;
|
|
126
126
|
const adapter = (0, external_routerAdapter_js_namespaceObject.useI18nRouterAdapter)();
|
|
127
127
|
const { language, supportedLanguages, localisedUrls } = (0, external_context_js_namespaceObject.useModernI18n)();
|
|
128
128
|
const config = {
|
|
@@ -180,7 +180,7 @@ const Link = (props)=>{
|
|
|
180
180
|
'aria-current': rest['aria-current'] ?? resolvedActiveProps['aria-current'] ?? 'page'
|
|
181
181
|
} : {};
|
|
182
182
|
if (!target) {
|
|
183
|
-
const {
|
|
183
|
+
const { replace: _replace, ...anchorProps } = rest;
|
|
184
184
|
return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("a", {
|
|
185
185
|
href: to,
|
|
186
186
|
...anchorProps,
|
|
@@ -189,7 +189,7 @@ const Link = (props)=>{
|
|
|
189
189
|
}
|
|
190
190
|
const { Link: RouterLink, hasRouter, framework } = adapter;
|
|
191
191
|
if (!hasRouter || !RouterLink) {
|
|
192
|
-
const {
|
|
192
|
+
const { replace: _replace, ...anchorProps } = rest;
|
|
193
193
|
const { className: activeClassName, style: activeStyle, ...activeRest } = resolvedActiveProps;
|
|
194
194
|
return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("a", {
|
|
195
195
|
href: target.href,
|
|
@@ -210,26 +210,38 @@ const Link = (props)=>{
|
|
|
210
210
|
...rest.style,
|
|
211
211
|
...activeStyle
|
|
212
212
|
};
|
|
213
|
-
if ('tanstack' === framework)
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
213
|
+
if ('tanstack' === framework) {
|
|
214
|
+
const tanstackPreload = void 0 !== preload ? preload : void 0 === prefetch ? void 0 : 'none' === prefetch ? false : prefetch;
|
|
215
|
+
return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(RouterLink, {
|
|
216
|
+
to: target.localizedPathname,
|
|
217
|
+
...target.searchObject ? {
|
|
218
|
+
search: target.searchObject
|
|
219
|
+
} : {},
|
|
220
|
+
...target.hash ? {
|
|
221
|
+
hash: target.hash
|
|
222
|
+
} : {},
|
|
223
|
+
...void 0 === hashScrollIntoView ? {} : {
|
|
224
|
+
hashScrollIntoView
|
|
225
|
+
},
|
|
226
|
+
...void 0 === tanstackPreload ? {} : {
|
|
227
|
+
preload: tanstackPreload
|
|
228
|
+
},
|
|
229
|
+
...rest,
|
|
230
|
+
...activeRest,
|
|
231
|
+
...activeAttributes,
|
|
232
|
+
className: mergedClassName,
|
|
233
|
+
style: mergedStyle,
|
|
234
|
+
children: children
|
|
235
|
+
});
|
|
236
|
+
}
|
|
231
237
|
return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(RouterLink, {
|
|
232
238
|
to: target.href,
|
|
239
|
+
...void 0 === prefetch ? {} : {
|
|
240
|
+
prefetch
|
|
241
|
+
},
|
|
242
|
+
...void 0 === preload ? {} : {
|
|
243
|
+
preload
|
|
244
|
+
},
|
|
233
245
|
...rest,
|
|
234
246
|
...activeRest,
|
|
235
247
|
...activeAttributes,
|
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __webpack_require__ = {};
|
|
3
|
+
(()=>{
|
|
4
|
+
__webpack_require__.n = (module)=>{
|
|
5
|
+
var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
|
|
6
|
+
__webpack_require__.d(getter, {
|
|
7
|
+
a: getter
|
|
8
|
+
});
|
|
9
|
+
return getter;
|
|
10
|
+
};
|
|
11
|
+
})();
|
|
3
12
|
(()=>{
|
|
4
13
|
__webpack_require__.d = (exports1, getters, values)=>{
|
|
5
14
|
var define = (defs, kind)=>{
|
|
@@ -27,9 +36,37 @@ var __webpack_require__ = {};
|
|
|
27
36
|
})();
|
|
28
37
|
var __webpack_exports__ = {};
|
|
29
38
|
__webpack_require__.r(__webpack_exports__);
|
|
39
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
40
|
+
DEFAULT_I18NEXT_BACKEND_OPTIONS: ()=>DEFAULT_I18NEXT_BACKEND_OPTIONS,
|
|
41
|
+
convertBackendOptions: ()=>convertBackendOptions,
|
|
42
|
+
resolveDefaultLocalesDir: ()=>resolveDefaultLocalesDir
|
|
43
|
+
});
|
|
44
|
+
const external_fs_namespaceObject = require("fs");
|
|
45
|
+
var external_fs_default = /*#__PURE__*/ __webpack_require__.n(external_fs_namespaceObject);
|
|
46
|
+
const external_path_namespaceObject = require("path");
|
|
47
|
+
var external_path_default = /*#__PURE__*/ __webpack_require__.n(external_path_namespaceObject);
|
|
48
|
+
const CONVENTIONAL_LOCALES_DIRS = [
|
|
49
|
+
'./locales',
|
|
50
|
+
'./config/public/locales'
|
|
51
|
+
];
|
|
52
|
+
const isDirectory = (dirPath)=>{
|
|
53
|
+
try {
|
|
54
|
+
return external_fs_default().statSync(dirPath).isDirectory();
|
|
55
|
+
} catch {
|
|
56
|
+
return false;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
const resolveDefaultLocalesDir = (cwd = process.cwd())=>{
|
|
60
|
+
for (const dir of CONVENTIONAL_LOCALES_DIRS)if (isDirectory(external_path_default().resolve(cwd, dir))) return dir;
|
|
61
|
+
return CONVENTIONAL_LOCALES_DIRS[0];
|
|
62
|
+
};
|
|
30
63
|
const DEFAULT_I18NEXT_BACKEND_OPTIONS = {
|
|
31
|
-
loadPath
|
|
32
|
-
|
|
64
|
+
get loadPath () {
|
|
65
|
+
return `${resolveDefaultLocalesDir()}/{{lng}}/{{ns}}.json`;
|
|
66
|
+
},
|
|
67
|
+
get addPath () {
|
|
68
|
+
return `${resolveDefaultLocalesDir()}/{{lng}}/{{ns}}.json`;
|
|
69
|
+
}
|
|
33
70
|
};
|
|
34
71
|
function convertPath(path) {
|
|
35
72
|
if (!path) return path;
|
|
@@ -45,16 +82,13 @@ function convertBackendOptions(options) {
|
|
|
45
82
|
if (converted.addPath) converted.addPath = convertPath(converted.addPath);
|
|
46
83
|
return converted;
|
|
47
84
|
}
|
|
48
|
-
__webpack_require__.d(__webpack_exports__, {
|
|
49
|
-
convertBackendOptions: ()=>convertBackendOptions
|
|
50
|
-
}, {
|
|
51
|
-
DEFAULT_I18NEXT_BACKEND_OPTIONS: DEFAULT_I18NEXT_BACKEND_OPTIONS
|
|
52
|
-
});
|
|
53
85
|
exports.DEFAULT_I18NEXT_BACKEND_OPTIONS = __webpack_exports__.DEFAULT_I18NEXT_BACKEND_OPTIONS;
|
|
54
86
|
exports.convertBackendOptions = __webpack_exports__.convertBackendOptions;
|
|
87
|
+
exports.resolveDefaultLocalesDir = __webpack_exports__.resolveDefaultLocalesDir;
|
|
55
88
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
56
89
|
"DEFAULT_I18NEXT_BACKEND_OPTIONS",
|
|
57
|
-
"convertBackendOptions"
|
|
90
|
+
"convertBackendOptions",
|
|
91
|
+
"resolveDefaultLocalesDir"
|
|
58
92
|
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
59
93
|
Object.defineProperty(exports, '__esModule', {
|
|
60
94
|
value: true
|
|
@@ -41,10 +41,7 @@ const external_utils_js_namespaceObject = require("./utils.js");
|
|
|
41
41
|
const localizePath = (pathname, language, config)=>(0, external_utils_js_namespaceObject.buildLocalizedUrl)(pathname, language, config.languages, config.localisedUrls);
|
|
42
42
|
const canonicalPath = (target, config)=>{
|
|
43
43
|
const { pathname, search, hash } = (0, external_utils_js_namespaceObject.splitUrlTarget)(target);
|
|
44
|
-
const
|
|
45
|
-
const pathWithoutLanguage = segments.length > 0 && config.languages.includes(segments[0]) ? `/${segments.slice(1).join('/')}` : pathname || '/';
|
|
46
|
-
const localisedUrlsConfig = (0, localisedUrls_js_namespaceObject.resolveLocalisedUrlsConfig)(config.localisedUrls);
|
|
47
|
-
const resolvedPath = localisedUrlsConfig.enabled ? (0, localisedUrls_js_namespaceObject.resolveCanonicalLocalisedPath)(pathWithoutLanguage, config.languages, localisedUrlsConfig.map) : pathWithoutLanguage;
|
|
44
|
+
const resolvedPath = (0, localisedUrls_js_namespaceObject.canonicalTargetPathname)(pathname, config.languages, config.localisedUrls);
|
|
48
45
|
return `${resolvedPath}${search}${hash}`;
|
|
49
46
|
};
|
|
50
47
|
const useLocalizedPaths = ()=>{
|
|
@@ -57,7 +57,7 @@ const getWindowLocation = ()=>{
|
|
|
57
57
|
};
|
|
58
58
|
};
|
|
59
59
|
const getRouterFramework = (runtimeContext, internalContext, inReactRouter)=>{
|
|
60
|
-
const framework =
|
|
60
|
+
const framework = (0, context_namespaceObject.getRouterRuntimeState)(internalContext)?.framework || (0, context_namespaceObject.getRouterRuntimeState)(runtimeContext)?.framework;
|
|
61
61
|
if (framework) return framework;
|
|
62
62
|
if (internalContext.router?.useRouter || runtimeContext.router?.useRouter) return 'tanstack';
|
|
63
63
|
if (internalContext.router?.useLocation || internalContext.router?.useHref || runtimeContext.router?.useLocation || runtimeContext.router?.useHref) return 'react-router';
|
|
@@ -65,7 +65,7 @@ const getRouterFramework = (runtimeContext, internalContext, inReactRouter)=>{
|
|
|
65
65
|
};
|
|
66
66
|
const getRouterInstance = (internalContext, contextRouter)=>{
|
|
67
67
|
if (contextRouter) return contextRouter;
|
|
68
|
-
const router =
|
|
68
|
+
const router = (0, context_namespaceObject.getRouterRuntimeState)(internalContext)?.instance;
|
|
69
69
|
if (!router || 'object' != typeof router) return null;
|
|
70
70
|
return router;
|
|
71
71
|
};
|
|
@@ -69,15 +69,8 @@ const splitUrlTarget = (target)=>{
|
|
|
69
69
|
};
|
|
70
70
|
const buildLocalizedUrl = (target, language, languages, localisedUrls)=>{
|
|
71
71
|
const { pathname, search, hash } = splitUrlTarget(target);
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
const pathWithoutLanguage = segments.length > 0 && languages.includes(segments[0]) ? `/${segments.slice(1).join('/')}` : pathname || '/';
|
|
75
|
-
const resolvedPath = localisedUrlsConfig.enabled ? (0, localisedUrls_js_namespaceObject.resolveLocalisedPath)(pathWithoutLanguage, language, languages, localisedUrlsConfig.map) : pathWithoutLanguage;
|
|
76
|
-
const resolvedSegments = resolvedPath.split('/').filter(Boolean);
|
|
77
|
-
return `/${[
|
|
78
|
-
language,
|
|
79
|
-
...resolvedSegments
|
|
80
|
-
].join('/')}${search}${hash}`;
|
|
72
|
+
const localizedPathname = (0, localisedUrls_js_namespaceObject.localiseTargetPathname)(pathname, language, languages, localisedUrls);
|
|
73
|
+
return `${localizedPathname}${search}${hash}`;
|
|
81
74
|
};
|
|
82
75
|
const detectLanguageFromPath = (pathname, languages, localePathRedirect)=>{
|
|
83
76
|
if (!localePathRedirect) return {
|
package/dist/cjs/server/index.js
CHANGED
|
@@ -162,15 +162,7 @@ const buildLocalizedUrl = (req, urlPath, language, languages, localisedUrls)=>{
|
|
|
162
162
|
const pathname = url.pathname;
|
|
163
163
|
const basePath = urlPath.replace('/*', '');
|
|
164
164
|
const remainingPath = pathname.startsWith(basePath) ? pathname.slice(basePath.length) : pathname;
|
|
165
|
-
const
|
|
166
|
-
const localisedUrlsConfig = (0, localisedUrls_js_namespaceObject.resolveLocalisedUrlsConfig)(localisedUrls);
|
|
167
|
-
const pathWithoutLanguage = segments.length > 0 && languages.includes(segments[0]) ? `/${segments.slice(1).join('/')}` : remainingPath;
|
|
168
|
-
const resolvedPath = localisedUrlsConfig.enabled ? (0, localisedUrls_js_namespaceObject.resolveLocalisedPath)(pathWithoutLanguage, language, languages, localisedUrlsConfig.map) : pathWithoutLanguage;
|
|
169
|
-
const resolvedSegments = resolvedPath.split('/').filter(Boolean);
|
|
170
|
-
const newPathname = `/${[
|
|
171
|
-
language,
|
|
172
|
-
...resolvedSegments
|
|
173
|
-
].join('/')}`;
|
|
165
|
+
const newPathname = (0, localisedUrls_js_namespaceObject.localiseTargetPathname)(remainingPath, language, languages, localisedUrls);
|
|
174
166
|
const suffix = `${url.search}${url.hash}`;
|
|
175
167
|
const localizedUrl = '/' === basePath ? newPathname + suffix : basePath + newPathname + suffix;
|
|
176
168
|
return localizedUrl;
|
|
@@ -32,12 +32,13 @@ const LOCALE_PARAM_NAMES = new Set([
|
|
|
32
32
|
'locale',
|
|
33
33
|
'language'
|
|
34
34
|
]);
|
|
35
|
-
const
|
|
35
|
+
const normaliseSlashes = (path)=>{
|
|
36
36
|
const withoutDuplicateSlashes = path.replace(/\/+/g, '/');
|
|
37
37
|
const withLeadingSlash = withoutDuplicateSlashes.startsWith('/') ? withoutDuplicateSlashes : `/${withoutDuplicateSlashes}`;
|
|
38
|
-
|
|
39
|
-
return withoutTrailingSlash.replace(/\[(.+?)\]/g, ':$1');
|
|
38
|
+
return withLeadingSlash.length > 1 ? withLeadingSlash.replace(/\/+$/, '') : withLeadingSlash;
|
|
40
39
|
};
|
|
40
|
+
const normalisePathPattern = (path)=>normaliseSlashes(path).replace(/\[(.+?)\]/g, ':$1');
|
|
41
|
+
const normalisePathname = (pathname)=>normaliseSlashes(pathname);
|
|
41
42
|
const normaliseRoutePath = (path)=>{
|
|
42
43
|
const normalized = normalisePathPattern(path);
|
|
43
44
|
return '/' === normalized ? '' : normalized.slice(1);
|
|
@@ -63,16 +64,12 @@ const getLeadingLocaleParam = (path)=>{
|
|
|
63
64
|
return getLocaleParamSegment(segments[0] || '');
|
|
64
65
|
};
|
|
65
66
|
const resolveLocalisedUrlsConfig = (option)=>{
|
|
66
|
-
if (
|
|
67
|
-
enabled: false,
|
|
68
|
-
map: {}
|
|
69
|
-
};
|
|
70
|
-
if (option && 'object' == typeof option) return {
|
|
67
|
+
if (option && 'object' == typeof option && Object.keys(option).length > 0) return {
|
|
71
68
|
enabled: true,
|
|
72
69
|
map: option
|
|
73
70
|
};
|
|
74
71
|
return {
|
|
75
|
-
enabled:
|
|
72
|
+
enabled: false,
|
|
76
73
|
map: {}
|
|
77
74
|
};
|
|
78
75
|
};
|
|
@@ -164,9 +161,13 @@ const applyLocalisedUrlsToRoutes = (routes, languages, localisedUrls)=>{
|
|
|
164
161
|
};
|
|
165
162
|
const escapeRegExp = (value)=>value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
166
163
|
const getParamName = (segment)=>segment.slice(1).replace(/\?$/, '');
|
|
164
|
+
const compiledPathPatternCache = new Map();
|
|
167
165
|
const compilePathPattern = (pattern)=>{
|
|
166
|
+
const normalizedPattern = normalisePathPattern(pattern);
|
|
167
|
+
const cached = compiledPathPatternCache.get(normalizedPattern);
|
|
168
|
+
if (cached) return cached;
|
|
168
169
|
const names = [];
|
|
169
|
-
const segments =
|
|
170
|
+
const segments = normalizedPattern.split('/').filter(Boolean);
|
|
170
171
|
const source = segments.map((segment)=>{
|
|
171
172
|
if (segment.startsWith(':')) {
|
|
172
173
|
names.push(getParamName(segment));
|
|
@@ -179,19 +180,55 @@ const compilePathPattern = (pattern)=>{
|
|
|
179
180
|
}
|
|
180
181
|
return `/${escapeRegExp(segment)}`;
|
|
181
182
|
}).join('');
|
|
182
|
-
|
|
183
|
+
const compiled = {
|
|
183
184
|
names,
|
|
184
185
|
regexp: new RegExp(`^${source || '/'}$`)
|
|
185
186
|
};
|
|
187
|
+
compiledPathPatternCache.set(normalizedPattern, compiled);
|
|
188
|
+
return compiled;
|
|
189
|
+
};
|
|
190
|
+
const getPatternSpecificity = (pattern)=>{
|
|
191
|
+
const segments = normalisePathPattern(pattern).split('/').filter(Boolean);
|
|
192
|
+
let staticSegments = 0;
|
|
193
|
+
let dynamicSegments = 0;
|
|
194
|
+
let splatSegments = 0;
|
|
195
|
+
for (const segment of segments)if ('*' === segment) splatSegments++;
|
|
196
|
+
else if (segment.startsWith(':')) dynamicSegments++;
|
|
197
|
+
else staticSegments++;
|
|
198
|
+
return {
|
|
199
|
+
staticSegments,
|
|
200
|
+
dynamicSegments,
|
|
201
|
+
splatSegments,
|
|
202
|
+
totalSegments: segments.length
|
|
203
|
+
};
|
|
204
|
+
};
|
|
205
|
+
const comparePatternSpecificity = (left, right)=>{
|
|
206
|
+
const a = getPatternSpecificity(left);
|
|
207
|
+
const b = getPatternSpecificity(right);
|
|
208
|
+
return b.staticSegments - a.staticSegments || b.totalSegments - a.totalSegments || a.splatSegments - b.splatSegments || a.dynamicSegments - b.dynamicSegments;
|
|
209
|
+
};
|
|
210
|
+
const sortPatternsBySpecificity = (patterns)=>patterns.map((pattern, index)=>({
|
|
211
|
+
pattern,
|
|
212
|
+
index
|
|
213
|
+
})).sort((left, right)=>comparePatternSpecificity(left.pattern.pattern, right.pattern.pattern) || left.index - right.index).map(({ pattern })=>pattern);
|
|
214
|
+
const decodePathParam = (value)=>{
|
|
215
|
+
try {
|
|
216
|
+
return decodeURIComponent(value);
|
|
217
|
+
} catch {
|
|
218
|
+
return null;
|
|
219
|
+
}
|
|
186
220
|
};
|
|
187
221
|
const matchPathPattern = (pathname, pattern)=>{
|
|
188
222
|
const { names, regexp } = compilePathPattern(pattern);
|
|
189
|
-
const match = regexp.exec(
|
|
223
|
+
const match = regexp.exec(normalisePathname(pathname));
|
|
190
224
|
if (!match) return null;
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
225
|
+
const params = {};
|
|
226
|
+
for(let index = 0; index < names.length; index++){
|
|
227
|
+
const decoded = decodePathParam(match[index + 1] || '');
|
|
228
|
+
if (null === decoded) return null;
|
|
229
|
+
params[names[index]] = decoded;
|
|
230
|
+
}
|
|
231
|
+
return params;
|
|
195
232
|
};
|
|
196
233
|
const buildPathFromPattern = (pattern, params)=>{
|
|
197
234
|
const segments = normalisePathPattern(pattern).split('/').filter(Boolean);
|
|
@@ -206,27 +243,41 @@ const buildPathFromPattern = (pattern, params)=>{
|
|
|
206
243
|
return `/${path}`;
|
|
207
244
|
};
|
|
208
245
|
const resolveLocalisedPath = (pathname, targetLanguage, languages, localisedUrls)=>{
|
|
209
|
-
const normalizedPathname =
|
|
210
|
-
|
|
246
|
+
const normalizedPathname = normalisePathname(pathname);
|
|
247
|
+
const canonicalCandidates = sortPatternsBySpecificity(Object.entries(localisedUrls).map(([canonicalPattern, localisedUrlEntry])=>({
|
|
248
|
+
pattern: canonicalPattern,
|
|
249
|
+
canonicalPattern,
|
|
250
|
+
localisedUrlEntry
|
|
251
|
+
})));
|
|
252
|
+
for (const { canonicalPattern, localisedUrlEntry } of canonicalCandidates){
|
|
211
253
|
const targetPattern = localisedUrlEntry[targetLanguage];
|
|
212
254
|
if (!targetPattern) continue;
|
|
213
255
|
const params = matchPathPattern(normalizedPathname, canonicalPattern);
|
|
214
256
|
if (params) return buildPathFromPattern(targetPattern, params);
|
|
215
257
|
}
|
|
216
|
-
|
|
258
|
+
const localisedCandidates = sortPatternsBySpecificity(Object.values(localisedUrls).flatMap((localisedUrlEntry)=>{
|
|
217
259
|
const targetPattern = localisedUrlEntry[targetLanguage];
|
|
218
|
-
if (targetPattern)
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
260
|
+
if (!targetPattern) return [];
|
|
261
|
+
return languages.map((language)=>localisedUrlEntry[language]).filter((sourcePattern)=>Boolean(sourcePattern)).map((sourcePattern)=>({
|
|
262
|
+
pattern: sourcePattern,
|
|
263
|
+
sourcePattern,
|
|
264
|
+
targetPattern
|
|
265
|
+
}));
|
|
266
|
+
}));
|
|
267
|
+
for (const { sourcePattern, targetPattern } of localisedCandidates){
|
|
268
|
+
const params = matchPathPattern(normalizedPathname, sourcePattern);
|
|
269
|
+
if (params) return buildPathFromPattern(targetPattern, params);
|
|
224
270
|
}
|
|
225
271
|
return normalizedPathname;
|
|
226
272
|
};
|
|
227
273
|
const resolveCanonicalLocalisedPath = (pathname, languages, localisedUrls)=>{
|
|
228
|
-
const normalizedPathname =
|
|
229
|
-
|
|
274
|
+
const normalizedPathname = normalisePathname(pathname);
|
|
275
|
+
const canonicalCandidates = sortPatternsBySpecificity(Object.entries(localisedUrls).map(([canonicalPattern, localisedUrlEntry])=>({
|
|
276
|
+
pattern: canonicalPattern,
|
|
277
|
+
canonicalPattern,
|
|
278
|
+
localisedUrlEntry
|
|
279
|
+
})));
|
|
280
|
+
for (const { canonicalPattern, localisedUrlEntry } of canonicalCandidates){
|
|
230
281
|
const canonicalParams = matchPathPattern(normalizedPathname, canonicalPattern);
|
|
231
282
|
if (canonicalParams) return buildPathFromPattern(canonicalPattern, canonicalParams);
|
|
232
283
|
for (const language of languages){
|
|
@@ -238,11 +289,34 @@ const resolveCanonicalLocalisedPath = (pathname, languages, localisedUrls)=>{
|
|
|
238
289
|
}
|
|
239
290
|
return normalizedPathname;
|
|
240
291
|
};
|
|
292
|
+
const stripLanguagePrefix = (pathname, languages)=>{
|
|
293
|
+
const segments = pathname.split('/').filter(Boolean);
|
|
294
|
+
if (segments.length > 0 && languages.includes(segments[0])) return `/${segments.slice(1).join('/')}`;
|
|
295
|
+
return pathname || '/';
|
|
296
|
+
};
|
|
297
|
+
const localiseTargetPathname = (pathname, language, languages, localisedUrls)=>{
|
|
298
|
+
const pathWithoutLanguage = stripLanguagePrefix(pathname, languages);
|
|
299
|
+
const localisedUrlsConfig = resolveLocalisedUrlsConfig(localisedUrls);
|
|
300
|
+
const resolvedPath = localisedUrlsConfig.enabled ? resolveLocalisedPath(pathWithoutLanguage, language, languages, localisedUrlsConfig.map) : pathWithoutLanguage;
|
|
301
|
+
const resolvedSegments = resolvedPath.split('/').filter(Boolean);
|
|
302
|
+
return `/${[
|
|
303
|
+
language,
|
|
304
|
+
...resolvedSegments
|
|
305
|
+
].join('/')}`;
|
|
306
|
+
};
|
|
307
|
+
const canonicalTargetPathname = (pathname, languages, localisedUrls)=>{
|
|
308
|
+
const pathWithoutLanguage = stripLanguagePrefix(pathname, languages);
|
|
309
|
+
const localisedUrlsConfig = resolveLocalisedUrlsConfig(localisedUrls);
|
|
310
|
+
return localisedUrlsConfig.enabled ? resolveCanonicalLocalisedPath(pathWithoutLanguage, languages, localisedUrlsConfig.map) : pathWithoutLanguage;
|
|
311
|
+
};
|
|
241
312
|
__webpack_require__.d(__webpack_exports__, {}, {
|
|
242
313
|
applyLocalisedUrlsToRoutes: applyLocalisedUrlsToRoutes,
|
|
243
314
|
buildPathFromPattern: buildPathFromPattern,
|
|
315
|
+
canonicalTargetPathname: canonicalTargetPathname,
|
|
316
|
+
localiseTargetPathname: localiseTargetPathname,
|
|
244
317
|
matchPathPattern: matchPathPattern,
|
|
245
318
|
normalisePathPattern: normalisePathPattern,
|
|
319
|
+
normalisePathname: normalisePathname,
|
|
246
320
|
resolveCanonicalLocalisedPath: resolveCanonicalLocalisedPath,
|
|
247
321
|
resolveLocalisedPath: resolveLocalisedPath,
|
|
248
322
|
resolveLocalisedUrlsConfig: resolveLocalisedUrlsConfig,
|
|
@@ -250,8 +324,11 @@ __webpack_require__.d(__webpack_exports__, {}, {
|
|
|
250
324
|
});
|
|
251
325
|
exports.applyLocalisedUrlsToRoutes = __webpack_exports__.applyLocalisedUrlsToRoutes;
|
|
252
326
|
exports.buildPathFromPattern = __webpack_exports__.buildPathFromPattern;
|
|
327
|
+
exports.canonicalTargetPathname = __webpack_exports__.canonicalTargetPathname;
|
|
328
|
+
exports.localiseTargetPathname = __webpack_exports__.localiseTargetPathname;
|
|
253
329
|
exports.matchPathPattern = __webpack_exports__.matchPathPattern;
|
|
254
330
|
exports.normalisePathPattern = __webpack_exports__.normalisePathPattern;
|
|
331
|
+
exports.normalisePathname = __webpack_exports__.normalisePathname;
|
|
255
332
|
exports.resolveCanonicalLocalisedPath = __webpack_exports__.resolveCanonicalLocalisedPath;
|
|
256
333
|
exports.resolveLocalisedPath = __webpack_exports__.resolveLocalisedPath;
|
|
257
334
|
exports.resolveLocalisedUrlsConfig = __webpack_exports__.resolveLocalisedUrlsConfig;
|
|
@@ -259,8 +336,11 @@ exports.validateLocalisedUrls = __webpack_exports__.validateLocalisedUrls;
|
|
|
259
336
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
260
337
|
"applyLocalisedUrlsToRoutes",
|
|
261
338
|
"buildPathFromPattern",
|
|
339
|
+
"canonicalTargetPathname",
|
|
340
|
+
"localiseTargetPathname",
|
|
262
341
|
"matchPathPattern",
|
|
263
342
|
"normalisePathPattern",
|
|
343
|
+
"normalisePathname",
|
|
264
344
|
"resolveCanonicalLocalisedPath",
|
|
265
345
|
"resolveLocalisedPath",
|
|
266
346
|
"resolveLocalisedUrlsConfig",
|
|
@@ -88,7 +88,7 @@ const mergeClassNames = (...values)=>{
|
|
|
88
88
|
return classNames.length > 0 ? classNames.join(' ') : void 0;
|
|
89
89
|
};
|
|
90
90
|
const Link = (props)=>{
|
|
91
|
-
const { to, params, children, hash: hashProp, search: searchProp, hashScrollIntoView, activeOptions, activeProps, ...rest } = props;
|
|
91
|
+
const { to, params, children, hash: hashProp, search: searchProp, hashScrollIntoView, activeOptions, activeProps, prefetch, preload, ...rest } = props;
|
|
92
92
|
const adapter = useI18nRouterAdapter();
|
|
93
93
|
const { language, supportedLanguages, localisedUrls } = useModernI18n();
|
|
94
94
|
const config = {
|
|
@@ -146,7 +146,7 @@ const Link = (props)=>{
|
|
|
146
146
|
'aria-current': rest['aria-current'] ?? resolvedActiveProps['aria-current'] ?? 'page'
|
|
147
147
|
} : {};
|
|
148
148
|
if (!target) {
|
|
149
|
-
const {
|
|
149
|
+
const { replace: _replace, ...anchorProps } = rest;
|
|
150
150
|
return /*#__PURE__*/ jsx("a", {
|
|
151
151
|
href: to,
|
|
152
152
|
...anchorProps,
|
|
@@ -155,7 +155,7 @@ const Link = (props)=>{
|
|
|
155
155
|
}
|
|
156
156
|
const { Link: RouterLink, hasRouter, framework } = adapter;
|
|
157
157
|
if (!hasRouter || !RouterLink) {
|
|
158
|
-
const {
|
|
158
|
+
const { replace: _replace, ...anchorProps } = rest;
|
|
159
159
|
const { className: activeClassName, style: activeStyle, ...activeRest } = resolvedActiveProps;
|
|
160
160
|
return /*#__PURE__*/ jsx("a", {
|
|
161
161
|
href: target.href,
|
|
@@ -176,26 +176,38 @@ const Link = (props)=>{
|
|
|
176
176
|
...rest.style,
|
|
177
177
|
...activeStyle
|
|
178
178
|
};
|
|
179
|
-
if ('tanstack' === framework)
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
179
|
+
if ('tanstack' === framework) {
|
|
180
|
+
const tanstackPreload = void 0 !== preload ? preload : void 0 === prefetch ? void 0 : 'none' === prefetch ? false : prefetch;
|
|
181
|
+
return /*#__PURE__*/ jsx(RouterLink, {
|
|
182
|
+
to: target.localizedPathname,
|
|
183
|
+
...target.searchObject ? {
|
|
184
|
+
search: target.searchObject
|
|
185
|
+
} : {},
|
|
186
|
+
...target.hash ? {
|
|
187
|
+
hash: target.hash
|
|
188
|
+
} : {},
|
|
189
|
+
...void 0 === hashScrollIntoView ? {} : {
|
|
190
|
+
hashScrollIntoView
|
|
191
|
+
},
|
|
192
|
+
...void 0 === tanstackPreload ? {} : {
|
|
193
|
+
preload: tanstackPreload
|
|
194
|
+
},
|
|
195
|
+
...rest,
|
|
196
|
+
...activeRest,
|
|
197
|
+
...activeAttributes,
|
|
198
|
+
className: mergedClassName,
|
|
199
|
+
style: mergedStyle,
|
|
200
|
+
children: children
|
|
201
|
+
});
|
|
202
|
+
}
|
|
197
203
|
return /*#__PURE__*/ jsx(RouterLink, {
|
|
198
204
|
to: target.href,
|
|
205
|
+
...void 0 === prefetch ? {} : {
|
|
206
|
+
prefetch
|
|
207
|
+
},
|
|
208
|
+
...void 0 === preload ? {} : {
|
|
209
|
+
preload
|
|
210
|
+
},
|
|
199
211
|
...rest,
|
|
200
212
|
...activeRest,
|
|
201
213
|
...activeAttributes,
|
|
@@ -1,6 +1,27 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path_0 from "path";
|
|
3
|
+
const CONVENTIONAL_LOCALES_DIRS = [
|
|
4
|
+
'./locales',
|
|
5
|
+
'./config/public/locales'
|
|
6
|
+
];
|
|
7
|
+
const isDirectory = (dirPath)=>{
|
|
8
|
+
try {
|
|
9
|
+
return fs.statSync(dirPath).isDirectory();
|
|
10
|
+
} catch {
|
|
11
|
+
return false;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
const resolveDefaultLocalesDir = (cwd = process.cwd())=>{
|
|
15
|
+
for (const dir of CONVENTIONAL_LOCALES_DIRS)if (isDirectory(path_0.resolve(cwd, dir))) return dir;
|
|
16
|
+
return CONVENTIONAL_LOCALES_DIRS[0];
|
|
17
|
+
};
|
|
1
18
|
const DEFAULT_I18NEXT_BACKEND_OPTIONS = {
|
|
2
|
-
loadPath
|
|
3
|
-
|
|
19
|
+
get loadPath () {
|
|
20
|
+
return `${resolveDefaultLocalesDir()}/{{lng}}/{{ns}}.json`;
|
|
21
|
+
},
|
|
22
|
+
get addPath () {
|
|
23
|
+
return `${resolveDefaultLocalesDir()}/{{lng}}/{{ns}}.json`;
|
|
24
|
+
}
|
|
4
25
|
};
|
|
5
26
|
function convertPath(path) {
|
|
6
27
|
if (!path) return path;
|
|
@@ -16,4 +37,4 @@ function convertBackendOptions(options) {
|
|
|
16
37
|
if (converted.addPath) converted.addPath = convertPath(converted.addPath);
|
|
17
38
|
return converted;
|
|
18
39
|
}
|
|
19
|
-
export { DEFAULT_I18NEXT_BACKEND_OPTIONS, convertBackendOptions };
|
|
40
|
+
export { DEFAULT_I18NEXT_BACKEND_OPTIONS, convertBackendOptions, resolveDefaultLocalesDir };
|
|
@@ -1,15 +1,12 @@
|
|
|
1
1
|
import { useMemo } from "react";
|
|
2
|
-
import {
|
|
2
|
+
import { canonicalTargetPathname } from "../shared/localisedUrls.mjs";
|
|
3
3
|
import { useModernI18n } from "./context.mjs";
|
|
4
4
|
import { useI18nRouterAdapter } from "./routerAdapter.mjs";
|
|
5
5
|
import { buildLocalizedUrl, splitUrlTarget } from "./utils.mjs";
|
|
6
6
|
const localizePath = (pathname, language, config)=>buildLocalizedUrl(pathname, language, config.languages, config.localisedUrls);
|
|
7
7
|
const canonicalPath = (target, config)=>{
|
|
8
8
|
const { pathname, search, hash } = splitUrlTarget(target);
|
|
9
|
-
const
|
|
10
|
-
const pathWithoutLanguage = segments.length > 0 && config.languages.includes(segments[0]) ? `/${segments.slice(1).join('/')}` : pathname || '/';
|
|
11
|
-
const localisedUrlsConfig = resolveLocalisedUrlsConfig(config.localisedUrls);
|
|
12
|
-
const resolvedPath = localisedUrlsConfig.enabled ? resolveCanonicalLocalisedPath(pathWithoutLanguage, config.languages, localisedUrlsConfig.map) : pathWithoutLanguage;
|
|
9
|
+
const resolvedPath = canonicalTargetPathname(pathname, config.languages, config.localisedUrls);
|
|
13
10
|
return `${resolvedPath}${search}${hash}`;
|
|
14
11
|
};
|
|
15
12
|
const useLocalizedPaths = ()=>{
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { RuntimeContext, isBrowser } from "@modern-js/runtime";
|
|
2
|
-
import { InternalRuntimeContext } from "@modern-js/runtime/context";
|
|
2
|
+
import { InternalRuntimeContext, getRouterRuntimeState } from "@modern-js/runtime/context";
|
|
3
3
|
import { Link as router_Link, useInRouterContext, useLocation, useNavigate, useParams } from "@modern-js/runtime/router";
|
|
4
4
|
import { useCallback, useContext, useEffect, useState } from "react";
|
|
5
5
|
const normalizeUrlPart = (value, prefix)=>{
|
|
@@ -25,7 +25,7 @@ const getWindowLocation = ()=>{
|
|
|
25
25
|
};
|
|
26
26
|
};
|
|
27
27
|
const getRouterFramework = (runtimeContext, internalContext, inReactRouter)=>{
|
|
28
|
-
const framework = internalContext
|
|
28
|
+
const framework = getRouterRuntimeState(internalContext)?.framework || getRouterRuntimeState(runtimeContext)?.framework;
|
|
29
29
|
if (framework) return framework;
|
|
30
30
|
if (internalContext.router?.useRouter || runtimeContext.router?.useRouter) return 'tanstack';
|
|
31
31
|
if (internalContext.router?.useLocation || internalContext.router?.useHref || runtimeContext.router?.useLocation || runtimeContext.router?.useHref) return 'react-router';
|
|
@@ -33,7 +33,7 @@ const getRouterFramework = (runtimeContext, internalContext, inReactRouter)=>{
|
|
|
33
33
|
};
|
|
34
34
|
const getRouterInstance = (internalContext, contextRouter)=>{
|
|
35
35
|
if (contextRouter) return contextRouter;
|
|
36
|
-
const router = internalContext
|
|
36
|
+
const router = getRouterRuntimeState(internalContext)?.instance;
|
|
37
37
|
if (!router || 'object' != typeof router) return null;
|
|
38
38
|
return router;
|
|
39
39
|
};
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { isBrowser } from "@modern-js/runtime";
|
|
2
2
|
import { getGlobalBasename } from "@modern-js/runtime/context";
|
|
3
|
-
import {
|
|
3
|
+
import { localiseTargetPathname } from "../shared/localisedUrls.mjs";
|
|
4
4
|
const getPathname = (context)=>{
|
|
5
5
|
if (isBrowser()) return window.location.pathname;
|
|
6
6
|
return context.ssrContext?.request?.pathname || '/';
|
|
@@ -31,15 +31,8 @@ const splitUrlTarget = (target)=>{
|
|
|
31
31
|
};
|
|
32
32
|
const buildLocalizedUrl = (target, language, languages, localisedUrls)=>{
|
|
33
33
|
const { pathname, search, hash } = splitUrlTarget(target);
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
const pathWithoutLanguage = segments.length > 0 && languages.includes(segments[0]) ? `/${segments.slice(1).join('/')}` : pathname || '/';
|
|
37
|
-
const resolvedPath = localisedUrlsConfig.enabled ? resolveLocalisedPath(pathWithoutLanguage, language, languages, localisedUrlsConfig.map) : pathWithoutLanguage;
|
|
38
|
-
const resolvedSegments = resolvedPath.split('/').filter(Boolean);
|
|
39
|
-
return `/${[
|
|
40
|
-
language,
|
|
41
|
-
...resolvedSegments
|
|
42
|
-
].join('/')}${search}${hash}`;
|
|
34
|
+
const localizedPathname = localiseTargetPathname(pathname, language, languages, localisedUrls);
|
|
35
|
+
return `${localizedPathname}${search}${hash}`;
|
|
43
36
|
};
|
|
44
37
|
const detectLanguageFromPath = (pathname, languages, localePathRedirect)=>{
|
|
45
38
|
if (!localePathRedirect) return {
|