@bleedingdev/modern-js-plugin-i18n 3.2.0-ultramodern.119 → 3.2.0-ultramodern.120

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 (42) hide show
  1. package/README.md +221 -11
  2. package/dist/cjs/runtime/I18nLink.js +7 -17
  3. package/dist/cjs/runtime/Link.js +252 -0
  4. package/dist/cjs/runtime/canonicalRoutes.js +18 -0
  5. package/dist/cjs/runtime/index.js +23 -0
  6. package/dist/cjs/runtime/localizedPaths.js +105 -0
  7. package/dist/cjs/runtime/utils.js +22 -5
  8. package/dist/cjs/shared/localisedUrls.js +32 -2
  9. package/dist/esm/runtime/I18nLink.mjs +6 -16
  10. package/dist/esm/runtime/Link.mjs +209 -0
  11. package/dist/esm/runtime/canonicalRoutes.mjs +0 -0
  12. package/dist/esm/runtime/index.mjs +4 -2
  13. package/dist/esm/runtime/localizedPaths.mjs +58 -0
  14. package/dist/esm/runtime/utils.mjs +18 -4
  15. package/dist/esm/shared/localisedUrls.mjs +24 -3
  16. package/dist/esm-node/runtime/I18nLink.mjs +6 -16
  17. package/dist/esm-node/runtime/Link.mjs +210 -0
  18. package/dist/esm-node/runtime/canonicalRoutes.mjs +1 -0
  19. package/dist/esm-node/runtime/index.mjs +4 -2
  20. package/dist/esm-node/runtime/localizedPaths.mjs +59 -0
  21. package/dist/esm-node/runtime/utils.mjs +18 -4
  22. package/dist/esm-node/shared/localisedUrls.mjs +24 -3
  23. package/dist/types/runtime/I18nLink.d.ts +4 -13
  24. package/dist/types/runtime/Link.d.ts +56 -0
  25. package/dist/types/runtime/canonicalRoutes.d.ts +60 -0
  26. package/dist/types/runtime/index.d.ts +5 -1
  27. package/dist/types/runtime/localizedPaths.d.ts +39 -0
  28. package/dist/types/runtime/utils.d.ts +12 -3
  29. package/dist/types/shared/localisedUrls.d.ts +8 -0
  30. package/package.json +13 -13
  31. package/rstest.config.mts +2 -2
  32. package/src/runtime/I18nLink.tsx +13 -46
  33. package/src/runtime/Link.tsx +414 -0
  34. package/src/runtime/canonicalRoutes.ts +93 -0
  35. package/src/runtime/index.tsx +24 -2
  36. package/src/runtime/localizedPaths.ts +118 -0
  37. package/src/runtime/utils.ts +24 -5
  38. package/src/shared/localisedUrls.ts +63 -3
  39. package/tests/link.test.tsx +475 -0
  40. package/tests/linkTypes.test.ts +28 -0
  41. package/tests/type-fixture/linkTypes.fixture.tsx +51 -0
  42. 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.
@@ -32,25 +32,15 @@ __webpack_require__.d(__webpack_exports__, {
32
32
  default: ()=>runtime_I18nLink
33
33
  });
34
34
  const jsx_runtime_namespaceObject = require("react/jsx-runtime");
