@hua-labs/i18n-core 2.2.0 → 2.2.1
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/LICENSE +21 -0
- package/README.md +4 -4
- package/dist/{chunk-7ZYOSEMW.mjs → chunk-4IYWT7MS.mjs} +143 -45
- package/dist/chunk-4IYWT7MS.mjs.map +1 -0
- package/dist/index.cjs +500 -288
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.mts +21 -21
- package/dist/index.d.ts +21 -21
- package/dist/index.mjs +361 -247
- package/dist/index.mjs.map +1 -1
- package/dist/{server-DgpyR0RE.d.mts → server-CQztOmd-.d.mts} +21 -7
- package/dist/{server-DgpyR0RE.d.ts → server-CQztOmd-.d.ts} +21 -7
- package/dist/server.cjs +141 -43
- package/dist/server.cjs.map +1 -1
- package/dist/server.d.mts +1 -1
- package/dist/server.d.ts +1 -1
- package/dist/server.mjs +1 -1
- package/package.json +9 -9
- package/src/__tests__/default-value.test.ts +149 -0
- package/src/core/translator.tsx +385 -188
- package/src/hooks/useI18n.tsx +490 -337
- package/src/types/index.ts +291 -163
- package/dist/chunk-7ZYOSEMW.mjs.map +0 -1
package/dist/server.cjs
CHANGED
|
@@ -1,9 +1,17 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
// src/types/index.ts
|
|
4
|
-
var PLURAL_CATEGORIES = /* @__PURE__ */ new Set([
|
|
4
|
+
var PLURAL_CATEGORIES = /* @__PURE__ */ new Set([
|
|
5
|
+
"zero",
|
|
6
|
+
"one",
|
|
7
|
+
"two",
|
|
8
|
+
"few",
|
|
9
|
+
"many",
|
|
10
|
+
"other"
|
|
11
|
+
]);
|
|
5
12
|
function isPluralValue(value) {
|
|
6
|
-
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
13
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
14
|
+
return false;
|
|
7
15
|
const obj = value;
|
|
8
16
|
const keys = Object.keys(obj);
|
|
9
17
|
return keys.length > 0 && keys.every((k) => PLURAL_CATEGORIES.has(k)) && Object.values(obj).every((v) => typeof v === "string") && typeof obj.other === "string";
|
|
@@ -68,7 +76,9 @@ var Translator = class {
|
|
|
68
76
|
this.currentLang = config.defaultLanguage;
|
|
69
77
|
if (config.initialTranslations) {
|
|
70
78
|
this.allTranslations = config.initialTranslations;
|
|
71
|
-
for (const [language, namespaces] of Object.entries(
|
|
79
|
+
for (const [language, namespaces] of Object.entries(
|
|
80
|
+
config.initialTranslations
|
|
81
|
+
)) {
|
|
72
82
|
for (const namespace of Object.keys(namespaces)) {
|
|
73
83
|
this.loadedNamespaces.add(`${language}:${namespace}`);
|
|
74
84
|
}
|
|
@@ -166,9 +176,15 @@ var Translator = class {
|
|
|
166
176
|
}
|
|
167
177
|
}
|
|
168
178
|
if (this.config.debug) {
|
|
169
|
-
console.log(
|
|
179
|
+
console.log(
|
|
180
|
+
"\u{1F30D} [TRANSLATOR] Initializing translator with languages:",
|
|
181
|
+
languages
|
|
182
|
+
);
|
|
170
183
|
console.log("\u{1F4CD} [TRANSLATOR] Current language:", this.currentLang);
|
|
171
|
-
console.log(
|
|
184
|
+
console.log(
|
|
185
|
+
"\u{1F4E6} [TRANSLATOR] Config namespaces:",
|
|
186
|
+
this.config.namespaces
|
|
187
|
+
);
|
|
172
188
|
}
|
|
173
189
|
for (const language of languages) {
|
|
174
190
|
if (this.config.debug) {
|
|
@@ -181,12 +197,23 @@ var Translator = class {
|
|
|
181
197
|
const cacheKey = `${language}:${namespace}`;
|
|
182
198
|
if (skipNamespaces.has(cacheKey)) {
|
|
183
199
|
if (this.config.debug) {
|
|
184
|
-
console.log(
|
|
200
|
+
console.log(
|
|
201
|
+
"\u23ED\uFE0F [TRANSLATOR] Skipping",
|
|
202
|
+
namespace,
|
|
203
|
+
"for",
|
|
204
|
+
language,
|
|
205
|
+
"(already loaded from SSR)"
|
|
206
|
+
);
|
|
185
207
|
}
|
|
186
208
|
continue;
|
|
187
209
|
}
|
|
188
210
|
if (this.config.debug) {
|
|
189
|
-
console.log(
|
|
211
|
+
console.log(
|
|
212
|
+
"Loading namespace:",
|
|
213
|
+
namespace,
|
|
214
|
+
"for language:",
|
|
215
|
+
language
|
|
216
|
+
);
|
|
190
217
|
}
|
|
191
218
|
try {
|
|
192
219
|
const data = await this.safeLoadTranslations(language, namespace);
|
|
@@ -206,7 +233,10 @@ var Translator = class {
|
|
|
206
233
|
if (isRecoverableError(translationError)) {
|
|
207
234
|
if (language !== this.config.fallbackLanguage) {
|
|
208
235
|
try {
|
|
209
|
-
const fallbackData = await this.safeLoadTranslations(
|
|
236
|
+
const fallbackData = await this.safeLoadTranslations(
|
|
237
|
+
this.config.fallbackLanguage || "en",
|
|
238
|
+
namespace
|
|
239
|
+
);
|
|
210
240
|
this.allTranslations[language][namespace] = fallbackData;
|
|
211
241
|
this.loadedNamespaces.add(`${language}:${namespace}`);
|
|
212
242
|
if (this.config.debug) {
|
|
@@ -244,7 +274,9 @@ var Translator = class {
|
|
|
244
274
|
this.logError(this.initializationError);
|
|
245
275
|
this.isInitialized = true;
|
|
246
276
|
if (this.config.debug) {
|
|
247
|
-
console.warn(
|
|
277
|
+
console.warn(
|
|
278
|
+
"Translator initialized with errors, using fallback translations"
|
|
279
|
+
);
|
|
248
280
|
}
|
|
249
281
|
}
|
|
250
282
|
}
|
|
@@ -259,7 +291,10 @@ var Translator = class {
|
|
|
259
291
|
const result = this.findInNamespace(namespace, actualKey, targetLang);
|
|
260
292
|
if (result) {
|
|
261
293
|
if (this.config.debug) {
|
|
262
|
-
console.log(
|
|
294
|
+
console.log(
|
|
295
|
+
`\u2705 [TRANSLATOR] Found fallback translation from initialTranslations:`,
|
|
296
|
+
result
|
|
297
|
+
);
|
|
263
298
|
}
|
|
264
299
|
return result;
|
|
265
300
|
}
|
|
@@ -323,10 +358,17 @@ var Translator = class {
|
|
|
323
358
|
}
|
|
324
359
|
if (!this.isInitialized) {
|
|
325
360
|
const raw = this.translateBeforeInitialized(key, targetLang);
|
|
361
|
+
if ((!raw || raw === key) && params && typeof params === "object" && "defaultValue" in params && typeof params.defaultValue === "string") {
|
|
362
|
+
return this.interpolate(params.defaultValue, params);
|
|
363
|
+
}
|
|
326
364
|
return params ? this.interpolate(raw, params) : raw;
|
|
327
365
|
}
|
|
328
366
|
const { namespace, key: actualKey } = this.parseKey(key);
|
|
329
|
-
let result = this.findInNamespace(
|
|
367
|
+
let result = this.findInNamespace(
|
|
368
|
+
namespace,
|
|
369
|
+
actualKey,
|
|
370
|
+
targetLang
|
|
371
|
+
);
|
|
330
372
|
if (result) {
|
|
331
373
|
this.cacheStats.hits++;
|
|
332
374
|
return params ? this.interpolate(result, params) : result;
|
|
@@ -340,6 +382,9 @@ var Translator = class {
|
|
|
340
382
|
return params ? this.interpolate(result, params) : result;
|
|
341
383
|
}
|
|
342
384
|
this.cacheStats.misses++;
|
|
385
|
+
if (params && typeof params === "object" && "defaultValue" in params && typeof params.defaultValue === "string") {
|
|
386
|
+
return this.interpolate(params.defaultValue, params);
|
|
387
|
+
}
|
|
343
388
|
if (this.config.debug) {
|
|
344
389
|
const missing = this.config.missingKeyHandler?.(key, targetLang, namespace) || key;
|
|
345
390
|
return params ? this.interpolate(missing, params) : missing;
|
|
@@ -356,11 +401,16 @@ var Translator = class {
|
|
|
356
401
|
if (!this.loadedNamespaces.has(cacheKey) && !this.loadingPromises.has(cacheKey)) {
|
|
357
402
|
this.loadTranslationData(language, namespace).catch((error) => {
|
|
358
403
|
if (this.config.debug) {
|
|
359
|
-
console.warn(
|
|
404
|
+
console.warn(
|
|
405
|
+
`\u26A0\uFE0F [TRANSLATOR] Auto-load failed for ${language}/${namespace}:`,
|
|
406
|
+
error
|
|
407
|
+
);
|
|
360
408
|
}
|
|
361
409
|
});
|
|
362
410
|
if (this.config.debug) {
|
|
363
|
-
console.warn(
|
|
411
|
+
console.warn(
|
|
412
|
+
`\u274C [TRANSLATOR] No translations found for ${language}/${namespace}, attempting auto-load...`
|
|
413
|
+
);
|
|
364
414
|
}
|
|
365
415
|
}
|
|
366
416
|
return "";
|
|
@@ -380,7 +430,9 @@ var Translator = class {
|
|
|
380
430
|
return nestedValue[Math.floor(Math.random() * nestedValue.length)];
|
|
381
431
|
}
|
|
382
432
|
if (this.config.debug) {
|
|
383
|
-
console.warn(
|
|
433
|
+
console.warn(
|
|
434
|
+
`\u274C [TRANSLATOR] No match found for key: ${key} in ${language}/${namespace}`
|
|
435
|
+
);
|
|
384
436
|
}
|
|
385
437
|
return "";
|
|
386
438
|
}
|
|
@@ -446,7 +498,10 @@ var Translator = class {
|
|
|
446
498
|
if (actualKey in fallbackTranslations) {
|
|
447
499
|
return fallbackTranslations[actualKey];
|
|
448
500
|
}
|
|
449
|
-
const fallbackNestedValue = this.getNestedValue(
|
|
501
|
+
const fallbackNestedValue = this.getNestedValue(
|
|
502
|
+
fallbackTranslations,
|
|
503
|
+
actualKey
|
|
504
|
+
);
|
|
450
505
|
if (fallbackNestedValue !== void 0) {
|
|
451
506
|
return fallbackNestedValue;
|
|
452
507
|
}
|
|
@@ -492,7 +547,9 @@ var Translator = class {
|
|
|
492
547
|
const raw = this.getRawValue(key, targetLang);
|
|
493
548
|
const mergedParams = { count, ...params };
|
|
494
549
|
if (isPluralValue(raw)) {
|
|
495
|
-
const category = this.getPluralRules(targetLang).select(
|
|
550
|
+
const category = this.getPluralRules(targetLang).select(
|
|
551
|
+
count
|
|
552
|
+
);
|
|
496
553
|
const text = raw[category] ?? raw.other;
|
|
497
554
|
return this.interpolate(text, mergedParams);
|
|
498
555
|
}
|
|
@@ -535,7 +592,9 @@ var Translator = class {
|
|
|
535
592
|
});
|
|
536
593
|
}
|
|
537
594
|
if (this.config.debug) {
|
|
538
|
-
console.log(
|
|
595
|
+
console.log(
|
|
596
|
+
`\u{1F310} [TRANSLATOR] Language changed: ${previousLanguage} -> ${language}`
|
|
597
|
+
);
|
|
539
598
|
}
|
|
540
599
|
}
|
|
541
600
|
/**
|
|
@@ -647,7 +706,11 @@ var Translator = class {
|
|
|
647
706
|
*/
|
|
648
707
|
logError(error) {
|
|
649
708
|
if (this.config.errorHandler) {
|
|
650
|
-
this.config.errorHandler(
|
|
709
|
+
this.config.errorHandler(
|
|
710
|
+
error,
|
|
711
|
+
error.language || "",
|
|
712
|
+
error.namespace || ""
|
|
713
|
+
);
|
|
651
714
|
}
|
|
652
715
|
}
|
|
653
716
|
/**
|
|
@@ -670,7 +733,9 @@ var Translator = class {
|
|
|
670
733
|
if (attempt === maxRetries) {
|
|
671
734
|
break;
|
|
672
735
|
}
|
|
673
|
-
await new Promise(
|
|
736
|
+
await new Promise(
|
|
737
|
+
(resolve) => setTimeout(resolve, Math.pow(2, attempt) * 1e3)
|
|
738
|
+
);
|
|
674
739
|
}
|
|
675
740
|
}
|
|
676
741
|
throw lastError;
|
|
@@ -680,21 +745,29 @@ var Translator = class {
|
|
|
680
745
|
*/
|
|
681
746
|
async safeLoadTranslations(language, namespace) {
|
|
682
747
|
if (this.config.debug) {
|
|
683
|
-
console.log(`\u{1F4E5} [TRANSLATOR] safeLoadTranslations called:`, {
|
|
748
|
+
console.log(`\u{1F4E5} [TRANSLATOR] safeLoadTranslations called:`, {
|
|
749
|
+
language,
|
|
750
|
+
namespace
|
|
751
|
+
});
|
|
684
752
|
}
|
|
685
753
|
const loadOperation = async () => {
|
|
686
754
|
if (!this.config.loadTranslations) {
|
|
687
755
|
throw new Error("No translation loader configured");
|
|
688
756
|
}
|
|
689
757
|
if (this.config.debug) {
|
|
690
|
-
console.log(`\u{1F504} [TRANSLATOR] Calling loadTranslations for:`, {
|
|
758
|
+
console.log(`\u{1F504} [TRANSLATOR] Calling loadTranslations for:`, {
|
|
759
|
+
language,
|
|
760
|
+
namespace
|
|
761
|
+
});
|
|
691
762
|
}
|
|
692
763
|
const data = await this.config.loadTranslations(language, namespace);
|
|
693
764
|
if (this.config.debug) {
|
|
694
765
|
console.log(`\u{1F4E6} [TRANSLATOR] loadTranslations returned:`, data);
|
|
695
766
|
}
|
|
696
767
|
if (!isTranslationNamespace(data)) {
|
|
697
|
-
throw new Error(
|
|
768
|
+
throw new Error(
|
|
769
|
+
`Invalid translation data for ${language}:${namespace}`
|
|
770
|
+
);
|
|
698
771
|
}
|
|
699
772
|
return data;
|
|
700
773
|
};
|
|
@@ -707,7 +780,10 @@ var Translator = class {
|
|
|
707
780
|
language,
|
|
708
781
|
namespace
|
|
709
782
|
);
|
|
710
|
-
return this.retryOperation(loadOperation, translationError, {
|
|
783
|
+
return this.retryOperation(loadOperation, translationError, {
|
|
784
|
+
language,
|
|
785
|
+
namespace
|
|
786
|
+
});
|
|
711
787
|
}
|
|
712
788
|
}
|
|
713
789
|
/**
|
|
@@ -744,35 +820,30 @@ var Translator = class {
|
|
|
744
820
|
if (!this.isInitialized) {
|
|
745
821
|
await this.initialize();
|
|
746
822
|
}
|
|
747
|
-
|
|
748
|
-
if (!params) {
|
|
749
|
-
return translated;
|
|
750
|
-
}
|
|
751
|
-
return this.interpolate(translated, params);
|
|
823
|
+
return this.translate(key, params);
|
|
752
824
|
}
|
|
753
825
|
/**
|
|
754
826
|
* 동기 번역 (고급 기능)
|
|
755
827
|
*/
|
|
756
828
|
translateSync(key, params) {
|
|
757
829
|
if (!this.isInitialized) {
|
|
830
|
+
if (params && typeof params === "object" && "defaultValue" in params && typeof params.defaultValue === "string") {
|
|
831
|
+
return this.interpolate(params.defaultValue, params);
|
|
832
|
+
}
|
|
758
833
|
if (this.config.debug) {
|
|
759
834
|
console.warn("Translator not initialized for sync translation");
|
|
760
835
|
}
|
|
761
836
|
const { namespace } = this.parseKey(key);
|
|
762
837
|
return this.config.missingKeyHandler?.(key, this.currentLang, namespace) || key;
|
|
763
838
|
}
|
|
764
|
-
|
|
765
|
-
if (!params) {
|
|
766
|
-
return translated;
|
|
767
|
-
}
|
|
768
|
-
return this.interpolate(translated, params);
|
|
839
|
+
return this.translate(key, params);
|
|
769
840
|
}
|
|
770
841
|
/**
|
|
771
842
|
* 키 파싱 (네임스페이스:키 형식)
|
|
772
|
-
*
|
|
843
|
+
*
|
|
773
844
|
* - 콜론(:)만 네임스페이스 구분자로 사용
|
|
774
845
|
* - 점(.)은 키 이름의 일부로 취급 (중첩 객체 접근용)
|
|
775
|
-
*
|
|
846
|
+
*
|
|
776
847
|
* @example
|
|
777
848
|
* parseKey("home:hero.badge") → { namespace: "home", key: "hero.badge" }
|
|
778
849
|
* parseKey("hero.badge") → { namespace: "common", key: "hero.badge" }
|
|
@@ -781,7 +852,10 @@ var Translator = class {
|
|
|
781
852
|
parseKey(key) {
|
|
782
853
|
const colonIndex = key.indexOf(":");
|
|
783
854
|
if (colonIndex !== -1) {
|
|
784
|
-
return {
|
|
855
|
+
return {
|
|
856
|
+
namespace: key.substring(0, colonIndex),
|
|
857
|
+
key: key.substring(colonIndex + 1)
|
|
858
|
+
};
|
|
785
859
|
}
|
|
786
860
|
return { namespace: "common", key };
|
|
787
861
|
}
|
|
@@ -820,7 +894,9 @@ var Translator = class {
|
|
|
820
894
|
this.loadedNamespaces.add(cacheKey);
|
|
821
895
|
this.setCacheEntry(cacheKey, data);
|
|
822
896
|
if (this.config.debug) {
|
|
823
|
-
console.log(
|
|
897
|
+
console.log(
|
|
898
|
+
`\u2705 [TRANSLATOR] Auto-loaded and saved ${language}/${namespace}`
|
|
899
|
+
);
|
|
824
900
|
}
|
|
825
901
|
this.notifyTranslationLoaded(language, namespace);
|
|
826
902
|
return data;
|
|
@@ -838,7 +914,9 @@ var Translator = class {
|
|
|
838
914
|
try {
|
|
839
915
|
const data = await this.config.loadTranslations(language, namespace);
|
|
840
916
|
if (!isTranslationNamespace(data)) {
|
|
841
|
-
throw new Error(
|
|
917
|
+
throw new Error(
|
|
918
|
+
`Invalid translation data for ${language}:${namespace}`
|
|
919
|
+
);
|
|
842
920
|
}
|
|
843
921
|
return data;
|
|
844
922
|
} catch (error) {
|
|
@@ -861,12 +939,20 @@ function ssrTranslate({
|
|
|
861
939
|
missingKeyHandler = (key2) => key2
|
|
862
940
|
}) {
|
|
863
941
|
const { namespace, key: actualKey } = parseKey(key);
|
|
864
|
-
let result = ssrFindInNamespace(
|
|
942
|
+
let result = ssrFindInNamespace(
|
|
943
|
+
translations,
|
|
944
|
+
namespace,
|
|
945
|
+
actualKey,
|
|
946
|
+
language);
|
|
865
947
|
if (result) {
|
|
866
948
|
return result;
|
|
867
949
|
}
|
|
868
950
|
if (language !== fallbackLanguage) {
|
|
869
|
-
result = ssrFindInNamespace(
|
|
951
|
+
result = ssrFindInNamespace(
|
|
952
|
+
translations,
|
|
953
|
+
namespace,
|
|
954
|
+
actualKey,
|
|
955
|
+
fallbackLanguage);
|
|
870
956
|
if (result) {
|
|
871
957
|
return result;
|
|
872
958
|
}
|
|
@@ -905,7 +991,10 @@ function isStringValue(value) {
|
|
|
905
991
|
function parseKey(key) {
|
|
906
992
|
const colonIndex = key.indexOf(":");
|
|
907
993
|
if (colonIndex !== -1) {
|
|
908
|
-
return {
|
|
994
|
+
return {
|
|
995
|
+
namespace: key.substring(0, colonIndex),
|
|
996
|
+
key: key.substring(colonIndex + 1)
|
|
997
|
+
};
|
|
909
998
|
}
|
|
910
999
|
return { namespace: "common", key };
|
|
911
1000
|
}
|
|
@@ -927,7 +1016,11 @@ function serverTranslate({
|
|
|
927
1016
|
return cached;
|
|
928
1017
|
}
|
|
929
1018
|
}
|
|
930
|
-
const result = findInTranslations(
|
|
1019
|
+
const result = findInTranslations(
|
|
1020
|
+
translations,
|
|
1021
|
+
key,
|
|
1022
|
+
language,
|
|
1023
|
+
fallbackLanguage);
|
|
931
1024
|
if (cache && result) {
|
|
932
1025
|
const cacheKey = `${language}:${key}`;
|
|
933
1026
|
cache.set(cacheKey, result);
|
|
@@ -943,7 +1036,12 @@ function findInTranslations(translations, key, language, fallbackLanguage, missi
|
|
|
943
1036
|
return result;
|
|
944
1037
|
}
|
|
945
1038
|
if (language !== fallbackLanguage) {
|
|
946
|
-
result = findInNamespace(
|
|
1039
|
+
result = findInNamespace(
|
|
1040
|
+
translations,
|
|
1041
|
+
namespace,
|
|
1042
|
+
actualKey,
|
|
1043
|
+
fallbackLanguage
|
|
1044
|
+
);
|
|
947
1045
|
if (result) {
|
|
948
1046
|
return result;
|
|
949
1047
|
}
|