@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.
Files changed (78) hide show
  1. package/README.md +57 -597
  2. package/dist/chunk-F4PDBJLO.mjs +973 -0
  3. package/dist/chunk-F4PDBJLO.mjs.map +1 -0
  4. package/dist/index.d.mts +249 -0
  5. package/dist/index.d.ts +117 -30
  6. package/dist/index.js +1818 -177
  7. package/dist/index.js.map +1 -1
  8. package/dist/index.mjs +845 -0
  9. package/dist/index.mjs.map +1 -0
  10. package/dist/server-4TeBq6hp.d.mts +367 -0
  11. package/dist/server-4TeBq6hp.d.ts +367 -0
  12. package/dist/server.d.mts +1 -0
  13. package/dist/server.d.ts +1 -0
  14. package/dist/server.js +977 -0
  15. package/dist/server.js.map +1 -0
  16. package/dist/server.mjs +3 -0
  17. package/dist/server.mjs.map +1 -0
  18. package/package.json +42 -19
  19. package/src/__tests__/debug-tools.test.ts +359 -0
  20. package/src/__tests__/default-translations.test.ts +179 -0
  21. package/src/__tests__/i18n-resource.test.ts +137 -0
  22. package/src/__tests__/lazy-loader.test.ts +109 -0
  23. package/src/__tests__/missing-key-overlay.test.tsx +339 -0
  24. package/src/__tests__/translator-factory.test.ts +120 -0
  25. package/src/__tests__/translator.test.ts +442 -0
  26. package/src/__tests__/types.test.ts +211 -0
  27. package/src/__tests__/useI18n.test.tsx +181 -0
  28. package/src/__tests__/useTranslation.test.tsx +110 -0
  29. package/src/components/MissingKeyOverlay.tsx +1 -1
  30. package/src/core/lazy-loader.ts +2 -2
  31. package/src/core/translator.tsx +151 -62
  32. package/src/hooks/useI18n.tsx +96 -115
  33. package/src/hooks/useTranslation.tsx +12 -10
  34. package/src/index.ts +102 -5
  35. package/src/server.ts +9 -0
  36. package/src/types/index.ts +67 -12
  37. package/LICENSE +0 -21
  38. package/dist/components/MissingKeyOverlay.d.ts +0 -33
  39. package/dist/components/MissingKeyOverlay.d.ts.map +0 -1
  40. package/dist/components/MissingKeyOverlay.js +0 -138
  41. package/dist/components/MissingKeyOverlay.js.map +0 -1
  42. package/dist/core/debug-tools.d.ts +0 -37
  43. package/dist/core/debug-tools.d.ts.map +0 -1
  44. package/dist/core/debug-tools.js +0 -241
  45. package/dist/core/debug-tools.js.map +0 -1
  46. package/dist/core/i18n-resource.d.ts +0 -59
  47. package/dist/core/i18n-resource.d.ts.map +0 -1
  48. package/dist/core/i18n-resource.js +0 -153
  49. package/dist/core/i18n-resource.js.map +0 -1
  50. package/dist/core/lazy-loader.d.ts +0 -82
  51. package/dist/core/lazy-loader.d.ts.map +0 -1
  52. package/dist/core/lazy-loader.js +0 -193
  53. package/dist/core/lazy-loader.js.map +0 -1
  54. package/dist/core/translator-factory.d.ts +0 -50
  55. package/dist/core/translator-factory.d.ts.map +0 -1
  56. package/dist/core/translator-factory.js +0 -117
  57. package/dist/core/translator-factory.js.map +0 -1
  58. package/dist/core/translator.d.ts +0 -202
  59. package/dist/core/translator.d.ts.map +0 -1
  60. package/dist/core/translator.js +0 -912
  61. package/dist/core/translator.js.map +0 -1
  62. package/dist/hooks/useI18n.d.ts +0 -39
  63. package/dist/hooks/useI18n.d.ts.map +0 -1
  64. package/dist/hooks/useI18n.js +0 -531
  65. package/dist/hooks/useI18n.js.map +0 -1
  66. package/dist/hooks/useTranslation.d.ts +0 -55
  67. package/dist/hooks/useTranslation.d.ts.map +0 -1
  68. package/dist/hooks/useTranslation.js +0 -58
  69. package/dist/hooks/useTranslation.js.map +0 -1
  70. package/dist/index.d.ts.map +0 -1
  71. package/dist/types/index.d.ts +0 -162
  72. package/dist/types/index.d.ts.map +0 -1
  73. package/dist/types/index.js +0 -191
  74. package/dist/types/index.js.map +0 -1
  75. package/dist/utils/default-translations.d.ts +0 -20
  76. package/dist/utils/default-translations.d.ts.map +0 -1
  77. package/dist/utils/default-translations.js +0 -123
  78. package/dist/utils/default-translations.js.map +0 -1
