@hua-labs/i18n-loaders 1.0.0 → 1.1.0-alpha.0.1

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,415 +1,415 @@
1
- # @hua-labs/i18n-loaders
2
-
3
- Production-ready translation loaders, caching, and preloading utilities. Use with `@hua-labs/i18n-core` to reuse proven loading strategies from SUM API.
4
-
5
- ## Key Features
6
-
7
- - API-based translation loader (`createApiTranslationLoader`)
8
- - Built-in TTL/global cache/duplicate request prevention
9
- - Namespace preloading & fallback language warming
10
- - Default translation (JSON) merging (SUM API style)
11
- - Works on both server and client
12
- - **Production tested**: Currently used in SUM API
13
-
14
- ## Installation
15
-
16
- ```bash
17
- pnpm add @hua-labs/i18n-loaders
18
- # or
19
- npm install @hua-labs/i18n-loaders
20
- ```
21
-
22
- ## Quick Start
23
-
24
- ### Basic Usage
25
-
26
- ```ts
27
- import { createCoreI18n } from '@hua-labs/i18n-core';
28
- import { createApiTranslationLoader } from '@hua-labs/i18n-loaders';
29
-
30
- const loadTranslations = createApiTranslationLoader({
31
- translationApiPath: '/api/translations',
32
- cacheTtlMs: 60_000, // 1 minute
33
- enableGlobalCache: true
34
- });
35
-
36
- export const I18nProvider = createCoreI18n({
37
- defaultLanguage: 'ko',
38
- fallbackLanguage: 'en',
39
- namespaces: ['common', 'dashboard'],
40
- translationLoader: 'custom',
41
- loadTranslations
42
- });
43
- ```
44
-
45
- ### Preloading
46
-
47
- ```ts
48
- import { createApiTranslationLoader, preloadNamespaces } from '@hua-labs/i18n-loaders';
49
-
50
- const loadTranslations = createApiTranslationLoader({
51
- translationApiPath: '/api/translations'
52
- });
53
-
54
- // Preload required namespaces at app startup
55
- preloadNamespaces('ko', ['common', 'dashboard'], loadTranslations);
56
- ```
57
-
58
- ### Using Default Translation Merging
59
-
60
- ```ts
61
- import { createApiTranslationLoader, withDefaultTranslations } from '@hua-labs/i18n-loaders';
62
-
63
- const apiLoader = createApiTranslationLoader({
64
- translationApiPath: '/api/translations'
65
- });
66
-
67
- const defaultTranslations = {
68
- ko: {
69
- common: {
70
- welcome: 'Welcome',
71
- hello: 'Hello'
72
- }
73
- },
74
- en: {
75
- common: {
76
- welcome: 'Welcome',
77
- hello: 'Hello'
78
- }
79
- }
80
- };
81
-
82
- // Use default translations if API fails, merge if API succeeds
83
- const loadTranslations = withDefaultTranslations(apiLoader, defaultTranslations);
84
- ```
85
-
86
- ## API Reference
87
-
88
- ### createApiTranslationLoader
89
-
90
- Creates an API-based translation loader. Includes TTL caching, duplicate request prevention, and global cache.
91
-
92
- ```ts
93
- function createApiTranslationLoader(
94
- options?: ApiLoaderOptions
95
- ): TranslationLoader
96
- ```
97
-
98
- #### Options
99
-
100
- ```ts
101
- interface ApiLoaderOptions {
102
- // API path (default: '/api/translations')
103
- translationApiPath?: string;
104
-
105
- // Base URL (for server-side use)
106
- baseUrl?: string;
107
-
108
- // Local fallback URL (for development)
109
- localFallbackBaseUrl?: string;
110
-
111
- // Cache TTL (milliseconds, default: 5 minutes)
112
- cacheTtlMs?: number;
113
-
114
- // Disable cache
115
- disableCache?: boolean;
116
-
117
- // Fetch request options
118
- requestInit?: RequestInit | ((language: string, namespace: string) => RequestInit | undefined);
119
-
120
- // Custom fetcher (for testing)
121
- fetcher?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
122
-
123
- // Logger (default: console)
124
- logger?: Pick<typeof console, 'log' | 'warn' | 'error'>;
125
-
126
- // Retry configuration for network errors
127
- retryCount?: number; // Number of retry attempts (default: 0, no retry)
128
- retryDelay?: number; // Base delay in milliseconds (default: 1000), uses exponential backoff
129
- }
130
- ```
131
-
132
- #### Examples
133
-
134
- ```ts
135
- // Basic usage
136
- const loader = createApiTranslationLoader();
137
-
138
- // Custom options
139
- const loader = createApiTranslationLoader({
140
- translationApiPath: '/api/v2/translations',
141
- cacheTtlMs: 10 * 60 * 1000, // 10 minutes
142
- disableCache: false,
143
- retryCount: 3, // Retry 3 times on network errors
144
- retryDelay: 1000, // 1 second base delay (exponential backoff)
145
- requestInit: {
146
- headers: {
147
- 'Authorization': 'Bearer token'
148
- }
149
- }
150
- });
151
-
152
- // Dynamic request options
153
- const loader = createApiTranslationLoader({
154
- requestInit: (language, namespace) => ({
155
- headers: {
156
- 'X-Language': language,
157
- 'X-Namespace': namespace
158
- }
159
- })
160
- });
161
- ```
162
-
163
- ### preloadNamespaces
164
-
165
- Preloads multiple namespaces in parallel.
166
-
167
- ```ts
168
- function preloadNamespaces(
169
- language: string,
170
- namespaces: string[],
171
- loader: TranslationLoader,
172
- options?: PreloadOptions
173
- ): Promise<{
174
- fulfilled: string[];
175
- rejected: unknown[];
176
- }>
177
- ```
178
-
179
- #### Options
180
-
181
- ```ts
182
- interface PreloadOptions {
183
- // Logger (default: console)
184
- logger?: Pick<typeof console, 'log' | 'warn'>;
185
-
186
- // Suppress errors (default: false)
187
- suppressErrors?: boolean;
188
- }
189
- ```
190
-
191
- #### Examples
192
-
193
- ```ts
194
- import { preloadNamespaces } from '@hua-labs/i18n-loaders';
195
-
196
- const loader = createApiTranslationLoader();
197
-
198
- // Preload multiple namespaces
199
- const result = await preloadNamespaces(
200
- 'ko',
201
- ['common', 'navigation', 'footer'],
202
- loader
203
- );
204
-
205
- console.log(`Loaded: ${result.fulfilled.length}`);
206
- console.log(`Failed: ${result.rejected.length}`);
207
- ```
208
-
209
- ### warmFallbackLanguages
210
-
211
- Pre-warms fallback languages.
212
-
213
- ```ts
214
- function warmFallbackLanguages(
215
- currentLanguage: string,
216
- languages: string[],
217
- namespaces: string[],
218
- loader: TranslationLoader,
219
- options?: PreloadOptions
220
- ): Promise<Array<{
221
- fulfilled: string[];
222
- rejected: unknown[];
223
- }>>
224
- ```
225
-
226
- #### Examples
227
-
228
- ```ts
229
- import { warmFallbackLanguages } from '@hua-labs/i18n-loaders';
230
-
231
- const loader = createApiTranslationLoader();
232
-
233
- // When current language is 'ko', preload 'en', 'ja'
234
- await warmFallbackLanguages(
235
- 'ko',
236
- ['ko', 'en', 'ja'],
237
- ['common', 'navigation'],
238
- loader
239
- );
240
- ```
241
-
242
- ### withDefaultTranslations
243
-
244
- Merges default translations with API translations. Uses default translations if API fails.
245
-
246
- ```ts
247
- function withDefaultTranslations(
248
- loader: TranslationLoader,
249
- defaults: DefaultTranslations
250
- ): TranslationLoader
251
- ```
252
-
253
- #### Types
254
-
255
- ```ts
256
- type DefaultTranslations = Record<
257
- string, // language
258
- Record<string, TranslationRecord> // namespace -> translations
259
- >;
260
- ```
261
-
262
- #### Examples
263
-
264
- ```ts
265
- import { withDefaultTranslations } from '@hua-labs/i18n-loaders';
266
-
267
- const apiLoader = createApiTranslationLoader();
268
-
269
- const defaults = {
270
- ko: {
271
- common: {
272
- welcome: 'Welcome',
273
- hello: 'Hello'
274
- }
275
- },
276
- en: {
277
- common: {
278
- welcome: 'Welcome',
279
- hello: 'Hello'
280
- }
281
- }
282
- };
283
-
284
- const loader = withDefaultTranslations(apiLoader, defaults);
285
-
286
- // Merges with default translations if API succeeds
287
- // Uses only default translations if API fails
288
- const translations = await loader('ko', 'common');
289
- ```
290
-
291
- ## Usage Scenarios
292
-
293
- ### Next.js App Router
294
-
295
- ```tsx
296
- // lib/i18n-config.ts
297
- import { createCoreI18n } from '@hua-labs/i18n-core';
298
- import { createApiTranslationLoader, preloadNamespaces } from '@hua-labs/i18n-loaders';
299
-
300
- const loadTranslations = createApiTranslationLoader({
301
- translationApiPath: '/api/translations',
302
- cacheTtlMs: 60_000
303
- });
304
-
305
- export const I18nProvider = createCoreI18n({
306
- defaultLanguage: 'ko',
307
- fallbackLanguage: 'en',
308
- namespaces: ['common', 'navigation', 'footer'],
309
- translationLoader: 'custom',
310
- loadTranslations
311
- });
312
-
313
- // Use in app/layout.tsx
314
- export default function RootLayout({ children }) {
315
- // Preload on client
316
- if (typeof window !== 'undefined') {
317
- preloadNamespaces('ko', ['common', 'navigation'], loadTranslations);
318
- }
319
-
320
- return (
321
- <html>
322
- <body>
323
- <I18nProvider>{children}</I18nProvider>
324
- </body>
325
- </html>
326
- );
327
- }
328
- ```
329
-
330
- ### Using with SSR
331
-
332
- ```tsx
333
- // app/layout.tsx (Server Component)
334
- import { loadSSRTranslations } from './lib/ssr-translations';
335
- import { createCoreI18n } from '@hua-labs/i18n-core';
336
- import { createApiTranslationLoader } from '@hua-labs/i18n-loaders';
337
-
338
- export default async function RootLayout({ children }) {
339
- // Load translations from SSR
340
- const ssrTranslations = await loadSSRTranslations('ko');
341
-
342
- // Client loader
343
- const loadTranslations = createApiTranslationLoader({
344
- translationApiPath: '/api/translations'
345
- });
346
-
347
- const I18nProvider = createCoreI18n({
348
- defaultLanguage: 'ko',
349
- fallbackLanguage: 'en',
350
- namespaces: ['common', 'navigation', 'footer'],
351
- translationLoader: 'custom',
352
- loadTranslations,
353
- initialTranslations: ssrTranslations // Pass SSR translations
354
- });
355
-
356
- return (
357
- <html lang="ko">
358
- <body>
359
- <I18nProvider>{children}</I18nProvider>
360
- </body>
361
- </html>
362
- );
363
- }
364
- ```
365
-
366
- ## Caching Behavior
367
-
368
- - **TTL Cache**: Each translation is cached for `cacheTtlMs` duration
369
- - **Duplicate Request Prevention**: Reuses existing Promise if same translation is loading
370
- - **Global Cache**: Same loader instance shares cache across all components
371
-
372
- ## Error Handling
373
-
374
- - **Automatic retry**: Network errors are automatically retried with exponential backoff (configurable via `retryCount` and `retryDelay`)
375
- - Throws error on API request failure after all retries are exhausted
376
- - Falls back to default translations when using `withDefaultTranslations`
377
- - `preloadNamespaces` uses `Promise.allSettled` to continue even if some fail
378
-
379
- ### Retry Configuration
380
-
381
- ```ts
382
- const loader = createApiTranslationLoader({
383
- translationApiPath: '/api/translations',
384
- retryCount: 3, // Retry up to 3 times on network errors (default: 0, no retry)
385
- retryDelay: 1000, // Start with 1 second delay, doubles on each retry (exponential backoff)
386
- });
387
- ```
388
-
389
- The retry mechanism uses exponential backoff:
390
- - 1st retry: waits `retryDelay` ms (e.g., 1000ms)
391
- - 2nd retry: waits `retryDelay * 2` ms (e.g., 2000ms)
392
- - 3rd retry: waits `retryDelay * 4` ms (e.g., 4000ms)
393
-
394
- ## Examples
395
-
396
- - **[Next.js Example](../../examples/next-app-router-example/)** - Complete example using API loader with caching
397
-
398
- ## Error Handling Improvements
399
-
400
- The API loader now includes enhanced error detection:
401
-
402
- - **Network error detection**: Improved detection of network failures
403
- - **HTTP status code handling**: Automatic retry for 5xx errors and 408 timeouts
404
- - **Exponential backoff**: Smart retry strategy with configurable delays
405
- - **Error type classification**: Better distinction between retryable and non-retryable errors
406
-
407
- See [API Loader Guide](./docs/API_LOADER.md) for detailed error handling documentation.
408
-
409
- ## Documentation
410
-
411
- - [API Loader Guide](./docs/API_LOADER.md) - Detailed API loader documentation and error handling
412
-
413
- ## License
414
-
415
- MIT License
1
+ # @hua-labs/i18n-loaders
2
+
3
+ Production-ready translation loaders, caching, and preloading utilities. Use with `@hua-labs/i18n-core` to reuse proven loading strategies from SUM API.
4
+
5
+ ## Key Features
6
+
7
+ - API-based translation loader (`createApiTranslationLoader`)
8
+ - Built-in TTL/global cache/duplicate request prevention
9
+ - Namespace preloading & fallback language warming
10
+ - Default translation (JSON) merging (SUM API style)
11
+ - Works on both server and client
12
+ - **Production tested**: Currently used in SUM API
13
+
14
+ ## Installation
15
+
16
+ ```bash
17
+ pnpm add @hua-labs/i18n-loaders
18
+ # or
19
+ npm install @hua-labs/i18n-loaders
20
+ ```
21
+
22
+ ## Quick Start
23
+
24
+ ### Basic Usage
25
+
26
+ ```ts
27
+ import { createCoreI18n } from '@hua-labs/i18n-core';
28
+ import { createApiTranslationLoader } from '@hua-labs/i18n-loaders';
29
+
30
+ const loadTranslations = createApiTranslationLoader({
31
+ translationApiPath: '/api/translations',
32
+ cacheTtlMs: 60_000, // 1 minute
33
+ enableGlobalCache: true
34
+ });
35
+
36
+ export const I18nProvider = createCoreI18n({
37
+ defaultLanguage: 'ko',
38
+ fallbackLanguage: 'en',
39
+ namespaces: ['common', 'dashboard'],
40
+ translationLoader: 'custom',
41
+ loadTranslations
42
+ });
43
+ ```
44
+
45
+ ### Preloading
46
+
47
+ ```ts
48
+ import { createApiTranslationLoader, preloadNamespaces } from '@hua-labs/i18n-loaders';
49
+
50
+ const loadTranslations = createApiTranslationLoader({
51
+ translationApiPath: '/api/translations'
52
+ });
53
+
54
+ // Preload required namespaces at app startup
55
+ preloadNamespaces('ko', ['common', 'dashboard'], loadTranslations);
56
+ ```
57
+
58
+ ### Using Default Translation Merging
59
+
60
+ ```ts
61
+ import { createApiTranslationLoader, withDefaultTranslations } from '@hua-labs/i18n-loaders';
62
+
63
+ const apiLoader = createApiTranslationLoader({
64
+ translationApiPath: '/api/translations'
65
+ });
66
+
67
+ const defaultTranslations = {
68
+ ko: {
69
+ common: {
70
+ welcome: 'Welcome',
71
+ hello: 'Hello'
72
+ }
73
+ },
74
+ en: {
75
+ common: {
76
+ welcome: 'Welcome',
77
+ hello: 'Hello'
78
+ }
79
+ }
80
+ };
81
+
82
+ // Use default translations if API fails, merge if API succeeds
83
+ const loadTranslations = withDefaultTranslations(apiLoader, defaultTranslations);
84
+ ```
85
+
86
+ ## API Reference
87
+
88
+ ### createApiTranslationLoader
89
+
90
+ Creates an API-based translation loader. Includes TTL caching, duplicate request prevention, and global cache.
91
+
92
+ ```ts
93
+ function createApiTranslationLoader(
94
+ options?: ApiLoaderOptions
95
+ ): TranslationLoader
96
+ ```
97
+
98
+ #### Options
99
+
100
+ ```ts
101
+ interface ApiLoaderOptions {
102
+ // API path (default: '/api/translations')
103
+ translationApiPath?: string;
104
+
105
+ // Base URL (for server-side use)
106
+ baseUrl?: string;
107
+
108
+ // Local fallback URL (for development)
109
+ localFallbackBaseUrl?: string;
110
+
111
+ // Cache TTL (milliseconds, default: 5 minutes)
112
+ cacheTtlMs?: number;
113
+
114
+ // Disable cache
115
+ disableCache?: boolean;
116
+
117
+ // Fetch request options
118
+ requestInit?: RequestInit | ((language: string, namespace: string) => RequestInit | undefined);
119
+
120
+ // Custom fetcher (for testing)
121
+ fetcher?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
122
+
123
+ // Logger (default: console)
124
+ logger?: Pick<typeof console, 'log' | 'warn' | 'error'>;
125
+
126
+ // Retry configuration for network errors
127
+ retryCount?: number; // Number of retry attempts (default: 0, no retry)
128
+ retryDelay?: number; // Base delay in milliseconds (default: 1000), uses exponential backoff
129
+ }
130
+ ```
131
+
132
+ #### Examples
133
+
134
+ ```ts
135
+ // Basic usage
136
+ const loader = createApiTranslationLoader();
137
+
138
+ // Custom options
139
+ const loader = createApiTranslationLoader({
140
+ translationApiPath: '/api/v2/translations',
141
+ cacheTtlMs: 10 * 60 * 1000, // 10 minutes
142
+ disableCache: false,
143
+ retryCount: 3, // Retry 3 times on network errors
144
+ retryDelay: 1000, // 1 second base delay (exponential backoff)
145
+ requestInit: {
146
+ headers: {
147
+ 'Authorization': 'Bearer token'
148
+ }
149
+ }
150
+ });
151
+
152
+ // Dynamic request options
153
+ const loader = createApiTranslationLoader({
154
+ requestInit: (language, namespace) => ({
155
+ headers: {
156
+ 'X-Language': language,
157
+ 'X-Namespace': namespace
158
+ }
159
+ })
160
+ });
161
+ ```
162
+
163
+ ### preloadNamespaces
164
+
165
+ Preloads multiple namespaces in parallel.
166
+
167
+ ```ts
168
+ function preloadNamespaces(
169
+ language: string,
170
+ namespaces: string[],
171
+ loader: TranslationLoader,
172
+ options?: PreloadOptions
173
+ ): Promise<{
174
+ fulfilled: string[];
175
+ rejected: unknown[];
176
+ }>
177
+ ```
178
+
179
+ #### Options
180
+
181
+ ```ts
182
+ interface PreloadOptions {
183
+ // Logger (default: console)
184
+ logger?: Pick<typeof console, 'log' | 'warn'>;
185
+
186
+ // Suppress errors (default: false)
187
+ suppressErrors?: boolean;
188
+ }
189
+ ```
190
+
191
+ #### Examples
192
+
193
+ ```ts
194
+ import { preloadNamespaces } from '@hua-labs/i18n-loaders';
195
+
196
+ const loader = createApiTranslationLoader();
197
+
198
+ // Preload multiple namespaces
199
+ const result = await preloadNamespaces(
200
+ 'ko',
201
+ ['common', 'navigation', 'footer'],
202
+ loader
203
+ );
204
+
205
+ console.log(`Loaded: ${result.fulfilled.length}`);
206
+ console.log(`Failed: ${result.rejected.length}`);
207
+ ```
208
+
209
+ ### warmFallbackLanguages
210
+
211
+ Pre-warms fallback languages.
212
+
213
+ ```ts
214
+ function warmFallbackLanguages(
215
+ currentLanguage: string,
216
+ languages: string[],
217
+ namespaces: string[],
218
+ loader: TranslationLoader,
219
+ options?: PreloadOptions
220
+ ): Promise<Array<{
221
+ fulfilled: string[];
222
+ rejected: unknown[];
223
+ }>>
224
+ ```
225
+
226
+ #### Examples
227
+
228
+ ```ts
229
+ import { warmFallbackLanguages } from '@hua-labs/i18n-loaders';
230
+
231
+ const loader = createApiTranslationLoader();
232
+
233
+ // When current language is 'ko', preload 'en', 'ja'
234
+ await warmFallbackLanguages(
235
+ 'ko',
236
+ ['ko', 'en', 'ja'],
237
+ ['common', 'navigation'],
238
+ loader
239
+ );
240
+ ```
241
+
242
+ ### withDefaultTranslations
243
+
244
+ Merges default translations with API translations. Uses default translations if API fails.
245
+
246
+ ```ts
247
+ function withDefaultTranslations(
248
+ loader: TranslationLoader,
249
+ defaults: DefaultTranslations
250
+ ): TranslationLoader
251
+ ```
252
+
253
+ #### Types
254
+
255
+ ```ts
256
+ type DefaultTranslations = Record<
257
+ string, // language
258
+ Record<string, TranslationRecord> // namespace -> translations
259
+ >;
260
+ ```
261
+
262
+ #### Examples
263
+
264
+ ```ts
265
+ import { withDefaultTranslations } from '@hua-labs/i18n-loaders';
266
+
267
+ const apiLoader = createApiTranslationLoader();
268
+
269
+ const defaults = {
270
+ ko: {
271
+ common: {
272
+ welcome: 'Welcome',
273
+ hello: 'Hello'
274
+ }
275
+ },
276
+ en: {
277
+ common: {
278
+ welcome: 'Welcome',
279
+ hello: 'Hello'
280
+ }
281
+ }
282
+ };
283
+
284
+ const loader = withDefaultTranslations(apiLoader, defaults);
285
+
286
+ // Merges with default translations if API succeeds
287
+ // Uses only default translations if API fails
288
+ const translations = await loader('ko', 'common');
289
+ ```
290
+
291
+ ## Usage Scenarios
292
+
293
+ ### Next.js App Router
294
+
295
+ ```tsx
296
+ // lib/i18n-config.ts
297
+ import { createCoreI18n } from '@hua-labs/i18n-core';
298
+ import { createApiTranslationLoader, preloadNamespaces } from '@hua-labs/i18n-loaders';
299
+
300
+ const loadTranslations = createApiTranslationLoader({
301
+ translationApiPath: '/api/translations',
302
+ cacheTtlMs: 60_000
303
+ });
304
+
305
+ export const I18nProvider = createCoreI18n({
306
+ defaultLanguage: 'ko',
307
+ fallbackLanguage: 'en',
308
+ namespaces: ['common', 'navigation', 'footer'],
309
+ translationLoader: 'custom',
310
+ loadTranslations
311
+ });
312
+
313
+ // Use in app/layout.tsx
314
+ export default function RootLayout({ children }) {
315
+ // Preload on client
316
+ if (typeof window !== 'undefined') {
317
+ preloadNamespaces('ko', ['common', 'navigation'], loadTranslations);
318
+ }
319
+
320
+ return (
321
+ <html>
322
+ <body>
323
+ <I18nProvider>{children}</I18nProvider>
324
+ </body>
325
+ </html>
326
+ );
327
+ }
328
+ ```
329
+
330
+ ### Using with SSR
331
+
332
+ ```tsx
333
+ // app/layout.tsx (Server Component)
334
+ import { loadSSRTranslations } from './lib/ssr-translations';
335
+ import { createCoreI18n } from '@hua-labs/i18n-core';
336
+ import { createApiTranslationLoader } from '@hua-labs/i18n-loaders';
337
+
338
+ export default async function RootLayout({ children }) {
339
+ // Load translations from SSR
340
+ const ssrTranslations = await loadSSRTranslations('ko');
341
+
342
+ // Client loader
343
+ const loadTranslations = createApiTranslationLoader({
344
+ translationApiPath: '/api/translations'
345
+ });
346
+
347
+ const I18nProvider = createCoreI18n({
348
+ defaultLanguage: 'ko',
349
+ fallbackLanguage: 'en',
350
+ namespaces: ['common', 'navigation', 'footer'],
351
+ translationLoader: 'custom',
352
+ loadTranslations,
353
+ initialTranslations: ssrTranslations // Pass SSR translations
354
+ });
355
+
356
+ return (
357
+ <html lang="ko">
358
+ <body>
359
+ <I18nProvider>{children}</I18nProvider>
360
+ </body>
361
+ </html>
362
+ );
363
+ }
364
+ ```
365
+
366
+ ## Caching Behavior
367
+
368
+ - **TTL Cache**: Each translation is cached for `cacheTtlMs` duration
369
+ - **Duplicate Request Prevention**: Reuses existing Promise if same translation is loading
370
+ - **Global Cache**: Same loader instance shares cache across all components
371
+
372
+ ## Error Handling
373
+
374
+ - **Automatic retry**: Network errors are automatically retried with exponential backoff (configurable via `retryCount` and `retryDelay`)
375
+ - Throws error on API request failure after all retries are exhausted
376
+ - Falls back to default translations when using `withDefaultTranslations`
377
+ - `preloadNamespaces` uses `Promise.allSettled` to continue even if some fail
378
+
379
+ ### Retry Configuration
380
+
381
+ ```ts
382
+ const loader = createApiTranslationLoader({
383
+ translationApiPath: '/api/translations',
384
+ retryCount: 3, // Retry up to 3 times on network errors (default: 0, no retry)
385
+ retryDelay: 1000, // Start with 1 second delay, doubles on each retry (exponential backoff)
386
+ });
387
+ ```
388
+
389
+ The retry mechanism uses exponential backoff:
390
+ - 1st retry: waits `retryDelay` ms (e.g., 1000ms)
391
+ - 2nd retry: waits `retryDelay * 2` ms (e.g., 2000ms)
392
+ - 3rd retry: waits `retryDelay * 4` ms (e.g., 4000ms)
393
+
394
+ ## Examples
395
+
396
+ - **[Next.js Example](../../examples/next-app-router-example/)** - Complete example using API loader with caching
397
+
398
+ ## Error Handling Improvements
399
+
400
+ The API loader now includes enhanced error detection:
401
+
402
+ - **Network error detection**: Improved detection of network failures
403
+ - **HTTP status code handling**: Automatic retry for 5xx errors and 408 timeouts
404
+ - **Exponential backoff**: Smart retry strategy with configurable delays
405
+ - **Error type classification**: Better distinction between retryable and non-retryable errors
406
+
407
+ See [API Loader Guide](./docs/API_LOADER.md) for detailed error handling documentation.
408
+
409
+ ## Documentation
410
+
411
+ - [API Loader Guide](./docs/API_LOADER.md) - Detailed API loader documentation and error handling
412
+
413
+ ## License
414
+
415
+ MIT License