35
- const external_context_js_namespaceObject = require("./context.js");
36
- const external_routerAdapter_js_namespaceObject = require("./routerAdapter.js");
37
- const external_utils_js_namespaceObject = require("./utils.js");
35
+ const external_Link_js_namespaceObject = require("./Link.js");
36
+ let warnedDeprecation = false;
38
37
  const I18nLink = ({ to, children, ...props })=>{
39
- const { Link, params, hasRouter } = (0, external_routerAdapter_js_namespaceObject.useI18nRouterAdapter)();
40
- const { language, supportedLanguages, localisedUrls } = (0, external_context_js_namespaceObject.useModernI18n)();
41
- const currentLang = language;
42
- const localizedTo = (0, external_utils_js_namespaceObject.buildLocalizedUrl)(to, currentLang, supportedLanguages, localisedUrls);
43
- 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.");
44
- if (!hasRouter || !Link) {
45
- const { prefetch: _prefetch, preload: _preload, ...anchorProps } = props;
46
- return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("a", {
47
- href: localizedTo,
48
- ...anchorProps,
49
- children: children
50
- });
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.");
51
41
  }
52
- return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(Link, {
53
- to: localizedTo,
42
+ return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(external_Link_js_namespaceObject.Link, {
43
+ to: to,
54
44
  ...props,
55
45
  children: children
56
46
  });
@@ -0,0 +1,252 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
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");
13
+ };
14
+ })();
15
+ (()=>{
16
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
17
+ })();
18
+ (()=>{
19
+ __webpack_require__.r = (exports1)=>{
20
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
21
+ value: 'Module'
22
+ });
23
+ Object.defineProperty(exports1, '__esModule', {
24
+ value: true
25
+ });
26
+ };
27
+ })();
28
+ var __webpack_exports__ = {};
29
+ __webpack_require__.r(__webpack_exports__);
30
+ __webpack_require__.d(__webpack_exports__, {
31
+ Link: ()=>Link,
32
+ default: ()=>runtime_Link,
33
+ interpolateRouteParams: ()=>interpolateRouteParams
34
+ });
35
+ const jsx_runtime_namespaceObject = require("react/jsx-runtime");
36
+ const external_react_namespaceObject = require("react");
37
+ const external_context_js_namespaceObject = require("./context.js");
38
+ const external_localizedPaths_js_namespaceObject = require("./localizedPaths.js");
39
+ const external_routerAdapter_js_namespaceObject = require("./routerAdapter.js");
40
+ const external_utils_js_namespaceObject = require("./utils.js");
41
+ const EXTERNAL_TARGET_RE = /^(?:[a-z][a-z0-9+.-]*:|\/\/)/i;
42
+ const warnedTargets = new Set();
43
+ const warnOnce = (key, message)=>{
44
+ if ('development' !== process.env.NODE_ENV || warnedTargets.has(key)) return;
45
+ warnedTargets.add(key);
46
+ console.warn(message);
47
+ };
48
+ const interpolateRouteParams = (pathname, params)=>{
49
+ if (!/[$:*{]/.test(pathname)) return pathname;
50
+ const resolveParam = (name)=>{
51
+ const value = params?.[name];
52
+ return void 0 === value ? void 0 : String(value);
53
+ };
54
+ const segments = pathname.split('/').map((segment)=>{
55
+ if (!segment) return segment;
56
+ if (segment.startsWith('{-$') && segment.endsWith('}')) {
57
+ const value = resolveParam(segment.slice(3, -1));
58
+ return void 0 === value ? null : encodeURIComponent(value);
59
+ }
60
+ if ('$' === segment || '*' === segment) {
61
+ const value = resolveParam('_splat') ?? resolveParam('*');
62
+ return void 0 === value ? null : value.split('/').map(encodeURIComponent).join('/');
63
+ }
64
+ if (segment.startsWith('$')) {
65
+ const value = resolveParam(segment.slice(1));
66
+ if (void 0 === value) {
67
+ warnOnce(`missing-param:${pathname}:${segment}`, `[plugin-i18n] <Link to="${pathname}"> is missing required param "${segment.slice(1)}".`);
68
+ return segment;
69
+ }
70
+ return encodeURIComponent(value);
71
+ }
72
+ if (segment.startsWith(':')) {
73
+ const optional = segment.endsWith('?');
74
+ const name = segment.slice(1, optional ? -1 : void 0);
75
+ const value = resolveParam(name);
76
+ if (void 0 === value) {
77
+ if (optional) return null;
78
+ warnOnce(`missing-param:${pathname}:${segment}`, `[plugin-i18n] <Link to="${pathname}"> is missing required param "${name}".`);
79
+ return segment;
80
+ }
81
+ return encodeURIComponent(value);
82
+ }
83
+ return segment;
84
+ }).filter((segment)=>null !== segment);
85
+ return segments.join('/') || '/';
86
+ };
87
+ const normalizeSearch = (search, searchFromTo)=>{
88
+ if (search && 'object' == typeof search) {
89
+ const entries = Object.entries(search).filter(([, value])=>null != value);
90
+ const searchObject = Object.fromEntries(entries.map(([key, value])=>[
91
+ key,
92
+ String(value)
93
+ ]));
94
+ const params = new URLSearchParams(searchObject);
95
+ const serialized = params.toString();
96
+ return {
97
+ searchString: serialized ? `?${serialized}` : '',
98
+ searchObject
99
+ };
100
+ }
101
+ const raw = 'string' == typeof search && search ? search : searchFromTo;
102
+ if (!raw) return {
103
+ searchString: '',
104
+ searchObject: void 0
105
+ };
106
+ const searchString = raw.startsWith('?') ? raw : `?${raw}`;
107
+ const searchObject = {};
108
+ new URLSearchParams(searchString).forEach((value, key)=>{
109
+ searchObject[key] = value;
110
+ });
111
+ return {
112
+ searchString,
113
+ searchObject
114
+ };
115
+ };
116
+ const splitActiveProps = (active, activeProps)=>{
117
+ if (!active || !activeProps) return {};
118
+ return activeProps;
119
+ };
120
+ const mergeClassNames = (...values)=>{
121
+ const classNames = values.filter((value)=>'string' == typeof value && value.length > 0);
122
+ return classNames.length > 0 ? classNames.join(' ') : void 0;
123
+ };
124
+ const Link = (props)=>{
125
+ const { to, params, children, hash: hashProp, search: searchProp, hashScrollIntoView, activeOptions, activeProps, ...rest } = props;
126
+ const adapter = (0, external_routerAdapter_js_namespaceObject.useI18nRouterAdapter)();
127
+ const { language, supportedLanguages, localisedUrls } = (0, external_context_js_namespaceObject.useModernI18n)();
128
+ const config = {
129
+ languages: supportedLanguages,
130
+ localisedUrls
131
+ };
132
+ const isExternal = EXTERNAL_TARGET_RE.test(to);
133
+ const isBareHash = to.startsWith('#');
134
+ const target = (0, external_react_namespaceObject.useMemo)(()=>{
135
+ if (isExternal || isBareHash) return null;
136
+ const { pathname, search: searchFromTo, hash: hashFromTo } = (0, external_utils_js_namespaceObject.splitUrlTarget)(to);
137
+ const interpolated = interpolateRouteParams(pathname || '/', params);
138
+ const firstSegment = interpolated.split('/').filter(Boolean)[0];
139
+ if (firstSegment && supportedLanguages.includes(firstSegment)) warnOnce(`lang-prefix:${to}`, `[plugin-i18n] <Link to="${to}"> starts with a language prefix. Write language-agnostic canonical paths; the Link localizes them automatically.`);
140
+ const localizedPathname = (0, external_utils_js_namespaceObject.buildLocalizedUrl)(interpolated, language, supportedLanguages, localisedUrls);
141
+ const hash = hashProp ?? (hashFromTo ? hashFromTo.slice(1) : '');
142
+ const { searchString, searchObject } = normalizeSearch(searchProp, searchFromTo);
143
+ return {
144
+ canonicalPathname: interpolated,
145
+ localizedPathname,
146
+ hash,
147
+ searchString,
148
+ searchObject,
149
+ href: `${localizedPathname}${searchString}${hash ? `#${hash}` : ''}`
150
+ };
151
+ }, [
152
+ to,
153
+ params,
154
+ hashProp,
155
+ searchProp,
156
+ isExternal,
157
+ isBareHash,
158
+ language,
159
+ supportedLanguages,
160
+ localisedUrls
161
+ ]);
162
+ const isActive = (0, external_react_namespaceObject.useMemo)(()=>{
163
+ if (!target || !adapter.location) return false;
164
+ const current = (0, external_localizedPaths_js_namespaceObject.canonicalPath)(adapter.location.pathname, config);
165
+ const targetCanonical = (0, external_localizedPaths_js_namespaceObject.canonicalPath)(target.canonicalPathname, config);
166
+ const exact = activeOptions?.exact ?? '/' === targetCanonical;
167
+ if (current === targetCanonical) return true;
168
+ if (exact) return false;
169
+ return current.startsWith('/' === targetCanonical ? '/' : `${targetCanonical}/`);
170
+ }, [
171
+ target,
172
+ adapter.location,
173
+ activeOptions?.exact,
174
+ supportedLanguages,
175
+ localisedUrls
176
+ ]);
177
+ const resolvedActiveProps = splitActiveProps(isActive, activeProps);
178
+ const activeAttributes = isActive ? {
179
+ 'data-status': 'active',
180
+ 'aria-current': rest['aria-current'] ?? resolvedActiveProps['aria-current'] ?? 'page'
181
+ } : {};
182
+ if (!target) {
183
+ const { prefetch: _prefetch, preload: _preload, replace: _replace, ...anchorProps } = rest;
184
+ return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("a", {
185
+ href: to,
186
+ ...anchorProps,
187
+ children: children
188
+ });
189
+ }
190
+ const { Link: RouterLink, hasRouter, framework } = adapter;
191
+ if (!hasRouter || !RouterLink) {
192
+ const { prefetch: _prefetch, preload: _preload, replace: _replace, ...anchorProps } = rest;
193
+ const { className: activeClassName, style: activeStyle, ...activeRest } = resolvedActiveProps;
194
+ return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)("a", {
195
+ href: target.href,
196
+ ...anchorProps,
197
+ ...activeRest,
198
+ ...activeAttributes,
199
+ className: mergeClassNames(rest.className, activeClassName),
200
+ style: {
201
+ ...rest.style,
202
+ ...activeStyle
203
+ },
204
+ children: children
205
+ });
206
+ }
207
+ const { className: activeClassName, style: activeStyle, ...activeRest } = resolvedActiveProps;
208
+ const mergedClassName = mergeClassNames(rest.className, activeClassName);
209
+ const mergedStyle = {
210
+ ...rest.style,
211
+ ...activeStyle
212
+ };
213
+ if ('tanstack' === framework) return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(RouterLink, {
214
+ to: target.localizedPathname,
215
+ ...target.searchObject ? {
216
+ search: target.searchObject
217
+ } : {},
218
+ ...target.hash ? {
219
+ hash: target.hash
220
+ } : {},
221
+ ...void 0 === hashScrollIntoView ? {} : {
222
+ hashScrollIntoView
223
+ },
224
+ ...rest,
225
+ ...activeRest,
226
+ ...activeAttributes,
227
+ className: mergedClassName,
228
+ style: mergedStyle,
229
+ children: children
230
+ });
231
+ return /*#__PURE__*/ (0, jsx_runtime_namespaceObject.jsx)(RouterLink, {
232
+ to: target.href,
233
+ ...rest,
234
+ ...activeRest,
235
+ ...activeAttributes,
236
+ className: mergedClassName,
237
+ style: mergedStyle,
238
+ children: children
239
+ });
240
+ };
241
+ const runtime_Link = Link;
242
+ exports.Link = __webpack_exports__.Link;
243
+ exports["default"] = __webpack_exports__["default"];
244
+ exports.interpolateRouteParams = __webpack_exports__.interpolateRouteParams;
245
+ for(var __rspack_i in __webpack_exports__)if (-1 === [
246
+ "Link",
247
+ "default",
248
+ "interpolateRouteParams"
249
+ ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
250
+ Object.defineProperty(exports, '__esModule', {
251
+ value: true
252
+ });
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __webpack_require__ = {};
3
+ (()=>{
4
+ __webpack_require__.r = (exports1)=>{
5
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
6
+ value: 'Module'
7
+ });
8
+ Object.defineProperty(exports1, '__esModule', {
9
+ value: true
10
+ });
11
+ };
12
+ })();
13
+ var __webpack_exports__ = {};
14
+ __webpack_require__.r(__webpack_exports__);
15
+ for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
16
+ Object.defineProperty(exports, '__esModule', {
17
+ value: true
18
+ });
@@ -29,8 +29,15 @@ var __webpack_exports__ = {};
29
29
  __webpack_require__.r(__webpack_exports__);
