@digitaldefiance/i18n-lib 4.3.0 → 4.3.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 +299 -43
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,6 +8,8 @@ Part of [Express Suite](https://github.com/Digital-Defiance/express-suite)
|
|
|
8
8
|
|
|
9
9
|
✨ **String Key Enum Registration** - Register branded string key enums for direct translation without specifying component IDs.
|
|
10
10
|
|
|
11
|
+
**Prerequisites**: Requires branded enums from v4.0.4+ (see [Branded Enums](#branded-enums) section)
|
|
12
|
+
|
|
11
13
|
**New Features:**
|
|
12
14
|
- **`registerStringKeyEnum()`**: Register branded enums for automatic component routing
|
|
13
15
|
- **`translateStringKey()`**: Translate keys directly - component ID resolved from branded enum
|
|
@@ -15,6 +17,15 @@ Part of [Express Suite](https://github.com/Digital-Defiance/express-suite)
|
|
|
15
17
|
- **`hasStringKeyEnum()` / `getStringKeyEnums()`**: Query registered enums
|
|
16
18
|
|
|
17
19
|
```typescript
|
|
20
|
+
import { createI18nStringKeysFromEnum, PluginI18nEngine, LanguageCodes } from '@digitaldefiance/i18n-lib';
|
|
21
|
+
|
|
22
|
+
// First, create a branded enum from your string keys
|
|
23
|
+
enum MyStringKeys {
|
|
24
|
+
Welcome = 'welcome',
|
|
25
|
+
Goodbye = 'goodbye',
|
|
26
|
+
}
|
|
27
|
+
const MyBrandedKeys = createI18nStringKeysFromEnum('my-component', MyStringKeys);
|
|
28
|
+
|
|
18
29
|
// Register your branded enum
|
|
19
30
|
engine.registerStringKeyEnum(MyBrandedKeys);
|
|
20
31
|
|
|
@@ -51,8 +62,8 @@ const text = engine.translateStringKey(MyBrandedKeys.Welcome, { name: 'Alice' })
|
|
|
51
62
|
- **Type Safety**: Full TypeScript support with generic types
|
|
52
63
|
- **Branded Enums**: Runtime-identifiable string keys with collision detection and component routing
|
|
53
64
|
- **Error Handling**: Comprehensive error classes with translation support and ICU formatting
|
|
54
|
-
- **93.22% Test Coverage**:
|
|
55
|
-
- **Security Hardened**:
|
|
65
|
+
- **93.22% Test Coverage**: 2,007 tests covering all features
|
|
66
|
+
- **Security Hardened**: Comprehensive protection against prototype pollution, ReDoS, and XSS attacks
|
|
56
67
|
|
|
57
68
|
## Installation
|
|
58
69
|
|
|
@@ -102,8 +113,12 @@ console.log(engine.translate('app', 'welcome', { appName: 'MyApp' }));
|
|
|
102
113
|
// Output: "Bienvenue sur MyApp!"
|
|
103
114
|
|
|
104
115
|
// Pluralization (automatic form selection)
|
|
105
|
-
engine.
|
|
106
|
-
|
|
116
|
+
engine.registerComponent({
|
|
117
|
+
component: {
|
|
118
|
+
id: 'cart',
|
|
119
|
+
name: 'Cart',
|
|
120
|
+
stringKeys: ['items']
|
|
121
|
+
},
|
|
107
122
|
strings: {
|
|
108
123
|
'en-US': {
|
|
109
124
|
items: {
|
|
@@ -122,7 +137,18 @@ console.log(engine.translate('cart', 'items', { count: 5 }));
|
|
|
122
137
|
|
|
123
138
|
## ICU MessageFormat
|
|
124
139
|
|
|
125
|
-
Industry-standard message formatting with powerful features. See [
|
|
140
|
+
Industry-standard message formatting with powerful features. See [docs/ICU_MESSAGEFORMAT.md](docs/ICU_MESSAGEFORMAT.md) for complete guide.
|
|
141
|
+
|
|
142
|
+
**When to use ICU MessageFormat:**
|
|
143
|
+
- Complex pluralization with multiple forms
|
|
144
|
+
- Gender-specific translations
|
|
145
|
+
- Number/date/time formatting with locale awareness
|
|
146
|
+
- Nested conditional logic (select within plural)
|
|
147
|
+
|
|
148
|
+
**When to use simple templates:**
|
|
149
|
+
- Basic variable substitution
|
|
150
|
+
- Component references ({{Component.key}})
|
|
151
|
+
- Simple string interpolation
|
|
126
152
|
|
|
127
153
|
### Quick Example
|
|
128
154
|
|
|
@@ -164,9 +190,9 @@ formatICUMessage(
|
|
|
164
190
|
|
|
165
191
|
### Documentation
|
|
166
192
|
|
|
167
|
-
- **[
|
|
168
|
-
- **[
|
|
169
|
-
- **[
|
|
193
|
+
- **[docs/ICU_MESSAGEFORMAT.md](docs/ICU_MESSAGEFORMAT.md)** - Complete guide with syntax reference and examples
|
|
194
|
+
- **[docs/ICU_COMPREHENSIVE_VALIDATION.md](docs/ICU_COMPREHENSIVE_VALIDATION.md)** - Validation report with test coverage
|
|
195
|
+
- **[docs/ICU_PROJECT_COMPLETE.md](docs/ICU_PROJECT_COMPLETE.md)** - Implementation summary
|
|
170
196
|
|
|
171
197
|
### API
|
|
172
198
|
|
|
@@ -190,11 +216,19 @@ import {
|
|
|
190
216
|
Automatic plural form selection based on count with CLDR-compliant rules for 37 languages:
|
|
191
217
|
|
|
192
218
|
```typescript
|
|
193
|
-
import { createPluralString } from '@digitaldefiance/i18n-lib';
|
|
219
|
+
import { createPluralString, PluginI18nEngine, LanguageCodes } from '@digitaldefiance/i18n-lib';
|
|
220
|
+
|
|
221
|
+
const engine = PluginI18nEngine.createInstance('app', [
|
|
222
|
+
{ id: LanguageCodes.EN_US, name: 'English', code: 'en-US', isDefault: true }
|
|
223
|
+
]);
|
|
194
224
|
|
|
195
225
|
// English (one/other)
|
|
196
|
-
engine.
|
|
197
|
-
|
|
226
|
+
engine.registerComponent({
|
|
227
|
+
component: {
|
|
228
|
+
id: 'shop',
|
|
229
|
+
name: 'Shop',
|
|
230
|
+
stringKeys: ['items']
|
|
231
|
+
},
|
|
198
232
|
strings: {
|
|
199
233
|
'en-US': {
|
|
200
234
|
items: createPluralString({
|
|
@@ -206,8 +240,12 @@ engine.register({
|
|
|
206
240
|
});
|
|
207
241
|
|
|
208
242
|
// Russian (one/few/many)
|
|
209
|
-
engine.
|
|
210
|
-
|
|
243
|
+
engine.registerComponent({
|
|
244
|
+
component: {
|
|
245
|
+
id: 'shop',
|
|
246
|
+
name: 'Shop',
|
|
247
|
+
stringKeys: ['items']
|
|
248
|
+
},
|
|
211
249
|
strings: {
|
|
212
250
|
'ru': {
|
|
213
251
|
items: createPluralString({
|
|
@@ -220,8 +258,12 @@ engine.register({
|
|
|
220
258
|
});
|
|
221
259
|
|
|
222
260
|
// Arabic (zero/one/two/few/many/other)
|
|
223
|
-
engine.
|
|
224
|
-
|
|
261
|
+
engine.registerComponent({
|
|
262
|
+
component: {
|
|
263
|
+
id: 'shop',
|
|
264
|
+
name: 'Shop',
|
|
265
|
+
stringKeys: ['items']
|
|
266
|
+
},
|
|
225
267
|
strings: {
|
|
226
268
|
'ar': {
|
|
227
269
|
items: createPluralString({
|
|
@@ -258,10 +300,18 @@ See [PLURALIZATION_SUPPORT.md](docs/PLURALIZATION_SUPPORT.md) for complete langu
|
|
|
258
300
|
Gender-aware translations with intelligent fallback:
|
|
259
301
|
|
|
260
302
|
```typescript
|
|
261
|
-
import { createGenderedString } from '@digitaldefiance/i18n-lib';
|
|
303
|
+
import { createGenderedString, PluginI18nEngine, LanguageCodes } from '@digitaldefiance/i18n-lib';
|
|
262
304
|
|
|
263
|
-
engine.
|
|
264
|
-
id: '
|
|
305
|
+
const engine = PluginI18nEngine.createInstance('app', [
|
|
306
|
+
{ id: LanguageCodes.EN_US, name: 'English', code: 'en-US', isDefault: true }
|
|
307
|
+
]);
|
|
308
|
+
|
|
309
|
+
engine.registerComponent({
|
|
310
|
+
component: {
|
|
311
|
+
id: 'profile',
|
|
312
|
+
name: 'Profile',
|
|
313
|
+
stringKeys: ['greeting']
|
|
314
|
+
},
|
|
265
315
|
strings: {
|
|
266
316
|
'en-US': {
|
|
267
317
|
greeting: createGenderedString({
|
|
@@ -364,10 +414,10 @@ The main engine class that manages translations, languages, and components.
|
|
|
364
414
|
```typescript
|
|
365
415
|
import { PluginI18nEngine, LanguageCodes } from '@digitaldefiance/i18n-lib';
|
|
366
416
|
|
|
367
|
-
// Create instance
|
|
417
|
+
// Recommended: Create named instance (supports multiple engines)
|
|
368
418
|
const engine = PluginI18nEngine.createInstance('myapp', languages);
|
|
369
419
|
|
|
370
|
-
//
|
|
420
|
+
// Alternative: Direct constructor (for single engine use cases)
|
|
371
421
|
const engine = new PluginI18nEngine(languages, config);
|
|
372
422
|
```
|
|
373
423
|
|
|
@@ -511,8 +561,16 @@ engine.translateStringKey(BrandedKeys.Welcome);
|
|
|
511
561
|
|
|
512
562
|
### Context Integration
|
|
513
563
|
|
|
564
|
+
Automatic injection of currency, timezone, and language from GlobalActiveContext:
|
|
565
|
+
|
|
514
566
|
```typescript
|
|
515
|
-
import {
|
|
567
|
+
import {
|
|
568
|
+
GlobalActiveContext,
|
|
569
|
+
CurrencyCode,
|
|
570
|
+
Timezone,
|
|
571
|
+
PluginI18nEngine,
|
|
572
|
+
LanguageCodes
|
|
573
|
+
} from '@digitaldefiance/i18n-lib';
|
|
516
574
|
|
|
517
575
|
// Set context variables
|
|
518
576
|
const context = GlobalActiveContext.getInstance();
|
|
@@ -531,16 +589,23 @@ engine.t('Price in {currency}', { currency: 'USD' }); // "Price in USD"
|
|
|
531
589
|
|
|
532
590
|
### Constants Management
|
|
533
591
|
|
|
592
|
+
Manage application-wide constants for use in translations:
|
|
593
|
+
|
|
534
594
|
```typescript
|
|
535
|
-
// Merge constants (adds/
|
|
595
|
+
// Merge constants (adds/updates specific keys, preserves others)
|
|
536
596
|
engine.mergeConstants({ Version: '2.0', NewKey: 'value' });
|
|
537
597
|
// Existing constants preserved, specified ones added/updated
|
|
538
598
|
|
|
539
|
-
// Update all constants (replaces
|
|
599
|
+
// Update all constants (replaces entire constants object)
|
|
540
600
|
engine.updateConstants({ Site: 'NewSite', Version: '2.0' });
|
|
541
601
|
// All previous constants removed, only these remain
|
|
542
602
|
```
|
|
543
603
|
|
|
604
|
+
**When to use:**
|
|
605
|
+
- **Constants**: Application-wide values that rarely change (AppName, Version)
|
|
606
|
+
- **Variables**: Request-specific or dynamic values passed to translate()
|
|
607
|
+
- **Context**: User-specific values (currency, timezone, language)
|
|
608
|
+
|
|
544
609
|
### Language Management
|
|
545
610
|
|
|
546
611
|
```typescript
|
|
@@ -550,9 +615,11 @@ engine.setLanguage(LanguageCodes.FR);
|
|
|
550
615
|
// Get current language
|
|
551
616
|
const lang = engine.getCurrentLanguage();
|
|
552
617
|
|
|
553
|
-
// Check if language exists
|
|
618
|
+
// Check if language exists (recommended before setLanguage)
|
|
554
619
|
if (engine.hasLanguage(LanguageCodes.ES)) {
|
|
555
620
|
engine.setLanguage(LanguageCodes.ES);
|
|
621
|
+
} else {
|
|
622
|
+
console.warn('Spanish not available, using default');
|
|
556
623
|
}
|
|
557
624
|
|
|
558
625
|
// Get all languages
|
|
@@ -561,22 +628,29 @@ const languages = engine.getLanguages();
|
|
|
561
628
|
|
|
562
629
|
### Admin Context
|
|
563
630
|
|
|
564
|
-
Separate language for admin interfaces:
|
|
631
|
+
Separate language for admin interfaces (useful for multi-tenant applications where admins need consistent UI language regardless of user's language):
|
|
565
632
|
|
|
566
633
|
```typescript
|
|
567
|
-
// Set admin language
|
|
634
|
+
// Set admin language (e.g., always English for admin panel)
|
|
568
635
|
engine.setAdminLanguage(LanguageCodes.EN_US);
|
|
569
636
|
|
|
570
|
-
// Switch to admin context
|
|
637
|
+
// Switch to admin context (uses admin language)
|
|
571
638
|
engine.switchToAdmin();
|
|
639
|
+
const adminText = engine.translate('app', 'dashboard'); // Uses EN_US
|
|
572
640
|
|
|
573
|
-
// Switch back to user context
|
|
641
|
+
// Switch back to user context (uses user's language)
|
|
574
642
|
engine.switchToUser();
|
|
643
|
+
const userText = engine.translate('app', 'dashboard'); // Uses user's language
|
|
575
644
|
```
|
|
576
645
|
|
|
646
|
+
**Use cases:**
|
|
647
|
+
- Admin panels in multi-language applications
|
|
648
|
+
- Support interfaces that need consistent language
|
|
649
|
+
- Internal tools accessed by multilingual teams
|
|
650
|
+
|
|
577
651
|
## Core System Strings
|
|
578
652
|
|
|
579
|
-
Pre-built translations for common UI elements:
|
|
653
|
+
Pre-built translations for common UI elements in 8 languages:
|
|
580
654
|
|
|
581
655
|
```typescript
|
|
582
656
|
import { getCoreI18nEngine, CoreStringKey, CoreI18nComponentId } from '@digitaldefiance/i18n-lib';
|
|
@@ -588,21 +662,23 @@ const yes = coreEngine.translate(CoreI18nComponentId, CoreStringKey.Common_Yes);
|
|
|
588
662
|
const error = coreEngine.translate(CoreI18nComponentId, CoreStringKey.Error_NotFound);
|
|
589
663
|
```
|
|
590
664
|
|
|
591
|
-
Available core string categories
|
|
665
|
+
**Available core string categories:**
|
|
666
|
+
|
|
667
|
+
- **Common** (30+ strings): Yes, No, Cancel, OK, Save, Delete, Edit, Create, Update, Loading, Search, Filter, Sort, Export, Import, Settings, Help, About, Contact, Terms, Privacy, Logout, Profile, Dashboard, Home, Back, Next, Previous, Submit, Reset
|
|
668
|
+
- **Errors** (25+ strings): InvalidInput, NetworkError, NotFound, AccessDenied, ValidationFailed, Unauthorized, Forbidden, ServerError, Timeout, BadRequest, Conflict, Gone, TooManyRequests, ServiceUnavailable
|
|
669
|
+
- **System** (20+ strings): Welcome, Goodbye, PleaseWait, ProcessingRequest, OperationComplete, OperationFailed, Success, Warning, Info, Confirm, AreYouSure, UnsavedChanges, SessionExpired, MaintenanceMode
|
|
592
670
|
|
|
593
|
-
|
|
594
|
-
- **Errors**: InvalidInput, NetworkError, NotFound, AccessDenied, ValidationFailed, etc.
|
|
595
|
-
- **System**: Welcome, Goodbye, PleaseWait, ProcessingRequest, OperationComplete, etc.
|
|
671
|
+
See `CoreStringKey` enum for complete list of available strings.
|
|
596
672
|
|
|
597
673
|
## Multiple Instances
|
|
598
674
|
|
|
599
|
-
Create isolated engines for different parts of your application:
|
|
675
|
+
Create isolated engines for different parts of your application (useful for micro-frontends, plugins, or multi-tenant systems):
|
|
600
676
|
|
|
601
677
|
```typescript
|
|
602
|
-
// Admin engine
|
|
678
|
+
// Admin engine with admin-specific languages
|
|
603
679
|
const adminEngine = PluginI18nEngine.createInstance('admin', adminLanguages);
|
|
604
680
|
|
|
605
|
-
// User engine
|
|
681
|
+
// User engine with user-facing languages
|
|
606
682
|
const userEngine = PluginI18nEngine.createInstance('user', userLanguages);
|
|
607
683
|
|
|
608
684
|
// Get instance by key
|
|
@@ -613,13 +689,19 @@ if (PluginI18nEngine.hasInstance('admin')) {
|
|
|
613
689
|
// ...
|
|
614
690
|
}
|
|
615
691
|
|
|
616
|
-
// Remove instance
|
|
692
|
+
// Remove instance (cleanup)
|
|
617
693
|
PluginI18nEngine.removeInstance('admin');
|
|
618
694
|
|
|
619
|
-
// Reset all instances
|
|
620
|
-
PluginI18nEngine.resetAll();
|
|
695
|
+
// Reset all instances (useful in tests)
|
|
696
|
+
PluginI18nEngine.resetAll(); // ⚠️ Removes ALL instances globally
|
|
621
697
|
```
|
|
622
698
|
|
|
699
|
+
**Use cases:**
|
|
700
|
+
- Micro-frontends with independent i18n
|
|
701
|
+
- Plugin systems with isolated translations
|
|
702
|
+
- Multi-tenant applications with tenant-specific languages
|
|
703
|
+
- Testing (create/destroy engines per test)
|
|
704
|
+
|
|
623
705
|
## Error Handling
|
|
624
706
|
|
|
625
707
|
### RegistryError
|
|
@@ -663,7 +745,7 @@ throw new MyError(LanguageCodes.FR); // Throws with French error message
|
|
|
663
745
|
|
|
664
746
|
## Translation Adapter
|
|
665
747
|
|
|
666
|
-
Adapt PluginI18nEngine to simpler TranslationEngine interface:
|
|
748
|
+
Adapt PluginI18nEngine to simpler TranslationEngine interface (useful when integrating with error classes or other components expecting a simplified translation interface):
|
|
667
749
|
|
|
668
750
|
```typescript
|
|
669
751
|
import { createTranslationAdapter } from '@digitaldefiance/i18n-lib';
|
|
@@ -671,6 +753,7 @@ import { createTranslationAdapter } from '@digitaldefiance/i18n-lib';
|
|
|
671
753
|
const adapter = createTranslationAdapter(engine, 'componentId');
|
|
672
754
|
|
|
673
755
|
// Use adapter where TranslationEngine is expected
|
|
756
|
+
// (e.g., error classes, third-party libraries)
|
|
674
757
|
const message = adapter.translate('key', { var: 'value' });
|
|
675
758
|
```
|
|
676
759
|
|
|
@@ -691,6 +774,23 @@ LanguageCodes.JA // 'ja'
|
|
|
691
774
|
LanguageCodes.UK // 'uk'
|
|
692
775
|
```
|
|
693
776
|
|
|
777
|
+
**Adding custom language codes:**
|
|
778
|
+
|
|
779
|
+
```typescript
|
|
780
|
+
// Define your custom language type
|
|
781
|
+
type MyLanguages = CoreLanguageCode | 'pt-BR' | 'it' | 'nl';
|
|
782
|
+
|
|
783
|
+
// Create engine with custom languages
|
|
784
|
+
const engine = PluginI18nEngine.createInstance<MyLanguages>('app', [
|
|
785
|
+
{ id: LanguageCodes.EN_US, name: 'English', code: 'en-US', isDefault: true },
|
|
786
|
+
{ id: 'pt-BR', name: 'Portuguese (Brazil)', code: 'pt-BR' },
|
|
787
|
+
{ id: 'it', name: 'Italian', code: 'it' },
|
|
788
|
+
{ id: 'nl', name: 'Dutch', code: 'nl' },
|
|
789
|
+
]);
|
|
790
|
+
```
|
|
791
|
+
|
|
792
|
+
**Note**: The 8 built-in codes have pre-translated core strings. Custom languages require you to provide all translations.
|
|
793
|
+
|
|
694
794
|
## API Reference
|
|
695
795
|
|
|
696
796
|
### PluginI18nEngine
|
|
@@ -1217,11 +1317,167 @@ function translateAnyValue<T extends string | number>(
|
|
|
1217
1317
|
|
|
1218
1318
|
## Browser Support
|
|
1219
1319
|
|
|
1220
|
-
- Chrome/Edge: Latest 2 versions
|
|
1221
|
-
- Firefox: Latest 2 versions
|
|
1222
|
-
- Safari: Latest 2 versions
|
|
1320
|
+
- Chrome/Edge: Latest 2 versions (minimum: Chrome 90, Edge 90)
|
|
1321
|
+
- Firefox: Latest 2 versions (minimum: Firefox 88)
|
|
1322
|
+
- Safari: Latest 2 versions (minimum: Safari 14)
|
|
1223
1323
|
- Node.js: 18+
|
|
1224
1324
|
|
|
1325
|
+
**Polyfills**: Not required for supported versions. For older browsers, you may need:
|
|
1326
|
+
- `Intl.PluralRules` polyfill
|
|
1327
|
+
- `Intl.NumberFormat` polyfill
|
|
1328
|
+
|
|
1329
|
+
## Troubleshooting
|
|
1330
|
+
|
|
1331
|
+
### Component Not Found Error
|
|
1332
|
+
|
|
1333
|
+
**Problem**: `RegistryError: Component 'xyz' not found`
|
|
1334
|
+
|
|
1335
|
+
**Solutions**:
|
|
1336
|
+
1. Ensure component is registered before use: `engine.registerComponent({...})`
|
|
1337
|
+
2. Check component ID spelling matches exactly
|
|
1338
|
+
3. Verify registration completed successfully (no errors thrown)
|
|
1339
|
+
|
|
1340
|
+
### Translation Returns `[componentId.key]`
|
|
1341
|
+
|
|
1342
|
+
**Problem**: Translation returns placeholder instead of translated text
|
|
1343
|
+
|
|
1344
|
+
**Solutions**:
|
|
1345
|
+
1. Check string key exists in component registration
|
|
1346
|
+
2. Verify current language has translation for this key
|
|
1347
|
+
3. Use `engine.validate()` to find missing translations
|
|
1348
|
+
4. Check if using `safeTranslate()` (returns placeholder on error)
|
|
1349
|
+
|
|
1350
|
+
### Plural Forms Not Working
|
|
1351
|
+
|
|
1352
|
+
**Problem**: Always shows same plural form regardless of count
|
|
1353
|
+
|
|
1354
|
+
**Solutions**:
|
|
1355
|
+
1. Ensure passing `count` variable: `engine.translate('id', 'key', { count: 5 })`
|
|
1356
|
+
2. Use `createPluralString()` helper to create plural object
|
|
1357
|
+
3. Verify language has correct plural rules (see PLURALIZATION_SUPPORT.md)
|
|
1358
|
+
4. Check required plural forms for language: `getRequiredPluralForms('ru')`
|
|
1359
|
+
|
|
1360
|
+
### ICU MessageFormat Not Parsing
|
|
1361
|
+
|
|
1362
|
+
**Problem**: ICU syntax appears as literal text
|
|
1363
|
+
|
|
1364
|
+
**Solutions**:
|
|
1365
|
+
1. Use `formatICUMessage()` function, not `translate()`
|
|
1366
|
+
2. Check ICU syntax is valid: `validateICUMessage(message)`
|
|
1367
|
+
3. Ensure variables are provided: `formatICUMessage(msg, { count: 1 })`
|
|
1368
|
+
4. See [docs/ICU_MESSAGEFORMAT.md](docs/ICU_MESSAGEFORMAT.md) for syntax
|
|
1369
|
+
|
|
1370
|
+
### Memory Leak with Multiple Instances
|
|
1371
|
+
|
|
1372
|
+
**Problem**: Memory usage grows over time
|
|
1373
|
+
|
|
1374
|
+
**Solutions**:
|
|
1375
|
+
1. Call `PluginI18nEngine.removeInstance(key)` when done
|
|
1376
|
+
2. Use `resetAll()` in test cleanup
|
|
1377
|
+
3. Reuse instances instead of creating new ones
|
|
1378
|
+
4. Check for circular references in custom code
|
|
1379
|
+
|
|
1380
|
+
### TypeScript Type Errors
|
|
1381
|
+
|
|
1382
|
+
**Problem**: Type errors with language codes or string keys
|
|
1383
|
+
|
|
1384
|
+
**Solutions**:
|
|
1385
|
+
1. Use generic types: `PluginI18nEngine.createInstance<MyLanguages>(...)`
|
|
1386
|
+
2. Ensure `as const` on string key objects
|
|
1387
|
+
3. Import types: `import type { ComponentRegistration } from '@digitaldefiance/i18n-lib'`
|
|
1388
|
+
4. Check TypeScript version (4.5+ recommended)
|
|
1389
|
+
|
|
1390
|
+
## FAQ
|
|
1391
|
+
|
|
1392
|
+
### When should I use ICU MessageFormat vs simple templates?
|
|
1393
|
+
|
|
1394
|
+
**Use ICU MessageFormat when:**
|
|
1395
|
+
- You need complex pluralization (multiple forms)
|
|
1396
|
+
- You need gender-specific translations
|
|
1397
|
+
- You need number/date/time formatting
|
|
1398
|
+
- You need nested conditional logic
|
|
1399
|
+
|
|
1400
|
+
**Use simple templates when:**
|
|
1401
|
+
- You only need variable substitution
|
|
1402
|
+
- You're referencing other components ({{Component.key}})
|
|
1403
|
+
- You want simpler, more readable strings
|
|
1404
|
+
|
|
1405
|
+
### How do I handle missing translations?
|
|
1406
|
+
|
|
1407
|
+
**Options:**
|
|
1408
|
+
1. Use `safeTranslate()` - returns `[componentId.key]` placeholder
|
|
1409
|
+
2. Set fallback language in config
|
|
1410
|
+
3. Use `engine.validate()` to find missing translations during development
|
|
1411
|
+
4. Implement custom error handling with try/catch
|
|
1412
|
+
|
|
1413
|
+
### Can I use this library without TypeScript?
|
|
1414
|
+
|
|
1415
|
+
**Yes**, but you lose type safety benefits:
|
|
1416
|
+
```javascript
|
|
1417
|
+
const { PluginI18nEngine, LanguageCodes } = require('@digitaldefiance/i18n-lib');
|
|
1418
|
+
const engine = PluginI18nEngine.createInstance('app', languages);
|
|
1419
|
+
```
|
|
1420
|
+
|
|
1421
|
+
### How do I add a new language?
|
|
1422
|
+
|
|
1423
|
+
**Steps:**
|
|
1424
|
+
1. Add language definition to engine creation
|
|
1425
|
+
2. Register components with translations for new language
|
|
1426
|
+
3. If using plurals, check required forms: `getRequiredPluralForms('pt-BR')`
|
|
1427
|
+
4. See [docs/ADDING_LANGUAGES.md](docs/ADDING_LANGUAGES.md) for details
|
|
1428
|
+
|
|
1429
|
+
### What's the difference between branded enums and regular enums?
|
|
1430
|
+
|
|
1431
|
+
**Branded enums** (v4.0.4+):
|
|
1432
|
+
- Runtime identification of component ownership
|
|
1433
|
+
- Collision detection between components
|
|
1434
|
+
- Automatic component routing
|
|
1435
|
+
- Created with `createI18nStringKeys()`
|
|
1436
|
+
|
|
1437
|
+
**Regular enums**:
|
|
1438
|
+
- Compile-time only (erased at runtime)
|
|
1439
|
+
- No collision detection
|
|
1440
|
+
- Must specify component ID in every translate call
|
|
1441
|
+
|
|
1442
|
+
### How do I migrate from v1.x to v4.x?
|
|
1443
|
+
|
|
1444
|
+
**Key changes:**
|
|
1445
|
+
1. `CoreLanguage` enum → `LanguageCodes` constants
|
|
1446
|
+
2. Language IDs now use BCP 47 codes ('en-US' not 'English (US)')
|
|
1447
|
+
3. `register()` → `registerComponent()` (more explicit)
|
|
1448
|
+
4. See [docs/MIGRATION_GUIDE.md](docs/MIGRATION_GUIDE.md) for complete guide
|
|
1449
|
+
|
|
1450
|
+
### Can I use multiple engines in the same application?
|
|
1451
|
+
|
|
1452
|
+
**Yes**, use named instances:
|
|
1453
|
+
```typescript
|
|
1454
|
+
const adminEngine = PluginI18nEngine.createInstance('admin', adminLangs);
|
|
1455
|
+
const userEngine = PluginI18nEngine.createInstance('user', userLangs);
|
|
1456
|
+
```
|
|
1457
|
+
|
|
1458
|
+
See [Multiple Instances](#multiple-instances) section for details.
|
|
1459
|
+
|
|
1460
|
+
### How do I test components that use i18n?
|
|
1461
|
+
|
|
1462
|
+
**Pattern:**
|
|
1463
|
+
```typescript
|
|
1464
|
+
import { PluginI18nEngine } from '@digitaldefiance/i18n-lib';
|
|
1465
|
+
|
|
1466
|
+
describe('MyComponent', () => {
|
|
1467
|
+
beforeEach(() => {
|
|
1468
|
+
PluginI18nEngine.resetAll();
|
|
1469
|
+
const engine = PluginI18nEngine.createInstance('test', languages);
|
|
1470
|
+
engine.registerComponent(/* ... */);
|
|
1471
|
+
});
|
|
1472
|
+
|
|
1473
|
+
afterEach(() => {
|
|
1474
|
+
PluginI18nEngine.resetAll();
|
|
1475
|
+
});
|
|
1476
|
+
});
|
|
1477
|
+
```
|
|
1478
|
+
|
|
1479
|
+
See [Testing](#testing) section for more patterns.
|
|
1480
|
+
|
|
1225
1481
|
## License
|
|
1226
1482
|
|
|
1227
1483
|
MIT License - See LICENSE file for details
|