@bleedingdev/modern-js-plugin-i18n 3.8.2-ultramodern.9 → 3.8.3-ultramodern.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/cli/index.js +3 -3
- package/dist/cjs/runtime/context.js +19 -6
- package/dist/cjs/runtime/hooks.js +8 -11
- package/dist/cjs/runtime/localizedPaths.js +2 -2
- package/dist/cjs/runtime/providerComposition.js +3 -1
- package/dist/cjs/runtime/utils.js +3 -3
- package/dist/cjs/server/index.js +2 -2
- package/dist/cjs/server/redirectPolicy.js +6 -6
- package/dist/esm/cli/index.mjs +1 -1
- package/dist/esm/runtime/context.mjs +19 -6
- package/dist/esm/runtime/hooks.mjs +8 -11
- package/dist/esm/runtime/localizedPaths.mjs +1 -1
- package/dist/esm/runtime/providerComposition.mjs +3 -1
- package/dist/esm/runtime/utils.mjs +1 -1
- package/dist/esm/server/index.mjs +1 -1
- package/dist/esm/server/redirectPolicy.mjs +1 -1
- package/dist/esm-node/cli/index.mjs +1 -1
- package/dist/esm-node/runtime/context.mjs +19 -6
- package/dist/esm-node/runtime/hooks.mjs +8 -11
- package/dist/esm-node/runtime/localizedPaths.mjs +1 -1
- package/dist/esm-node/runtime/providerComposition.mjs +3 -1
- package/dist/esm-node/runtime/utils.mjs +1 -1
- package/dist/esm-node/server/index.mjs +1 -1
- package/dist/esm-node/server/redirectPolicy.mjs +1 -1
- package/dist/types/runtime/context.d.ts +3 -2
- package/dist/types/runtime/contextHelpers.d.ts +1 -1
- package/dist/types/runtime/hooks.d.ts +1 -1
- package/dist/types/runtime/localizedPaths.d.ts +1 -1
- package/dist/types/runtime/utils.d.ts +1 -1
- package/dist/types/shared/type.d.ts +1 -1
- package/package.json +32 -11
- package/src/cli/index.ts +4 -4
- package/src/runtime/context.tsx +38 -14
- package/src/runtime/contextHelpers.ts +1 -1
- package/src/runtime/hooks.ts +9 -10
- package/src/runtime/localizedPaths.ts +4 -2
- package/src/runtime/providerComposition.tsx +5 -2
- package/src/runtime/utils.ts +4 -4
- package/src/server/index.ts +1 -1
- package/src/server/redirectPolicy.ts +1 -1
- package/src/shared/type.ts +1 -1
- package/tests/federatedI18nBoundary.client.test.tsx +21 -4
- package/tests/federatedI18nBoundary.test.tsx +15 -3
- package/tests/linkTypes.test.ts +20 -0
- package/tests/localisedUrlRewriteMatrix.fork.test.ts +1 -1
- package/tests/localisedUrls.test.ts +0 -351
- package/tests/routerAdapter.test.tsx +5 -12
- package/dist/cjs/shared/localisedUrls/config.js +0 -49
- package/dist/cjs/shared/localisedUrls/index.js +0 -91
- package/dist/cjs/shared/localisedUrls/normalise.js +0 -87
- package/dist/cjs/shared/localisedUrls/pathname.js +0 -68
- package/dist/cjs/shared/localisedUrls/patterns.js +0 -130
- package/dist/cjs/shared/localisedUrls/redirect.js +0 -103
- package/dist/cjs/shared/localisedUrls/resolve.js +0 -88
- package/dist/cjs/shared/localisedUrls/routes.js +0 -130
- package/dist/cjs/shared/localisedUrls/types.js +0 -18
- package/dist/cjs/shared/localisedUrls.js +0 -85
- package/dist/esm/shared/localisedUrls/config.mjs +0 -11
- package/dist/esm/shared/localisedUrls/index.mjs +0 -7
- package/dist/esm/shared/localisedUrls/normalise.mjs +0 -37
- package/dist/esm/shared/localisedUrls/pathname.mjs +0 -24
- package/dist/esm/shared/localisedUrls/patterns.mjs +0 -86
- package/dist/esm/shared/localisedUrls/redirect.mjs +0 -56
- package/dist/esm/shared/localisedUrls/resolve.mjs +0 -47
- package/dist/esm/shared/localisedUrls/routes.mjs +0 -89
- package/dist/esm/shared/localisedUrls/types.mjs +0 -0
- package/dist/esm/shared/localisedUrls.mjs +0 -1
- package/dist/esm-node/shared/localisedUrls/config.mjs +0 -12
- package/dist/esm-node/shared/localisedUrls/index.mjs +0 -8
- package/dist/esm-node/shared/localisedUrls/normalise.mjs +0 -38
- package/dist/esm-node/shared/localisedUrls/pathname.mjs +0 -25
- package/dist/esm-node/shared/localisedUrls/patterns.mjs +0 -87
- package/dist/esm-node/shared/localisedUrls/redirect.mjs +0 -57
- package/dist/esm-node/shared/localisedUrls/resolve.mjs +0 -48
- package/dist/esm-node/shared/localisedUrls/routes.mjs +0 -90
- package/dist/esm-node/shared/localisedUrls/types.mjs +0 -1
- package/dist/esm-node/shared/localisedUrls.mjs +0 -2
- package/dist/types/shared/localisedUrls/config.d.ts +0 -9
- package/dist/types/shared/localisedUrls/index.d.ts +0 -9
- package/dist/types/shared/localisedUrls/normalise.d.ts +0 -10
- package/dist/types/shared/localisedUrls/pathname.d.ts +0 -4
- package/dist/types/shared/localisedUrls/patterns.d.ts +0 -5
- package/dist/types/shared/localisedUrls/redirect.d.ts +0 -11
- package/dist/types/shared/localisedUrls/resolve.d.ts +0 -7
- package/dist/types/shared/localisedUrls/routes.d.ts +0 -3
- package/dist/types/shared/localisedUrls/types.d.ts +0 -14
- package/dist/types/shared/localisedUrls.d.ts +0 -2
- package/src/shared/localisedUrls/config.ts +0 -18
- package/src/shared/localisedUrls/index.ts +0 -24
- package/src/shared/localisedUrls/normalise.ts +0 -67
- package/src/shared/localisedUrls/pathname.ts +0 -60
- package/src/shared/localisedUrls/patterns.ts +0 -160
- package/src/shared/localisedUrls/redirect.ts +0 -93
- package/src/shared/localisedUrls/resolve.ts +0 -135
- package/src/shared/localisedUrls/routes.ts +0 -234
- package/src/shared/localisedUrls/types.ts +0 -18
- package/src/shared/localisedUrls.ts +0 -26
package/package.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"modern",
|
|
18
18
|
"modern.js"
|
|
19
19
|
],
|
|
20
|
-
"version": "3.8.
|
|
20
|
+
"version": "3.8.3-ultramodern.2",
|
|
21
21
|
"engines": {
|
|
22
22
|
"node": ">=20"
|
|
23
23
|
},
|
|
@@ -48,6 +48,13 @@
|
|
|
48
48
|
},
|
|
49
49
|
"default": "./dist/esm/runtime/index.mjs"
|
|
50
50
|
},
|
|
51
|
+
"./runtime/consumer": {
|
|
52
|
+
"types": "./dist/types/runtime/context.d.ts",
|
|
53
|
+
"node": {
|
|
54
|
+
"module": "./dist/esm/runtime/context.mjs"
|
|
55
|
+
},
|
|
56
|
+
"default": "./dist/esm/runtime/context.mjs"
|
|
57
|
+
},
|
|
51
58
|
"./runtime/no-react-i18next": {
|
|
52
59
|
"types": "./dist/types/runtime/no-react-i18next.d.ts",
|
|
53
60
|
"node": {
|
|
@@ -55,6 +62,13 @@
|
|
|
55
62
|
},
|
|
56
63
|
"default": "./dist/esm/runtime/no-react-i18next.mjs"
|
|
57
64
|
},
|
|
65
|
+
"./runtime/no-react-i18next/consumer": {
|
|
66
|
+
"types": "./dist/types/runtime/context.d.ts",
|
|
67
|
+
"node": {
|
|
68
|
+
"module": "./dist/esm/runtime/context.mjs"
|
|
69
|
+
},
|
|
70
|
+
"default": "./dist/esm/runtime/context.mjs"
|
|
71
|
+
},
|
|
58
72
|
"./server": {
|
|
59
73
|
"types": "./dist/types/server/index.d.ts",
|
|
60
74
|
"node": {
|
|
@@ -79,9 +93,15 @@
|
|
|
79
93
|
"runtime": [
|
|
80
94
|
"./dist/types/runtime/index.d.ts"
|
|
81
95
|
],
|
|
96
|
+
"runtime/consumer": [
|
|
97
|
+
"./dist/types/runtime/context.d.ts"
|
|
98
|
+
],
|
|
82
99
|
"runtime/no-react-i18next": [
|
|
83
100
|
"./dist/types/runtime/no-react-i18next.d.ts"
|
|
84
101
|
],
|
|
102
|
+
"runtime/no-react-i18next/consumer": [
|
|
103
|
+
"./dist/types/runtime/context.d.ts"
|
|
104
|
+
],
|
|
85
105
|
"server": [
|
|
86
106
|
"./dist/types/server/index.d.ts"
|
|
87
107
|
],
|
|
@@ -91,12 +111,13 @@
|
|
|
91
111
|
}
|
|
92
112
|
},
|
|
93
113
|
"dependencies": {
|
|
94
|
-
"@modern-js/
|
|
95
|
-
"@modern-js/
|
|
96
|
-
"@modern-js/
|
|
97
|
-
"@modern-js/server-
|
|
98
|
-
"@modern-js/
|
|
99
|
-
"@modern-js/
|
|
114
|
+
"@modern-js/i18n-runtime-extensions": "npm:@bleedingdev/modern-js-i18n-runtime-extensions@3.8.3-ultramodern.2",
|
|
115
|
+
"@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.8.3-ultramodern.2",
|
|
116
|
+
"@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.8.3-ultramodern.2",
|
|
117
|
+
"@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.8.3-ultramodern.2",
|
|
118
|
+
"@modern-js/server-runtime": "npm:@bleedingdev/modern-js-server-runtime@3.8.3-ultramodern.2",
|
|
119
|
+
"@modern-js/types": "npm:@bleedingdev/modern-js-types@3.8.3-ultramodern.2",
|
|
120
|
+
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.8.3-ultramodern.2",
|
|
100
121
|
"@swc/helpers": "^0.5.23",
|
|
101
122
|
"i18next-browser-languagedetector": "^8.2.1",
|
|
102
123
|
"i18next-chained-backend": "^5.0.5",
|
|
@@ -105,7 +126,7 @@
|
|
|
105
126
|
"i18next-http-middleware": "^3.9.8"
|
|
106
127
|
},
|
|
107
128
|
"peerDependencies": {
|
|
108
|
-
"@modern-js/runtime": "3.8.
|
|
129
|
+
"@modern-js/runtime": "3.8.3-ultramodern.2",
|
|
109
130
|
"i18next": ">=25.7.4",
|
|
110
131
|
"react": "^19.2.8",
|
|
111
132
|
"react-dom": "^19.2.8",
|
|
@@ -120,8 +141,8 @@
|
|
|
120
141
|
}
|
|
121
142
|
},
|
|
122
143
|
"devDependencies": {
|
|
123
|
-
"@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.8.
|
|
124
|
-
"@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.8.
|
|
144
|
+
"@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.8.3-ultramodern.2",
|
|
145
|
+
"@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.8.3-ultramodern.2",
|
|
125
146
|
"@rslib/core": "0.23.2",
|
|
126
147
|
"@types/node": "^26.2.0",
|
|
127
148
|
"@typescript/native-preview": "7.0.0-dev.20260707.2",
|
|
@@ -139,7 +160,7 @@
|
|
|
139
160
|
},
|
|
140
161
|
"scripts": {
|
|
141
162
|
"dev": "rslib build --watch",
|
|
142
|
-
"build": "rslib build
|
|
163
|
+
"build": "rslib build",
|
|
143
164
|
"test": "rstest --passWithNoTests"
|
|
144
165
|
}
|
|
145
166
|
}
|
package/src/cli/index.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import type { AppTools, CliPlugin } from '@modern-js/app-tools';
|
|
4
|
+
import {
|
|
5
|
+
applyLocalisedUrlsToRoutes,
|
|
6
|
+
resolveLocalisedUrlsConfig,
|
|
7
|
+
} from '@modern-js/i18n-runtime-extensions';
|
|
4
8
|
import { getPublicDirRoutePrefixes } from '@modern-js/server-core';
|
|
5
9
|
import type {
|
|
6
10
|
Entrypoint,
|
|
7
11
|
NestedRouteForCli,
|
|
8
12
|
PageRoute,
|
|
9
13
|
} from '@modern-js/types';
|
|
10
|
-
import {
|
|
11
|
-
applyLocalisedUrlsToRoutes,
|
|
12
|
-
resolveLocalisedUrlsConfig,
|
|
13
|
-
} from '../shared/localisedUrls';
|
|
14
14
|
import type { BackendOptions, LocaleDetectionOptions } from '../shared/type';
|
|
15
15
|
import { getBackendOptions, getLocaleDetectionOptions } from '../shared/utils';
|
|
16
16
|
import { applyDetectedBackendPaths, detectLocalesDirectory } from './locales';
|
package/src/runtime/context.tsx
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { LocalisedUrlsOption } from '@modern-js/i18n-runtime-extensions';
|
|
2
|
+
import type { ComponentType, FC, ReactNode } from 'react';
|
|
2
3
|
import {
|
|
3
4
|
createContext,
|
|
4
5
|
useCallback,
|
|
@@ -6,7 +7,6 @@ import {
|
|
|
6
7
|
useEffect,
|
|
7
8
|
useMemo,
|
|
8
9
|
} from 'react';
|
|
9
|
-
import type { LocalisedUrlsOption } from '../shared/localisedUrls';
|
|
10
10
|
import {
|
|
11
11
|
changeModernI18nLanguage,
|
|
12
12
|
getPathLanguage,
|
|
@@ -36,36 +36,52 @@ interface ModernI18nContextValue {
|
|
|
36
36
|
const modernI18nContextKey = Symbol.for(
|
|
37
37
|
'@modern-js/plugin-i18n/runtime/ModernI18nContext',
|
|
38
38
|
);
|
|
39
|
+
const reactI18nextProviderContextKey = Symbol.for(
|
|
40
|
+
'@modern-js/plugin-i18n/runtime/ReactI18nextProviderContext',
|
|
41
|
+
);
|
|
39
42
|
|
|
40
|
-
type
|
|
41
|
-
[key: symbol]:
|
|
42
|
-
| ReturnType<typeof createContext<ModernI18nContextValue | null>>
|
|
43
|
-
| undefined;
|
|
43
|
+
type GlobalContextStore<T> = typeof globalThis & {
|
|
44
|
+
[key: symbol]: ReturnType<typeof createContext<T>> | undefined;
|
|
44
45
|
};
|
|
45
46
|
|
|
46
|
-
const
|
|
47
|
-
const globalStore = globalThis as
|
|
48
|
-
globalStore[
|
|
49
|
-
|
|
50
|
-
return globalStore[modernI18nContextKey];
|
|
47
|
+
const getGlobalContext = <T,>(key: symbol, defaultValue: T) => {
|
|
48
|
+
const globalStore = globalThis as GlobalContextStore<T>;
|
|
49
|
+
globalStore[key] ??= createContext<T>(defaultValue);
|
|
50
|
+
return globalStore[key];
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
-
const ModernI18nContext =
|
|
53
|
+
const ModernI18nContext = getGlobalContext<ModernI18nContextValue | null>(
|
|
54
|
+
modernI18nContextKey,
|
|
55
|
+
null,
|
|
56
|
+
);
|
|
57
|
+
const ReactI18nextProviderContext = getGlobalContext<ComponentType<any> | null>(
|
|
58
|
+
reactI18nextProviderContextKey,
|
|
59
|
+
null,
|
|
60
|
+
);
|
|
54
61
|
|
|
55
62
|
interface ModernI18nProviderProps {
|
|
56
63
|
children: ReactNode;
|
|
64
|
+
i18nextProvider?: ComponentType<any> | null;
|
|
57
65
|
value: ModernI18nContextValue;
|
|
58
66
|
}
|
|
59
67
|
|
|
60
68
|
export const ModernI18nProvider: FC<ModernI18nProviderProps> = ({
|
|
61
69
|
children,
|
|
70
|
+
i18nextProvider,
|
|
62
71
|
value,
|
|
63
72
|
}) => {
|
|
64
|
-
|
|
73
|
+
const content = (
|
|
65
74
|
<ModernI18nContext.Provider value={value}>
|
|
66
75
|
{children}
|
|
67
76
|
</ModernI18nContext.Provider>
|
|
68
77
|
);
|
|
78
|
+
return i18nextProvider === undefined ? (
|
|
79
|
+
content
|
|
80
|
+
) : (
|
|
81
|
+
<ReactI18nextProviderContext.Provider value={i18nextProvider}>
|
|
82
|
+
{content}
|
|
83
|
+
</ReactI18nextProviderContext.Provider>
|
|
84
|
+
);
|
|
69
85
|
};
|
|
70
86
|
|
|
71
87
|
export interface FederatedI18nBoundaryProps {
|
|
@@ -89,6 +105,7 @@ export const FederatedI18nBoundary: FC<FederatedI18nBoundaryProps> = ({
|
|
|
89
105
|
supportedLanguages,
|
|
90
106
|
}) => {
|
|
91
107
|
const parent = useContext(ModernI18nContext);
|
|
108
|
+
const I18nextProvider = useContext(ReactI18nextProviderContext);
|
|
92
109
|
if (!parent) {
|
|
93
110
|
throw new Error(
|
|
94
111
|
'FederatedI18nBoundary must be used within ModernI18nProvider',
|
|
@@ -184,7 +201,14 @@ export const FederatedI18nBoundary: FC<FederatedI18nBoundaryProps> = ({
|
|
|
184
201
|
[languages, parent, scopedInstance],
|
|
185
202
|
);
|
|
186
203
|
|
|
187
|
-
|
|
204
|
+
const scopedContent = (
|
|
205
|
+
<ModernI18nProvider value={value}>{children}</ModernI18nProvider>
|
|
206
|
+
);
|
|
207
|
+
return I18nextProvider ? (
|
|
208
|
+
<I18nextProvider i18n={scopedInstance}>{scopedContent}</I18nextProvider>
|
|
209
|
+
) : (
|
|
210
|
+
scopedContent
|
|
211
|
+
);
|
|
188
212
|
};
|
|
189
213
|
|
|
190
214
|
export interface UseModernI18nReturn<
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
+
import type { LocalisedUrlsOption } from '@modern-js/i18n-runtime-extensions';
|
|
1
2
|
import { isBrowser } from '@modern-js/runtime';
|
|
2
|
-
import type { LocalisedUrlsOption } from '../shared/localisedUrls';
|
|
3
3
|
import type { I18nInstance } from './i18n';
|
|
4
4
|
import type { SdkBackend } from './i18n/backend/sdk-backend';
|
|
5
5
|
import { cacheUserLanguage } from './i18n/detection';
|
package/src/runtime/hooks.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
+
import type { LocalisedUrlsOption } from '@modern-js/i18n-runtime-extensions';
|
|
1
2
|
import { isBrowser } from '@modern-js/runtime';
|
|
2
3
|
import type React from 'react';
|
|
3
4
|
import { useCallback, useEffect, useRef } from 'react';
|
|
4
|
-
import type { LocalisedUrlsOption } from '../shared/localisedUrls';
|
|
5
5
|
import {
|
|
6
6
|
cacheI18nLanguage,
|
|
7
7
|
changeI18nInstanceLanguage,
|
|
@@ -181,7 +181,7 @@ export function useClientSideRedirect(
|
|
|
181
181
|
ignoreRedirectRoutes?: string[] | ((pathname: string) => boolean),
|
|
182
182
|
localisedUrls?: LocalisedUrlsOption,
|
|
183
183
|
) {
|
|
184
|
-
const
|
|
184
|
+
const urlRef = useRef('');
|
|
185
185
|
const { navigate, location, hasRouter } = useI18nRouterAdapter();
|
|
186
186
|
|
|
187
187
|
useEffect(() => {
|
|
@@ -201,7 +201,7 @@ export function useClientSideRedirect(
|
|
|
201
201
|
// Ignore errors when checking SSR data
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
if (
|
|
204
|
+
if (urlRef.current === (location?.pathname || window.location.pathname)) {
|
|
205
205
|
return;
|
|
206
206
|
}
|
|
207
207
|
|
|
@@ -230,12 +230,12 @@ export function useClientSideRedirect(
|
|
|
230
230
|
localePathRedirect,
|
|
231
231
|
);
|
|
232
232
|
|
|
233
|
-
if (pathDetection.detected) {
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
233
|
const targetLanguage =
|
|
238
|
-
|
|
234
|
+
pathDetection.language ||
|
|
235
|
+
i18nInstance.language ||
|
|
236
|
+
fallbackLanguage ||
|
|
237
|
+
languages[0] ||
|
|
238
|
+
'en';
|
|
239
239
|
|
|
240
240
|
const newPath = buildLocalizedUrl(
|
|
241
241
|
relativePath,
|
|
@@ -245,9 +245,8 @@ export function useClientSideRedirect(
|
|
|
245
245
|
);
|
|
246
246
|
const newUrl = entryPath + newPath + currentSearch + currentHash;
|
|
247
247
|
|
|
248
|
+
urlRef.current = currentPathname;
|
|
248
249
|
if (newUrl !== currentPathname + currentSearch + currentHash) {
|
|
249
|
-
hasRedirectedRef.current = true;
|
|
250
|
-
|
|
251
250
|
// Use navigate if router is available (similar to changeLanguage implementation)
|
|
252
251
|
if (hasRouter && navigate && location) {
|
|
253
252
|
navigate(newUrl, { replace: true });
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import {
|
|
2
|
+
canonicalTargetPathname,
|
|
3
|
+
type LocalisedUrlsOption,
|
|
4
|
+
} from '@modern-js/i18n-runtime-extensions';
|
|
1
5
|
import { useMemo } from 'react';
|
|
2
|
-
import type { LocalisedUrlsOption } from '../shared/localisedUrls';
|
|
3
|
-
import { canonicalTargetPathname } from '../shared/localisedUrls';
|
|
4
6
|
import { useModernI18n } from './context';
|
|
5
7
|
import { useI18nRouterAdapter } from './routerAdapter';
|
|
6
8
|
import { buildLocalizedUrl, splitUrlTarget } from './utils';
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useLanguageSync } from '@modern-js/i18n-runtime-extensions';
|
|
1
2
|
import { RuntimeContext } from '@modern-js/runtime';
|
|
2
3
|
import { Helmet } from '@modern-js/runtime/head';
|
|
3
4
|
import type React from 'react';
|
|
@@ -7,7 +8,6 @@ import { ModernI18nProvider } from './context';
|
|
|
7
8
|
import {
|
|
8
9
|
createContextValue,
|
|
9
10
|
useClientSideRedirect,
|
|
10
|
-
useLanguageSync,
|
|
11
11
|
useSdkResourcesLoader,
|
|
12
12
|
} from './hooks';
|
|
13
13
|
import type { I18nInstance } from './i18n';
|
|
@@ -140,7 +140,10 @@ export const createI18nRootWrapper =
|
|
|
140
140
|
return (
|
|
141
141
|
<>
|
|
142
142
|
{Boolean(htmlLangAttr) && <Helmet htmlAttributes={{ lang }} />}
|
|
143
|
-
<ModernI18nProvider
|
|
143
|
+
<ModernI18nProvider
|
|
144
|
+
i18nextProvider={I18nextProvider}
|
|
145
|
+
value={contextValue}
|
|
146
|
+
>
|
|
144
147
|
{appContent}
|
|
145
148
|
</ModernI18nProvider>
|
|
146
149
|
</>
|
package/src/runtime/utils.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { isBrowser } from '@modern-js/runtime';
|
|
2
|
-
import { getGlobalBasename } from '@modern-js/runtime/context';
|
|
3
|
-
import type { LocalisedUrlsOption } from '../shared/localisedUrls';
|
|
4
1
|
import {
|
|
2
|
+
type LocalisedUrlsOption,
|
|
5
3
|
localiseTargetPathname,
|
|
6
4
|
shouldSkipLocaleRedirect,
|
|
7
|
-
} from '
|
|
5
|
+
} from '@modern-js/i18n-runtime-extensions';
|
|
6
|
+
import { isBrowser } from '@modern-js/runtime';
|
|
7
|
+
import { getGlobalBasename } from '@modern-js/runtime/context';
|
|
8
8
|
|
|
9
9
|
// Structural parameter: hooks.ts passes a public-TRuntimeContext-based
|
|
10
10
|
// context while core.tsx passes the internal one; both carry the request
|
package/src/server/index.ts
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { resolveLocalisedUrlsConfig } from '@modern-js/i18n-runtime-extensions';
|
|
1
2
|
import * as honoPkg from '@modern-js/server-core/hono';
|
|
2
3
|
|
|
3
4
|
const { languageDetector } = honoPkg;
|
|
4
5
|
|
|
5
6
|
import type { Context, Next, ServerPlugin } from '@modern-js/server-runtime';
|
|
6
|
-
import { resolveLocalisedUrlsConfig } from '../shared/localisedUrls.js';
|
|
7
7
|
import type { LocaleDetectionOptions } from '../shared/type';
|
|
8
8
|
import { getLocaleDetectionOptions } from '../shared/utils.js';
|
|
9
9
|
import { collectApiPrefixes, matchesApiPrefix } from './apiPrefix.js';
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
matchesPathPrefix,
|
|
5
5
|
shouldSkipLocaleRedirect,
|
|
6
6
|
stripLanguagePrefix,
|
|
7
|
-
} from '
|
|
7
|
+
} from '@modern-js/i18n-runtime-extensions';
|
|
8
8
|
import type { LocaleDetectionOptions } from '../shared/type';
|
|
9
9
|
|
|
10
10
|
interface LocaleRedirectRequest {
|
package/src/shared/type.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import type { LocalisedUrlsOption } from '@modern-js/i18n-runtime-extensions';
|
|
1
2
|
import type {
|
|
2
3
|
LanguageDetectorOptions,
|
|
3
4
|
Resources,
|
|
4
5
|
} from '../runtime/i18n/instance';
|
|
5
|
-
import type { LocalisedUrlsOption } from './localisedUrls';
|
|
6
6
|
|
|
7
7
|
export interface BaseLocaleDetectionOptions {
|
|
8
8
|
localePathRedirect?: boolean;
|
|
@@ -3,6 +3,7 @@ import i18next, { type i18n } from 'i18next';
|
|
|
3
3
|
import { act, useState } from 'react';
|
|
4
4
|
import { hydrateRoot } from 'react-dom/client';
|
|
5
5
|
import { renderToString } from 'react-dom/server';
|
|
6
|
+
import { I18nextProvider, useTranslation } from 'react-i18next';
|
|
6
7
|
import { ModernI18nProvider } from '../src/runtime/context';
|
|
7
8
|
import { FederatedI18nBoundary, useModernI18n } from '../src/runtime/core';
|
|
8
9
|
|
|
@@ -21,9 +22,19 @@ const remoteResources = {
|
|
|
21
22
|
};
|
|
22
23
|
|
|
23
24
|
function InventoryCopy() {
|
|
24
|
-
const
|
|
25
|
+
const modernI18n = useModernI18n<i18n>();
|
|
26
|
+
const reactI18n = useTranslation('inventory');
|
|
27
|
+
const reactI18nInstance = reactI18n.i18n as i18n & { __original?: i18n };
|
|
28
|
+
const sharesScopedInstance =
|
|
29
|
+
reactI18nInstance === modernI18n.i18nInstance ||
|
|
30
|
+
reactI18nInstance.__original === modernI18n.i18nInstance;
|
|
25
31
|
|
|
26
|
-
return
|
|
32
|
+
return (
|
|
33
|
+
<p data-testid="inventory-copy">
|
|
34
|
+
{modernI18n.t('inventory.widgetBody')}|
|
|
35
|
+
{reactI18n.t('inventory.widgetBody')}|{String(sharesScopedInstance)}
|
|
36
|
+
</p>
|
|
37
|
+
);
|
|
27
38
|
}
|
|
28
39
|
|
|
29
40
|
function Host({ hostI18n }: { hostI18n: i18n }) {
|
|
@@ -31,6 +42,7 @@ function Host({ hostI18n }: { hostI18n: i18n }) {
|
|
|
31
42
|
|
|
32
43
|
return (
|
|
33
44
|
<ModernI18nProvider
|
|
45
|
+
i18nextProvider={I18nextProvider}
|
|
34
46
|
value={{
|
|
35
47
|
i18nInstance: hostI18n,
|
|
36
48
|
language,
|
|
@@ -97,14 +109,19 @@ test('hydrates remote-owned copy and follows the host language', async () => {
|
|
|
97
109
|
await act(async () => undefined);
|
|
98
110
|
expect(
|
|
99
111
|
container.querySelector('[data-testid="inventory-copy"]')?.textContent,
|
|
100
|
-
).toBe(
|
|
112
|
+
).toBe(
|
|
113
|
+
'C1 inventory surface owned by the MicroVertical.|C1 inventory surface owned by the MicroVertical.|true',
|
|
114
|
+
);
|
|
101
115
|
|
|
102
116
|
await act(async () => {
|
|
103
117
|
(container.querySelector('button') as HTMLButtonElement).click();
|
|
104
118
|
});
|
|
105
119
|
expect(
|
|
106
120
|
container.querySelector('[data-testid="inventory-copy"]')?.textContent,
|
|
107
|
-
).toBe(
|
|
121
|
+
).toBe(
|
|
122
|
+
'C1 skladová plocha vlastněná MicroVerticalem.|C1 skladová plocha vlastněná MicroVerticalem.|true',
|
|
123
|
+
);
|
|
124
|
+
expect(clientI18n.t('inventory.widgetBody')).toBe('stale host Czech copy');
|
|
108
125
|
expect(hydrationErrors).toEqual([]);
|
|
109
126
|
|
|
110
127
|
await act(async () => root.unmount());
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { describe, expect, test } from '@rstest/core';
|
|
2
|
-
import i18next from 'i18next';
|
|
2
|
+
import i18next, { type i18n } from 'i18next';
|
|
3
3
|
import { renderToStaticMarkup } from 'react-dom/server';
|
|
4
|
+
import { I18nextProvider, useTranslation } from 'react-i18next';
|
|
4
5
|
import { ModernI18nProvider } from '../src/runtime/context';
|
|
5
6
|
import { FederatedI18nBoundary, useModernI18n } from '../src/runtime/core';
|
|
6
7
|
|
|
@@ -11,11 +12,19 @@ const HOST_ONLY_INVENTORY_COPY = 'inventory copy owned only by the shell';
|
|
|
11
12
|
const HOST_ONLY_SHELL_COPY = 'private shell copy';
|
|
12
13
|
|
|
13
14
|
function InventoryCopy() {
|
|
14
|
-
const
|
|
15
|
+
const modernI18n = useModernI18n<i18n>();
|
|
16
|
+
const reactI18n = useTranslation('inventory');
|
|
17
|
+
const reactI18nInstance = reactI18n.i18n as i18n & { __original?: i18n };
|
|
18
|
+
const sharesScopedInstance =
|
|
19
|
+
reactI18nInstance === modernI18n.i18nInstance ||
|
|
20
|
+
reactI18nInstance.__original === modernI18n.i18nInstance;
|
|
15
21
|
|
|
16
22
|
return (
|
|
17
23
|
<p>
|
|
18
|
-
{t('inventory.widgetBody')}|
|
|
24
|
+
{modernI18n.t('inventory.widgetBody')}|
|
|
25
|
+
{reactI18n.t('inventory.widgetBody')}|{String(sharesScopedInstance)}|
|
|
26
|
+
{modernI18n.t('inventory.hostOnly')}|{modernI18n.t('shell:private')}|
|
|
27
|
+
{reactI18n.t('inventory.hostOnly')}|{reactI18n.t('shell:private')}
|
|
19
28
|
</p>
|
|
20
29
|
);
|
|
21
30
|
}
|
|
@@ -45,6 +54,7 @@ describe('FederatedI18nBoundary', () => {
|
|
|
45
54
|
|
|
46
55
|
const html = renderToStaticMarkup(
|
|
47
56
|
<ModernI18nProvider
|
|
57
|
+
i18nextProvider={I18nextProvider}
|
|
48
58
|
value={{
|
|
49
59
|
i18nInstance: hostI18n,
|
|
50
60
|
language: 'en',
|
|
@@ -68,6 +78,7 @@ describe('FederatedI18nBoundary', () => {
|
|
|
68
78
|
|
|
69
79
|
expect(html).toContain(C1_COPY);
|
|
70
80
|
expect(html).not.toContain(C0_COPY);
|
|
81
|
+
expect(html).toContain(`${C1_COPY}|${C1_COPY}|true`);
|
|
71
82
|
expect(html).not.toContain(HOST_ONLY_INVENTORY_COPY);
|
|
72
83
|
expect(html).not.toContain(HOST_ONLY_SHELL_COPY);
|
|
73
84
|
expect(hostI18n.t('inventory.widgetBody')).toBe(C0_COPY);
|
|
@@ -94,6 +105,7 @@ describe('FederatedI18nBoundary', () => {
|
|
|
94
105
|
expect(() =>
|
|
95
106
|
renderToStaticMarkup(
|
|
96
107
|
<ModernI18nProvider
|
|
108
|
+
i18nextProvider={I18nextProvider}
|
|
97
109
|
value={{
|
|
98
110
|
i18nInstance: hostI18n,
|
|
99
111
|
language: 'en',
|
package/tests/linkTypes.test.ts
CHANGED
|
@@ -18,6 +18,9 @@ function resolveTsgoBin() {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
const tsgoBin = resolveTsgoBin();
|
|
21
|
+
const useShell = process.platform === 'win32';
|
|
22
|
+
const execOptions = { cwd: path.dirname(__dirname), shell: useShell };
|
|
23
|
+
execFileSync('pnpm', ['build'], execOptions);
|
|
21
24
|
|
|
22
25
|
describe('Link type-level tests', () => {
|
|
23
26
|
test('fixture type-checks correctly: valid uses compile, invalid uses are rejected', () => {
|
|
@@ -36,4 +39,21 @@ describe('Link type-level tests', () => {
|
|
|
36
39
|
throw new Error(`TypeScript type-check failed:\n${stdout}\n${stderr}`);
|
|
37
40
|
}
|
|
38
41
|
}, 60_000);
|
|
42
|
+
|
|
43
|
+
test('consumer declarations avoid toolchain internals', () => {
|
|
44
|
+
const output = execFileSync(process.execPath, [
|
|
45
|
+
tsgoBin,
|
|
46
|
+
'--ignoreConfig',
|
|
47
|
+
'--listFilesOnly',
|
|
48
|
+
'--module',
|
|
49
|
+
'preserve',
|
|
50
|
+
'--moduleResolution',
|
|
51
|
+
'bundler',
|
|
52
|
+
path.resolve(__dirname, '../dist/types/runtime/context.d.ts'),
|
|
53
|
+
]);
|
|
54
|
+
const files = output.toString().replaceAll('\\', '/');
|
|
55
|
+
expect(files).not.toMatch(
|
|
56
|
+
/\/packages\/(cli|runtime\/plugin-runtime\/dist\/types|solutions\/app-tools|toolkit\/(types|utils))\//u,
|
|
57
|
+
);
|
|
58
|
+
}, 60_000);
|
|
39
59
|
});
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { LocalisedUrlsMap } from '@modern-js/i18n-runtime-extensions';
|
|
1
2
|
import { describe, expect, test } from '@rstest/core';
|
|
2
3
|
import {
|
|
3
4
|
interpolateRouteParams,
|
|
@@ -5,7 +6,6 @@ import {
|
|
|
5
6
|
} from '../src/runtime/linkHelpers';
|
|
6
7
|
import { canonicalPath, localizePath } from '../src/runtime/localizedPaths';
|
|
7
8
|
import { buildLocalizedUrl } from '../src/runtime/utils';
|
|
8
|
-
import type { LocalisedUrlsMap } from '../src/shared/localisedUrls';
|
|
9
9
|
|
|
10
10
|
const languages = ['en', 'cs'];
|
|
11
11
|
|