@hua-labs/i18n-core 2.1.0 → 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.
- package/README.md +7 -1
- package/dist/chunk-7ZYOSEMW.mjs +1006 -0
- package/dist/chunk-7ZYOSEMW.mjs.map +1 -0
- package/dist/index.cjs +1874 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.mts +3 -3
- package/dist/index.d.ts +249 -0
- package/dist/index.mjs +13 -18
- package/dist/index.mjs.map +1 -1
- package/dist/{server-4TeBq6hp.d.mts → server-DgpyR0RE.d.mts} +27 -4
- package/dist/server-DgpyR0RE.d.ts +390 -0
- package/dist/{chunk-EZL5TNH5.mjs → server.cjs} +7 -3
- package/dist/server.cjs.map +1 -0
- package/dist/server.d.mts +1 -1
- package/dist/server.d.ts +1 -0
- package/dist/server.mjs +1 -1
- package/package.json +10 -7
- package/src/components/MissingKeyOverlay.tsx +6 -4
- package/src/core/translator.tsx +7 -7
- package/src/hooks/useI18n.tsx +25 -34
- package/src/index.ts +5 -2
- package/src/types/index.ts +59 -2
- package/dist/chunk-EZL5TNH5.mjs.map +0 -1
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
import React$1 from 'react';
|
|
2
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
3
|
+
import { I as I18nConfig, a as I18nContextType, L as LanguageConfig, R as ResolveStringKey, T as TranslationParams, b as ResolvePluralKey, c as ResolveArrayKey, d as TranslationError } from './server-DgpyR0RE.js';
|
|
4
|
+
export { e as I18nPlatformAdapter, P as PluralCategory, f as PluralValue, g as Translator, h as TypedTranslationKeys, i as headlessPlatformAdapter, s as serverTranslate, j as ssrTranslate, w as webPlatformAdapter } from './server-DgpyR0RE.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* I18n Provider 컴포넌트
|
|
8
|
+
*/
|
|
9
|
+
declare function I18nProvider({ config, children }: {
|
|
10
|
+
config: I18nConfig & {
|
|
11
|
+
autoLanguageSync?: boolean;
|
|
12
|
+
};
|
|
13
|
+
children: React.ReactNode;
|
|
14
|
+
}): react_jsx_runtime.JSX.Element;
|
|
15
|
+
/**
|
|
16
|
+
* I18n 훅
|
|
17
|
+
*/
|
|
18
|
+
declare function useI18n(): I18nContextType;
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* 간단한 번역 훅 (원본 SDK와 호환)
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```tsx
|
|
25
|
+
* import { useTranslation } from '@hua-labs/i18n-core';
|
|
26
|
+
*
|
|
27
|
+
* function MyComponent() {
|
|
28
|
+
* const { t, currentLanguage, setLanguage, isLoading, error } = useTranslation();
|
|
29
|
+
*
|
|
30
|
+
* return (
|
|
31
|
+
* <div>
|
|
32
|
+
* <h1>{t('welcome')}</h1>
|
|
33
|
+
* <p>Current language: {currentLanguage}</p>
|
|
34
|
+
* <button onClick={() => setLanguage('en')}>Switch to English</button>
|
|
35
|
+
* </div>
|
|
36
|
+
* );
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
declare function useTranslation(): {
|
|
41
|
+
t: (key: ResolveStringKey, paramsOrLang?: TranslationParams | string, language?: string) => string;
|
|
42
|
+
tPlural: (key: ResolvePluralKey, count: number, params?: Record<string, unknown>, language?: string) => string;
|
|
43
|
+
tArray: (key: ResolveArrayKey, language?: string) => string[];
|
|
44
|
+
currentLanguage: string;
|
|
45
|
+
setLanguage: (language: string) => void | Promise<void>;
|
|
46
|
+
getRawValue: <T = unknown>(key: string, language?: string) => T | undefined;
|
|
47
|
+
isLoading: boolean;
|
|
48
|
+
error: TranslationError | null;
|
|
49
|
+
supportedLanguages: LanguageConfig[];
|
|
50
|
+
debug: {
|
|
51
|
+
getCurrentLanguage: () => string;
|
|
52
|
+
getSupportedLanguages: () => string[];
|
|
53
|
+
getLoadedNamespaces: () => string[];
|
|
54
|
+
getAllTranslations: () => Record<string, Record<string, unknown>>;
|
|
55
|
+
isReady: () => boolean;
|
|
56
|
+
getInitializationError: () => TranslationError | null;
|
|
57
|
+
clearCache: () => void;
|
|
58
|
+
reloadTranslations: () => Promise<void>;
|
|
59
|
+
getCacheStats: () => {
|
|
60
|
+
size: number;
|
|
61
|
+
hits: number;
|
|
62
|
+
misses: number;
|
|
63
|
+
};
|
|
64
|
+
};
|
|
65
|
+
isInitialized: boolean;
|
|
66
|
+
};
|
|
67
|
+
/**
|
|
68
|
+
* 언어 변경 전용 훅
|
|
69
|
+
*/
|
|
70
|
+
declare function useLanguageChange(): {
|
|
71
|
+
currentLanguage: string;
|
|
72
|
+
changeLanguage: (language: string) => void;
|
|
73
|
+
supportedLanguages: LanguageConfig[];
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @hua-labs/i18n-core - 핵심 기능 전용 엔트리포인트
|
|
78
|
+
*
|
|
79
|
+
* 이 모듈은 기본적인 번역 기능만 필요한 경우 사용합니다.
|
|
80
|
+
* 플러그인, 고급 기능, 디버깅 도구 없이 순수한 번역 기능만 제공합니다.
|
|
81
|
+
*/
|
|
82
|
+
|
|
83
|
+
declare global {
|
|
84
|
+
interface Window {
|
|
85
|
+
__I18N_DEBUG_MODE__?: boolean;
|
|
86
|
+
__I18N_DEBUG_MISSING_KEYS__?: Record<string, string[]>;
|
|
87
|
+
__I18N_DEBUG_ERRORS__?: Array<{
|
|
88
|
+
timestamp: string;
|
|
89
|
+
language: string;
|
|
90
|
+
namespace: string;
|
|
91
|
+
error: string;
|
|
92
|
+
stack?: string;
|
|
93
|
+
}>;
|
|
94
|
+
__I18N_PERFORMANCE_DATA__?: Record<string, number[]>;
|
|
95
|
+
__I18N_PERFORMANCE_ALERTS__?: Array<{
|
|
96
|
+
id: string;
|
|
97
|
+
type: 'warning' | 'error' | 'info';
|
|
98
|
+
severity: 'low' | 'medium' | 'high' | 'critical';
|
|
99
|
+
message: string;
|
|
100
|
+
metric: string;
|
|
101
|
+
value: number;
|
|
102
|
+
threshold: number;
|
|
103
|
+
timestamp: number;
|
|
104
|
+
resolved: boolean;
|
|
105
|
+
}>;
|
|
106
|
+
__I18N_ANALYTICS_DATA__?: {
|
|
107
|
+
missingKeys?: Set<string> | string[];
|
|
108
|
+
performance?: {
|
|
109
|
+
totalTime: number;
|
|
110
|
+
averageTime: number;
|
|
111
|
+
calls: number;
|
|
112
|
+
};
|
|
113
|
+
usage?: {
|
|
114
|
+
keys?: Map<string, number> | Record<string, number>;
|
|
115
|
+
languages?: Map<string, number> | Record<string, number>;
|
|
116
|
+
namespaces?: Map<string, number> | Record<string, number>;
|
|
117
|
+
};
|
|
118
|
+
errors?: Array<{
|
|
119
|
+
timestamp: number;
|
|
120
|
+
error: string;
|
|
121
|
+
context: string;
|
|
122
|
+
}>;
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* 핵심 기능용 설정 함수
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```tsx
|
|
131
|
+
* // app/layout.tsx (Next.js App Router)
|
|
132
|
+
* import { createCoreI18n } from '@hua-labs/i18n-core';
|
|
133
|
+
*
|
|
134
|
+
* export default function RootLayout({ children }) {
|
|
135
|
+
* return (
|
|
136
|
+
* <html>
|
|
137
|
+
* <body>
|
|
138
|
+
* {createCoreI18n({
|
|
139
|
+
* defaultLanguage: 'ko',
|
|
140
|
+
* fallbackLanguage: 'en',
|
|
141
|
+
* namespaces: ['common', 'auth']
|
|
142
|
+
* })({ children })}
|
|
143
|
+
* </body>
|
|
144
|
+
* </html>
|
|
145
|
+
* );
|
|
146
|
+
* }
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
declare function createCoreI18n(options?: {
|
|
150
|
+
defaultLanguage?: string;
|
|
151
|
+
fallbackLanguage?: string;
|
|
152
|
+
namespaces?: string[];
|
|
153
|
+
debug?: boolean;
|
|
154
|
+
loadTranslations?: (language: string, namespace: string) => Promise<Record<string, string>>;
|
|
155
|
+
/**
|
|
156
|
+
* 번역 파일 로딩 방식 설정
|
|
157
|
+
* - 'api': API route를 통해 동적 로드 (기본값, 권장)
|
|
158
|
+
* - 'static': 정적 파일 경로에서 로드
|
|
159
|
+
* - 'custom': loadTranslations 함수 사용
|
|
160
|
+
*/
|
|
161
|
+
translationLoader?: 'api' | 'static' | 'custom';
|
|
162
|
+
/**
|
|
163
|
+
* API route 경로 (translationLoader가 'api'일 때 사용)
|
|
164
|
+
* 기본값: '/api/translations'
|
|
165
|
+
*/
|
|
166
|
+
translationApiPath?: string;
|
|
167
|
+
/**
|
|
168
|
+
* SSR에서 전달된 초기 번역 데이터 (네트워크 요청 없이 사용)
|
|
169
|
+
* 형식: { [language]: { [namespace]: { [key]: value } } }
|
|
170
|
+
*/
|
|
171
|
+
initialTranslations?: Record<string, Record<string, Record<string, string>>>;
|
|
172
|
+
/**
|
|
173
|
+
* 지원 언어 목록 (LanguageConfig 배열 또는 언어 코드 문자열 배열)
|
|
174
|
+
* 기본값: ['ko', 'en']
|
|
175
|
+
*/
|
|
176
|
+
supportedLanguages?: Array<{
|
|
177
|
+
code: string;
|
|
178
|
+
name: string;
|
|
179
|
+
nativeName: string;
|
|
180
|
+
}> | string[];
|
|
181
|
+
/**
|
|
182
|
+
* 자동 언어 동기화 활성화 여부
|
|
183
|
+
* 기본값: false (Zustand 어댑터 등 외부에서 직접 처리하는 경우)
|
|
184
|
+
*/
|
|
185
|
+
autoLanguageSync?: boolean;
|
|
186
|
+
/**
|
|
187
|
+
* 서버사이드 렌더링 시 사용할 기본 URL
|
|
188
|
+
* 환경 변수보다 우선 적용됨
|
|
189
|
+
*/
|
|
190
|
+
baseUrl?: string;
|
|
191
|
+
/**
|
|
192
|
+
* 로컬 개발 환경 fallback URL
|
|
193
|
+
* 기본값: 'http://localhost:3010'
|
|
194
|
+
*/
|
|
195
|
+
localFallbackBaseUrl?: string;
|
|
196
|
+
}): ({ children }: {
|
|
197
|
+
children: React$1.ReactNode;
|
|
198
|
+
}) => React$1.FunctionComponentElement<{
|
|
199
|
+
config: I18nConfig & {
|
|
200
|
+
autoLanguageSync?: boolean;
|
|
201
|
+
};
|
|
202
|
+
children: React$1.ReactNode;
|
|
203
|
+
}>;
|
|
204
|
+
/**
|
|
205
|
+
* 가장 기본적인 Provider (최소한의 설정)
|
|
206
|
+
*/
|
|
207
|
+
declare function CoreProvider({ children }: {
|
|
208
|
+
children: React$1.ReactNode;
|
|
209
|
+
}): React$1.FunctionComponentElement<{
|
|
210
|
+
config: I18nConfig & {
|
|
211
|
+
autoLanguageSync?: boolean;
|
|
212
|
+
};
|
|
213
|
+
children: React$1.ReactNode;
|
|
214
|
+
}>;
|
|
215
|
+
/**
|
|
216
|
+
* 언어별 Provider (언어만 지정)
|
|
217
|
+
*/
|
|
218
|
+
declare function createLanguageProvider(language: string): ({ children }: {
|
|
219
|
+
children: React$1.ReactNode;
|
|
220
|
+
}) => React$1.FunctionComponentElement<{
|
|
221
|
+
config: I18nConfig & {
|
|
222
|
+
autoLanguageSync?: boolean;
|
|
223
|
+
};
|
|
224
|
+
children: React$1.ReactNode;
|
|
225
|
+
}>;
|
|
226
|
+
/**
|
|
227
|
+
* 네임스페이스별 Provider (네임스페이스만 지정)
|
|
228
|
+
*/
|
|
229
|
+
declare function createNamespaceProvider(namespaces: string[]): ({ children }: {
|
|
230
|
+
children: React$1.ReactNode;
|
|
231
|
+
}) => React$1.FunctionComponentElement<{
|
|
232
|
+
config: I18nConfig & {
|
|
233
|
+
autoLanguageSync?: boolean;
|
|
234
|
+
};
|
|
235
|
+
children: React$1.ReactNode;
|
|
236
|
+
}>;
|
|
237
|
+
/**
|
|
238
|
+
* 커스텀 로더 Provider (사용자 정의 번역 로더 사용)
|
|
239
|
+
*/
|
|
240
|
+
declare function createCustomLoaderProvider(loadTranslations: (language: string, namespace: string) => Promise<Record<string, string>>): ({ children }: {
|
|
241
|
+
children: React$1.ReactNode;
|
|
242
|
+
}) => React$1.FunctionComponentElement<{
|
|
243
|
+
config: I18nConfig & {
|
|
244
|
+
autoLanguageSync?: boolean;
|
|
245
|
+
};
|
|
246
|
+
children: React$1.ReactNode;
|
|
247
|
+
}>;
|
|
248
|
+
|
|
249
|
+
export { CoreProvider, I18nConfig, I18nContextType, I18nProvider, ResolveArrayKey, ResolvePluralKey, ResolveStringKey, TranslationParams, createCoreI18n, createCustomLoaderProvider, createLanguageProvider, createNamespaceProvider, useI18n, useLanguageChange, useTranslation };
|
package/dist/index.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { validateI18nConfig, Translator } from './chunk-
|
|
2
|
-
export { Translator, serverTranslate, ssrTranslate } from './chunk-
|
|
1
|
+
import { validateI18nConfig, webPlatformAdapter, Translator } from './chunk-7ZYOSEMW.mjs';
|
|
2
|
+
export { Translator, headlessPlatformAdapter, serverTranslate, ssrTranslate, webPlatformAdapter } from './chunk-7ZYOSEMW.mjs';
|
|
3
3
|
import React, { createContext, useState, useEffect, useMemo, useCallback, useContext } from 'react';
|
|
4
4
|
import { jsx } from 'react/jsx-runtime';
|
|
5
5
|
|
|
@@ -208,11 +208,12 @@ function resolveInitialLanguage(config) {
|
|
|
208
208
|
if (config.defaultLanguage) {
|
|
209
209
|
return config.defaultLanguage;
|
|
210
210
|
}
|
|
211
|
-
|
|
212
|
-
|
|
211
|
+
const adapter = config.platformAdapter ?? webPlatformAdapter;
|
|
212
|
+
const deviceLang = adapter.getDeviceLanguage();
|
|
213
|
+
if (deviceLang) {
|
|
213
214
|
const supportedCodes = config.supportedLanguages?.map((l) => l.code) ?? [];
|
|
214
|
-
if (supportedCodes.includes(
|
|
215
|
-
return
|
|
215
|
+
if (supportedCodes.includes(deviceLang)) {
|
|
216
|
+
return deviceLang;
|
|
216
217
|
}
|
|
217
218
|
}
|
|
218
219
|
return config.supportedLanguages?.[0]?.code ?? "ko";
|
|
@@ -310,25 +311,19 @@ function I18nProvider({
|
|
|
310
311
|
return unsubscribe;
|
|
311
312
|
}, [translator, isInitialized, currentLanguage, config.debug]);
|
|
312
313
|
useEffect(() => {
|
|
313
|
-
if (!config.autoLanguageSync
|
|
314
|
+
if (!config.autoLanguageSync) {
|
|
314
315
|
return;
|
|
315
316
|
}
|
|
316
|
-
const
|
|
317
|
-
|
|
318
|
-
if (
|
|
317
|
+
const adapter = config.platformAdapter ?? webPlatformAdapter;
|
|
318
|
+
return adapter.onLanguageChange((newLanguage) => {
|
|
319
|
+
if (newLanguage !== currentLanguage) {
|
|
319
320
|
if (config.debug) {
|
|
320
321
|
console.log("\u{1F310} Auto language sync:", newLanguage);
|
|
321
322
|
}
|
|
322
323
|
setLanguage(newLanguage);
|
|
323
324
|
}
|
|
324
|
-
};
|
|
325
|
-
|
|
326
|
-
window.addEventListener("i18nLanguageChanged", handleLanguageChange);
|
|
327
|
-
return () => {
|
|
328
|
-
window.removeEventListener("huaI18nLanguageChange", handleLanguageChange);
|
|
329
|
-
window.removeEventListener("i18nLanguageChanged", handleLanguageChange);
|
|
330
|
-
};
|
|
331
|
-
}, [config.autoLanguageSync, currentLanguage]);
|
|
325
|
+
});
|
|
326
|
+
}, [config.autoLanguageSync, config.platformAdapter, currentLanguage]);
|
|
332
327
|
const setLanguage = useCallback(async (language) => {
|
|
333
328
|
if (!translator) {
|
|
334
329
|
return;
|