@bleedingdev/modern-js-plugin-i18n 3.2.0-ultramodern.71 → 3.2.0-ultramodern.73

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.
@@ -133,18 +133,6 @@ const initializeI18nInstance = async (i18nInstance, finalLanguage, fallbackLangu
133
133
  };
134
134
  }
135
135
  }
136
- if (mergedBackend && hasOptions(i18nInstance)) {
137
- const defaultNS = initOptions.defaultNS || initOptions.ns || 'translation';
138
- const ns = Array.isArray(defaultNS) ? defaultNS[0] : defaultNS;
139
- let retries = 20;
140
- while(retries > 0){
141
- const actualInstance = (0, external_instance_js_namespaceObject.getActualI18nextInstance)(i18nInstance);
142
- const store = actualInstance.store;
143
- if (store?.data?.[finalLanguage]?.[ns]) break;
144
- await new Promise((resolve)=>setTimeout(resolve, 100));
145
- retries--;
146
- }
147
- }
148
136
  }
149
137
  };
150
138
  function hasOptions(instance) {
@@ -100,18 +100,6 @@ const initializeI18nInstance = async (i18nInstance, finalLanguage, fallbackLangu
100
100
  };
101
101
  }
102
102
  }
103
- if (mergedBackend && hasOptions(i18nInstance)) {
104
- const defaultNS = initOptions.defaultNS || initOptions.ns || 'translation';
105
- const ns = Array.isArray(defaultNS) ? defaultNS[0] : defaultNS;
106
- let retries = 20;
107
- while(retries > 0){
108
- const actualInstance = getActualI18nextInstance(i18nInstance);
109
- const store = actualInstance.store;
110
- if (store?.data?.[finalLanguage]?.[ns]) break;
111
- await new Promise((resolve)=>setTimeout(resolve, 100));
112
- retries--;
113
- }
114
- }
115
103
  }
116
104
  };
117
105
  function hasOptions(instance) {
@@ -101,18 +101,6 @@ const initializeI18nInstance = async (i18nInstance, finalLanguage, fallbackLangu
101
101
  };
102
102
  }
103
103
  }
104
- if (mergedBackend && hasOptions(i18nInstance)) {
105
- const defaultNS = initOptions.defaultNS || initOptions.ns || 'translation';
106
- const ns = Array.isArray(defaultNS) ? defaultNS[0] : defaultNS;
107
- let retries = 20;
108
- while(retries > 0){
109
- const actualInstance = getActualI18nextInstance(i18nInstance);
110
- const store = actualInstance.store;
111
- if (store?.data?.[finalLanguage]?.[ns]) break;
112
- await new Promise((resolve)=>setTimeout(resolve, 100));
113
- retries--;
114
- }
115
- }
116
104
  }
117
105
  };
118
106
  function hasOptions(instance) {
package/package.json CHANGED
@@ -17,7 +17,7 @@
17
17
  "modern",
18
18
  "modern.js"
19
19
  ],
20
- "version": "3.2.0-ultramodern.71",
20
+ "version": "3.2.0-ultramodern.73",
21
21
  "engines": {
22
22
  "node": ">=20"
23
23
  },
@@ -87,15 +87,15 @@
87
87
  "i18next-fs-backend": "^2.6.6",
88
88
  "i18next-http-backend": "^4.0.0",
89
89
  "i18next-http-middleware": "^3.9.7",
90
- "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.2.0-ultramodern.71",
91
- "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.2.0-ultramodern.71",
92
- "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.2.0-ultramodern.71",
93
- "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.2.0-ultramodern.71",
94
- "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.2.0-ultramodern.71",
95
- "@modern-js/server-runtime": "npm:@bleedingdev/modern-js-server-runtime@3.2.0-ultramodern.71"
90
+ "@modern-js/plugin": "npm:@bleedingdev/modern-js-plugin@3.2.0-ultramodern.73",
91
+ "@modern-js/server-runtime": "npm:@bleedingdev/modern-js-server-runtime@3.2.0-ultramodern.73",
92
+ "@modern-js/server-core": "npm:@bleedingdev/modern-js-server-core@3.2.0-ultramodern.73",
93
+ "@modern-js/types": "npm:@bleedingdev/modern-js-types@3.2.0-ultramodern.73",
94
+ "@modern-js/utils": "npm:@bleedingdev/modern-js-utils@3.2.0-ultramodern.73",
95
+ "@modern-js/runtime-utils": "npm:@bleedingdev/modern-js-runtime-utils@3.2.0-ultramodern.73"
96
96
  },
