@modern-js/plugin-i18n 2.69.5 → 3.0.0-alpha.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 (151) hide show
  1. package/README.md +6 -0
  2. package/dist/cjs/cli/index.cjs +154 -0
  3. package/dist/cjs/runtime/I18nLink.cjs +68 -0
  4. package/dist/cjs/runtime/context.cjs +138 -0
  5. package/dist/cjs/runtime/hooks.cjs +189 -0
  6. package/dist/cjs/runtime/i18n/backend/config.cjs +39 -0
  7. package/dist/cjs/runtime/i18n/backend/defaults.cjs +56 -0
  8. package/dist/cjs/runtime/i18n/backend/defaults.node.cjs +56 -0
  9. package/dist/cjs/runtime/i18n/backend/index.cjs +108 -0
  10. package/dist/cjs/runtime/i18n/backend/middleware.cjs +54 -0
  11. package/dist/cjs/runtime/i18n/backend/middleware.common.cjs +105 -0
  12. package/dist/cjs/runtime/i18n/backend/middleware.node.cjs +58 -0
  13. package/dist/cjs/runtime/i18n/backend/sdk-backend.cjs +171 -0
  14. package/dist/cjs/runtime/i18n/detection/config.cjs +63 -0
  15. package/dist/cjs/runtime/i18n/detection/index.cjs +309 -0
  16. package/dist/cjs/runtime/i18n/detection/middleware.cjs +185 -0
  17. package/dist/cjs/runtime/i18n/detection/middleware.node.cjs +74 -0
  18. package/dist/cjs/runtime/i18n/index.cjs +43 -0
  19. package/dist/cjs/runtime/i18n/instance.cjs +132 -0
  20. package/dist/cjs/runtime/i18n/utils.cjs +185 -0
  21. package/dist/cjs/runtime/index.cjs +162 -0
  22. package/dist/cjs/runtime/types.cjs +18 -0
  23. package/dist/cjs/runtime/utils.cjs +134 -0
  24. package/dist/cjs/server/index.cjs +178 -0
  25. package/dist/cjs/shared/deepMerge.cjs +54 -0
  26. package/dist/cjs/shared/detection.cjs +105 -0
  27. package/dist/cjs/shared/type.cjs +18 -0
  28. package/dist/cjs/shared/utils.cjs +78 -0
  29. package/dist/esm/cli/index.js +106 -0
  30. package/dist/esm/runtime/I18nLink.js +31 -0
  31. package/dist/esm/runtime/context.js +101 -0
  32. package/dist/esm/runtime/hooks.js +146 -0
  33. package/dist/esm/runtime/i18n/backend/config.js +5 -0
  34. package/dist/esm/runtime/i18n/backend/defaults.js +19 -0
  35. package/dist/esm/runtime/i18n/backend/defaults.node.js +19 -0
  36. package/dist/esm/runtime/i18n/backend/index.js +74 -0
  37. package/dist/esm/runtime/i18n/backend/middleware.common.js +61 -0
  38. package/dist/esm/runtime/i18n/backend/middleware.js +7 -0
  39. package/dist/esm/runtime/i18n/backend/middleware.node.js +8 -0
  40. package/dist/esm/runtime/i18n/backend/sdk-backend.js +137 -0
  41. package/dist/esm/runtime/i18n/detection/config.js +26 -0
  42. package/dist/esm/runtime/i18n/detection/index.js +260 -0
  43. package/dist/esm/runtime/i18n/detection/middleware.js +132 -0
  44. package/dist/esm/runtime/i18n/detection/middleware.node.js +31 -0
  45. package/dist/esm/runtime/i18n/index.js +3 -0
  46. package/dist/esm/runtime/i18n/instance.js +77 -0
  47. package/dist/esm/runtime/i18n/utils.js +136 -0
  48. package/dist/esm/runtime/index.js +119 -0
  49. package/dist/esm/runtime/types.js +0 -0
  50. package/dist/esm/runtime/utils.js +82 -0
  51. package/dist/esm/server/index.js +168 -0
  52. package/dist/esm/shared/deepMerge.js +20 -0
  53. package/dist/esm/shared/detection.js +71 -0
  54. package/dist/esm/shared/type.js +0 -0
  55. package/dist/esm/shared/utils.js +35 -0
  56. package/dist/esm-node/cli/index.js +106 -0
  57. package/dist/esm-node/runtime/I18nLink.js +31 -0
  58. package/dist/esm-node/runtime/context.js +101 -0
  59. package/dist/esm-node/runtime/hooks.js +146 -0
  60. package/dist/esm-node/runtime/i18n/backend/config.js +5 -0
  61. package/dist/esm-node/runtime/i18n/backend/defaults.js +19 -0
  62. package/dist/esm-node/runtime/i18n/backend/defaults.node.js +19 -0
  63. package/dist/esm-node/runtime/i18n/backend/index.js +74 -0
  64. package/dist/esm-node/runtime/i18n/backend/middleware.common.js +61 -0
  65. package/dist/esm-node/runtime/i18n/backend/middleware.js +7 -0
  66. package/dist/esm-node/runtime/i18n/backend/middleware.node.js +8 -0
  67. package/dist/esm-node/runtime/i18n/backend/sdk-backend.js +137 -0
  68. package/dist/esm-node/runtime/i18n/detection/config.js +26 -0
  69. package/dist/esm-node/runtime/i18n/detection/index.js +260 -0
  70. package/dist/esm-node/runtime/i18n/detection/middleware.js +132 -0
  71. package/dist/esm-node/runtime/i18n/detection/middleware.node.js +31 -0
  72. package/dist/esm-node/runtime/i18n/index.js +3 -0
  73. package/dist/esm-node/runtime/i18n/instance.js +77 -0
  74. package/dist/esm-node/runtime/i18n/utils.js +136 -0
  75. package/dist/esm-node/runtime/index.js +119 -0
  76. package/dist/esm-node/runtime/types.js +0 -0
  77. package/dist/esm-node/runtime/utils.js +82 -0
  78. package/dist/esm-node/server/index.js +168 -0
  79. package/dist/esm-node/shared/deepMerge.js +20 -0
  80. package/dist/esm-node/shared/detection.js +71 -0
  81. package/dist/esm-node/shared/type.js +0 -0
  82. package/dist/esm-node/shared/utils.js +35 -0
  83. package/dist/types/cli/index.d.ts +21 -0
  84. package/dist/types/runtime/I18nLink.d.ts +8 -0
  85. package/dist/types/runtime/context.d.ts +38 -0
  86. package/dist/types/runtime/hooks.d.ts +28 -0
  87. package/dist/types/runtime/i18n/backend/config.d.ts +2 -0
  88. package/dist/types/runtime/i18n/backend/defaults.d.ts +13 -0
  89. package/dist/types/runtime/i18n/backend/defaults.node.d.ts +8 -0
  90. package/dist/types/runtime/i18n/backend/index.d.ts +3 -0
  91. package/dist/types/runtime/i18n/backend/middleware.common.d.ts +14 -0
  92. package/dist/types/runtime/i18n/backend/middleware.d.ts +12 -0
  93. package/dist/types/runtime/i18n/backend/middleware.node.d.ts +13 -0
  94. package/dist/types/runtime/i18n/backend/sdk-backend.d.ts +52 -0
  95. package/dist/types/runtime/i18n/detection/config.d.ts +11 -0
  96. package/dist/types/runtime/i18n/detection/index.d.ts +50 -0
  97. package/dist/types/runtime/i18n/detection/middleware.d.ts +24 -0
  98. package/dist/types/runtime/i18n/detection/middleware.node.d.ts +17 -0
  99. package/dist/types/runtime/i18n/index.d.ts +3 -0
  100. package/dist/types/runtime/i18n/instance.d.ts +93 -0
  101. package/dist/types/runtime/i18n/utils.d.ts +29 -0
  102. package/dist/types/runtime/index.d.ts +19 -0
  103. package/dist/types/runtime/types.d.ts +15 -0
  104. package/dist/types/runtime/utils.d.ts +33 -0
  105. package/dist/types/server/index.d.ts +8 -0
  106. package/dist/types/shared/deepMerge.d.ts +1 -0
  107. package/dist/types/shared/detection.d.ts +11 -0
  108. package/dist/types/shared/type.d.ts +156 -0
  109. package/dist/types/shared/utils.d.ts +5 -0
  110. package/package.json +100 -34
  111. package/rslib.config.mts +4 -0
  112. package/src/cli/index.ts +245 -0
  113. package/src/runtime/I18nLink.tsx +76 -0
  114. package/src/runtime/context.tsx +256 -0
  115. package/src/runtime/hooks.ts +274 -0
  116. package/src/runtime/i18n/backend/config.ts +10 -0
  117. package/src/runtime/i18n/backend/defaults.node.ts +31 -0
  118. package/src/runtime/i18n/backend/defaults.ts +37 -0
  119. package/src/runtime/i18n/backend/index.ts +181 -0
  120. package/src/runtime/i18n/backend/middleware.common.ts +116 -0
  121. package/src/runtime/i18n/backend/middleware.node.ts +32 -0
  122. package/src/runtime/i18n/backend/middleware.ts +28 -0
  123. package/src/runtime/i18n/backend/sdk-backend.ts +292 -0
  124. package/src/runtime/i18n/detection/config.ts +32 -0
  125. package/src/runtime/i18n/detection/index.ts +641 -0
  126. package/src/runtime/i18n/detection/middleware.node.ts +84 -0
  127. package/src/runtime/i18n/detection/middleware.ts +251 -0
  128. package/src/runtime/i18n/index.ts +8 -0
  129. package/src/runtime/i18n/instance.ts +227 -0
  130. package/src/runtime/i18n/utils.ts +333 -0
  131. package/src/runtime/index.tsx +275 -0
  132. package/src/runtime/types.ts +17 -0
  133. package/src/runtime/utils.ts +151 -0
  134. package/src/server/index.ts +336 -0
  135. package/src/shared/deepMerge.ts +38 -0
  136. package/src/shared/detection.ts +131 -0
  137. package/src/shared/type.ts +170 -0
  138. package/src/shared/utils.ts +82 -0
  139. package/tsconfig.json +12 -0
  140. package/dist/cjs/index.js +0 -73
  141. package/dist/cjs/languageDetector.js +0 -51
  142. package/dist/cjs/utils/index.js +0 -39
  143. package/dist/esm/index.js +0 -61
  144. package/dist/esm/languageDetector.js +0 -33
  145. package/dist/esm/utils/index.js +0 -16
  146. package/dist/esm-node/index.js +0 -49
  147. package/dist/esm-node/languageDetector.js +0 -26
  148. package/dist/esm-node/utils/index.js +0 -15
  149. package/dist/types/index.d.ts +0 -34
  150. package/dist/types/languageDetector.d.ts +0 -6
  151. package/dist/types/utils/index.d.ts +0 -5
