@bleedingdev/modern-js-plugin-i18n 3.4.0-ultramodern.2 → 3.4.0-ultramodern.4
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/runtime/context.js +8 -0
- package/dist/cjs/runtime/i18n/backend/middleware.node.js +22 -4
- package/dist/esm/runtime/context.mjs +8 -0
- package/dist/esm/runtime/i18n/backend/middleware.node.mjs +19 -4
- package/dist/esm-node/runtime/context.mjs +8 -0
- package/dist/esm-node/runtime/i18n/backend/middleware.node.mjs +19 -4
- package/dist/types/runtime/context.d.ts +1 -0
- package/dist/types/runtime/i18n/backend/middleware.node.d.ts +4 -1
- package/package.json +10 -10
- package/src/runtime/context.tsx +13 -0
- package/src/runtime/i18n/backend/middleware.node.ts +31 -1
- package/tests/i18nUtils.test.ts +34 -0
- package/tests/routerAdapter.test.tsx +58 -1
|
@@ -122,6 +122,13 @@ const useModernI18n = ()=>{
|
|
|
122
122
|
navigate,
|
|
123
123
|
location
|
|
124
124
|
]);
|
|
125
|
+
const t = (0, external_react_namespaceObject.useCallback)((key, ...args)=>{
|
|
126
|
+
if ('function' != typeof i18nInstance.t) throw new Error('i18nInstance.t is required');
|
|
127
|
+
return i18nInstance.t(key, ...args);
|
|
128
|
+
}, [
|
|
129
|
+
currentLanguage,
|
|
130
|
+
i18nInstance
|
|
131
|
+
]);
|
|
125
132
|
const isLanguageSupported = (0, external_react_namespaceObject.useCallback)((lang)=>languages?.includes(lang) || false, [
|
|
126
133
|
languages
|
|
127
134
|
]);
|
|
@@ -155,6 +162,7 @@ const useModernI18n = ()=>{
|
|
|
155
162
|
return {
|
|
156
163
|
language: currentLanguage,
|
|
157
164
|
changeLanguage,
|
|
165
|
+
t,
|
|
158
166
|
i18nInstance,
|
|
159
167
|
supportedLanguages: languages || [],
|
|
160
168
|
localisedUrls,
|
|
@@ -39,22 +39,40 @@ __webpack_require__.r(__webpack_exports__);
|
|
|
39
39
|
__webpack_require__.d(__webpack_exports__, {
|
|
40
40
|
FsBackendWithSave: ()=>FsBackendWithSave,
|
|
41
41
|
HttpBackendWithSave: ()=>HttpBackendWithSave,
|
|
42
|
+
resolveFsBackendConstructor: ()=>resolveFsBackendConstructor,
|
|
42
43
|
useI18nextBackend: ()=>useI18nextBackend
|
|
43
44
|
});
|
|
44
|
-
const
|
|
45
|
-
var
|
|
45
|
+
const external_i18next_fs_backend_namespaceObject = require("i18next-fs-backend");
|
|
46
|
+
var external_i18next_fs_backend_default = /*#__PURE__*/ __webpack_require__.n(external_i18next_fs_backend_namespaceObject);
|
|
46
47
|
const external_middleware_common_js_namespaceObject = require("./middleware.common.js");
|
|
47
|
-
|
|
48
|
+
const resolveFsBackendConstructor = (backendModule)=>{
|
|
49
|
+
const nestedDefault = backendModule?.default?.default;
|
|
50
|
+
const nestedModuleExports = backendModule?.default?.['module.exports'];
|
|
51
|
+
const candidates = [
|
|
52
|
+
backendModule,
|
|
53
|
+
backendModule?.default,
|
|
54
|
+
backendModule?.['module.exports'],
|
|
55
|
+
nestedDefault,
|
|
56
|
+
nestedModuleExports
|
|
57
|
+
];
|
|
58
|
+
const Backend = candidates.find((candidate)=>'function' == typeof candidate);
|
|
59
|
+
if (!Backend) throw new Error('Failed to resolve i18next-fs-backend constructor for the i18n Node backend.');
|
|
60
|
+
return Backend;
|
|
61
|
+
};
|
|
62
|
+
const middleware_node_Backend = resolveFsBackendConstructor(external_i18next_fs_backend_default());
|
|
63
|
+
class FsBackendWithSave extends middleware_node_Backend {
|
|
48
64
|
save(_language, _namespace, _data) {}
|
|
49
65
|
}
|
|
50
66
|
const HttpBackendWithSave = FsBackendWithSave;
|
|
51
|
-
const useI18nextBackend = (i18nInstance, backend)=>(0, external_middleware_common_js_namespaceObject.useI18nextBackendCommon)(i18nInstance, FsBackendWithSave,
|
|
67
|
+
const useI18nextBackend = (i18nInstance, backend)=>(0, external_middleware_common_js_namespaceObject.useI18nextBackendCommon)(i18nInstance, FsBackendWithSave, middleware_node_Backend, backend);
|
|
52
68
|
exports.FsBackendWithSave = __webpack_exports__.FsBackendWithSave;
|
|
53
69
|
exports.HttpBackendWithSave = __webpack_exports__.HttpBackendWithSave;
|
|
70
|
+
exports.resolveFsBackendConstructor = __webpack_exports__.resolveFsBackendConstructor;
|
|
54
71
|
exports.useI18nextBackend = __webpack_exports__.useI18nextBackend;
|
|
55
72
|
for(var __rspack_i in __webpack_exports__)if (-1 === [
|
|
56
73
|
"FsBackendWithSave",
|
|
57
74
|
"HttpBackendWithSave",
|
|
75
|
+
"resolveFsBackendConstructor",
|
|
58
76
|
"useI18nextBackend"
|
|
59
77
|
].indexOf(__rspack_i)) exports[__rspack_i] = __webpack_exports__[__rspack_i];
|
|
60
78
|
Object.defineProperty(exports, '__esModule', {
|
|
@@ -89,6 +89,13 @@ const useModernI18n = ()=>{
|
|
|
89
89
|
navigate,
|
|
90
90
|
location
|
|
91
91
|
]);
|
|
92
|
+
const t = useCallback((key, ...args)=>{
|
|
93
|
+
if ('function' != typeof i18nInstance.t) throw new Error('i18nInstance.t is required');
|
|
94
|
+
return i18nInstance.t(key, ...args);
|
|
95
|
+
}, [
|
|
96
|
+
currentLanguage,
|
|
97
|
+
i18nInstance
|
|
98
|
+
]);
|
|
92
99
|
const isLanguageSupported = useCallback((lang)=>languages?.includes(lang) || false, [
|
|
93
100
|
languages
|
|
94
101
|
]);
|
|
@@ -122,6 +129,7 @@ const useModernI18n = ()=>{
|
|
|
122
129
|
return {
|
|
123
130
|
language: currentLanguage,
|
|
124
131
|
changeLanguage,
|
|
132
|
+
t,
|
|
125
133
|
i18nInstance,
|
|
126
134
|
supportedLanguages: languages || [],
|
|
127
135
|
localisedUrls,
|
|
@@ -1,8 +1,23 @@
|
|
|
1
|
-
import
|
|
1
|
+
import i18next_fs_backend from "i18next-fs-backend";
|
|
2
2
|
import { useI18nextBackendCommon } from "./middleware.common.mjs";
|
|
3
|
-
|
|
3
|
+
const resolveFsBackendConstructor = (backendModule)=>{
|
|
4
|
+
const nestedDefault = backendModule?.default?.default;
|
|
5
|
+
const nestedModuleExports = backendModule?.default?.['module.exports'];
|
|
6
|
+
const candidates = [
|
|
7
|
+
backendModule,
|
|
8
|
+
backendModule?.default,
|
|
9
|
+
backendModule?.['module.exports'],
|
|
10
|
+
nestedDefault,
|
|
11
|
+
nestedModuleExports
|
|
12
|
+
];
|
|
13
|
+
const Backend = candidates.find((candidate)=>'function' == typeof candidate);
|
|
14
|
+
if (!Backend) throw new Error('Failed to resolve i18next-fs-backend constructor for the i18n Node backend.');
|
|
15
|
+
return Backend;
|
|
16
|
+
};
|
|
17
|
+
const middleware_node_Backend = resolveFsBackendConstructor(i18next_fs_backend);
|
|
18
|
+
class FsBackendWithSave extends middleware_node_Backend {
|
|
4
19
|
save(_language, _namespace, _data) {}
|
|
5
20
|
}
|
|
6
21
|
const HttpBackendWithSave = FsBackendWithSave;
|
|
7
|
-
const useI18nextBackend = (i18nInstance, backend)=>useI18nextBackendCommon(i18nInstance, FsBackendWithSave,
|
|
8
|
-
export { FsBackendWithSave, HttpBackendWithSave, useI18nextBackend };
|
|
22
|
+
const useI18nextBackend = (i18nInstance, backend)=>useI18nextBackendCommon(i18nInstance, FsBackendWithSave, middleware_node_Backend, backend);
|
|
23
|
+
export { FsBackendWithSave, HttpBackendWithSave, resolveFsBackendConstructor, useI18nextBackend };
|
|
@@ -90,6 +90,13 @@ const useModernI18n = ()=>{
|
|
|
90
90
|
navigate,
|
|
91
91
|
location
|
|
92
92
|
]);
|
|
93
|
+
const t = useCallback((key, ...args)=>{
|
|
94
|
+
if ('function' != typeof i18nInstance.t) throw new Error('i18nInstance.t is required');
|
|
95
|
+
return i18nInstance.t(key, ...args);
|
|
96
|
+
}, [
|
|
97
|
+
currentLanguage,
|
|
98
|
+
i18nInstance
|
|
99
|
+
]);
|
|
93
100
|
const isLanguageSupported = useCallback((lang)=>languages?.includes(lang) || false, [
|
|
94
101
|
languages
|
|
95
102
|
]);
|
|
@@ -123,6 +130,7 @@ const useModernI18n = ()=>{
|
|
|
123
130
|
return {
|
|
124
131
|
language: currentLanguage,
|
|
125
132
|
changeLanguage,
|
|
133
|
+
t,
|
|
126
134
|
i18nInstance,
|
|
127
135
|
supportedLanguages: languages || [],
|
|
128
136
|
localisedUrls,
|
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
import "node:module";
|
|
2
|
-
import
|
|
2
|
+
import i18next_fs_backend from "i18next-fs-backend";
|
|
3
3
|
import { useI18nextBackendCommon } from "./middleware.common.mjs";
|
|
4
|
-
|
|
4
|
+
const resolveFsBackendConstructor = (backendModule)=>{
|
|
5
|
+
const nestedDefault = backendModule?.default?.default;
|
|
6
|
+
const nestedModuleExports = backendModule?.default?.['module.exports'];
|
|
7
|
+
const candidates = [
|
|
8
|
+
backendModule,
|
|
9
|
+
backendModule?.default,
|
|
10
|
+
backendModule?.['module.exports'],
|
|
11
|
+
nestedDefault,
|
|
12
|
+
nestedModuleExports
|
|
13
|
+
];
|
|
14
|
+
const Backend = candidates.find((candidate)=>'function' == typeof candidate);
|
|
15
|
+
if (!Backend) throw new Error('Failed to resolve i18next-fs-backend constructor for the i18n Node backend.');
|
|
16
|
+
return Backend;
|
|
17
|
+
};
|
|
18
|
+
const middleware_node_Backend = resolveFsBackendConstructor(i18next_fs_backend);
|
|
19
|
+
class FsBackendWithSave extends middleware_node_Backend {
|
|
5
20
|
save(_language, _namespace, _data) {}
|
|
6
21
|
}
|
|
7
22
|
const HttpBackendWithSave = FsBackendWithSave;
|
|
8
|
-
const useI18nextBackend = (i18nInstance, backend)=>useI18nextBackendCommon(i18nInstance, FsBackendWithSave,
|
|
9
|
-
export { FsBackendWithSave, HttpBackendWithSave, useI18nextBackend };
|
|
23
|
+
const useI18nextBackend = (i18nInstance, backend)=>useI18nextBackendCommon(i18nInstance, FsBackendWithSave, middleware_node_Backend, backend);
|
|
24
|
+
export { FsBackendWithSave, HttpBackendWithSave, resolveFsBackendConstructor, useI18nextBackend };
|
|
@@ -19,6 +19,7 @@ export declare const ModernI18nProvider: FC<ModernI18nProviderProps>;
|
|
|
19
19
|
export interface UseModernI18nReturn {
|
|
20
20
|
language: string;
|
|
21
21
|
changeLanguage: (newLang: string) => Promise<void>;
|
|
22
|
+
t: (key: string | string[], ...args: any[]) => string;
|
|
22
23
|
i18nInstance: I18nInstance;
|
|
23
24
|
supportedLanguages: string[];
|
|
24
25
|
localisedUrls?: LocalisedUrlsOption;
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import Backend from 'i18next-fs-backend/cjs';
|
|
2
1
|
import type { ExtendedBackendOptions } from '../../../shared/type';
|
|
3
2
|
import type { I18nInstance } from '../instance';
|
|
3
|
+
type BackendConstructor = new (...args: any[]) => any;
|
|
4
|
+
export declare const resolveFsBackendConstructor: (backendModule: unknown) => BackendConstructor;
|
|
5
|
+
declare const Backend: BackendConstructor;
|
|
4
6
|
/**
|
|
5
7
|
* Wrapper for FS backend to add a no-op save method
|
|
6
8
|
* This is required for i18next-chained-backend to trigger refresh logic
|
|
@@ -11,3 +13,4 @@ export declare class FsBackendWithSave extends Backend {
|
|
|
11
13
|
}
|
|
12
14
|
export declare const HttpBackendWithSave: typeof FsBackendWithSave;
|
|
13
15
|
export declare const useI18nextBackend: (i18nInstance: I18nInstance, backend?: ExtendedBackendOptions) => void;
|
|
16
|
+
export {};
|
package/package.json
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"modern",
|
|
18
18
|
"modern.js"
|
|
19
19
|
],
|
|
20
|
-
"version": "3.4.0-ultramodern.
|
|
20
|
+
"version": "3.4.0-ultramodern.4",
|
|
21
21
|
"engines": {
|
|
22
22
|
"node": ">=20"
|
|
23
23
|
},
|
|
@@ -97,15 +97,15 @@
|
|
|
97
97
|
"i18next-fs-backend": "^2.6.6",
|
|
98
98
|
"i18next-http-backend": "^4.0.0",
|
|
99
99
|
"i18next-http-middleware": "^3.9.7",
|
|
100
|
-
"@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.4.0-ultramodern.
|
|
101
|
-
"@modern-js/
|
|
102
|
-
"@modern-js/
|
|
103
|
-
"@modern-js/
|
|
104
|
-
"@modern-js/
|
|
105
|
-
"@modern-js/
|
|
100
|
+
"@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.4.0-ultramodern.4",
|
|
101
|
+
"@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.4.0-ultramodern.4",
|
|
102
|
+
"@modern-js/types": "npm:@bleedingdev/modern-js-types@3.4.0-ultramodern.4",
|
|
103
|
+
"@modern-js/server-runtime": "npm:@bleedingdev/modern-js-server-runtime@3.4.0-ultramodern.4",
|
|
104
|
+
"@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.4.0-ultramodern.4",
|
|
105
|
+
"@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.4.0-ultramodern.4"
|
|
106
106
|
},
|
|
107
107
|
"peerDependencies": {
|
|
108
|
-
"@modern-js/runtime": "3.4.0-ultramodern.
|
|
108
|
+
"@modern-js/runtime": "3.4.0-ultramodern.4",
|
|
109
109
|
"i18next": ">=26.3.1",
|
|
110
110
|
"react": "^19.2.7",
|
|
111
111
|
"react-dom": "^19.2.7",
|
|
@@ -129,8 +129,8 @@
|
|
|
129
129
|
"react-i18next": "17.0.8",
|
|
130
130
|
"ts-node": "^10.9.2",
|
|
131
131
|
"typescript": "^6.0.3",
|
|
132
|
-
"@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.4.0-ultramodern.
|
|
133
|
-
"@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.4.0-ultramodern.
|
|
132
|
+
"@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.4.0-ultramodern.4",
|
|
133
|
+
"@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.4.0-ultramodern.4"
|
|
134
134
|
},
|
|
135
135
|
"sideEffects": false,
|
|
136
136
|
"publishConfig": {
|
package/src/runtime/context.tsx
CHANGED
|
@@ -53,6 +53,7 @@ export const ModernI18nProvider: FC<ModernI18nProviderProps> = ({
|
|
|
53
53
|
export interface UseModernI18nReturn {
|
|
54
54
|
language: string;
|
|
55
55
|
changeLanguage: (newLang: string) => Promise<void>;
|
|
56
|
+
t: (key: string | string[], ...args: any[]) => string;
|
|
56
57
|
i18nInstance: I18nInstance;
|
|
57
58
|
supportedLanguages: string[];
|
|
58
59
|
localisedUrls?: LocalisedUrlsOption;
|
|
@@ -250,6 +251,17 @@ export const useModernI18n = (): UseModernI18nReturn => {
|
|
|
250
251
|
],
|
|
251
252
|
);
|
|
252
253
|
|
|
254
|
+
const t = useCallback(
|
|
255
|
+
(key: string | string[], ...args: any[]) => {
|
|
256
|
+
if (typeof i18nInstance.t !== 'function') {
|
|
257
|
+
throw new Error('i18nInstance.t is required');
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
return i18nInstance.t(key, ...args) as string;
|
|
261
|
+
},
|
|
262
|
+
[currentLanguage, i18nInstance],
|
|
263
|
+
);
|
|
264
|
+
|
|
253
265
|
// Helper function to check if a language is supported
|
|
254
266
|
const isLanguageSupported = useCallback(
|
|
255
267
|
(lang: string) => {
|
|
@@ -310,6 +322,7 @@ export const useModernI18n = (): UseModernI18nReturn => {
|
|
|
310
322
|
return {
|
|
311
323
|
language: currentLanguage,
|
|
312
324
|
changeLanguage,
|
|
325
|
+
t,
|
|
313
326
|
i18nInstance,
|
|
314
327
|
supportedLanguages: languages || [],
|
|
315
328
|
localisedUrls,
|
|
@@ -1,8 +1,38 @@
|
|
|
1
|
-
import
|
|
1
|
+
import FsBackendModule from 'i18next-fs-backend';
|
|
2
2
|
import type { ExtendedBackendOptions } from '../../../shared/type';
|
|
3
3
|
import type { I18nInstance } from '../instance';
|
|
4
4
|
import { useI18nextBackendCommon } from './middleware.common';
|
|
5
5
|
|
|
6
|
+
type BackendConstructor = new (...args: any[]) => any;
|
|
7
|
+
|
|
8
|
+
export const resolveFsBackendConstructor = (
|
|
9
|
+
backendModule: unknown,
|
|
10
|
+
): BackendConstructor => {
|
|
11
|
+
const nestedDefault = (backendModule as { default?: { default?: unknown } })
|
|
12
|
+
?.default?.default;
|
|
13
|
+
const nestedModuleExports = (
|
|
14
|
+
backendModule as { default?: { 'module.exports'?: unknown } }
|
|
15
|
+
)?.default?.['module.exports'];
|
|
16
|
+
const candidates = [
|
|
17
|
+
backendModule,
|
|
18
|
+
(backendModule as { default?: unknown })?.default,
|
|
19
|
+
(backendModule as { 'module.exports'?: unknown })?.['module.exports'],
|
|
20
|
+
nestedDefault,
|
|
21
|
+
nestedModuleExports,
|
|
22
|
+
];
|
|
23
|
+
const Backend = candidates.find(candidate => typeof candidate === 'function');
|
|
24
|
+
|
|
25
|
+
if (!Backend) {
|
|
26
|
+
throw new Error(
|
|
27
|
+
'Failed to resolve i18next-fs-backend constructor for the i18n Node backend.',
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
return Backend as BackendConstructor;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
const Backend = resolveFsBackendConstructor(FsBackendModule);
|
|
35
|
+
|
|
6
36
|
/**
|
|
7
37
|
* Wrapper for FS backend to add a no-op save method
|
|
8
38
|
* This is required for i18next-chained-backend to trigger refresh logic
|
package/tests/i18nUtils.test.ts
CHANGED
|
@@ -4,6 +4,10 @@ import {
|
|
|
4
4
|
DEFAULT_I18NEXT_BACKEND_OPTIONS as NODE_DEFAULT_I18NEXT_BACKEND_OPTIONS,
|
|
5
5
|
resolveDefaultLocalesDir,
|
|
6
6
|
} from '../src/runtime/i18n/backend/defaults.node';
|
|
7
|
+
import {
|
|
8
|
+
FsBackendWithSave,
|
|
9
|
+
resolveFsBackendConstructor,
|
|
10
|
+
} from '../src/runtime/i18n/backend/middleware.node';
|
|
7
11
|
import { initializeI18nInstance } from '../src/runtime/i18n/utils';
|
|
8
12
|
|
|
9
13
|
function createBackendI18nInstance(): I18nInstance {
|
|
@@ -20,6 +24,36 @@ function createBackendI18nInstance(): I18nInstance {
|
|
|
20
24
|
}
|
|
21
25
|
|
|
22
26
|
describe('i18n runtime utils', () => {
|
|
27
|
+
test('normalizes node fs backend CJS and ESM namespace shapes', () => {
|
|
28
|
+
class FakeBackend {}
|
|
29
|
+
|
|
30
|
+
expect(resolveFsBackendConstructor(FakeBackend)).toBe(FakeBackend);
|
|
31
|
+
expect(resolveFsBackendConstructor({ default: FakeBackend })).toBe(
|
|
32
|
+
FakeBackend,
|
|
33
|
+
);
|
|
34
|
+
expect(resolveFsBackendConstructor({ 'module.exports': FakeBackend })).toBe(
|
|
35
|
+
FakeBackend,
|
|
36
|
+
);
|
|
37
|
+
expect(
|
|
38
|
+
resolveFsBackendConstructor({ default: { default: FakeBackend } }),
|
|
39
|
+
).toBe(FakeBackend);
|
|
40
|
+
expect(
|
|
41
|
+
resolveFsBackendConstructor({
|
|
42
|
+
default: { 'module.exports': FakeBackend },
|
|
43
|
+
}),
|
|
44
|
+
).toBe(FakeBackend);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('node fs backend wrapper extends a resolved constructor', () => {
|
|
48
|
+
const backend = new FsBackendWithSave({}, {}, {}) as {
|
|
49
|
+
type?: string;
|
|
50
|
+
save: (language: string, namespace: string, data: unknown) => void;
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
expect(backend.type).toBe('backend');
|
|
54
|
+
expect(() => backend.save('en', 'translation', {})).not.toThrow();
|
|
55
|
+
});
|
|
56
|
+
|
|
23
57
|
test('node fs backend defaults follow the detected locales directory', () => {
|
|
24
58
|
// The default must match whichever conventional root exists at runtime
|
|
25
59
|
// (./locales first, then ./config/public/locales), mirroring the CLI
|
|
@@ -5,7 +5,7 @@ import {
|
|
|
5
5
|
} from '@modern-js/runtime/context';
|
|
6
6
|
import type React from 'react';
|
|
7
7
|
import type { ComponentType, PropsWithChildren } from 'react';
|
|
8
|
-
import { act } from 'react';
|
|
8
|
+
import { act, useState } from 'react';
|
|
9
9
|
import { createRoot, type Root } from 'react-dom/client';
|
|
10
10
|
import { i18nPlugin } from '../src/runtime';
|
|
11
11
|
import { ModernI18nProvider, useModernI18n } from '../src/runtime/context';
|
|
@@ -453,4 +453,61 @@ describe('i18n router adapter', () => {
|
|
|
453
453
|
replace: true,
|
|
454
454
|
});
|
|
455
455
|
});
|
|
456
|
+
|
|
457
|
+
test('exposes a language-scoped t function for rendered copy', async () => {
|
|
458
|
+
const i18nInstance = createI18nInstance('en');
|
|
459
|
+
i18nInstance.t = (key: string) => `${i18nInstance.language}:${key}`;
|
|
460
|
+
const renderTranslations: Array<(key: string) => string> = [];
|
|
461
|
+
let setProviderLanguage: ((language: string) => void) | undefined;
|
|
462
|
+
|
|
463
|
+
const StatefulI18nProvider = ({ children }: PropsWithChildren) => {
|
|
464
|
+
const [language, setLanguage] = useState('en');
|
|
465
|
+
setProviderLanguage = setLanguage;
|
|
466
|
+
|
|
467
|
+
return (
|
|
468
|
+
<ModernI18nProvider
|
|
469
|
+
value={{
|
|
470
|
+
language,
|
|
471
|
+
i18nInstance,
|
|
472
|
+
languages: ['en', 'cs'],
|
|
473
|
+
localePathRedirect: true,
|
|
474
|
+
localisedUrls,
|
|
475
|
+
updateLanguage: setLanguage,
|
|
476
|
+
}}
|
|
477
|
+
>
|
|
478
|
+
{children}
|
|
479
|
+
</ModernI18nProvider>
|
|
480
|
+
);
|
|
481
|
+
};
|
|
482
|
+
|
|
483
|
+
const Harness = () => {
|
|
484
|
+
const { t } = useModernI18n();
|
|
485
|
+
renderTranslations.push(t);
|
|
486
|
+
return <span data-testid="translation">{t('key')}</span>;
|
|
487
|
+
};
|
|
488
|
+
|
|
489
|
+
rendered = await renderWithRuntime(
|
|
490
|
+
<StatefulI18nProvider>
|
|
491
|
+
<Harness />
|
|
492
|
+
</StatefulI18nProvider>,
|
|
493
|
+
createReactRouterRuntimeContext({ navigate: rstest.fn() }),
|
|
494
|
+
);
|
|
495
|
+
|
|
496
|
+
expect(
|
|
497
|
+
rendered.container.querySelector('[data-testid="translation"]')
|
|
498
|
+
?.textContent,
|
|
499
|
+
).toBe('en:key');
|
|
500
|
+
const initialT = renderTranslations.at(-1);
|
|
501
|
+
|
|
502
|
+
await act(async () => {
|
|
503
|
+
i18nInstance.language = 'cs';
|
|
504
|
+
setProviderLanguage?.('cs');
|
|
505
|
+
});
|
|
506
|
+
|
|
507
|
+
expect(
|
|
508
|
+
rendered.container.querySelector('[data-testid="translation"]')
|
|
509
|
+
?.textContent,
|
|
510
|
+
).toBe('cs:key');
|
|
511
|
+
expect(renderTranslations.at(-1)).not.toBe(initialT);
|
|
512
|
+
});
|
|
456
513
|
});
|