@bleedingdev/modern-js-plugin-i18n 3.2.0-ultramodern.99 → 3.4.0-ultramodern.0
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/README.md +221 -11
- package/dist/cjs/cli/index.js +17 -64
- package/dist/cjs/cli/locales.js +132 -0
- package/dist/cjs/runtime/I18nLink.js +17 -20
- package/dist/cjs/runtime/Link.js +264 -0
- package/dist/cjs/runtime/canonicalRoutes.js +18 -0
- package/dist/cjs/runtime/context.js +9 -5
- package/dist/cjs/runtime/hooks.js +9 -5
- package/dist/cjs/runtime/i18n/backend/config.js +9 -5
- package/dist/cjs/runtime/i18n/backend/defaults.js +20 -11
- package/dist/cjs/runtime/i18n/backend/defaults.node.js +79 -10
- package/dist/cjs/runtime/i18n/backend/index.js +9 -5
- package/dist/cjs/runtime/i18n/backend/middleware.common.js +9 -5
- package/dist/cjs/runtime/i18n/backend/middleware.js +9 -5
- package/dist/cjs/runtime/i18n/backend/middleware.node.js +9 -5
- package/dist/cjs/runtime/i18n/backend/sdk-backend.js +9 -5
- package/dist/cjs/runtime/i18n/backend/sdk-event.js +16 -11
- package/dist/cjs/runtime/i18n/detection/config.js +9 -5
- package/dist/cjs/runtime/i18n/detection/index.js +9 -5
- package/dist/cjs/runtime/i18n/detection/middleware.js +9 -5
- package/dist/cjs/runtime/i18n/detection/middleware.node.js +9 -5
- package/dist/cjs/runtime/i18n/index.js +9 -5
- package/dist/cjs/runtime/i18n/instance.js +17 -13
- package/dist/cjs/runtime/i18n/react-i18next.js +12 -8
- package/dist/cjs/runtime/i18n/utils.js +9 -5
- package/dist/cjs/runtime/index.js +32 -5
- package/dist/cjs/runtime/localizedPaths.js +102 -0
- package/dist/cjs/runtime/routerAdapter.js +11 -7
- package/dist/cjs/runtime/utils.js +31 -17
- package/dist/cjs/server/index.js +10 -14
- package/dist/cjs/shared/deepMerge.js +12 -8
- package/dist/cjs/shared/detection.js +9 -5
- package/dist/cjs/shared/localisedUrls.js +148 -34
- package/dist/cjs/shared/utils.js +15 -11
- package/dist/esm/cli/index.mjs +8 -48
- package/dist/esm/cli/locales.mjs +80 -0
- package/dist/esm/runtime/I18nLink.mjs +7 -14
- package/dist/esm/runtime/Link.mjs +221 -0
- package/dist/esm/runtime/canonicalRoutes.mjs +0 -0
- package/dist/esm/runtime/i18n/backend/defaults.mjs +6 -2
- package/dist/esm/runtime/i18n/backend/defaults.node.mjs +56 -5
- package/dist/esm/runtime/index.mjs +4 -2
- package/dist/esm/runtime/localizedPaths.mjs +55 -0
- package/dist/esm/runtime/routerAdapter.mjs +3 -3
- package/dist/esm/runtime/utils.mjs +19 -12
- package/dist/esm/server/index.mjs +2 -10
- package/dist/esm/shared/localisedUrls.mjs +115 -23
- package/dist/esm-node/cli/index.mjs +8 -48
- package/dist/esm-node/cli/locales.mjs +81 -0
- package/dist/esm-node/runtime/I18nLink.mjs +7 -14
- package/dist/esm-node/runtime/Link.mjs +222 -0
- package/dist/esm-node/runtime/canonicalRoutes.mjs +1 -0
- package/dist/esm-node/runtime/i18n/backend/defaults.mjs +6 -2
- package/dist/esm-node/runtime/i18n/backend/defaults.node.mjs +56 -5
- package/dist/esm-node/runtime/index.mjs +4 -2
- package/dist/esm-node/runtime/localizedPaths.mjs +56 -0
- package/dist/esm-node/runtime/routerAdapter.mjs +3 -3
- package/dist/esm-node/runtime/utils.mjs +19 -12
- package/dist/esm-node/server/index.mjs +2 -10
- package/dist/esm-node/shared/localisedUrls.mjs +115 -23
- package/dist/types/cli/index.d.ts +1 -0
- package/dist/types/cli/locales.d.ts +17 -0
- package/dist/types/runtime/I18nLink.d.ts +4 -13
- package/dist/types/runtime/Link.d.ts +66 -0
- package/dist/types/runtime/canonicalRoutes.d.ts +60 -0
- package/dist/types/runtime/i18n/backend/defaults.d.ts +10 -7
- package/dist/types/runtime/i18n/backend/defaults.node.d.ts +13 -4
- package/dist/types/runtime/index.d.ts +5 -1
- package/dist/types/runtime/localizedPaths.d.ts +39 -0
- package/dist/types/runtime/types.d.ts +1 -1
- package/dist/types/runtime/utils.d.ts +13 -4
- package/dist/types/shared/localisedUrls.d.ts +23 -0
- package/dist/types/shared/type.d.ts +27 -5
- package/package.json +28 -25
- package/rstest.config.mts +7 -2
- package/src/cli/index.ts +25 -98
- package/src/cli/locales.ts +186 -0
- package/src/runtime/I18nLink.tsx +13 -44
- package/src/runtime/Link.tsx +430 -0
- package/src/runtime/canonicalRoutes.ts +93 -0
- package/src/runtime/i18n/backend/defaults.node.ts +112 -7
- package/src/runtime/i18n/backend/defaults.ts +20 -18
- package/src/runtime/index.tsx +24 -2
- package/src/runtime/localizedPaths.ts +107 -0
- package/src/runtime/routerAdapter.tsx +4 -5
- package/src/runtime/types.ts +1 -1
- package/src/runtime/utils.ts +33 -26
- package/src/server/index.ts +7 -17
- package/src/shared/localisedUrls.ts +256 -26
- package/src/shared/type.ts +27 -5
- package/tests/backendDefaults.test.ts +51 -0
- package/tests/i18nUtils.test.ts +10 -3
- package/tests/link.test.tsx +525 -0
- package/tests/linkTypes.test.ts +28 -0
- package/tests/localisedUrls.test.ts +224 -0
- package/tests/routerAdapter.test.tsx +86 -12
- package/tests/type-fixture/linkTypes.fixture.tsx +51 -0
- package/tests/type-fixture/tsconfig.json +15 -0
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __webpack_require__ = {};
|
|
3
3
|
(()=>{
|
|
4
|
-
__webpack_require__.d = (exports1,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
__webpack_require__.d = (exports1, getters, values)=>{
|
|
5
|
+
var define = (defs, kind)=>{
|
|
6
|
+
for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
[kind]: defs[key]
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
define(getters, "get");
|
|
12
|
+
define(values, "value");
|
|
9
13
|
};
|
|
10
14
|
})();
|
|
11
15
|
(()=>{
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __webpack_require__ = {};
|
|
3
3
|
(()=>{
|
|
4
|
-
__webpack_require__.d = (exports1,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
__webpack_require__.d = (exports1, getters, values)=>{
|
|
5
|
+
var define = (defs, kind)=>{
|
|
6
|
+
for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
[kind]: defs[key]
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
define(getters, "get");
|
|
12
|
+
define(values, "value");
|
|
9
13
|
};
|
|
10
14
|
})();
|
|
11
15
|
(()=>{
|
|
@@ -23,24 +27,18 @@ var __webpack_require__ = {};
|
|
|
23
27
|
})();
|
|
24
28
|
var __webpack_exports__ = {};
|
|
25
29
|
__webpack_require__.r(__webpack_exports__);
|
|
26
|
-
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
-
applyLocalisedUrlsToRoutes: ()=>applyLocalisedUrlsToRoutes,
|
|
28
|
-
normalisePathPattern: ()=>normalisePathPattern,
|
|
29
|
-
resolveLocalisedPath: ()=>resolveLocalisedPath,
|
|
30
|
-
resolveLocalisedUrlsConfig: ()=>resolveLocalisedUrlsConfig,
|
|
31
|
-
validateLocalisedUrls: ()=>validateLocalisedUrls
|
|
32
|
-
});
|
|
33
30
|
const LOCALE_PARAM_NAMES = new Set([
|
|
34
31
|
'lang',
|
|
35
32
|
'locale',
|
|
36
33
|
'language'
|
|
37
34
|
]);
|
|
38
|
-
const
|
|
35
|
+
const normaliseSlashes = (path)=>{
|
|
39
36
|
const withoutDuplicateSlashes = path.replace(/\/+/g, '/');
|
|
40
37
|
const withLeadingSlash = withoutDuplicateSlashes.startsWith('/') ? withoutDuplicateSlashes : `/${withoutDuplicateSlashes}`;
|
|
41
|
-
|
|
42
|
-
return withoutTrailingSlash.replace(/\[(.+?)\]/g, ':$1');
|
|
38
|
+
return withLeadingSlash.length > 1 ? withLeadingSlash.replace(/\/+$/, '') : withLeadingSlash;
|
|
43
39
|
};
|
|
40
|
+
const normalisePathPattern = (path)=>normaliseSlashes(path).replace(/\[(.+?)\]/g, ':$1');
|
|
41
|
+
const normalisePathname = (pathname)=>normaliseSlashes(pathname);
|
|
44
42
|
const normaliseRoutePath = (path)=>{
|
|
45
43
|
const normalized = normalisePathPattern(path);
|
|
46
44
|
return '/' === normalized ? '' : normalized.slice(1);
|
|
@@ -66,16 +64,12 @@ const getLeadingLocaleParam = (path)=>{
|
|
|
66
64
|
return getLocaleParamSegment(segments[0] || '');
|
|
67
65
|
};
|
|
68
66
|
const resolveLocalisedUrlsConfig = (option)=>{
|
|
69
|
-
if (
|
|
70
|
-
enabled: false,
|
|
71
|
-
map: {}
|
|
72
|
-
};
|
|
73
|
-
if (option && 'object' == typeof option) return {
|
|
67
|
+
if (option && 'object' == typeof option && Object.keys(option).length > 0) return {
|
|
74
68
|
enabled: true,
|
|
75
69
|
map: option
|
|
76
70
|
};
|
|
77
71
|
return {
|
|
78
|
-
enabled:
|
|
72
|
+
enabled: false,
|
|
79
73
|
map: {}
|
|
80
74
|
};
|
|
81
75
|
};
|
|
@@ -132,7 +126,7 @@ const transformLocalisedRoute = (route, parentCanonicalPath, parentLocalisedPath
|
|
|
132
126
|
if (!localisedUrlEntry) return [
|
|
133
127
|
baseRoute
|
|
134
128
|
];
|
|
135
|
-
return getLocalisedRoutePaths(canonicalPath, parentLocalisedPaths, languages, localisedUrlEntry).map((localisedPath, index)=>cloneRouteWithLocalisedPath(baseRoute, localisedPath, index));
|
|
129
|
+
return getLocalisedRoutePaths(canonicalPath, parentLocalisedPaths, languages, localisedUrlEntry).map((localisedPath, index)=>cloneRouteWithLocalisedPath(baseRoute, localisedPath, index, canonicalPath));
|
|
136
130
|
};
|
|
137
131
|
const legalRouteIdPart = (value)=>value.replace(/[^a-zA-Z0-9_$-]+/g, '_').replace(/^_+|_+$/g, '') || 'index';
|
|
138
132
|
const suffixRouteIds = (route, suffix)=>{
|
|
@@ -147,13 +141,14 @@ const suffixRouteIds = (route, suffix)=>{
|
|
|
147
141
|
} : {}
|
|
148
142
|
};
|
|
149
143
|
};
|
|
150
|
-
const cloneRouteWithLocalisedPath = (route, path, index)=>{
|
|
144
|
+
const cloneRouteWithLocalisedPath = (route, path, index, canonicalPath)=>{
|
|
151
145
|
const leadingLocaleParam = getLeadingLocaleParam(route.path);
|
|
152
146
|
const localisedPath = leadingLocaleParam ? normaliseRoutePath(`${leadingLocaleParam}/${path}`) : path;
|
|
153
147
|
const routeWithPath = {
|
|
154
148
|
...route,
|
|
155
149
|
path: localisedPath
|
|
156
150
|
};
|
|
151
|
+
routeWithPath.modernCanonicalPath = canonicalPath;
|
|
157
152
|
return 0 === index ? routeWithPath : suffixRouteIds(routeWithPath, legalRouteIdPart(localisedPath));
|
|
158
153
|
};
|
|
159
154
|
const applyLocalisedUrlsToRoutes = (routes, languages, localisedUrls)=>{
|
|
@@ -166,9 +161,13 @@ const applyLocalisedUrlsToRoutes = (routes, languages, localisedUrls)=>{
|
|
|
166
161
|
};
|
|
167
162
|
const escapeRegExp = (value)=>value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
168
163
|
const getParamName = (segment)=>segment.slice(1).replace(/\?$/, '');
|
|
164
|
+
const compiledPathPatternCache = new Map();
|
|
169
165
|
const compilePathPattern = (pattern)=>{
|
|
166
|
+
const normalizedPattern = normalisePathPattern(pattern);
|
|
167
|
+
const cached = compiledPathPatternCache.get(normalizedPattern);
|
|
168
|
+
if (cached) return cached;
|
|
170
169
|
const names = [];
|
|
171
|
-
const segments =
|
|
170
|
+
const segments = normalizedPattern.split('/').filter(Boolean);
|
|
172
171
|
const source = segments.map((segment)=>{
|
|
173
172
|
if (segment.startsWith(':')) {
|
|
174
173
|
names.push(getParamName(segment));
|
|
@@ -181,19 +180,55 @@ const compilePathPattern = (pattern)=>{
|
|
|
181
180
|
}
|
|
182
181
|
return `/${escapeRegExp(segment)}`;
|
|
183
182
|
}).join('');
|
|
184
|
-
|
|
183
|
+
const compiled = {
|
|
185
184
|
names,
|
|
186
185
|
regexp: new RegExp(`^${source || '/'}$`)
|
|
187
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
|
+
}
|
|
188
220
|
};
|
|
189
221
|
const matchPathPattern = (pathname, pattern)=>{
|
|
190
222
|
const { names, regexp } = compilePathPattern(pattern);
|
|
191
|
-
const match = regexp.exec(
|
|
223
|
+
const match = regexp.exec(normalisePathname(pathname));
|
|
192
224
|
if (!match) return null;
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
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;
|
|
197
232
|
};
|
|
198
233
|
const buildPathFromPattern = (pattern, params)=>{
|
|
199
234
|
const segments = normalisePathPattern(pattern).split('/').filter(Boolean);
|
|
@@ -208,26 +243,105 @@ const buildPathFromPattern = (pattern, params)=>{
|
|
|
208
243
|
return `/${path}`;
|
|
209
244
|
};
|
|
210
245
|
const resolveLocalisedPath = (pathname, targetLanguage, languages, localisedUrls)=>{
|
|
211
|
-
const normalizedPathname =
|
|
212
|
-
|
|
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){
|
|
213
253
|
const targetPattern = localisedUrlEntry[targetLanguage];
|
|
214
|
-
if (targetPattern)
|
|
254
|
+
if (!targetPattern) continue;
|
|
255
|
+
const params = matchPathPattern(normalizedPathname, canonicalPattern);
|
|
256
|
+
if (params) return buildPathFromPattern(targetPattern, params);
|
|
257
|
+
}
|
|
258
|
+
const localisedCandidates = sortPatternsBySpecificity(Object.values(localisedUrls).flatMap((localisedUrlEntry)=>{
|
|
259
|
+
const targetPattern = localisedUrlEntry[targetLanguage];
|
|
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);
|
|
270
|
+
}
|
|
271
|
+
return normalizedPathname;
|
|
272
|
+
};
|
|
273
|
+
const resolveCanonicalLocalisedPath = (pathname, languages, localisedUrls)=>{
|
|
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){
|
|
281
|
+
const canonicalParams = matchPathPattern(normalizedPathname, canonicalPattern);
|
|
282
|
+
if (canonicalParams) return buildPathFromPattern(canonicalPattern, canonicalParams);
|
|
283
|
+
for (const language of languages){
|
|
215
284
|
const sourcePattern = localisedUrlEntry[language];
|
|
216
285
|
if (!sourcePattern) continue;
|
|
217
286
|
const params = matchPathPattern(normalizedPathname, sourcePattern);
|
|
218
|
-
if (params) return buildPathFromPattern(
|
|
287
|
+
if (params) return buildPathFromPattern(canonicalPattern, params);
|
|
219
288
|
}
|
|
220
289
|
}
|
|
221
290
|
return normalizedPathname;
|
|
222
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
|
+
};
|
|
312
|
+
__webpack_require__.d(__webpack_exports__, {}, {
|
|
313
|
+
applyLocalisedUrlsToRoutes: applyLocalisedUrlsToRoutes,
|
|
314
|
+
buildPathFromPattern: buildPathFromPattern,
|
|
315
|
+
canonicalTargetPathname: canonicalTargetPathname,
|
|
316
|
+
localiseTargetPathname: localiseTargetPathname,
|
|
317
|
+
matchPathPattern: matchPathPattern,
|
|
318
|
+
normalisePathPattern: normalisePathPattern,
|
|
319
|
+
normalisePathname: normalisePathname,
|
|
320
|
+
resolveCanonicalLocalisedPath: resolveCanonicalLocalisedPath,
|
|
321
|
+
resolveLocalisedPath: resolveLocalisedPath,
|
|
322
|
+
resolveLocalisedUrlsConfig: resolveLocalisedUrlsConfig,
|
|
323
|
+
validateLocalisedUrls: validateLocalisedUrls
|
|
324
|
+
});
|
|
223
325
|
exports.applyLocalisedUrlsToRoutes = __webpack_exports__.applyLocalisedUrlsToRoutes;
|
|
326
|
+
exports.buildPathFromPattern = __webpack_exports__.buildPathFromPattern;
|
|
327
|
+
exports.canonicalTargetPathname = __webpack_exports__.canonicalTargetPathname;
|
|
328
|
+
exports.localiseTargetPathname = __webpack_exports__.localiseTargetPathname;
|
|
329
|
+
exports.matchPathPattern = __webpack_exports__.matchPathPattern;
|
|
224
330
|
exports.normalisePathPattern = __webpack_exports__.normalisePathPattern;
|
|
331
|
+
exports.normalisePathname = __webpack_exports__.normalisePathname;
|
|
332
|
+
exports.resolveCanonicalLocalisedPath = __webpack_exports__.resolveCanonicalLocalisedPath;
|
|
225
333
|
exports.resolveLocalisedPath = __webpack_exports__.resolveLocalisedPath;
|
|
226
334
|
exports.resolveLocalisedUrlsConfig = __webpack_exports__.resolveLocalisedUrlsConfig;
|
|
227
335
|
exports.validateLocalisedUrls = __webpack_exports__.validateLocalisedUrls;
|
|
228
336
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
229
337
|
"applyLocalisedUrlsToRoutes",
|
|
338
|
+
"buildPathFromPattern",
|
|
339
|
+
"canonicalTargetPathname",
|
|
340
|
+
"localiseTargetPathname",
|
|
341
|
+
"matchPathPattern",
|
|
230
342
|
"normalisePathPattern",
|
|
343
|
+
"normalisePathname",
|
|
344
|
+
"resolveCanonicalLocalisedPath",
|
|
231
345
|
"resolveLocalisedPath",
|
|
232
346
|
"resolveLocalisedUrlsConfig",
|
|
233
347
|
"validateLocalisedUrls"
|
package/dist/cjs/shared/utils.js
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
var __webpack_require__ = {};
|
|
3
3
|
(()=>{
|
|
4
|
-
__webpack_require__.d = (exports1,
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
__webpack_require__.d = (exports1, getters, values)=>{
|
|
5
|
+
var define = (defs, kind)=>{
|
|
6
|
+
for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
|
|
7
|
+
enumerable: true,
|
|
8
|
+
[kind]: defs[key]
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
define(getters, "get");
|
|
12
|
+
define(values, "value");
|
|
9
13
|
};
|
|
10
14
|
})();
|
|
11
15
|
(()=>{
|
|
@@ -23,12 +27,6 @@ var __webpack_require__ = {};
|
|
|
23
27
|
})();
|
|
24
28
|
var __webpack_exports__ = {};
|
|
25
29
|
__webpack_require__.r(__webpack_exports__);
|
|
26
|
-
__webpack_require__.d(__webpack_exports__, {
|
|
27
|
-
getBackendOptions: ()=>getBackendOptions,
|
|
28
|
-
getEntryConfig: ()=>getEntryConfig,
|
|
29
|
-
getLocaleDetectionOptions: ()=>getLocaleDetectionOptions,
|
|
30
|
-
removeEntryConfigKey: ()=>removeEntryConfigKey
|
|
31
|
-
});
|
|
32
30
|
function getEntryConfig(entryName, config, entryKey) {
|
|
33
31
|
const entryConfigMap = config[entryKey];
|
|
34
32
|
return entryConfigMap?.[entryName];
|
|
@@ -63,6 +61,12 @@ function getBackendOptions(entryName, backend) {
|
|
|
63
61
|
if ('backendOptionsByEntry' in fullConfig) return removeEntryConfigKey(fullConfig, 'backendOptionsByEntry');
|
|
64
62
|
return backend;
|
|
65
63
|
}
|
|
64
|
+
__webpack_require__.d(__webpack_exports__, {
|
|
65
|
+
getBackendOptions: ()=>getBackendOptions,
|
|
66
|
+
getEntryConfig: ()=>getEntryConfig,
|
|
67
|
+
getLocaleDetectionOptions: ()=>getLocaleDetectionOptions,
|
|
68
|
+
removeEntryConfigKey: ()=>removeEntryConfigKey
|
|
69
|
+
});
|
|
66
70
|
exports.getBackendOptions = __webpack_exports__.getBackendOptions;
|
|
67
71
|
exports.getEntryConfig = __webpack_exports__.getEntryConfig;
|
|
68
72
|
exports.getLocaleDetectionOptions = __webpack_exports__.getLocaleDetectionOptions;
|
package/dist/esm/cli/index.mjs
CHANGED
|
@@ -1,38 +1,8 @@
|
|
|
1
1
|
import { getPublicDirRoutePrefixes } from "@modern-js/server-core";
|
|
2
|
-
import fs from "fs";
|
|
3
|
-
import path from "path";
|
|
4
2
|
import { applyLocalisedUrlsToRoutes, resolveLocalisedUrlsConfig } from "../shared/localisedUrls.mjs";
|
|
5
3
|
import { getBackendOptions, getLocaleDetectionOptions } from "../shared/utils.mjs";
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
if (!fs.existsSync(dirPath) || !fs.statSync(dirPath).isDirectory()) return false;
|
|
9
|
-
const entries = fs.readdirSync(dirPath);
|
|
10
|
-
for (const entry of entries){
|
|
11
|
-
const entryPath = path.join(dirPath, entry);
|
|
12
|
-
const stat = fs.statSync(entryPath);
|
|
13
|
-
if (stat.isFile() && entry.endsWith('.json')) return true;
|
|
14
|
-
if (stat.isDirectory()) {
|
|
15
|
-
if (hasJsonFiles(entryPath)) return true;
|
|
16
|
-
}
|
|
17
|
-
}
|
|
18
|
-
return false;
|
|
19
|
-
} catch {
|
|
20
|
-
return false;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
function detectLocalesDirectory(appDirectory, normalizedConfig) {
|
|
24
|
-
const rootLocalesPath = path.join(appDirectory, 'locales');
|
|
25
|
-
if (hasJsonFiles(rootLocalesPath)) return true;
|
|
26
|
-
const configPublicPath = path.join(appDirectory, 'config', 'public', 'locales');
|
|
27
|
-
if (hasJsonFiles(configPublicPath)) return true;
|
|
28
|
-
const publicDir = normalizedConfig?.server?.publicDir;
|
|
29
|
-
if (publicDir) {
|
|
30
|
-
const publicDirPath = Array.isArray(publicDir) ? publicDir[0] : publicDir;
|
|
31
|
-
const localesPath = path.isAbsolute(publicDirPath) ? path.join(publicDirPath, 'locales') : path.join(appDirectory, publicDirPath, 'locales');
|
|
32
|
-
if (hasJsonFiles(localesPath)) return true;
|
|
33
|
-
}
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
4
|
+
import { applyDetectedBackendPaths, detectLocalesDirectory } from "./locales.mjs";
|
|
5
|
+
import "../runtime/types.mjs";
|
|
36
6
|
const i18nPlugin = (options = {})=>({
|
|
37
7
|
name: '@modern-js/plugin-i18n',
|
|
38
8
|
setup: (api)=>{
|
|
@@ -42,26 +12,16 @@ const i18nPlugin = (options = {})=>({
|
|
|
42
12
|
let backendOptions;
|
|
43
13
|
const { appDirectory } = api.getAppContext();
|
|
44
14
|
const normalizedConfig = api.getNormalizedConfig();
|
|
15
|
+
const detectedLocales = detectLocalesDirectory(appDirectory, normalizedConfig);
|
|
45
16
|
if (backend) {
|
|
46
17
|
const entryBackendOptions = getBackendOptions(entrypoint.entryName, backend);
|
|
47
|
-
|
|
48
|
-
else if (entryBackendOptions?.loadPath || entryBackendOptions?.addPath) backendOptions = {
|
|
18
|
+
backendOptions = entryBackendOptions?.enabled === false ? entryBackendOptions : detectedLocales ? applyDetectedBackendPaths(entryBackendOptions, detectedLocales) : entryBackendOptions?.loadPath || entryBackendOptions?.addPath ? {
|
|
49
19
|
...entryBackendOptions,
|
|
50
20
|
enabled: true
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
...entryBackendOptions,
|
|
56
|
-
enabled: true
|
|
57
|
-
} : entryBackendOptions;
|
|
58
|
-
} else backendOptions = entryBackendOptions;
|
|
59
|
-
} else {
|
|
60
|
-
const hasLocales = detectLocalesDirectory(appDirectory, normalizedConfig);
|
|
61
|
-
if (hasLocales) backendOptions = getBackendOptions(entrypoint.entryName, {
|
|
62
|
-
enabled: true
|
|
63
|
-
});
|
|
64
|
-
}
|
|
21
|
+
} : entryBackendOptions;
|
|
22
|
+
} else if (detectedLocales) backendOptions = applyDetectedBackendPaths(getBackendOptions(entrypoint.entryName, {
|
|
23
|
+
enabled: true
|
|
24
|
+
}), detectedLocales);
|
|
65
25
|
const { metaName } = api.getAppContext();
|
|
66
26
|
let extendedConfig = restOptions;
|
|
67
27
|
if (transformRuntimeConfig) extendedConfig = transformRuntimeConfig(restOptions, entrypoint);
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { getPublicDirRoutePrefixes, normalizePublicDir, normalizePublicDirPath } from "@modern-js/server-core";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
const LOCALES_RESOURCE_PATTERN = '{{lng}}/{{ns}}.json';
|
|
5
|
+
function hasJsonFiles(dirPath) {
|
|
6
|
+
try {
|
|
7
|
+
if (!fs.existsSync(dirPath) || !fs.statSync(dirPath).isDirectory()) return false;
|
|
8
|
+
const entries = fs.readdirSync(dirPath);
|
|
9
|
+
for (const entry of entries){
|
|
10
|
+
const entryPath = path.join(dirPath, entry);
|
|
11
|
+
const stat = fs.statSync(entryPath);
|
|
12
|
+
if (stat.isFile() && entry.endsWith('.json')) return true;
|
|
13
|
+
if (stat.isDirectory() && hasJsonFiles(entryPath)) return true;
|
|
14
|
+
}
|
|
15
|
+
return false;
|
|
16
|
+
} catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function toPosixPath(filePath) {
|
|
21
|
+
return filePath.split(path.sep).join(path.posix.sep);
|
|
22
|
+
}
|
|
23
|
+
function getPublicDirOutputPath(publicDir) {
|
|
24
|
+
return normalizePublicDirPath(publicDir);
|
|
25
|
+
}
|
|
26
|
+
function buildDetectedLocalesDirectory(clientBasePath, serverBasePath, serverBasePathCandidates = [
|
|
27
|
+
serverBasePath
|
|
28
|
+
]) {
|
|
29
|
+
const normalizedClientBasePath = clientBasePath.startsWith('/') ? clientBasePath : `/${clientBasePath}`;
|
|
30
|
+
const normalizedServerBasePath = toPosixPath(serverBasePath).replace(/\/$/, '');
|
|
31
|
+
const normalizedServerBasePathCandidates = Array.from(new Set(serverBasePathCandidates.map((candidate)=>toPosixPath(candidate).replace(/\/$/, ''))));
|
|
32
|
+
const serverLoadPaths = normalizedServerBasePathCandidates.map((candidate)=>`${candidate}/${LOCALES_RESOURCE_PATTERN}`);
|
|
33
|
+
const serverAddPaths = normalizedServerBasePathCandidates.map((candidate)=>`${candidate}/${LOCALES_RESOURCE_PATTERN}`);
|
|
34
|
+
return {
|
|
35
|
+
loadPath: `${normalizedClientBasePath}/${LOCALES_RESOURCE_PATTERN}`,
|
|
36
|
+
addPath: `${normalizedClientBasePath}/${LOCALES_RESOURCE_PATTERN}`,
|
|
37
|
+
serverLoadPath: `${normalizedServerBasePath}/${LOCALES_RESOURCE_PATTERN}`,
|
|
38
|
+
serverAddPath: `${normalizedServerBasePath}/${LOCALES_RESOURCE_PATTERN}`,
|
|
39
|
+
serverLoadPaths,
|
|
40
|
+
serverAddPaths
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function detectLocalesDirectory(appDirectory, normalizedConfig) {
|
|
44
|
+
const publicDirs = normalizePublicDir(normalizedConfig?.server?.publicDir);
|
|
45
|
+
const publicDirPrefixes = getPublicDirRoutePrefixes(normalizedConfig?.server?.publicDir);
|
|
46
|
+
for(let index = 0; index < publicDirs.length; index++){
|
|
47
|
+
const publicDir = publicDirs[index];
|
|
48
|
+
if (path.isAbsolute(publicDir)) continue;
|
|
49
|
+
const publicDirPath = path.join(appDirectory, publicDir);
|
|
50
|
+
const publicDirPrefix = publicDirPrefixes[index] || `/${normalizePublicDirPath(publicDir)}`;
|
|
51
|
+
const publicDirOutputPath = getPublicDirOutputPath(publicDir);
|
|
52
|
+
if ('locales' === path.basename(normalizePublicDirPath(publicDir)) && hasJsonFiles(publicDirPath)) return buildDetectedLocalesDirectory(publicDirPrefix, `./${publicDirOutputPath}`);
|
|
53
|
+
const localesPath = path.join(publicDirPath, 'locales');
|
|
54
|
+
if (hasJsonFiles(localesPath)) return buildDetectedLocalesDirectory(`${publicDirPrefix}/locales`, `./${publicDirOutputPath}/locales`);
|
|
55
|
+
}
|
|
56
|
+
const configPublicPath = path.join(appDirectory, 'config', 'public', 'locales');
|
|
57
|
+
if (hasJsonFiles(configPublicPath)) return buildDetectedLocalesDirectory('/locales', './public/locales', [
|
|
58
|
+
'./config/public/locales',
|
|
59
|
+
'./public/locales'
|
|
60
|
+
]);
|
|
61
|
+
const rootLocalesPath = path.join(appDirectory, 'locales');
|
|
62
|
+
if (hasJsonFiles(rootLocalesPath)) return buildDetectedLocalesDirectory('/locales', './locales');
|
|
63
|
+
}
|
|
64
|
+
function applyDetectedBackendPaths(backendOptions, detectedLocales) {
|
|
65
|
+
if (!detectedLocales) return backendOptions;
|
|
66
|
+
const backendWithDetectedPaths = {
|
|
67
|
+
...backendOptions,
|
|
68
|
+
enabled: true,
|
|
69
|
+
loadPath: backendOptions.loadPath ?? detectedLocales.loadPath,
|
|
70
|
+
addPath: backendOptions.addPath ?? detectedLocales.addPath,
|
|
71
|
+
serverLoadPath: backendOptions.serverLoadPath ?? detectedLocales.serverLoadPath,
|
|
72
|
+
serverLoadPaths: backendOptions.serverLoadPath || backendOptions.serverLoadPaths ? backendOptions.serverLoadPaths : detectedLocales.serverLoadPaths,
|
|
73
|
+
serverAddPath: backendOptions.serverAddPath ?? detectedLocales.serverAddPath,
|
|
74
|
+
serverAddPaths: backendOptions.serverAddPath || backendOptions.serverAddPaths ? backendOptions.serverAddPaths : detectedLocales.serverAddPaths,
|
|
75
|
+
_detectedLoadPath: detectedLocales.loadPath,
|
|
76
|
+
_detectedAddPath: detectedLocales.addPath
|
|
77
|
+
};
|
|
78
|
+
return backendWithDetectedPaths;
|
|
79
|
+
}
|
|
80
|
+
export { applyDetectedBackendPaths, detectLocalesDirectory };
|
|
@@ -1,20 +1,13 @@
|
|
|
1
1
|
import { jsx } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
import { buildLocalizedUrl } from "./utils.mjs";
|
|
2
|
+
import { Link } from "./Link.mjs";
|
|
3
|
+
let warnedDeprecation = false;
|
|
5
4
|
const I18nLink = ({ to, children, ...props })=>{
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
if ('development' === process.env.NODE_ENV && hasRouter && !params.lang) console.warn("I18nLink is being used outside of a :lang dynamic route context. This may cause unexpected behavior. Please ensure I18nLink is used within a route that has a :lang parameter.");
|
|
11
|
-
if (!hasRouter || !Link) return /*#__PURE__*/ jsx("a", {
|
|
12
|
-
href: localizedTo,
|
|
13
|
-
...props,
|
|
14
|
-
children: children
|
|
15
|
-
});
|
|
5
|
+
if ('development' === process.env.NODE_ENV && !warnedDeprecation) {
|
|
6
|
+
warnedDeprecation = true;
|
|
7
|
+
console.warn("[plugin-i18n] I18nLink is deprecated. Import { Link } from '@modern-js/plugin-i18n/runtime' instead — it accepts the same language-agnostic `to` values.");
|
|
8
|
+
}
|
|
16
9
|
return /*#__PURE__*/ jsx(Link, {
|
|
17
|
-
to:
|
|
10
|
+
to: to,
|
|
18
11
|
...props,
|
|
19
12
|
children: children
|
|
20
13
|
});
|