30
30
  __webpack_require__.d(__webpack_exports__, {
31
31
  I18nLink: ()=>external_I18nLink_js_namespaceObject.I18nLink,
32
+ Link: ()=>external_Link_js_namespaceObject.Link,
33
+ buildLocalizedUrl: ()=>external_utils_js_namespaceObject.buildLocalizedUrl,
34
+ canonicalPath: ()=>external_localizedPaths_js_namespaceObject.canonicalPath,
32
35
  default: ()=>runtime,
33
36
  i18nPlugin: ()=>i18nPlugin,
37
+ localizePath: ()=>external_localizedPaths_js_namespaceObject.localizePath,
38
+ splitUrlTarget: ()=>external_utils_js_namespaceObject.splitUrlTarget,
39
+ useLocalizedLocation: ()=>external_localizedPaths_js_namespaceObject.useLocalizedLocation,
40
+ useLocalizedPaths: ()=>external_localizedPaths_js_namespaceObject.useLocalizedPaths,
34
41
  useModernI18n: ()=>external_context_js_namespaceObject.useModernI18n
35
42
  });
36
43
  const jsx_runtime_namespaceObject = require("react/jsx-runtime");
@@ -50,6 +57,8 @@ const utils_js_namespaceObject = require("./i18n/utils.js");
50
57
  const external_utils_js_namespaceObject = require("./utils.js");
