@bleedingdev/modern-js-plugin-i18n 3.2.0-ultramodern.98 → 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.
Files changed (98) hide show
  1. package/README.md +221 -11
  2. package/dist/cjs/cli/index.js +17 -64
  3. package/dist/cjs/cli/locales.js +132 -0
  4. package/dist/cjs/runtime/I18nLink.js +17 -20
  5. package/dist/cjs/runtime/Link.js +264 -0
  6. package/dist/cjs/runtime/canonicalRoutes.js +18 -0
  7. package/dist/cjs/runtime/context.js +9 -5
  8. package/dist/cjs/runtime/hooks.js +9 -5
  9. package/dist/cjs/runtime/i18n/backend/config.js +9 -5
  10. package/dist/cjs/runtime/i18n/backend/defaults.js +20 -11
  11. package/dist/cjs/runtime/i18n/backend/defaults.node.js +79 -10
  12. package/dist/cjs/runtime/i18n/backend/index.js +9 -5
  13. package/dist/cjs/runtime/i18n/backend/middleware.common.js +9 -5
  14. package/dist/cjs/runtime/i18n/backend/middleware.js +9 -5
  15. package/dist/cjs/runtime/i18n/backend/middleware.node.js +9 -5
  16. package/dist/cjs/runtime/i18n/backend/sdk-backend.js +9 -5
  17. package/dist/cjs/runtime/i18n/backend/sdk-event.js +16 -11
  18. package/dist/cjs/runtime/i18n/detection/config.js +9 -5
  19. package/dist/cjs/runtime/i18n/detection/index.js +9 -5
  20. package/dist/cjs/runtime/i18n/detection/middleware.js +9 -5
  21. package/dist/cjs/runtime/i18n/detection/middleware.node.js +9 -5
  22. package/dist/cjs/runtime/i18n/index.js +9 -5
  23. package/dist/cjs/runtime/i18n/instance.js +17 -13
  24. package/dist/cjs/runtime/i18n/react-i18next.js +12 -8
  25. package/dist/cjs/runtime/i18n/utils.js +9 -5
  26. package/dist/cjs/runtime/index.js +32 -5
  27. package/dist/cjs/runtime/localizedPaths.js +102 -0
  28. package/dist/cjs/runtime/routerAdapter.js +11 -7
  29. package/dist/cjs/runtime/utils.js +31 -17
  30. package/dist/cjs/server/index.js +10 -14
  31. package/dist/cjs/shared/deepMerge.js +12 -8
  32. package/dist/cjs/shared/detection.js +9 -5
  33. package/dist/cjs/shared/localisedUrls.js +148 -34
  34. package/dist/cjs/shared/utils.js +15 -11
  35. package/dist/esm/cli/index.mjs +8 -48
  36. package/dist/esm/cli/locales.mjs +80 -0
  37. package/dist/esm/runtime/I18nLink.mjs +7 -14
  38. package/dist/esm/runtime/Link.mjs +221 -0
  39. package/dist/esm/runtime/canonicalRoutes.mjs +0 -0
  40. package/dist/esm/runtime/i18n/backend/defaults.mjs +6 -2
  41. package/dist/esm/runtime/i18n/backend/defaults.node.mjs +56 -5
  42. package/dist/esm/runtime/index.mjs +4 -2
  43. package/dist/esm/runtime/localizedPaths.mjs +55 -0
  44. package/dist/esm/runtime/routerAdapter.mjs +3 -3
  45. package/dist/esm/runtime/utils.mjs +19 -12
  46. package/dist/esm/server/index.mjs +2 -10
  47. package/dist/esm/shared/localisedUrls.mjs +115 -23
  48. package/dist/esm-node/cli/index.mjs +8 -48
  49. package/dist/esm-node/cli/locales.mjs +81 -0
  50. package/dist/esm-node/runtime/I18nLink.mjs +7 -14
  51. package/dist/esm-node/runtime/Link.mjs +222 -0
  52. package/dist/esm-node/runtime/canonicalRoutes.mjs +1 -0
  53. package/dist/esm-node/runtime/i18n/backend/defaults.mjs +6 -2
  54. package/dist/esm-node/runtime/i18n/backend/defaults.node.mjs +56 -5
  55. package/dist/esm-node/runtime/index.mjs +4 -2
  56. package/dist/esm-node/runtime/localizedPaths.mjs +56 -0
  57. package/dist/esm-node/runtime/routerAdapter.mjs +3 -3
  58. package/dist/esm-node/runtime/utils.mjs +19 -12
  59. package/dist/esm-node/server/index.mjs +2 -10
  60. package/dist/esm-node/shared/localisedUrls.mjs +115 -23
  61. package/dist/types/cli/index.d.ts +1 -0
  62. package/dist/types/cli/locales.d.ts +17 -0
  63. package/dist/types/runtime/I18nLink.d.ts +4 -13
  64. package/dist/types/runtime/Link.d.ts +66 -0
  65. package/dist/types/runtime/canonicalRoutes.d.ts +60 -0
  66. package/dist/types/runtime/i18n/backend/defaults.d.ts +10 -7
  67. package/dist/types/runtime/i18n/backend/defaults.node.d.ts +13 -4
  68. package/dist/types/runtime/index.d.ts +5 -1
  69. package/dist/types/runtime/localizedPaths.d.ts +39 -0
  70. package/dist/types/runtime/types.d.ts +1 -1
  71. package/dist/types/runtime/utils.d.ts +13 -4
  72. package/dist/types/shared/localisedUrls.d.ts +23 -0
  73. package/dist/types/shared/type.d.ts +27 -5
  74. package/package.json +28 -25
  75. package/rstest.config.mts +7 -2
  76. package/src/cli/index.ts +25 -98
  77. package/src/cli/locales.ts +186 -0
  78. package/src/runtime/I18nLink.tsx +13 -44
  79. package/src/runtime/Link.tsx +430 -0
  80. package/src/runtime/canonicalRoutes.ts +93 -0
  81. package/src/runtime/i18n/backend/defaults.node.ts +112 -7
  82. package/src/runtime/i18n/backend/defaults.ts +20 -18
  83. package/src/runtime/index.tsx +24 -2
  84. package/src/runtime/localizedPaths.ts +107 -0
  85. package/src/runtime/routerAdapter.tsx +4 -5
  86. package/src/runtime/types.ts +1 -1
  87. package/src/runtime/utils.ts +33 -26
  88. package/src/server/index.ts +7 -17
  89. package/src/shared/localisedUrls.ts +256 -26
  90. package/src/shared/type.ts +27 -5
  91. package/tests/backendDefaults.test.ts +51 -0
  92. package/tests/i18nUtils.test.ts +10 -3
  93. package/tests/link.test.tsx +525 -0
  94. package/tests/linkTypes.test.ts +28 -0
  95. package/tests/localisedUrls.test.ts +224 -0
  96. package/tests/routerAdapter.test.tsx +86 -12
  97. package/tests/type-fixture/linkTypes.fixture.tsx +51 -0
  98. 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, definition)=>{
5
- for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
6
- enumerable: true,
7
- get: definition[key]
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, definition)=>{
5
- for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
6
- enumerable: true,
7
- get: definition[key]
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 normalisePathPattern = (path)=>{
35
+ const normaliseSlashes = (path)=>{
39
36
  const withoutDuplicateSlashes = path.replace(/\/+/g, '/');
40
37
  const withLeadingSlash = withoutDuplicateSlashes.startsWith('/') ? withoutDuplicateSlashes : `/${withoutDuplicateSlashes}`;
41
- const withoutTrailingSlash = withLeadingSlash.length > 1 ? withLeadingSlash.replace(/\/+$/, '') : withLeadingSlash;
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 (false === option) return {
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: true,
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 = normalisePathPattern(pattern).split('/').filter(Boolean);
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
- return {
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(normalisePathPattern(pathname));
223
+ const match = regexp.exec(normalisePathname(pathname));
192
224
  if (!match) return null;
193
- return names.reduce((params, name, index)=>{
194
- params[name] = decodeURIComponent(match[index + 1] || '');
195
- return params;
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 = normalisePathPattern(pathname);
212
- for (const localisedUrlEntry of Object.values(localisedUrls)){
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) for (const language of languages){
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(targetPattern, params);
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"
@@ -1,11 +1,15 @@
1
1
  "use strict";
2
2
  var __webpack_require__ = {};
3
3
  (()=>{
4
- __webpack_require__.d = (exports1, definition)=>{
5
- for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
6
- enumerable: true,
7
- get: definition[key]
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;
@@ -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
- function hasJsonFiles(dirPath) {
7
- try {
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
- if (entryBackendOptions?.enabled === false) backendOptions = entryBackendOptions;
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
- else if (entryBackendOptions?.enabled !== true) {
53
- const hasLocales = detectLocalesDirectory(appDirectory, normalizedConfig);
54
- backendOptions = hasLocales ? {
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 { useModernI18n } from "./context.mjs";
3
- import { useI18nRouterAdapter } from "./routerAdapter.mjs";
4
- import { buildLocalizedUrl } from "./utils.mjs";
2
+ import { Link } from "./Link.mjs";
3
+ let warnedDeprecation = false;
5
4
  const I18nLink = ({ to, children, ...props })=>{
6
- const { Link, params, hasRouter } = useI18nRouterAdapter();
7
- const { language, supportedLanguages, localisedUrls } = useModernI18n();
8
- const currentLang = language;
9
- const localizedTo = buildLocalizedUrl(to, currentLang, supportedLanguages, localisedUrls);
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: localizedTo,
10
+ to: to,
18
11
  ...props,
19
12
  children: children
20
13
  });