@hua-labs/i18n-core 2.0.0 → 2.0.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/README.md +57 -597
- package/dist/chunk-F4PDBJLO.mjs +973 -0
- package/dist/chunk-F4PDBJLO.mjs.map +1 -0
- package/dist/index.d.mts +249 -0
- package/dist/index.d.ts +117 -30
- package/dist/index.js +1818 -177
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +845 -0
- package/dist/index.mjs.map +1 -0
- package/dist/server-4TeBq6hp.d.mts +367 -0
- package/dist/server-4TeBq6hp.d.ts +367 -0
- package/dist/server.d.mts +1 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +977 -0
- package/dist/server.js.map +1 -0
- package/dist/server.mjs +3 -0
- package/dist/server.mjs.map +1 -0
- package/package.json +42 -19
- package/src/__tests__/debug-tools.test.ts +359 -0
- package/src/__tests__/default-translations.test.ts +179 -0
- package/src/__tests__/i18n-resource.test.ts +137 -0
- package/src/__tests__/lazy-loader.test.ts +109 -0
- package/src/__tests__/missing-key-overlay.test.tsx +339 -0
- package/src/__tests__/translator-factory.test.ts +120 -0
- package/src/__tests__/translator.test.ts +442 -0
- package/src/__tests__/types.test.ts +211 -0
- package/src/__tests__/useI18n.test.tsx +181 -0
- package/src/__tests__/useTranslation.test.tsx +110 -0
- package/src/components/MissingKeyOverlay.tsx +1 -1
- package/src/core/lazy-loader.ts +2 -2
- package/src/core/translator.tsx +151 -62
- package/src/hooks/useI18n.tsx +96 -115
- package/src/hooks/useTranslation.tsx +12 -10
- package/src/index.ts +102 -5
- package/src/server.ts +9 -0
- package/src/types/index.ts +67 -12
- package/LICENSE +0 -21
- package/dist/components/MissingKeyOverlay.d.ts +0 -33
- package/dist/components/MissingKeyOverlay.d.ts.map +0 -1
- package/dist/components/MissingKeyOverlay.js +0 -138
- package/dist/components/MissingKeyOverlay.js.map +0 -1
- package/dist/core/debug-tools.d.ts +0 -37
- package/dist/core/debug-tools.d.ts.map +0 -1
- package/dist/core/debug-tools.js +0 -241
- package/dist/core/debug-tools.js.map +0 -1
- package/dist/core/i18n-resource.d.ts +0 -59
- package/dist/core/i18n-resource.d.ts.map +0 -1
- package/dist/core/i18n-resource.js +0 -153
- package/dist/core/i18n-resource.js.map +0 -1
- package/dist/core/lazy-loader.d.ts +0 -82
- package/dist/core/lazy-loader.d.ts.map +0 -1
- package/dist/core/lazy-loader.js +0 -193
- package/dist/core/lazy-loader.js.map +0 -1
- package/dist/core/translator-factory.d.ts +0 -50
- package/dist/core/translator-factory.d.ts.map +0 -1
- package/dist/core/translator-factory.js +0 -117
- package/dist/core/translator-factory.js.map +0 -1
- package/dist/core/translator.d.ts +0 -202
- package/dist/core/translator.d.ts.map +0 -1
- package/dist/core/translator.js +0 -912
- package/dist/core/translator.js.map +0 -1
- package/dist/hooks/useI18n.d.ts +0 -39
- package/dist/hooks/useI18n.d.ts.map +0 -1
- package/dist/hooks/useI18n.js +0 -531
- package/dist/hooks/useI18n.js.map +0 -1
- package/dist/hooks/useTranslation.d.ts +0 -55
- package/dist/hooks/useTranslation.d.ts.map +0 -1
- package/dist/hooks/useTranslation.js +0 -58
- package/dist/hooks/useTranslation.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/types/index.d.ts +0 -162
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -191
- package/dist/types/index.js.map +0 -1
- package/dist/utils/default-translations.d.ts +0 -20
- package/dist/utils/default-translations.d.ts.map +0 -1
- package/dist/utils/default-translations.js +0 -123
- package/dist/utils/default-translations.js.map +0 -1
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,845 @@
|
|
|
1
|
+
import { validateI18nConfig, Translator } from './chunk-F4PDBJLO.mjs';
|
|
2
|
+
export { Translator, serverTranslate, ssrTranslate } from './chunk-F4PDBJLO.mjs';
|
|
3
|
+
import React, { createContext, useState, useEffect, useMemo, useCallback, useContext } from 'react';
|
|
4
|
+
import { jsx } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
// src/core/translator-factory.ts
|
|
7
|
+
var TranslatorFactory = class {
|
|
8
|
+
// 최대 인스턴스 수 제한
|
|
9
|
+
/**
|
|
10
|
+
* Config를 기반으로 고유 키 생성
|
|
11
|
+
*/
|
|
12
|
+
static generateConfigKey(config) {
|
|
13
|
+
const keyParts = [
|
|
14
|
+
config.defaultLanguage,
|
|
15
|
+
config.fallbackLanguage || "en",
|
|
16
|
+
config.namespaces?.join(",") || "common",
|
|
17
|
+
config.debug ? "debug" : "prod"
|
|
18
|
+
];
|
|
19
|
+
return keyParts.join("|");
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Config가 변경되었는지 확인
|
|
23
|
+
*/
|
|
24
|
+
static isConfigChanged(configKey, newConfig) {
|
|
25
|
+
const cachedConfig = this.configCache.get(configKey);
|
|
26
|
+
if (!cachedConfig) return true;
|
|
27
|
+
return cachedConfig.defaultLanguage !== newConfig.defaultLanguage || cachedConfig.fallbackLanguage !== newConfig.fallbackLanguage || JSON.stringify(cachedConfig.namespaces) !== JSON.stringify(newConfig.namespaces) || cachedConfig.debug !== newConfig.debug;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Translator 인스턴스 생성 또는 반환
|
|
31
|
+
*/
|
|
32
|
+
static create(config) {
|
|
33
|
+
const configKey = this.generateConfigKey(config);
|
|
34
|
+
if (!this.instances.has(configKey) || this.isConfigChanged(configKey, config)) {
|
|
35
|
+
if (this.instances.size >= this.MAX_INSTANCES && !this.instances.has(configKey)) {
|
|
36
|
+
const oldestKey = this.instances.keys().next().value;
|
|
37
|
+
if (oldestKey) {
|
|
38
|
+
const oldInstance = this.instances.get(oldestKey);
|
|
39
|
+
if (oldInstance) {
|
|
40
|
+
oldInstance.clearCache();
|
|
41
|
+
}
|
|
42
|
+
this.instances.delete(oldestKey);
|
|
43
|
+
this.configCache.delete(oldestKey);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (this.instances.has(configKey)) {
|
|
47
|
+
const oldInstance = this.instances.get(configKey);
|
|
48
|
+
oldInstance.clearCache();
|
|
49
|
+
}
|
|
50
|
+
const newInstance = new Translator(config);
|
|
51
|
+
this.instances.set(configKey, newInstance);
|
|
52
|
+
this.configCache.set(configKey, { ...config });
|
|
53
|
+
}
|
|
54
|
+
return this.instances.get(configKey);
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* 특정 Config 키의 Translator 인스턴스 반환
|
|
58
|
+
*/
|
|
59
|
+
static get(config) {
|
|
60
|
+
const configKey = this.generateConfigKey(config);
|
|
61
|
+
return this.instances.get(configKey) || null;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* 모든 Translator 인스턴스 정리 (테스트용)
|
|
65
|
+
*/
|
|
66
|
+
static clear() {
|
|
67
|
+
for (const instance of this.instances.values()) {
|
|
68
|
+
instance.clearCache();
|
|
69
|
+
}
|
|
70
|
+
this.instances.clear();
|
|
71
|
+
this.configCache.clear();
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* 특정 Config 키의 인스턴스만 정리
|
|
75
|
+
*/
|
|
76
|
+
static clearConfig(config) {
|
|
77
|
+
const configKey = this.generateConfigKey(config);
|
|
78
|
+
const instance = this.instances.get(configKey);
|
|
79
|
+
if (instance) {
|
|
80
|
+
instance.clearCache();
|
|
81
|
+
this.instances.delete(configKey);
|
|
82
|
+
this.configCache.delete(configKey);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 현재 관리 중인 인스턴스 수 반환
|
|
87
|
+
*/
|
|
88
|
+
static getInstanceCount() {
|
|
89
|
+
return this.instances.size;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* 디버깅용: 모든 인스턴스 정보 반환
|
|
93
|
+
*/
|
|
94
|
+
static debug() {
|
|
95
|
+
return {
|
|
96
|
+
instanceCount: this.instances.size,
|
|
97
|
+
configKeys: Array.from(this.instances.keys()),
|
|
98
|
+
instances: this.instances
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
};
|
|
102
|
+
TranslatorFactory.instances = /* @__PURE__ */ new Map();
|
|
103
|
+
TranslatorFactory.configCache = /* @__PURE__ */ new Map();
|
|
104
|
+
TranslatorFactory.MAX_INSTANCES = 10;
|
|
105
|
+
|
|
106
|
+
// src/utils/default-translations.ts
|
|
107
|
+
var DEFAULT_TRANSLATIONS = {
|
|
108
|
+
ko: {
|
|
109
|
+
common: {
|
|
110
|
+
welcome: "\uD658\uC601\uD569\uB2C8\uB2E4",
|
|
111
|
+
greeting: "\uC548\uB155\uD558\uC138\uC694",
|
|
112
|
+
goodbye: "\uC548\uB155\uD788 \uAC00\uC138\uC694",
|
|
113
|
+
loading: "\uB85C\uB529 \uC911...",
|
|
114
|
+
error: "\uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4",
|
|
115
|
+
success: "\uC131\uACF5\uD588\uC2B5\uB2C8\uB2E4",
|
|
116
|
+
cancel: "\uCDE8\uC18C",
|
|
117
|
+
confirm: "\uD655\uC778",
|
|
118
|
+
save: "\uC800\uC7A5",
|
|
119
|
+
delete: "\uC0AD\uC81C",
|
|
120
|
+
edit: "\uD3B8\uC9D1",
|
|
121
|
+
add: "\uCD94\uAC00",
|
|
122
|
+
search: "\uAC80\uC0C9",
|
|
123
|
+
filter: "\uD544\uD130",
|
|
124
|
+
sort: "\uC815\uB82C",
|
|
125
|
+
refresh: "\uC0C8\uB85C\uACE0\uCE68",
|
|
126
|
+
back: "\uB4A4\uB85C",
|
|
127
|
+
next: "\uB2E4\uC74C",
|
|
128
|
+
previous: "\uC774\uC804",
|
|
129
|
+
home: "\uD648",
|
|
130
|
+
about: "\uC18C\uAC1C",
|
|
131
|
+
contact: "\uC5F0\uB77D\uCC98",
|
|
132
|
+
settings: "\uC124\uC815",
|
|
133
|
+
profile: "\uD504\uB85C\uD544",
|
|
134
|
+
logout: "\uB85C\uADF8\uC544\uC6C3",
|
|
135
|
+
login: "\uB85C\uADF8\uC778",
|
|
136
|
+
register: "\uD68C\uC6D0\uAC00\uC785"
|
|
137
|
+
},
|
|
138
|
+
auth: {
|
|
139
|
+
login: "\uB85C\uADF8\uC778",
|
|
140
|
+
logout: "\uB85C\uADF8\uC544\uC6C3",
|
|
141
|
+
register: "\uD68C\uC6D0\uAC00\uC785",
|
|
142
|
+
email: "\uC774\uBA54\uC77C",
|
|
143
|
+
password: "\uBE44\uBC00\uBC88\uD638",
|
|
144
|
+
forgot_password: "\uBE44\uBC00\uBC88\uD638 \uCC3E\uAE30",
|
|
145
|
+
remember_me: "\uB85C\uADF8\uC778 \uC0C1\uD0DC \uC720\uC9C0"
|
|
146
|
+
},
|
|
147
|
+
errors: {
|
|
148
|
+
not_found: "\uD398\uC774\uC9C0\uB97C \uCC3E\uC744 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4",
|
|
149
|
+
server_error: "\uC11C\uBC84 \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4",
|
|
150
|
+
network_error: "\uB124\uD2B8\uC6CC\uD06C \uC624\uB958\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4",
|
|
151
|
+
unauthorized: "\uC778\uC99D\uC774 \uD544\uC694\uD569\uB2C8\uB2E4",
|
|
152
|
+
forbidden: "\uC811\uADFC\uC774 \uAC70\uBD80\uB418\uC5C8\uC2B5\uB2C8\uB2E4"
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
en: {
|
|
156
|
+
common: {
|
|
157
|
+
welcome: "Welcome",
|
|
158
|
+
greeting: "Hello",
|
|
159
|
+
goodbye: "Goodbye",
|
|
160
|
+
loading: "Loading...",
|
|
161
|
+
error: "An error occurred",
|
|
162
|
+
success: "Success",
|
|
163
|
+
cancel: "Cancel",
|
|
164
|
+
confirm: "Confirm",
|
|
165
|
+
save: "Save",
|
|
166
|
+
delete: "Delete",
|
|
167
|
+
edit: "Edit",
|
|
168
|
+
add: "Add",
|
|
169
|
+
search: "Search",
|
|
170
|
+
filter: "Filter",
|
|
171
|
+
sort: "Sort",
|
|
172
|
+
refresh: "Refresh",
|
|
173
|
+
back: "Back",
|
|
174
|
+
next: "Next",
|
|
175
|
+
previous: "Previous",
|
|
176
|
+
home: "Home",
|
|
177
|
+
about: "About",
|
|
178
|
+
contact: "Contact",
|
|
179
|
+
settings: "Settings",
|
|
180
|
+
profile: "Profile",
|
|
181
|
+
logout: "Logout",
|
|
182
|
+
login: "Login",
|
|
183
|
+
register: "Register"
|
|
184
|
+
},
|
|
185
|
+
auth: {
|
|
186
|
+
login: "Login",
|
|
187
|
+
logout: "Logout",
|
|
188
|
+
register: "Register",
|
|
189
|
+
email: "Email",
|
|
190
|
+
password: "Password",
|
|
191
|
+
forgot_password: "Forgot Password",
|
|
192
|
+
remember_me: "Remember Me"
|
|
193
|
+
},
|
|
194
|
+
errors: {
|
|
195
|
+
not_found: "Page not found",
|
|
196
|
+
server_error: "Server error occurred",
|
|
197
|
+
network_error: "Network error occurred",
|
|
198
|
+
unauthorized: "Authentication required",
|
|
199
|
+
forbidden: "Access denied"
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
function getDefaultTranslations(language, namespace) {
|
|
204
|
+
return DEFAULT_TRANSLATIONS[language]?.[namespace] || {};
|
|
205
|
+
}
|
|
206
|
+
var I18nContext = createContext(null);
|
|
207
|
+
function I18nProvider({
|
|
208
|
+
config,
|
|
209
|
+
children
|
|
210
|
+
}) {
|
|
211
|
+
const [currentLanguage, setCurrentLanguageState] = useState(config.defaultLanguage);
|
|
212
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
213
|
+
const [isInitialized, setIsInitialized] = useState(false);
|
|
214
|
+
const [error, setError] = useState(null);
|
|
215
|
+
const [translationVersion, setTranslationVersion] = useState(0);
|
|
216
|
+
useEffect(() => {
|
|
217
|
+
if (!isInitialized && config.defaultLanguage !== currentLanguage) {
|
|
218
|
+
setCurrentLanguageState(config.defaultLanguage);
|
|
219
|
+
}
|
|
220
|
+
}, [config.defaultLanguage, currentLanguage, isInitialized]);
|
|
221
|
+
const translator = useMemo(() => {
|
|
222
|
+
if (!validateI18nConfig(config)) {
|
|
223
|
+
throw new Error("Invalid I18nConfig provided to I18nProvider");
|
|
224
|
+
}
|
|
225
|
+
return TranslatorFactory.create(config);
|
|
226
|
+
}, [config]);
|
|
227
|
+
useEffect(() => {
|
|
228
|
+
if (isInitialized) {
|
|
229
|
+
const translatorLang = translator.getCurrentLanguage();
|
|
230
|
+
if (translatorLang !== currentLanguage) {
|
|
231
|
+
if (config.debug) {
|
|
232
|
+
console.log(`\u{1F504} [USEI18N] Syncing translator language: ${translatorLang} -> ${currentLanguage} (already initialized)`);
|
|
233
|
+
}
|
|
234
|
+
translator.setLanguage(currentLanguage);
|
|
235
|
+
}
|
|
236
|
+
return;
|
|
237
|
+
}
|
|
238
|
+
if (config.debug) {
|
|
239
|
+
console.log("\u{1F504} [USEI18N] useEffect triggered:", {
|
|
240
|
+
hasTranslator: !!translator,
|
|
241
|
+
currentLanguage,
|
|
242
|
+
debug: config.debug,
|
|
243
|
+
isInitialized
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
const initializeTranslator = async () => {
|
|
247
|
+
try {
|
|
248
|
+
setIsLoading(true);
|
|
249
|
+
setError(null);
|
|
250
|
+
if (config.debug) {
|
|
251
|
+
console.log("\u{1F680} [USEI18N] Starting translator initialization...");
|
|
252
|
+
}
|
|
253
|
+
translator.setLanguage(currentLanguage);
|
|
254
|
+
await translator.initialize();
|
|
255
|
+
setIsInitialized(true);
|
|
256
|
+
if (config.debug) {
|
|
257
|
+
console.log("\u2705 [USEI18N] Translator initialization completed successfully");
|
|
258
|
+
}
|
|
259
|
+
} catch (err) {
|
|
260
|
+
const initError = err;
|
|
261
|
+
setError(initError);
|
|
262
|
+
if (config.debug) {
|
|
263
|
+
console.error("\u274C [USEI18N] Failed to initialize translator:", initError);
|
|
264
|
+
}
|
|
265
|
+
setIsInitialized(true);
|
|
266
|
+
} finally {
|
|
267
|
+
setIsLoading(false);
|
|
268
|
+
}
|
|
269
|
+
};
|
|
270
|
+
initializeTranslator();
|
|
271
|
+
}, [translator, currentLanguage, config.debug, isInitialized]);
|
|
272
|
+
useEffect(() => {
|
|
273
|
+
if (!translator || !isInitialized) {
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
const unsubscribe = translator.onTranslationLoaded(() => {
|
|
277
|
+
setTranslationVersion((prev) => prev + 1);
|
|
278
|
+
if (config.debug) {
|
|
279
|
+
console.log("\u{1F504} [USEI18N] Translation loaded, triggering re-render");
|
|
280
|
+
}
|
|
281
|
+
});
|
|
282
|
+
return unsubscribe;
|
|
283
|
+
}, [translator, isInitialized, config.debug]);
|
|
284
|
+
useEffect(() => {
|
|
285
|
+
if (!translator || !isInitialized) {
|
|
286
|
+
return;
|
|
287
|
+
}
|
|
288
|
+
const unsubscribe = translator.onLanguageChanged((newLanguage) => {
|
|
289
|
+
if (newLanguage !== currentLanguage) {
|
|
290
|
+
if (config.debug) {
|
|
291
|
+
console.log(`\u{1F504} [USEI18N] Language changed event: ${currentLanguage} -> ${newLanguage}`);
|
|
292
|
+
}
|
|
293
|
+
setCurrentLanguageState(newLanguage);
|
|
294
|
+
setTranslationVersion((prev) => prev + 1);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
return unsubscribe;
|
|
298
|
+
}, [translator, isInitialized, currentLanguage, config.debug]);
|
|
299
|
+
useEffect(() => {
|
|
300
|
+
if (!config.autoLanguageSync || typeof window === "undefined") {
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
const handleLanguageChange = (event) => {
|
|
304
|
+
const newLanguage = event.detail;
|
|
305
|
+
if (typeof newLanguage === "string" && newLanguage !== currentLanguage) {
|
|
306
|
+
if (config.debug) {
|
|
307
|
+
console.log("\u{1F310} Auto language sync:", newLanguage);
|
|
308
|
+
}
|
|
309
|
+
setLanguage(newLanguage);
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
window.addEventListener("huaI18nLanguageChange", handleLanguageChange);
|
|
313
|
+
window.addEventListener("i18nLanguageChanged", handleLanguageChange);
|
|
314
|
+
return () => {
|
|
315
|
+
window.removeEventListener("huaI18nLanguageChange", handleLanguageChange);
|
|
316
|
+
window.removeEventListener("i18nLanguageChanged", handleLanguageChange);
|
|
317
|
+
};
|
|
318
|
+
}, [config.autoLanguageSync, currentLanguage]);
|
|
319
|
+
const setLanguage = useCallback(async (language) => {
|
|
320
|
+
if (!translator) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
const currentLang = translator.getCurrentLanguage();
|
|
324
|
+
if (currentLang === language) {
|
|
325
|
+
if (config.debug) {
|
|
326
|
+
console.log(`\u23ED\uFE0F [USEI18N] Language unchanged, skipping: ${language}`);
|
|
327
|
+
}
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
if (config.debug) {
|
|
331
|
+
if (config.debug) {
|
|
332
|
+
console.log(`\u{1F504} [USEI18N] setLanguage called: ${currentLang} -> ${language}`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
setIsLoading(true);
|
|
336
|
+
try {
|
|
337
|
+
translator.setLanguage(language);
|
|
338
|
+
setCurrentLanguageState(language);
|
|
339
|
+
await new Promise((resolve) => setTimeout(resolve, 0));
|
|
340
|
+
if (config.debug) {
|
|
341
|
+
console.log(`\u2705 [USEI18N] Language changed to ${language}`);
|
|
342
|
+
}
|
|
343
|
+
} catch (error2) {
|
|
344
|
+
if (config.debug) {
|
|
345
|
+
console.error(`\u274C [USEI18N] Failed to change language to ${language}:`, error2);
|
|
346
|
+
}
|
|
347
|
+
} finally {
|
|
348
|
+
setIsLoading(false);
|
|
349
|
+
}
|
|
350
|
+
}, [translator, config.debug]);
|
|
351
|
+
const parseKey = useCallback((key) => {
|
|
352
|
+
const parts = key.split(":");
|
|
353
|
+
if (parts.length >= 2) {
|
|
354
|
+
return { namespace: parts[0], key: parts.slice(1).join(":") };
|
|
355
|
+
}
|
|
356
|
+
return { namespace: "common", key };
|
|
357
|
+
}, []);
|
|
358
|
+
const resolveNestedKey = useCallback((obj, key) => {
|
|
359
|
+
if (key in obj && typeof obj[key] === "string") {
|
|
360
|
+
return obj[key];
|
|
361
|
+
}
|
|
362
|
+
const parts = key.split(".");
|
|
363
|
+
let current = obj;
|
|
364
|
+
for (const part of parts) {
|
|
365
|
+
if (current && typeof current === "object" && current !== null && part in current) {
|
|
366
|
+
current = current[part];
|
|
367
|
+
} else {
|
|
368
|
+
return null;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
return typeof current === "string" ? current : null;
|
|
372
|
+
}, []);
|
|
373
|
+
const findInSSRTranslations = useCallback((key, targetLang) => {
|
|
374
|
+
if (!config.initialTranslations) {
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
const { namespace, key: actualKey } = parseKey(key);
|
|
378
|
+
const ssrTranslations = config.initialTranslations[targetLang]?.[namespace];
|
|
379
|
+
if (ssrTranslations) {
|
|
380
|
+
const value2 = resolveNestedKey(ssrTranslations, actualKey);
|
|
381
|
+
if (value2 !== null) {
|
|
382
|
+
return value2;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
const fallbackLang = config.fallbackLanguage || "en";
|
|
386
|
+
if (targetLang !== fallbackLang) {
|
|
387
|
+
const fallbackTranslations = config.initialTranslations[fallbackLang]?.[namespace];
|
|
388
|
+
if (fallbackTranslations) {
|
|
389
|
+
const value2 = resolveNestedKey(fallbackTranslations, actualKey);
|
|
390
|
+
if (value2 !== null) {
|
|
391
|
+
return value2;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
return null;
|
|
396
|
+
}, [config.initialTranslations, config.fallbackLanguage, parseKey, resolveNestedKey]);
|
|
397
|
+
const findInDefaultTranslations = useCallback((key, targetLang) => {
|
|
398
|
+
const { namespace, key: actualKey } = parseKey(key);
|
|
399
|
+
const defaultTranslations = getDefaultTranslations(targetLang, namespace);
|
|
400
|
+
const fallbackTranslations = getDefaultTranslations(config.fallbackLanguage || "en", namespace);
|
|
401
|
+
return resolveNestedKey(defaultTranslations, actualKey) || resolveNestedKey(fallbackTranslations, actualKey) || null;
|
|
402
|
+
}, [config.fallbackLanguage, parseKey, resolveNestedKey]);
|
|
403
|
+
const t = useCallback((key, paramsOrLang, language) => {
|
|
404
|
+
if (!translator) {
|
|
405
|
+
return key;
|
|
406
|
+
}
|
|
407
|
+
let params;
|
|
408
|
+
let lang;
|
|
409
|
+
if (typeof paramsOrLang === "string") {
|
|
410
|
+
lang = paramsOrLang;
|
|
411
|
+
} else if (typeof paramsOrLang === "object" && paramsOrLang !== null) {
|
|
412
|
+
params = paramsOrLang;
|
|
413
|
+
lang = language;
|
|
414
|
+
}
|
|
415
|
+
const targetLang = lang || currentLanguage;
|
|
416
|
+
try {
|
|
417
|
+
const result = translator.translate(key, params || lang, params ? lang : void 0);
|
|
418
|
+
if (result && result !== key && result !== "") {
|
|
419
|
+
return result;
|
|
420
|
+
}
|
|
421
|
+
} catch (error2) {
|
|
422
|
+
}
|
|
423
|
+
const interpolate = (text) => {
|
|
424
|
+
if (!params) return text;
|
|
425
|
+
return text.replace(/\{\{(\w+)\}\}/g, (match, k) => {
|
|
426
|
+
const value2 = params[k];
|
|
427
|
+
return value2 !== void 0 ? String(value2) : match;
|
|
428
|
+
});
|
|
429
|
+
};
|
|
430
|
+
const ssrResult = findInSSRTranslations(key, targetLang);
|
|
431
|
+
if (ssrResult) {
|
|
432
|
+
return interpolate(ssrResult);
|
|
433
|
+
}
|
|
434
|
+
const defaultResult = findInDefaultTranslations(key, targetLang);
|
|
435
|
+
if (defaultResult) {
|
|
436
|
+
return interpolate(defaultResult);
|
|
437
|
+
}
|
|
438
|
+
if (config.debug) {
|
|
439
|
+
return interpolate(key);
|
|
440
|
+
}
|
|
441
|
+
return "";
|
|
442
|
+
}, [translator, config.debug, currentLanguage, config.fallbackLanguage, translationVersion, findInSSRTranslations, findInDefaultTranslations]);
|
|
443
|
+
const tAsync = useCallback(async (key, params) => {
|
|
444
|
+
if (!translator) {
|
|
445
|
+
if (config.debug) {
|
|
446
|
+
console.warn("Translator not initialized");
|
|
447
|
+
}
|
|
448
|
+
return key;
|
|
449
|
+
}
|
|
450
|
+
setIsLoading(true);
|
|
451
|
+
try {
|
|
452
|
+
const result = await translator.translateAsync(key, params);
|
|
453
|
+
return result;
|
|
454
|
+
} catch (error2) {
|
|
455
|
+
if (config.debug) {
|
|
456
|
+
console.error("Translation error:", error2);
|
|
457
|
+
}
|
|
458
|
+
return key;
|
|
459
|
+
} finally {
|
|
460
|
+
setIsLoading(false);
|
|
461
|
+
}
|
|
462
|
+
}, [translator, config.debug]);
|
|
463
|
+
const tSync = useCallback((key, namespace, params) => {
|
|
464
|
+
if (!translator) {
|
|
465
|
+
if (config.debug) {
|
|
466
|
+
console.warn("Translator not initialized");
|
|
467
|
+
}
|
|
468
|
+
return key;
|
|
469
|
+
}
|
|
470
|
+
return translator.translateSync(key, params);
|
|
471
|
+
}, [translator, config.debug]);
|
|
472
|
+
const getRawValue = useCallback((key, language) => {
|
|
473
|
+
if (!translator || !isInitialized) {
|
|
474
|
+
return void 0;
|
|
475
|
+
}
|
|
476
|
+
return translator.getRawValue(key, language);
|
|
477
|
+
}, [translator, isInitialized]);
|
|
478
|
+
const tArray = useCallback((key, language) => {
|
|
479
|
+
if (!translator || !isInitialized) {
|
|
480
|
+
return [];
|
|
481
|
+
}
|
|
482
|
+
return translator.tArray(key, language);
|
|
483
|
+
}, [translator, isInitialized, translationVersion, currentLanguage]);
|
|
484
|
+
const tPlural = useCallback((key, count, params, language) => {
|
|
485
|
+
if (!translator || !isInitialized) {
|
|
486
|
+
return key;
|
|
487
|
+
}
|
|
488
|
+
return translator.tPlural(key, count, params, language);
|
|
489
|
+
}, [translator, isInitialized, translationVersion, currentLanguage]);
|
|
490
|
+
const debug = useMemo(() => ({
|
|
491
|
+
getCurrentLanguage: () => {
|
|
492
|
+
try {
|
|
493
|
+
return translator?.getCurrentLanguage() || currentLanguage;
|
|
494
|
+
} catch {
|
|
495
|
+
return currentLanguage;
|
|
496
|
+
}
|
|
497
|
+
},
|
|
498
|
+
getSupportedLanguages: () => {
|
|
499
|
+
try {
|
|
500
|
+
return translator?.getSupportedLanguages() || config.supportedLanguages?.map((l) => l.code) || [];
|
|
501
|
+
} catch {
|
|
502
|
+
return config.supportedLanguages?.map((l) => l.code) || [];
|
|
503
|
+
}
|
|
504
|
+
},
|
|
505
|
+
getLoadedNamespaces: () => {
|
|
506
|
+
try {
|
|
507
|
+
const debugInfo = translator?.debug();
|
|
508
|
+
if (debugInfo && debugInfo.loadedNamespaces) {
|
|
509
|
+
return Array.from(debugInfo.loadedNamespaces);
|
|
510
|
+
}
|
|
511
|
+
if (debugInfo && debugInfo.allTranslations) {
|
|
512
|
+
const namespaces = /* @__PURE__ */ new Set();
|
|
513
|
+
Object.values(debugInfo.allTranslations).forEach((langData) => {
|
|
514
|
+
if (langData && typeof langData === "object") {
|
|
515
|
+
Object.keys(langData).forEach((namespace) => {
|
|
516
|
+
namespaces.add(namespace);
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
});
|
|
520
|
+
return Array.from(namespaces);
|
|
521
|
+
}
|
|
522
|
+
return [];
|
|
523
|
+
} catch (error2) {
|
|
524
|
+
return [];
|
|
525
|
+
}
|
|
526
|
+
},
|
|
527
|
+
getAllTranslations: () => {
|
|
528
|
+
try {
|
|
529
|
+
return translator?.debug()?.allTranslations || {};
|
|
530
|
+
} catch (error2) {
|
|
531
|
+
return {};
|
|
532
|
+
}
|
|
533
|
+
},
|
|
534
|
+
isReady: () => {
|
|
535
|
+
try {
|
|
536
|
+
return translator?.isReady() || isInitialized;
|
|
537
|
+
} catch {
|
|
538
|
+
return isInitialized;
|
|
539
|
+
}
|
|
540
|
+
},
|
|
541
|
+
getInitializationError: () => {
|
|
542
|
+
try {
|
|
543
|
+
return translator?.getInitializationError() || error;
|
|
544
|
+
} catch {
|
|
545
|
+
return error;
|
|
546
|
+
}
|
|
547
|
+
},
|
|
548
|
+
clearCache: () => {
|
|
549
|
+
try {
|
|
550
|
+
translator?.clearCache();
|
|
551
|
+
} catch {
|
|
552
|
+
}
|
|
553
|
+
},
|
|
554
|
+
getCacheStats: () => {
|
|
555
|
+
try {
|
|
556
|
+
const debugInfo = translator?.debug();
|
|
557
|
+
if (debugInfo && debugInfo.cacheStats) {
|
|
558
|
+
return {
|
|
559
|
+
size: debugInfo.cacheSize || 0,
|
|
560
|
+
hits: debugInfo.cacheStats.hits || 0,
|
|
561
|
+
misses: debugInfo.cacheStats.misses || 0
|
|
562
|
+
};
|
|
563
|
+
}
|
|
564
|
+
return { size: 0, hits: 0, misses: 0 };
|
|
565
|
+
} catch (error2) {
|
|
566
|
+
return { size: 0, hits: 0, misses: 0 };
|
|
567
|
+
}
|
|
568
|
+
},
|
|
569
|
+
reloadTranslations: async () => {
|
|
570
|
+
if (translator) {
|
|
571
|
+
setIsLoading(true);
|
|
572
|
+
setError(null);
|
|
573
|
+
try {
|
|
574
|
+
await translator.initialize();
|
|
575
|
+
} catch (err) {
|
|
576
|
+
setError(err);
|
|
577
|
+
} finally {
|
|
578
|
+
setIsLoading(false);
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
}), [translator, currentLanguage, error, isInitialized, config.supportedLanguages]);
|
|
583
|
+
const value = useMemo(() => ({
|
|
584
|
+
currentLanguage,
|
|
585
|
+
setLanguage,
|
|
586
|
+
t,
|
|
587
|
+
tPlural,
|
|
588
|
+
tArray,
|
|
589
|
+
tAsync,
|
|
590
|
+
tSync,
|
|
591
|
+
getRawValue,
|
|
592
|
+
isLoading,
|
|
593
|
+
error,
|
|
594
|
+
supportedLanguages: config.supportedLanguages,
|
|
595
|
+
debug,
|
|
596
|
+
isInitialized,
|
|
597
|
+
translationVersion
|
|
598
|
+
}), [currentLanguage, setLanguage, t, tPlural, tArray, tAsync, tSync, getRawValue, isLoading, error, config.supportedLanguages, debug, isInitialized, translationVersion]);
|
|
599
|
+
return /* @__PURE__ */ jsx(I18nContext.Provider, { value, children });
|
|
600
|
+
}
|
|
601
|
+
function useI18n() {
|
|
602
|
+
const context = useContext(I18nContext);
|
|
603
|
+
if (!context) {
|
|
604
|
+
return {
|
|
605
|
+
currentLanguage: "ko",
|
|
606
|
+
setLanguage: () => {
|
|
607
|
+
},
|
|
608
|
+
t: (key) => key,
|
|
609
|
+
tPlural: (key) => key,
|
|
610
|
+
tAsync: async (key) => key,
|
|
611
|
+
tSync: (key) => key,
|
|
612
|
+
getRawValue: () => void 0,
|
|
613
|
+
tArray: () => [],
|
|
614
|
+
isLoading: false,
|
|
615
|
+
error: null,
|
|
616
|
+
supportedLanguages: [
|
|
617
|
+
{ code: "ko", name: "Korean", nativeName: "\uD55C\uAD6D\uC5B4" },
|
|
618
|
+
{ code: "en", name: "English", nativeName: "English" }
|
|
619
|
+
],
|
|
620
|
+
isInitialized: false,
|
|
621
|
+
debug: {
|
|
622
|
+
getCurrentLanguage: () => "ko",
|
|
623
|
+
getSupportedLanguages: () => ["ko", "en"],
|
|
624
|
+
getLoadedNamespaces: () => [],
|
|
625
|
+
getAllTranslations: () => ({}),
|
|
626
|
+
isReady: () => false,
|
|
627
|
+
getInitializationError: () => null,
|
|
628
|
+
clearCache: () => {
|
|
629
|
+
},
|
|
630
|
+
getCacheStats: () => ({ size: 0, hits: 0, misses: 0 }),
|
|
631
|
+
reloadTranslations: async () => {
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
};
|
|
635
|
+
}
|
|
636
|
+
return context;
|
|
637
|
+
}
|
|
638
|
+
function useTranslation() {
|
|
639
|
+
const { t, tPlural, tArray, currentLanguage, setLanguage, getRawValue, isLoading, error, supportedLanguages, debug, isInitialized } = useI18n();
|
|
640
|
+
return {
|
|
641
|
+
t,
|
|
642
|
+
tPlural,
|
|
643
|
+
tArray,
|
|
644
|
+
currentLanguage,
|
|
645
|
+
setLanguage,
|
|
646
|
+
getRawValue,
|
|
647
|
+
isLoading,
|
|
648
|
+
error,
|
|
649
|
+
supportedLanguages,
|
|
650
|
+
debug,
|
|
651
|
+
isInitialized
|
|
652
|
+
};
|
|
653
|
+
}
|
|
654
|
+
function useLanguageChange() {
|
|
655
|
+
const { currentLanguage, setLanguage, supportedLanguages } = useI18n();
|
|
656
|
+
const changeLanguage = useCallback((language) => {
|
|
657
|
+
const supported = supportedLanguages.find((lang) => lang.code === language);
|
|
658
|
+
if (supported) {
|
|
659
|
+
setLanguage(language);
|
|
660
|
+
} else {
|
|
661
|
+
if (process.env.NODE_ENV !== "production") console.warn(`Language ${language} is not supported`);
|
|
662
|
+
}
|
|
663
|
+
}, [setLanguage, supportedLanguages]);
|
|
664
|
+
return {
|
|
665
|
+
currentLanguage,
|
|
666
|
+
changeLanguage,
|
|
667
|
+
supportedLanguages
|
|
668
|
+
};
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
// src/index.ts
|
|
672
|
+
var defaultLanguages = [
|
|
673
|
+
{ code: "ko", name: "Korean", nativeName: "\uD55C\uAD6D\uC5B4" },
|
|
674
|
+
{ code: "en", name: "English", nativeName: "English" },
|
|
675
|
+
{ code: "en-IN", name: "English (India)", nativeName: "English (India)" },
|
|
676
|
+
{ code: "ja", name: "Japanese", nativeName: "\u65E5\u672C\u8A9E" },
|
|
677
|
+
{ code: "zh", name: "Chinese (Simplified)", nativeName: "\u7B80\u4F53\u4E2D\u6587" },
|
|
678
|
+
{ code: "zh-TW", name: "Chinese (Traditional)", nativeName: "\u7E41\u9AD4\u4E2D\u6587" },
|
|
679
|
+
{ code: "es", name: "Spanish", nativeName: "Espa\xF1ol" },
|
|
680
|
+
{ code: "ru", name: "Russian", nativeName: "\u0420\u0443\u0441\u0441\u043A\u0438\u0439" },
|
|
681
|
+
{ code: "de", name: "German", nativeName: "Deutsch" },
|
|
682
|
+
{ code: "fr", name: "French", nativeName: "Fran\xE7ais" }
|
|
683
|
+
];
|
|
684
|
+
function createCoreI18n(options) {
|
|
685
|
+
const {
|
|
686
|
+
defaultLanguage = "ko",
|
|
687
|
+
fallbackLanguage = "en",
|
|
688
|
+
namespaces = ["common"],
|
|
689
|
+
debug = false,
|
|
690
|
+
loadTranslations,
|
|
691
|
+
translationLoader = "api",
|
|
692
|
+
translationApiPath = "/api/translations",
|
|
693
|
+
initialTranslations,
|
|
694
|
+
supportedLanguages: providedSupportedLanguages,
|
|
695
|
+
autoLanguageSync = false,
|
|
696
|
+
// 기본값 false (Zustand 어댑터 등 외부에서 직접 처리)
|
|
697
|
+
baseUrl,
|
|
698
|
+
localFallbackBaseUrl
|
|
699
|
+
} = options || {};
|
|
700
|
+
let supportedLanguagesConfig;
|
|
701
|
+
if (providedSupportedLanguages) {
|
|
702
|
+
if (Array.isArray(providedSupportedLanguages) && providedSupportedLanguages.length > 0) {
|
|
703
|
+
if (typeof providedSupportedLanguages[0] === "string") {
|
|
704
|
+
const languageMap = {
|
|
705
|
+
ko: { name: "Korean", nativeName: "\uD55C\uAD6D\uC5B4" },
|
|
706
|
+
en: { name: "English", nativeName: "English" },
|
|
707
|
+
ja: { name: "Japanese", nativeName: "\u65E5\u672C\u8A9E" },
|
|
708
|
+
zh: { name: "Chinese", nativeName: "\u4E2D\u6587" },
|
|
709
|
+
es: { name: "Spanish", nativeName: "Espa\xF1ol" },
|
|
710
|
+
fr: { name: "French", nativeName: "Fran\xE7ais" },
|
|
711
|
+
de: { name: "German", nativeName: "Deutsch" },
|
|
712
|
+
pt: { name: "Portuguese", nativeName: "Portugu\xEAs" },
|
|
713
|
+
it: { name: "Italian", nativeName: "Italiano" },
|
|
714
|
+
ru: { name: "Russian", nativeName: "\u0420\u0443\u0441\u0441\u043A\u0438\u0439" }
|
|
715
|
+
};
|
|
716
|
+
supportedLanguagesConfig = providedSupportedLanguages.map((code) => ({
|
|
717
|
+
code,
|
|
718
|
+
name: languageMap[code]?.name || code,
|
|
719
|
+
nativeName: languageMap[code]?.nativeName || code
|
|
720
|
+
}));
|
|
721
|
+
} else {
|
|
722
|
+
supportedLanguagesConfig = providedSupportedLanguages;
|
|
723
|
+
}
|
|
724
|
+
} else {
|
|
725
|
+
supportedLanguagesConfig = defaultLanguages;
|
|
726
|
+
}
|
|
727
|
+
} else {
|
|
728
|
+
supportedLanguagesConfig = defaultLanguages;
|
|
729
|
+
}
|
|
730
|
+
const apiRouteLoader = async (language, namespace) => {
|
|
731
|
+
try {
|
|
732
|
+
if (typeof window !== "undefined") {
|
|
733
|
+
const apiUrl = `${translationApiPath}/${language}/${namespace}`;
|
|
734
|
+
const response = await fetch(apiUrl);
|
|
735
|
+
if (response.ok) {
|
|
736
|
+
const data = await response.json();
|
|
737
|
+
if (debug) {
|
|
738
|
+
console.log(`\u2705 Loaded translation from API: ${language}/${namespace}`);
|
|
739
|
+
}
|
|
740
|
+
return data;
|
|
741
|
+
} else if (response.status === 404) {
|
|
742
|
+
if (debug) {
|
|
743
|
+
console.warn(`\u26A0\uFE0F Translation not found in API: ${language}/${namespace}`);
|
|
744
|
+
}
|
|
745
|
+
}
|
|
746
|
+
}
|
|
747
|
+
return getDefaultTranslations(language, namespace);
|
|
748
|
+
} catch (error) {
|
|
749
|
+
if (debug) {
|
|
750
|
+
console.warn(`Failed to load translation from API: ${language}/${namespace}`, error);
|
|
751
|
+
}
|
|
752
|
+
return getDefaultTranslations(language, namespace);
|
|
753
|
+
}
|
|
754
|
+
};
|
|
755
|
+
const staticFileLoader = async (language, namespace) => {
|
|
756
|
+
try {
|
|
757
|
+
let data = null;
|
|
758
|
+
if (typeof window !== "undefined") {
|
|
759
|
+
const possiblePaths = [
|
|
760
|
+
`/translations/${language}/${namespace}.json`,
|
|
761
|
+
`../translations/${language}/${namespace}.json`,
|
|
762
|
+
`./translations/${language}/${namespace}.json`,
|
|
763
|
+
`translations/${language}/${namespace}.json`,
|
|
764
|
+
`../../translations/${language}/${namespace}.json`
|
|
765
|
+
];
|
|
766
|
+
for (const path of possiblePaths) {
|
|
767
|
+
try {
|
|
768
|
+
const response = await fetch(path);
|
|
769
|
+
if (response.ok) {
|
|
770
|
+
data = await response.json();
|
|
771
|
+
if (debug) {
|
|
772
|
+
console.log(`\u2705 Loaded translation from static path: ${path}`);
|
|
773
|
+
}
|
|
774
|
+
break;
|
|
775
|
+
}
|
|
776
|
+
} catch (pathError) {
|
|
777
|
+
continue;
|
|
778
|
+
}
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
if (data) {
|
|
782
|
+
return data;
|
|
783
|
+
}
|
|
784
|
+
return getDefaultTranslations(language, namespace);
|
|
785
|
+
} catch (error) {
|
|
786
|
+
if (debug) {
|
|
787
|
+
console.warn(`Failed to load translation file: ${language}/${namespace}.json`);
|
|
788
|
+
}
|
|
789
|
+
return getDefaultTranslations(language, namespace);
|
|
790
|
+
}
|
|
791
|
+
};
|
|
792
|
+
const defaultFileLoader = translationLoader === "api" ? apiRouteLoader : translationLoader === "static" ? staticFileLoader : loadTranslations || apiRouteLoader;
|
|
793
|
+
const config = {
|
|
794
|
+
defaultLanguage,
|
|
795
|
+
fallbackLanguage,
|
|
796
|
+
supportedLanguages: supportedLanguagesConfig,
|
|
797
|
+
namespaces,
|
|
798
|
+
loadTranslations: translationLoader === "custom" && loadTranslations ? loadTranslations : defaultFileLoader,
|
|
799
|
+
initialTranslations,
|
|
800
|
+
// SSR 번역 데이터 전달
|
|
801
|
+
debug,
|
|
802
|
+
missingKeyHandler: (key, language, namespace) => {
|
|
803
|
+
if (debug) {
|
|
804
|
+
console.warn(`Missing translation key: ${key}`);
|
|
805
|
+
if (typeof window !== "undefined" && window.__I18N_DEBUG_MISSING_KEYS__) {
|
|
806
|
+
const missingKeys = window.__I18N_DEBUG_MISSING_KEYS__;
|
|
807
|
+
const keyPath = `${language || "unknown"}:${namespace || "unknown"}`;
|
|
808
|
+
missingKeys[keyPath] = missingKeys[keyPath] || [];
|
|
809
|
+
if (!missingKeys[keyPath].includes(key)) {
|
|
810
|
+
missingKeys[keyPath].push(key);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
return `[MISSING: ${key}]`;
|
|
814
|
+
}
|
|
815
|
+
return key.split(".").pop() || key;
|
|
816
|
+
},
|
|
817
|
+
errorHandler: (error, language, namespace) => {
|
|
818
|
+
if (debug) {
|
|
819
|
+
console.error(`Translation error for ${language}:${namespace}:`, error);
|
|
820
|
+
}
|
|
821
|
+
},
|
|
822
|
+
// autoLanguageSync는 기본적으로 false (Zustand 어댑터 등 외부에서 직접 처리하는 경우)
|
|
823
|
+
// 필요시 options에서 명시적으로 true로 설정 가능
|
|
824
|
+
autoLanguageSync: options?.autoLanguageSync ?? false
|
|
825
|
+
};
|
|
826
|
+
return function CoreI18nProvider({ children }) {
|
|
827
|
+
return React.createElement(I18nProvider, { config, children });
|
|
828
|
+
};
|
|
829
|
+
}
|
|
830
|
+
function CoreProvider({ children }) {
|
|
831
|
+
return createCoreI18n()({ children });
|
|
832
|
+
}
|
|
833
|
+
function createLanguageProvider(language) {
|
|
834
|
+
return createCoreI18n({ defaultLanguage: language });
|
|
835
|
+
}
|
|
836
|
+
function createNamespaceProvider(namespaces) {
|
|
837
|
+
return createCoreI18n({ namespaces });
|
|
838
|
+
}
|
|
839
|
+
function createCustomLoaderProvider(loadTranslations) {
|
|
840
|
+
return createCoreI18n({ loadTranslations });
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
export { CoreProvider, I18nProvider, createCoreI18n, createCustomLoaderProvider, createLanguageProvider, createNamespaceProvider, useI18n, useLanguageChange, useTranslation };
|
|
844
|
+
//# sourceMappingURL=index.mjs.map
|
|
845
|
+
//# sourceMappingURL=index.mjs.map
|