@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
package/README.md CHANGED
@@ -2,20 +2,236 @@
2
2
  <a href="https://modernjs.dev" target="blank"><img src="https://lf3-static.bytednsdoc.com/obj/eden-cn/ylaelkeh7nuhfnuhf/modernjs-cover.png" width="300" alt="Modern.js Logo" /></a>
3
3
  </p>
4
4
 
5
- <h1 align="center">Modern.js</h1>
5
+ <h1 align="center">@modern-js/plugin-i18n</h1>
6
6
 
7
7
  <p align="center">
8
- A Progressive React Framework for modern web development.
8
+ Internationalization plugin for Modern.js, providing multi-language support with locale-aware routing and navigation.
9
9
  </p>
10
10
 
11
+ ## Features
12
+
13
+ - **Locale detection**: Supports detection from URL path, Cookie, LocalStorage, request headers, browser settings, and other sources.
14
+ - **Resource loading**: Supports HTTP static files, file system loading for SSR, custom SDK functions, and chained backend progressive loading.
15
+ - **Routing integration**: Automatically adds locale path prefixes and provides the `Link` component for language-agnostic navigation.
16
+ - **SSR support**: Detects the language on the server and injects it into the page to avoid language flicker.
17
+ - **TypeScript support**: Provides complete type definitions and type-safe route navigation.
18
+
11
19
  ## Getting Started
12
20
 