51
58
  require("./types.js");
52
59
  const external_I18nLink_js_namespaceObject = require("./I18nLink.js");
60
+ const external_Link_js_namespaceObject = require("./Link.js");
61
+ const external_localizedPaths_js_namespaceObject = require("./localizedPaths.js");
53
62
  const i18nPlugin = (options)=>({
54
63
  name: '@modern-js/plugin-i18n',
55
64
  setup: (api)=>{
@@ -172,13 +181,27 @@ const i18nPlugin = (options)=>({
172
181
  });
173
182
  const runtime = i18nPlugin;
174
183
  exports.I18nLink = __webpack_exports__.I18nLink;
184
+ exports.Link = __webpack_exports__.Link;
185
+ exports.buildLocalizedUrl = __webpack_exports__.buildLocalizedUrl;
186
+ exports.canonicalPath = __webpack_exports__.canonicalPath;
175
187
  exports["default"] = __webpack_exports__["default"];
176
188
  exports.i18nPlugin = __webpack_exports__.i18nPlugin;
189
+ exports.localizePath = __webpack_exports__.localizePath;
190
+ exports.splitUrlTarget = __webpack_exports__.splitUrlTarget;
191
+ exports.useLocalizedLocation = __webpack_exports__.useLocalizedLocation;
192
+ exports.useLocalizedPaths = __webpack_exports__.useLocalizedPaths;
177
193
  exports.useModernI18n = __webpack_exports__.useModernI18n;
178
194
  for(var __rspack_i in __webpack_exports__)if (-1 === [
179
195
  "I18nLink",
196
+ "Link",
197
+ "buildLocalizedUrl",
198
+ "canonicalPath",
180
199
  "default",
181
200
  "i18nPlugin",
201
+ "localizePath",
202
+ "splitUrlTarget",
203
+ "useLocalizedLocation",
204
+ "useLocalizedPaths",
182
205
  "useModernI18n"
183
206
  ].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
184
207
  Object.defineProperty(exports, '__esModule', {