@hua-labs/i18n-core 2.0.0 → 2.0.2
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 +57 -597
- package/dist/chunk-F4PDBJLO.mjs +973 -0
- package/dist/chunk-F4PDBJLO.mjs.map +1 -0
- package/dist/index.d.mts +249 -0
- package/dist/index.d.ts +117 -30
- package/dist/index.js +1818 -177
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +845 -0
- package/dist/index.mjs.map +1 -0
- package/dist/server-4TeBq6hp.d.mts +367 -0
- package/dist/server-4TeBq6hp.d.ts +367 -0
- package/dist/server.d.mts +1 -0
- package/dist/server.d.ts +1 -0
- package/dist/server.js +977 -0
- package/dist/server.js.map +1 -0
- package/dist/server.mjs +3 -0
- package/dist/server.mjs.map +1 -0
- package/package.json +42 -19
- package/src/__tests__/debug-tools.test.ts +359 -0
- package/src/__tests__/default-translations.test.ts +179 -0
- package/src/__tests__/i18n-resource.test.ts +137 -0
- package/src/__tests__/lazy-loader.test.ts +109 -0
- package/src/__tests__/missing-key-overlay.test.tsx +339 -0
- package/src/__tests__/translator-factory.test.ts +120 -0
- package/src/__tests__/translator.test.ts +442 -0
- package/src/__tests__/types.test.ts +211 -0
- package/src/__tests__/useI18n.test.tsx +181 -0
- package/src/__tests__/useTranslation.test.tsx +110 -0
- package/src/components/MissingKeyOverlay.tsx +1 -1
- package/src/core/lazy-loader.ts +2 -2
- package/src/core/translator.tsx +151 -62
- package/src/hooks/useI18n.tsx +96 -115
- package/src/hooks/useTranslation.tsx +12 -10
- package/src/index.ts +102 -5
- package/src/server.ts +9 -0
- package/src/types/index.ts +67 -12
- package/LICENSE +0 -21
- package/dist/components/MissingKeyOverlay.d.ts +0 -33
- package/dist/components/MissingKeyOverlay.d.ts.map +0 -1
- package/dist/components/MissingKeyOverlay.js +0 -138
- package/dist/components/MissingKeyOverlay.js.map +0 -1
- package/dist/core/debug-tools.d.ts +0 -37
- package/dist/core/debug-tools.d.ts.map +0 -1
- package/dist/core/debug-tools.js +0 -241
- package/dist/core/debug-tools.js.map +0 -1
- package/dist/core/i18n-resource.d.ts +0 -59
- package/dist/core/i18n-resource.d.ts.map +0 -1
- package/dist/core/i18n-resource.js +0 -153
- package/dist/core/i18n-resource.js.map +0 -1
- package/dist/core/lazy-loader.d.ts +0 -82
- package/dist/core/lazy-loader.d.ts.map +0 -1
- package/dist/core/lazy-loader.js +0 -193
- package/dist/core/lazy-loader.js.map +0 -1
- package/dist/core/translator-factory.d.ts +0 -50
- package/dist/core/translator-factory.d.ts.map +0 -1
- package/dist/core/translator-factory.js +0 -117
- package/dist/core/translator-factory.js.map +0 -1
- package/dist/core/translator.d.ts +0 -202
- package/dist/core/translator.d.ts.map +0 -1
- package/dist/core/translator.js +0 -912
- package/dist/core/translator.js.map +0 -1
- package/dist/hooks/useI18n.d.ts +0 -39
- package/dist/hooks/useI18n.d.ts.map +0 -1
- package/dist/hooks/useI18n.js +0 -531
- package/dist/hooks/useI18n.js.map +0 -1
- package/dist/hooks/useTranslation.d.ts +0 -55
- package/dist/hooks/useTranslation.d.ts.map +0 -1
- package/dist/hooks/useTranslation.js +0 -58
- package/dist/hooks/useTranslation.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/types/index.d.ts +0 -162
- package/dist/types/index.d.ts.map +0 -1
- package/dist/types/index.js +0 -191
- package/dist/types/index.js.map +0 -1
- package/dist/utils/default-translations.d.ts +0 -20
- package/dist/utils/default-translations.d.ts.map +0 -1
- package/dist/utils/default-translations.js +0 -123
- package/dist/utils/default-translations.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,636 +1,96 @@
|
|
|
1
1
|
# @hua-labs/i18n-core
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Lightweight, production-ready i18n library for React. Delivers zero-flicker language transitions through intelligent caching, SSR-first hydration handling, and built-in state management integration. ~6.5KB gzipped with zero dependencies (React only).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/@hua-labs/i18n-core)
|
|
6
|
+
[](https://www.npmjs.com/package/@hua-labs/i18n-core)
|
|
7
|
+
[](https://github.com/HUA-Labs/HUA-Labs-public/blob/main/LICENSE)
|
|
8
|
+
[](https://www.typescriptlang.org/)
|
|
9
|
+
[](https://reactjs.org/)
|
|
6
10
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
## Why @hua-labs/i18n-core?
|
|
10
|
-
|
|
11
|
-
Struggling with flickering on language changes or hydration mismatches? @hua-labs/i18n-core provides a pragmatic, production-ready solution for React i18n.
|
|
12
|
-
|
|
13
|
-
**Key advantages:**
|
|
14
|
-
- **Zero flickering**: Automatically shows previous language translation during switch
|
|
15
|
-
- **SSR-first**: Built-in hydration handling, no mismatch issues
|
|
16
|
-
- **State management integration**: First-class Zustand support
|
|
17
|
-
- **Small bundle**: ~2.8KB gzipped, zero dependencies (React only)
|
|
18
|
-
- **Framework agnostic**: Works with Next.js, Remix, Vite, and more
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
## Examples
|
|
11
|
+
## Features
|
|
22
12
|
|
|
23
|
-
- **
|
|
24
|
-
- **
|
|
13
|
+
- **Zero-flicker — Shows previous language during transition**
|
|
14
|
+
- **SSR-first — Built-in hydration handling, no mismatch issues**
|
|
15
|
+
- **Namespace lazy loading — Load translations on demand**
|
|
16
|
+
- **Pluralization — ICU-compliant plural support via Intl.PluralRules (zero bundle cost)**
|
|
17
|
+
- **Type-safe arrays — tArray() returns string[] without casting**
|
|
18
|
+
- **State management — First-class Zustand support via i18n-core-zustand**
|
|
19
|
+
- **Automatic retry — Exponential backoff for API loader failures**
|
|
20
|
+
- **~6.5KB gzipped — Zero external dependencies**
|
|
25
21
|
|
|
26
22
|
## Installation
|
|
27
23
|
|
|
28
24
|
```bash
|
|
29
|
-
npm install @hua-labs/i18n-core
|
|
30
|
-
# or
|
|
31
|
-
yarn add @hua-labs/i18n-core
|
|
32
|
-
# or
|
|
33
25
|
pnpm add @hua-labs/i18n-core
|
|
34
26
|
```
|
|
35
27
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
- Lightweight core translation functionality
|
|
39
|
-
- Multiple translation loader strategies (API, static files, custom)
|
|
40
|
-
- Lazy loading support for namespaces
|
|
41
|
-
- SSR/SSG support with initial translations
|
|
42
|
-
- TypeScript support
|
|
43
|
-
- Zero external dependencies (except React)
|
|
44
|
-
- Built-in caching
|
|
45
|
-
- Error handling and fallback support
|
|
46
|
-
- Debug mode for development
|
|
47
|
-
- **Language change flickering prevention**: Automatically shows previous language translation during language switch
|
|
48
|
-
- **State management integration**: Works seamlessly with Zustand via `@hua-labs/i18n-core-zustand`
|
|
49
|
-
- **Raw value access**: Get arrays, objects, or any non-string values from translations via `getRawValue`
|
|
50
|
-
- **Automatic retry**: Network errors are automatically retried with exponential backoff (when using API loader)
|
|
51
|
-
- **Memory leak prevention**: LRU cache for Translator instances to prevent memory accumulation
|
|
52
|
-
- **Production-optimized**: Console logs are automatically suppressed in production mode
|
|
28
|
+
> Peer dependencies: react >=19.0.0
|
|
53
29
|
|
|
54
30
|
## Quick Start
|
|
55
31
|
|
|
56
|
-
### Basic Setup
|
|
57
|
-
|
|
58
|
-
```tsx
|
|
59
|
-
// app/layout.tsx (Next.js App Router)
|
|
60
|
-
import { createCoreI18n } from '@hua-labs/i18n-core';
|
|
61
|
-
|
|
62
|
-
export default function RootLayout({ children }) {
|
|
63
|
-
return (
|
|
64
|
-
<html>
|
|
65
|
-
<body>
|
|
66
|
-
{createCoreI18n({
|
|
67
|
-
defaultLanguage: 'ko',
|
|
68
|
-
fallbackLanguage: 'en',
|
|
69
|
-
namespaces: ['common', 'pages']
|
|
70
|
-
})({ children })}
|
|
71
|
-
</body>
|
|
72
|
-
</html>
|
|
73
|
-
);
|
|
74
|
-
}
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
### Using Translations
|
|
78
|
-
|
|
79
|
-
```tsx
|
|
80
|
-
import { useTranslation } from '@hua-labs/i18n-core';
|
|
81
|
-
|
|
82
|
-
function MyComponent() {
|
|
83
|
-
const { t } = useTranslation();
|
|
84
|
-
|
|
85
|
-
return (
|
|
86
|
-
<div>
|
|
87
|
-
<h1>{t('common:welcome')}</h1>
|
|
88
|
-
<p>{t('pages:home.title')}</p>
|
|
89
|
-
</div>
|
|
90
|
-
);
|
|
91
|
-
}
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
## Translation Loaders
|
|
95
|
-
|
|
96
|
-
The library supports three translation loading strategies:
|
|
97
|
-
|
|
98
|
-
### 1. API Loader (Default, Recommended)
|
|
99
|
-
|
|
100
|
-
Loads translations through API routes. Best for production environments.
|
|
101
|
-
|
|
102
32
|
```tsx
|
|
103
|
-
createCoreI18n
|
|
104
|
-
translationLoader: 'api',
|
|
105
|
-
translationApiPath: '/api/translations', // default
|
|
106
|
-
defaultLanguage: 'ko',
|
|
107
|
-
namespaces: ['common', 'pages']
|
|
108
|
-
})
|
|
109
|
-
```
|
|
110
|
-
|
|
111
|
-
**API Route Example (Next.js):**
|
|
112
|
-
|
|
113
|
-
```tsx
|
|
114
|
-
// app/api/translations/[language]/[namespace]/route.ts
|
|
115
|
-
import { NextResponse } from 'next/server';
|
|
116
|
-
import { readFile } from 'fs/promises';
|
|
117
|
-
import { join } from 'path';
|
|
33
|
+
import { createCoreI18n, useTranslation } from '@hua-labs/i18n-core';
|
|
118
34
|
|
|
119
|
-
|
|
120
|
-
request: Request,
|
|
121
|
-
{ params }: { params: Promise<{ language: string; namespace: string }> }
|
|
122
|
-
) {
|
|
123
|
-
const { language, namespace } = await params;
|
|
124
|
-
const translationPath = join(
|
|
125
|
-
process.cwd(),
|
|
126
|
-
'translations',
|
|
127
|
-
language,
|
|
128
|
-
`${namespace}.json`
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
try {
|
|
132
|
-
const fileContent = await readFile(translationPath, 'utf-8');
|
|
133
|
-
return NextResponse.json(JSON.parse(fileContent), {
|
|
134
|
-
headers: {
|
|
135
|
-
'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate=86400'
|
|
136
|
-
}
|
|
137
|
-
});
|
|
138
|
-
} catch (error) {
|
|
139
|
-
return NextResponse.json({ error: 'Translation not found' }, { status: 404 });
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
### 2. Static File Loader
|
|
145
|
-
|
|
146
|
-
Loads translations from static JSON files in the public directory.
|
|
147
|
-
|
|
148
|
-
```tsx
|
|
149
|
-
createCoreI18n({
|
|
150
|
-
translationLoader: 'static',
|
|
151
|
-
defaultLanguage: 'ko',
|
|
152
|
-
namespaces: ['common', 'pages']
|
|
153
|
-
})
|
|
154
|
-
```
|
|
155
|
-
|
|
156
|
-
The loader will try these paths:
|
|
157
|
-
- `/translations/{language}/{namespace}.json`
|
|
158
|
-
- `../translations/{language}/{namespace}.json`
|
|
159
|
-
- `./translations/{language}/${namespace}.json`
|
|
160
|
-
- `translations/{language}/${namespace}.json`
|
|
161
|
-
- `../../translations/{language}/${namespace}.json`
|
|
162
|
-
|
|
163
|
-
### 3. Custom Loader
|
|
164
|
-
|
|
165
|
-
Use your own translation loading function.
|
|
166
|
-
|
|
167
|
-
```tsx
|
|
168
|
-
createCoreI18n({
|
|
169
|
-
translationLoader: 'custom',
|
|
170
|
-
loadTranslations: async (language, namespace) => {
|
|
171
|
-
// Load from database, CMS, or any other source
|
|
172
|
-
const response = await fetch(`https://api.example.com/translations/${language}/${namespace}`);
|
|
173
|
-
return response.json();
|
|
174
|
-
},
|
|
35
|
+
const I18nProvider = createCoreI18n({
|
|
175
36
|
defaultLanguage: 'ko',
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
## File Structure
|
|
181
|
-
|
|
182
|
-
Recommended file structure for translations:
|
|
183
|
-
|
|
184
|
-
```
|
|
185
|
-
your-app/
|
|
186
|
-
├── translations/
|
|
187
|
-
│ ├── ko/
|
|
188
|
-
│ │ ├── common.json
|
|
189
|
-
│ │ ├── pages.json
|
|
190
|
-
│ │ └── footer.json
|
|
191
|
-
│ └── en/
|
|
192
|
-
│ ├── common.json
|
|
193
|
-
│ ├── pages.json
|
|
194
|
-
│ └── footer.json
|
|
195
|
-
└── app/
|
|
196
|
-
└── layout.tsx
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
## Translation File Format
|
|
200
|
-
|
|
201
|
-
```json
|
|
202
|
-
// translations/en/common.json
|
|
203
|
-
{
|
|
204
|
-
"welcome": "Welcome",
|
|
205
|
-
"hello": "Hello",
|
|
206
|
-
"goodbye": "Goodbye",
|
|
207
|
-
"loading": "Loading...",
|
|
208
|
-
"error": "An error occurred"
|
|
209
|
-
}
|
|
210
|
-
```
|
|
211
|
-
|
|
212
|
-
```json
|
|
213
|
-
// translations/en/pages.json
|
|
214
|
-
{
|
|
215
|
-
"home": {
|
|
216
|
-
"title": "Home",
|
|
217
|
-
"description": "Home page"
|
|
218
|
-
},
|
|
219
|
-
"about": {
|
|
220
|
-
"title": "About",
|
|
221
|
-
"description": "About page"
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
## Usage
|
|
227
|
-
|
|
228
|
-
### Basic Translation
|
|
229
|
-
|
|
230
|
-
```tsx
|
|
231
|
-
import { useTranslation } from '@hua-labs/i18n-core';
|
|
232
|
-
|
|
233
|
-
function MyComponent() {
|
|
234
|
-
const { t } = useTranslation();
|
|
235
|
-
|
|
236
|
-
return (
|
|
237
|
-
<div>
|
|
238
|
-
<h1>{t('common:welcome')}</h1>
|
|
239
|
-
<p>{t('pages:home.title')}</p>
|
|
240
|
-
</div>
|
|
241
|
-
);
|
|
242
|
-
}
|
|
243
|
-
```
|
|
244
|
-
|
|
245
|
-
### Translation with Parameters
|
|
246
|
-
|
|
247
|
-
```tsx
|
|
248
|
-
import { useTranslation } from '@hua-labs/i18n-core';
|
|
249
|
-
|
|
250
|
-
function MyComponent() {
|
|
251
|
-
const { tWithParams } = useTranslation();
|
|
252
|
-
|
|
253
|
-
return (
|
|
254
|
-
<div>
|
|
255
|
-
<p>{tWithParams('common:greeting', { name: 'John' })}</p>
|
|
256
|
-
</div>
|
|
257
|
-
);
|
|
258
|
-
}
|
|
259
|
-
```
|
|
260
|
-
|
|
261
|
-
Translation file:
|
|
262
|
-
```json
|
|
263
|
-
{
|
|
264
|
-
"greeting": "Hello, {{name}}!"
|
|
265
|
-
}
|
|
266
|
-
```
|
|
267
|
-
|
|
268
|
-
### Getting Raw Values (Arrays and Objects)
|
|
269
|
-
|
|
270
|
-
Use `getRawValue` to access arrays, objects, or any non-string values from translation files:
|
|
271
|
-
|
|
272
|
-
```tsx
|
|
273
|
-
import { useTranslation } from '@hua-labs/i18n-core';
|
|
274
|
-
|
|
275
|
-
function MyComponent() {
|
|
276
|
-
const { getRawValue } = useTranslation();
|
|
277
|
-
|
|
278
|
-
// Get an array
|
|
279
|
-
const features = getRawValue('common:features') as string[];
|
|
280
|
-
|
|
281
|
-
// Get an object
|
|
282
|
-
const metadata = getRawValue('common:metadata') as Record<string, string>;
|
|
283
|
-
|
|
284
|
-
return (
|
|
285
|
-
<div>
|
|
286
|
-
<ul>
|
|
287
|
-
{features?.map((feature, index) => (
|
|
288
|
-
<li key={index}>{feature}</li>
|
|
289
|
-
))}
|
|
290
|
-
</ul>
|
|
291
|
-
<div>
|
|
292
|
-
<p>Version: {metadata?.version}</p>
|
|
293
|
-
<p>Author: {metadata?.author}</p>
|
|
294
|
-
</div>
|
|
295
|
-
</div>
|
|
296
|
-
);
|
|
297
|
-
}
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
Translation file:
|
|
301
|
-
```json
|
|
302
|
-
{
|
|
303
|
-
"features": ["Fast", "Lightweight", "Type-safe"],
|
|
304
|
-
"metadata": {
|
|
305
|
-
"version": "1.0.0",
|
|
306
|
-
"author": "HUA Labs"
|
|
307
|
-
}
|
|
308
|
-
}
|
|
309
|
-
```
|
|
310
|
-
|
|
311
|
-
### Language Switching
|
|
312
|
-
|
|
313
|
-
```tsx
|
|
314
|
-
import { useLanguageChange } from '@hua-labs/i18n-core';
|
|
37
|
+
fallbackLanguage: 'en',
|
|
38
|
+
namespaces: ['common', 'pages'],
|
|
39
|
+
});
|
|
315
40
|
|
|
316
|
-
function
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
return (
|
|
320
|
-
<div>
|
|
321
|
-
{supportedLanguages.map(lang => (
|
|
322
|
-
<button
|
|
323
|
-
key={lang.code}
|
|
324
|
-
onClick={() => changeLanguage(lang.code)}
|
|
325
|
-
disabled={lang.code === currentLanguage}
|
|
326
|
-
>
|
|
327
|
-
{lang.nativeName}
|
|
328
|
-
</button>
|
|
329
|
-
))}
|
|
330
|
-
</div>
|
|
331
|
-
);
|
|
41
|
+
export default function Layout({ children }) {
|
|
42
|
+
return <I18nProvider>{children}</I18nProvider>;
|
|
332
43
|
}
|
|
333
|
-
```
|
|
334
44
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
```tsx
|
|
338
|
-
import { useTranslation } from '@hua-labs/i18n-core';
|
|
339
|
-
|
|
340
|
-
function MyComponent() {
|
|
341
|
-
const {
|
|
342
|
-
t,
|
|
343
|
-
tWithParams,
|
|
344
|
-
currentLanguage,
|
|
345
|
-
setLanguage,
|
|
346
|
-
isLoading,
|
|
347
|
-
error,
|
|
348
|
-
supportedLanguages,
|
|
349
|
-
isInitialized,
|
|
350
|
-
debug
|
|
351
|
-
} = useTranslation();
|
|
352
|
-
|
|
353
|
-
if (isLoading) {
|
|
354
|
-
return <div>Loading translations...</div>;
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
if (error) {
|
|
358
|
-
return <div>Error: {error.message}</div>;
|
|
359
|
-
}
|
|
360
|
-
|
|
45
|
+
function Welcome() {
|
|
46
|
+
const { t, tPlural } = useTranslation();
|
|
361
47
|
return (
|
|
362
48
|
<div>
|
|
363
49
|
<h1>{t('common:welcome')}</h1>
|
|
364
|
-
<p>
|
|
365
|
-
<p>Loaded namespaces: {debug.getLoadedNamespaces().join(', ')}</p>
|
|
50
|
+
<p>{tPlural('common:total_count', 5)}</p>
|
|
366
51
|
</div>
|
|
367
52
|
);
|
|
368
53
|
}
|
|
369
|
-
```
|
|
370
|
-
|
|
371
|
-
## SSR Support
|
|
372
|
-
|
|
373
|
-
### Server-Side Translation
|
|
374
|
-
|
|
375
|
-
```tsx
|
|
376
|
-
import { Translator, ssrTranslate, serverTranslate } from '@hua-labs/i18n-core';
|
|
377
|
-
|
|
378
|
-
// Using Translator class
|
|
379
|
-
export async function getServerTranslations(language: string) {
|
|
380
|
-
const translator = await Translator.create({
|
|
381
|
-
defaultLanguage: language,
|
|
382
|
-
namespaces: ['common', 'pages'],
|
|
383
|
-
loadTranslations: async (lang, namespace) => {
|
|
384
|
-
const path = `./translations/${lang}/${namespace}.json`;
|
|
385
|
-
return (await import(path)).default;
|
|
386
|
-
}
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
return {
|
|
390
|
-
welcome: translator.translate('common:welcome'),
|
|
391
|
-
title: translator.translate('pages:home.title')
|
|
392
|
-
};
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
// Using helper functions
|
|
396
|
-
export function getStaticTranslations(language: string) {
|
|
397
|
-
const translations = require(`./translations/${language}/common.json`);
|
|
398
|
-
|
|
399
|
-
return {
|
|
400
|
-
welcome: ssrTranslate({
|
|
401
|
-
translations,
|
|
402
|
-
key: 'common:welcome',
|
|
403
|
-
language
|
|
404
|
-
})
|
|
405
|
-
};
|
|
406
|
-
}
|
|
407
|
-
```
|
|
408
|
-
|
|
409
|
-
### SSR with Initial Translations (Recommended)
|
|
410
|
-
|
|
411
|
-
```tsx
|
|
412
|
-
// app/layout.tsx (Server Component)
|
|
413
|
-
import { loadSSRTranslations } from './lib/ssr-translations';
|
|
414
|
-
import { createCoreI18n } from '@hua-labs/i18n-core';
|
|
415
|
-
|
|
416
|
-
export default async function RootLayout({ children }) {
|
|
417
|
-
// Load translation data from SSR
|
|
418
|
-
const ssrTranslations = await loadSSRTranslations('ko');
|
|
419
|
-
|
|
420
|
-
const I18nProvider = createCoreI18n({
|
|
421
|
-
defaultLanguage: 'ko',
|
|
422
|
-
fallbackLanguage: 'en',
|
|
423
|
-
namespaces: ['common', 'navigation', 'footer'],
|
|
424
|
-
initialTranslations: ssrTranslations, // Pass SSR translation data
|
|
425
|
-
translationLoader: 'api'
|
|
426
|
-
});
|
|
427
|
-
|
|
428
|
-
return (
|
|
429
|
-
<html lang="ko">
|
|
430
|
-
<body>
|
|
431
|
-
<I18nProvider>
|
|
432
|
-
{children}
|
|
433
|
-
</I18nProvider>
|
|
434
|
-
</body>
|
|
435
|
-
</html>
|
|
436
|
-
);
|
|
437
|
-
}
|
|
438
|
-
```
|
|
439
|
-
|
|
440
|
-
## Key Rules
|
|
441
54
|
|
|
442
|
-
### Namespace Keys
|
|
443
|
-
|
|
444
|
-
Always include namespace in the key:
|
|
445
|
-
|
|
446
|
-
```tsx
|
|
447
|
-
t('common:welcome') // common.json -> welcome
|
|
448
|
-
t('pages:home.title') // pages.json -> home.title
|
|
449
|
-
t('footer:brand_name') // footer.json -> brand_name
|
|
450
|
-
```
|
|
451
|
-
|
|
452
|
-
### Common Namespace Shortcut
|
|
453
|
-
|
|
454
|
-
If the key doesn't include a namespace, it defaults to 'common':
|
|
455
|
-
|
|
456
|
-
```tsx
|
|
457
|
-
t('welcome') // same as t('common:welcome')
|
|
458
|
-
t('hello') // same as t('common:hello')
|
|
459
|
-
```
|
|
460
|
-
|
|
461
|
-
## Configuration Options
|
|
462
|
-
|
|
463
|
-
```tsx
|
|
464
|
-
createCoreI18n({
|
|
465
|
-
// Required
|
|
466
|
-
defaultLanguage: 'ko',
|
|
467
|
-
|
|
468
|
-
// Optional
|
|
469
|
-
fallbackLanguage: 'en',
|
|
470
|
-
namespaces: ['common', 'pages'],
|
|
471
|
-
debug: false,
|
|
472
|
-
|
|
473
|
-
// Loader options
|
|
474
|
-
translationLoader: 'api' | 'static' | 'custom',
|
|
475
|
-
translationApiPath: '/api/translations',
|
|
476
|
-
loadTranslations: async (language, namespace) => {
|
|
477
|
-
// Custom loader function
|
|
478
|
-
},
|
|
479
|
-
|
|
480
|
-
// SSR optimization: Pre-loaded translations (no network requests)
|
|
481
|
-
// Prevents missing key exposure during initial load
|
|
482
|
-
initialTranslations: {
|
|
483
|
-
ko: {
|
|
484
|
-
common: { /* ... */ },
|
|
485
|
-
navigation: { /* ... */ }
|
|
486
|
-
},
|
|
487
|
-
en: {
|
|
488
|
-
common: { /* ... */ },
|
|
489
|
-
navigation: { /* ... */ }
|
|
490
|
-
}
|
|
491
|
-
},
|
|
492
|
-
|
|
493
|
-
// Auto language sync (disabled by default when using Zustand adapter)
|
|
494
|
-
autoLanguageSync: false
|
|
495
|
-
})
|
|
496
55
|
```
|
|
497
56
|
|
|
498
|
-
##
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
The library automatically prevents flickering during language changes by temporarily showing translations from the previous language while new translations are loading.
|
|
525
|
-
|
|
526
|
-
**How it works:**
|
|
527
|
-
1. When language changes, `translator.setLanguage()` is called
|
|
528
|
-
2. If a translation key is not found in the new language yet, the library checks other loaded languages
|
|
529
|
-
3. If found, it temporarily returns the previous language's translation
|
|
530
|
-
4. Once the new language's translation is loaded, it automatically updates
|
|
531
|
-
|
|
532
|
-
This ensures a smooth user experience without showing translation keys or empty strings.
|
|
533
|
-
|
|
534
|
-
## Debug Mode
|
|
535
|
-
|
|
536
|
-
Enable debug mode to see translation loading and missing keys:
|
|
537
|
-
|
|
538
|
-
```tsx
|
|
539
|
-
createCoreI18n({
|
|
540
|
-
debug: true,
|
|
541
|
-
// ... other options
|
|
542
|
-
})
|
|
543
|
-
```
|
|
544
|
-
|
|
545
|
-
**Note**: In production (`debug: false`), console logs are automatically suppressed to improve performance and prevent information leakage.
|
|
546
|
-
|
|
547
|
-
### Missing Key Overlay (Development)
|
|
548
|
-
|
|
549
|
-
Display missing translation keys in development:
|
|
550
|
-
|
|
551
|
-
```tsx
|
|
552
|
-
import { MissingKeyOverlay } from '@hua-labs/i18n-core/components/MissingKeyOverlay';
|
|
553
|
-
|
|
554
|
-
function DebugBar() {
|
|
555
|
-
if (process.env.NODE_ENV !== 'development') return null;
|
|
556
|
-
|
|
557
|
-
return <MissingKeyOverlay />;
|
|
558
|
-
}
|
|
559
|
-
```
|
|
560
|
-
|
|
561
|
-
## Error Handling
|
|
562
|
-
|
|
563
|
-
The library includes built-in error handling:
|
|
564
|
-
|
|
565
|
-
- **Automatic fallback**: Falls back to default language when translations are missing
|
|
566
|
-
- **Missing key handling**: Returns key in debug mode, empty string in production
|
|
567
|
-
- **Network error recovery**: Automatic retry with exponential backoff (when using API loader)
|
|
568
|
-
- **Cache invalidation**: Automatically clears cache on errors
|
|
569
|
-
- **Error classification**: Distinguishes between recoverable and non-recoverable errors
|
|
570
|
-
- **Memory leak prevention**: LRU cache for Translator instances (max 10 instances)
|
|
571
|
-
|
|
572
|
-
## API Reference
|
|
573
|
-
|
|
574
|
-
### Main Exports
|
|
575
|
-
|
|
576
|
-
- `createCoreI18n(options?)` - Creates i18n Provider component
|
|
577
|
-
- `useTranslation()` - Hook for translations and language state
|
|
578
|
-
- `useLanguageChange()` - Hook for language switching
|
|
579
|
-
- `Translator` - Core translation class (for SSR)
|
|
580
|
-
- `ssrTranslate()` / `serverTranslate()` - Server-side translation helpers
|
|
581
|
-
|
|
582
|
-
## Requirements
|
|
583
|
-
|
|
584
|
-
- React >= 16.8.0
|
|
585
|
-
- TypeScript (recommended)
|
|
586
|
-
|
|
587
|
-
## Bundle Size
|
|
588
|
-
|
|
589
|
-
- **~2.8 KB** gzipped
|
|
590
|
-
- Zero dependencies (React only as peer dependency)
|
|
591
|
-
|
|
592
|
-
## Troubleshooting
|
|
593
|
-
|
|
594
|
-
### Translations Not Loading
|
|
595
|
-
|
|
596
|
-
1. Check file paths match the expected structure
|
|
597
|
-
2. Verify JSON format is valid
|
|
598
|
-
3. Check network requests in browser DevTools
|
|
599
|
-
4. Enable debug mode to see loading logs
|
|
600
|
-
|
|
601
|
-
### Missing Keys
|
|
602
|
-
|
|
603
|
-
1. Ensure namespace is included in key: `t('namespace:key')`
|
|
604
|
-
2. Check translation files contain the key
|
|
605
|
-
3. Verify namespace is included in config: `namespaces: ['namespace']`
|
|
606
|
-
|
|
607
|
-
### API Loader Not Working
|
|
608
|
-
|
|
609
|
-
1. Verify API route is accessible
|
|
610
|
-
2. Check API route returns valid JSON
|
|
611
|
-
3. Ensure API route handles 404 errors gracefully
|
|
612
|
-
4. Check CORS settings if loading from different domain
|
|
57
|
+
## API
|
|
58
|
+
|
|
59
|
+
| Export | Type | Description |
|
|
60
|
+
|--------|------|-------------|
|
|
61
|
+
| `useTranslation` | hook | Main translation hook — returns t, tPlural, tArray, getRawValue and state |
|
|
62
|
+
| `useLanguageChange` | hook | Language change hook with validation against supported languages |
|
|
63
|
+
| `useI18n` | hook | Full context hook — all translation functions plus language management |
|
|
64
|
+
| `I18nProvider` | component | Direct Provider component (for advanced use) |
|
|
65
|
+
| `Translator` | class | Core translator class (for manual instantiation) |
|
|
66
|
+
| `ssrTranslate` | function | Server-side translation function (no React needed) |
|
|
67
|
+
| `serverTranslate` | function | Server-side translate with full config |
|
|
68
|
+
| `I18nConfig` | type | |
|
|
69
|
+
| `I18nContextType` | type | |
|
|
70
|
+
| `TranslationParams` | type | |
|
|
71
|
+
| `TypedTranslationKeys` | type | |
|
|
72
|
+
| `ResolveStringKey` | type | |
|
|
73
|
+
| `ResolveArrayKey` | type | |
|
|
74
|
+
| `ResolvePluralKey` | type | |
|
|
75
|
+
| `PluralValue` | type | |
|
|
76
|
+
| `PluralCategory` | type | |
|
|
77
|
+
| `createCoreI18n` | function | Create an i18n Provider component with configuration |
|
|
78
|
+
| `CoreProvider` | function | Minimal Provider with zero config |
|
|
79
|
+
| `createLanguageProvider` | function | Create a Provider with just a language setting |
|
|
80
|
+
| `createNamespaceProvider` | function | Create a Provider with just namespace settings |
|
|
81
|
+
| `createCustomLoaderProvider` | function | Create a Provider with custom translation loader |
|
|
613
82
|
|
|
614
83
|
## Documentation
|
|
615
84
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
## Code Quality
|
|
619
|
-
|
|
620
|
-
This package has been refactored for better maintainability:
|
|
621
|
-
|
|
622
|
-
- **Modular functions**: Translation logic split into focused helper methods
|
|
623
|
-
- **Type safety**: Improved type guards and error handling
|
|
624
|
-
- **Performance**: Optimized translation lookup with proper memoization
|
|
625
|
-
- **Code clarity**: Removed commented code and improved function organization
|
|
85
|
+
[Full Documentation](https://docs.hua-labs.com)
|
|
626
86
|
|
|
627
87
|
## Related Packages
|
|
628
88
|
|
|
629
|
-
- `@hua-labs/i18n-core-zustand
|
|
630
|
-
- `@hua-labs/i18n-
|
|
631
|
-
- `@hua-labs/i18n-
|
|
632
|
-
- `@hua-labs/i18n-
|
|
89
|
+
- [`@hua-labs/i18n-core-zustand`](https://www.npmjs.com/package/@hua-labs/i18n-core-zustand)
|
|
90
|
+
- [`@hua-labs/eslint-plugin-i18n`](https://www.npmjs.com/package/@hua-labs/eslint-plugin-i18n)
|
|
91
|
+
- [`@hua-labs/i18n-loaders`](https://www.npmjs.com/package/@hua-labs/i18n-loaders)
|
|
92
|
+
- [`@hua-labs/i18n-formatters`](https://www.npmjs.com/package/@hua-labs/i18n-formatters)
|
|
633
93
|
|
|
634
94
|
## License
|
|
635
95
|
|
|
636
|
-
MIT
|
|
96
|
+
MIT — [HUA Labs](https://hua-labs.com)
|