@hua-labs/i18n-core 2.0.5 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/types/index.ts","../src/core/translator.tsx"],"names":["key"],"mappings":"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","file":"server.cjs","sourcesContent":["// ---------------------------------------------------------------------------\n// Plural (ICU / Intl.PluralRules)\n// ---------------------------------------------------------------------------\nexport type PluralCategory = 'zero' | 'one' | 'two' | 'few' | 'many' | 'other';\n\nexport interface PluralValue {\n zero?: string;\n one?: string;\n two?: string;\n few?: string;\n many?: string;\n other: string; // 필수\n}\n\nconst PLURAL_CATEGORIES = new Set<string>(['zero', 'one', 'two', 'few', 'many', 'other']);\n\nexport function isPluralValue(value: unknown): value is PluralValue {\n if (typeof value !== 'object' || value === null || Array.isArray(value)) return false;\n const obj = value as Record<string, unknown>;\n const keys = Object.keys(obj);\n return (\n keys.length > 0 &&\n keys.every(k => PLURAL_CATEGORIES.has(k)) &&\n Object.values(obj).every(v => typeof v === 'string') &&\n typeof obj.other === 'string'\n );\n}\n\n// ---------------------------------------------------------------------------\n// Platform Adapter (cross-platform: Web / React Native / Flutter bridge)\n// ---------------------------------------------------------------------------\n\n/**\n * 플랫폼별 i18n 어댑터 인터페이스.\n *\n * Web(기본), React Native, Flutter 브릿지 등 환경에 맞는 구현체를 주입.\n * I18nConfig.platformAdapter로 전달하면 useI18n이 자동으로 사용한다.\n */\nexport interface I18nPlatformAdapter {\n /** 디바이스/브라우저 언어 코드 (e.g. 'ko', 'en'). 감지 불가 시 undefined */\n getDeviceLanguage(): string | undefined;\n\n /** 시스템 언어 변경 이벤트 구독. 해제 함수 반환 */\n onLanguageChange(cb: (lang: string) => void): () => void;\n}\n\n/**\n * Web 기본 어댑터.\n * navigator.language + CustomEvent 기반. SSR에서는 안전하게 no-op.\n */\nexport const webPlatformAdapter: I18nPlatformAdapter = {\n getDeviceLanguage() {\n if (typeof globalThis !== 'undefined' && typeof navigator !== 'undefined' && navigator.language) {\n return navigator.language.slice(0, 2).toLowerCase();\n }\n return undefined;\n },\n onLanguageChange(cb) {\n if (typeof globalThis === 'undefined' || typeof window === 'undefined' || typeof CustomEvent === 'undefined') {\n return () => {};\n }\n const handler = (e: Event) => {\n const lang = (e as CustomEvent).detail;\n if (typeof lang === 'string') cb(lang);\n };\n window.addEventListener('huaI18nLanguageChange', handler);\n window.addEventListener('i18nLanguageChanged', handler);\n return () => {\n window.removeEventListener('huaI18nLanguageChange', handler);\n window.removeEventListener('i18nLanguageChanged', handler);\n };\n },\n};\n\n/**\n * Headless 어댑터 (SSR / 테스트 / Flutter 브릿지용).\n * 언어 감지·이벤트 없이 config.defaultLanguage만 사용.\n */\nexport const headlessPlatformAdapter: I18nPlatformAdapter = {\n getDeviceLanguage() { return undefined; },\n onLanguageChange() { return () => {}; },\n};\n\n// ---------------------------------------------------------------------------\n// Translation namespace\n// ---------------------------------------------------------------------------\nexport interface TranslationNamespace {\n [key: string]: string | string[] | PluralValue | TranslationNamespace;\n}\n\nexport interface TranslationData {\n [namespace: string]: TranslationNamespace;\n}\n\nexport interface LanguageConfig {\n code: string;\n name: string;\n nativeName: string;\n tone?: 'emotional' | 'encouraging' | 'calm' | 'gentle' | 'formal' | 'technical' | 'informal';\n formality?: 'informal' | 'casual' | 'formal' | 'polite';\n}\n\n// 더 구체적인 설정 타입 정의\nexport interface I18nConfig {\n defaultLanguage: string;\n fallbackLanguage?: string;\n supportedLanguages: LanguageConfig[];\n namespaces?: string[];\n loadTranslations: (language: string, namespace: string) => Promise<TranslationNamespace>;\n // SSR에서 전달된 초기 번역 데이터 (네트워크 요청 없이 사용)\n initialTranslations?: Record<string, Record<string, TranslationNamespace>>;\n // 개발 모드 설정\n debug?: boolean;\n // 번역 키가 없을 때의 동작\n missingKeyHandler?: (key: string, language: string, namespace: string) => string;\n // 번역 로딩 실패 시 동작\n errorHandler?: (error: Error, language: string, namespace: string) => void;\n // 캐시 설정\n cacheOptions?: {\n maxSize?: number;\n ttl?: number; // Time to live in milliseconds\n };\n // 성능 설정\n performanceOptions?: {\n preloadAll?: boolean;\n lazyLoad?: boolean;\n };\n // 에러 처리 설정\n errorHandling?: {\n recoveryStrategy?: ErrorRecoveryStrategy;\n logging?: ErrorLoggingConfig;\n userFriendlyMessages?: boolean;\n suppressErrors?: boolean;\n };\n // 자동 언어 전환 이벤트 처리 (withDefaultConfig용)\n autoLanguageSync?: boolean;\n // 플랫폼 어댑터 (기본: webPlatformAdapter)\n platformAdapter?: I18nPlatformAdapter;\n}\n\n// 에러 타입 정의\nexport interface TranslationError extends Error {\n code: 'MISSING_KEY' | 'LOAD_FAILED' | 'INVALID_KEY' | 'NETWORK_ERROR' | 'INITIALIZATION_ERROR' | 'VALIDATION_ERROR' | 'CACHE_ERROR' | 'FALLBACK_LOAD_FAILED' | 'INITIALIZATION_FAILED' | 'RETRY_FAILED';\n language?: string;\n namespace?: string;\n key?: string;\n originalError?: Error;\n retryCount?: number;\n maxRetries?: number;\n timestamp: number;\n context?: Record<string, unknown>;\n}\n\n// 에러 복구 전략\nexport interface ErrorRecoveryStrategy {\n maxRetries: number;\n retryDelay: number; // milliseconds\n backoffMultiplier: number;\n shouldRetry: (error: TranslationError) => boolean;\n onRetry: (error: TranslationError, attempt: number) => void;\n onMaxRetriesExceeded: (error: TranslationError) => void;\n}\n\n// 에러 로깅 설정\nexport interface ErrorLoggingConfig {\n enabled: boolean;\n level: 'error' | 'warn' | 'info' | 'debug';\n includeStack: boolean;\n includeContext: boolean;\n customLogger?: (error: TranslationError) => void;\n}\n\n// 사용자 친화적 에러 메시지\nexport interface UserFriendlyError {\n code: string;\n message: string;\n suggestion?: string;\n action?: string;\n severity: 'low' | 'medium' | 'high' | 'critical';\n}\n\n// 캐시 엔트리 타입\nexport interface CacheEntry {\n data: TranslationNamespace;\n timestamp: number;\n ttl: number;\n}\n\n// 로딩 상태 타입\nexport interface LoadingState {\n isLoading: boolean;\n error: TranslationError | null;\n progress?: {\n loaded: number;\n total: number;\n };\n}\n\n// 번역 결과 타입\nexport interface TranslationResult {\n text: string;\n language: string;\n namespace: string;\n key: string;\n isFallback: boolean;\n cacheHit: boolean;\n}\n\nexport interface I18nContextType {\n currentLanguage: string;\n setLanguage: (language: string) => void | Promise<void>;\n // 통합 번역 함수: t(key), t(key, language), t(key, params), t(key, params, language)\n t: (key: ResolveStringKey, paramsOrLang?: TranslationParams | string, language?: string) => string;\n // 복수형 번역 함수: tPlural(key, count), tPlural(key, count, params), tPlural(key, count, params, language)\n tPlural: (key: ResolvePluralKey, count: number, params?: Record<string, unknown>, language?: string) => string;\n // 기존 비동기 번역 함수 (하위 호환성)\n tAsync: (key: string, params?: TranslationParams) => Promise<string>;\n // 기존 동기 번역 함수 (하위 호환성)\n tSync: (key: string, namespace?: string, params?: TranslationParams) => string;\n // 원시 값 가져오기 (배열, 객체 포함) — 제네릭으로 타입 캐스팅 가능\n getRawValue: <T = unknown>(key: string, language?: string) => T | undefined;\n // 배열 번역 값 가져오기 (타입 안전)\n tArray: (key: ResolveArrayKey, language?: string) => string[];\n isLoading: boolean;\n error: TranslationError | null;\n supportedLanguages: LanguageConfig[];\n // 초기화 상태\n isInitialized: boolean;\n // 개발자 도구\n debug: {\n getCurrentLanguage: () => string;\n getSupportedLanguages: () => string[];\n getLoadedNamespaces: () => string[];\n getAllTranslations: () => Record<string, Record<string, unknown>>;\n isReady: () => boolean;\n getInitializationError: () => TranslationError | null;\n clearCache: () => void;\n reloadTranslations: () => Promise<void>;\n getCacheStats: () => {\n size: number;\n hits: number;\n misses: number;\n };\n };\n}\n\nexport interface TranslationParams {\n [key: string]: string | number;\n}\n\n/**\n * 타입 안전한 번역 키를 위한 augmentation point.\n *\n * 앱에서 declaration merging으로 좁힐 수 있음:\n * ```ts\n * declare module '@hua-labs/i18n-core' {\n * interface TypedTranslationKeys {\n * stringKey: TranslationStringKey;\n * arrayKey: TranslationArrayKey;\n * }\n * }\n * ```\n *\n * augmentation이 없으면 string으로 fallback (breaking 없음).\n */\n \nexport interface TypedTranslationKeys {}\n\n/** augmentation 시 좁혀진 타입, 미설정 시 string */\nexport type ResolveStringKey = TypedTranslationKeys extends { stringKey: infer K } ? K & string : string;\nexport type ResolveArrayKey = TypedTranslationKeys extends { arrayKey: infer K } ? K & string : string;\nexport type ResolvePluralKey = TypedTranslationKeys extends { pluralKey: infer K } ? K & string : string;\n\n// 타입 안전한 번역 키 시스템 (단순화된 버전)\nexport type TranslationKey<T> = T extends Record<string, unknown>\n ? {\n [K in keyof T]: T[K] extends string\n ? K\n : T[K] extends Record<string, unknown>\n ? `${K & string}.${TranslationKey<T[K]> & string}`\n : never;\n }[keyof T]\n : never;\n\n// 타입 안전한 번역 함수들\nexport interface TypedI18nContextType<T extends TranslationData> extends Omit<I18nContextType, 't' | 'tSync'> {\n // 타입 안전한 번역 함수\n t: <K extends TranslationKey<T>>(key: K, paramsOrLang?: TranslationParams | string, language?: string) => string;\n tSync: <K extends TranslationKey<T>>(key: K, namespace?: string, params?: TranslationParams) => string;\n}\n\n// 간단한 번역 키 타입 (무한 재귀 방지)\nexport type SimpleTranslationKey = string;\n\n// 고급 번역 키 타입 (제한된 깊이)\nexport type TranslationKeyLegacy<T extends Record<string, unknown>, D extends number = 3> = \n [D] extends [never] \n ? never \n : T extends Record<string, unknown>\n ? {\n [K in keyof T]: T[K] extends string \n ? K \n : T[K] extends Record<string, unknown>\n ? `${K & string}.${TranslationKeyLegacy<T[K], Prev<D>> & string}` \n : never;\n }[keyof T]\n : never;\n\ntype Prev<T extends number> = T extends 0 ? never : T extends 1 ? 0 : T extends 2 ? 1 : T extends 3 ? 2 : never;\n\n// 유틸리티 타입들\nexport type ExtractTranslationKeys<T> = T extends Record<string, unknown>\n ? {\n [K in keyof T]: T[K] extends string\n ? K\n : T[K] extends Record<string, unknown>\n ? `${K & string}.${ExtractTranslationKeys<T[K]> & string}`\n : never;\n }[keyof T]\n : never;\n\n// 네임스페이스별 타입 정의를 위한 헬퍼\nexport type NamespaceKeys<T extends TranslationData, N extends keyof T> = ExtractTranslationKeys<T[N]>;\n\n// 타입 안전한 번역 키 생성 헬퍼\nexport const createTranslationKey = <T extends TranslationData, N extends keyof T, K extends NamespaceKeys<T, N>>(\n namespace: N,\n key: K\n): `${N & string}.${K & string}` => `${String(namespace)}.${String(key)}` as `${N & string}.${K & string}`; \n\n// 타입 가드 함수들\nexport function isTranslationNamespace(value: unknown): value is TranslationNamespace {\n return typeof value === 'object' && value !== null && !Array.isArray(value);\n}\n\nexport function isLanguageConfig(value: unknown): value is LanguageConfig {\n return (\n typeof value === 'object' &&\n value !== null &&\n typeof (value as LanguageConfig).code === 'string' &&\n typeof (value as LanguageConfig).name === 'string' &&\n typeof (value as LanguageConfig).nativeName === 'string'\n );\n}\n\nexport function isTranslationError(value: unknown): value is TranslationError {\n return (\n value instanceof Error &&\n typeof (value as TranslationError).code === 'string' &&\n ['MISSING_KEY', 'LOAD_FAILED', 'INVALID_KEY', 'NETWORK_ERROR', 'INITIALIZATION_ERROR'].includes(\n (value as TranslationError).code\n )\n );\n}\n\n// 설정 검증 함수\nexport function validateI18nConfig(config: unknown): config is I18nConfig {\n if (!config || typeof config !== 'object') {\n return false;\n }\n\n const c = config as I18nConfig;\n \n return (\n typeof c.defaultLanguage === 'string' &&\n Array.isArray(c.supportedLanguages) &&\n c.supportedLanguages.every(isLanguageConfig) &&\n typeof c.loadTranslations === 'function'\n );\n}\n\n// 에러 처리 유틸리티 함수들\nexport function createTranslationError(\n code: TranslationError['code'],\n message: string,\n originalError?: Error,\n context?: {\n language?: string;\n namespace?: string;\n key?: string;\n retryCount?: number;\n maxRetries?: number;\n }\n): TranslationError {\n const error = new Error(message) as TranslationError;\n error.code = code;\n error.language = context?.language;\n error.namespace = context?.namespace;\n error.key = context?.key;\n error.originalError = originalError;\n error.retryCount = context?.retryCount || 0;\n error.maxRetries = context?.maxRetries || 3;\n error.timestamp = Date.now();\n error.name = 'TranslationError';\n return error;\n}\n\n// 사용자 친화적 에러 메시지 생성\nexport function createUserFriendlyError(error: TranslationError): UserFriendlyError {\n const errorMessages: Record<TranslationError['code'], UserFriendlyError> = {\n MISSING_KEY: {\n code: 'MISSING_KEY',\n message: '번역 키를 찾을 수 없습니다',\n suggestion: '번역 파일에 해당 키가 있는지 확인해주세요',\n action: '번역 파일 업데이트',\n severity: 'low'\n },\n LOAD_FAILED: {\n code: 'LOAD_FAILED',\n message: '번역 파일을 불러오는데 실패했습니다',\n suggestion: '네트워크 연결과 파일 경로를 확인해주세요',\n action: '재시도',\n severity: 'medium'\n },\n INVALID_KEY: {\n code: 'INVALID_KEY',\n message: '잘못된 번역 키 형식입니다',\n suggestion: '키 형식을 \"namespace.key\" 형태로 입력해주세요',\n action: '키 형식 수정',\n severity: 'low'\n },\n NETWORK_ERROR: {\n code: 'NETWORK_ERROR',\n message: '네트워크 오류가 발생했습니다',\n suggestion: '인터넷 연결을 확인하고 다시 시도해주세요',\n action: '재시도',\n severity: 'high'\n },\n INITIALIZATION_ERROR: {\n code: 'INITIALIZATION_ERROR',\n message: '번역 시스템 초기화에 실패했습니다',\n suggestion: '설정을 확인하고 페이지를 새로고침해주세요',\n action: '페이지 새로고침',\n severity: 'critical'\n },\n VALIDATION_ERROR: {\n code: 'VALIDATION_ERROR',\n message: '설정 검증에 실패했습니다',\n suggestion: '번역 설정을 확인해주세요',\n action: '설정 수정',\n severity: 'medium'\n },\n CACHE_ERROR: {\n code: 'CACHE_ERROR',\n message: '캐시 처리 중 오류가 발생했습니다',\n suggestion: '캐시를 초기화하고 다시 시도해주세요',\n action: '캐시 초기화',\n severity: 'low'\n },\n FALLBACK_LOAD_FAILED: {\n code: 'FALLBACK_LOAD_FAILED',\n message: '폴백 언어 로딩에 실패했습니다',\n suggestion: '폴백 언어 파일을 확인해주세요',\n action: '폴백 언어 파일 수정',\n severity: 'medium'\n },\n INITIALIZATION_FAILED: {\n code: 'INITIALIZATION_FAILED',\n message: '초기화에 실패했습니다',\n suggestion: '설정을 확인하고 다시 시도해주세요',\n action: '설정 확인',\n severity: 'critical'\n },\n RETRY_FAILED: {\n code: 'RETRY_FAILED',\n message: '재시도에 실패했습니다',\n suggestion: '네트워크 연결을 확인해주세요',\n action: '재시도',\n severity: 'high'\n }\n };\n\n return errorMessages[error.code];\n}\n\n// 에러 복구 가능 여부 확인\nexport function isRecoverableError(error: TranslationError): boolean {\n const recoverableCodes: TranslationError['code'][] = [\n 'LOAD_FAILED',\n 'NETWORK_ERROR',\n 'CACHE_ERROR'\n ];\n \n return recoverableCodes.includes(error.code) && \n (error.retryCount || 0) < (error.maxRetries || 3);\n}\n\n// 기본 에러 복구 전략\nexport const defaultErrorRecoveryStrategy: ErrorRecoveryStrategy = {\n maxRetries: 3,\n retryDelay: 1000,\n backoffMultiplier: 2,\n shouldRetry: isRecoverableError,\n onRetry: (error: TranslationError, attempt: number) => {\n if (process.env.NODE_ENV !== 'production') console.warn(`Retrying translation operation (attempt ${attempt}/${error.maxRetries}):`, error.message);\n },\n onMaxRetriesExceeded: (error: TranslationError) => {\n console.error('Max retries exceeded for translation operation:', error.message);\n }\n};\n\n// 기본 에러 로깅 설정\nexport const defaultErrorLoggingConfig: ErrorLoggingConfig = {\n enabled: true,\n level: 'error',\n includeStack: true,\n includeContext: true,\n customLogger: undefined\n};\n\n// 에러 로깅 함수\nexport function logTranslationError(\n error: TranslationError, \n config: ErrorLoggingConfig = defaultErrorLoggingConfig\n): void {\n if (!config.enabled) return;\n\n const logData: Record<string, unknown> = {\n code: error.code,\n message: error.message,\n timestamp: error.timestamp,\n retryCount: error.retryCount,\n maxRetries: error.maxRetries\n };\n\n if (config.includeContext) {\n logData.language = error.language;\n logData.namespace = error.namespace;\n logData.key = error.key;\n logData.context = error.context;\n }\n\n if (config.includeStack && error.stack) {\n logData.stack = error.stack;\n }\n\n if (config.customLogger) {\n config.customLogger(error);\n } else {\n switch (config.level) {\n case 'error':\n console.error('Translation Error:', logData);\n break;\n case 'warn':\n if (process.env.NODE_ENV !== 'production') console.warn('Translation Warning:', logData);\n break;\n case 'info':\n console.info('Translation Info:', logData);\n break;\n case 'debug':\n console.debug('Translation Debug:', logData);\n break;\n }\n }\n} ","import {\n I18nConfig,\n TranslationNamespace,\n TranslationError,\n CacheEntry,\n TranslationResult,\n isTranslationNamespace,\n validateI18nConfig,\n createTranslationError,\n logTranslationError,\n defaultErrorRecoveryStrategy,\n defaultErrorLoggingConfig,\n isRecoverableError,\n isPluralValue,\n PluralCategory\n} from '../types';\n\nexport interface TranslatorInterface {\n translate(key: string, paramsOrLang?: Record<string, unknown> | string, language?: string): string;\n tPlural(key: string, count: number, params?: Record<string, unknown>, language?: string): string;\n setLanguage(lang: string): void;\n getCurrentLanguage(): string;\n initialize(): Promise<void>;\n isReady(): boolean;\n debug(): unknown;\n getRawValue<T = unknown>(key: string, language?: string): T | undefined;\n tArray(key: string, language?: string): string[];\n}\n\nexport class Translator implements TranslatorInterface {\n private cache = new Map<string, CacheEntry>();\n private pluralRulesCache = new Map<string, Intl.PluralRules>();\n private loadedNamespaces = new Set<string>();\n private loadingPromises = new Map<string, Promise<TranslationNamespace>>();\n private allTranslations: Record<string, Record<string, TranslationNamespace>> = {};\n private isInitialized = false;\n private initializationError: TranslationError | null = null;\n private config: I18nConfig;\n private currentLang: string = 'en';\n private cacheStats = {\n hits: 0,\n misses: 0,\n };\n // 번역 로드 완료 시 React 리렌더링을 위한 콜백\n private onTranslationLoadedCallbacks: Set<() => void> = new Set();\n // 언어 변경 시 React 리렌더링을 위한 콜백\n private onLanguageChangedCallbacks: Set<(language: string) => void> = new Set();\n // 디바운싱을 위한 타이머\n private notifyTimer: ReturnType<typeof setTimeout> | null = null;\n // 최근 알림한 네임스페이스 (중복 알림 방지)\n private recentlyNotified = new Set<string>();\n \n /**\n * 번역 로드 완료 콜백 등록\n */\n onTranslationLoaded(callback: () => void): () => void {\n this.onTranslationLoadedCallbacks.add(callback);\n return () => {\n this.onTranslationLoadedCallbacks.delete(callback);\n };\n }\n\n /**\n * 언어 변경 콜백 등록\n */\n onLanguageChanged(callback: (language: string) => void): () => void {\n this.onLanguageChangedCallbacks.add(callback);\n return () => {\n this.onLanguageChangedCallbacks.delete(callback);\n };\n }\n\n /**\n * 언어 변경 이벤트 발생\n */\n private notifyLanguageChanged(language: string): void {\n this.onLanguageChangedCallbacks.forEach(callback => {\n try {\n callback(language);\n } catch (error) {\n if (this.config.debug) {\n console.error('Error in language changed callback:', error);\n }\n }\n });\n }\n \n /**\n * 번역 로드 완료 이벤트 발생 (디바운싱 적용)\n */\n private notifyTranslationLoaded(language: string, namespace: string): void {\n const cacheKey = `${language}:${namespace}`;\n \n // 최근에 알림한 네임스페이스는 스킵 (중복 알림 방지)\n if (this.recentlyNotified.has(cacheKey)) {\n return;\n }\n \n this.recentlyNotified.add(cacheKey);\n \n // 디바운싱: 짧은 시간 내 여러 번역이 로드되면 한 번만 알림\n if (this.notifyTimer) {\n clearTimeout(this.notifyTimer);\n }\n \n this.notifyTimer = setTimeout(() => {\n this.onTranslationLoadedCallbacks.forEach(callback => {\n try {\n callback();\n } catch (error) {\n if (this.config.debug) {\n console.warn('Error in translation loaded callback:', error);\n }\n }\n });\n \n // 100ms 후 recentlyNotified 초기화 (같은 네임스페이스도 다시 알림 가능하도록)\n setTimeout(() => {\n this.recentlyNotified.clear();\n }, 100);\n \n this.notifyTimer = null;\n }, 50); // 50ms 디바운싱\n }\n\n constructor(config: I18nConfig) {\n if (!validateI18nConfig(config)) {\n throw new Error('Invalid I18nConfig provided');\n }\n\n this.config = {\n fallbackLanguage: 'en',\n namespaces: ['common'],\n debug: false,\n missingKeyHandler: (key: string) => key,\n errorHandler: (error: Error) => {\n // Silent by default, user can override\n },\n ...config\n };\n this.currentLang = config.defaultLanguage;\n \n // SSR에서 전달된 초기 번역 데이터가 있으면 즉시 설정 (네트워크 요청 없음)\n if (config.initialTranslations) {\n this.allTranslations = config.initialTranslations;\n // 로드된 네임스페이스 마킹\n for (const [language, namespaces] of Object.entries(config.initialTranslations)) {\n for (const namespace of Object.keys(namespaces)) {\n this.loadedNamespaces.add(`${language}:${namespace}`);\n }\n }\n // initialTranslations가 있으면 초기화 완료로 간주 (SSR에서 이미 로드됨)\n // 이렇게 하면 초기화 전 상태에서도 번역을 사용할 수 있음\n this.isInitialized = true;\n }\n }\n\n /**\n * 모든 번역 데이터를 미리 로드 (hua-api 스타일)\n */\n async initialize(): Promise<void> {\n if (this.isInitialized) {\n if (this.config.debug) {\n console.log('🚫 [TRANSLATOR] Already initialized, skipping');\n }\n return;\n }\n\n if (this.config.debug) {\n console.log('🚀 [TRANSLATOR] Starting initialization...');\n }\n\n try {\n // Ensure allTranslations is initialized\n if (!this.allTranslations) {\n this.allTranslations = {};\n }\n\n const languages = [this.currentLang];\n if (this.config.fallbackLanguage && this.config.fallbackLanguage !== this.currentLang) {\n languages.push(this.config.fallbackLanguage);\n }\n \n // 초기 번역 데이터가 이미 있으면 해당 네임스페이스는 스킵\n const skipNamespaces = new Set<string>();\n for (const language of languages) {\n if (this.allTranslations[language]) {\n for (const namespace of Object.keys(this.allTranslations[language])) {\n skipNamespaces.add(`${language}:${namespace}`);\n }\n }\n }\n\n if (this.config.debug) {\n console.log('🌍 [TRANSLATOR] Initializing translator with languages:', languages);\n console.log('📍 [TRANSLATOR] Current language:', this.currentLang);\n console.log('📦 [TRANSLATOR] Config namespaces:', this.config.namespaces);\n }\n\n for (const language of languages) {\n if (this.config.debug) {\n console.log('Processing language:', language);\n }\n\n if (!this.allTranslations[language]) {\n this.allTranslations[language] = {};\n }\n\n for (const namespace of this.config.namespaces || []) {\n const cacheKey = `${language}:${namespace}`;\n \n // 이미 초기 번역 데이터가 있으면 스킵 (네트워크 요청 없음)\n if (skipNamespaces.has(cacheKey)) {\n if (this.config.debug) {\n console.log('⏭️ [TRANSLATOR] Skipping', namespace, 'for', language, '(already loaded from SSR)');\n }\n continue;\n }\n \n if (this.config.debug) {\n console.log('Loading namespace:', namespace, 'for language:', language);\n }\n\n try {\n const data = await this.safeLoadTranslations(language, namespace);\n\n if (this.config.debug) {\n console.log('Loaded data for', language, namespace, ':', data);\n }\n\n this.allTranslations[language][namespace] = data;\n this.loadedNamespaces.add(`${language}:${namespace}`);\n\n } catch (error) {\n const translationError = this.createTranslationError(\n 'LOAD_FAILED',\n error as Error,\n language,\n namespace\n );\n\n this.logError(translationError);\n\n // 복구 가능한 에러인지 확인\n if (isRecoverableError(translationError)) {\n // 폴백 언어로 시도\n if (language !== this.config.fallbackLanguage) {\n try {\n const fallbackData = await this.safeLoadTranslations(this.config.fallbackLanguage || 'en', namespace);\n this.allTranslations[language][namespace] = fallbackData;\n this.loadedNamespaces.add(`${language}:${namespace}`);\n\n if (this.config.debug) {\n console.log('Using fallback data for', language, namespace);\n }\n } catch (fallbackError) {\n const fallbackTranslationError = this.createTranslationError(\n 'FALLBACK_LOAD_FAILED',\n fallbackError as Error,\n this.config.fallbackLanguage,\n namespace\n );\n\n this.logError(fallbackTranslationError);\n\n // 기본 번역 데이터 사용\n this.allTranslations[language][namespace] = {};\n }\n } else {\n // 기본 번역 데이터 사용\n this.allTranslations[language][namespace] = {};\n }\n } else {\n // 복구 불가능한 에러는 기본 번역 데이터 사용\n this.allTranslations[language][namespace] = {};\n }\n }\n }\n }\n\n this.isInitialized = true;\n\n if (this.config.debug) {\n console.log('Translator initialized successfully');\n console.log('Loaded translations:', this.allTranslations);\n }\n\n } catch (error) {\n this.initializationError = this.createTranslationError(\n 'INITIALIZATION_FAILED',\n error as Error\n );\n\n this.logError(this.initializationError);\n\n // 에러가 발생해도 초기화 완료로 표시 (기본 번역 사용)\n this.isInitialized = true;\n\n if (this.config.debug) {\n console.warn('Translator initialized with errors, using fallback translations');\n }\n }\n }\n\n /**\n * 초기화되지 않은 상태에서 번역 시도\n */\n private translateBeforeInitialized(key: string, targetLang: string): string {\n if (this.config.debug) {\n console.warn('Translator not initialized. Call initialize() first.');\n }\n \n // 초기화되지 않았을 때도 기본 번역 시도 (initialTranslations 사용)\n const { namespace, key: actualKey } = this.parseKey(key);\n \n // findInNamespace를 사용하여 중첩 키도 처리\n const result = this.findInNamespace(namespace, actualKey, targetLang);\n if (result) {\n if (this.config.debug) {\n console.log(`✅ [TRANSLATOR] Found fallback translation from initialTranslations:`, result);\n }\n return result;\n }\n \n if (this.config.debug) {\n const translations = this.allTranslations[targetLang]?.[namespace];\n console.log(`🔍 [TRANSLATOR] Not initialized, fallback failed:`, {\n namespace,\n actualKey,\n hasTranslations: !!translations,\n translationsKeys: translations ? Object.keys(translations) : []\n });\n }\n return this.config.missingKeyHandler?.(key, targetLang, 'default') || key;\n }\n\n /**\n * 다른 로드된 언어에서 번역 찾기 (언어 변경 중 깜빡임 방지)\n */\n private findInOtherLanguages(namespace: string, key: string, targetLang: string): string | null {\n if (!this.allTranslations || Object.keys(this.allTranslations).length === 0) {\n return null;\n }\n\n const loadedLanguages = Object.keys(this.allTranslations);\n for (const lang of loadedLanguages) {\n if (lang !== targetLang) {\n const result = this.findInNamespace(namespace, key, lang);\n if (result) {\n return result;\n }\n }\n }\n \n return null;\n }\n\n /**\n * 폴백 언어에서 번역 찾기\n */\n private findInFallbackLanguage(namespace: string, key: string, targetLang: string): string | null {\n const fallbackLang = this.config.fallbackLanguage || 'en';\n if (targetLang === fallbackLang) {\n return null;\n }\n\n const result = this.findInNamespace(namespace, key, fallbackLang);\n if (result) {\n this.cacheStats.hits++;\n return result;\n }\n \n return null;\n }\n\n /**\n * 번역 키를 번역된 텍스트로 변환\n */\n translate(key: string, paramsOrLang?: Record<string, unknown> | string, language?: string): string {\n // 두 번째 인자 타입으로 분기\n let params: Record<string, unknown> | undefined;\n let targetLang: string;\n if (typeof paramsOrLang === 'string') {\n targetLang = paramsOrLang;\n } else if (typeof paramsOrLang === 'object' && paramsOrLang !== null) {\n params = paramsOrLang;\n targetLang = language || this.currentLang;\n } else {\n targetLang = this.currentLang;\n }\n\n // 초기화되지 않은 경우 처리\n if (!this.isInitialized) {\n const raw = this.translateBeforeInitialized(key, targetLang);\n return params ? this.interpolate(raw, params) : raw;\n }\n\n const { namespace, key: actualKey } = this.parseKey(key);\n\n // 1단계: 현재 언어에서 찾기\n let result: string | null = this.findInNamespace(namespace, actualKey, targetLang);\n if (result) {\n this.cacheStats.hits++;\n return params ? this.interpolate(result, params) : result;\n }\n\n // 2단계: 다른 로드된 언어에서 찾기 (언어 변경 중 깜빡임 방지)\n result = this.findInOtherLanguages(namespace, actualKey, targetLang);\n if (result) {\n return params ? this.interpolate(result, params) : result;\n }\n\n // 3단계: 폴백 언어에서 찾기\n result = this.findInFallbackLanguage(namespace, actualKey, targetLang);\n if (result) {\n return params ? this.interpolate(result, params) : result;\n }\n\n // 모든 단계에서 찾지 못한 경우\n this.cacheStats.misses++;\n\n if (this.config.debug) {\n const missing = this.config.missingKeyHandler?.(key, targetLang, namespace) || key;\n return params ? this.interpolate(missing, params) : missing;\n }\n\n // 프로덕션에서는 빈 문자열 반환 (미싱 키 노출 방지)\n return '';\n }\n\n /**\n * 네임스페이스에서 키 찾기\n */\n private findInNamespace(namespace: string, key: string, language: string): string {\n const translations = this.allTranslations[language]?.[namespace];\n\n if (!translations) {\n // 네임스페이스가 없으면 자동으로 로드 시도 (비동기, 백그라운드)\n const cacheKey = `${language}:${namespace}`;\n if (!this.loadedNamespaces.has(cacheKey) && !this.loadingPromises.has(cacheKey)) {\n // 로딩 시작 (비동기, 즉시 반환하지 않음)\n this.loadTranslationData(language, namespace).catch(error => {\n if (this.config.debug) {\n console.warn(`⚠️ [TRANSLATOR] Auto-load failed for ${language}/${namespace}:`, error);\n }\n });\n \n // 디버그 모드에서만 첫 시도 시에만 경고 출력 (중복 방지)\n if (this.config.debug) {\n console.warn(`❌ [TRANSLATOR] No translations found for ${language}/${namespace}, attempting auto-load...`);\n }\n }\n return '';\n }\n\n // 직접 키 매칭\n const directValue = translations[key];\n if (this.isStringValue(directValue)) {\n return directValue;\n }\n if (this.isStringArray(directValue)) {\n return directValue[Math.floor(Math.random() * directValue.length)];\n }\n\n // 중첩 키 매칭 (예: \"user.profile.name\")\n const nestedValue = this.getNestedValue(translations, key);\n if (this.isStringValue(nestedValue)) {\n return nestedValue;\n }\n if (this.isStringArray(nestedValue)) {\n return nestedValue[Math.floor(Math.random() * nestedValue.length)];\n }\n\n if (this.config.debug) {\n console.warn(`❌ [TRANSLATOR] No match found for key: ${key} in ${language}/${namespace}`);\n }\n return '';\n }\n\n /**\n * 중첩된 객체에서 값을 가져오기\n * 배열도 지원: 최종 값이 string[]이면 그대로 반환\n */\n private getNestedValue(obj: unknown, path: string): unknown {\n if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {\n return undefined;\n }\n\n return path.split('.').reduce((current: unknown, key: string) => {\n if (current == null) return undefined;\n if (Array.isArray(current)) {\n const idx = Number(key);\n return Number.isInteger(idx) ? current[idx] : undefined;\n }\n if (typeof current === 'object' && key in (current as Record<string, unknown>)) {\n return (current as Record<string, unknown>)[key];\n }\n return undefined;\n }, obj);\n }\n \n /**\n * 문자열 값인지 확인하는 타입 가드\n */\n private isStringValue(value: unknown): value is string {\n return typeof value === 'string' && value.length > 0;\n }\n\n /**\n * string[] 배열인지 확인하는 타입 가드\n * 배열 값이 t()에 전달되면 랜덤으로 하나를 선택하여 반환\n */\n private isStringArray(value: unknown): value is string[] {\n return Array.isArray(value) && value.length > 0 && value.every(v => typeof v === 'string');\n }\n\n /**\n * 원시 값 가져오기 (배열, 객체 포함)\n */\n getRawValue<T = unknown>(key: string, language?: string): T | undefined {\n const targetLang = language || this.currentLang;\n\n if (!this.isInitialized) {\n if (this.config.debug) {\n console.warn('Translator not initialized. Call initialize() first.');\n }\n return undefined;\n }\n\n const { namespace, key: actualKey } = this.parseKey(key);\n const translations = this.allTranslations[targetLang]?.[namespace];\n\n if (!translations) {\n return undefined;\n }\n\n // 직접 키 매칭\n if (actualKey in translations) {\n return translations[actualKey] as T;\n }\n\n // 중첩 키 매칭\n const nestedValue = this.getNestedValue(translations, actualKey);\n if (nestedValue !== undefined) {\n return nestedValue as T;\n }\n\n // 폴백 언어에서 찾기\n if (targetLang !== this.config.fallbackLanguage) {\n const fallbackTranslations = this.allTranslations[this.config.fallbackLanguage || 'en']?.[namespace];\n if (fallbackTranslations) {\n if (actualKey in fallbackTranslations) {\n return fallbackTranslations[actualKey] as T;\n }\n const fallbackNestedValue = this.getNestedValue(fallbackTranslations, actualKey);\n if (fallbackNestedValue !== undefined) {\n return fallbackNestedValue as T;\n }\n }\n }\n\n return undefined;\n }\n\n /**\n * 배열 번역 값 가져오기 (타입 안전)\n */\n tArray(key: string, language?: string): string[] {\n const raw = this.getRawValue(key, language);\n if (Array.isArray(raw) && raw.every((v: unknown) => typeof v === 'string')) {\n return raw as string[];\n }\n if (process.env.NODE_ENV === 'development') {\n console.warn(`tArray: \"${key}\" is not a string array`);\n }\n return [];\n }\n\n /**\n * Intl.PluralRules 인스턴스 (언어별 캐시)\n */\n private getPluralRules(language: string): Intl.PluralRules {\n let rules = this.pluralRulesCache.get(language);\n if (!rules) {\n rules = new Intl.PluralRules(language);\n this.pluralRulesCache.set(language, rules);\n }\n return rules;\n }\n\n /**\n * 복수형 번역 (ICU / Intl.PluralRules 기반)\n *\n * JSON: { \"other\": \"총 {count}개\" } (ko)\n * { \"one\": \"{count} item\", \"other\": \"{count} items\" } (en)\n *\n * tPlural('common:total_count', 1) → en: \"1 item\" / ko: \"총 1개\"\n * tPlural('common:total_count', 5) → en: \"5 items\" / ko: \"총 5개\"\n */\n tPlural(key: string, count: number, params?: Record<string, unknown>, language?: string): string {\n const targetLang = language || this.currentLang;\n const raw = this.getRawValue(key, targetLang);\n const mergedParams: Record<string, unknown> = { count, ...params };\n\n // PluralValue 객체인 경우: Intl.PluralRules로 카테고리 결정\n if (isPluralValue(raw)) {\n const category = this.getPluralRules(targetLang).select(count) as PluralCategory;\n const text = raw[category] ?? raw.other;\n return this.interpolate(text, mergedParams);\n }\n\n // fallback: plain string이면 interpolate만\n if (typeof raw === 'string') {\n return this.interpolate(raw, mergedParams);\n }\n\n // 키를 찾지 못한 경우\n if (this.config.debug) {\n return this.interpolate(key, mergedParams);\n }\n return '';\n }\n\n /**\n * 매개변수 보간\n *\n * 지원 형식:\n * - {key} - 단일 중괄호 (일반적인 i18n 형식)\n * - {{key}} - 이중 중괄호 (하위 호환성)\n */\n private interpolate(text: string, params: Record<string, unknown>): string {\n // 단일 중괄호 {key} 또는 이중 중괄호 {{key}} 모두 지원\n return text.replace(/\\{\\{?(\\w+)\\}?\\}/g, (match, key) => {\n const value = params[key];\n return value !== undefined ? String(value) : match;\n });\n }\n\n /**\n * 언어 설정\n */\n setLanguage(language: string): void {\n if (this.currentLang === language) {\n return;\n }\n\n const previousLanguage = this.currentLang;\n this.currentLang = language;\n\n // 언어 변경 이벤트 발생\n this.notifyLanguageChanged(language);\n\n // 새로운 언어의 데이터가 로드되지 않았다면 로드\n if (!this.allTranslations[language]) {\n this.loadLanguageData(language).catch(error => {\n if (this.config.debug) {\n console.warn('Failed to load language data:', error);\n }\n });\n }\n\n if (this.config.debug) {\n console.log(`🌐 [TRANSLATOR] Language changed: ${previousLanguage} -> ${language}`);\n }\n }\n\n /**\n * 언어 데이터 로드\n */\n private async loadLanguageData(language: string): Promise<void> {\n if (!this.allTranslations[language]) {\n this.allTranslations[language] = {};\n }\n\n for (const namespace of this.config.namespaces || []) {\n try {\n const data = await this.safeLoadTranslations(language, namespace);\n this.allTranslations[language][namespace] = data;\n this.loadedNamespaces.add(`${language}:${namespace}`);\n \n // 언어 변경 시 번역 로드 완료 알림\n this.notifyTranslationLoaded(language, namespace);\n } catch (error) {\n const translationError = this.createTranslationError(\n 'LOAD_FAILED',\n error as Error,\n language,\n namespace\n );\n\n this.logError(translationError);\n\n // 복구 가능한 에러인지 확인\n if (isRecoverableError(translationError)) {\n // 재시도는 safeLoadTranslations 내부에서 처리되므로 여기서는 기본값 사용\n this.allTranslations[language][namespace] = {};\n } else {\n // 복구 불가능한 에러는 기본 번역 데이터 사용\n this.allTranslations[language][namespace] = {};\n }\n }\n }\n }\n\n /**\n * 현재 언어 가져오기\n */\n getCurrentLanguage(): string {\n return this.currentLang;\n }\n\n /**\n * 지원되는 언어 목록 가져오기\n */\n getSupportedLanguages(): string[] {\n return this.config.supportedLanguages?.map(lang => lang.code) || [];\n }\n\n /**\n * 초기화 완료 여부 확인\n */\n isReady(): boolean {\n return this.isInitialized && !this.initializationError;\n }\n\n /**\n * 초기화 오류 가져오기\n */\n getInitializationError(): TranslationError | null {\n return this.initializationError;\n }\n\n /**\n * 캐시 클리어\n */\n clearCache(): void {\n this.cache.clear();\n this.cacheStats = { hits: 0, misses: 0 };\n\n if (this.config.debug) {\n console.log('Cache cleared');\n }\n }\n\n /**\n * 캐시 엔트리 설정\n */\n private setCacheEntry(key: string, data: TranslationNamespace): void {\n this.cache.set(key, {\n data,\n timestamp: Date.now(),\n ttl: 5 * 60 * 1000 // 5분\n });\n }\n\n /**\n * 캐시 엔트리 가져오기\n */\n private getCacheEntry(key: string): TranslationNamespace | null {\n const entry = this.cache.get(key);\n\n if (!entry) {\n return null;\n }\n\n // TTL 체크\n if (Date.now() - entry.timestamp > entry.ttl) {\n this.cache.delete(key);\n return null;\n }\n\n return entry.data;\n }\n\n /**\n * 번역 오류 생성\n */\n private createTranslationError(\n code: TranslationError['code'],\n originalError: Error,\n language?: string,\n namespace?: string,\n key?: string\n ): TranslationError {\n return {\n name: 'TranslationError',\n code,\n message: originalError.message,\n originalError,\n language,\n namespace,\n key,\n timestamp: Date.now(),\n stack: originalError.stack\n };\n }\n\n /**\n * 오류 로깅\n */\n private logError(error: TranslationError): void {\n if (this.config.errorHandler) {\n this.config.errorHandler(error, error.language || '', error.namespace || '');\n }\n }\n\n /**\n * 재시도 작업\n */\n private async retryOperation<T>(\n operation: () => Promise<T>,\n error: TranslationError,\n context: { language?: string; namespace?: string; key?: string }\n ): Promise<T> {\n const maxRetries = 3;\n let lastError = error;\n\n for (let attempt = 1; attempt <= maxRetries; attempt++) {\n try {\n return await operation();\n } catch (retryError) {\n lastError = this.createTranslationError(\n 'RETRY_FAILED',\n retryError as Error,\n context.language,\n context.namespace,\n context.key\n );\n\n if (attempt === maxRetries) {\n break;\n }\n\n // 지수 백오프\n await new Promise(resolve => setTimeout(resolve, Math.pow(2, attempt) * 1000));\n }\n }\n\n throw lastError;\n }\n\n /**\n * 안전한 번역 로드\n */\n private async safeLoadTranslations(language: string, namespace: string): Promise<TranslationNamespace> {\n if (this.config.debug) {\n console.log(`📥 [TRANSLATOR] safeLoadTranslations called:`, { language, namespace });\n }\n\n const loadOperation = async (): Promise<TranslationNamespace> => {\n if (!this.config.loadTranslations) {\n throw new Error('No translation loader configured');\n }\n\n if (this.config.debug) {\n console.log(`🔄 [TRANSLATOR] Calling loadTranslations for:`, { language, namespace });\n }\n\n const data = await this.config.loadTranslations(language, namespace);\n\n if (this.config.debug) {\n console.log(`📦 [TRANSLATOR] loadTranslations returned:`, data);\n }\n\n if (!isTranslationNamespace(data)) {\n throw new Error(`Invalid translation data for ${language}:${namespace}`);\n }\n\n return data;\n };\n\n try {\n return await loadOperation();\n } catch (error) {\n const translationError = this.createTranslationError(\n 'LOAD_FAILED',\n error as Error,\n language,\n namespace\n );\n\n return this.retryOperation(loadOperation, translationError, { language, namespace });\n }\n }\n\n /**\n * 디버그 정보\n */\n debug() {\n return {\n isInitialized: this.isInitialized,\n currentLanguage: this.currentLang,\n loadedNamespaces: Array.from(this.loadedNamespaces),\n cacheStats: this.cacheStats,\n cacheSize: this.cache.size,\n allTranslations: this.allTranslations,\n initializationError: this.initializationError,\n config: this.config\n };\n }\n\n /**\n * SSR에서 하이드레이션\n */\n hydrateFromSSR(translations: Record<string, Record<string, TranslationNamespace>>): void {\n this.allTranslations = translations;\n this.isInitialized = true;\n\n // 로드된 네임스페이스 업데이트\n for (const [language, namespaces] of Object.entries(translations)) {\n for (const namespace of Object.keys(namespaces)) {\n this.loadedNamespaces.add(`${language}:${namespace}`);\n }\n }\n }\n\n /**\n * 비동기 번역 (고급 기능)\n */\n async translateAsync(key: string, params?: Record<string, unknown>): Promise<string> {\n if (!this.isInitialized) {\n await this.initialize();\n }\n\n const translated = this.translate(key);\n\n if (!params) {\n return translated;\n }\n\n return this.interpolate(translated, params);\n }\n\n /**\n * 동기 번역 (고급 기능)\n */\n translateSync(key: string, params?: Record<string, unknown>): string {\n if (!this.isInitialized) {\n if (this.config.debug) {\n console.warn('Translator not initialized for sync translation');\n }\n const { namespace } = this.parseKey(key);\n return this.config.missingKeyHandler?.(key, this.currentLang, namespace) || key;\n }\n\n const translated = this.translate(key);\n\n if (!params) {\n return translated;\n }\n\n return this.interpolate(translated, params);\n }\n\n /**\n * 키 파싱 (네임스페이스:키 형식)\n * \n * - 콜론(:)만 네임스페이스 구분자로 사용\n * - 점(.)은 키 이름의 일부로 취급 (중첩 객체 접근용)\n * \n * @example\n * parseKey(\"home:hero.badge\") → { namespace: \"home\", key: \"hero.badge\" }\n * parseKey(\"hero.badge\") → { namespace: \"common\", key: \"hero.badge\" }\n * parseKey(\"save\") → { namespace: \"common\", key: \"save\" }\n */\n private parseKey(key: string): { namespace: string; key: string } {\n // 콜론(:)만 네임스페이스 구분자로 사용\n const colonIndex = key.indexOf(':');\n if (colonIndex !== -1) {\n return { namespace: key.substring(0, colonIndex), key: key.substring(colonIndex + 1) };\n }\n\n // 콜론이 없으면 common 네임스페이스로 간주\n // 점(.)은 키 이름의 일부 (중첩 객체 접근은 getNestedValue에서 처리)\n return { namespace: 'common', key };\n }\n\n /**\n * 번역 데이터 로드 (고급 기능)\n */\n private async loadTranslationData(language: string, namespace: string): Promise<TranslationNamespace> {\n const cacheKey = `${language}:${namespace}`;\n\n // 이미 로드된 네임스페이스인지 확인\n if (this.loadedNamespaces.has(cacheKey)) {\n const existing = this.allTranslations[language]?.[namespace];\n if (existing) {\n return existing;\n }\n }\n\n // 캐시에서 확인\n const cached = this.getCacheEntry(cacheKey);\n if (cached) {\n // 캐시에 있으면 allTranslations에도 저장\n if (!this.allTranslations[language]) {\n this.allTranslations[language] = {};\n }\n this.allTranslations[language][namespace] = cached;\n this.loadedNamespaces.add(cacheKey);\n return cached;\n }\n\n // 로딩 중인지 확인\n const loadingPromise = this.loadingPromises.get(cacheKey);\n if (loadingPromise) {\n return loadingPromise;\n }\n\n // 새로 로드\n const loadPromise = this._loadTranslationData(language, namespace);\n this.loadingPromises.set(cacheKey, loadPromise);\n\n try {\n const data = await loadPromise;\n \n // allTranslations에 저장 (중요: 이렇게 해야 findInNamespace에서 찾을 수 있음)\n if (!this.allTranslations[language]) {\n this.allTranslations[language] = {};\n }\n this.allTranslations[language][namespace] = data;\n this.loadedNamespaces.add(cacheKey);\n \n // 캐시에도 저장\n this.setCacheEntry(cacheKey, data);\n \n if (this.config.debug) {\n console.log(`✅ [TRANSLATOR] Auto-loaded and saved ${language}/${namespace}`);\n }\n \n // React 리렌더링 트리거 (디바운싱 적용)\n this.notifyTranslationLoaded(language, namespace);\n \n return data;\n } finally {\n this.loadingPromises.delete(cacheKey);\n }\n }\n\n /**\n * 실제 번역 데이터 로드\n */\n private async _loadTranslationData(language: string, namespace: string): Promise<TranslationNamespace> {\n if (!this.config.loadTranslations) {\n throw new Error('No translation loader configured');\n }\n\n try {\n const data = await this.config.loadTranslations(language, namespace);\n\n if (!isTranslationNamespace(data)) {\n throw new Error(`Invalid translation data for ${language}:${namespace}`);\n }\n\n return data;\n } catch (error) {\n const translationError = this.createTranslationError(\n 'LOAD_FAILED',\n error as Error,\n language,\n namespace\n );\n\n this.logError(translationError);\n\n // 기본 번역 데이터 반환\n return {};\n }\n }\n}\n\n// SSR 번역 함수들\nexport function ssrTranslate({\n translations,\n key,\n language = 'ko',\n fallbackLanguage = 'en',\n missingKeyHandler = (key: string) => key\n}: {\n translations: Record<string, Record<string, TranslationNamespace>>;\n key: string;\n language?: string;\n fallbackLanguage?: string;\n missingKeyHandler?: (key: string) => string;\n}): string {\n const { namespace, key: actualKey } = parseKey(key);\n\n // 현재 언어에서 찾기\n let result = ssrFindInNamespace(translations, namespace, actualKey, language, fallbackLanguage, missingKeyHandler);\n\n if (result) {\n return result;\n }\n\n // 폴백 언어에서 찾기\n if (language !== fallbackLanguage) {\n result = ssrFindInNamespace(translations, namespace, actualKey, fallbackLanguage, fallbackLanguage, missingKeyHandler);\n if (result) {\n return result;\n }\n }\n\n return missingKeyHandler(key);\n}\n\nfunction ssrFindInNamespace(\n translations: Record<string, Record<string, TranslationNamespace>>,\n namespace: string,\n key: string,\n language: string,\n fallbackLanguage: string,\n missingKeyHandler: (key: string) => string\n): string {\n const namespaceData = translations[language]?.[namespace];\n\n if (!namespaceData) {\n return '';\n }\n\n // 직접 키 매칭\n const directValue = namespaceData[key];\n if (isStringValue(directValue)) {\n return directValue;\n }\n\n // 중첩 키 매칭\n const nestedValue = getNestedValue(namespaceData, key);\n if (isStringValue(nestedValue)) {\n return nestedValue;\n }\n\n return '';\n}\n\nfunction getNestedValue(obj: unknown, path: string): unknown {\n if (typeof obj !== 'object' || obj === null || Array.isArray(obj)) {\n return undefined;\n }\n\n return path.split('.').reduce((current: unknown, key: string) => {\n if (current && typeof current === 'object' && !Array.isArray(current) && key in current) {\n return (current as Record<string, unknown>)[key];\n }\n return undefined;\n }, obj);\n}\n\n/**\n * 문자열 값인지 확인하는 타입 가드\n */\nfunction isStringValue(value: unknown): value is string {\n return typeof value === 'string' && value.length > 0;\n}\n\n/**\n * 키 파싱 (네임스페이스:키 형식) - SSR용 standalone 함수\n * \n * - 콜론(:)만 네임스페이스 구분자로 사용\n * - 점(.)은 키 이름의 일부로 취급 (중첩 객체 접근용)\n */\nfunction parseKey(key: string): { namespace: string; key: string } {\n // 콜론(:)만 네임스페이스 구분자로 사용\n const colonIndex = key.indexOf(':');\n if (colonIndex !== -1) {\n return { namespace: key.substring(0, colonIndex), key: key.substring(colonIndex + 1) };\n }\n\n // 콜론이 없으면 common 네임스페이스로 간주\n return { namespace: 'common', key };\n}\n\n// 서버 번역 함수 (고급 기능 포함)\nexport function serverTranslate({\n translations,\n key,\n language = 'ko',\n fallbackLanguage = 'en',\n missingKeyHandler = (key: string) => key,\n options = {}\n}: {\n translations: Record<string, unknown>; // 번역 데이터\n key: string; // 번역 키\n language?: string; // 언어 코드\n fallbackLanguage?: string; // 폴백 언어\n missingKeyHandler?: (key: string) => string; // 누락 키 처리\n options?: {\n cache?: Map<string, string>; // 캐시 (선택적)\n metrics?: { hits: number; misses: number }; // 메트릭 (선택적)\n debug?: boolean; // 디버그 모드 (선택적)\n };\n}): string {\n const { cache, metrics, debug } = options;\n\n // 캐시에서 확인\n if (cache) {\n const cacheKey = `${language}:${key}`;\n const cached = cache.get(cacheKey);\n if (cached) {\n if (metrics) metrics.hits++;\n if (debug) console.log(`[CACHE HIT] ${cacheKey}`);\n return cached;\n }\n }\n\n // 번역 찾기\n const result = findInTranslations(translations, key, language, fallbackLanguage, missingKeyHandler);\n\n // 캐시에 저장\n if (cache && result) {\n const cacheKey = `${language}:${key}`;\n cache.set(cacheKey, result);\n }\n\n if (metrics) metrics.misses++;\n if (debug) console.log(`[TRANSLATE] ${key} -> ${result}`);\n\n return result;\n}\n\nfunction findInTranslations(\n translations: Record<string, unknown>,\n key: string,\n language: string,\n fallbackLanguage: string,\n missingKeyHandler: (key: string) => string\n): string {\n const { namespace, key: actualKey } = parseKey(key);\n\n // 현재 언어에서 찾기\n let result = findInNamespace(translations, namespace, actualKey, language);\n\n if (result) {\n return result;\n }\n\n // 폴백 언어에서 찾기\n if (language !== fallbackLanguage) {\n result = findInNamespace(translations, namespace, actualKey, fallbackLanguage);\n if (result) {\n return result;\n }\n }\n\n return '';\n}\n\nfunction findInNamespace(\n translations: Record<string, unknown>,\n namespace: string,\n key: string,\n language: string\n): string {\n // 언어 데이터 가져오기\n const languageData = translations[language];\n\n // 언어 데이터가 객체인지 확인\n if (!languageData || typeof languageData !== 'object' || Array.isArray(languageData)) {\n return '';\n }\n\n // 네임스페이스 데이터 가져오기\n const namespaceData = (languageData as Record<string, unknown>)[namespace];\n\n if (!namespaceData || typeof namespaceData !== 'object' || Array.isArray(namespaceData)) {\n return '';\n }\n\n // 타입 단언: namespaceData는 객체임을 확인했으므로 Record로 단언\n const data = namespaceData as Record<string, unknown>;\n\n // 직접 키 매칭\n if (data[key] && typeof data[key] === 'string') {\n return data[key] as string;\n }\n\n // 중첩 키 매칭\n const nestedValue = getNestedValue(namespaceData, key);\n if (typeof nestedValue === 'string') {\n return nestedValue;\n }\n\n return '';\n}"]}
package/dist/server.d.mts CHANGED
@@ -1 +1 @@
1
- export { I as I18nConfig, f as Translator, s as serverTranslate, h as ssrTranslate } from './server-4TeBq6hp.mjs';
1
+ export { I as I18nConfig, g as Translator, s as serverTranslate, j as ssrTranslate } from './server-DgpyR0RE.mjs';
package/dist/server.d.ts CHANGED
@@ -1 +1 @@
1
- export { I as I18nConfig, f as Translator, s as serverTranslate, h as ssrTranslate } from './server-4TeBq6hp.js';
1
+ export { I as I18nConfig, g as Translator, s as serverTranslate, j as ssrTranslate } from './server-DgpyR0RE.js';
package/dist/server.mjs CHANGED
@@ -1,3 +1,3 @@
1
- export { Translator, serverTranslate, ssrTranslate } from './chunk-EZL5TNH5.mjs';
1
+ export { Translator, serverTranslate, ssrTranslate } from './chunk-7ZYOSEMW.mjs';
2
2
  //# sourceMappingURL=server.mjs.map