@@ -0,0 +1,251 @@
1
+ import { isBrowser } from '@modern-js/runtime';
2
+ import LanguageDetector from 'i18next-browser-languagedetector';
3
+ import type { I18nInstance } from '../instance';
4
+ import { getActualI18nextInstance, isI18nWrapperInstance } from '../instance';
5
+
6
+ /**
7
+ * Register LanguageDetector plugin to i18n instance
8
+ * Must be called before init() to properly register the detector
9
+ * For wrapper instances, ensure detector is registered on the underlying i18next instance
10
+ */
11
+ export const useI18nextLanguageDetector = (i18nInstance: I18nInstance) => {
12
+ if (!i18nInstance.isInitialized) {
13
+ // For wrapper instances, also register on the underlying instance
14
+ if (isI18nWrapperInstance(i18nInstance)) {
15
+ const actualInstance = getActualI18nextInstance(i18nInstance);
16
+ if (actualInstance && !actualInstance.isInitialized) {
17
+ actualInstance.use(LanguageDetector);
18
+ }
19
+ }
20
+ return i18nInstance.use(LanguageDetector);
21
+ }
22
+ return i18nInstance;
23
+ };
24
+
25
+ /**
26
+ * Read language directly from localStorage/cookie
27
+ * Fallback when detector is not available in services
28
+ */
29
+ export const readLanguageFromStorage = (
30
+ detectionOptions?: any,
31
+ ): string | undefined => {
32
+ try {
33
+ const options = detectionOptions || {};
34
+ const order = options.order || [
35
+ 'querystring',
36
+ 'cookie',
37
+ 'localStorage',
38
+ 'navigator',
39
+ 'htmlTag',
40
+ 'path',
41
+ 'subdomain',
42
+ ];
43
+
44
+ // Follow the detection order
45
+ for (const method of order) {
46
+ switch (method) {
47
+ case 'querystring': {
48
+ const lookupKey = options.lookupQuerystring || 'lng';
49
+ const urlParams = new URLSearchParams(window.location.search);
50
+ const lang = urlParams.get(lookupKey);
51
+ if (lang) {
52
+ return lang;
53
+ }
54
+ break;
55
+ }
56
+ case 'cookie': {
57
+ const lookupKey = options.lookupCookie || 'i18next';
58
+ const cookies = document.cookie
59
+ .split(';')
60
+ .reduce((acc: Record<string, string>, item: string) => {
61
+ const [key, value] = item.trim().split('=');
62
+ if (key && value) {
63
+ acc[key] = decodeURIComponent(value);
64
+ }
65
+ return acc;
66
+ }, {});
67
+ if (cookies[lookupKey]) {
68
+ return cookies[lookupKey];
69
+ }
70
+ break;
71
+ }
72
+ case 'localStorage': {
73
+ const lookupKey = options.lookupLocalStorage || 'i18nextLng';
74
+ const keysToCheck = [lookupKey];
75
+ if (lookupKey === 'i18nextLng') {
76
+ keysToCheck.push('i18next');
77
+ }
78
+ for (const key of keysToCheck) {
79
+ const stored = localStorage.getItem(key);
80
+ if (stored) {
81
+ return stored;
82
+ }
83
+ }
84
+ break;
85
+ }
86
+ case 'navigator': {
87
+ if (navigator.language) {
88
+ return navigator.language.split('-')[0];
89
+ }
90
+ break;
91
+ }
92
+ case 'htmlTag': {
93
+ const htmlLang = document.documentElement.lang;
94
+ if (htmlLang) {
95
+ return htmlLang.split('-')[0];
96
+ }
97
+ break;
98
+ }
99
+ }
100
+ }
101
+ } catch (error) {
102
+ // Ignore errors
103
+ }
104
+
105
+ return undefined;
106
+ };
107
+
108
+ /**
109
+ * Detect language using i18next-browser-languagedetector
110
+ * For initialized instances without detector in services, manually create a detector instance
111
+ * For wrapper instances, access the underlying i18next instance's services
112
+ */
113
+ export const detectLanguage = (
114
+ i18nInstance: I18nInstance,
115
+ _request?: any,
116
+ detectionOptions?: any,
117
+ ): string | undefined => {
118
+ try {
119
+ // For wrapper instances, get the underlying i18next instance
120
+ const actualInstance = isI18nWrapperInstance(i18nInstance)
121
+ ? getActualI18nextInstance(i18nInstance)
122
+ : i18nInstance;
123
+
124
+ // Check if either instance is initialized
125
+ const isInitialized =
126
+ i18nInstance.isInitialized || actualInstance?.isInitialized;
127
+
128
+ // Try to get detector from services (prefer actual instance for wrapper)
129
+ const detector =
130
+ actualInstance?.services?.languageDetector ||
131
+ i18nInstance.services?.languageDetector;
132
+ if (detector && typeof detector.detect === 'function') {
133
+ const result = detector.detect();
134
+ if (typeof result === 'string') {
135
+ return result;
136
+ }
137
+ if (Array.isArray(result) && result.length > 0) {
138
+ return result[0];
139
+ }
140
+ // If detector exists but returns undefined, continue to fallback logic
141
+ }
142
+
143
+ // Fallback: read directly from storage (always try this in browser)
144
+ // This is important for wrapper instances where detector might not be properly initialized
145
+ if (isBrowser()) {
146
+ const directRead = readLanguageFromStorage(detectionOptions);
147
+ if (directRead) {
148
+ return directRead;
149
+ }
150
+ }
151
+
152
+ // If instance is initialized, try creating manual detector
153
+ if (isInitialized) {
154
+ // Use actual instance's services/options for wrapper, otherwise use wrapper's
155
+ const servicesToUse = actualInstance?.services || i18nInstance.services;
156
+ const optionsToUse = actualInstance?.options || i18nInstance.options;
157
+
158
+ if (servicesToUse && optionsToUse) {
159
+ const manualDetector = new LanguageDetector();
160
+ const mergedOptions = detectionOptions
161
+ ? { ...optionsToUse, detection: detectionOptions }
162
+ : optionsToUse;
163
+ manualDetector.init(servicesToUse, mergedOptions as any);
164
+
165
+ const result = manualDetector.detect();
166
+ if (typeof result === 'string') {
167
+ return result;
168
+ }
169
+ if (Array.isArray(result) && result.length > 0) {
170
+ return result[0];
171
+ }
172
+ }
173
+ }
174
+ } catch (error) {
175
+ return undefined;
176
+ }
177
+
178
+ return undefined;
179
+ };
180
+
181
+ /**
182
+ * Cache user language to localStorage/cookie
183
+ * Uses LanguageDetector's cacheUserLanguage method when available
184
+ * For wrapper instances, access the underlying i18next instance's services
185
+ */
186
+ export const cacheUserLanguage = (
187
+ i18nInstance: I18nInstance,
188
+ language: string,
189
+ detectionOptions?: any,
190
+ ): void => {
191
+ if (typeof window === 'undefined') {
192
+ return;
193
+ }
194
+
195
+ try {
196
+ // For wrapper instances, get the underlying i18next instance
197
+ const actualInstance = isI18nWrapperInstance(i18nInstance)
198
+ ? getActualI18nextInstance(i18nInstance)
199
+ : i18nInstance;
200
+
201
+ // Try to use detector's cacheUserLanguage method first
202
+ // Prefer actual instance's detector for wrapper instances
203
+ const detector =
204
+ actualInstance?.services?.languageDetector ||
205
+ i18nInstance.services?.languageDetector;
206
+ if (detector && typeof detector.cacheUserLanguage === 'function') {
207
+ try {
208
+ detector.cacheUserLanguage(language);
209
+ return;
210
+ } catch (error) {
211
+ if (process.env.NODE_ENV === 'development') {
212
+ console.warn(
213
+ '[i18n] Failed to cache via detector, falling back to manual cache:',
214
+ error,
215
+ );
216
+ }
217
+ }
218
+ }
219
+
220
+ // Fallback: manually create detector instance if i18n is initialized
221
+ const isInitialized =
222
+ i18nInstance.isInitialized || actualInstance?.isInitialized;
223
+ const servicesToUse = actualInstance?.services || i18nInstance.services;
224
+ const optionsToUse = actualInstance?.options || i18nInstance.options;
225
+
226
+ if (isInitialized && servicesToUse && optionsToUse) {
227
+ try {
228
+ const userOptions = detectionOptions || optionsToUse?.detection;
229
+ const mergedOptions = userOptions
230
+ ? { ...optionsToUse, detection: userOptions }
231
+ : optionsToUse;
232
+
233
+ const manualDetector = new LanguageDetector();
234
+ manualDetector.init(servicesToUse, mergedOptions as any);
235
+
236
+ if (typeof manualDetector.cacheUserLanguage === 'function') {
237
+ manualDetector.cacheUserLanguage(language);
238
+ return;
239
+ }
240
+ } catch (error) {
241
+ if (process.env.NODE_ENV === 'development') {
242
+ console.warn('[i18n] Failed to create manual detector:', error);
243
+ }
244
+ }
245
+ }
246
+ } catch (error) {
247
+ if (process.env.NODE_ENV === 'development') {
248
+ console.error('Failed to cache user language:', error);
249
+ }
250
+ }
251
+ };
@@ -0,0 +1,8 @@
1
+ export type {
2
+ I18nInstance,
3
+ I18nInitOptions,
4
+ } from './instance';
5
+
6
+ export { isI18nInstance, getI18nInstance } from './instance';
7
+
8
+ export { assertI18nInstance } from './utils';
@@ -0,0 +1,227 @@
1
+ import type { BaseBackendOptions } from '../../shared/type';
2
+
3
+ export interface I18nResourceStore {
4
+ data?: {
5
+ [language: string]: {
6
+ [namespace: string]: string | { [key: string]: any };
7
+ };
8
+ };
9
+ addResourceBundle?: (
10
+ language: string,
11
+ namespace: string,
12
+ resources: Record<string, string>,
13
+ deep?: boolean,
14
+ overwrite?: boolean,
15
+ ) => void;
16
+ }
17
+
18
+ export function isI18nWrapperInstance(obj: any): boolean {
19
+ if (!obj || typeof obj !== 'object') {
20
+ return false;
21
+ }
22
+ if (!obj.i18nInstance || typeof obj.i18nInstance !== 'object') {
23
+ return false;
24
+ }
25
+ if (!obj.i18nInstance.instance) {
26
+ return false;
27
+ }
28
+ if (typeof obj.init !== 'function' || typeof obj.use !== 'function') {
29
+ return false;
30
+ }
31
+ return true;
32
+ }
33
+
34
+ export function getI18nWrapperI18nextInstance(wrapperInstance: any): any {
35
+ if (isI18nWrapperInstance(wrapperInstance)) {
36
+ return wrapperInstance.i18nInstance?.instance;
37
+ }
38
+ return null;
39
+ }
40
+
41
+ export function getActualI18nextInstance(instance: I18nInstance | any): any {
42
+ if (isI18nWrapperInstance(instance)) {
43
+ const i18nextInstance = getI18nWrapperI18nextInstance(instance);
44
+ return i18nextInstance || instance;
45
+ }
46
+ return instance;
47
+ }
48
+
49
+ export interface I18nInstance {
50
+ language: string;
51
+ isInitialized: boolean;
52
+ init: {
53
+ (callback?: (error: any, t: any) => void): Promise<any>;
54
+ (
55
+ options: I18nInitOptions,
56
+ callback?: (error: any, t: any) => void,
57
+ ): Promise<any>;
58
+ };
59
+ changeLanguage?: (
60
+ lng?: string,
61
+ callback?: (error: any, t: any) => void,
62
+ ) => Promise<any>;
63
+ setLang?: (lang: string) => void | Promise<void>;
64
+ use: (plugin: any) => void;
65
+ createInstance: (options?: I18nInitOptions) => I18nInstance;
66
+ cloneInstance?: () => I18nInstance; // ssr need
67
+ // i18next store (may not be in type definition but exists at runtime)
68
+ store?: I18nResourceStore;
69
+ emit?: (event: string, ...args: any[]) => void;
70
+ reloadResources?: (language?: string, namespace?: string) => Promise<void>;
71
+ services?: {
72
+ languageDetector?: {
73
+ detect: (request?: any, options?: any) => string | string[] | undefined;
74
+ [key: string]: any;
75
+ };
76
+ resourceStore?: I18nResourceStore;
77
+ backend?: any; // Backend instance (e.g., SdkBackend)
78
+ [key: string]: any;
79
+ };
80
+ // i18next instance options (available after initialization)
81
+ options?: {
82
+ backend?: BackendOptions;
83
+ [key: string]: any;
84
+ };
85
+ [key: string]: any;
86
+ }
87
+
88
+ type LanguageDetectorOrder = string[];
89
+ type LanguageDetectorCaches = boolean | string[];
90
+ export interface LanguageDetectorOptions {
91
+ order?: LanguageDetectorOrder;
92
+ lookupQuerystring?: string;
93
+ lookupCookie?: string;
94
+ lookupLocalStorage?: string;
95
+ lookupSession?: string;
96
+ lookupFromPathIndex?: number;
97
+ caches?: LanguageDetectorCaches;
98
+ cookieExpirationDate?: Date;
99
+ cookieDomain?: string;
100
+ lookupHeader?: string;
101
+ }
102
+
103
+ export interface BackendOptions extends Omit<BaseBackendOptions, 'enabled'> {
104
+ parse?: (data: string) => any;
105
+ stringify?: (data: any) => string;
106
+ [key: string]: any;
107
+ }
108
+
109
+ export interface Resources {
110
+ [lng: string]: {
111
+ [source: string]: string | Record<string, string>;
112
+ };
113
+ }
114
+
115
+ export type I18nInitOptions = {
116
+ lng?: string;
117
+ fallbackLng?: string;
118
+ supportedLngs?: string[];
119
+ initImmediate?: boolean;
120
+ detection?: LanguageDetectorOptions;
121
+ backend?: BackendOptions;
122
+ resources?: Resources;
123
+ ns?: string | string[];
124
+ defaultNS?: string | string[];
125
+ interpolation?: {
126
+ escapeValue?: boolean;
127
+ [key: string]: any;
128
+ };
129
+ react?: {
130
+ useSuspense?: boolean;
131
+ [key: string]: any;
132
+ };
133
+ };
134
+
135
+ export function isI18nInstance(obj: any): obj is I18nInstance {
136
+ if (!obj || typeof obj !== 'object') {
137
+ return false;
138
+ }
139
+
140
+ if (isI18nWrapperInstance(obj)) {
141
+ return true;
142
+ }
143
+
144
+ return typeof obj.init === 'function' && typeof obj.use === 'function';
145
+ }
146
+
147
+ async function tryImportI18next(): Promise<I18nInstance | null> {
148
+ try {
149
+ const i18next = await import('i18next');
150
+ return i18next.default as unknown as I18nInstance;
151
+ } catch (error) {
152
+ return null;
153
+ }
154
+ }
155
+
156
+ async function createI18nextInstance(): Promise<I18nInstance | null> {
157
+ try {
158
+ const i18next = await tryImportI18next();
159
+ if (!i18next) {
160
+ return null;
161
+ }
162
+ return i18next.createInstance({
163
+ initImmediate: false,
164
+ }) as unknown as I18nInstance;
165
+ } catch (error) {
166
+ return null;
167
+ }
168
+ }
169
+
170
+ async function tryImportReactI18next() {
171
+ try {
172
+ const reactI18next = await import('react-i18next');
173
+ return reactI18next;
174
+ } catch (error) {
175
+ return null;
176
+ }
177
+ }
178
+
179
+ export function getI18nextInstanceForProvider(
180
+ instance: I18nInstance | any,
181
+ ): any {
182
+ if (isI18nWrapperInstance(instance)) {
183
+ const i18nextInstance = getI18nWrapperI18nextInstance(instance);
184
+ if (i18nextInstance) {
185
+ return i18nextInstance;
186
+ }
187
+ }
188
+
189
+ return instance;
190
+ }
191
+
192
+ export async function getI18nInstance(
193
+ userInstance?: I18nInstance | any,
194
+ ): Promise<I18nInstance> {
195
+ if (userInstance) {
196
+ if (isI18nWrapperInstance(userInstance)) {
197
+ return userInstance as I18nInstance;
198
+ }
199
+
200
+ if (isI18nInstance(userInstance)) {
201
+ return userInstance;
202
+ }
203
+ }
204
+
205
+ const i18nextInstance = await createI18nextInstance();
206
+ if (i18nextInstance) {
207
+ return i18nextInstance;
208
+ }
209
+
210
+ throw new Error('No i18n instance found');
211
+ }
212
+
213
+ export async function getInitReactI18next() {
214
+ const reactI18nextModule = await tryImportReactI18next();
215
+ if (reactI18nextModule) {
216
+ return reactI18nextModule.initReactI18next;
217
+ }
218
+ return null;
219
+ }
220
+
221
+ export async function getI18nextProvider() {
222
+ const reactI18nextModule = await tryImportReactI18next();
223
+ if (reactI18nextModule) {
224
+ return reactI18nextModule.I18nextProvider;
225
+ }
226
+ return null;
227
+ }