@minejs/i18n 0.0.4 → 0.0.5
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 +340 -735
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +73 -84
- package/dist/index.d.ts +73 -84
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,736 +1,341 @@
|
|
|
1
|
-
<!-- ╔══════════════════════════════ BEG ══════════════════════════════╗ -->
|
|
2
|
-
|
|
3
|
-
<br>
|
|
4
|
-
<div align="center">
|
|
5
|
-
<p>
|
|
6
|
-
<img src="./assets/img/logo.png" alt="logo" style="" height="60" />
|
|
7
|
-
</p>
|
|
8
|
-
</div>
|
|
9
|
-
|
|
10
|
-
<div align="center">
|
|
11
|
-
<img src="https://img.shields.io/badge/v-0.0.
|
|
12
|
-
<img src="https://img.shields.io/badge/🔥-@minejs-black"
|
|
13
|
-
<
|
|
14
|
-
<
|
|
15
|
-
<img src="https://img.shields.io/
|
|
16
|
-
<img src="https://img.shields.io/github/
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
-
|
|
125
|
-
|
|
126
|
-
```
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
```
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
```
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
"
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
- #### `loadLanguage(lang: LanguageCode, translations: object): void`
|
|
342
|
-
> Load translations for a specific language. Nested objects are flattened.
|
|
343
|
-
|
|
344
|
-
```typescript
|
|
345
|
-
i18n.loadLanguage('en', {
|
|
346
|
-
app: { name: 'MyApp' },
|
|
347
|
-
greeting: 'Hello'
|
|
348
|
-
})
|
|
349
|
-
|
|
350
|
-
i18n.t('app.name') // "MyApp"
|
|
351
|
-
i18n.t('greeting') // "Hello"
|
|
352
|
-
```
|
|
353
|
-
|
|
354
|
-
- #### `loadTranslations(translations: TranslationSet): void`
|
|
355
|
-
> Load multiple languages at once.
|
|
356
|
-
|
|
357
|
-
```typescript
|
|
358
|
-
i18n.loadTranslations({
|
|
359
|
-
en: { greeting: 'Hello' },
|
|
360
|
-
ar: { greeting: 'مرحبا' },
|
|
361
|
-
fr: { greeting: 'Bonjour' }
|
|
362
|
-
})
|
|
363
|
-
```
|
|
364
|
-
|
|
365
|
-
- #### `t(key: string, params?: object): string`
|
|
366
|
-
> Translate a key with optional parameter replacement.
|
|
367
|
-
|
|
368
|
-
```typescript
|
|
369
|
-
i18n.loadLanguage('en', {
|
|
370
|
-
greeting: 'Hello {name}'
|
|
371
|
-
})
|
|
372
|
-
|
|
373
|
-
i18n.t('greeting', { name: 'John' }) // "Hello John"
|
|
374
|
-
i18n.t('missing.key') // "missing.key" (returns key if not found)
|
|
375
|
-
```
|
|
376
|
-
|
|
377
|
-
- #### `tLang(key: string, lang: LanguageCode, params?: object): string`
|
|
378
|
-
> Translate a key with a specific language temporarily.
|
|
379
|
-
|
|
380
|
-
```typescript
|
|
381
|
-
i18n.loadLanguage('ar', { greeting: 'مرحبا' })
|
|
382
|
-
|
|
383
|
-
const result = i18n.tLang('greeting', 'ar')
|
|
384
|
-
// Returns Arabic translation without changing current language
|
|
385
|
-
```
|
|
386
|
-
|
|
387
|
-
- #### `tParse(key: string, params?: object): TranslationToken[]`
|
|
388
|
-
> Parse translation with HTML tags into tokens. Converts \n and /n to <br> tags.
|
|
389
|
-
|
|
390
|
-
```typescript
|
|
391
|
-
i18n.loadLanguage('en', {
|
|
392
|
-
message: 'Hello <strong>World</strong>\\nLine 2'
|
|
393
|
-
})
|
|
394
|
-
|
|
395
|
-
const tokens = i18n.tParse('message')
|
|
396
|
-
// [
|
|
397
|
-
// { type: 'text', content: 'Hello ' },
|
|
398
|
-
// { type: 'tag', tag: 'strong', content: 'World' },
|
|
399
|
-
// { type: 'tag', tag: 'br', content: '' },
|
|
400
|
-
// { type: 'text', content: 'Line 2' }
|
|
401
|
-
// ]
|
|
402
|
-
```
|
|
403
|
-
|
|
404
|
-
- #### `setLanguage(lang: LanguageCode): Promise<void>`
|
|
405
|
-
> Set current language and trigger change listeners.
|
|
406
|
-
|
|
407
|
-
```typescript
|
|
408
|
-
await i18n.setLanguage('ar')
|
|
409
|
-
// Language is changed and persisted to storage if available
|
|
410
|
-
```
|
|
411
|
-
|
|
412
|
-
- #### `getLanguage(): LanguageCode`
|
|
413
|
-
> Get current language code.
|
|
414
|
-
|
|
415
|
-
```typescript
|
|
416
|
-
const lang = i18n.getLanguage() // "en"
|
|
417
|
-
```
|
|
418
|
-
|
|
419
|
-
- #### `getSupportedLanguages(): LanguageCode[]`
|
|
420
|
-
> Get array of all supported languages.
|
|
421
|
-
|
|
422
|
-
```typescript
|
|
423
|
-
const langs = i18n.getSupportedLanguages() // ['en', 'ar', 'fr']
|
|
424
|
-
```
|
|
425
|
-
|
|
426
|
-
- #### `isLanguageSupported(lang: LanguageCode): boolean`
|
|
427
|
-
> Check if a language is supported.
|
|
428
|
-
|
|
429
|
-
```typescript
|
|
430
|
-
i18n.isLanguageSupported('ar') // true
|
|
431
|
-
i18n.isLanguageSupported('unsupported') // false
|
|
432
|
-
```
|
|
433
|
-
|
|
434
|
-
- #### `hasKey(key: string): boolean`
|
|
435
|
-
> Check if translation key exists in current or fallback language.
|
|
436
|
-
|
|
437
|
-
```typescript
|
|
438
|
-
i18n.hasKey('greeting') // true
|
|
439
|
-
i18n.hasKey('missing') // false
|
|
440
|
-
```
|
|
441
|
-
|
|
442
|
-
- #### `getTranslations(): Record<string, string>`
|
|
443
|
-
> Get all translations for current language.
|
|
444
|
-
|
|
445
|
-
```typescript
|
|
446
|
-
const all = i18n.getTranslations()
|
|
447
|
-
```
|
|
448
|
-
|
|
449
|
-
- #### `isRTL(): boolean`
|
|
450
|
-
> Check if current language is right-to-left.
|
|
451
|
-
|
|
452
|
-
```typescript
|
|
453
|
-
await i18n.setLanguage('ar')
|
|
454
|
-
i18n.isRTL() // true
|
|
455
|
-
|
|
456
|
-
await i18n.setLanguage('en')
|
|
457
|
-
i18n.isRTL() // false
|
|
458
|
-
```
|
|
459
|
-
|
|
460
|
-
- #### `isRTLLanguage(lang: LanguageCode): boolean`
|
|
461
|
-
> Check if specific language is right-to-left.
|
|
462
|
-
|
|
463
|
-
```typescript
|
|
464
|
-
i18n.isRTLLanguage('ar') // true
|
|
465
|
-
i18n.isRTLLanguage('he') // true
|
|
466
|
-
i18n.isRTLLanguage('en') // false
|
|
467
|
-
```
|
|
468
|
-
|
|
469
|
-
- #### `onChange(callback: (lang: LanguageCode) => void): () => void`
|
|
470
|
-
> Subscribe to language changes. Returns unsubscribe function.
|
|
471
|
-
|
|
472
|
-
```typescript
|
|
473
|
-
const unsubscribe = i18n.onChange((lang) => {
|
|
474
|
-
console.log('Language changed to:', lang)
|
|
475
|
-
updateUI(lang)
|
|
476
|
-
})
|
|
477
|
-
|
|
478
|
-
// Unsubscribe later
|
|
479
|
-
unsubscribe()
|
|
480
|
-
```
|
|
481
|
-
|
|
482
|
-
<br>
|
|
483
|
-
|
|
484
|
-
- ### LazyLoader Class
|
|
485
|
-
|
|
486
|
-
- #### `constructor(baseUrl: string, manager: I18nManager)`
|
|
487
|
-
> Create a lazy loader for on-demand language loading.
|
|
488
|
-
|
|
489
|
-
```typescript
|
|
490
|
-
const manager = new I18nManager({
|
|
491
|
-
supportedLanguages: ['en', 'ar', 'fr']
|
|
492
|
-
})
|
|
493
|
-
const loader = new LazyLoader('https://cdn.com/i18n/', manager)
|
|
494
|
-
```
|
|
495
|
-
|
|
496
|
-
- #### `load(lang: LanguageCode): Promise<void>`
|
|
497
|
-
> Load a language file on-demand. Caches promises to prevent duplicate requests.
|
|
498
|
-
|
|
499
|
-
```typescript
|
|
500
|
-
await loader.load('ar')
|
|
501
|
-
// Expects https://cdn.com/i18n/ar.json
|
|
502
|
-
|
|
503
|
-
// Subsequent calls return cached promise
|
|
504
|
-
await loader.load('ar') // Returns immediately
|
|
505
|
-
```
|
|
506
|
-
|
|
507
|
-
- #### `isLoaded(lang: LanguageCode): boolean`
|
|
508
|
-
> Check if language is already loaded.
|
|
509
|
-
|
|
510
|
-
```typescript
|
|
511
|
-
if (!loader.isLoaded('ar')) {
|
|
512
|
-
await loader.load('ar')
|
|
513
|
-
}
|
|
514
|
-
```
|
|
515
|
-
|
|
516
|
-
<br>
|
|
517
|
-
|
|
518
|
-
- ### Storage Adapters
|
|
519
|
-
|
|
520
|
-
- #### `browserStorage: I18nStorage`
|
|
521
|
-
> Store language preference in browser localStorage.
|
|
522
|
-
|
|
523
|
-
```typescript
|
|
524
|
-
import { browserStorage } from '@minejs/i18n'
|
|
525
|
-
|
|
526
|
-
const i18n = new I18nManager({
|
|
527
|
-
storage: browserStorage
|
|
528
|
-
})
|
|
529
|
-
```
|
|
530
|
-
|
|
531
|
-
- #### `memoryStorage: I18nStorage`
|
|
532
|
-
> In-memory storage (useful for SSR and testing).
|
|
533
|
-
|
|
534
|
-
```typescript
|
|
535
|
-
import { memoryStorage } from '@minejs/i18n'
|
|
536
|
-
|
|
537
|
-
const i18n = new I18nManager({
|
|
538
|
-
storage: memoryStorage
|
|
539
|
-
})
|
|
540
|
-
```
|
|
541
|
-
|
|
542
|
-
- #### `fetchTranslations(urls: string | string[], manager: I18nManager): Promise<void>`
|
|
543
|
-
> Fetch translations from remote URLs. Extracts language from filename (e.g., `en.json`).
|
|
544
|
-
|
|
545
|
-
```typescript
|
|
546
|
-
import { fetchTranslations } from '@minejs/i18n'
|
|
547
|
-
|
|
548
|
-
const manager = new I18nManager()
|
|
549
|
-
|
|
550
|
-
// Single URL
|
|
551
|
-
await fetchTranslations('https://cdn.com/i18n/en.json', manager)
|
|
552
|
-
|
|
553
|
-
// Multiple URLs
|
|
554
|
-
await fetchTranslations([
|
|
555
|
-
'https://cdn.com/i18n/en.json',
|
|
556
|
-
'https://cdn.com/i18n/ar.json'
|
|
557
|
-
], manager)
|
|
558
|
-
```
|
|
559
|
-
|
|
560
|
-
<br>
|
|
561
|
-
|
|
562
|
-
- ### Global Instance Functions
|
|
563
|
-
|
|
564
|
-
- #### `getI18n(): I18nManager`
|
|
565
|
-
> Get or create global I18n instance.
|
|
566
|
-
|
|
567
|
-
```typescript
|
|
568
|
-
const i18n = getI18n()
|
|
569
|
-
```
|
|
570
|
-
|
|
571
|
-
- #### `setupI18n(config: I18nConfig): Promise<I18nManager>`
|
|
572
|
-
> Initialize global instance with config.
|
|
573
|
-
|
|
574
|
-
```typescript
|
|
575
|
-
const i18n = await setupI18n({
|
|
576
|
-
defaultLanguage: 'en',
|
|
577
|
-
supportedLanguages: ['en', 'ar', 'fr']
|
|
578
|
-
})
|
|
579
|
-
```
|
|
580
|
-
|
|
581
|
-
- #### `createLazyLoader(baseUrl: string, fileExtension?: string): LazyLoader`
|
|
582
|
-
> Create lazy loader for global instance with support for custom file extensions.
|
|
583
|
-
|
|
584
|
-
```typescript
|
|
585
|
-
const loader = createLazyLoader('https://cdn.com/i18n/', 'json')
|
|
586
|
-
await loader.load('ar')
|
|
587
|
-
```
|
|
588
|
-
|
|
589
|
-
- #### `setupLazy(config: I18nConfig & {basePath?: string; baseUrl?: string}): Promise<LazyLoader>`
|
|
590
|
-
> Setup with lazy loading and load default language only. Supports both file paths and URLs.
|
|
591
|
-
|
|
592
|
-
```typescript
|
|
593
|
-
const loader = await setupLazy({
|
|
594
|
-
defaultLanguage: 'en',
|
|
595
|
-
supportedLanguages: ['en', 'ar', 'fr'],
|
|
596
|
-
basePath: '/i18n/'
|
|
597
|
-
})
|
|
598
|
-
```
|
|
599
|
-
|
|
600
|
-
- #### `setupAuto(config: I18nConfig & {basePath: string}): Promise<LazyLoader>`
|
|
601
|
-
> Auto-setup with environment detection. Automatically uses fetch in browser and file system on server.
|
|
602
|
-
|
|
603
|
-
```typescript
|
|
604
|
-
// Works in both browser and Node.js
|
|
605
|
-
const loader = await setupAuto({
|
|
606
|
-
defaultLanguage: 'en',
|
|
607
|
-
supportedLanguages: ['en', 'ar', 'fr'],
|
|
608
|
-
basePath: 'http://localhost:3000/i18n/',
|
|
609
|
-
fileExtension: 'json'
|
|
610
|
-
})
|
|
611
|
-
```
|
|
612
|
-
|
|
613
|
-
```typescript
|
|
614
|
-
const loader = await setupLazy({
|
|
615
|
-
defaultLanguage: 'en',
|
|
616
|
-
supportedLanguages: ['en', 'ar', 'fr'],
|
|
617
|
-
baseUrl: 'https://cdn.com/i18n/'
|
|
618
|
-
})
|
|
619
|
-
```
|
|
620
|
-
|
|
621
|
-
<br>
|
|
622
|
-
|
|
623
|
-
- ### Convenience Functions (Global Instance)
|
|
624
|
-
|
|
625
|
-
- #### `t(key: string, params?: object): string`
|
|
626
|
-
> Translate using global instance.
|
|
627
|
-
|
|
628
|
-
- #### `tLang(key: string, lang: LanguageCode, params?: object): string`
|
|
629
|
-
> Translate with specific language using global instance.
|
|
630
|
-
|
|
631
|
-
- #### `tParse(key: string, params?: object): TranslationToken[]`
|
|
632
|
-
> Parse translation using global instance.
|
|
633
|
-
|
|
634
|
-
- #### `setLanguage(lang: LanguageCode): Promise<void>`
|
|
635
|
-
> Set language on global instance.
|
|
636
|
-
|
|
637
|
-
- #### `getLanguage(): LanguageCode`
|
|
638
|
-
> Get current language from global instance.
|
|
639
|
-
|
|
640
|
-
- #### `getSupportedLanguages(): LanguageCode[]`
|
|
641
|
-
> Get supported languages from global instance.
|
|
642
|
-
|
|
643
|
-
- #### `hasKey(key: string): boolean`
|
|
644
|
-
> Check if key exists in global instance.
|
|
645
|
-
|
|
646
|
-
- #### `isRTL(): boolean`
|
|
647
|
-
> Check if current language is RTL on global instance.
|
|
648
|
-
|
|
649
|
-
- #### `isRTLLanguage(lang: LanguageCode): boolean`
|
|
650
|
-
> Check if specific language is RTL.
|
|
651
|
-
|
|
652
|
-
- #### `onChange(callback: (lang: LanguageCode) => void): () => void`
|
|
653
|
-
> Subscribe to language changes on global instance.
|
|
654
|
-
|
|
655
|
-
- #### `loadLanguage(lang: LanguageCode, translations: object): void`
|
|
656
|
-
> Load translations using global instance.
|
|
657
|
-
|
|
658
|
-
- #### `loadTranslations(translations: TranslationSet): void`
|
|
659
|
-
> Load multiple languages using global instance.
|
|
660
|
-
|
|
661
|
-
<br>
|
|
662
|
-
|
|
663
|
-
- ### Utility Functions
|
|
664
|
-
|
|
665
|
-
- #### `genPageTitle(key: string, prefix?: string): string`
|
|
666
|
-
> Generate page title with app name and proper RTL formatting.
|
|
667
|
-
|
|
668
|
-
```typescript
|
|
669
|
-
// LTR: "Page Name - App Name"
|
|
670
|
-
// RTL: "App Name - Page Name"
|
|
671
|
-
|
|
672
|
-
const title = genPageTitle('home', 'page.')
|
|
673
|
-
// Translates 'page.home' and 'app.name'
|
|
674
|
-
```
|
|
675
|
-
|
|
676
|
-
- #### `plural(count: number, singleKey: string, pluralKey: string): string`
|
|
677
|
-
> Select translation based on count and replace {count} parameter.
|
|
678
|
-
|
|
679
|
-
```typescript
|
|
680
|
-
loadLanguage('en', {
|
|
681
|
-
'item.single': '1 item',
|
|
682
|
-
'item.plural': '{count} items'
|
|
683
|
-
})
|
|
684
|
-
|
|
685
|
-
plural(1, 'item.single', 'item.plural') // "1 item"
|
|
686
|
-
plural(5, 'item.single', 'item.plural') // "5 items"
|
|
687
|
-
```
|
|
688
|
-
|
|
689
|
-
<br>
|
|
690
|
-
|
|
691
|
-
- ## Types 📘
|
|
692
|
-
|
|
693
|
-
```typescript
|
|
694
|
-
type LanguageCode = string
|
|
695
|
-
|
|
696
|
-
interface I18nConfig {
|
|
697
|
-
defaultLanguage?: LanguageCode
|
|
698
|
-
supportedLanguages?: LanguageCode[]
|
|
699
|
-
fallbackLanguage?: LanguageCode
|
|
700
|
-
onLanguageChange?: (lang: LanguageCode) => void
|
|
701
|
-
storage?: I18nStorage
|
|
702
|
-
basePath?: string
|
|
703
|
-
fileExtension?: string
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
interface I18nStorage {
|
|
707
|
-
get(key: string): string | null | Promise<string | null>
|
|
708
|
-
set(key: string, value: string): void | Promise<void>
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
interface TranslationToken {
|
|
712
|
-
type: 'text' | 'tag'
|
|
713
|
-
tag?: string
|
|
714
|
-
content: string
|
|
715
|
-
}
|
|
716
|
-
|
|
717
|
-
type TranslationSet = Record<string, Record<string, string>>
|
|
718
|
-
```
|
|
719
|
-
|
|
720
|
-
<br>
|
|
721
|
-
|
|
722
|
-
<!-- ╚═════════════════════════════════════════════════════════════════╝ -->
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
<!-- ╔══════════════════════════════ END ══════════════════════════════╗ -->
|
|
727
|
-
|
|
728
|
-
<br>
|
|
729
|
-
|
|
730
|
-
---
|
|
731
|
-
|
|
732
|
-
<div align="center">
|
|
733
|
-
<a href="https://github.com/maysara-elshewehy"><img src="https://img.shields.io/badge/by-Maysara-black"/></a>
|
|
734
|
-
</div>
|
|
735
|
-
|
|
1
|
+
<!-- ╔══════════════════════════════ BEG ══════════════════════════════╗ -->
|
|
2
|
+
|
|
3
|
+
<br>
|
|
4
|
+
<div align="center">
|
|
5
|
+
<p>
|
|
6
|
+
<img src="./assets/img/logo.png" alt="logo" style="" height="60" />
|
|
7
|
+
</p>
|
|
8
|
+
</div>
|
|
9
|
+
|
|
10
|
+
<div align="center">
|
|
11
|
+
<img src="https://img.shields.io/badge/v-0.0.5-black"/>
|
|
12
|
+
<a href="https://img.shields.io/github/stars/minejs-org"><img src="https://img.shields.io/badge/🔥-@minejs-black"/></a>
|
|
13
|
+
<br>
|
|
14
|
+
<img src="https://img.shields.io/badge/coverage-94.40%25-brightgreen" alt="Test Coverage" />
|
|
15
|
+
<img src="https://img.shields.io/github/issues/minejs-org/i18n?style=flat" alt="Github Repo Issues" />
|
|
16
|
+
<img src="https://img.shields.io/github/stars/minejs-org/i18n?style=social" alt="GitHub Repo stars" />
|
|
17
|
+
</div>
|
|
18
|
+
<br>
|
|
19
|
+
|
|
20
|
+
<!-- ╚═════════════════════════════════════════════════════════════════╝ -->
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
<!-- ╔══════════════════════════════ DOC ══════════════════════════════╗ -->
|
|
25
|
+
|
|
26
|
+
- ## Overview 👀
|
|
27
|
+
|
|
28
|
+
- #### Why ?
|
|
29
|
+
> To unify the translation system and languages on the server and client, faster, cleaner, more maintainable.
|
|
30
|
+
|
|
31
|
+
- #### When ?
|
|
32
|
+
> When you need to add a translation to your server or client.
|
|
33
|
+
|
|
34
|
+
> When you use [@cruxjs/app](https://github.com/cruxjs-org/app).
|
|
35
|
+
|
|
36
|
+
<br>
|
|
37
|
+
<br>
|
|
38
|
+
|
|
39
|
+
- ## Quick Start 🔥
|
|
40
|
+
|
|
41
|
+
> install [`hmm`](https://github.com/minejs-org/hmm) first.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# in your terminal
|
|
45
|
+
hmm i @minejs/i18n
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
<div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> </div>
|
|
49
|
+
|
|
50
|
+
- #### Setup
|
|
51
|
+
|
|
52
|
+
- ##### JSON
|
|
53
|
+
|
|
54
|
+
> Save the translation **keys and their values** in `.json` files.
|
|
55
|
+
|
|
56
|
+
```jsonc
|
|
57
|
+
// ./src/shared/dist/u18n/en.json
|
|
58
|
+
{
|
|
59
|
+
"key": "value"
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
```jsonc
|
|
64
|
+
// ./src/shared/dist/u18n/ar.json
|
|
65
|
+
{
|
|
66
|
+
"key": "قيـمة"
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
- ##### I18n
|
|
71
|
+
|
|
72
|
+
> ***🌟 If you are using [`@cruxjs/app`](https://github.com/cruxjs-org/app) you can skip this step. 🌟***
|
|
73
|
+
|
|
74
|
+
> Then in your project ***(works with any environment: `server`, `browser`, ..)***
|
|
75
|
+
>
|
|
76
|
+
> Call the `setupI18n(..)` function **only once** in your application's lifecycle :
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
import { setupI18n } from `@minejs/i18n`;
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
await this.setupI18n({
|
|
84
|
+
defaultLanguage : 'en',
|
|
85
|
+
supportedLanguages : ['en', 'ar'],
|
|
86
|
+
basePath : '/static/dist/i18n', // for client side (or your custom public url)
|
|
87
|
+
: './src/shared/dist/i18n', // for server side (or your custom local path)
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
<div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> </div>
|
|
92
|
+
<br>
|
|
93
|
+
|
|
94
|
+
- #### Usage
|
|
95
|
+
|
|
96
|
+
> Now you can call the `t(..)` function anywhere in your project :
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
import { t } from `@minejs/i18n`;
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
```ts
|
|
103
|
+
t('key', { params }, fallback) // just it !
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
<div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> </div>
|
|
107
|
+
|
|
108
|
+
- #### Language Switching
|
|
109
|
+
|
|
110
|
+
```typescript
|
|
111
|
+
import { setLanguage, onChange } from '@minejs/i18n';
|
|
112
|
+
|
|
113
|
+
// Listen to changes
|
|
114
|
+
onChange((lang) => {
|
|
115
|
+
console.log('Language changed to:', lang);
|
|
116
|
+
document.documentElement.lang = lang;
|
|
117
|
+
document.dir = isRTL() ? 'rtl' : 'ltr';
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
// Change language
|
|
121
|
+
await setLanguage('ar');
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
- #### Parameterized Translations
|
|
125
|
+
|
|
126
|
+
```json
|
|
127
|
+
{
|
|
128
|
+
"greeting": "Hello {name}, you have {count} messages"
|
|
129
|
+
}
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
```typescript
|
|
133
|
+
t('greeting', { name: 'John', count: '5' })
|
|
134
|
+
// "Hello John, you have 5 messages"
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
- #### HTML Tag Parsing
|
|
138
|
+
|
|
139
|
+
```json
|
|
140
|
+
{
|
|
141
|
+
"terms": "I agree to the <link>Terms of Service</link>"
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
```typescript
|
|
146
|
+
const tokens = tParse('terms');
|
|
147
|
+
// [
|
|
148
|
+
// { type: 'text', content: 'I agree to the ' },
|
|
149
|
+
// { type: 'tag', tag: 'link', content: 'Terms of Service' }
|
|
150
|
+
// ]
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
- #### Page Titles (with RTL support)
|
|
154
|
+
|
|
155
|
+
```typescript
|
|
156
|
+
import { genPageTitle } from '@minejs/i18n';
|
|
157
|
+
|
|
158
|
+
// en: "Settings - MyApp"
|
|
159
|
+
// ar: "MyApp - الإعدادات"
|
|
160
|
+
const title = genPageTitle('settings');
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
<br>
|
|
164
|
+
<br>
|
|
165
|
+
|
|
166
|
+
- ## Documentation 📑
|
|
167
|
+
|
|
168
|
+
- ### API
|
|
169
|
+
|
|
170
|
+
- #### Types
|
|
171
|
+
|
|
172
|
+
```typescript
|
|
173
|
+
type LanguageCode = string;
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
interface I18nConfig {
|
|
178
|
+
defaultLanguage? : LanguageCode;
|
|
179
|
+
supportedLanguages? : LanguageCode[];
|
|
180
|
+
fallbackLanguage? : LanguageCode;
|
|
181
|
+
onLanguageChange? : (lang: LanguageCode) => void;
|
|
182
|
+
storage? : I18nStorage;
|
|
183
|
+
basePath? : string;
|
|
184
|
+
fileExtension? : string;
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
interface TranslationToken {
|
|
190
|
+
type : 'text' | 'tag';
|
|
191
|
+
tag? : string;
|
|
192
|
+
content : string;
|
|
193
|
+
}
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
<div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> </div>
|
|
197
|
+
|
|
198
|
+
- #### Functions
|
|
199
|
+
|
|
200
|
+
- #### `setupI18n(config)`
|
|
201
|
+
|
|
202
|
+
> Initialize i18n with auto-detection
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
await setupI18n({
|
|
206
|
+
defaultLanguage : 'en',
|
|
207
|
+
supportedLanguages : ['en', 'ar', 'fr'],
|
|
208
|
+
basePath : '/i18n/', // URL (browser) or path (server)
|
|
209
|
+
fileExtension : 'json' // optional
|
|
210
|
+
});
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
- #### `t(key, params?)`
|
|
214
|
+
|
|
215
|
+
> Translate with parameter replacement
|
|
216
|
+
|
|
217
|
+
```typescript
|
|
218
|
+
t('greeting', { name: 'John' }) // "Hello John"
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
- #### `tLang(key, lang, params?)`
|
|
222
|
+
|
|
223
|
+
> Translate with specific language
|
|
224
|
+
|
|
225
|
+
```typescript
|
|
226
|
+
tLang('greeting', 'ar', { name: 'أحمد' })
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
- #### `tParse(key, params?)`
|
|
230
|
+
|
|
231
|
+
> Parse translation with HTML tags
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
tParse('message') // Returns TokenArray
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
- #### `setLanguage(lang)`
|
|
238
|
+
|
|
239
|
+
> Change current language
|
|
240
|
+
|
|
241
|
+
```typescript
|
|
242
|
+
await setLanguage('ar')
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
- #### `getLanguage()`
|
|
246
|
+
|
|
247
|
+
> Get current language code
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
const lang = getLanguage() // 'en'
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
- #### `getSupportedLanguages()`
|
|
254
|
+
|
|
255
|
+
> Get all supported languages
|
|
256
|
+
|
|
257
|
+
```typescript
|
|
258
|
+
const langs = getSupportedLanguages() // ['en', 'ar', 'fr']
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
- #### `isRTL()`
|
|
262
|
+
|
|
263
|
+
> Check if current language is RTL
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
if (isRTL()) { /* Handle RTL layout */ }
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
- #### `onChange(callback)`
|
|
270
|
+
|
|
271
|
+
> Subscribe to language changes
|
|
272
|
+
|
|
273
|
+
```typescript
|
|
274
|
+
const unsubscribe = onChange((lang) => console.log('Changed to:', lang))
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
- #### `genPageTitle(key, prefix?)`
|
|
278
|
+
|
|
279
|
+
> Generate page title with app name
|
|
280
|
+
|
|
281
|
+
```typescript
|
|
282
|
+
genPageTitle('home') // "Home - MyApp" or "MyApp - الرئيسية"
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
- #### `plural(count, singleKey, pluralKey)`
|
|
286
|
+
|
|
287
|
+
> Handle pluralization
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
plural(5, 'item.single', 'item.plural') // "5 items"
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
- #### `hasKey(key)`
|
|
294
|
+
|
|
295
|
+
> Check if translation exists
|
|
296
|
+
|
|
297
|
+
```typescript
|
|
298
|
+
if (hasKey('settings.theme')) { /* ... */ }
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
- #### `loadLanguage(lang, translations)`
|
|
302
|
+
|
|
303
|
+
> Load translations for a language
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
loadLanguage('en', { greeting: 'Hello' })
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
- #### `loadTranslations(translations)`
|
|
310
|
+
|
|
311
|
+
> Load multiple languages at once
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
loadTranslations({
|
|
315
|
+
en: { greeting: 'Hello' },
|
|
316
|
+
ar: { greeting: 'مرحبا' }
|
|
317
|
+
})
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
<div align="center"> <img src="./assets/img/line.png" alt="line" style="display: block; margin-top:20px;margin-bottom:20px;width:500px;"/> </div>
|
|
321
|
+
|
|
322
|
+
- #### Related
|
|
323
|
+
|
|
324
|
+
- ##### [@cruxjs/app](https://github.com/cruxjs-org/app)
|
|
325
|
+
|
|
326
|
+
<!-- ╚═════════════════════════════════════════════════════════════════╝ -->
|
|
327
|
+
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
<!-- ╔══════════════════════════════ END ══════════════════════════════╗ -->
|
|
331
|
+
|
|
332
|
+
<br>
|
|
333
|
+
<br>
|
|
334
|
+
|
|
335
|
+
---
|
|
336
|
+
|
|
337
|
+
<div align="center">
|
|
338
|
+
<a href="https://github.com/maysara-elshewehy"><img src="https://img.shields.io/badge/by-Maysara-black"/></a>
|
|
339
|
+
</div>
|
|
340
|
+
|
|
736
341
|
<!-- ╚═════════════════════════════════════════════════════════════════╝ -->
|