@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.
@@ -1,7 +1,7 @@
1
1
  // ---------------------------------------------------------------------------
2
2
  // Plural (ICU / Intl.PluralRules)
3
3
  // ---------------------------------------------------------------------------
4
- export type PluralCategory = 'zero' | 'one' | 'two' | 'few' | 'many' | 'other';
4
+ export type PluralCategory = "zero" | "one" | "two" | "few" | "many" | "other";
5
5
 
6
6
  export interface PluralValue {
7
7
  zero?: string;
@@ -12,17 +12,25 @@ export interface PluralValue {
12
12
  other: string; // 필수
13
13
  }
14
14
 
15
- const PLURAL_CATEGORIES = new Set<string>(['zero', 'one', 'two', 'few', 'many', 'other']);
15
+ const PLURAL_CATEGORIES = new Set<string>([
16
+ "zero",
17
+ "one",
18
+ "two",
19
+ "few",
20
+ "many",
21
+ "other",
22
+ ]);
16
23
 
17
24
  export function isPluralValue(value: unknown): value is PluralValue {
18
- if (typeof value !== 'object' || value === null || Array.isArray(value)) return false;
25
+ if (typeof value !== "object" || value === null || Array.isArray(value))
26
+ return false;
19
27
  const obj = value as Record<string, unknown>;
20
28
  const keys = Object.keys(obj);
21
29
  return (
22
30
  keys.length > 0 &&
23
- keys.every(k => PLURAL_CATEGORIES.has(k)) &&
24
- Object.values(obj).every(v => typeof v === 'string') &&
25
- typeof obj.other === 'string'
31
+ keys.every((k) => PLURAL_CATEGORIES.has(k)) &&
32
+ Object.values(obj).every((v) => typeof v === "string") &&
33
+ typeof obj.other === "string"
26
34
  );
27
35
  }
28
36
 
@@ -50,24 +58,32 @@ export interface I18nPlatformAdapter {
50
58
  */
51
59
  export const webPlatformAdapter: I18nPlatformAdapter = {
52
60
  getDeviceLanguage() {
53
- if (typeof globalThis !== 'undefined' && typeof navigator !== 'undefined' && navigator.language) {
61
+ if (
62
+ typeof globalThis !== "undefined" &&
63
+ typeof navigator !== "undefined" &&
64
+ navigator.language
65
+ ) {
54
66
  return navigator.language.slice(0, 2).toLowerCase();
55
67
  }
56
68
  return undefined;
57
69
  },
58
70
  onLanguageChange(cb) {
59
- if (typeof globalThis === 'undefined' || typeof window === 'undefined' || typeof CustomEvent === 'undefined') {
71
+ if (
72
+ typeof globalThis === "undefined" ||
73
+ typeof window === "undefined" ||
74
+ typeof CustomEvent === "undefined"
75
+ ) {
60
76
  return () => {};
61
77
  }
62
78
  const handler = (e: Event) => {
63
79
  const lang = (e as CustomEvent).detail;
64
- if (typeof lang === 'string') cb(lang);
80
+ if (typeof lang === "string") cb(lang);
65
81
  };
66
- window.addEventListener('huaI18nLanguageChange', handler);
67
- window.addEventListener('i18nLanguageChanged', handler);
82
+ window.addEventListener("huaI18nLanguageChange", handler);
83
+ window.addEventListener("i18nLanguageChanged", handler);
68
84
  return () => {
69
- window.removeEventListener('huaI18nLanguageChange', handler);
70
- window.removeEventListener('i18nLanguageChanged', handler);
85
+ window.removeEventListener("huaI18nLanguageChange", handler);
86
+ window.removeEventListener("i18nLanguageChanged", handler);
71
87
  };
72
88
  },
73
89
  };
@@ -77,8 +93,12 @@ export const webPlatformAdapter: I18nPlatformAdapter = {
77
93
  * 언어 감지·이벤트 없이 config.defaultLanguage만 사용.
78
94
  */
79
95
  export const headlessPlatformAdapter: I18nPlatformAdapter = {
80
- getDeviceLanguage() { return undefined; },
81
- onLanguageChange() { return () => {}; },
96
+ getDeviceLanguage() {
97
+ return undefined;
98
+ },
99
+ onLanguageChange() {
100
+ return () => {};
101
+ },
82
102
  };
83
103
 
84
104
  // ---------------------------------------------------------------------------
@@ -96,8 +116,15 @@ export interface LanguageConfig {
96
116
  code: string;
97
117
  name: string;
98
118
  nativeName: string;
99
- tone?: 'emotional' | 'encouraging' | 'calm' | 'gentle' | 'formal' | 'technical' | 'informal';
100
- formality?: 'informal' | 'casual' | 'formal' | 'polite';
119
+ tone?:
120
+ | "emotional"
121
+ | "encouraging"
122
+ | "calm"
123
+ | "gentle"
124
+ | "formal"
125
+ | "technical"
126
+ | "informal";
127
+ formality?: "informal" | "casual" | "formal" | "polite";
101
128
  }
102
129
 
103
130
  // 더 구체적인 설정 타입 정의
@@ -106,13 +133,20 @@ export interface I18nConfig {
106
133
  fallbackLanguage?: string;
107
134
  supportedLanguages: LanguageConfig[];
108
135
  namespaces?: string[];
109
- loadTranslations: (language: string, namespace: string) => Promise<TranslationNamespace>;
136
+ loadTranslations: (
137
+ language: string,
138
+ namespace: string,
139
+ ) => Promise<TranslationNamespace>;
110
140
  // SSR에서 전달된 초기 번역 데이터 (네트워크 요청 없이 사용)
111
141
  initialTranslations?: Record<string, Record<string, TranslationNamespace>>;
112
142
  // 개발 모드 설정
113
143
  debug?: boolean;
114
144
  // 번역 키가 없을 때의 동작
115
- missingKeyHandler?: (key: string, language: string, namespace: string) => string;
145
+ missingKeyHandler?: (
146
+ key: string,
147
+ language: string,
148
+ namespace: string,
149
+ ) => string;
116
150
  // 번역 로딩 실패 시 동작
117
151
  errorHandler?: (error: Error, language: string, namespace: string) => void;
118
152
  // 캐시 설정
@@ -140,7 +174,17 @@ export interface I18nConfig {
140
174
 
141
175
  // 에러 타입 정의
142
176
  export interface TranslationError extends Error {
143
- code: 'MISSING_KEY' | 'LOAD_FAILED' | 'INVALID_KEY' | 'NETWORK_ERROR' | 'INITIALIZATION_ERROR' | 'VALIDATION_ERROR' | 'CACHE_ERROR' | 'FALLBACK_LOAD_FAILED' | 'INITIALIZATION_FAILED' | 'RETRY_FAILED';
177
+ code:
178
+ | "MISSING_KEY"
179
+ | "LOAD_FAILED"
180
+ | "INVALID_KEY"
181
+ | "NETWORK_ERROR"
182
+ | "INITIALIZATION_ERROR"
183
+ | "VALIDATION_ERROR"
184
+ | "CACHE_ERROR"
185
+ | "FALLBACK_LOAD_FAILED"
186
+ | "INITIALIZATION_FAILED"
187
+ | "RETRY_FAILED";
144
188
  language?: string;
145
189
  namespace?: string;
146
190
  key?: string;
@@ -164,7 +208,7 @@ export interface ErrorRecoveryStrategy {
164
208
  // 에러 로깅 설정
165
209
  export interface ErrorLoggingConfig {
166
210
  enabled: boolean;
167
- level: 'error' | 'warn' | 'info' | 'debug';
211
+ level: "error" | "warn" | "info" | "debug";
168
212
  includeStack: boolean;
169
213
  includeContext: boolean;
170
214
  customLogger?: (error: TranslationError) => void;
@@ -176,7 +220,7 @@ export interface UserFriendlyError {
176
220
  message: string;
177
221
  suggestion?: string;
178
222
  action?: string;
179
- severity: 'low' | 'medium' | 'high' | 'critical';
223
+ severity: "low" | "medium" | "high" | "critical";
180
224
  }
181
225
 
182
226
  // 캐시 엔트리 타입
@@ -210,13 +254,26 @@ export interface I18nContextType {
210
254
  currentLanguage: string;
211
255
  setLanguage: (language: string) => void | Promise<void>;
212
256
  // 통합 번역 함수: t(key), t(key, language), t(key, params), t(key, params, language)
213
- t: (key: ResolveStringKey, paramsOrLang?: TranslationParams | string, language?: string) => string;
257
+ t: (
258
+ key: ResolveStringKey,
259
+ paramsOrLang?: TranslationParams | string,
260
+ language?: string,
261
+ ) => string;
214
262
  // 복수형 번역 함수: tPlural(key, count), tPlural(key, count, params), tPlural(key, count, params, language)
215
- tPlural: (key: ResolvePluralKey, count: number, params?: Record<string, unknown>, language?: string) => string;
216
- // 기존 비동기 번역 함수 (하위 호환성)
263
+ tPlural: (
264
+ key: ResolvePluralKey,
265
+ count: number,
266
+ params?: Record<string, unknown>,
267
+ language?: string,
268
+ ) => string;
269
+ /** @deprecated Use `t()` instead. Will be removed in a future major version. */
217
270
  tAsync: (key: string, params?: TranslationParams) => Promise<string>;
218
- // 기존 동기 번역 함수 (하위 호환성)
219
- tSync: (key: string, namespace?: string, params?: TranslationParams) => string;
271
+ /** @deprecated Use `t()` instead. Will be removed in a future major version. */
272
+ tSync: (
273
+ key: string,
274
+ namespace?: string,
275
+ params?: TranslationParams,
276
+ ) => string;
220
277
  // 원시 값 가져오기 (배열, 객체 포함) — 제네릭으로 타입 캐스팅 가능
221
278
  getRawValue: <T = unknown>(key: string, language?: string) => T | undefined;
222
279
  // 배열 번역 값 가져오기 (타입 안전)
@@ -244,6 +301,18 @@ export interface I18nContextType {
244
301
  };
245
302
  }
246
303
 
304
+ /**
305
+ * Parameters for the `t()` function.
306
+ *
307
+ * `defaultValue` is a reserved key: when translation lookup fails entirely,
308
+ * the string provided here is returned instead of empty string (production)
309
+ * or the raw key (debug). Interpolation variables still work:
310
+ *
311
+ * ```ts
312
+ * t('missing:key', { defaultValue: 'Hello {{name}}', name: 'World' })
313
+ * // → 'Hello World'
314
+ * ```
315
+ */
247
316
  export interface TranslationParams {
248
317
  [key: string]: string | number;
249
318
  }
@@ -263,115 +332,162 @@ export interface TranslationParams {
263
332
  *
264
333
  * augmentation이 없으면 string으로 fallback (breaking 없음).
265
334
  */
266
-
335
+
267
336
  export interface TypedTranslationKeys {}
268
337
 
269
338
  /** augmentation 시 좁혀진 타입, 미설정 시 string */
270
- export type ResolveStringKey = TypedTranslationKeys extends { stringKey: infer K } ? K & string : string;
271
- export type ResolveArrayKey = TypedTranslationKeys extends { arrayKey: infer K } ? K & string : string;
272
- export type ResolvePluralKey = TypedTranslationKeys extends { pluralKey: infer K } ? K & string : string;
339
+ export type ResolveStringKey = TypedTranslationKeys extends {
340
+ stringKey: infer K;
341
+ }
342
+ ? K & string
343
+ : string;
344
+ export type ResolveArrayKey = TypedTranslationKeys extends { arrayKey: infer K }
345
+ ? K & string
346
+ : string;
347
+ export type ResolvePluralKey = TypedTranslationKeys extends {
348
+ pluralKey: infer K;
349
+ }
350
+ ? K & string
351
+ : string;
273
352
 
274
353
  // 타입 안전한 번역 키 시스템 (단순화된 버전)
275
- export type TranslationKey<T> = T extends Record<string, unknown>
276
- ? {
277
- [K in keyof T]: T[K] extends string
278
- ? K
279
- : T[K] extends Record<string, unknown>
280
- ? `${K & string}.${TranslationKey<T[K]> & string}`
281
- : never;
282
- }[keyof T]
283
- : never;
354
+ export type TranslationKey<T> =
355
+ T extends Record<string, unknown>
356
+ ? {
357
+ [K in keyof T]: T[K] extends string
358
+ ? K
359
+ : T[K] extends Record<string, unknown>
360
+ ? `${K & string}.${TranslationKey<T[K]> & string}`
361
+ : never;
362
+ }[keyof T]
363
+ : never;
284
364
 
285
365
  // 타입 안전한 번역 함수들
286
- export interface TypedI18nContextType<T extends TranslationData> extends Omit<I18nContextType, 't' | 'tSync'> {
366
+ export interface TypedI18nContextType<T extends TranslationData> extends Omit<
367
+ I18nContextType,
368
+ "t" | "tSync"
369
+ > {
287
370
  // 타입 안전한 번역 함수
288
- t: <K extends TranslationKey<T>>(key: K, paramsOrLang?: TranslationParams | string, language?: string) => string;
289
- tSync: <K extends TranslationKey<T>>(key: K, namespace?: string, params?: TranslationParams) => string;
371
+ t: <K extends TranslationKey<T>>(
372
+ key: K,
373
+ paramsOrLang?: TranslationParams | string,
374
+ language?: string,
375
+ ) => string;
376
+ tSync: <K extends TranslationKey<T>>(
377
+ key: K,
378
+ namespace?: string,
379
+ params?: TranslationParams,
380
+ ) => string;
290
381
  }
291
382
 
292
383
  // 간단한 번역 키 타입 (무한 재귀 방지)
293
384
  export type SimpleTranslationKey = string;
294
385
 
295
386
  // 고급 번역 키 타입 (제한된 깊이)
296
- export type TranslationKeyLegacy<T extends Record<string, unknown>, D extends number = 3> =
297
- [D] extends [never]
298
- ? never
299
- : T extends Record<string, unknown>
300
- ? {
301
- [K in keyof T]: T[K] extends string
302
- ? K
303
- : T[K] extends Record<string, unknown>
304
- ? `${K & string}.${TranslationKeyLegacy<T[K], Prev<D>> & string}`
305
- : never;
306
- }[keyof T]
307
- : never;
308
-
309
- type Prev<T extends number> = T extends 0 ? never : T extends 1 ? 0 : T extends 2 ? 1 : T extends 3 ? 2 : never;
387
+ export type TranslationKeyLegacy<
388
+ T extends Record<string, unknown>,
389
+ D extends number = 3,
390
+ > = [D] extends [never]
391
+ ? never
392
+ : T extends Record<string, unknown>
393
+ ? {
394
+ [K in keyof T]: T[K] extends string
395
+ ? K
396
+ : T[K] extends Record<string, unknown>
397
+ ? `${K & string}.${TranslationKeyLegacy<T[K], Prev<D>> & string}`
398
+ : never;
399
+ }[keyof T]
400
+ : never;
401
+
402
+ type Prev<T extends number> = T extends 0
403
+ ? never
404
+ : T extends 1
405
+ ? 0
406
+ : T extends 2
407
+ ? 1
408
+ : T extends 3
409
+ ? 2
410
+ : never;
310
411
 
311
412
  // 유틸리티 타입들
312
- export type ExtractTranslationKeys<T> = T extends Record<string, unknown>
313
- ? {
314
- [K in keyof T]: T[K] extends string
315
- ? K
316
- : T[K] extends Record<string, unknown>
317
- ? `${K & string}.${ExtractTranslationKeys<T[K]> & string}`
318
- : never;
319
- }[keyof T]
320
- : never;
413
+ export type ExtractTranslationKeys<T> =
414
+ T extends Record<string, unknown>
415
+ ? {
416
+ [K in keyof T]: T[K] extends string
417
+ ? K
418
+ : T[K] extends Record<string, unknown>
419
+ ? `${K & string}.${ExtractTranslationKeys<T[K]> & string}`
420
+ : never;
421
+ }[keyof T]
422
+ : never;
321
423
 
322
424
  // 네임스페이스별 타입 정의를 위한 헬퍼
323
- export type NamespaceKeys<T extends TranslationData, N extends keyof T> = ExtractTranslationKeys<T[N]>;
425
+ export type NamespaceKeys<
426
+ T extends TranslationData,
427
+ N extends keyof T,
428
+ > = ExtractTranslationKeys<T[N]>;
324
429
 
325
430
  // 타입 안전한 번역 키 생성 헬퍼
326
- export const createTranslationKey = <T extends TranslationData, N extends keyof T, K extends NamespaceKeys<T, N>>(
431
+ export const createTranslationKey = <
432
+ T extends TranslationData,
433
+ N extends keyof T,
434
+ K extends NamespaceKeys<T, N>,
435
+ >(
327
436
  namespace: N,
328
- key: K
329
- ): `${N & string}.${K & string}` => `${String(namespace)}.${String(key)}` as `${N & string}.${K & string}`;
437
+ key: K,
438
+ ): `${N & string}.${K & string}` =>
439
+ `${String(namespace)}.${String(key)}` as `${N & string}.${K & string}`;
330
440
 
331
441
  // 타입 가드 함수들
332
- export function isTranslationNamespace(value: unknown): value is TranslationNamespace {
333
- return typeof value === 'object' && value !== null && !Array.isArray(value);
442
+ export function isTranslationNamespace(
443
+ value: unknown,
444
+ ): value is TranslationNamespace {
445
+ return typeof value === "object" && value !== null && !Array.isArray(value);
334
446
  }
335
447
 
336
448
  export function isLanguageConfig(value: unknown): value is LanguageConfig {
337
449
  return (
338
- typeof value === 'object' &&
450
+ typeof value === "object" &&
339
451
  value !== null &&
340
- typeof (value as LanguageConfig).code === 'string' &&
341
- typeof (value as LanguageConfig).name === 'string' &&
342
- typeof (value as LanguageConfig).nativeName === 'string'
452
+ typeof (value as LanguageConfig).code === "string" &&
453
+ typeof (value as LanguageConfig).name === "string" &&
454
+ typeof (value as LanguageConfig).nativeName === "string"
343
455
  );
344
456
  }
345
457
 
346
458
  export function isTranslationError(value: unknown): value is TranslationError {
347
459
  return (
348
460
  value instanceof Error &&
349
- typeof (value as TranslationError).code === 'string' &&
350
- ['MISSING_KEY', 'LOAD_FAILED', 'INVALID_KEY', 'NETWORK_ERROR', 'INITIALIZATION_ERROR'].includes(
351
- (value as TranslationError).code
352
- )
461
+ typeof (value as TranslationError).code === "string" &&
462
+ [
463
+ "MISSING_KEY",
464
+ "LOAD_FAILED",
465
+ "INVALID_KEY",
466
+ "NETWORK_ERROR",
467
+ "INITIALIZATION_ERROR",
468
+ ].includes((value as TranslationError).code)
353
469
  );
354
470
  }
355
471
 
356
472
  // 설정 검증 함수
357
473
  export function validateI18nConfig(config: unknown): config is I18nConfig {
358
- if (!config || typeof config !== 'object') {
474
+ if (!config || typeof config !== "object") {
359
475
  return false;
360
476
  }
361
477
 
362
478
  const c = config as I18nConfig;
363
-
479
+
364
480
  return (
365
- typeof c.defaultLanguage === 'string' &&
481
+ typeof c.defaultLanguage === "string" &&
366
482
  Array.isArray(c.supportedLanguages) &&
367
483
  c.supportedLanguages.every(isLanguageConfig) &&
368
- typeof c.loadTranslations === 'function'
484
+ typeof c.loadTranslations === "function"
369
485
  );
370
486
  }
371
487
 
372
488
  // 에러 처리 유틸리티 함수들
373
489
  export function createTranslationError(
374
- code: TranslationError['code'],
490
+ code: TranslationError["code"],
375
491
  message: string,
376
492
  originalError?: Error,
377
493
  context?: {
@@ -380,7 +496,7 @@ export function createTranslationError(
380
496
  key?: string;
381
497
  retryCount?: number;
382
498
  maxRetries?: number;
383
- }
499
+ },
384
500
  ): TranslationError {
385
501
  const error = new Error(message) as TranslationError;
386
502
  error.code = code;
@@ -391,83 +507,85 @@ export function createTranslationError(
391
507
  error.retryCount = context?.retryCount || 0;
392
508
  error.maxRetries = context?.maxRetries || 3;
393
509
  error.timestamp = Date.now();
394
- error.name = 'TranslationError';
510
+ error.name = "TranslationError";
395
511
  return error;
396
512
  }
397
513
 
398
514
  // 사용자 친화적 에러 메시지 생성
399
- export function createUserFriendlyError(error: TranslationError): UserFriendlyError {
400
- const errorMessages: Record<TranslationError['code'], UserFriendlyError> = {
515
+ export function createUserFriendlyError(
516
+ error: TranslationError,
517
+ ): UserFriendlyError {
518
+ const errorMessages: Record<TranslationError["code"], UserFriendlyError> = {
401
519
  MISSING_KEY: {
402
- code: 'MISSING_KEY',
403
- message: '번역 키를 찾을 수 없습니다',
404
- suggestion: '번역 파일에 해당 키가 있는지 확인해주세요',
405
- action: '번역 파일 업데이트',
406
- severity: 'low'
520
+ code: "MISSING_KEY",
521
+ message: "번역 키를 찾을 수 없습니다",
522
+ suggestion: "번역 파일에 해당 키가 있는지 확인해주세요",
523
+ action: "번역 파일 업데이트",
524
+ severity: "low",
407
525
  },
408
526
  LOAD_FAILED: {
409
- code: 'LOAD_FAILED',
410
- message: '번역 파일을 불러오는데 실패했습니다',
411
- suggestion: '네트워크 연결과 파일 경로를 확인해주세요',
412
- action: '재시도',
413
- severity: 'medium'
527
+ code: "LOAD_FAILED",
528
+ message: "번역 파일을 불러오는데 실패했습니다",
529
+ suggestion: "네트워크 연결과 파일 경로를 확인해주세요",
530
+ action: "재시도",
531
+ severity: "medium",
414
532
  },
415
533
  INVALID_KEY: {
416
- code: 'INVALID_KEY',
417
- message: '잘못된 번역 키 형식입니다',
534
+ code: "INVALID_KEY",
535
+ message: "잘못된 번역 키 형식입니다",
418
536
  suggestion: '키 형식을 "namespace.key" 형태로 입력해주세요',
419
- action: '키 형식 수정',
420
- severity: 'low'
537
+ action: "키 형식 수정",
538
+ severity: "low",
421
539
  },
422
540
  NETWORK_ERROR: {
423
- code: 'NETWORK_ERROR',
424
- message: '네트워크 오류가 발생했습니다',
425
- suggestion: '인터넷 연결을 확인하고 다시 시도해주세요',
426
- action: '재시도',
427
- severity: 'high'
541
+ code: "NETWORK_ERROR",
542
+ message: "네트워크 오류가 발생했습니다",
543
+ suggestion: "인터넷 연결을 확인하고 다시 시도해주세요",
544
+ action: "재시도",
545
+ severity: "high",
428
546
  },
429
547
  INITIALIZATION_ERROR: {
430
- code: 'INITIALIZATION_ERROR',
431
- message: '번역 시스템 초기화에 실패했습니다',
432
- suggestion: '설정을 확인하고 페이지를 새로고침해주세요',
433
- action: '페이지 새로고침',
434
- severity: 'critical'
548
+ code: "INITIALIZATION_ERROR",
549
+ message: "번역 시스템 초기화에 실패했습니다",
550
+ suggestion: "설정을 확인하고 페이지를 새로고침해주세요",
551
+ action: "페이지 새로고침",
552
+ severity: "critical",
435
553
  },
436
554
  VALIDATION_ERROR: {
437
- code: 'VALIDATION_ERROR',
438
- message: '설정 검증에 실패했습니다',
439
- suggestion: '번역 설정을 확인해주세요',
440
- action: '설정 수정',
441
- severity: 'medium'
555
+ code: "VALIDATION_ERROR",
556
+ message: "설정 검증에 실패했습니다",
557
+ suggestion: "번역 설정을 확인해주세요",
558
+ action: "설정 수정",
559
+ severity: "medium",
442
560
  },
443
561
  CACHE_ERROR: {
444
- code: 'CACHE_ERROR',
445
- message: '캐시 처리 중 오류가 발생했습니다',
446
- suggestion: '캐시를 초기화하고 다시 시도해주세요',
447
- action: '캐시 초기화',
448
- severity: 'low'
562
+ code: "CACHE_ERROR",
563
+ message: "캐시 처리 중 오류가 발생했습니다",
564
+ suggestion: "캐시를 초기화하고 다시 시도해주세요",
565
+ action: "캐시 초기화",
566
+ severity: "low",
449
567
  },
450
568
  FALLBACK_LOAD_FAILED: {
451
- code: 'FALLBACK_LOAD_FAILED',
452
- message: '폴백 언어 로딩에 실패했습니다',
453
- suggestion: '폴백 언어 파일을 확인해주세요',
454
- action: '폴백 언어 파일 수정',
455
- severity: 'medium'
569
+ code: "FALLBACK_LOAD_FAILED",
570
+ message: "폴백 언어 로딩에 실패했습니다",
571
+ suggestion: "폴백 언어 파일을 확인해주세요",
572
+ action: "폴백 언어 파일 수정",
573
+ severity: "medium",
456
574
  },
457
575
  INITIALIZATION_FAILED: {
458
- code: 'INITIALIZATION_FAILED',
459
- message: '초기화에 실패했습니다',
460
- suggestion: '설정을 확인하고 다시 시도해주세요',
461
- action: '설정 확인',
462
- severity: 'critical'
576
+ code: "INITIALIZATION_FAILED",
577
+ message: "초기화에 실패했습니다",
578
+ suggestion: "설정을 확인하고 다시 시도해주세요",
579
+ action: "설정 확인",
580
+ severity: "critical",
463
581
  },
464
582
  RETRY_FAILED: {
465
- code: 'RETRY_FAILED',
466
- message: '재시도에 실패했습니다',
467
- suggestion: '네트워크 연결을 확인해주세요',
468
- action: '재시도',
469
- severity: 'high'
470
- }
583
+ code: "RETRY_FAILED",
584
+ message: "재시도에 실패했습니다",
585
+ suggestion: "네트워크 연결을 확인해주세요",
586
+ action: "재시도",
587
+ severity: "high",
588
+ },
471
589
  };
472
590
 
473
591
  return errorMessages[error.code];
@@ -475,14 +593,16 @@ export function createUserFriendlyError(error: TranslationError): UserFriendlyEr
475
593
 
476
594
  // 에러 복구 가능 여부 확인
477
595
  export function isRecoverableError(error: TranslationError): boolean {
478
- const recoverableCodes: TranslationError['code'][] = [
479
- 'LOAD_FAILED',
480
- 'NETWORK_ERROR',
481
- 'CACHE_ERROR'
596
+ const recoverableCodes: TranslationError["code"][] = [
597
+ "LOAD_FAILED",
598
+ "NETWORK_ERROR",
599
+ "CACHE_ERROR",
482
600
  ];
483
-
484
- return recoverableCodes.includes(error.code) &&
485
- (error.retryCount || 0) < (error.maxRetries || 3);
601
+
602
+ return (
603
+ recoverableCodes.includes(error.code) &&
604
+ (error.retryCount || 0) < (error.maxRetries || 3)
605
+ );
486
606
  }
487
607
 
488
608
  // 기본 에러 복구 전략
@@ -492,26 +612,33 @@ export const defaultErrorRecoveryStrategy: ErrorRecoveryStrategy = {
492
612
  backoffMultiplier: 2,
493
613
  shouldRetry: isRecoverableError,
494
614
  onRetry: (error: TranslationError, attempt: number) => {
495
- if (process.env.NODE_ENV !== 'production') console.warn(`Retrying translation operation (attempt ${attempt}/${error.maxRetries}):`, error.message);
615
+ if (process.env.NODE_ENV !== "production")
616
+ console.warn(
617
+ `Retrying translation operation (attempt ${attempt}/${error.maxRetries}):`,
618
+ error.message,
619
+ );
496
620
  },
497
621
  onMaxRetriesExceeded: (error: TranslationError) => {
498
- console.error('Max retries exceeded for translation operation:', error.message);
499
- }
622
+ console.error(
623
+ "Max retries exceeded for translation operation:",
624
+ error.message,
625
+ );
626
+ },
500
627
  };
501
628
 
502
629
  // 기본 에러 로깅 설정
503
630
  export const defaultErrorLoggingConfig: ErrorLoggingConfig = {
504
631
  enabled: true,
505
- level: 'error',
632
+ level: "error",
506
633
  includeStack: true,
507
634
  includeContext: true,
508
- customLogger: undefined
635
+ customLogger: undefined,
509
636
  };
510
637
 
511
638
  // 에러 로깅 함수
512
639
  export function logTranslationError(
513
- error: TranslationError,
514
- config: ErrorLoggingConfig = defaultErrorLoggingConfig
640
+ error: TranslationError,
641
+ config: ErrorLoggingConfig = defaultErrorLoggingConfig,
515
642
  ): void {
516
643
  if (!config.enabled) return;
517
644
 
@@ -520,7 +647,7 @@ export function logTranslationError(
520
647
  message: error.message,
521
648
  timestamp: error.timestamp,
522
649
  retryCount: error.retryCount,
523
- maxRetries: error.maxRetries
650
+ maxRetries: error.maxRetries,
524
651
  };
525
652
 
526
653
  if (config.includeContext) {
@@ -538,18 +665,19 @@ export function logTranslationError(
538
665
  config.customLogger(error);
539
666
  } else {
540
667
  switch (config.level) {
541
- case 'error':
542
- console.error('Translation Error:', logData);
668
+ case "error":
669
+ console.error("Translation Error:", logData);
543
670
  break;
544
- case 'warn':
545
- if (process.env.NODE_ENV !== 'production') console.warn('Translation Warning:', logData);
671
+ case "warn":
672
+ if (process.env.NODE_ENV !== "production")
673
+ console.warn("Translation Warning:", logData);
546
674
  break;
547
- case 'info':
548
- console.info('Translation Info:', logData);
675
+ case "info":
676
+ console.info("Translation Info:", logData);
549
677
  break;
550
- case 'debug':
551
- console.debug('Translation Debug:', logData);
678
+ case "debug":
679
+ console.debug("Translation Debug:", logData);
552
680
  break;
553
681
  }
554
682
  }
555
- }
683
+ }