@@ -0,0 +1,367 @@
1
+ type PluralCategory = 'zero' | 'one' | 'two' | 'few' | 'many' | 'other';
2
+ interface PluralValue {
3
+ zero?: string;
4
+ one?: string;
5
+ two?: string;
6
+ few?: string;
7
+ many?: string;
8
+ other: string;
9
+ }
10
+ interface TranslationNamespace {
11
+ [key: string]: string | string[] | PluralValue | TranslationNamespace;
12
+ }
13
+ interface LanguageConfig {
14
+ code: string;
15
+ name: string;
16
+ nativeName: string;
17
+ tone?: 'emotional' | 'encouraging' | 'calm' | 'gentle' | 'formal' | 'technical' | 'informal';
18
+ formality?: 'informal' | 'casual' | 'formal' | 'polite';
19
+ }
20
+ interface I18nConfig {
21
+ defaultLanguage: string;
22
+ fallbackLanguage?: string;
23
+ supportedLanguages: LanguageConfig[];
24
+ namespaces?: string[];
25
+ loadTranslations: (language: string, namespace: string) => Promise<TranslationNamespace>;
26
+ initialTranslations?: Record<string, Record<string, TranslationNamespace>>;
27
+ debug?: boolean;
28
+ missingKeyHandler?: (key: string, language: string, namespace: string) => string;
29
+ errorHandler?: (error: Error, language: string, namespace: string) => void;
30
+ cacheOptions?: {
31
+ maxSize?: number;
32
+ ttl?: number;
33
+ };
34
+ performanceOptions?: {
35
+ preloadAll?: boolean;
36
+ lazyLoad?: boolean;
37
+ };
38
+ errorHandling?: {
39
+ recoveryStrategy?: ErrorRecoveryStrategy;
40
+ logging?: ErrorLoggingConfig;
41
+ userFriendlyMessages?: boolean;
42
+ suppressErrors?: boolean;
43
+ };
44
+ autoLanguageSync?: boolean;
45
+ }
46
+ interface TranslationError extends Error {
47
+ code: 'MISSING_KEY' | 'LOAD_FAILED' | 'INVALID_KEY' | 'NETWORK_ERROR' | 'INITIALIZATION_ERROR' | 'VALIDATION_ERROR' | 'CACHE_ERROR' | 'FALLBACK_LOAD_FAILED' | 'INITIALIZATION_FAILED' | 'RETRY_FAILED';
48
+ language?: string;
49
+ namespace?: string;
50
+ key?: string;
51
+ originalError?: Error;
52
+ retryCount?: number;
53
+ maxRetries?: number;
54
+ timestamp: number;
55
+ context?: Record<string, unknown>;
56
+ }
57
+ interface ErrorRecoveryStrategy {
58
+ maxRetries: number;
59
+ retryDelay: number;
60
+ backoffMultiplier: number;
61
+ shouldRetry: (error: TranslationError) => boolean;
62
+ onRetry: (error: TranslationError, attempt: number) => void;
63
+ onMaxRetriesExceeded: (error: TranslationError) => void;
64
+ }
65
+ interface ErrorLoggingConfig {
66
+ enabled: boolean;
67
+ level: 'error' | 'warn' | 'info' | 'debug';
68
+ includeStack: boolean;
69
+ includeContext: boolean;
70
+ customLogger?: (error: TranslationError) => void;
71
+ }
72
+ interface I18nContextType {
73
+ currentLanguage: string;
74
+ setLanguage: (language: string) => void | Promise<void>;
75
+ t: (key: ResolveStringKey, paramsOrLang?: TranslationParams | string, language?: string) => string;
76
+ tPlural: (key: ResolvePluralKey, count: number, params?: Record<string, unknown>, language?: string) => string;
77
+ tAsync: (key: string, params?: TranslationParams) => Promise<string>;
78
+ tSync: (key: string, namespace?: string, params?: TranslationParams) => string;
79
+ getRawValue: (key: string, language?: string) => unknown;
80
+ tArray: (key: ResolveArrayKey, language?: string) => string[];
81
+ isLoading: boolean;
82
+ error: TranslationError | null;
83
+ supportedLanguages: LanguageConfig[];
84
+ isInitialized: boolean;
85
+ debug: {
86
+ getCurrentLanguage: () => string;
87
+ getSupportedLanguages: () => string[];
88
+ getLoadedNamespaces: () => string[];
89
+ getAllTranslations: () => Record<string, Record<string, unknown>>;
90
+ isReady: () => boolean;
91
+ getInitializationError: () => TranslationError | null;
92
+ clearCache: () => void;
93
+ reloadTranslations: () => Promise<void>;
94
+ getCacheStats: () => {
95
+ size: number;
96
+ hits: number;
97
+ misses: number;
98
+ };
99
+ };
100
+ }
101
+ interface TranslationParams {
102
+ [key: string]: string | number;
103
+ }
104
+ /**
105
+ * 타입 안전한 번역 키를 위한 augmentation point.
106
+ *
107
+ * 앱에서 declaration merging으로 좁힐 수 있음:
108
+ * ```ts
109
+ * declare module '@hua-labs/i18n-core' {
110
+ * interface TypedTranslationKeys {
111
+ * stringKey: TranslationStringKey;
112
+ * arrayKey: TranslationArrayKey;
113
+ * }
114
+ * }
115
+ * ```
116
+ *
117
+ * augmentation이 없으면 string으로 fallback (breaking 없음).
118
+ */
119
+ interface TypedTranslationKeys {
120
+ }
121
+ /** augmentation 시 좁혀진 타입, 미설정 시 string */
122
+ type ResolveStringKey = TypedTranslationKeys extends {
123
+ stringKey: infer K;
124
+ } ? K & string : string;
125
+ type ResolveArrayKey = TypedTranslationKeys extends {
126
+ arrayKey: infer K;
127
+ } ? K & string : string;
128
+ type ResolvePluralKey = TypedTranslationKeys extends {
129
+ pluralKey: infer K;
130
+ } ? K & string : string;
131
+
132
+ interface TranslatorInterface {
133
+ translate(key: string, paramsOrLang?: Record<string, unknown> | string, language?: string): string;
134
+ tPlural(key: string, count: number, params?: Record<string, unknown>, language?: string): string;
135
+ setLanguage(lang: string): void;
136
+ getCurrentLanguage(): string;
137
+ initialize(): Promise<void>;
138
+ isReady(): boolean;
139
+ debug(): unknown;
140
+ getRawValue(key: string, language?: string): unknown;
141
+ tArray(key: string, language?: string): string[];
142
+ }
143
+ declare class Translator implements TranslatorInterface {
144
+ private cache;
145
+ private pluralRulesCache;
146
+ private loadedNamespaces;
147
+ private loadingPromises;
148
+ private allTranslations;
149
+ private isInitialized;
150
+ private initializationError;
151
+ private config;
152
+ private currentLang;
153
+ private cacheStats;
154
+ private onTranslationLoadedCallbacks;
155
+ private onLanguageChangedCallbacks;
156
+ private notifyTimer;
157
+ private recentlyNotified;
158
+ /**
159
+ * 번역 로드 완료 콜백 등록
160
+ */
161
+ onTranslationLoaded(callback: () => void): () => void;
162
+ /**
163
+ * 언어 변경 콜백 등록
164
+ */
165
+ onLanguageChanged(callback: (language: string) => void): () => void;
166
+ /**
167
+ * 언어 변경 이벤트 발생
168
+ */
169
+ private notifyLanguageChanged;
170
+ /**
171
+ * 번역 로드 완료 이벤트 발생 (디바운싱 적용)
172
+ */
173
+ private notifyTranslationLoaded;
174
+ constructor(config: I18nConfig);
175
+ /**
176
+ * 모든 번역 데이터를 미리 로드 (hua-api 스타일)
177
+ */
178
+ initialize(): Promise<void>;
179
+ /**
180
+ * 초기화되지 않은 상태에서 번역 시도
181
+ */
182
+ private translateBeforeInitialized;
183
+ /**
184
+ * 다른 로드된 언어에서 번역 찾기 (언어 변경 중 깜빡임 방지)
185
+ */
186
+ private findInOtherLanguages;
187
+ /**
188
+ * 폴백 언어에서 번역 찾기
189
+ */
190
+ private findInFallbackLanguage;
191
+ /**
192
+ * 번역 키를 번역된 텍스트로 변환
193
+ */
194
+ translate(key: string, paramsOrLang?: Record<string, unknown> | string, language?: string): string;
195
+ /**
196
+ * 네임스페이스에서 키 찾기
197
+ */
198
+ private findInNamespace;
199
+ /**
200
+ * 중첩된 객체에서 값을 가져오기
201
+ * 배열도 지원: 최종 값이 string[]이면 그대로 반환
202
+ */
203
+ private getNestedValue;
204
+ /**
205
+ * 문자열 값인지 확인하는 타입 가드
206
+ */
207
+ private isStringValue;
208
+ /**
209
+ * string[] 배열인지 확인하는 타입 가드
210
+ * 배열 값이 t()에 전달되면 랜덤으로 하나를 선택하여 반환
211
+ */
212
+ private isStringArray;
213
+ /**
214
+ * 원시 값 가져오기 (배열, 객체 포함)
215
+ */
216
+ getRawValue(key: string, language?: string): unknown;
217
+ /**
218
+ * 배열 번역 값 가져오기 (타입 안전)
219
+ */
220
+ tArray(key: string, language?: string): string[];
221
+ /**
222
+ * Intl.PluralRules 인스턴스 (언어별 캐시)
223
+ */
224
+ private getPluralRules;
225
+ /**
226
+ * 복수형 번역 (ICU / Intl.PluralRules 기반)
227
+ *
228
+ * JSON: { "other": "총 {count}개" } (ko)
229
+ * { "one": "{count} item", "other": "{count} items" } (en)
230
+ *
231
+ * tPlural('common:total_count', 1) → en: "1 item" / ko: "총 1개"
232
+ * tPlural('common:total_count', 5) → en: "5 items" / ko: "총 5개"
233
+ */
234
+ tPlural(key: string, count: number, params?: Record<string, unknown>, language?: string): string;
235
+ /**
236
+ * 매개변수 보간
237
+ *
238
+ * 지원 형식:
239
+ * - {key} - 단일 중괄호 (일반적인 i18n 형식)
240
+ * - {{key}} - 이중 중괄호 (하위 호환성)
241
+ */
242
+ private interpolate;
243
+ /**
244
+ * 언어 설정
245
+ */
246
+ setLanguage(language: string): void;
247
+ /**
248
+ * 언어 데이터 로드
249
+ */
250
+ private loadLanguageData;
251
+ /**
252
+ * 현재 언어 가져오기
253
+ */
254
+ getCurrentLanguage(): string;
255
+ /**
256
+ * 지원되는 언어 목록 가져오기
257
+ */
258
+ getSupportedLanguages(): string[];
259
+ /**
260
+ * 초기화 완료 여부 확인
261
+ */
262
+ isReady(): boolean;
263
+ /**
264
+ * 초기화 오류 가져오기
265
+ */
266
+ getInitializationError(): TranslationError | null;
267
+ /**
268
+ * 캐시 클리어
269
+ */
270
+ clearCache(): void;
271
+ /**
272
+ * 캐시 엔트리 설정
273
+ */
274
+ private setCacheEntry;
275
+ /**
276
+ * 캐시 엔트리 가져오기
277
+ */
278
+ private getCacheEntry;
279
+ /**
280
+ * 번역 오류 생성
281
+ */
282
+ private createTranslationError;
283
+ /**
284
+ * 오류 로깅
285
+ */
286
+ private logError;
287
+ /**
288
+ * 재시도 작업
289
+ */
290
+ private retryOperation;
291
+ /**
292
+ * 안전한 번역 로드
293
+ */
294
+ private safeLoadTranslations;
295
+ /**
296
+ * 디버그 정보
297
+ */
298
+ debug(): {
299
+ isInitialized: boolean;
300
+ currentLanguage: string;
301
+ loadedNamespaces: string[];
302
+ cacheStats: {
303
+ hits: number;
304
+ misses: number;
305
+ };
306
+ cacheSize: number;
307
+ allTranslations: Record<string, Record<string, TranslationNamespace>>;
308
+ initializationError: TranslationError | null;
309
+ config: I18nConfig;
310
+ };
311
+ /**
312
+ * SSR에서 하이드레이션
313
+ */
314
+ hydrateFromSSR(translations: Record<string, Record<string, TranslationNamespace>>): void;
315
+ /**
316
+ * 비동기 번역 (고급 기능)
317
+ */
318
+ translateAsync(key: string, params?: Record<string, unknown>): Promise<string>;
319
+ /**
320
+ * 동기 번역 (고급 기능)
321
+ */
322
+ translateSync(key: string, params?: Record<string, unknown>): string;
323
+ /**
324
+ * 키 파싱 (네임스페이스:키 형식)
325
+ *
326
+ * - 콜론(:)만 네임스페이스 구분자로 사용
327
+ * - 점(.)은 키 이름의 일부로 취급 (중첩 객체 접근용)
328
+ *
329
+ * @example
330
+ * parseKey("home:hero.badge") → { namespace: "home", key: "hero.badge" }
331
+ * parseKey("hero.badge") → { namespace: "common", key: "hero.badge" }
332
+ * parseKey("save") → { namespace: "common", key: "save" }
333
+ */
334
+ private parseKey;
335
+ /**
336
+ * 번역 데이터 로드 (고급 기능)
337
+ */
338
+ private loadTranslationData;
339
+ /**
340
+ * 실제 번역 데이터 로드
341
+ */
342
+ private _loadTranslationData;
343
+ }
344
+ declare function ssrTranslate({ translations, key, language, fallbackLanguage, missingKeyHandler }: {
345
+ translations: Record<string, Record<string, TranslationNamespace>>;
346
+ key: string;
347
+ language?: string;
348
+ fallbackLanguage?: string;
349
+ missingKeyHandler?: (key: string) => string;
350
+ }): string;
351
+ declare function serverTranslate({ translations, key, language, fallbackLanguage, missingKeyHandler, options }: {
352
+ translations: Record<string, unknown>;
353
+ key: string;
354
+ language?: string;
355
+ fallbackLanguage?: string;
356
+ missingKeyHandler?: (key: string) => string;
357
+ options?: {
358
+ cache?: Map<string, string>;
359
+ metrics?: {
360
+ hits: number;
361
+ misses: number;
362
+ };
363
+ debug?: boolean;
364
+ };
365
+ }): string;
366
+
367
+ export { type I18nConfig as I, type LanguageConfig as L, type PluralCategory as P, type ResolveStringKey as R, type TranslationParams as T, type I18nContextType as a, type ResolvePluralKey as b, type ResolveArrayKey as c, type TranslationError as d, type PluralValue as e, Translator as f, type TypedTranslationKeys as g, ssrTranslate as h, serverTranslate as s };
@@ -0,0 +1 @@
1
+ export { I as I18nConfig, f as Translator, s as serverTranslate, h as ssrTranslate } from './server-4TeBq6hp.mjs';
@@ -0,0 +1 @@
1
+ export { I as I18nConfig, f as Translator, s as serverTranslate, h as ssrTranslate } from './server-4TeBq6hp.js';