@hua-labs/i18n-core 1.0.0 → 2.0.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 CHANGED
@@ -1,636 +1,636 @@
1
- # @hua-labs/i18n-core
2
-
3
- **Type-safe i18n library with SSR/CSR support and state management integration**
4
-
5
- HUA Labs - Core Internationalization Library
6
-
7
- Lightweight i18n library for React applications with essential translation features only.
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
22
-
23
- - **[CodeSandbox Template](../../examples/codesandbox-template/)** - Quick start template
24
- - **[Next.js Example](../../examples/next-app-router-example/)** - Complete Next.js App Router example
25
-
26
- ## Installation
27
-
28
- ```bash
29
- npm install @hua-labs/i18n-core
30
- # or
31
- yarn add @hua-labs/i18n-core
32
- # or
33
- pnpm add @hua-labs/i18n-core
34
- ```
35
-
36
- ## Features
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
53
-
54
- ## Quick Start
55
-
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
- ```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';
118
-
119
- export async function GET(
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
- },
175
- defaultLanguage: 'ko',
176
- namespaces: ['common', 'pages']
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';
315
-
316
- function LanguageSwitcher() {
317
- const { changeLanguage, supportedLanguages, currentLanguage } = useLanguageChange();
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
- );
332
- }
333
- ```
334
-
335
- ### Advanced Hook Usage
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
-
361
- return (
362
- <div>
363
- <h1>{t('common:welcome')}</h1>
364
- <p>Current language: {currentLanguage}</p>
365
- <p>Loaded namespaces: {debug.getLoadedNamespaces().join(', ')}</p>
366
- </div>
367
- );
368
- }
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
-
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
- ```
497
-
498
- ## State Management Integration
499
-
500
- ### Zustand Integration
501
-
502
- For Zustand users, use the dedicated adapter package:
503
-
504
- ```bash
505
- pnpm add @hua-labs/i18n-core-zustand zustand
506
- ```
507
-
508
- ```tsx
509
- import { createZustandI18n } from '@hua-labs/i18n-core-zustand';
510
- import { useAppStore } from './store/useAppStore';
511
-
512
- export const I18nProvider = createZustandI18n(useAppStore, {
513
- fallbackLanguage: 'en',
514
- namespaces: ['common', 'navigation'],
515
- defaultLanguage: 'ko', // SSR initial language
516
- initialTranslations: ssrTranslations // Optional: SSR translations
517
- });
518
- ```
519
-
520
- See [@hua-labs/i18n-core-zustand README](../hua-i18n-core-zustand/README.md) for full documentation.
521
-
522
- ## Language Change Optimization
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
613
-
614
- ## Documentation
615
-
616
- - [Architecture Guide](./docs/ARCHITECTURE.md) - Core architecture and design patterns
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
626
-
627
- ## Related Packages
628
-
629
- - `@hua-labs/i18n-core-zustand`: Zustand state management integration adapter
630
- - `@hua-labs/i18n-loaders`: Production-ready loaders, caching, and preloading helpers
631
- - `@hua-labs/i18n-advanced`: Advanced features like pluralization, date formatting, etc.
632
- - `@hua-labs/i18n-debug`: Debug tools and development helpers
633
-
634
- ## License
635
-
636
- MIT
1
+ # @hua-labs/i18n-core
2
+
3
+ **Type-safe i18n library with SSR/CSR support and state management integration**
4
+
5
+ HUA Labs - Core Internationalization Library
6
+
7
+ Lightweight i18n library for React applications with essential translation features only.
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
22
+
23
+ - **[CodeSandbox Template](../../examples/codesandbox-template/)** - Quick start template
24
+ - **[Next.js Example](../../examples/next-app-router-example/)** - Complete Next.js App Router example
25
+
26
+ ## Installation
27
+
28
+ ```bash
29
+ npm install @hua-labs/i18n-core
30
+ # or
31
+ yarn add @hua-labs/i18n-core
32
+ # or
33
+ pnpm add @hua-labs/i18n-core
34
+ ```
35
+
36
+ ## Features
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
53
+
54
+ ## Quick Start
55
+
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
+ ```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';
118
+
119
+ export async function GET(
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
+ },
175
+ defaultLanguage: 'ko',
176
+ namespaces: ['common', 'pages']
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';
315
+
316
+ function LanguageSwitcher() {
317
+ const { changeLanguage, supportedLanguages, currentLanguage } = useLanguageChange();
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
+ );
332
+ }
333
+ ```
334
+
335
+ ### Advanced Hook Usage
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
+
361
+ return (
362
+ <div>
363
+ <h1>{t('common:welcome')}</h1>
364
+ <p>Current language: {currentLanguage}</p>
365
+ <p>Loaded namespaces: {debug.getLoadedNamespaces().join(', ')}</p>
366
+ </div>
367
+ );
368
+ }
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
+
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
+ ```
497
+
498
+ ## State Management Integration
499
+
500
+ ### Zustand Integration
501
+
502
+ For Zustand users, use the dedicated adapter package:
503
+
504
+ ```bash
505
+ pnpm add @hua-labs/i18n-core-zustand zustand
506
+ ```
507
+
508
+ ```tsx
509
+ import { createZustandI18n } from '@hua-labs/i18n-core-zustand';
510
+ import { useAppStore } from './store/useAppStore';
511
+
512
+ export const I18nProvider = createZustandI18n(useAppStore, {
513
+ fallbackLanguage: 'en',
514
+ namespaces: ['common', 'navigation'],
515
+ defaultLanguage: 'ko', // SSR initial language
516
+ initialTranslations: ssrTranslations // Optional: SSR translations
517
+ });
518
+ ```
519
+
520
+ See [@hua-labs/i18n-core-zustand README](../hua-i18n-core-zustand/README.md) for full documentation.
521
+
522
+ ## Language Change Optimization
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
613
+
614
+ ## Documentation
615
+
616
+ - [Architecture Guide](./docs/ARCHITECTURE.md) - Core architecture and design patterns
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
626
+
627
+ ## Related Packages
628
+
629
+ - `@hua-labs/i18n-core-zustand`: Zustand state management integration adapter
630
+ - `@hua-labs/i18n-loaders`: Production-ready loaders, caching, and preloading helpers
631
+ - `@hua-labs/i18n-advanced`: Advanced features like pluralization, date formatting, etc.
632
+ - `@hua-labs/i18n-debug`: Debug tools and development helpers
633
+
634
+ ## License
635
+
636
+ MIT