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