97
97
  "peerDependencies": {
98
- "@modern-js/runtime": "3.2.0-ultramodern.71",
98
+ "@modern-js/runtime": "3.2.0-ultramodern.73",
99
99
  "i18next": ">=25.7.4",
100
100
  "react": "^19.2.6",
101
101
  "react-dom": "^19.2.6",
@@ -120,8 +120,8 @@
120
120
  "react-dom": "^19.2.6",
121
121
  "react-i18next": "17.0.8",
122
122
  "ts-jest": "^29.4.11",
123
- "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.2.0-ultramodern.71",
124
- "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.2.0-ultramodern.71"
123
+ "@modern-js/app-tools": "npm:@bleedingdev/modern-js-app-tools@3.2.0-ultramodern.73",
124
+ "@modern-js/runtime": "npm:@bleedingdev/modern-js-runtime@3.2.0-ultramodern.73"
125
125
  },
126
126
  "sideEffects": false,
127
127
  "publishConfig": {
@@ -238,32 +238,10 @@ export const initializeI18nInstance = async (
238
238
  }
239
239
  }
240
240
 
241
- if (mergedBackend && hasOptions(i18nInstance)) {
242
- // For chained backend with cacheHitMode: 'refreshAndUpdateStore',
243
- // i18next-chained-backend automatically:
244
- // 1. Loads from the first backend (HTTP/FS) and displays immediately
245
- // 2. Asynchronously loads from the second backend (SDK) and updates the store
246
- // 3. Triggers 'loaded' event when SDK resources are loaded, which causes React to re-render
247
- //
248
- // Note: i18next.init() returns a Promise that resolves when the first backend loads.
249
- // For chained backend, it does NOT wait for the second backend (SDK) to load.
250
- // The SDK backend loads asynchronously and triggers 'loaded' event automatically.
251
- const defaultNS =
252
- initOptions.defaultNS || initOptions.ns || 'translation';
253
- const ns = Array.isArray(defaultNS) ? defaultNS[0] : defaultNS;
254
-
255
- let retries = 20;
256
- while (retries > 0) {
257
- // Get the actual i18next instance to access store property
258
- const actualInstance = getActualI18nextInstance(i18nInstance);
259
- const store = (actualInstance as any).store;
260
- if (store?.data?.[finalLanguage]?.[ns]) {
261
- break;
262
- }
263
- await new Promise(resolve => setTimeout(resolve, 100));
264
- retries--;
265
- }
266
- }
241
+ // i18next.init() is the synchronization boundary for the primary backend.
242
+ // Chained SDK refreshes update the store through their own loaded events and
243
+ // must not block SSR HTML, otherwise missing/edge-only resources add fixed
244
+ // latency to every route render.
267
245
  }
268
246
  };
269
247
 
@@ -0,0 +1,45 @@
1
+ import { describe, expect, test } from '@rstest/core';
2
+ import type { I18nInstance } from '../src/runtime/i18n';
3
+ import { initializeI18nInstance } from '../src/runtime/i18n/utils';
4
+
5
+ function createBackendI18nInstance(): I18nInstance {
6
+ return {
7
+ language: 'en',
8
+ isInitialized: false,
9
+ init: async () => undefined,
10
+ use: () => {},
11
+ options: {},
12
+ store: {
13
+ data: {},
14
+ },
15
+ };
16
+ }
17
+
18
+ describe('i18n runtime utils', () => {
19
+ test('does not poll for backend resources after init', async () => {
20
+ const i18nInstance = createBackendI18nInstance();
21
+ const init = rstest.fn(async () => {
22
+ i18nInstance.isInitialized = true;
23
+ });
24
+ i18nInstance.init = init as I18nInstance['init'];
25
+ const setTimeoutSpy = rstest.spyOn(globalThis, 'setTimeout');
26
+
27
+ await initializeI18nInstance(
28
+ i18nInstance,
29
+ 'cs',
30
+ 'en',
31
+ ['en', 'cs'],
32
+ {},
33
+ {
34
+ enabled: true,
35
+ loadPath: '/locales/{{lng}}/{{ns}}.json',
36
+ },
37
+ {},
38
+ );
39
+
40
+ expect(init).toHaveBeenCalledTimes(1);
41
+ expect(setTimeoutSpy).not.toHaveBeenCalled();
42
+
43
+ setTimeoutSpy.mockRestore();
44
+ });
45
+ });