3
3
  //# sourceMappingURL=server.mjs.map
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "@hua-labs/i18n-core",
3
- "version": "2.0.5",
3
+ "version": "2.2.0",
4
4
  "description": "HUA Labs - Core i18n functionality with SSR/CSR support and state management integration",
5
- "main": "./dist/index.js",
5
+ "main": "./dist/index.cjs",
6
6
  "module": "./dist/index.mjs",
7
7
  "types": "./dist/index.d.ts",
8
8
  "files": [
@@ -11,14 +11,17 @@
11
11
  ],
12
12
  "exports": {
13
13
  ".": {
14
+ "react-native": "./dist/index.mjs",
14
15
  "types": "./dist/index.d.ts",
15
16
  "import": "./dist/index.mjs",
16
- "require": "./dist/index.js"
17
+ "require": "./dist/index.cjs",
18
+ "default": "./dist/index.mjs"
17
19
  },
18
20
  "./server": {
19
21
  "types": "./dist/server.d.ts",
20
22
  "import": "./dist/server.mjs",
21
- "require": "./dist/server.js"
23
+ "require": "./dist/server.cjs",
24
+ "default": "./dist/server.mjs"
22
25
  }
23
26
  },
24
27
  "sideEffects": false,
@@ -28,11 +31,11 @@
28
31
  "devDependencies": {
29
32
  "@testing-library/jest-dom": "^6.6.3",
30
33
  "@testing-library/react": "^16.3.0",
31
- "@types/node": "^25.2.0",
34
+ "@types/node": "^25.3.5",
32
35
  "@types/react": "^19.2.14",
33
36
  "@vitejs/plugin-react": "^5.1.4",
34
- "eslint": "^10.0.0",
35
- "jsdom": "^28.0.0",
37
+ "eslint": "^10.0.3",
38
+ "jsdom": "^28.1.0",
36
39
  "react": "^19.2.4",
37
40
  "react-dom": "^19.2.4",
38
41
  "tsup": "^8.5.1",
@@ -59,12 +62,12 @@
59
62
  "license": "MIT",
60
63
  "repository": {
61
64
  "type": "git",
62
- "url": "https://github.com/HUA-Labs/HUA-Labs-public.git"
65
+ "url": "https://github.com/HUA-Labs/hua-packages.git"
63
66
  },
64
67
  "bugs": {
65
- "url": "https://github.com/HUA-Labs/HUA-Labs-public/issues"
68
+ "url": "https://github.com/HUA-Labs/hua-packages/issues"
66
69
  },
67
- "homepage": "https://github.com/HUA-Labs/HUA-Labs-public#readme",
70
+ "homepage": "https://github.com/HUA-Labs/hua-packages#readme",
68
71
  "publishConfig": {
69
72
  "access": "public",
70
73
  "provenance": true
@@ -189,10 +189,12 @@ export const reportMissingKey = (key: string, options: {
189
189
  component: options.component
190
190
  };
191
191
 
192
- // 커스텀 이벤트 발생
193
- window.dispatchEvent(new CustomEvent('i18n:missing-key', {
194
- detail: missingKey
195
- }));
192
+ // 커스텀 이벤트 발생 (브라우저 환경에서만)
193
+ if (typeof window !== 'undefined' && typeof CustomEvent !== 'undefined') {
194
+ window.dispatchEvent(new CustomEvent('i18n:missing-key', {
195
+ detail: missingKey
196
+ }));
197
+ }
196
198
 
197
199
  // 콘솔에도 로그
198
200
  if (process.env.NODE_ENV === 'development') console.warn(`Missing translation key: ${key}`, {
@@ -23,7 +23,7 @@ export interface TranslatorInterface {
23
23
  initialize(): Promise<void>;
24
24
  isReady(): boolean;
25
25
  debug(): unknown;
26
- getRawValue(key: string, language?: string): unknown;
26
+ getRawValue<T = unknown>(key: string, language?: string): T | undefined;
27
27
  tArray(key: string, language?: string): string[];
28
28
  }
29
29
 
@@ -46,7 +46,7 @@ export class Translator implements TranslatorInterface {
46
46
  // 언어 변경 시 React 리렌더링을 위한 콜백
47
47
  private onLanguageChangedCallbacks: Set<(language: string) => void> = new Set();
48
48
  // 디바운싱을 위한 타이머
49
- private notifyTimer: NodeJS.Timeout | null = null;
49
+ private notifyTimer: ReturnType<typeof setTimeout> | null = null;
50
50
  // 최근 알림한 네임스페이스 (중복 알림 방지)
51
51
  private recentlyNotified = new Set<string>();
52
52
 
@@ -517,7 +517,7 @@ export class Translator implements TranslatorInterface {
517
517
  /**
518
518
  * 원시 값 가져오기 (배열, 객체 포함)
519
519
  */
520
- getRawValue(key: string, language?: string): unknown {
520
+ getRawValue<T = unknown>(key: string, language?: string): T | undefined {
521
521
  const targetLang = language || this.currentLang;
522
522
 
523
523
  if (!this.isInitialized) {
@@ -536,13 +536,13 @@ export class Translator implements TranslatorInterface {
536
536
 
537
537
  // 직접 키 매칭
538
538
  if (actualKey in translations) {
539
- return translations[actualKey];
539
+ return translations[actualKey] as T;
540
540
  }
541
541
 
542
542
  // 중첩 키 매칭
543
543
  const nestedValue = this.getNestedValue(translations, actualKey);
544
544
  if (nestedValue !== undefined) {
545
- return nestedValue;
545
+ return nestedValue as T;
546
546
  }
547
547
 
548
548
  // 폴백 언어에서 찾기
@@ -550,11 +550,11 @@ export class Translator implements TranslatorInterface {
550
550
  const fallbackTranslations = this.allTranslations[this.config.fallbackLanguage || 'en']?.[namespace];
551
551
  if (fallbackTranslations) {
552
552
  if (actualKey in fallbackTranslations) {
553
- return fallbackTranslations[actualKey];
553
+ return fallbackTranslations[actualKey] as T;
554
554
  }
555
555
  const fallbackNestedValue = this.getNestedValue(fallbackTranslations, actualKey);
556
556
  if (fallbackNestedValue !== undefined) {
557
- return fallbackNestedValue;
557
+ return fallbackNestedValue as T;
558
558
  }
559
559
  }
560
560
  }
@@ -2,29 +2,57 @@
2
2
  import { useState, useEffect, useCallback, useContext, createContext, useMemo } from 'react';
3
3
  import { Translator } from '../core/translator';
4
4
  import { TranslatorFactory } from '../core/translator-factory';
5
- import {
6
- I18nConfig,
7
- I18nContextType,
8
- TranslationParams,
5
+ import {
6
+ I18nConfig,
7
+ I18nContextType,
8
+ TranslationParams,
9
9
  TranslationError,
10
- validateI18nConfig
10
+ validateI18nConfig,
11
+ webPlatformAdapter
11
12
  } from '../types';
12
13
  import { getDefaultTranslations } from '../utils/default-translations';
13
14
 
14
15
  // React Context
15
16
  const I18nContext = createContext<I18nContextType | null>(null);
16
17
 
18
+ /**
19
+ * 초기 언어를 결정하는 헬퍼 함수
20
+ * 우선순위: config.defaultLanguage > platformAdapter.getDeviceLanguage 매칭 > supportedLanguages[0]
21
+ * config.defaultLanguage가 명시적으로 제공되지 않은 경우에만 디바이스 언어 감지 동작
22
+ */
23
+ function resolveInitialLanguage(
24
+ config: I18nConfig & { autoLanguageSync?: boolean }
25
+ ): string {
26
+ // 1. config.defaultLanguage가 명시적으로 제공된 경우 우선 사용
27
+ if (config.defaultLanguage) {
28
+ return config.defaultLanguage;
29
+ }
30
+
31
+ // 2. 플랫폼 어댑터로 디바이스 언어 감지
32
+ const adapter = config.platformAdapter ?? webPlatformAdapter;
33
+ const deviceLang = adapter.getDeviceLanguage();
34
+ if (deviceLang) {
35
+ const supportedCodes = config.supportedLanguages?.map(l => l.code) ?? [];
36
+ if (supportedCodes.includes(deviceLang)) {
37
+ return deviceLang;
38
+ }
39
+ }
40
+
41
+ // 3. 첫 번째 지원 언어로 폴백
42
+ return config.supportedLanguages?.[0]?.code ?? 'ko';
43
+ }
44
+
17
45
  /**
18
46
  * I18n Provider 컴포넌트
19
47
  */
20
- export function I18nProvider({
21
- config,
22
- children
23
- }: {
24
- config: I18nConfig & { autoLanguageSync?: boolean };
25
- children: React.ReactNode;
48
+ export function I18nProvider({
49
+ config,
50
+ children
51
+ }: {
52
+ config: I18nConfig & { autoLanguageSync?: boolean };
53
+ children: React.ReactNode;
26
54
  }) {
27
- const [currentLanguage, setCurrentLanguageState] = useState(config.defaultLanguage);
55
+ const [currentLanguage, setCurrentLanguageState] = useState(() => resolveInitialLanguage(config));
28
56
  const [isLoading, setIsLoading] = useState(true);
29
57
  const [isInitialized, setIsInitialized] = useState(false);
30
58
  const [error, setError] = useState<TranslationError | null>(null);
@@ -144,33 +172,22 @@ export function I18nProvider({
144
172
  return unsubscribe;
145
173
  }, [translator, isInitialized, currentLanguage, config.debug]);
146
174
 
147
- // 자동 언어 전환 이벤트 처리
175
+ // 자동 언어 전환 이벤트 처리 (플랫폼 어댑터 위임)
148
176
  useEffect(() => {
149
- if (!config.autoLanguageSync || typeof window === 'undefined') {
177
+ if (!config.autoLanguageSync) {
150
178
  return;
151
179
  }
152
180
 
153
- const handleLanguageChange = (event: CustomEvent) => {
154
- const newLanguage = event.detail;
155
- if (typeof newLanguage === 'string' && newLanguage !== currentLanguage) {
181
+ const adapter = config.platformAdapter ?? webPlatformAdapter;
182
+ return adapter.onLanguageChange((newLanguage) => {
183
+ if (newLanguage !== currentLanguage) {
156
184
  if (config.debug) {
157
185
  console.log('🌐 Auto language sync:', newLanguage);
158
186
  }
159
187
  setLanguage(newLanguage);
160
188
  }
161
- };
162
-
163
- // HUA i18n 언어 전환 이벤트 감지
164
- window.addEventListener('huaI18nLanguageChange', handleLanguageChange as EventListener);
165
-
166
- // 일반적인 언어 변경 이벤트도 감지
167
- window.addEventListener('i18nLanguageChanged', handleLanguageChange as EventListener);
168
-
169
- return () => {
170
- window.removeEventListener('huaI18nLanguageChange', handleLanguageChange as EventListener);
171
- window.removeEventListener('i18nLanguageChanged', handleLanguageChange as EventListener);
172
- };
173
- }, [config.autoLanguageSync, currentLanguage]);
189
+ });
190
+ }, [config.autoLanguageSync, config.platformAdapter, currentLanguage]);
174
191
 
175
192
  // 언어 변경 함수 (메모이제이션)
176
193
  const setLanguage = useCallback(async (language: string) => {
@@ -385,12 +402,12 @@ export function I18nProvider({
385
402
  return translator.translateSync(key, params);
386
403
  }, [translator, config.debug]);
387
404
 
388
- // 원시 값 가져오기 (배열, 객체 포함)
389
- const getRawValue = useCallback((key: string, language?: string): unknown => {
405
+ // 원시 값 가져오기 (배열, 객체 포함) — 제네릭으로 타입 캐스팅 가능
406
+ const getRawValue = useCallback(<T = unknown>(key: string, language?: string): T | undefined => {
390
407
  if (!translator || !isInitialized) {
391
408
  return undefined;
392
409
  }
393
- return translator.getRawValue(key, language);
410
+ return translator.getRawValue<T>(key, language);
394
411
  }, [translator, isInitialized]);
395
412
 
396
413
  // 배열 번역 값 가져오기 (타입 안전)
@@ -524,8 +541,7 @@ export function I18nProvider({
524
541
  supportedLanguages: config.supportedLanguages,
525
542
  debug,
526
543
  isInitialized,
527
- translationVersion,
528
- }), [currentLanguage, setLanguage, t, tPlural, tArray, tAsync, tSync, getRawValue, isLoading, error, config.supportedLanguages, debug, isInitialized, translationVersion]);
544
+ }), [currentLanguage, setLanguage, t, tPlural, tArray, tAsync, tSync, getRawValue, isLoading, error, config.supportedLanguages, debug, isInitialized]);
529
545
 
530
546
  return (
531
547
  <I18nContext.Provider value={value}>
@@ -548,7 +564,7 @@ export function useI18n(): I18nContextType {
548
564
  tPlural: (key: string) => key,
549
565
  tAsync: async (key: string) => key,
550
566
  tSync: (key: string) => key,
551
- getRawValue: () => undefined,
567
+ getRawValue: <T = unknown>() => undefined as T | undefined,
552
568
  tArray: () => [],
553
569
  isLoading: false,
554
570
  error: null,
package/src/index.ts CHANGED
@@ -9,7 +9,7 @@ import React from 'react';
9
9
  import { I18nProvider, useI18n } from './hooks/useI18n';
10
10
  import { useTranslation, useLanguageChange } from './hooks/useTranslation';
11
11
  import { Translator, ssrTranslate, serverTranslate } from './core/translator';
12
- import { I18nConfig, I18nContextType, TranslationParams, TypedTranslationKeys, ResolveStringKey, ResolveArrayKey, ResolvePluralKey, PluralValue, PluralCategory } from './types';
12
+ import { I18nConfig, I18nContextType, TranslationParams, TypedTranslationKeys, ResolveStringKey, ResolveArrayKey, ResolvePluralKey, PluralValue, PluralCategory, I18nPlatformAdapter, webPlatformAdapter, headlessPlatformAdapter } from './types';
13
13
 
14
14
  // Window 객체 타입 확장
15
15
  declare global {
@@ -391,5 +391,8 @@ export { I18nProvider };
391
391
  // 핵심 클래스/함수들 export
392
392
  export { Translator, ssrTranslate, serverTranslate };
393
393
 
394
+ // 플랫폼 어댑터 export
395
+ export { webPlatformAdapter, headlessPlatformAdapter };
396
+
394
397
  // 타입 export
395
- export type { I18nConfig, I18nContextType, TranslationParams, TypedTranslationKeys, ResolveStringKey, ResolveArrayKey, ResolvePluralKey, PluralValue, PluralCategory };
398
+ export type { I18nConfig, I18nContextType, TranslationParams, TypedTranslationKeys, ResolveStringKey, ResolveArrayKey, ResolvePluralKey, PluralValue, PluralCategory, I18nPlatformAdapter };
@@ -26,6 +26,61 @@ export function isPluralValue(value: unknown): value is PluralValue {
26
26
  );
27
27
  }
28
28
 
29
+ // ---------------------------------------------------------------------------
30
+ // Platform Adapter (cross-platform: Web / React Native / Flutter bridge)
31
+ // ---------------------------------------------------------------------------
32
+
33
+ /**
34
+ * 플랫폼별 i18n 어댑터 인터페이스.
35
+ *
36
+ * Web(기본), React Native, Flutter 브릿지 등 환경에 맞는 구현체를 주입.
37
+ * I18nConfig.platformAdapter로 전달하면 useI18n이 자동으로 사용한다.
38
+ */
39
+ export interface I18nPlatformAdapter {
40
+ /** 디바이스/브라우저 언어 코드 (e.g. 'ko', 'en'). 감지 불가 시 undefined */
41
+ getDeviceLanguage(): string | undefined;
42
+
43
+ /** 시스템 언어 변경 이벤트 구독. 해제 함수 반환 */
44
+ onLanguageChange(cb: (lang: string) => void): () => void;
45
+ }
46
+
47
+ /**
48
+ * Web 기본 어댑터.
49
+ * navigator.language + CustomEvent 기반. SSR에서는 안전하게 no-op.
50
+ */
51
+ export const webPlatformAdapter: I18nPlatformAdapter = {
52
+ getDeviceLanguage() {
53
+ if (typeof globalThis !== 'undefined' && typeof navigator !== 'undefined' && navigator.language) {
54
+ return navigator.language.slice(0, 2).toLowerCase();
55
+ }
56
+ return undefined;
57
+ },
58
+ onLanguageChange(cb) {
59
+ if (typeof globalThis === 'undefined' || typeof window === 'undefined' || typeof CustomEvent === 'undefined') {
60
+ return () => {};
61
+ }
62
+ const handler = (e: Event) => {
63
+ const lang = (e as CustomEvent).detail;
64
+ if (typeof lang === 'string') cb(lang);
65
+ };
66
+ window.addEventListener('huaI18nLanguageChange', handler);
67
+ window.addEventListener('i18nLanguageChanged', handler);
68
+ return () => {
69
+ window.removeEventListener('huaI18nLanguageChange', handler);
70
+ window.removeEventListener('i18nLanguageChanged', handler);
71
+ };
72
+ },
73
+ };
74
+
75
+ /**
76
+ * Headless 어댑터 (SSR / 테스트 / Flutter 브릿지용).
77
+ * 언어 감지·이벤트 없이 config.defaultLanguage만 사용.
78
+ */
79
+ export const headlessPlatformAdapter: I18nPlatformAdapter = {
80
+ getDeviceLanguage() { return undefined; },
81
+ onLanguageChange() { return () => {}; },
82
+ };
83
+
29
84
  // ---------------------------------------------------------------------------
30
85
  // Translation namespace
31
86
  // ---------------------------------------------------------------------------
@@ -79,6 +134,8 @@ export interface I18nConfig {
79
134
  };
80
135
  // 자동 언어 전환 이벤트 처리 (withDefaultConfig용)
81
136
  autoLanguageSync?: boolean;
137
+ // 플랫폼 어댑터 (기본: webPlatformAdapter)
138
+ platformAdapter?: I18nPlatformAdapter;
82
139
  }
83
140
 
84
141
  // 에러 타입 정의
@@ -160,8 +217,8 @@ export interface I18nContextType {
160
217
  tAsync: (key: string, params?: TranslationParams) => Promise<string>;
161
218
  // 기존 동기 번역 함수 (하위 호환성)
162
219
  tSync: (key: string, namespace?: string, params?: TranslationParams) => string;
163
- // 원시 값 가져오기 (배열, 객체 포함)
164
- getRawValue: (key: string, language?: string) => unknown;
220
+ // 원시 값 가져오기 (배열, 객체 포함) — 제네릭으로 타입 캐스팅 가능
221
+ getRawValue: <T = unknown>(key: string, language?: string) => T | undefined;
165
222
  // 배열 번역 값 가져오기 (타입 안전)
166
223
  tArray: (key: ResolveArrayKey, language?: string) => string[];
167
224
  isLoading: boolean;