@digitaldefiance/i18n-lib 4.2.20 → 4.3.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
@@ -4,6 +4,35 @@ A production-ready TypeScript internationalization library with component-based
4
4
 
5
5
  Part of [Express Suite](https://github.com/Digital-Defiance/express-suite)
6
6
 
7
+ ## What's New in v4.3.0
8
+
9
+ ✨ **String Key Enum Registration** - Register branded string key enums for direct translation without specifying component IDs.
10
+
11
+ **Prerequisites**: Requires branded enums from v4.0.4+ (see [Branded Enums](#branded-enums) section)
12
+
13
+ **New Features:**
14
+ - **`registerStringKeyEnum()`**: Register branded enums for automatic component routing
15
+ - **`translateStringKey()`**: Translate keys directly - component ID resolved from branded enum
16
+ - **`safeTranslateStringKey()`**: Safe version returning placeholder on failure
17
+ - **`hasStringKeyEnum()` / `getStringKeyEnums()`**: Query registered enums
18
+
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
+
29
+ // Register your branded enum
30
+ engine.registerStringKeyEnum(MyBrandedKeys);
31
+
32
+ // Translate without component ID - it's resolved automatically
33
+ const text = engine.translateStringKey(MyBrandedKeys.Welcome, { name: 'Alice' });
34
+ ```
35
+
7
36
  ## Features
8
37
 
9
38
  - **Production-Grade Security**: Comprehensive protection against common attacks
@@ -33,8 +62,8 @@ Part of [Express Suite](https://github.com/Digital-Defiance/express-suite)
33
62
  - **Type Safety**: Full TypeScript support with generic types
34
63
  - **Branded Enums**: Runtime-identifiable string keys with collision detection and component routing
35
64
  - **Error Handling**: Comprehensive error classes with translation support and ICU formatting
36
- - **93.22% Test Coverage**: 1,738 tests covering all features
37
- - **Security Hardened**: See [SECURITY.md](SECURITY.md) for details
65
+ - **93.22% Test Coverage**: 1,779 tests covering all features
66
+ - **Security Hardened**: Comprehensive protection against prototype pollution, ReDoS, and XSS attacks
38
67
 
39
68
  ## Installation
40
69
 
@@ -84,8 +113,12 @@ console.log(engine.translate('app', 'welcome', { appName: 'MyApp' }));
84
113
  // Output: "Bienvenue sur MyApp!"
85
114
 
86
115
  // Pluralization (automatic form selection)
87
- engine.register({
88
- id: 'cart',
116
+ engine.registerComponent({
117
+ component: {
118
+ id: 'cart',
119
+ name: 'Cart',
120
+ stringKeys: ['items']
121
+ },
89
122
  strings: {
90
123
  'en-US': {
91
124
  items: {
@@ -104,7 +137,18 @@ console.log(engine.translate('cart', 'items', { count: 5 }));
104
137
 
105
138
  ## ICU MessageFormat
106
139
 
107
- Industry-standard message formatting with powerful features. See [@docs/ICU_MESSAGEFORMAT.md](../../docs/ICU_MESSAGEFORMAT.md) for complete guide.
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
108
152
 
109
153
  ### Quick Example
110
154
 
@@ -146,9 +190,9 @@ formatICUMessage(
146
190
 
147
191
  ### Documentation
148
192
 
149
- - **[@docs/ICU_MESSAGEFORMAT.md](../../docs/ICU_MESSAGEFORMAT.md)** - Complete guide with syntax reference and examples
150
- - **[@docs/ICU_COMPREHENSIVE_VALIDATION.md](../../docs/ICU_COMPREHENSIVE_VALIDATION.md)** - Validation report with test coverage
151
- - **[@docs/ICU_PROJECT_COMPLETE.md](../../docs/ICU_PROJECT_COMPLETE.md)** - Implementation summary
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
152
196
 
153
197
  ### API
154
198
 
@@ -172,11 +216,19 @@ import {
172
216
  Automatic plural form selection based on count with CLDR-compliant rules for 37 languages:
173
217
 
174
218
  ```typescript
175
- 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
+ ]);
176
224
 
177
225
  // English (one/other)
178
- engine.register({
179
- id: 'shop',
226
+ engine.registerComponent({
227
+ component: {
228
+ id: 'shop',
229
+ name: 'Shop',
230
+ stringKeys: ['items']
231
+ },
180
232
  strings: {
181
233
  'en-US': {
182
234
  items: createPluralString({
@@ -188,8 +240,12 @@ engine.register({
188
240
  });
189
241
 
190
242
  // Russian (one/few/many)
191
- engine.register({
192
- id: 'shop',
243
+ engine.registerComponent({
244
+ component: {
245
+ id: 'shop',
246
+ name: 'Shop',
247
+ stringKeys: ['items']
248
+ },
193
249
  strings: {
194
250
  'ru': {
195
251
  items: createPluralString({
@@ -202,8 +258,12 @@ engine.register({
202
258
  });
203
259
 
204
260
  // Arabic (zero/one/two/few/many/other)
205
- engine.register({
206
- id: 'shop',
261
+ engine.registerComponent({
262
+ component: {
263
+ id: 'shop',
264
+ name: 'Shop',
265
+ stringKeys: ['items']
266
+ },
207
267
  strings: {
208
268
  'ar': {
209
269
  items: createPluralString({
@@ -240,10 +300,18 @@ See [PLURALIZATION_SUPPORT.md](docs/PLURALIZATION_SUPPORT.md) for complete langu
240
300
  Gender-aware translations with intelligent fallback:
241
301
 
242
302
  ```typescript
243
- import { createGenderedString } from '@digitaldefiance/i18n-lib';
303
+ import { createGenderedString, PluginI18nEngine, LanguageCodes } from '@digitaldefiance/i18n-lib';
244
304
 
245
- engine.register({
246
- id: 'profile',
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
+ },
247
315
  strings: {
248
316
  'en-US': {
249
317
  greeting: createGenderedString({
@@ -346,10 +414,10 @@ The main engine class that manages translations, languages, and components.
346
414
  ```typescript
347
415
  import { PluginI18nEngine, LanguageCodes } from '@digitaldefiance/i18n-lib';
348
416
 
349
- // Create instance
417
+ // Recommended: Create named instance (supports multiple engines)
350
418
  const engine = PluginI18nEngine.createInstance('myapp', languages);
351
419
 
352
- // Or use constructor
420
+ // Alternative: Direct constructor (for single engine use cases)
353
421
  const engine = new PluginI18nEngine(languages, config);
354
422
  ```
355
423
 
@@ -464,10 +532,45 @@ const engine = I18nBuilder.create()
464
532
  .build();
465
533
  ```
466
534
 
535
+ #### Builder with String Key Enum Registration
536
+
537
+ Register branded string key enums during engine construction for direct translation via `translateStringKey()`:
538
+
539
+ ```typescript
540
+ import { I18nBuilder, createI18nStringKeysFromEnum } from '@digitaldefiance/i18n-lib';
541
+
542
+ // Create branded enum from your string keys
543
+ enum MyStringKeys {
544
+ Welcome = 'welcome',
545
+ Goodbye = 'goodbye',
546
+ }
547
+ const BrandedKeys = createI18nStringKeysFromEnum('my-component', MyStringKeys);
548
+
549
+ const engine = I18nBuilder.create()
550
+ .withLanguages([
551
+ { id: 'en-US', name: 'English', code: 'en-US', isDefault: true },
552
+ ])
553
+ .withStringKeyEnum(BrandedKeys) // Register single enum
554
+ // Or register multiple at once:
555
+ // .withStringKeyEnums([BrandedKeys, OtherKeys])
556
+ .build();
557
+
558
+ // Now you can translate directly without component ID
559
+ engine.translateStringKey(BrandedKeys.Welcome);
560
+ ```
561
+
467
562
  ### Context Integration
468
563
 
564
+ Automatic injection of currency, timezone, and language from GlobalActiveContext:
565
+
469
566
  ```typescript
470
- import { GlobalActiveContext, CurrencyCode, Timezone } from '@digitaldefiance/i18n-lib';
567
+ import {
568
+ GlobalActiveContext,
569
+ CurrencyCode,
570
+ Timezone,
571
+ PluginI18nEngine,
572
+ LanguageCodes
573
+ } from '@digitaldefiance/i18n-lib';
471
574
 
472
575
  // Set context variables
473
576
  const context = GlobalActiveContext.getInstance();
@@ -486,16 +589,23 @@ engine.t('Price in {currency}', { currency: 'USD' }); // "Price in USD"
486
589
 
487
590
  ### Constants Management
488
591
 
592
+ Manage application-wide constants for use in translations:
593
+
489
594
  ```typescript
490
- // Merge constants (adds/overwrites specific keys)
595
+ // Merge constants (adds/updates specific keys, preserves others)
491
596
  engine.mergeConstants({ Version: '2.0', NewKey: 'value' });
492
597
  // Existing constants preserved, specified ones added/updated
493
598
 
494
- // Update all constants (replaces everything)
599
+ // Update all constants (replaces entire constants object)
495
600
  engine.updateConstants({ Site: 'NewSite', Version: '2.0' });
496
601
  // All previous constants removed, only these remain
497
602
  ```
498
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
+
499
609
  ### Language Management
500
610
 
501
611
  ```typescript
@@ -505,9 +615,11 @@ engine.setLanguage(LanguageCodes.FR);
505
615
  // Get current language
506
616
  const lang = engine.getCurrentLanguage();
507
617
 
508
- // Check if language exists
618
+ // Check if language exists (recommended before setLanguage)
509
619
  if (engine.hasLanguage(LanguageCodes.ES)) {
510
620
  engine.setLanguage(LanguageCodes.ES);
621
+ } else {
622
+ console.warn('Spanish not available, using default');
511
623
  }
512
624
 
513
625
  // Get all languages
@@ -516,22 +628,29 @@ const languages = engine.getLanguages();
516
628
 
517
629
  ### Admin Context
518
630
 
519
- 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):
520
632
 
521
633
  ```typescript
522
- // Set admin language
634
+ // Set admin language (e.g., always English for admin panel)
523
635
  engine.setAdminLanguage(LanguageCodes.EN_US);
524
636
 
525
- // Switch to admin context
637
+ // Switch to admin context (uses admin language)
526
638
  engine.switchToAdmin();
639
+ const adminText = engine.translate('app', 'dashboard'); // Uses EN_US
527
640
 
528
- // Switch back to user context
641
+ // Switch back to user context (uses user's language)
529
642
  engine.switchToUser();
643
+ const userText = engine.translate('app', 'dashboard'); // Uses user's language
530
644
  ```
531
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
+
532
651
  ## Core System Strings
533
652
 
534
- Pre-built translations for common UI elements:
653
+ Pre-built translations for common UI elements in 8 languages:
535
654
 
536
655
  ```typescript
537
656
  import { getCoreI18nEngine, CoreStringKey, CoreI18nComponentId } from '@digitaldefiance/i18n-lib';
@@ -543,21 +662,23 @@ const yes = coreEngine.translate(CoreI18nComponentId, CoreStringKey.Common_Yes);
543
662
  const error = coreEngine.translate(CoreI18nComponentId, CoreStringKey.Error_NotFound);
544
663
  ```
545
664
 
546
- Available core string categories:
665
+ **Available core string categories:**
547
666
 
548
- - **Common**: Yes, No, Cancel, OK, Save, Delete, Edit, Create, Update, Loading, etc.
549
- - **Errors**: InvalidInput, NetworkError, NotFound, AccessDenied, ValidationFailed, etc.
550
- - **System**: Welcome, Goodbye, PleaseWait, ProcessingRequest, OperationComplete, etc.
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
670
+
671
+ See `CoreStringKey` enum for complete list of available strings.
551
672
 
552
673
  ## Multiple Instances
553
674
 
554
- 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):
555
676
 
556
677
  ```typescript
557
- // Admin engine
678
+ // Admin engine with admin-specific languages
558
679
  const adminEngine = PluginI18nEngine.createInstance('admin', adminLanguages);
559
680
 
560
- // User engine
681
+ // User engine with user-facing languages
561
682
  const userEngine = PluginI18nEngine.createInstance('user', userLanguages);
562
683
 
563
684
  // Get instance by key
@@ -568,13 +689,19 @@ if (PluginI18nEngine.hasInstance('admin')) {
568
689
  // ...
569
690
  }
570
691
 
571
- // Remove instance
692
+ // Remove instance (cleanup)
572
693
  PluginI18nEngine.removeInstance('admin');
573
694
 
574
- // Reset all instances
575
- PluginI18nEngine.resetAll();
695
+ // Reset all instances (useful in tests)
696
+ PluginI18nEngine.resetAll(); // ⚠️ Removes ALL instances globally
576
697
  ```
577
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
+
578
705
  ## Error Handling
579
706
 
580
707
  ### RegistryError
@@ -618,7 +745,7 @@ throw new MyError(LanguageCodes.FR); // Throws with French error message
618
745
 
619
746
  ## Translation Adapter
620
747
 
621
- 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):
622
749
 
623
750
  ```typescript
624
751
  import { createTranslationAdapter } from '@digitaldefiance/i18n-lib';
@@ -626,6 +753,7 @@ import { createTranslationAdapter } from '@digitaldefiance/i18n-lib';
626
753
  const adapter = createTranslationAdapter(engine, 'componentId');
627
754
 
628
755
  // Use adapter where TranslationEngine is expected
756
+ // (e.g., error classes, third-party libraries)
629
757
  const message = adapter.translate('key', { var: 'value' });
630
758
  ```
631
759
 
@@ -646,6 +774,23 @@ LanguageCodes.JA // 'ja'
646
774
  LanguageCodes.UK // 'uk'
647
775
  ```
648
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
+
649
794
  ## API Reference
650
795
 
651
796
  ### PluginI18nEngine
@@ -674,6 +819,11 @@ LanguageCodes.UK // 'uk'
674
819
  - `validate()` - Validate all components
675
820
  - `registerBrandedComponent(registration)` - Register component with branded string keys
676
821
  - `getCollisionReport()` - Get map of key collisions across components
822
+ - `registerStringKeyEnum(enum)` - Register a branded string key enum for direct translation
823
+ - `translateStringKey(key, variables?, language?)` - Translate a branded string key directly
824
+ - `safeTranslateStringKey(key, variables?, language?)` - Safe version returning placeholder on failure
825
+ - `hasStringKeyEnum(enum)` - Check if a branded enum is registered
826
+ - `getStringKeyEnums()` - Get all registered branded enums
677
827
 
678
828
  ### Branded Enum Functions
679
829
 
@@ -924,6 +1074,77 @@ const allValues = getStringKeyValues(AllKeys);
924
1074
 
925
1075
  For complete migration guide, see [BRANDED_ENUM_MIGRATION.md](docs/BRANDED_ENUM_MIGRATION.md).
926
1076
 
1077
+ ### String Key Enum Registration
1078
+
1079
+ Register branded string key enums with the engine for direct translation without specifying component IDs. This simplifies translation calls and enables automatic component routing.
1080
+
1081
+ #### Registering String Key Enums
1082
+
1083
+ ```typescript
1084
+ import { createI18nStringKeysFromEnum, PluginI18nEngine } from '@digitaldefiance/i18n-lib';
1085
+
1086
+ // Create a branded enum from your string keys
1087
+ enum MyStringKeys {
1088
+ Welcome = 'welcome',
1089
+ Goodbye = 'goodbye',
1090
+ }
1091
+
1092
+ const BrandedKeys = createI18nStringKeysFromEnum('my-component', MyStringKeys);
1093
+
1094
+ // Create engine and register component
1095
+ const engine = PluginI18nEngine.createInstance('myapp', languages);
1096
+ engine.registerBrandedComponent({
1097
+ component: {
1098
+ id: 'my-component',
1099
+ name: 'My Component',
1100
+ brandedStringKeys: BrandedKeys,
1101
+ },
1102
+ strings: {
1103
+ [LanguageCodes.EN_US]: {
1104
+ [BrandedKeys.Welcome]: 'Welcome!',
1105
+ [BrandedKeys.Goodbye]: 'Goodbye!',
1106
+ },
1107
+ },
1108
+ });
1109
+
1110
+ // Register the enum for direct translation
1111
+ engine.registerStringKeyEnum(BrandedKeys);
1112
+ ```
1113
+
1114
+ #### Direct Translation with translateStringKey
1115
+
1116
+ Once registered, translate keys directly without specifying the component ID:
1117
+
1118
+ ```typescript
1119
+ // Before: Required component ID
1120
+ const text = engine.translate('my-component', BrandedKeys.Welcome, { name: 'Alice' });
1121
+
1122
+ // After: Component ID resolved automatically from branded enum
1123
+ const text = engine.translateStringKey(BrandedKeys.Welcome, { name: 'Alice' });
1124
+
1125
+ // Safe version returns placeholder on failure instead of throwing
1126
+ const safeText = engine.safeTranslateStringKey(BrandedKeys.Welcome, { name: 'Alice' });
1127
+ ```
1128
+
1129
+ #### Checking Registration Status
1130
+
1131
+ ```typescript
1132
+ // Check if an enum is registered
1133
+ if (engine.hasStringKeyEnum(BrandedKeys)) {
1134
+ console.log('BrandedKeys is registered');
1135
+ }
1136
+
1137
+ // Get all registered enums
1138
+ const registeredEnums = engine.getStringKeyEnums();
1139
+ ```
1140
+
1141
+ #### Benefits
1142
+
1143
+ - **Cleaner Code**: No need to repeat component IDs in every translation call
1144
+ - **Automatic Routing**: Component ID resolved from branded enum metadata
1145
+ - **Type Safety**: Full TypeScript support with branded enum types
1146
+ - **Idempotent**: Safe to call `registerStringKeyEnum()` multiple times
1147
+
927
1148
  ### Branded Enum Translation
928
1149
 
929
1150
  The enum translation system supports branded enums from `@digitaldefiance/branded-enum`, enabling automatic name inference and type-safe enum value translations.
@@ -1096,11 +1317,167 @@ function translateAnyValue<T extends string | number>(
1096
1317
 
1097
1318
  ## Browser Support
1098
1319
 
1099
- - Chrome/Edge: Latest 2 versions
1100
- - Firefox: Latest 2 versions
1101
- - 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)
1102
1323
  - Node.js: 18+
1103
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
+
1104
1481
  ## License
1105
1482
 
1106
1483
  MIT License - See LICENSE file for details
@@ -1123,6 +1500,107 @@ Contributions welcome! Please:
1123
1500
 
1124
1501
  ## ChangeLog
1125
1502
 
1503
+ ### Version 4.3.0
1504
+
1505
+ **String Key Enum Registration for Direct Translation**
1506
+
1507
+ This release adds the ability to register branded string key enums with the I18nEngine for direct translation via `translateStringKey()`. This enables automatic component ID resolution from branded enum values.
1508
+
1509
+ **New Features:**
1510
+
1511
+ - **`registerStringKeyEnum()`**: Register a branded string key enum for direct translation support
1512
+ ```typescript
1513
+ import { createI18nStringKeysFromEnum } from '@digitaldefiance/i18n-lib';
1514
+
1515
+ const MyKeys = createI18nStringKeysFromEnum('my-component', MyStringKeyEnum);
1516
+ engine.registerStringKeyEnum(MyKeys);
1517
+ ```
1518
+
1519
+ - **`translateStringKey()`**: Translate a branded string key value directly without specifying component ID
1520
+ ```typescript
1521
+ // Before: engine.translate('my-component', MyKeys.Welcome, { name: 'Alice' });
1522
+ // After:
1523
+ engine.translateStringKey(MyKeys.Welcome, { name: 'Alice' });
1524
+ ```
1525
+
1526
+ - **`safeTranslateStringKey()`**: Safe version that returns a placeholder on failure instead of throwing
1527
+
1528
+ - **`hasStringKeyEnum()`**: Check if a branded enum is registered
1529
+
1530
+ - **`getStringKeyEnums()`**: Get all registered branded enums
1531
+
1532
+ **New Error Codes:**
1533
+
1534
+ - `INVALID_STRING_KEY_ENUM` - When a non-branded enum is passed to `registerStringKeyEnum()`
1535
+ - `STRING_KEY_NOT_REGISTERED` - When translating a key from an unregistered enum
1536
+
1537
+ **Benefits:**
1538
+
1539
+ - Cleaner translation calls without repeating component IDs
1540
+ - Automatic component routing based on branded enum metadata
1541
+ - Type-safe translations with full TypeScript support
1542
+ - Idempotent registration (safe to call multiple times)
1543
+
1544
+ ### Version 4.2.0
1545
+
1546
+ **Branded Enum Translation Support**
1547
+
1548
+ This release adds comprehensive support for translating branded enums from `@digitaldefiance/branded-enum`, enabling automatic name inference and type-safe enum value translations.
1549
+
1550
+ **New Features:**
1551
+
1552
+ - **Automatic Name Inference**: When registering a branded enum, the name is automatically extracted from the enum's component ID
1553
+ ```typescript
1554
+ const Status = createBrandedEnum('status', { Active: 'active', Inactive: 'inactive' });
1555
+ engine.registerEnum(Status, translations); // Name 'status' inferred automatically
1556
+ ```
1557
+
1558
+ - **New Utility Functions**:
1559
+ - `isBrandedEnum()` - Type guard to detect branded enums
1560
+ - `getBrandedEnumComponentId()` - Extract component ID from branded enum
1561
+ - `getBrandedEnumId()` - Get raw brand ID for debugging
1562
+
1563
+ - **Enhanced `registerEnum()`**: Now accepts branded enums and returns the registered translations object
1564
+
1565
+ - **Property-Based Tests**: Comprehensive property tests for branded enum translation ensuring:
1566
+ - Registration idempotence
1567
+ - Translation consistency across languages
1568
+ - Proper error handling for missing translations
1569
+
1570
+ **Documentation:**
1571
+
1572
+ - Added "Branded Enum Translation" section to README
1573
+ - Comprehensive JSDoc documentation for all new utilities
1574
+
1575
+ ### Version 4.1.0
1576
+
1577
+ **Enhanced Type Documentation and BrandedMasterStringsCollection**
1578
+
1579
+ This release focuses on improved documentation and ergonomic type aliases for working with branded enums.
1580
+
1581
+ **New Features:**
1582
+
1583
+ - **`BrandedMasterStringsCollection<E, TLanguage>`**: Ergonomic type alias for defining translation collections with branded enums
1584
+ ```typescript
1585
+ const translations: BrandedMasterStringsCollection<typeof MyKeys, CoreLanguageCode> = {
1586
+ [LanguageCodes.EN_US]: { 'my.welcome': 'Welcome!' },
1587
+ [LanguageCodes.FR]: { 'my.welcome': 'Bienvenue!' },
1588
+ };
1589
+ ```
1590
+
1591
+ - **`BrandedPluralMasterStringsCollection<E, TLanguage>`**: Type alias for plural-aware branded translations
1592
+
1593
+ - **Comprehensive Module Documentation**: Added extensive JSDoc documentation to `types.ts` explaining:
1594
+ - Traditional enums vs branded enums
1595
+ - Migration patterns from legacy enums
1596
+ - Complete examples for defining translations
1597
+
1598
+ **Documentation:**
1599
+
1600
+ - Step-by-step guide for creating branded string keys
1601
+ - Examples showing `BrandedMasterStringsCollection` usage
1602
+ - Migration guide from traditional `EnumLanguageTranslation` types
1603
+
1126
1604
  ### Version 4.0.4
1127
1605
 
1128
1606
  **Branded Enums for Runtime String Key Identification**