13
- Please follow [Quick Start](https://modernjs.dev/en/guides/get-started/quick-start) to get started with Modern.js.
21
+ For full documentation, see the [Modern.js Internationalization Guide](https://modernjs.dev/en/guides/advanced-features/international/).
22
+
23
+ ## Link Component
24
+
25
+ The `Link` component is the standard way to create navigation links in i18n-enabled applications. It automatically localizes language-agnostic canonical paths and provides type-safe navigation with params validation.
26
+
27
+ ### Basic Usage
28
+
29
+ ```tsx
30
+ import { Link } from '@modern-js/plugin-i18n/runtime';
31
+
32
+ function Navigation() {
33
+ return (
34
+ <nav>
35
+ <Link to="/">Home</Link>
36
+ <Link to="/about">About</Link>
37
+ <Link to="/talks/$slug" params={{ slug: 'my-talk' }} />
38
+ <Link to="/#work-with-me" />
39
+ </nav>
40
+ );
41
+ }
42
+ ```
43
+
44
+ ### Key Features
45
+
46
+ **Canonical path localization**: `to` accepts language-agnostic canonical paths. The Link automatically adds the locale prefix and applies `localisedUrls` slug mappings:
47
+
48
+ ```tsx
49
+ // When current language is 'cs' and localisedUrls maps '/platform' to '/platforma':
50
+ <Link to="/platform" />
51
+ // renders as: <a href="/cs/platforma">...</a>
52
+ ```
53
+
54
+ **Hash and query preservation**: Hash fragments and query strings in `to` are preserved during localization:
55
+
56
+ ```tsx
57
+ <Link to="/#work-with-me" /> // Cross-page anchor → /cs#work-with-me
58
+ <Link to="/talks?sort=date" /> // Search params preserved
59
+ ```
60
+
61
+ **Typed routes and params**: When used with TanStack Router codegen, `to` accepts typed canonical route paths with validated params:
62
+
63
+ ```tsx
64
+ // Route /talks/$slug exists → TypeScript validates this:
65
+ <Link to="/talks/$slug" params={{ slug: 'my-talk' }} />
66
+
67
+ // TypeScript error: route does not exist
68
+ <Link to="/talkz" />
69
+
70
+ // TypeScript error: missing required param
71
+ <Link to="/talks/$slug" />
72
+ ```
73
+
74
+ **Language-invariant active state**: The link is marked active when the location matches any localized variant of the canonical route:
75
+
76
+ ```tsx
77
+ // Matches /en/talks/my-talk, /cs/prednaska/muj-pohovor, etc.
78
+ <Link to="/talks/$slug" params={{ slug: 'my-talk' }} activeProps={{ className: 'active' }} />
79
+ ```
80
+
81
+ **Active state props and classes**:
82
+
83
+ ```tsx
84
+ <Link
85
+ to="/about"
86
+ activeProps={{ className: 'active-link' }}
87
+ activeOptions={{ exact: false }} // Nested routes also match
88
+ />
89
+ // When active, renders: <a href="..." className="active-link" data-status="active" aria-current="page">
90
+ ```
91
+
92
+ **External URLs and bare anchors render as plain `<a>` tags**:
93
+
94
+ ```tsx
95
+ <Link to="https://example.com" /> // → <a href="https://example.com">
96
+ <Link to="#contact" /> // → <a href="#contact">
97
+ ```
98
+
99
+ ### Props
100
+
101
+ - `to` (string): Canonical (language-agnostic) target path, optionally with `#hash` and `?query` suffixes.
102
+ - `params` (object, optional): Route param values for dynamic segments (e.g., `$slug`).
103
+ - `hash` (string, optional): Hash fragment (overrides any `#hash` in `to`).
104
+ - `search` (string | object, optional): Query string or object (overrides any `?query` in `to`).
105
+ - `hashScrollIntoView` (boolean | ScrollIntoViewOptions, optional): TanStack Router scroll behavior.
106
+ - `activeProps` (object, optional): Props applied when the link is active.
107
+ - `activeOptions` (object, optional): `{ exact?: boolean }` for active state matching.
108
+ - All standard `<a>` HTML attributes (className, style, onClick, etc.).
109
+
110
+ ## Localization Utilities
111
+
112
+ ### `localizePath(pathname, language, config)`
113
+
114
+ Localize a canonical pathname for a given language, applying language prefix and `localisedUrls` mapping:
115
+
116
+ ```tsx
117
+ import { localizePath } from '@modern-js/plugin-i18n/runtime';
118
+
119
+ localizePath('/about', 'cs', {
120
+ languages: ['en', 'cs'],
121
+ localisedUrls: { '/about': { en: '/about', cs: '/o-nas' } }
122
+ })
123
+ // → '/cs/o-nas'
124
+ ```
125
+
126
+ ### `canonicalPath(target, config)`
127
+
128
+ Reverse of `localizePath`: strip language prefix and reverse `localisedUrls` mapping:
129
+
130
+ ```tsx
131
+ import { canonicalPath } from '@modern-js/plugin-i18n/runtime';
132
+
133
+ canonicalPath('/cs/o-nas', {
134
+ languages: ['en', 'cs'],
135
+ localisedUrls: { '/about': { en: '/about', cs: '/o-nas' } }
136
+ })
137
+ // → '/about'
138
+ ```
139
+
140
+ ### `useLocalizedPaths()`
141
+
142
+ Hook for context-bound `localizePath` and `canonicalPath` (reads plugin config automatically):
143
+
144
+ ```tsx
145
+ import { useLocalizedPaths } from '@modern-js/plugin-i18n/runtime';
146
+
147
+ function MyComponent() {
148
+ const { localizePath, canonicalPath } = useLocalizedPaths();
149
+
150
+ const localized = localizePath('/about', 'cs');
151
+ const canonical = canonicalPath('/cs/o-nas');
152
+ }
153
+ ```
154
+
155
+ ### `useLocalizedLocation()`
156
+
157
+ Hook for hreflang tags and language switchers. Returns the current location's canonical path and per-language hrefs:
158
+
159
+ ```tsx
160
+ import { useLocalizedLocation } from '@modern-js/plugin-i18n/runtime';
161
+
162
+ function HrefLang() {
163
+ const { language, canonical, alternates } = useLocalizedLocation();
164
+
165
+ return (
166
+ <>
167
+ <link rel="canonical" href={alternates['en']} />
168
+ {Object.entries(alternates).map(([lang, href]) => (
169
+ <link key={lang} rel="alternate" hrefLang={lang} href={href} />
170
+ ))}
171
+ </>
172
+ );
173
+ }
174
+
175
+ function LanguageSwitcher() {
176
+ const { alternates } = useLocalizedLocation();
177
+
178
+ return (
179
+ <select onChange={(e) => window.location.href = alternates[e.target.value]}>
180
+ {Object.entries(alternates).map(([lang, href]) => (
181
+ <option key={lang} value={lang}>{lang}</option>
182
+ ))}
183
+ </select>
184
+ );
185
+ }
186
+ ```
187
+
188
+ ## Migration from Deprecated I18nLink
189
+
190
+ The deprecated `I18nLink` component is now an alias for `Link`. Update your code:
191
+
192
+ | Old Pattern | New Pattern | Notes |
193
+ |-------------|-------------|-------|
194
+ | `import { I18nLink }` | `import { Link }` | Drop the `I18n` prefix. |
195
+ | `<I18nLink to="/">` | `<Link to="/">` | Identical props and behavior. |
196
+ | Hand-rolled `localizePath` helper | `useLocalizedPaths()` hook | Reads config from context automatically. |
197
+ | Hand-rolled `matchPattern` for active state | `Link` with `activeProps`/`activeOptions` | Language-invariant matching. |
198
+ | Manual hreflang blocks | `useLocalizedLocation()` hook | Generates per-language hrefs automatically. |
199
+
200
+ **I18nLink deprecation notice:** `I18nLink` is deprecated and will be removed in a future version. A one-time development console warning is logged when used.
201
+
202
+ ```tsx
203
+ // ❌ Old
204
+ import { I18nLink } from '@modern-js/plugin-i18n/runtime';
205
+ <I18nLink to="/about">About</I18nLink>
206
+
207
+ // ✅ New
208
+ import { Link } from '@modern-js/plugin-i18n/runtime';
209
+ <Link to="/about">About</Link>
210
+ ```
211
+
212
+ ## Exported Types
213
+
214
+ For TypeScript projects, the following types are exported from `@modern-js/plugin-i18n/runtime`:
215
+
216
+ - `LinkProps<TTo>`: Props type for the `Link` component with typed `to` and `params`.
217
+ - `LinkBaseProps`: Base props shared across router frameworks.
218
+ - `LinkParams`: Record of route param values.
219
+ - `LinkActiveOptions`: Active state matching options.
220
+ - `UltramodernCanonicalRoutes`: Canonical route map (emitted by TanStack Router codegen).
221
+ - `CanonicalRoutePath`: Typed canonical route path.
222
+ - `AllowedLinkTarget`: Union of all valid canonical routes (when codegen is active).
223
+
224
+ ## Exported Utilities
225
+
226
+ - `buildLocalizedUrl(pathname, language, languages, localisedUrls)`: Localize a pathname (used internally by `Link`).
227
+ - `splitUrlTarget(urlString)`: Parse a URL into pathname, search, and hash.
14
228
 
15
229
  ## Documentation
16
230
 
17
- - [English Documentation](https://modernjs.dev/en/)
18
- - [中文文档](https://modernjs.dev)
231
+ For complete documentation, including locale detection, resource loading, SSR, and custom route configuration, see:
232
+
233
+ - [English Documentation](https://modernjs.dev/en/guides/advanced-features/international/)
234
+ - [中文文档](https://modernjs.dev/guides/advanced-features/international/)
19
235
 
20
236
  ## Contributing
21
237
 
@@ -24,9 +240,3 @@ Please read the [Contributing Guide](https://github.com/web-infra-dev/modern.js/
24
240
  ## License
25
241
 
26
242
  Modern.js is [MIT licensed](https://github.com/web-infra-dev/modern.js/blob/main/LICENSE).
27
-
28
- ## Credist
29
-
30
- Thanks to:
31
-
32
- - [@loadable/webpack-plugin](https://github.com/gregberge/loadable-components) to create a webpack plugin prepare for loadable usage in ssr.
@@ -1,20 +1,15 @@
1
1
  "use strict";
2
2
  var __webpack_require__ = {};
3
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
- })();
12
- (()=>{
13
- __webpack_require__.d = (exports1, definition)=>{
14
- for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
15
- enumerable: true,
16
- get: definition[key]
17
- });
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");
18
13
  };
19
14
  })();
20
15
  (()=>{
@@ -37,42 +32,10 @@ __webpack_require__.d(__webpack_exports__, {
37
32
  i18nPlugin: ()=>i18nPlugin
38
33
  });
39
34
  const server_core_namespaceObject = require("@modern-js/server-core");
40
- const external_fs_namespaceObject = require("fs");
41
- var external_fs_default = /*#__PURE__*/ __webpack_require__.n(external_fs_namespaceObject);
42
- const external_path_namespaceObject = require("path");
43
- var external_path_default = /*#__PURE__*/ __webpack_require__.n(external_path_namespaceObject);
44
35
  const localisedUrls_js_namespaceObject = require("../shared/localisedUrls.js");
45
36
  const utils_js_namespaceObject = require("../shared/utils.js");
46
- function hasJsonFiles(dirPath) {
47
- try {
48
- if (!external_fs_default().existsSync(dirPath) || !external_fs_default().statSync(dirPath).isDirectory()) return false;
49
- const entries = external_fs_default().readdirSync(dirPath);
50
- for (const entry of entries){
51
- const entryPath = external_path_default().join(dirPath, entry);
52
- const stat = external_fs_default().statSync(entryPath);
53
- if (stat.isFile() && entry.endsWith('.json')) return true;
54
- if (stat.isDirectory()) {
55
- if (hasJsonFiles(entryPath)) return true;
56
- }
57
- }
58
- return false;
59
- } catch {
60
- return false;
61
- }
62
- }
63
- function detectLocalesDirectory(appDirectory, normalizedConfig) {
64
- const rootLocalesPath = external_path_default().join(appDirectory, 'locales');
65
- if (hasJsonFiles(rootLocalesPath)) return true;
66
- const configPublicPath = external_path_default().join(appDirectory, 'config', 'public', 'locales');
67
- if (hasJsonFiles(configPublicPath)) return true;
68
- const publicDir = normalizedConfig?.server?.publicDir;
69
- if (publicDir) {
70
- const publicDirPath = Array.isArray(publicDir) ? publicDir[0] : publicDir;
71
- const localesPath = external_path_default().isAbsolute(publicDirPath) ? external_path_default().join(publicDirPath, 'locales') : external_path_default().join(appDirectory, publicDirPath, 'locales');
72
- if (hasJsonFiles(localesPath)) return true;
73
- }
74
- return false;
75
- }
37
+ const external_locales_js_namespaceObject = require("./locales.js");
38
+ require("../runtime/types.js");
76
39
  const i18nPlugin = (options = {})=>({
77
40
  name: '@modern-js/plugin-i18n',
78
41
  setup: (api)=>{
@@ -82,26 +45,16 @@ const i18nPlugin = (options = {})=>({
82
45
  let backendOptions;
83
46
  const { appDirectory } = api.getAppContext();
84
47
  const normalizedConfig = api.getNormalizedConfig();
48
+ const detectedLocales = (0, external_locales_js_namespaceObject.detectLocalesDirectory)(appDirectory, normalizedConfig);
85
49
  if (backend) {
86
50
  const entryBackendOptions = (0, utils_js_namespaceObject.getBackendOptions)(entrypoint.entryName, backend);
87
- if (entryBackendOptions?.enabled === false) backendOptions = entryBackendOptions;
88
- else if (entryBackendOptions?.loadPath || entryBackendOptions?.addPath) backendOptions = {
51
+ backendOptions = entryBackendOptions?.enabled === false ? entryBackendOptions : detectedLocales ? (0, external_locales_js_namespaceObject.applyDetectedBackendPaths)(entryBackendOptions, detectedLocales) : entryBackendOptions?.loadPath || entryBackendOptions?.addPath ? {
89
52
  ...entryBackendOptions,
90
53
  enabled: true
91
- };
92
- else if (entryBackendOptions?.enabled !== true) {
93
- const hasLocales = detectLocalesDirectory(appDirectory, normalizedConfig);
94
- backendOptions = hasLocales ? {
95
- ...entryBackendOptions,
96
- enabled: true
97
- } : entryBackendOptions;
98
- } else backendOptions = entryBackendOptions;
99
- } else {
100
- const hasLocales = detectLocalesDirectory(appDirectory, normalizedConfig);
101
- if (hasLocales) backendOptions = (0, utils_js_namespaceObject.getBackendOptions)(entrypoint.entryName, {
102
- enabled: true
103
- });
104
- }
54
+ } : entryBackendOptions;
55
+ } else if (detectedLocales) backendOptions = (0, external_locales_js_namespaceObject.applyDetectedBackendPaths)((0, utils_js_namespaceObject.getBackendOptions)(entrypoint.entryName, {
56
+ enabled: true
57
+ }), detectedLocales);
105
58
  const { metaName } = api.getAppContext();
106
59
  let extendedConfig = restOptions;
107
60
  if (transformRuntimeConfig) extendedConfig = transformRuntimeConfig(restOptions, entrypoint);
@@ -0,0 +1,132 @@
1
+ "use strict";
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
+ })();
12
+ (()=>{
13
+ __webpack_require__.d = (exports1, getters, values)=>{
14
+ var define = (defs, kind)=>{
15
+ for(var key in defs)if (__webpack_require__.o(defs, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
16
+ enumerable: true,
17
+ [kind]: defs[key]
18
+ });
19
+ };
20
+ define(getters, "get");
21
+ define(values, "value");
22
+ };
23
+ })();
24
+ (()=>{
25
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
26
+ })();
27
+ (()=>{
28
+ __webpack_require__.r = (exports1)=>{
29
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
30
+ value: 'Module'
31
+ });
32
+ Object.defineProperty(exports1, '__esModule', {
33
+ value: true
34
+ });
35
+ };
36
+ })();
37
+ var __webpack_exports__ = {};
38
+ __webpack_require__.r(__webpack_exports__);
39
+ __webpack_require__.d(__webpack_exports__, {
40
+ applyDetectedBackendPaths: ()=>applyDetectedBackendPaths,
41
+ detectLocalesDirectory: ()=>detectLocalesDirectory
42
+ });
43
+ const server_core_namespaceObject = require("@modern-js/server-core");
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 LOCALES_RESOURCE_PATTERN = '{{lng}}/{{ns}}.json';
49
+ function hasJsonFiles(dirPath) {
50
+ try {
51
+ if (!external_fs_default().existsSync(dirPath) || !external_fs_default().statSync(dirPath).isDirectory()) return false;
52
+ const entries = external_fs_default().readdirSync(dirPath);
53
+ for (const entry of entries){
54
+ const entryPath = external_path_default().join(dirPath, entry);
55
+ const stat = external_fs_default().statSync(entryPath);
56
+ if (stat.isFile() && entry.endsWith('.json')) return true;
57
+ if (stat.isDirectory() && hasJsonFiles(entryPath)) return true;
58
+ }
59
+ return false;
60
+ } catch {
61
+ return false;
62
+ }
63
+ }
64
+ function toPosixPath(filePath) {
65
+ return filePath.split(external_path_default().sep).join(external_path_default().posix.sep);
66
+ }
67
+ function getPublicDirOutputPath(publicDir) {
68
+ return (0, server_core_namespaceObject.normalizePublicDirPath)(publicDir);
69
+ }
70
+ function buildDetectedLocalesDirectory(clientBasePath, serverBasePath, serverBasePathCandidates = [
71
+ serverBasePath
72
+ ]) {
73
+ const normalizedClientBasePath = clientBasePath.startsWith('/') ? clientBasePath : `/${clientBasePath}`;
74
+ const normalizedServerBasePath = toPosixPath(serverBasePath).replace(/\/$/, '');
75
+ const normalizedServerBasePathCandidates = Array.from(new Set(serverBasePathCandidates.map((candidate)=>toPosixPath(candidate).replace(/\/$/, ''))));
76
+ const serverLoadPaths = normalizedServerBasePathCandidates.map((candidate)=>`${candidate}/${LOCALES_RESOURCE_PATTERN}`);
77
+ const serverAddPaths = normalizedServerBasePathCandidates.map((candidate)=>`${candidate}/${LOCALES_RESOURCE_PATTERN}`);
78
+ return {
79
+ loadPath: `${normalizedClientBasePath}/${LOCALES_RESOURCE_PATTERN}`,
80
+ addPath: `${normalizedClientBasePath}/${LOCALES_RESOURCE_PATTERN}`,
81
+ serverLoadPath: `${normalizedServerBasePath}/${LOCALES_RESOURCE_PATTERN}`,
82
+ serverAddPath: `${normalizedServerBasePath}/${LOCALES_RESOURCE_PATTERN}`,
83
+ serverLoadPaths,
84
+ serverAddPaths
85
+ };
86
+ }
87
+ function detectLocalesDirectory(appDirectory, normalizedConfig) {
88
+ const publicDirs = (0, server_core_namespaceObject.normalizePublicDir)(normalizedConfig?.server?.publicDir);
89
+ const publicDirPrefixes = (0, server_core_namespaceObject.getPublicDirRoutePrefixes)(normalizedConfig?.server?.publicDir);
90
+ for(let index = 0; index < publicDirs.length; index++){
91
+ const publicDir = publicDirs[index];
92
+ if (external_path_default().isAbsolute(publicDir)) continue;
93
+ const publicDirPath = external_path_default().join(appDirectory, publicDir);
94
+ const publicDirPrefix = publicDirPrefixes[index] || `/${(0, server_core_namespaceObject.normalizePublicDirPath)(publicDir)}`;
95
+ const publicDirOutputPath = getPublicDirOutputPath(publicDir);
96
+ if ('locales' === external_path_default().basename((0, server_core_namespaceObject.normalizePublicDirPath)(publicDir)) && hasJsonFiles(publicDirPath)) return buildDetectedLocalesDirectory(publicDirPrefix, `./${publicDirOutputPath}`);
97
+ const localesPath = external_path_default().join(publicDirPath, 'locales');
98
+ if (hasJsonFiles(localesPath)) return buildDetectedLocalesDirectory(`${publicDirPrefix}/locales`, `./${publicDirOutputPath}/locales`);
99
+ }
100
+ const configPublicPath = external_path_default().join(appDirectory, 'config', 'public', 'locales');
101
+ if (hasJsonFiles(configPublicPath)) return buildDetectedLocalesDirectory('/locales', './public/locales', [
102
+ './config/public/locales',
103
+ './public/locales'
104
+ ]);
105
+ const rootLocalesPath = external_path_default().join(appDirectory, 'locales');
106
+ if (hasJsonFiles(rootLocalesPath)) return buildDetectedLocalesDirectory('/locales', './locales');
107
+ }
108
+ function applyDetectedBackendPaths(backendOptions, detectedLocales) {
109
+ if (!detectedLocales) return backendOptions;
110
+ const backendWithDetectedPaths = {
111
+ ...backendOptions,
112
+ enabled: true,
113
+ loadPath: backendOptions.loadPath ?? detectedLocales.loadPath,
114
+ addPath: backendOptions.addPath ?? detectedLocales.addPath,
115
+ serverLoadPath: backendOptions.serverLoadPath ?? detectedLocales.serverLoadPath,
116
+ serverLoadPaths: backendOptions.serverLoadPath || backendOptions.serverLoadPaths ? backendOptions.serverLoadPaths : detectedLocales.serverLoadPaths,
117
+ serverAddPath: backendOptions.serverAddPath ?? detectedLocales.serverAddPath,
118
+ serverAddPaths: backendOptions.serverAddPath || backendOptions.serverAddPaths ? backendOptions.serverAddPaths : detectedLocales.serverAddPaths,
119
+ _detectedLoadPath: detectedLocales.loadPath,
120
+ _detectedAddPath: detectedLocales.addPath
121
+ };
122
+ return backendWithDetectedPaths;
123
+ }
124
+ exports.applyDetectedBackendPaths = __webpack_exports__.applyDetectedBackendPaths;
125
+ exports.detectLocalesDirectory = __webpack_exports__.detectLocalesDirectory;
126
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
127
+ "applyDetectedBackendPaths",
128
+ "detectLocalesDirectory"
129
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
130
+ Object.defineProperty(exports, '__esModule', {
131
+ value: true
132
+ });
@@ -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
  (()=>{
@@ -28,22 +32,15 @@ __webpack_require__.d(__webpack_exports__, {
28
32
  default: ()=>runtime_I18nLink
29
33
  });
30
34
  const jsx_runtime_namespaceObject = require("react/jsx-runtime");
31
- const external_context_js_namespaceObject = require("./context.js");
32
- const external_routerAdapter_js_namespaceObject = require("./routerAdapter.js");
33
- const external_utils_js_namespaceObject = require("./utils.js");
35
+ const external_Link_js_namespaceObject = require("./Link.js");
36
+ let warnedDeprecation = false;
34
37
  const I18nLink = ({ to, children, ...props })=>{
35
- const { Link, params, hasRouter } = (0, external_routerAdapter_js_namespaceObject.useI18nRouterAdapter)();
36
- const { language, supportedLanguages, localisedUrls } = (0, external_context_js_namespaceObject.useModernI18n)();
37
- const currentLang = language;
38
- const localizedTo = (0, external_utils_js_namespaceObject.buildLocalizedUrl)(to, currentLang, supportedLanguages, localisedUrls);
39
- 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.");
40
- if (!hasRouter || !Link) return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("a", {
41
- href: localizedTo,
42
- ...props,
43
- children: children
44
- });
45
- return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(Link, {
46
- to: localizedTo,
38
+ if ('development' === process.env.NODE_ENV && !warnedDeprecation) {
39
+ warnedDeprecation = true;
40
+ console.warn("[plugin-i18n] I18nLink is deprecated. Import { Link } from '@modern-js/plugin-i18n/runtime' instead — it accepts the same language-agnostic `to` values.");
41
+ }
42
+ return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_Link_js_namespaceObject.Link, {
43
+ to: to,
47
44
  ...props,
48
45
  children: children
49
46
  });