@digitaldefiance/i18n-lib 1.3.27 → 2.0.0

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.
Files changed (236) hide show
  1. package/README.md +394 -2262
  2. package/package.json +4 -1
  3. package/src/active-context.d.ts +2 -2
  4. package/src/active-context.d.ts.map +1 -1
  5. package/src/builders/i18n-builder.d.ts +24 -0
  6. package/src/builders/i18n-builder.d.ts.map +1 -0
  7. package/src/builders/i18n-builder.js +62 -0
  8. package/src/builders/i18n-builder.js.map +1 -0
  9. package/src/builders/index.d.ts +5 -0
  10. package/src/builders/index.d.ts.map +1 -0
  11. package/src/builders/index.js +8 -0
  12. package/src/builders/index.js.map +1 -0
  13. package/src/core/component-store.d.ts +20 -0
  14. package/src/core/component-store.d.ts.map +1 -0
  15. package/src/core/component-store.js +105 -0
  16. package/src/core/component-store.js.map +1 -0
  17. package/src/core/context-manager.d.ts +20 -0
  18. package/src/core/context-manager.d.ts.map +1 -0
  19. package/src/core/context-manager.js +50 -0
  20. package/src/core/context-manager.js.map +1 -0
  21. package/src/core/enum-registry.d.ts +14 -0
  22. package/src/core/enum-registry.d.ts.map +1 -0
  23. package/src/core/enum-registry.js +48 -0
  24. package/src/core/enum-registry.js.map +1 -0
  25. package/src/core/i18n-engine.d.ts +44 -0
  26. package/src/core/i18n-engine.d.ts.map +1 -0
  27. package/src/core/i18n-engine.js +192 -0
  28. package/src/core/i18n-engine.js.map +1 -0
  29. package/src/core/index.d.ts +9 -0
  30. package/src/core/index.d.ts.map +1 -0
  31. package/src/core/index.js +12 -0
  32. package/src/core/index.js.map +1 -0
  33. package/src/core/language-registry.d.ts +41 -0
  34. package/src/core/language-registry.d.ts.map +1 -0
  35. package/src/core/language-registry.js +157 -0
  36. package/src/core/language-registry.js.map +1 -0
  37. package/src/core-i18n.d.ts +4 -0
  38. package/src/core-i18n.d.ts.map +1 -1
  39. package/src/core-i18n.js +109 -4
  40. package/src/core-i18n.js.map +1 -1
  41. package/src/core-string-key.d.ts +10 -1
  42. package/src/core-string-key.d.ts.map +1 -1
  43. package/src/core-string-key.js +10 -0
  44. package/src/core-string-key.js.map +1 -1
  45. package/src/create-translation-adapter.d.ts +15 -3
  46. package/src/create-translation-adapter.d.ts.map +1 -1
  47. package/src/create-translation-adapter.js +44 -9
  48. package/src/create-translation-adapter.js.map +1 -1
  49. package/src/{context-error.d.ts → errors/context-error.d.ts} +1 -1
  50. package/src/errors/context-error.d.ts.map +1 -0
  51. package/src/errors/context-error.js +21 -0
  52. package/src/errors/context-error.js.map +1 -0
  53. package/src/{handleable.d.ts → errors/handleable.d.ts} +2 -2
  54. package/src/errors/handleable.d.ts.map +1 -0
  55. package/src/errors/handleable.js.map +1 -0
  56. package/src/errors/i18n-error.d.ts +32 -0
  57. package/src/errors/i18n-error.d.ts.map +1 -0
  58. package/src/errors/i18n-error.js +61 -0
  59. package/src/errors/i18n-error.js.map +1 -0
  60. package/src/errors/index.d.ts +5 -0
  61. package/src/errors/index.d.ts.map +1 -0
  62. package/src/errors/index.js +8 -0
  63. package/src/errors/index.js.map +1 -0
  64. package/src/{plugin-translatable-generic-error.d.ts → errors/plugin-translatable-generic.d.ts} +7 -7
  65. package/src/errors/plugin-translatable-generic.d.ts.map +1 -0
  66. package/src/{plugin-translatable-generic-error.js → errors/plugin-translatable-generic.js} +2 -3
  67. package/src/errors/plugin-translatable-generic.js.map +1 -0
  68. package/src/{plugin-translatable-handleable-generic.d.ts → errors/plugin-translatable-handleable-generic.d.ts} +4 -4
  69. package/src/errors/plugin-translatable-handleable-generic.d.ts.map +1 -0
  70. package/src/{plugin-translatable-handleable-generic.js → errors/plugin-translatable-handleable-generic.js} +2 -2
  71. package/src/errors/plugin-translatable-handleable-generic.js.map +1 -0
  72. package/src/errors/plugin-typed-handleable.d.ts +14 -0
  73. package/src/errors/plugin-typed-handleable.d.ts.map +1 -0
  74. package/src/{plugin-typed-handleable.js → errors/plugin-typed-handleable.js} +4 -4
  75. package/src/errors/plugin-typed-handleable.js.map +1 -0
  76. package/src/errors/translatable.d.ts +4 -0
  77. package/src/errors/translatable.d.ts.map +1 -0
  78. package/src/{translatable.js → errors/translatable.js} +4 -2
  79. package/src/errors/translatable.js.map +1 -0
  80. package/src/errors/typed-handleable.d.ts +13 -0
  81. package/src/errors/typed-handleable.d.ts.map +1 -0
  82. package/src/{typed-handleable.js → errors/typed-handleable.js} +8 -7
  83. package/src/errors/typed-handleable.js.map +1 -0
  84. package/src/{typed-error.d.ts → errors/typed.d.ts} +13 -15
  85. package/src/errors/typed.d.ts.map +1 -0
  86. package/src/{typed-error.js → errors/typed.js} +27 -23
  87. package/src/errors/typed.js.map +1 -0
  88. package/src/global-active-context.d.ts +4 -5
  89. package/src/global-active-context.d.ts.map +1 -1
  90. package/src/global-active-context.js +4 -4
  91. package/src/global-active-context.js.map +1 -1
  92. package/src/index.d.ts +15 -32
  93. package/src/index.d.ts.map +1 -1
  94. package/src/index.js +29 -37
  95. package/src/index.js.map +1 -1
  96. package/src/interfaces/component-config.interface.d.ts +9 -0
  97. package/src/interfaces/component-config.interface.d.ts.map +1 -0
  98. package/src/interfaces/component-config.interface.js +6 -0
  99. package/src/interfaces/component-config.interface.js.map +1 -0
  100. package/src/interfaces/engine-config.interface.d.ts +13 -0
  101. package/src/interfaces/engine-config.interface.d.ts.map +1 -0
  102. package/src/interfaces/engine-config.interface.js +6 -0
  103. package/src/interfaces/engine-config.interface.js.map +1 -0
  104. package/src/{i-global-active-context.d.ts → interfaces/global-active-context.d.ts} +5 -5
  105. package/src/interfaces/global-active-context.d.ts.map +1 -0
  106. package/src/{currency-format.js → interfaces/global-active-context.js} +1 -1
  107. package/src/interfaces/global-active-context.js.map +1 -0
  108. package/src/{i-handleable-error-options.d.ts → interfaces/handleable-error-options.d.ts} +1 -1
  109. package/src/interfaces/handleable-error-options.d.ts.map +1 -0
  110. package/src/{i-global-active-context.js → interfaces/handleable-error-options.js} +1 -1
  111. package/src/interfaces/handleable-error-options.js.map +1 -0
  112. package/src/{i-handleable.d.ts → interfaces/handleable.d.ts} +1 -1
  113. package/src/interfaces/handleable.d.ts.map +1 -0
  114. package/src/{i18n-config.js → interfaces/handleable.js} +1 -1
  115. package/src/interfaces/handleable.js.map +1 -0
  116. package/src/interfaces/i18n-engine.interface.d.ts +25 -0
  117. package/src/interfaces/i18n-engine.interface.d.ts.map +1 -0
  118. package/src/interfaces/i18n-engine.interface.js +6 -0
  119. package/src/interfaces/i18n-engine.interface.js.map +1 -0
  120. package/src/interfaces/index.d.ts +10 -0
  121. package/src/interfaces/index.d.ts.map +1 -0
  122. package/src/interfaces/index.js +13 -0
  123. package/src/interfaces/index.js.map +1 -0
  124. package/src/interfaces/language-definition.interface.d.ts +10 -0
  125. package/src/interfaces/language-definition.interface.d.ts.map +1 -0
  126. package/src/interfaces/language-definition.interface.js +6 -0
  127. package/src/interfaces/language-definition.interface.js.map +1 -0
  128. package/src/interfaces/translation-options.interface.d.ts +9 -0
  129. package/src/interfaces/translation-options.interface.d.ts.map +1 -0
  130. package/src/interfaces/translation-options.interface.js +6 -0
  131. package/src/interfaces/translation-options.interface.js.map +1 -0
  132. package/src/interfaces/validation-result.interface.d.ts +14 -0
  133. package/src/interfaces/validation-result.interface.d.ts.map +1 -0
  134. package/src/interfaces/validation-result.interface.js +6 -0
  135. package/src/interfaces/validation-result.interface.js.map +1 -0
  136. package/src/plugin-i18n-engine.d.ts.map +1 -1
  137. package/src/plugin-i18n-engine.js +19 -7
  138. package/src/plugin-i18n-engine.js.map +1 -1
  139. package/src/registry-config.d.ts +2 -2
  140. package/src/registry-config.d.ts.map +1 -1
  141. package/src/registry-error.d.ts +1 -1
  142. package/src/registry-error.d.ts.map +1 -1
  143. package/src/registry-error.js +3 -2
  144. package/src/registry-error.js.map +1 -1
  145. package/src/translation-engine.d.ts +2 -2
  146. package/src/translation-engine.d.ts.map +1 -1
  147. package/src/utils/currency.d.ts +19 -0
  148. package/src/utils/currency.d.ts.map +1 -0
  149. package/src/{currency.js → utils/currency.js} +25 -14
  150. package/src/utils/currency.js.map +1 -0
  151. package/src/utils/index.d.ts +7 -0
  152. package/src/utils/index.d.ts.map +1 -0
  153. package/src/utils/index.js +10 -0
  154. package/src/utils/index.js.map +1 -0
  155. package/src/utils/string-utils.d.ts +6 -0
  156. package/src/utils/string-utils.d.ts.map +1 -0
  157. package/src/utils/string-utils.js +34 -0
  158. package/src/utils/string-utils.js.map +1 -0
  159. package/src/utils/timezone.d.ts +13 -0
  160. package/src/utils/timezone.d.ts.map +1 -0
  161. package/src/utils/timezone.js +35 -0
  162. package/src/utils/timezone.js.map +1 -0
  163. package/src/context-error.d.ts.map +0 -1
  164. package/src/context-error.js +0 -18
  165. package/src/context-error.js.map +0 -1
  166. package/src/context-manager.d.ts +0 -34
  167. package/src/context-manager.d.ts.map +0 -1
  168. package/src/context-manager.js +0 -60
  169. package/src/context-manager.js.map +0 -1
  170. package/src/context.d.ts +0 -45
  171. package/src/context.d.ts.map +0 -1
  172. package/src/context.js +0 -70
  173. package/src/context.js.map +0 -1
  174. package/src/currency-code.d.ts +0 -20
  175. package/src/currency-code.d.ts.map +0 -1
  176. package/src/currency-code.js +0 -37
  177. package/src/currency-code.js.map +0 -1
  178. package/src/currency-format.d.ts +0 -11
  179. package/src/currency-format.d.ts.map +0 -1
  180. package/src/currency-format.js.map +0 -1
  181. package/src/currency.d.ts +0 -12
  182. package/src/currency.d.ts.map +0 -1
  183. package/src/currency.js.map +0 -1
  184. package/src/default-config.d.ts +0 -33
  185. package/src/default-config.d.ts.map +0 -1
  186. package/src/default-config.js +0 -131
  187. package/src/default-config.js.map +0 -1
  188. package/src/handleable.d.ts.map +0 -1
  189. package/src/handleable.js.map +0 -1
  190. package/src/i-global-active-context.d.ts.map +0 -1
  191. package/src/i-global-active-context.js.map +0 -1
  192. package/src/i-handleable-error-options.d.ts.map +0 -1
  193. package/src/i-handleable-error-options.js +0 -3
  194. package/src/i-handleable-error-options.js.map +0 -1
  195. package/src/i-handleable.d.ts.map +0 -1
  196. package/src/i-handleable.js +0 -3
  197. package/src/i-handleable.js.map +0 -1
  198. package/src/i18n-config.d.ts +0 -21
  199. package/src/i18n-config.d.ts.map +0 -1
  200. package/src/i18n-config.js.map +0 -1
  201. package/src/i18n-context.d.ts +0 -15
  202. package/src/i18n-context.d.ts.map +0 -1
  203. package/src/i18n-context.js +0 -3
  204. package/src/i18n-context.js.map +0 -1
  205. package/src/i18n-engine.d.ts +0 -179
  206. package/src/i18n-engine.d.ts.map +0 -1
  207. package/src/i18n-engine.js +0 -355
  208. package/src/i18n-engine.js.map +0 -1
  209. package/src/language-registry.d.ts +0 -114
  210. package/src/language-registry.d.ts.map +0 -1
  211. package/src/language-registry.js +0 -218
  212. package/src/language-registry.js.map +0 -1
  213. package/src/plugin-translatable-generic-error.d.ts.map +0 -1
  214. package/src/plugin-translatable-generic-error.js.map +0 -1
  215. package/src/plugin-translatable-handleable-generic.d.ts.map +0 -1
  216. package/src/plugin-translatable-handleable-generic.js.map +0 -1
  217. package/src/plugin-typed-handleable.d.ts +0 -15
  218. package/src/plugin-typed-handleable.d.ts.map +0 -1
  219. package/src/plugin-typed-handleable.js.map +0 -1
  220. package/src/timezone.d.ts +0 -12
  221. package/src/timezone.d.ts.map +0 -1
  222. package/src/timezone.js +0 -24
  223. package/src/timezone.js.map +0 -1
  224. package/src/translatable.d.ts +0 -6
  225. package/src/translatable.d.ts.map +0 -1
  226. package/src/translatable.js.map +0 -1
  227. package/src/typed-error.d.ts.map +0 -1
  228. package/src/typed-error.js.map +0 -1
  229. package/src/typed-handleable.d.ts +0 -15
  230. package/src/typed-handleable.d.ts.map +0 -1
  231. package/src/typed-handleable.js.map +0 -1
  232. package/src/unified-translator.d.ts +0 -31
  233. package/src/unified-translator.d.ts.map +0 -1
  234. package/src/unified-translator.js +0 -71
  235. package/src/unified-translator.js.map +0 -1
  236. /package/src/{handleable.js → errors/handleable.js} +0 -0
package/README.md CHANGED
@@ -1,2395 +1,447 @@
1
1
  # @digitaldefiance/i18n-lib
2
2
 
3
- A comprehensive, production-ready TypeScript internationalization (i18n) library featuring plugin-based architecture, compile-time type safety, component registration, enum translation, template processing, and advanced context management. Built for enterprise applications requiring robust multilingual support with zero-knowledge security patterns.
3
+ A production-ready TypeScript internationalization library with component-based architecture, type-safe translations, and comprehensive error handling.
4
4
 
5
- ## 🚀 Plugin-Based Architecture
6
-
7
- **Version 1.1.0+** introduces a revolutionary plugin-based architecture with component registration and rigid compile-time type safety:
8
-
9
- - **Component Registration System**: Register translation components with their own isolated string keys
10
- - **Language Plugin Support**: Add new languages dynamically with automatic validation
11
- - **Compile-Time Type Safety**: TypeScript ensures all strings are complete for all languages at build time
12
- - **Automatic Validation**: Comprehensive validation with detailed error reporting and missing key detection
13
- - **Intelligent Fallback System**: Graceful degradation to default languages with missing translation tracking
14
- - **Multi-Instance Support**: Named instances for different application contexts (admin, user, API, etc.)
15
- - **Global Context Management**: Centralized context with per-instance language, currency, and timezone settings
16
- - **Translation Adapters**: Generic adapter utilities for seamless integration with error classes and other components
17
-
18
- ## Features
19
-
20
- ### Core Translation Features
21
-
22
- - **Type-Safe Translations**: Full TypeScript support with generic types for strings, languages, and contexts
23
- - **Plugin Architecture**: Register components and languages dynamically with full compile-time type safety
24
- - **Configuration Validation**: Automatic validation ensures all languages have complete string collections
25
- - **Localized Error Messages**: Error messages translated using the engine's own translation system
26
- - **Enum Translation Registry**: Translate enum values with complete type safety and automatic validation
27
- - **Advanced Template Processing**:
28
- - Component-based patterns: `{{componentId.stringKey}}` (supports component IDs, registered aliases, or enum-name prefixes such as `{{SuiteCoreStringKey.User_Login}}`)
29
- - Legacy enum patterns: `{{EnumName.EnumKey}}`
30
- - Variable replacement: `{variableName}`
31
- - Nested template support with multiple variable objects
32
- - **Context Management**:
33
- - Admin vs user translation contexts
34
- - Automatic language switching based on context
35
- - Per-instance context isolation
36
- - Global context with named instance support
37
- - **Currency Formatting**: Built-in currency formatting utilities with locale-aware symbol positioning
38
- - **Timezone Support**: Validated timezone handling with moment-timezone integration
39
- - **Intelligent Fallback System**:
40
- - Graceful degradation when translations are missing
41
- - Fallback to default language with tracking
42
- - Placeholder generation for missing keys: `[componentId.stringKey]`
43
- - **Extensible Configuration**: Module augmentation support for layered library extension
44
- - **Backward Compatibility**: Legacy I18nEngine remains fully supported for migration paths
45
-
46
- ### Plugin System Features
47
-
48
- - **Component Registry**:
49
- - Manage translation components with automatic validation
50
- - Component isolation with independent string key namespaces
51
- - Dynamic component registration and updates
52
- - Comprehensive validation reporting
53
- - **Language Registry**:
54
- - Dynamic language registration with metadata (name, code, default flag)
55
- - BCP 47 language code support
56
- - Language lookup by ID or ISO code
57
- - Display name mapping for UI rendering
58
- - **Type-Safe Registration**:
59
- - Compile-time guarantees for translation completeness
60
- - Strict type helpers for enforcing complete translations
61
- - Partial registration support with fallback generation
62
- - **Validation System**:
63
- - Detailed missing translation reports
64
- - Per-component validation results
65
- - Global validation across all components
66
- - Configurable validation strictness
67
- - **Instance Management**:
68
- - Named instances for different application contexts
69
- - Singleton pattern with automatic default instance
70
- - Instance cleanup utilities for testing
71
- - Per-instance context and configuration
72
-
73
- ### Advanced Features
74
-
75
- - **Translatable Errors**:
76
- - Generic translatable error class for any component
77
- - Automatic translation with fallback support
78
- - Error retranslation for dynamic language switching
79
- - Metadata attachment for debugging
80
- - **Translation Adapters**:
81
- - Generic adapter creation for PluginI18nEngine
82
- - Seamless integration with error classes
83
- - Zero-overhead delegation pattern
84
- - **Context Change Monitoring**:
85
- - Reactive context proxies with change listeners
86
- - Property-level change notifications
87
- - Error-safe listener execution
88
- - **Strict Type Enforcement**:
89
- - Compile-time completeness checking
90
- - Helper functions for strict translation maps
91
- - Type utilities for extracting string keys and languages
92
- - **Testing Utilities**:
93
- - Instance cleanup methods
94
- - Component registry reset
95
- - Global engine reset for test isolation
96
-
97
- ## Table of Contents
98
-
99
- - [Installation](#installation)
100
- - [Quick Start](#quick-start)
101
- - [Plugin-Based Architecture](#plugin-based-architecture)
102
- - [Core Components](#core-components)
103
- - [Advanced Features](#advanced-features)
104
- - [API Reference](#api-reference)
105
- - [Type Definitions](#type-definitions)
106
- - [Testing](#testing)
107
- - [Best Practices](#best-practices)
108
- - [Migration Guide](#migration-guide)
109
- - [Changelog](#changelog)
110
-
111
- ## Installation
112
-
113
- ```bash
114
- npm install @digitaldefiance/i18n-lib
115
- # or
116
- yarn add @digitaldefiance/i18n-lib
117
- ```
118
-
119
- ### Dependencies
120
-
121
- - `currency-codes`: Currency code validation
122
- - `moment-timezone`: Timezone validation and handling
123
- - TypeScript 4.5+ recommended for full type safety
124
-
125
- ## Quick Start
126
-
127
- ```typescript
128
- import {
129
- PluginI18nEngine,
130
- ComponentDefinition,
131
- ComponentRegistration,
132
- LanguageDefinition,
133
- CurrencyCode,
134
- Timezone,
135
- } from '@digitaldefiance/i18n-lib';
136
-
137
- enum MyStringKey {
138
- Welcome = 'welcome',
139
- UserGreetingTemplate = 'userGreetingTemplate',
140
- }
141
-
142
- type MyLanguages = 'en-US' | 'es';
143
-
144
- const languages: ReadonlyArray<LanguageDefinition> = [
145
- { id: 'en-US', name: 'English (US)', code: 'en-US', isDefault: true },
146
- { id: 'es', name: 'Español', code: 'es' },
147
- ];
148
-
149
- const engine = new PluginI18nEngine<MyLanguages>(languages, {
150
- defaultCurrencyCode: new CurrencyCode('USD'),
151
- timezone: new Timezone('UTC'),
152
- adminTimezone: new Timezone('UTC'),
153
- });
154
-
155
- const MyComponent: ComponentDefinition<MyStringKey> = {
156
- id: 'my-component',
157
- name: 'My Component',
158
- stringKeys: Object.values(MyStringKey),
159
- };
160
-
161
- const registration: ComponentRegistration<MyStringKey, MyLanguages> = {
162
- component: MyComponent,
163
- strings: {
164
- 'en-US': {
165
- [MyStringKey.Welcome]: 'Welcome!',
166
- [MyStringKey.UserGreetingTemplate]: 'Hello, {name}!'
167
- },
168
- 'es': {
169
- [MyStringKey.Welcome]: '¡Bienvenido!',
170
- [MyStringKey.UserGreetingTemplate]: '¡Hola, {name}!'
171
- }
172
- }
173
- };
174
-
175
- engine.registerComponent(registration);
176
-
177
- const welcome = engine.translate('my-component', MyStringKey.Welcome);
178
- // "Welcome!"
179
-
180
- const greeting = engine.translate('my-component', MyStringKey.UserGreetingTemplate, { name: 'John' });
181
- // "Hello, John!"
182
-
183
- engine.setLanguage('es');
184
- const spanishGreeting = engine.translate('my-component', MyStringKey.UserGreetingTemplate, { name: 'Juan' });
185
- // "¡Hola, Juan!"
186
-
187
- const templated = engine.t('User: {name} → {{my-component.UserGreetingTemplate}}', 'es', { name: 'Laura' });
188
- // "User: Laura → ¡Hola, Laura!"
189
- ```
190
-
191
- The constructor automatically registers the provided languages, seeds the global context (language, currency, timezone), and makes the instance discoverable through `PluginI18nEngine.getInstance()`. Prefer `setLanguage` or `updateContext({ language: 'es' })` instead of mutating context objects directly.
192
-
193
- ## Core Components
194
-
195
- The library is built around several key components that work together to provide comprehensive i18n support:
196
-
197
- ### Component Registry
198
-
199
- Manages translation components with automatic validation:
200
-
201
- ```typescript
202
- import { ComponentRegistry, ComponentDefinition, ComponentRegistration } from '@digitaldefiance/i18n-lib';
203
-
204
- // Define component
205
- const myComponent: ComponentDefinition<MyStringKeys> = {
206
- id: 'my-component',
207
- name: 'My Component',
208
- stringKeys: Object.values(MyStringKeys)
209
- };
210
-
211
- // Register with translations
212
- const registration: ComponentRegistration<MyStringKeys, Languages> = {
213
- component: myComponent,
214
- strings: {
215
- 'en-US': { /* translations */ },
216
- 'fr': { /* translations */ }
217
- }
218
- };
219
- ```
220
-
221
- **Key Features:**
222
-
223
- - Component isolation with independent string key namespaces
224
- - Automatic validation of translation completeness
225
- - Dynamic component registration and updates
226
- - Fallback generation for missing translations
227
- - Detailed validation reporting
228
-
229
- ### Language Registry
230
-
231
- Manages supported languages with metadata:
232
-
233
- ```typescript
234
- import { LanguageRegistry, LanguageDefinition, createLanguageDefinition } from '@digitaldefiance/i18n-lib';
235
-
236
- const registry = new LanguageRegistry<'en-US' | 'fr' | 'es'>();
237
-
238
- // Register languages
239
- registry.registerLanguage(createLanguageDefinition('en-US', 'English (US)', 'en-US', true));
240
- registry.registerLanguage(createLanguageDefinition('fr', 'Français', 'fr'));
241
-
242
- // Query languages
243
- const language = registry.getLanguage('en-US');
244
- const byCode = registry.getLanguageByCode('fr');
245
- const allLanguages = registry.getAllLanguages();
246
- const displayNames = registry.getLanguageDisplayNames();
247
-
248
- // Get language codes for Mongoose enum
249
- const languageCodes = registry.getLanguageIds(); // ['en-US', 'fr', 'es']
250
- const isoCodes = registry.getLanguageCodes(); // ['en-US', 'fr', 'es']
251
-
252
- // Get matching language code with fallback logic
253
- const matchedCode = registry.getMatchingLanguageCode(
254
- 'de-DE', // Requested code (not registered)
255
- 'fr', // User default (registered)
256
- // Falls back to site default if neither match
257
- );
258
- // Returns: 'fr' (user default)
259
-
260
- const defaultCode = registry.getMatchingLanguageCode();
261
- // Returns: 'en-US' (site default)
262
- ```
263
-
264
- **Key Features:**
265
-
266
- - BCP 47 language code support
267
- - Language lookup by ID or ISO code
268
- - Display name mapping for UI rendering
269
- - Default language management
270
- - Duplicate detection and validation
271
- - Extract language codes for schema definitions
272
- - Intelligent language code matching with fallback chain
273
-
274
- ### Language Code Resolution
275
-
276
- The Language Registry provides intelligent language code matching with a fallback chain for handling user preferences and browser language headers:
277
-
278
- ```typescript
279
- import { LanguageRegistry } from '@digitaldefiance/i18n-lib';
280
-
281
- const registry = new LanguageRegistry<'en-US' | 'fr' | 'es'>();
282
- registry.registerLanguages([
283
- createLanguageDefinition('en-US', 'English (US)', 'en-US', true),
284
- createLanguageDefinition('fr', 'Français', 'fr'),
285
- createLanguageDefinition('es', 'Español', 'es'),
286
- ]);
287
-
288
- // Fallback chain: requested → user default → site default
289
- const languageCode = registry.getMatchingLanguageCode(
290
- req.headers['accept-language'], // 1. Try requested code first
291
- req.user?.siteLanguage, // 2. Fall back to user default
292
- // 3. Falls back to site default if neither match
293
- );
294
-
295
- // Example scenarios:
296
- registry.getMatchingLanguageCode('fr', 'es'); // Returns: 'fr' (requested exists)
297
- registry.getMatchingLanguageCode('de', 'es'); // Returns: 'es' (user default exists)
298
- registry.getMatchingLanguageCode('de', 'it'); // Returns: 'en-US' (site default)
299
- registry.getMatchingLanguageCode(); // Returns: 'en-US' (site default)
300
- registry.getMatchingLanguageCode('', ''); // Returns: 'en-US' (empty strings ignored)
301
- ```
302
-
303
- **Use Cases:**
304
-
305
- - HTTP Accept-Language header processing
306
- - User preference resolution
307
- - Browser language detection with fallback
308
- - Multi-tenant applications with per-user defaults
309
-
310
- **Error Handling:**
311
-
312
- ```typescript
313
- try {
314
- const emptyRegistry = new LanguageRegistry();
315
- emptyRegistry.getMatchingLanguageCode('en-US');
316
- } catch (error) {
317
- // Throws RegistryError if no default language configured
318
- console.error('No default language configured');
319
- }
320
- ```
321
-
322
- ### Enum Translation Registry
323
-
324
- Translates enum values with type safety:
325
-
326
- ```typescript
327
- import { EnumTranslationRegistry } from '@digitaldefiance/i18n-lib';
328
-
329
- enum Status {
330
- Active = 'active',
331
- Inactive = 'inactive'
332
- }
333
-
334
- const enumRegistry = new EnumTranslationRegistry<string, 'en-US' | 'fr'>(
335
- ['en-US', 'fr'],
336
- (key, vars) => engine.translate('core', key, vars)
337
- );
338
-
339
- enumRegistry.register(Status, {
340
- 'en-US': {
341
- [Status.Active]: 'Active',
342
- [Status.Inactive]: 'Inactive'
343
- },
344
- 'fr': {
345
- [Status.Active]: 'Actif',
346
- [Status.Inactive]: 'Inactif'
347
- }
348
- }, 'Status');
349
-
350
- const translated = enumRegistry.translate(Status, Status.Active, 'fr'); // 'Actif'
351
- ```
352
-
353
- **Key Features:**
354
-
355
- - Complete enum coverage validation
356
- - Numeric and string enum support
357
- - Automatic key resolution for numeric enums
358
- - Localized error messages
359
-
360
- ### Global Active Context
361
-
362
- Centralized context management for all engine instances:
363
-
364
- ```typescript
365
- import { GlobalActiveContext, IActiveContext } from '@digitaldefiance/i18n-lib';
366
-
367
- const globalContext = GlobalActiveContext.getInstance<'en-US' | 'fr', IActiveContext<'en-US' | 'fr'>>();
368
-
369
- // Create context for an instance
370
- globalContext.createContext('en-US', 'en-US', 'my-app');
371
-
372
- // Update context properties
373
- globalContext.setUserLanguage('fr', 'my-app');
374
- globalContext.setAdminLanguage('en-US', 'my-app');
375
- globalContext.setCurrencyCode(new CurrencyCode('EUR'), 'my-app');
376
- globalContext.setUserTimezone(new Timezone('Europe/Paris'), 'my-app');
377
-
378
- // Get context
379
- const context = globalContext.getContext('my-app');
380
- console.log(context.language); // 'fr'
381
- console.log(context.adminLanguage); // 'en-US'
382
- ```
383
-
384
- **Key Features:**
385
-
386
- - Per-instance context isolation
387
- - User and admin language separation
388
- - Currency code management
389
- - Timezone handling
390
- - Context space management (admin, user, system, api)
391
-
392
- ### Context Manager
393
-
394
- Reactive context with change listeners:
395
-
396
- ```typescript
397
- import { ContextManager } from '@digitaldefiance/i18n-lib';
398
-
399
- interface AppContext {
400
- language: string;
401
- theme: string;
402
- }
403
-
404
- const manager = new ContextManager<AppContext>();
405
-
406
- // Add change listener
407
- manager.addListener((property, oldValue, newValue) => {
408
- console.log(`${property} changed from ${oldValue} to ${newValue}`);
409
- });
410
-
411
- // Create reactive proxy
412
- const context = { language: 'en', theme: 'dark' };
413
- const reactiveContext = manager.createProxy(context);
414
-
415
- // Changes trigger listeners
416
- reactiveContext.language = 'fr'; // Logs: "language changed from en to fr"
417
- ```
418
-
419
- **Key Features:**
420
-
421
- - Property-level change notifications
422
- - Multiple listener support
423
- - Error-safe listener execution
424
- - Proxy-based reactivity
425
-
426
- ## 🆕 Plugin-Based Architecture (New in v1.1.0)
427
-
428
- The new plugin-based architecture provides a component registration system with rigid compile-time type safety.
429
-
430
- ### Quick Start with Plugin Architecture
431
-
432
- ```typescript
433
- import {
434
- createCoreI18nEngine,
435
- CoreStringKey,
436
- CoreLanguageCode,
437
- LanguageCodes,
438
- getCoreLanguageCodes,
439
- ComponentDefinition,
440
- ComponentRegistration
441
- } from '@digitaldefiance/i18n-lib';
442
-
443
- // Create engine with default languages and core strings
444
- const i18n = createCoreI18nEngine('myapp');
445
-
446
- // Type-safe language parameter
447
- const lang: CoreLanguageCode = LanguageCodes.FR; // Type-checked!
448
-
449
- // Get supported language codes from registry (runtime)
450
- const supportedLanguages = i18n.getLanguages().map((lang) => lang.id);
451
- // ['en-US', 'en-GB', 'fr', 'es', 'de', 'zh-CN', 'ja', 'uk']
452
-
453
- // Use core translations with type safety
454
- const welcomeMessage = i18n.translate('core', CoreStringKey.System_Welcome, undefined, lang);
455
- const errorMessage = i18n.translate('core', CoreStringKey.Error_ValidationFailed);
456
-
457
- // Define your own component with type safety
458
- enum MyComponentStringKey {
459
- Welcome = 'welcome',
460
- Goodbye = 'goodbye',
461
- UserGreetingTemplate = 'userGreetingTemplate'
462
- }
463
-
464
- const MyComponent: ComponentDefinition<MyComponentStringKey> = {
465
- id: 'my-component',
466
- name: 'My Custom Component',
467
- stringKeys: Object.values(MyComponentStringKey)
468
- };
469
-
470
- // Define translations for all supported languages
471
- const myComponentStrings = {
472
- [LanguageCodes.EN_US]: {
473
- [MyComponentStringKey.Welcome]: 'Welcome to my component!',
474
- [MyComponentStringKey.Goodbye]: 'Goodbye from my component!',
475
- [MyComponentStringKey.UserGreetingTemplate]: 'Hello, {name}!'
476
- },
477
- [LanguageCodes.FR]: {
478
- [MyComponentStringKey.Welcome]: 'Bienvenue dans mon composant !',
479
- [MyComponentStringKey.Goodbye]: 'Au revoir de mon composant !',
480
- [MyComponentStringKey.UserGreetingTemplate]: 'Bonjour, {name} !'
481
- },
482
- [LanguageCodes.ES]: {
483
- [MyComponentStringKey.Welcome]: '¡Bienvenido a mi componente!',
484
- [MyComponentStringKey.Goodbye]: '¡Adiós desde mi componente!',
485
- [MyComponentStringKey.UserGreetingTemplate]: '¡Hola, {name}!'
486
- }
487
- // TypeScript ensures all language codes are handled
488
- };
489
-
490
- // Register component (with validation)
491
- const registration: ComponentRegistration<MyComponentStringKey, CoreLanguageCode> = {
492
- component: MyComponent,
493
- strings: myComponentStrings
494
- };
495
-
496
- const validationResult = i18n.registerComponent(registration);
497
- if (!validationResult.isValid) {
498
- console.warn('Missing translations:', validationResult.missingKeys);
499
- }
500
-
501
- // Use your component's translations
502
- const welcome = i18n.translate('my-component', MyComponentStringKey.Welcome);
503
- const greeting = i18n.translate('my-component', MyComponentStringKey.UserGreetingTemplate, {
504
- name: 'John'
505
- });
506
-
507
- // Change language - affects all components
508
- i18n.setLanguage(LanguageCodes.FR);
509
- const frenchWelcome = i18n.translate('my-component', MyComponentStringKey.Welcome);
510
- // "Bienvenue dans mon composant !"
511
- ```
512
-
513
- ### Key Plugin Architecture Benefits
514
-
515
- 1. **Compile-Time Type Safety**: TypeScript ensures all string keys exist for all languages
516
- 2. **Component Isolation**: Each component manages its own strings independently
517
- 3. **Flexible Language Support**: Components can support different subsets of system languages
518
- 4. **Comprehensive Validation**: Automatic detection of missing translations with detailed reporting
519
- 5. **Fallback System**: Intelligent fallback to default language when translations are missing
520
- 6. **Dynamic Language Addition**: Add new languages at runtime with automatic validation updates
521
- 7. **Extensibility**: Easy to add new languages and components dynamically
522
-
523
- ### Pre-built Components
524
-
525
- The library includes several pre-built components:
526
-
527
- #### Core I18n Component
528
-
529
- Provides essential system strings in 8 languages:
530
-
531
- - English (US/UK), French, Spanish, German, Chinese (Simplified), Japanese, Ukrainian
532
-
533
- **Language Codes**: Use `LanguageCodes` constants or define your own:
534
-
535
- ```typescript
536
- import { LanguageCodes } from '@digitaldefiance/i18n-lib';
537
-
538
- // Common codes provided
539
- LanguageCodes.EN_US // 'en-US'
540
- LanguageCodes.FR // 'fr'
541
- LanguageCodes.ES // 'es'
542
- // ... or use any string: 'custom-lang'
543
- ```
544
-
545
- ```typescript
546
- import { createCoreI18nEngine, CoreStringKey } from '@digitaldefiance/i18n-lib';
547
-
548
- const i18n = createCoreI18nEngine();
549
- const saveText = i18n.translate('core', CoreStringKey.Common_Save);
550
- const errorMsg = i18n.translate('core', CoreStringKey.Error_ValidationFailed);
551
- ```
552
-
553
- #### Custom Component Example
554
-
555
- Create your own component with translations:
556
-
557
- ```typescript
558
- import {
559
- ComponentDefinition,
560
- ComponentRegistration,
561
- LanguageCodes
562
- } from '@digitaldefiance/i18n-lib';
563
-
564
- enum UserStringKey {
565
- Auth_Login = 'auth_login',
566
- Error_UserNotFoundTemplate = 'error_user_not_found_template'
567
- }
568
-
569
- const userComponent: ComponentDefinition<UserStringKey> = {
570
- id: 'user-system',
571
- name: 'User System',
572
- stringKeys: Object.values(UserStringKey)
573
- };
574
-
575
- const registration: ComponentRegistration<UserStringKey, CoreLanguageCode> = {
576
- component: userComponent,
577
- strings: {
578
- [LanguageCodes.EN_US]: {
579
- [UserStringKey.Auth_Login]: 'Login',
580
- [UserStringKey.Error_UserNotFoundTemplate]: 'User "{username}" not found'
581
- }
582
- }
583
- };
584
-
585
- i18n.registerComponent(registration);
586
-
587
- // Use translations
588
- const loginText = i18n.translate('user-system', UserStringKey.Auth_Login);
589
- const userNotFound = i18n.translate(
590
- 'user-system',
591
- UserStringKey.Error_UserNotFoundTemplate,
592
- { username: 'john_doe' }
593
- );
594
- ```
595
-
596
- ## Advanced Features
597
-
598
- ### Translation Adapters
599
-
600
- Create adapters to use PluginI18nEngine with components expecting the TranslationEngine interface:
601
-
602
- ```typescript
603
- import { createTranslationAdapter, PluginI18nEngine, TranslationEngine } from '@digitaldefiance/i18n-lib';
604
-
605
- const pluginEngine = PluginI18nEngine.getInstance<'en-US' | 'fr'>();
606
-
607
- // Create adapter for a specific component
608
- const adapter: TranslationEngine<MyStringKey> = createTranslationAdapter(
609
- pluginEngine,
610
- 'my-component'
611
- );
612
-
613
- // Use with error classes or other components
614
- class MyError extends Error {
615
- constructor(
616
- type: ErrorType,
617
- engine: TranslationEngine<ErrorStringKey>
618
- ) {
619
- const message = engine.translate(type);
620
- super(message);
621
- }
622
- }
623
-
624
- const error = new MyError(ErrorType.NotFound, adapter);
625
- ```
626
-
627
- **Key Features:**
628
-
629
- - Zero-overhead delegation to PluginI18nEngine
630
- - Maintains full type safety
631
- - Graceful error handling with fallback to key strings
632
- - Seamless integration with existing code
633
-
634
- ### Translatable Errors
635
-
636
- Generic error class with automatic translation:
637
-
638
- ```typescript
639
- import { TranslatableGenericError, PluginI18nEngine } from '@digitaldefiance/i18n-lib';
640
-
641
- enum UserErrorKey {
642
- UserNotFound = 'userNotFound',
643
- InvalidCredentials = 'invalidCredentials'
644
- }
645
-
646
- // Throw translatable error
647
- throw new TranslatableGenericError(
648
- 'user-errors',
649
- UserErrorKey.UserNotFound,
650
- { username: 'john_doe' },
651
- 'en-US',
652
- { userId: 123 },
653
- 'myapp'
654
- );
655
-
656
- // Create with explicit engine
657
- const engine = PluginI18nEngine.getInstance<'en-US' | 'fr'>();
658
- const error = TranslatableGenericError.withEngine(
659
- engine,
660
- 'user-errors',
661
- UserErrorKey.InvalidCredentials,
662
- undefined,
663
- 'fr'
664
- );
665
-
666
- // Retranslate dynamically
667
- try {
668
- // ... code that throws TranslatableGenericError
669
- } catch (error) {
670
- if (error instanceof TranslatableGenericError) {
671
- const localizedMessage = error.retranslate('fr', 'myapp');
672
- sendToUser(localizedMessage);
673
- }
674
- }
675
- ```
676
-
677
- **Key Features:**
678
-
679
- - Works with any registered component
680
- - Uses safeTranslate for consistent fallback behavior
681
- - Stores error context for retranslation
682
- - Never throws during construction
683
- - Metadata attachment for debugging
684
-
685
- ### Typed Errors
686
-
687
- Base classes for creating strongly-typed error hierarchies:
688
-
689
- ```typescript
690
- import { BaseTypedError, CompleteReasonMap, TranslationEngine } from '@digitaldefiance/i18n-lib';
691
-
692
- enum DatabaseErrorType {
693
- ConnectionFailed = 'connectionFailed',
694
- QueryTimeout = 'queryTimeout'
695
- }
696
-
697
- enum DatabaseErrorKey {
698
- ConnectionFailedMessage = 'connectionFailedMessage',
699
- QueryTimeoutMessage = 'queryTimeoutMessage'
700
- }
701
-
702
- const reasonMap: CompleteReasonMap<typeof DatabaseErrorType, DatabaseErrorKey> = {
703
- [DatabaseErrorType.ConnectionFailed]: DatabaseErrorKey.ConnectionFailedMessage,
704
- [DatabaseErrorType.QueryTimeout]: DatabaseErrorKey.QueryTimeoutMessage
705
- };
706
-
707
- class DatabaseError extends BaseTypedError<typeof DatabaseErrorType, DatabaseErrorKey> {
708
- static create(
709
- engine: TranslationEngine<DatabaseErrorKey>,
710
- type: DatabaseErrorType,
711
- metadata?: Record<string, any>
712
- ): DatabaseError {
713
- return DatabaseError.createTranslated(
714
- engine,
715
- 'database',
716
- type,
717
- reasonMap,
718
- undefined,
719
- undefined,
720
- metadata
721
- );
722
- }
723
- }
724
- ```
725
-
726
- **Key Features:**
727
-
728
- - Complete enum coverage enforcement
729
- - Translation engine integration
730
- - Simple and translated error creation
731
- - Metadata support
732
-
733
- ### Template Processing
734
-
735
- Advanced template system with multiple pattern types:
736
-
737
- ```typescript
738
- import { PluginI18nEngine } from '@digitaldefiance/i18n-lib';
739
-
740
- const engine = PluginI18nEngine.getInstance<'en-US'>();
741
-
742
- // Component-based patterns
743
- const message1 = engine.t(
744
- '{{core.Common_Welcome}} {{user.UserGreetingTemplate}}',
745
- 'en-US',
746
- { name: 'John' }
747
- );
748
- // "Welcome Hello, John!"
749
-
750
- // Variable replacement
751
- const message2 = engine.t(
752
- 'User {username} has {count} messages',
753
- 'en-US',
754
- { username: 'john_doe', count: 5 }
755
- );
756
- // "User john_doe has 5 messages"
757
-
758
- // Mixed patterns
759
- const message3 = engine.t(
760
- '{{core.System_Welcome}}, {name}! {{core.System_PleaseWait}}',
761
- 'en-US',
762
- { name: 'Alice' }
763
- );
764
- // "Welcome, Alice! Please wait..."
765
- ```
766
-
767
- **Pattern Types:**
768
-
769
- - `{{componentId.stringKey}}`: Component-based translation
770
- - `{variableName}`: Variable replacement
771
- - Template strings automatically use first variable object
772
- - Multiple variable objects merged for replacement
773
-
774
- ### Constants Support
775
-
776
- Automatically replace constants in template strings:
777
-
778
- ```typescript
779
- import { PluginI18nEngine } from '@digitaldefiance/i18n-lib';
780
-
781
- const constants = {
782
- Site: 'MyApp.com',
783
- Version: '2.0',
784
- SupportEmail: 'support@myapp.com'
785
- };
786
-
787
- const engine = new PluginI18nEngine(languages, { constants });
788
-
789
- const component: ComponentDefinition<'welcomeTemplate'> = {
790
- id: 'app',
791
- name: 'Application',
792
- stringKeys: ['welcomeTemplate'],
793
- };
794
-
795
- const registration: ComponentRegistration<'welcomeTemplate', 'en'> = {
796
- component,
797
- strings: {
798
- en: { welcomeTemplate: 'Welcome to {Site} v{Version}' },
799
- },
800
- };
801
-
802
- engine.registerComponent(registration);
803
- const message = engine.translate('app', 'welcomeTemplate');
804
- // "Welcome to MyApp.com v2.0"
805
-
806
- // Variables override constants
807
- const custom = engine.translate('app', 'welcomeTemplate', { Site: 'CustomSite.com' });
808
- // "Welcome to CustomSite.com v2.0"
809
- ```
810
-
811
- **Key Features:**
812
-
813
- - Automatic constant replacement in template strings
814
- - Variables take precedence over constants
815
- - Shared constants across all components
816
- - No need to pass constants on every translate call
817
-
818
- ### Currency Formatting
819
-
820
- Locale-aware currency formatting:
821
-
822
- ```typescript
823
- import { getCurrencyFormat, CurrencyCode } from '@digitaldefiance/i18n-lib';
824
-
825
- // Get format details
826
- const usdFormat = getCurrencyFormat('en-US', 'USD');
827
- console.log(usdFormat);
828
- // {
829
- // symbol: '$',
830
- // position: 'prefix',
831
- // groupSeparator: ',',
832
- // decimalSeparator: '.'
833
- // }
834
-
835
- const eurFormat = getCurrencyFormat('de-DE', 'EUR');
836
- console.log(eurFormat);
837
- // {
838
- // symbol: '€',
839
- // position: 'postfix',
840
- // groupSeparator: '.',
841
- // decimalSeparator: ','
842
- // }
843
-
844
- // Validate currency codes
845
- const currencyCode = new CurrencyCode('USD');
846
- console.log(currencyCode.value); // 'USD'
847
- console.log(CurrencyCode.values); // Array of all valid ISO 4217 codes
848
- ```
849
-
850
- **Key Features:**
851
-
852
- - ISO 4217 currency code validation
853
- - Locale-aware symbol positioning
854
- - Group and decimal separator detection
855
- - Intl.NumberFormat integration
856
-
857
- ### Timezone Handling
858
-
859
- Validated timezone management:
860
-
861
- ```typescript
862
- import { Timezone, isValidTimezone } from '@digitaldefiance/i18n-lib';
863
-
864
- // Create validated timezone
865
- const tz = new Timezone('America/New_York');
866
- console.log(tz.value); // 'America/New_York'
867
-
868
- // Validate timezone strings
869
- if (isValidTimezone('Europe/Paris')) {
870
- const parisTz = new Timezone('Europe/Paris');
871
- }
872
-
873
- // Invalid timezone throws error
874
- try {
875
- new Timezone('Invalid/Timezone');
876
- } catch (error) {
877
- console.error('Invalid timezone');
878
- }
879
- ```
880
-
881
- **Key Features:**
882
-
883
- - Moment-timezone validation
884
- - Immutable timezone values
885
- - Validation utilities
886
- - IANA timezone database support
887
-
888
- ### Utility Functions
889
-
890
- Helper functions for common operations:
891
-
892
- ```typescript
893
- import {
894
- replaceVariables,
895
- isTemplate,
896
- toStringKey,
897
- buildReasonMap,
898
- validateReasonMap,
899
- createCompleteReasonMap
900
- } from '@digitaldefiance/i18n-lib';
901
-
902
- // Variable replacement
903
- const result = replaceVariables(
904
- 'Hello, {name}! You have {count} messages.',
905
- { name: 'John', count: 5 }
906
- );
907
- // "Hello, John! You have 5 messages."
908
-
909
- // Template detection
910
- if (isTemplate('userGreetingTemplate')) {
911
- // Handle as template
912
- }
913
-
914
- // String key construction
915
- const key = toStringKey('error', 'validation', 'failed');
916
- // 'error_validation_failed'
917
-
918
- // Reason map building
919
- enum ErrorType {
920
- NotFound = 'notFound',
921
- AccessDenied = 'accessDenied'
922
- }
923
-
924
- const reasonMap = buildReasonMap(ErrorType, ['error']);
925
- // {
926
- // notFound: 'error_notFound',
927
- // accessDenied: 'error_accessDenied'
928
- // }
929
-
930
- // Validate reason map completeness
931
- if (validateReasonMap(ErrorType, reasonMap)) {
932
- // All enum values are mapped
933
- }
934
- ```
935
-
936
- ### Advanced Plugin Usage
937
-
938
- #### Compile-Time Completeness Enforcement (Strict Mode)
939
-
940
- By default the plugin engine performs runtime validation and provides fallbacks. If you want **compile-time** enforcement that every language mapping contains every string key, use the helper in `strict-types`:
941
-
942
- ```typescript
943
- import { createCompleteComponentStrings } from '@digitaldefiance/i18n-lib';
944
-
945
- enum MyStrings {
946
- Welcome = 'welcome',
947
- Farewell = 'farewell'
948
- }
949
-
950
- type AppLang = 'en' | 'fr';
951
-
952
- // This will only compile if BOTH languages contain BOTH keys.
953
- const myStrictStrings = createCompleteComponentStrings<MyStrings, AppLang>({
954
- en: {
955
- [MyStrings.Welcome]: 'Welcome',
956
- [MyStrings.Farewell]: 'Goodbye'
957
- },
958
- fr: {
959
- [MyStrings.Welcome]: 'Bienvenue',
960
- [MyStrings.Farewell]: 'Au revoir'
961
- }
962
- });
963
-
964
- // If any key is missing, TypeScript reports an error before runtime.
965
- ```
966
-
967
- The core library itself uses this helper for the core component (`createCoreComponentStrings`) to guarantee internal completeness. For partial / iterative authoring you can still start with normal objects and later switch to the strict helper when translations stabilize.
968
-
969
- #### Adding New Languages
970
-
971
- ```typescript
972
- import { createLanguageDefinition } from '@digitaldefiance/i18n-lib';
973
-
974
- // Add Italian support
975
- const italian = createLanguageDefinition('it', 'Italiano', 'it');
976
- i18n.registerLanguage(italian);
977
-
978
- // Update existing components with Italian translations
979
- i18n.updateComponentStrings('my-component', {
980
- it: {
981
- [MyComponentStringKey.Welcome]: 'Benvenuto nel mio componente!',
982
- [MyComponentStringKey.Goodbye]: 'Arrivederci dal mio componente!',
983
- [MyComponentStringKey.UserGreetingTemplate]: 'Ciao, {name}!'
984
- }
985
- });
986
- ```
987
-
988
- #### Component Registration Validation
989
-
990
- The plugin engine provides comprehensive validation to ensure translation completeness:
991
-
992
- ```typescript
993
- // Each component is validated against ALL system languages
994
- enum MyStrings {
995
- Welcome = 'welcome',
996
- Goodbye = 'goodbye'
997
- }
998
-
999
- const myComponent: ComponentDefinition<MyStrings> = {
1000
- id: 'my-component',
1001
- name: 'My Component',
1002
- stringKeys: Object.values(MyStrings)
1003
- };
1004
-
1005
- // System has EN, FR, ES languages - component must provide translations for all three
1006
- const registration: ComponentRegistration<MyStrings, CoreLanguageCode> = {
1007
- component: myComponent,
1008
- strings: {
1009
- [LanguageCodes.EN_US]: {
1010
- [MyStrings.Welcome]: 'Welcome',
1011
- [MyStrings.Goodbye]: 'Goodbye'
1012
- },
1013
- [LanguageCodes.FR]: {
1014
- [MyStrings.Welcome]: 'Bienvenue',
1015
- [MyStrings.Goodbye]: 'Au revoir'
1016
- },
1017
- [LanguageCodes.ES]: {
1018
- [MyStrings.Welcome]: 'Bienvenido',
1019
- [MyStrings.Goodbye]: 'Adiós'
1020
- }
1021
- }
1022
- };
1023
-
1024
- const result = i18n.registerComponent(registration);
1025
- if (!result.isValid) {
1026
- console.log('Missing translations:', result.missingKeys);
1027
- // Shows exactly which string keys are missing for which languages
1028
- }
1029
- ```
1030
-
1031
- #### Flexible Language Support
1032
-
1033
- Components can support different subsets of system languages:
1034
-
1035
- ```typescript
1036
- // Component A supports EN, FR, ES
1037
- const componentA = {
1038
- component: { id: 'comp-a', name: 'Component A', stringKeys: ['hello'] },
1039
- strings: {
1040
- 'en-US': { hello: 'Hello' },
1041
- 'fr': { hello: 'Bonjour' },
1042
- 'es': { hello: 'Hola' }
1043
- }
1044
- };
1045
-
1046
- // Component B only supports EN and DE (added later)
1047
- const componentB = {
1048
- component: { id: 'comp-b', name: 'Component B', stringKeys: ['save'] },
1049
- strings: {
1050
- 'en-US': { save: 'Save' },
1051
- 'de': { save: 'Speichern' }
1052
- }
1053
- };
1054
-
1055
- // Both components can coexist - missing translations use fallback
1056
- i18n.registerComponent(componentA); // ✓ Complete
1057
- i18n.registerComponent(componentB); // ⚠ Missing FR, ES - uses fallback
1058
-
1059
- // Usage automatically handles fallbacks
1060
- i18n.translate('comp-b', 'save', {}, 'fr'); // Returns 'Save' (en-US fallback)
1061
- ```
1062
-
1063
- #### Dynamic Language Addition
1064
-
1065
- ```typescript
1066
- import { createLanguageDefinition } from '@digitaldefiance/i18n-lib';
1067
-
1068
- // Add new language to system
1069
- const germanLang = createLanguageDefinition('de', 'Deutsch', 'de');
1070
- i18n.registerLanguage(germanLang);
1071
-
1072
- // New component registrations now require German translations
1073
- const newRegistration = {
1074
- component: { id: 'new-comp', name: 'New Component', stringKeys: ['test'] },
1075
- strings: {
1076
- 'en-US': { test: 'Test' },
1077
- 'fr': { test: 'Test' },
1078
- 'es': { test: 'Prueba' }
1079
- // Missing 'de' - validation will flag this
1080
- }
1081
- };
1082
-
1083
- const result = i18n.registerComponent(newRegistration);
1084
- console.log(result.missingKeys); // Shows missing German translations
1085
- ```
1086
-
1087
- #### Validation and Error Handling
1088
-
1089
- ```typescript
1090
- // Comprehensive validation
1091
- const globalValidation = i18n.validateAllComponents();
1092
- if (!globalValidation.isValid) {
1093
- console.error('Validation errors:', globalValidation.errors);
1094
- console.warn('Warnings:', globalValidation.warnings);
1095
- }
1096
-
1097
- // Handle registration errors
1098
- try {
1099
- i18n.registerComponent(incompleteRegistration);
1100
- } catch (error) {
1101
- if (error instanceof RegistryError) {
1102
- console.error(`Registry error: ${error.type}`, error.metadata);
1103
- }
1104
- }
1105
-
1106
- // Strict validation mode (rejects incomplete registrations)
1107
- const strictEngine = new PluginI18nEngine(languages, {
1108
- validation: {
1109
- requireCompleteStrings: true,
1110
- allowPartialRegistration: false,
1111
- fallbackLanguageId: 'en-US'
1112
- }
1113
- });
1114
-
1115
- // Optional instance registration overrides (third constructor argument)
1116
- const reportingEngine = new PluginI18nEngine(languages, {}, {
1117
- instanceKey: 'reporting',
1118
- registerInstance: false,
1119
- setAsDefault: false
1120
- });
1121
- ```
1122
-
1123
- #### Multi-Instance Support
1124
-
1125
- ```typescript
1126
- // Create separate instances for different contexts
1127
- const adminI18n = PluginI18nEngine.createInstance('admin', languages);
1128
- const userI18n = PluginI18nEngine.createInstance('user', languages);
1129
-
1130
- // Register different components for each
1131
- adminI18n.registerComponent(adminComponentRegistration);
1132
- userI18n.registerComponent(userComponentRegistration);
1133
-
1134
- // Create an isolated instance without touching the global registry (handy for tests)
1135
- const detachedI18n = new PluginI18nEngine(languages, {}, { registerInstance: false });
1136
- detachedI18n.registerComponent(myTestRegistration);
1137
- ```
1138
-
1139
- For complete documentation on the plugin architecture, see [PLUGIN_ARCHITECTURE.md](./PLUGIN_ARCHITECTURE.md).
1140
-
1141
- ## Advanced Features
1142
-
1143
- ### Translatable Errors
1144
-
1145
- The `TranslatableGenericError` class provides a simple way to create errors with translated messages that work across any component:
1146
-
1147
- ```typescript
1148
- import { TranslatableGenericError, CoreStringKey, LanguageCodes } from '@digitaldefiance/i18n-lib';
1149
-
1150
- // Define your error string keys
1151
- enum UserErrorKey {
1152
- UserNotFound = 'userNotFound',
1153
- InvalidCredentials = 'invalidCredentials',
1154
- AccountLocked = 'accountLocked',
1155
- }
1156
-
1157
- // Register your component with translations
1158
- const userErrorComponent = {
1159
- id: 'user-errors',
1160
- name: 'User Errors',
1161
- stringKeys: Object.values(UserErrorKey)
1162
- };
1163
-
1164
- const registration = {
1165
- component: userErrorComponent,
1166
- strings: {
1167
- 'en-US': {
1168
- [UserErrorKey.UserNotFound]: 'User "{username}" not found',
1169
- [UserErrorKey.InvalidCredentials]: 'Invalid credentials provided',
1170
- [UserErrorKey.AccountLocked]: 'Account locked until {unlockTime}'
1171
- },
1172
- 'fr': {
1173
- [UserErrorKey.UserNotFound]: 'Utilisateur "{username}" introuvable',
1174
- [UserErrorKey.InvalidCredentials]: 'Identifiants invalides fournis',
1175
- [UserErrorKey.AccountLocked]: 'Compte verrouillé jusqu\'à {unlockTime}'
1176
- }
1177
- }
1178
- };
1179
-
1180
- i18n.registerComponent(registration);
1181
-
1182
- // Throw translatable errors
1183
- throw new TranslatableGenericError(
1184
- 'user-errors',
1185
- UserErrorKey.UserNotFound,
1186
- { username: 'john_doe' },
1187
- 'en-US',
1188
- { userId: 123 }, // metadata
1189
- 'myapp' // engine instance key
1190
- );
1191
-
1192
- // Use with explicit engine instance
1193
- const error = TranslatableGenericError.withEngine(
1194
- i18n,
1195
- 'user-errors',
1196
- UserErrorKey.InvalidCredentials,
1197
- undefined,
1198
- 'fr'
1199
- );
1200
-
1201
- // Retranslate errors dynamically
1202
- try {
1203
- // ... code that throws TranslatableGenericError
1204
- } catch (error) {
1205
- if (error instanceof TranslatableGenericError) {
1206
- const localizedMessage = error.retranslate(userLanguage, 'myapp');
1207
- sendToUser(localizedMessage);
1208
- }
1209
- }
1210
-
1211
- // Use with core strings
1212
- throw new TranslatableGenericError(
1213
- 'core',
1214
- CoreStringKey.Error_AccessDenied,
1215
- undefined,
1216
- LanguageCodes.EN_US,
1217
- { requestId: '12345' },
1218
- 'myapp'
1219
- );
1220
- ```
1221
-
1222
- **Key Features:**
1223
-
1224
- - Works with any registered component and string keys
1225
- - Uses `safeTranslate` for consistent fallback behavior (`[componentId.stringKey]`)
1226
- - Stores error context: stringKey, componentId, language, variables, metadata
1227
- - Supports dynamic retranslation with `retranslate()` method
1228
- - Never throws during construction - always returns a valid error
1229
- - Compatible with both constructor and static factory methods
1230
-
1231
- For complete documentation, see [TRANSLATABLE_ERROR_GUIDE.md](./TRANSLATABLE_ERROR_GUIDE.md).
1232
-
1233
- ### Enum Translation Registry
1234
-
1235
- ```typescript
1236
- enum Status {
1237
- Active = 'active',
1238
- Inactive = 'inactive',
1239
- Pending = 'pending'
1240
- }
1241
-
1242
- // Register enum translations (requires complete translations)
1243
- i18n.registerEnum(Status, {
1244
- [MyLanguages.English]: {
1245
- [Status.Active]: 'Active',
1246
- [Status.Inactive]: 'Inactive',
1247
- [Status.Pending]: 'Pending'
1248
- },
1249
- [MyLanguages.Spanish]: {
1250
- [Status.Active]: 'Activo',
1251
- [Status.Inactive]: 'Inactivo',
1252
- [Status.Pending]: 'Pendiente'
1253
- }
1254
- }, 'Status');
1255
-
1256
- // Translate enum values
1257
- const statusText = i18n.translateEnum(Status, Status.Active, MyLanguages.Spanish);
1258
- // "Activo"
1259
- ```
1260
-
1261
- ### Template Processing
1262
-
1263
- ```typescript
1264
- import { PluginI18nEngine } from '@digitaldefiance/i18n-lib';
1265
-
1266
- const engine = PluginI18nEngine.getInstance<'en-US' | 'es'>();
1267
-
1268
- // Component placeholders are resolved before variable replacement
1269
- const message = engine.t(
1270
- '{{core.System_Welcome}} {name}! {{core.System_PleaseWait}}',
1271
- 'en-US',
1272
- { name: 'John' }
1273
- );
1274
- // "Welcome John! Please wait..."
1275
-
1276
- // Templates ending with "Template" automatically receive the first var map
1277
- const greeting = engine.t(
1278
- '{{my-component.UserGreetingTemplate}}',
1279
- 'es',
1280
- { name: 'Laura' },
1281
- { fallbackName: 'Usuario' }
1282
- );
1283
- // "¡Hola, Laura!"
1284
-
1285
- // Additional variable objects are merged left→right
1286
- const merged = engine.t(
1287
- 'User {username} has {count} notifications',
1288
- 'en-US',
1289
- { username: 'alice' },
1290
- { count: 5 }
1291
- );
1292
- // "User alice has 5 notifications"
1293
- ```
1294
-
1295
- ### Context Management
1296
-
1297
- ```typescript
1298
- import { PluginI18nEngine, CurrencyCode, Timezone } from '@digitaldefiance/i18n-lib';
1299
-
1300
- const engine = PluginI18nEngine.getInstance<'en-US' | 'es'>();
1301
-
1302
- engine.updateContext({
1303
- language: 'es',
1304
- adminLanguage: 'en-US',
1305
- currencyCode: new CurrencyCode('EUR'),
1306
- timezone: new Timezone('Europe/Madrid'),
1307
- adminTimezone: new Timezone('UTC'),
1308
- });
1309
-
1310
- engine.updateContext({ currentContext: 'admin' });
1311
-
1312
- const context = engine.getContext();
1313
- console.log(context.currentContext); // 'admin'
1314
- console.log(context.language); // 'es'
1315
-
1316
- engine.updateContext({ currentContext: 'user' });
1317
- engine.setLanguage('en-US');
1318
- ```
1319
-
1320
- ### Context Change Monitoring
1321
-
1322
- ```typescript
1323
- import { ContextManager } from '@digitaldefiance/i18n-lib';
1324
-
1325
- interface AppContext {
1326
- language: string;
1327
- theme: string;
1328
- }
1329
-
1330
- const manager = new ContextManager<AppContext>();
1331
-
1332
- // Add listeners for context changes
1333
- manager.addListener((property, oldValue, newValue) => {
1334
- console.log(`${property} changed from ${oldValue} to ${newValue}`);
1335
- });
1336
-
1337
- // Create reactive context
1338
- const context = { language: 'en', theme: 'dark' };
1339
- const reactiveContext = manager.createProxy(context);
1340
-
1341
- // Changes are automatically detected
1342
- reactiveContext.language = 'es'; // Triggers listener
1343
- ```
1344
-
1345
- ### Currency Formatting
1346
-
1347
- ```typescript
1348
- import { getCurrencyFormat } from '@digitaldefiance/i18n-lib';
1349
-
1350
- // Get currency formatting information
1351
- const usdFormat = getCurrencyFormat('en-US', 'USD');
1352
- // { symbol: '$', position: 'prefix', groupSeparator: ',', decimalSeparator: '.' }
1353
-
1354
- const eurFormat = getCurrencyFormat('de-DE', 'EUR');
1355
- // { symbol: '€', position: 'postfix', groupSeparator: '.', decimalSeparator: ',' }
1356
- ```
1357
-
1358
- ### Instance Management
1359
-
1360
- ```typescript
1361
- import { PluginI18nEngine, LanguageDefinition } from '@digitaldefiance/i18n-lib';
1362
-
1363
- const languages: ReadonlyArray<LanguageDefinition> = [
1364
- { id: 'en-US', name: 'English (US)', code: 'en-US', isDefault: true },
1365
- { id: 'fr', name: 'Français', code: 'fr' },
1366
- ];
1367
-
1368
- // Create named instances
1369
- const mainI18n = PluginI18nEngine.createInstance('main', languages);
1370
- const adminI18n = PluginI18nEngine.createInstance('admin', languages);
1371
-
1372
- // Get instances by key
1373
- const instance = PluginI18nEngine.getInstance('main');
1374
-
1375
- // Clean up instances (useful for testing)
1376
- PluginI18nEngine.removeInstance('main');
1377
- PluginI18nEngine.resetAll();
1378
- ```
1379
-
1380
- ## Supported Languages
1381
-
1382
- The library includes pre-built translations for 8 languages in the core component:
1383
-
1384
- | Language Code | Display Name | ISO Code |
1385
- |--------------|--------------|----------|
1386
- | `en-US` | English (US) | en-US |
1387
- | `en-GB` | English (UK) | en-GB |
1388
- | `fr` | Français | fr |
1389
- | `es` | Español | es |
1390
- | `de` | Deutsch | de |
1391
- | `zh-CN` | 中文 (简体) | zh-CN |
1392
- | `ja` | 日本語 | ja |
1393
- | `uk` | Українська | uk |
1394
-
1395
- ### Language Code Constants
1396
-
1397
- ```typescript
1398
- import { LanguageCodes, LanguageDisplayNames, getCoreLanguageCodes } from '@digitaldefiance/i18n-lib';
1399
-
1400
- // Use constants for type safety
1401
- const lang = LanguageCodes.FR; // 'fr'
1402
- const displayName = LanguageDisplayNames[LanguageCodes.FR]; // 'Français'
1403
-
1404
- // Get all core language codes as array (for Mongoose enums, etc.)
1405
- const coreLanguageCodes = getCoreLanguageCodes();
1406
- // ['en-US', 'en-GB', 'fr', 'es', 'de', 'zh-CN', 'ja', 'uk']
1407
-
1408
- // All available codes
1409
- const codes = {
1410
- EN_US: 'en-US',
1411
- EN_GB: 'en-GB',
1412
- FR: 'fr',
1413
- ES: 'es',
1414
- DE: 'de',
1415
- ZH_CN: 'zh-CN',
1416
- JA: 'ja',
1417
- UK: 'uk'
1418
- };
1419
- ```
1420
-
1421
- ### Custom Language Codes
1422
-
1423
- Extend core languages with custom ones while maintaining type safety:
1424
-
1425
- ```typescript
1426
- import {
1427
- CoreLanguageCode,
1428
- getCoreLanguageDefinitions,
1429
- createLanguageDefinition,
1430
- PluginI18nEngine
1431
- } from '@digitaldefiance/i18n-lib';
1432
-
1433
- // Define custom language type extending core
1434
- type MyLanguageCode = CoreLanguageCode | 'pt-BR' | 'it';
1435
-
1436
- // Create engine with extended languages
1437
- const engine = PluginI18nEngine.createInstance<MyLanguageCode>(
1438
- 'myapp',
1439
- [
1440
- ...getCoreLanguageDefinitions(),
1441
- createLanguageDefinition('pt-BR', 'Português (Brasil)', 'pt-BR'),
1442
- createLanguageDefinition('it', 'Italiano', 'it')
1443
- ]
1444
- );
1445
-
1446
- // Type-safe language usage
1447
- const lang1: MyLanguageCode = 'en-US'; // ✓ Core language
1448
- const lang2: MyLanguageCode = 'pt-BR'; // ✓ Custom language
1449
- const lang3: MyLanguageCode = 'invalid'; // ✗ Type error!
1450
- ```
1451
-
1452
- ### Helper Functions for Mongoose Schemas
1453
-
1454
- Extract language codes from the registry (single source of truth):
1455
-
1456
- ```typescript
1457
- import { PluginI18nEngine, LanguageRegistry, getCoreLanguageCodes, LanguageCodes } from '@digitaldefiance/i18n-lib';
1458
- import { Schema } from 'mongoose';
1459
-
1460
- // Static approach: Get core language codes as array
1461
- const coreLanguageCodes = getCoreLanguageCodes();
1462
- // ['en-US', 'en-GB', 'fr', 'es', 'de', 'zh-CN', 'ja', 'uk']
1463
-
1464
- // Dynamic approach: Get from engine instance (includes custom languages)
1465
- const engine = PluginI18nEngine.getInstance<string>();
1466
- const languageDefinitions = engine.getLanguages();
1467
- const languageIds = languageDefinitions.map((lang) => lang.id);
1468
- const isoCodes = languageDefinitions.map((lang) => lang.code);
1469
-
1470
- // Use in Mongoose schema
1471
- const userSchema = new Schema({
1472
- language: {
1473
- type: String,
1474
- enum: coreLanguageCodes, // Static core languages
1475
- default: LanguageCodes.EN_US
1476
- },
1477
- adminLanguage: {
1478
- type: String,
1479
- enum: languageIds, // Dynamic from registry
1480
- default: LanguageCodes.EN_US
1481
- }
1482
- });
1483
-
1484
- // Get display names for validation messages
1485
- const displayNames = LanguageRegistry.getLanguageDisplayNames();
1486
- // { 'en-US': 'English (US)', 'fr': 'Français', ... }
1487
- ```
1488
-
1489
- ## Core String Keys
1490
-
1491
- The core component provides 40+ system strings organized by category:
1492
-
1493
- ### Common Strings
1494
-
1495
- - `Common_Yes`, `Common_No`, `Common_Cancel`, `Common_OK`
1496
- - `Common_Save`, `Common_Delete`, `Common_Edit`, `Common_Create`, `Common_Update`
1497
- - `Common_Loading`, `Common_Error`, `Common_Success`, `Common_Warning`, `Common_Info`
1498
- - `Common_Disposed`
1499
-
1500
- ### Error Messages
1501
-
1502
- - `Error_InvalidInput`, `Error_NetworkError`, `Error_NotFound`
1503
- - `Error_AccessDenied`, `Error_InternalServer`, `Error_ValidationFailed`
1504
- - `Error_RequiredField`, `Error_InvalidContextTemplate`
1505
- - `Error_MissingTranslationKeyTemplate`
1506
-
1507
- ### Registry Error Templates
1508
-
1509
- - `Error_ComponentNotFoundTemplate`
1510
- - `Error_LanguageNotFoundTemplate`
1511
- - `Error_StringKeyNotFoundTemplate`
1512
- - `Error_IncompleteRegistrationTemplate`
1513
- - `Error_DuplicateComponentTemplate`
1514
- - `Error_DuplicateLanguageTemplate`
1515
- - `Error_ValidationFailedTemplate`
1516
-
1517
- ### System Messages
1518
-
1519
- - `System_Welcome`, `System_Goodbye`, `System_PleaseWait`
1520
- - `System_ProcessingRequest`, `System_OperationComplete`
1521
- - `System_NoDataAvailable`
1522
-
1523
- ## API Reference
1524
-
1525
- ### Plugin Architecture API
1526
-
1527
- #### PluginI18nEngine
1528
-
1529
- **Constructor**
1530
-
1531
- - `new PluginI18nEngine<TLanguages>(languages, config?, options?)` - Create new plugin engine
1532
- - `PluginI18nEngine.createInstance<TLanguages>(key, languages, config?)` - Create named instance
1533
- - `PluginI18nEngine.getInstance<TLanguages>(key?)` - Get existing instance (throws error if not found)
1534
-
1535
- `options` enables fine-grained control over how the engine participates in the static registry:
1536
-
1537
- - `instanceKey` – Override the implicit registry key when instantiating directly (defaults to `'default'`).
1538
- - `registerInstance` – Set to `false` to keep the engine out of the shared instance map (useful for tests or short-lived contexts).
1539
- - `setAsDefault` – Force this instance to become the default lookup target even if another default exists.
1540
-
1541
- **Component Management**
1542
-
1543
- - `registerComponent<TStringKeys>(registration)` - Register component with translations
1544
- - `updateComponentStrings<TStringKeys>(componentId, strings)` - Update existing component strings
1545
- - `getComponents()` - Get all registered components
1546
- - `hasComponent(componentId)` - Check if component exists
1547
-
1548
- **Translation Methods**
1549
-
1550
- - `translate<TStringKeys>(componentId, stringKey, variables?, language?)` - Translate component string
1551
- - `safeTranslate(componentId, stringKey, variables?, language?)` - Safe translate with fallback
1552
- - `getTranslationDetails<TStringKeys>(componentId, stringKey, variables?, language?)` - Get detailed translation response
1553
-
1554
- **Language Management**
1555
-
1556
- - `registerLanguage(language)` - Register new language
1557
- - `registerLanguages(languages)` - Register multiple languages
1558
- - `getLanguages()` - Get all registered languages
1559
- - `hasLanguage(language)` - Check if language exists
1560
- - `setLanguage(language)` - Set current language
1561
- - `getLanguageByCode(code)` - Get language by ISO code
1562
- - `getMatchingLanguageCode(requestedCode?, userDefaultCode?)` - Get matching language code with fallback logic
1563
-
1564
- **Context & Validation**
1565
-
1566
- - `getContext()` - Read the active context (language, adminLanguage, currency, timezone)
1567
- - `updateContext(updates)` - Apply partial context updates
1568
- - `validateAllComponents()` - Validate all registered components
1569
- - `getComponentRegistry()` - Access component registry directly
1570
- - `getEnumRegistry()` - Access enum registry directly
1571
- - Use `LanguageRegistry` statics (e.g. `LanguageRegistry.getLanguageIds()`) for low-level language metadata
1572
-
1573
- #### Core I18n Functions
1574
-
1575
- - `createCoreI18nEngine(instanceKey?)` - Create engine with core components
1576
- - `getCoreTranslation(stringKey, variables?, language?, instanceKey?)` - Get core translation
1577
- - `safeCoreTranslation(stringKey, variables?, language?, instanceKey?)` - Safe core translation
1578
-
1579
- #### Component Registration Types
1580
-
1581
- ```typescript
1582
- interface ComponentDefinition<TStringKeys extends string> {
1583
- readonly id: string;
1584
- readonly name: string;
1585
- readonly stringKeys: readonly TStringKeys[];
1586
- }
1587
-
1588
- interface ComponentRegistration<TStringKeys extends string, TLanguages extends string> {
1589
- readonly component: ComponentDefinition<TStringKeys>;
1590
- readonly strings: PartialComponentLanguageStrings<TStringKeys, TLanguages>;
1591
- }
1592
-
1593
- interface LanguageDefinition {
1594
- readonly id: string;
1595
- readonly name: string;
1596
- readonly code: string;
1597
- readonly isDefault?: boolean;
1598
- }
1599
- ```
1600
-
1601
- ### Legacy I18nEngine (Still Supported)
1602
-
1603
- #### Constructor
1604
-
1605
- - `new I18nEngine<TStringKey, TLanguage>(config, key?)` - Create new engine instance
1606
-
1607
- #### Translation Methods
1608
-
1609
- - `translate(key, vars?, language?, fallbackLanguage?)` - Translate string with optional variables
1610
- - `translateEnum(enumObj, value, language)` - Translate enum value
1611
- - `t(templateString, language?, ...vars)` - Process template string with `{{EnumName.EnumKey}}` patterns
1612
-
1613
- #### Registration
1614
-
1615
- - `registerEnum(enumObj, translations, enumName)` - Register enum translations
1616
-
1617
- #### Language Management
1618
-
1619
- - `getLanguageCode(language)` - Get language code for language
1620
- - `getLanguageFromCode(code)` - Get language from code
1621
- - `getAllLanguageCodes()` - Get all language codes
1622
- - `getAvailableLanguages()` - Get available languages
1623
- - `isLanguageAvailable(language)` - Check if language is available
1624
-
1625
- #### Context Management
1626
-
1627
- - `get context()` - Get current context
1628
- - `set context(context)` - Set context properties
1629
-
1630
- #### Static Methods
1631
-
1632
- - `getInstance(key?)` - Get instance by key
1633
- - `clearInstances()` - Clear all instances
1634
- - `removeInstance(key?)` - Remove specific instance
1635
-
1636
- ### Context Utilities
1637
-
1638
- - `createContext(defaultLanguage, defaultContext)` - Create new context
1639
- - `setLanguage(context, language)` - Set user language
1640
- - `setAdminLanguage(context, language)` - Set admin language
1641
- - `setContext(context, contextType)` - Set context type
1642
-
1643
- ### ContextManager
1644
-
1645
- - `addListener(listener)` - Add change listener
1646
- - `removeListener(listener)` - Remove change listener
1647
- - `createProxy(context)` - Create reactive context proxy
1648
-
1649
- ### Currency Utilities
1650
-
1651
- - `getCurrencyFormat(locale, currencyCode)` - Get currency formatting info
1652
-
1653
- ### Type Utilities
1654
-
1655
- - `createTranslations(translations)` - Helper for creating typed translations
1656
-
1657
- ## Type Definitions
1658
-
1659
- ### Core Types
1660
-
1661
- ```typescript
1662
- type EnumTranslation<T extends string | number> = {
1663
- [K in T]: string;
1664
- };
1665
-
1666
- type EnumLanguageTranslation<T extends string | number, TLanguage extends string> = Partial<{
1667
- [L in TLanguage]: EnumTranslation<T>;
1668
- }>;
1669
-
1670
- interface I18nConfig<TStringKey, TLanguage, TConstants?, TTranslationContext?> {
1671
- stringNames: TStringKey[];
1672
- strings: MasterStringsCollection<TStringKey, TLanguage>;
1673
- defaultLanguage: TLanguage;
1674
- defaultTranslationContext: TTranslationContext;
1675
- defaultCurrencyCode: CurrencyCode;
1676
- languageCodes: LanguageCodeCollection<TLanguage>;
1677
- languages: TLanguage[];
1678
- constants?: TConstants;
1679
- enumName?: string;
1680
- enumObj?: Record<string, TStringKey>;
1681
- timezone: Timezone;
1682
- adminTimezone: Timezone;
1683
- }
1684
- ```
1685
-
1686
- ## Testing
1687
-
1688
- The library includes comprehensive test coverage:
1689
-
1690
- ```bash
1691
- # Run tests
1692
- yarn test
1693
-
1694
- # Run specific test suites
1695
- yarn test context-manager.spec.ts
1696
- yarn test enum-registry.spec.ts
1697
- yarn test i18n-engine.spec.ts
1698
- ```
1699
-
1700
- ### Test Cleanup and Instance Management
1701
-
1702
- For proper test isolation when using the plugin-based architecture, use the cleanup utilities:
1703
-
1704
- ```typescript
1705
- import { PluginI18nEngine, resetAllI18nEngines } from '@digitaldefiance/i18n-lib';
1706
-
1707
- describe('My tests', () => {
1708
- beforeEach(() => {
1709
- // Clean up any existing instances before each test
1710
- PluginI18nEngine.clearAllInstances();
1711
- });
1712
-
1713
- afterEach(() => {
1714
- // Or use the convenience function
1715
- resetAllI18nEngines();
1716
- });
1717
-
1718
- // Or use specific cleanup methods
1719
- it('should manage instances', () => {
1720
- const engine1 = PluginI18nEngine.createInstance('app1', [englishLang]);
1721
- const engine2 = PluginI18nEngine.createInstance('app2', [frenchLang]);
5
+ ## Features
1722
6
 
1723
- // Check if instances exist
1724
- expect(PluginI18nEngine.hasInstance('app1')).toBe(true);
1725
- expect(PluginI18nEngine.hasInstance('app2')).toBe(true);
7
+ - **Component-Based Architecture**: Register translation components with full type safety
8
+ - **8 Built-in Languages**: English (US/UK), French, Spanish, German, Chinese, Japanese, Ukrainian
9
+ - **Template Processing**: Support for variable substitution and component references
10
+ - **Multiple Instances**: Create isolated i18n engines for different contexts
11
+ - **Core System Strings**: Pre-built translations for common UI elements and errors
12
+ - **Type Safety**: Full TypeScript support with generic types
13
+ - **Error Handling**: Comprehensive error classes with translation support
14
+ - **Zero Dependencies**: Lightweight with no external runtime dependencies
1726
15
 
1727
- // Remove specific instance
1728
- PluginI18nEngine.removeInstance('app1');
1729
- expect(PluginI18nEngine.hasInstance('app1')).toBe(false);
16
+ ## Installation
1730
17
 
1731
- // Clear all instances and component registrations
1732
- PluginI18nEngine.resetAll();
1733
- expect(PluginI18nEngine.hasInstance('app2')).toBe(false);
1734
- });
1735
- });
18
+ ```bash
19
+ npm install @digitaldefiance/i18n-lib
20
+ # or
21
+ yarn add @digitaldefiance/i18n-lib
1736
22
  ```
1737
23
 
1738
- #### Available Cleanup Methods
1739
-
1740
- - `PluginI18nEngine.clearAllInstances()` - Remove all engine instances
1741
- - `PluginI18nEngine.removeInstance(key?)` - Remove specific instance by key (returns boolean)
1742
- - `PluginI18nEngine.hasInstance(key?)` - Check if instance exists (returns boolean)
1743
- - `PluginI18nEngine.getInstance(key?)` - Get existing instance (throws RegistryError if not found)
1744
- - `PluginI18nEngine.resetAll()` - Clear instances and component registrations
1745
- - `resetAllI18nEngines()` - Convenience function that calls `resetAll()`
1746
-
1747
- ## Extensible Configuration
1748
-
1749
- The library supports layered extension across multiple libraries using TypeScript module augmentation:
1750
-
1751
- ### Base Library Setup
24
+ ## Quick Start
1752
25
 
1753
26
  ```typescript
1754
- // In your base library
1755
- import { DefaultStringKey, DefaultLanguage } from '@digitaldefiance/i18n-lib';
27
+ import { PluginI18nEngine, LanguageCodes } from '@digitaldefiance/i18n-lib';
1756
28
 
1757
- enum MyLibStringKey {
1758
- Feature_Save = 'feature_save',
1759
- Feature_Load = 'feature_load',
1760
- }
29
+ // Create engine with languages
30
+ const engine = PluginI18nEngine.createInstance('myapp', [
31
+ { id: LanguageCodes.EN_US, name: 'English (US)', code: 'en-US', isDefault: true },
32
+ { id: LanguageCodes.FR, name: 'Français', code: 'fr' }
33
+ ]);
1761
34
 
1762
- // Extend the global configuration
1763
- declare global {
1764
- namespace I18n {
1765
- interface Config {
1766
- StringKey: DefaultStringKey | MyLibStringKey;
35
+ // Register component with translations
36
+ engine.registerComponent({
37
+ component: {
38
+ id: 'app',
39
+ name: 'Application',
40
+ stringKeys: ['welcome', 'goodbye']
41
+ },
42
+ strings: {
43
+ [LanguageCodes.EN_US]: {
44
+ welcome: 'Welcome to {appName}!',
45
+ goodbye: 'Goodbye!'
46
+ },
47
+ [LanguageCodes.FR]: {
48
+ welcome: 'Bienvenue sur {appName}!',
49
+ goodbye: 'Au revoir!'
1767
50
  }
1768
51
  }
1769
- }
1770
- ```
1771
-
1772
- ### Extending Another Library
1773
-
1774
- ```typescript
1775
- // In a library that extends the base library
1776
- import { DefaultStringKey } from '@digitaldefiance/i18n-lib';
1777
- import { MyLibStringKey } from 'my-base-lib';
52
+ });
1778
53
 
1779
- enum AdvancedStringKey {
1780
- Advanced_Export = 'advanced_export',
1781
- Advanced_Import = 'advanced_import',
1782
- }
54
+ // Translate
55
+ console.log(engine.translate('app', 'welcome', { appName: 'MyApp' }));
56
+ // Output: "Welcome to MyApp!"
1783
57
 
1784
- // Extend with union types
1785
- declare global {
1786
- namespace I18n {
1787
- interface Config {
1788
- StringKey: DefaultStringKey | MyLibStringKey | AdvancedStringKey;
1789
- }
1790
- }
1791
- }
58
+ // Switch language
59
+ engine.setLanguage(LanguageCodes.FR);
60
+ console.log(engine.translate('app', 'welcome', { appName: 'MyApp' }));
61
+ // Output: "Bienvenue sur MyApp!"
1792
62
  ```
1793
63
 
1794
- ### Final Application
1795
-
1796
- ```typescript
1797
- // In your final application
1798
- import { StringKey, Language, getI18nEngine } from 'my-extended-lib';
1799
-
1800
- // All string keys from all layers are now available
1801
- const engine = getI18nEngine();
1802
- const translation = engine.translate('advanced_export' as StringKey);
1803
- ```
64
+ ## Core Concepts
1804
65
 
1805
- ### Default Configuration Helper
66
+ ### PluginI18nEngine
1806
67
 
1807
- Use the default configuration helper for quick setup:
68
+ The main engine class that manages translations, languages, and components.
1808
69
 
1809
70
  ```typescript
1810
- import { getDefaultI18nEngine } from '@digitaldefiance/i18n-lib';
1811
-
1812
- // Create engine with default configuration
1813
- const engine = getDefaultI18nEngine(
1814
- { APP_NAME: 'MyApp' }, // constants
1815
- new Timezone('America/New_York'), // user timezone
1816
- new Timezone('UTC') // admin timezone
1817
- );
1818
- ```
1819
-
1820
- ## Configuration Validation
71
+ import { PluginI18nEngine, LanguageCodes } from '@digitaldefiance/i18n-lib';
1821
72
 
1822
- The engine automatically validates configurations during construction to ensure completeness:
1823
-
1824
- ### Validation Rules
73
+ // Create instance
74
+ const engine = PluginI18nEngine.createInstance('myapp', languages);
1825
75
 
1826
- 1. **All languages in `languageCodes` must have corresponding `strings` collections**
1827
- 2. **All `stringNames` must be present in every language's string collection**
1828
- 3. **The `defaultLanguage` must have a string collection**
76
+ // Or use constructor
77
+ const engine = new PluginI18nEngine(languages, config);
78
+ ```
1829
79
 
1830
- ### Localized Error Messages
80
+ ### Component Registration
1831
81
 
1832
- Validation errors can be localized by including error message keys in your configuration:
82
+ Components group related translations together:
1833
83
 
1834
84
  ```typescript
1835
- enum MyStrings {
1836
- Welcome = 'welcome',
1837
- // Error message keys
1838
- Error_MissingStringCollectionTemplate = 'error_missing_string_collection_template',
1839
- Error_MissingTranslationTemplate = 'error_missing_translation_template',
1840
- Error_DefaultLanguageNoCollectionTemplate = 'error_default_language_no_collection_template'
1841
- }
1842
-
1843
- const config: I18nConfig<MyStrings, MyLanguages> = {
1844
- stringNames: Object.values(MyStrings),
85
+ engine.registerComponent({
86
+ component: {
87
+ id: 'auth',
88
+ name: 'Authentication',
89
+ stringKeys: ['login', 'logout', 'error']
90
+ },
1845
91
  strings: {
1846
- [MyLanguages.English]: {
1847
- [MyStrings.Welcome]: 'Welcome!',
1848
- [MyStrings.Error_MissingStringCollectionTemplate]: 'Missing translations for language: {language}',
1849
- [MyStrings.Error_MissingTranslationTemplate]: 'Key \'{key}\' not found in {language}',
1850
- [MyStrings.Error_DefaultLanguageNoCollectionTemplate]: 'Default language \'{language}\' has no translations'
92
+ [LanguageCodes.EN_US]: {
93
+ login: 'Login',
94
+ logout: 'Logout',
95
+ error: 'Authentication failed'
96
+ },
97
+ [LanguageCodes.FR]: {
98
+ login: 'Connexion',
99
+ logout: 'Déconnexion',
100
+ error: 'Échec de l\'authentification'
1851
101
  }
1852
102
  },
1853
- // ... rest of config
1854
- };
103
+ aliases: ['authentication'] // Optional aliases
104
+ });
1855
105
  ```
1856
106
 
1857
- ### Fallback Error Messages
1858
-
1859
- If localized error messages aren't provided, the engine falls back to English templates:
1860
-
1861
- - `Missing string collection for language: {language}`
1862
- - `Missing translation for key '{key}' in language '{language}'`
1863
- - `Default language '{language}' has no string collection`
1864
-
1865
- ## Error Handling
1866
-
1867
- The library provides comprehensive error handling with localized error messages:
1868
-
1869
- ### Registry Errors
107
+ ### Translation
1870
108
 
1871
109
  ```typescript
1872
- import { RegistryError, RegistryErrorType } from '@digitaldefiance/i18n-lib';
1873
-
1874
- try {
1875
- engine.registerComponent(invalidRegistration);
1876
- } catch (error) {
1877
- if (error instanceof RegistryError) {
1878
- console.error(`Error type: ${error.type}`);
1879
- console.error(`Metadata:`, error.metadata);
1880
-
1881
- switch (error.type) {
1882
- case RegistryErrorType.ComponentNotFound:
1883
- // Handle missing component
1884
- break;
1885
- case RegistryErrorType.DuplicateComponent:
1886
- // Handle duplicate registration
1887
- break;
1888
- case RegistryErrorType.ValidationFailed:
1889
- // Handle validation failure
1890
- break;
1891
- }
1892
- }
1893
- }
1894
- ```
1895
-
1896
- **Error Types:**
110
+ // Simple translation
111
+ const text = engine.translate('auth', 'login');
1897
112
 
1898
- - `ComponentNotFound`: Component ID not registered
1899
- - `LanguageNotFound`: Language not registered
1900
- - `StringKeyNotFound`: Translation key not found
1901
- - `IncompleteRegistration`: Missing translations detected
1902
- - `DuplicateComponent`: Component already registered
1903
- - `DuplicateLanguage`: Language already registered
1904
- - `ValidationFailed`: Validation check failed
113
+ // With variables
114
+ const greeting = engine.translate('app', 'welcome', { name: 'John' });
1905
115
 
1906
- ### Context Errors
116
+ // Specific language
117
+ const french = engine.translate('auth', 'login', {}, LanguageCodes.FR);
1907
118
 
1908
- ```typescript
1909
- import { ContextError, ContextErrorType } from '@digitaldefiance/i18n-lib';
1910
-
1911
- try {
1912
- const context = globalContext.getContext('invalid-key');
1913
- } catch (error) {
1914
- if (error instanceof ContextError) {
1915
- console.error(`Invalid context: ${error.contextKey}`);
1916
- }
1917
- }
119
+ // Safe translation (returns fallback on error)
120
+ const safe = engine.safeTranslate('missing', 'key'); // Returns "[missing.key]"
1918
121
  ```
1919
122
 
1920
- ### Safe Translation
1921
-
1922
- Use `safeTranslate` to prevent errors:
123
+ ### Template Processing
1923
124
 
1924
125
  ```typescript
1925
- // Regular translate throws on missing key
1926
- try {
1927
- const text = engine.translate('component', 'missingKey');
1928
- } catch (error) {
1929
- // Handle error
1930
- }
1931
-
1932
- // Safe translate returns placeholder
1933
- const text = engine.safeTranslate('component', 'missingKey');
1934
- // Returns: "[component.missingKey]"
1935
- ```
1936
-
1937
- ## Validation
126
+ // Component references: {{componentId.stringKey}}
127
+ engine.t('Click {{auth.login}} to continue');
1938
128
 
1939
- ### Component Validation
129
+ // Variables: {variableName}
130
+ engine.t('Hello, {username}!', { username: 'Alice' });
1940
131
 
1941
- ```typescript
1942
- // Validate during registration
1943
- const result = engine.registerComponent(registration);
1944
-
1945
- if (!result.isValid) {
1946
- console.error('Validation errors:', result.errors);
1947
- console.warn('Missing keys:', result.missingKeys);
1948
-
1949
- result.missingKeys.forEach(missing => {
1950
- console.log(`Missing: ${missing.stringKey} for ${missing.languageId} in ${missing.componentId}`);
1951
- });
1952
- }
132
+ // Mixed
133
+ engine.t('{{auth.login}}: {username}', { username: 'admin' });
1953
134
  ```
1954
135
 
1955
- ### Global Validation
136
+ ### Language Management
1956
137
 
1957
138
  ```typescript
1958
- // Validate all components
1959
- const validation = engine.validateAllComponents();
139
+ // Set current language
140
+ engine.setLanguage(LanguageCodes.FR);
141
+
142
+ // Get current language
143
+ const lang = engine.getCurrentLanguage();
1960
144
 
1961
- if (!validation.isValid) {
1962
- console.error('Errors:', validation.errors);
1963
- console.warn('Warnings:', validation.warnings);
145
+ // Check if language exists
146
+ if (engine.hasLanguage(LanguageCodes.ES)) {
147
+ engine.setLanguage(LanguageCodes.ES);
1964
148
  }
1965
149
 
1966
- // Example output:
1967
- // Errors: ["Component 'user' missing strings for language 'de'"]
1968
- // Warnings: ["Component 'user' missing key 'greeting' for language 'fr'"]
150
+ // Get all languages
151
+ const languages = engine.getLanguages();
1969
152
  ```
1970
153
 
1971
- ### Validation Configuration
154
+ ### Admin Context
1972
155
 
1973
- ```typescript
1974
- import { ValidationConfig } from '@digitaldefiance/i18n-lib';
156
+ Separate language for admin interfaces:
1975
157
 
1976
- const strictConfig: ValidationConfig = {
1977
- requireCompleteStrings: true,
1978
- allowPartialRegistration: false,
1979
- fallbackLanguageId: 'en-US'
1980
- };
158
+ ```typescript
159
+ // Set admin language
160
+ engine.setAdminLanguage(LanguageCodes.EN_US);
1981
161
 
1982
- const lenientConfig: ValidationConfig = {
1983
- requireCompleteStrings: false,
1984
- allowPartialRegistration: true,
1985
- fallbackLanguageId: 'en-US'
1986
- };
162
+ // Switch to admin context
163
+ engine.switchToAdmin();
1987
164
 
1988
- const engine = new PluginI18nEngine(languages, {
1989
- validation: strictConfig
1990
- });
165
+ // Switch back to user context
166
+ engine.switchToUser();
1991
167
  ```
1992
168
 
1993
- ## Best Practices
1994
-
1995
- ### Plugin Architecture (Recommended for New Projects)
169
+ ## Core System Strings
1996
170
 
1997
- 1. **Use Component Registration**: Organize translations into logical components with `PluginI18nEngine`
1998
- 2. **Define String Key Enums**: Always use TypeScript enums for string keys to ensure compile-time type safety
1999
- 3. **Complete Component Translations**: Provide translations for all supported languages in each component
2000
- 4. **Validate Registrations**: Check validation results and handle missing translations appropriately
2001
- 5. **Use Core Components**: Start with `createCoreI18nEngine()` for built-in system strings
2002
- 6. **Fallback Strategy**: Configure appropriate fallback languages for missing translations
2003
- 7. **Component Isolation**: Keep related strings together in the same component
2004
- 8. **Template Variables**: Use template strings with variables for dynamic content
2005
- 9. **Multi-Instance Architecture**: Use named instances for different application contexts (admin, user, etc.)
171
+ Pre-built translations for common UI elements:
2006
172
 
2007
- ### Legacy System (Still Supported)
2008
-
2009
- 1. **Complete Translations**: EnumTranslation requires all enum values to be translated
2010
- 2. **Type Safety**: Use TypeScript enums for string keys and languages
2011
- 3. **Context Separation**: Use different contexts for admin and user interfaces
2012
- 4. **Instance Management**: Use named instances for different parts of your application
2013
- 5. **Error Handling**: Handle missing translations gracefully with fallback languages
2014
- 6. **Layered Extension**: Use union types when extending configurations across libraries
2015
- 7. **Default Configuration**: Use `getDefaultI18nEngine()` for standard setups
2016
-
2017
- ### Migration Strategy
173
+ ```typescript
174
+ import { getCoreI18nEngine, CoreStringKey, CoreI18nComponentId } from '@digitaldefiance/i18n-lib';
2018
175
 
2019
- When migrating from legacy to plugin architecture:
176
+ const coreEngine = getCoreI18nEngine();
2020
177
 
2021
- ```typescript
2022
- // Legacy approach
2023
- const legacy = new I18nEngine(config);
2024
- const text = legacy.translate(MyStrings.Welcome);
2025
-
2026
- // New plugin approach
2027
- const modern = createCoreI18nEngine();
2028
- modern.registerComponent(myComponentRegistration);
2029
- const text = modern.translate('my-component', MyStrings.Welcome);
178
+ // Use core strings
179
+ const yes = coreEngine.translate(CoreI18nComponentId, CoreStringKey.Common_Yes);
180
+ const error = coreEngine.translate(CoreI18nComponentId, CoreStringKey.Error_NotFound);
2030
181
  ```
2031
182
 
2032
- Both systems can coexist in the same application during migration.
183
+ Available core string categories:
2033
184
 
2034
- ## Performance Considerations
185
+ - **Common**: Yes, No, Cancel, OK, Save, Delete, Edit, Create, Update, Loading, etc.
186
+ - **Errors**: InvalidInput, NetworkError, NotFound, AccessDenied, ValidationFailed, etc.
187
+ - **System**: Welcome, Goodbye, PleaseWait, ProcessingRequest, OperationComplete, etc.
2035
188
 
2036
- ### Instance Management
189
+ ## Multiple Instances
2037
190
 
2038
- - Use named instances to isolate different application contexts
2039
- - Reuse instances rather than creating new ones
2040
- - Clean up instances in tests to prevent memory leaks
191
+ Create isolated engines for different parts of your application:
2041
192
 
2042
- ### Translation Caching
2043
-
2044
- - Translations are stored in memory for fast access
2045
- - Component strings are validated once during registration
2046
- - Fallback translations are generated during registration, not at runtime
193
+ ```typescript
194
+ // Admin engine
195
+ const adminEngine = PluginI18nEngine.createInstance('admin', adminLanguages);
2047
196
 
2048
- ### Type Safety Overhead
197
+ // User engine
198
+ const userEngine = PluginI18nEngine.createInstance('user', userLanguages);
2049
199
 
2050
- - Compile-time type checking has zero runtime cost
2051
- - Generic types are erased during compilation
2052
- - Validation runs only during registration, not translation
200
+ // Get instance by key
201
+ const admin = PluginI18nEngine.getInstance('admin');
2053
202
 
2054
- ## Security Considerations
203
+ // Check if instance exists
204
+ if (PluginI18nEngine.hasInstance('admin')) {
205
+ // ...
206
+ }
2055
207
 
2056
- ### Zero-Knowledge Patterns
208
+ // Remove instance
209
+ PluginI18nEngine.removeInstance('admin');
2057
210
 
2058
- The library is designed to support zero-knowledge security patterns:
211
+ // Reset all instances
212
+ PluginI18nEngine.resetAll();
213
+ ```
2059
214
 
2060
- - No translation data is sent to external services
2061
- - All translations are stored locally
2062
- - No telemetry or analytics
2063
- - Suitable for sensitive applications
215
+ ## Error Handling
2064
216
 
2065
- ### Input Sanitization
217
+ ### RegistryError
2066
218
 
2067
- Always sanitize user input before using in translations:
219
+ Errors related to component/language registration:
2068
220
 
2069
221
  ```typescript
2070
- import { replaceVariables } from '@digitaldefiance/i18n-lib';
2071
-
2072
- // BAD: Direct user input
2073
- const message = engine.translate('component', 'template', {
2074
- userInput: req.body.unsafeInput
2075
- });
222
+ import { RegistryError, RegistryErrorType } from '@digitaldefiance/i18n-lib';
2076
223
 
2077
- // GOOD: Sanitized input
2078
- const message = engine.translate('component', 'template', {
2079
- userInput: sanitize(req.body.unsafeInput)
2080
- });
224
+ try {
225
+ engine.translate('missing', 'key');
226
+ } catch (error) {
227
+ if (error instanceof RegistryError) {
228
+ console.log(error.type); // RegistryErrorType.COMPONENT_NOT_FOUND
229
+ console.log(error.message); // "Component 'missing' not found"
230
+ console.log(error.metadata); // { componentId: 'missing' }
231
+ }
232
+ }
2081
233
  ```
2082
234
 
2083
- ### Error Message Exposure
235
+ ### TranslatableError
2084
236
 
2085
- Be careful not to expose sensitive information in error messages:
237
+ Base class for errors with translated messages:
2086
238
 
2087
239
  ```typescript
2088
- // BAD: Exposes internal details
2089
- throw new TranslatableGenericError(
2090
- 'auth',
2091
- 'loginFailed',
2092
- { password: user.password }, // Don't include sensitive data
2093
- language
2094
- );
2095
-
2096
- // GOOD: Safe error message
2097
- throw new TranslatableGenericError(
2098
- 'auth',
2099
- 'loginFailed',
2100
- { username: user.username },
2101
- language,
2102
- { userId: user.id } // Metadata for logging only
2103
- );
2104
- ```
240
+ import { TranslatableError, CoreStringKey, CoreI18nComponentId } from '@digitaldefiance/i18n-lib';
2105
241
 
2106
- ## Mongoose Integration
2107
-
2108
- ### Schema Definition with Language Enums
2109
-
2110
- ```typescript
2111
- import { Schema, model } from 'mongoose';
2112
- import { LanguageCodes, getCoreLanguageCodes } from '@digitaldefiance/i18n-lib';
2113
-
2114
- // Get core language codes from helper (single source of truth)
2115
- const supportedLanguages = getCoreLanguageCodes();
2116
- // ['en-US', 'en-GB', 'fr', 'es', 'de', 'zh-CN', 'ja', 'uk']
2117
-
2118
- const userSchema = new Schema({
2119
- language: {
2120
- type: String,
2121
- enum: supportedLanguages,
2122
- default: LanguageCodes.EN_US,
2123
- required: true
2124
- },
2125
- adminLanguage: {
2126
- type: String,
2127
- enum: supportedLanguages,
2128
- default: LanguageCodes.EN_US
242
+ class MyError extends TranslatableError {
243
+ constructor(language?: string) {
244
+ super(
245
+ CoreI18nComponentId,
246
+ CoreStringKey.Error_AccessDenied,
247
+ {},
248
+ language
249
+ );
2129
250
  }
2130
- });
251
+ }
2131
252
 
2132
- export const User = model('User', userSchema);
253
+ throw new MyError(LanguageCodes.FR); // Throws with French error message
2133
254
  ```
2134
255
 
2135
- ### Dynamic Language Enum from Engine
256
+ ## Translation Adapter
257
+
258
+ Adapt PluginI18nEngine to simpler TranslationEngine interface:
2136
259
 
2137
260
  ```typescript
2138
- import { PluginI18nEngine, LanguageRegistry } from '@digitaldefiance/i18n-lib';
2139
-
2140
- // Ensure the engine has registered its languages
2141
- const engine = PluginI18nEngine.getInstance();
2142
- const languageEnum = LanguageRegistry.getLanguageIds();
2143
- const defaultLanguage =
2144
- LanguageRegistry.getDefaultLanguageId() ?? engine.getContext().language;
2145
-
2146
- const contentSchema = new Schema({
2147
- language: {
2148
- type: String,
2149
- enum: languageEnum,
2150
- default: defaultLanguage
2151
- }
2152
- });
2153
- ```
261
+ import { createTranslationAdapter } from '@digitaldefiance/i18n-lib';
2154
262
 
2155
- ### Validation with Display Names
263
+ const adapter = createTranslationAdapter(engine, 'componentId');
2156
264
 
2157
- ```typescript
2158
- import { LanguageRegistry } from '@digitaldefiance/i18n-lib';
2159
-
2160
- const displayNames = LanguageRegistry.getLanguageDisplayNames();
2161
-
2162
- const settingsSchema = new Schema({
2163
- language: {
2164
- type: String,
2165
- enum: Object.keys(displayNames),
2166
- validate: {
2167
- validator: (v: string) => v in displayNames,
2168
- message: (props) => {
2169
- const validLanguages = Object.entries(displayNames)
2170
- .map(([code, name]) => `${name} (${code})`)
2171
- .join(', ');
2172
- return `${props.value} is not a valid language. Valid options: ${validLanguages}`;
2173
- }
2174
- }
2175
- }
2176
- });
265
+ // Use adapter where TranslationEngine is expected
266
+ const message = adapter.translate('key', { var: 'value' });
2177
267
  ```
2178
268
 
2179
- ## Integration Examples
269
+ ## Language Codes
2180
270
 
2181
- ### Express.js Middleware
271
+ Built-in language codes following BCP 47 standard:
2182
272
 
2183
273
  ```typescript
2184
- import { PluginI18nEngine, CoreLanguageCode } from '@digitaldefiance/i18n-lib';
2185
- import { Request, Response, NextFunction } from 'express';
2186
-
2187
- const engine = PluginI18nEngine.getInstance<CoreLanguageCode>();
2188
-
2189
- function i18nMiddleware(req: Request, res: Response, next: NextFunction) {
2190
- // Get language from header, query, or cookie
2191
- const language = (req.headers['accept-language'] ||
2192
- req.query.lang ||
2193
- req.cookies.language ||
2194
- 'en-US') as CoreLanguageCode;
2195
-
2196
- // Set language for this request (type-safe)
2197
- engine.setLanguage(language);
2198
-
2199
- // Add translation helper to response locals
2200
- res.locals.t = (componentId: string, key: string, vars?: any) => {
2201
- return engine.translate(componentId, key, vars, language);
2202
- };
2203
-
2204
- next();
2205
- }
274
+ import { LanguageCodes } from '@digitaldefiance/i18n-lib';
2206
275
 
2207
- app.use(i18nMiddleware);
276
+ LanguageCodes.EN_US // 'en-US'
277
+ LanguageCodes.EN_GB // 'en-GB'
278
+ LanguageCodes.FR // 'fr'
279
+ LanguageCodes.ES // 'es'
280
+ LanguageCodes.DE // 'de'
281
+ LanguageCodes.ZH_CN // 'zh-CN'
282
+ LanguageCodes.JA // 'ja'
283
+ LanguageCodes.UK // 'uk'
2208
284
  ```
2209
285
 
2210
- ### React Integration
2211
-
2212
- ```typescript
2213
- import React, { createContext, useContext, useState } from 'react';
2214
- import { PluginI18nEngine, CoreLanguageCode, LanguageCodes } from '@digitaldefiance/i18n-lib';
2215
-
2216
- const I18nContext = createContext<{
2217
- engine: PluginI18nEngine<CoreLanguageCode>;
2218
- language: CoreLanguageCode;
2219
- setLanguage: (lang: CoreLanguageCode) => void;
2220
- } | null>(null);
2221
-
2222
- export function I18nProvider({ children }: { children: React.ReactNode }) {
2223
- const [engine] = useState(() => PluginI18nEngine.getInstance<CoreLanguageCode>());
2224
- const [language, setLanguageState] = useState<CoreLanguageCode>(LanguageCodes.EN_US);
2225
-
2226
- const setLanguage = (lang: CoreLanguageCode) => {
2227
- engine.setLanguage(lang);
2228
- setLanguageState(lang);
2229
- };
2230
-
2231
- return (
2232
- <I18nContext.Provider value={{ engine, language, setLanguage }}>
2233
- {children}
2234
- </I18nContext.Provider>
2235
- );
2236
- }
2237
-
2238
- export function useI18n() {
2239
- const context = useContext(I18nContext);
2240
- if (!context) throw new Error('useI18n must be used within I18nProvider');
2241
-
2242
- const t = (componentId: string, key: string, vars?: any) => {
2243
- return context.engine.translate(componentId, key, vars, context.language);
2244
- };
2245
-
2246
- return { ...context, t };
2247
- }
286
+ ## API Reference
2248
287
 
2249
- // Usage in component
2250
- function MyComponent() {
2251
- const { t, language, setLanguage } = useI18n();
2252
-
2253
- return (
2254
- <div>
2255
- <h1>{t('core', CoreStringKey.System_Welcome)}</h1>
2256
- <button onClick={() => setLanguage('fr')}>
2257
- Switch to French
2258
- </button>
2259
- </div>
2260
- );
2261
- }
2262
- ```
288
+ ### PluginI18nEngine
2263
289
 
2264
- ### Vue.js Plugin
290
+ **Static Methods**
2265
291
 
2266
- ```typescript
2267
- import { Plugin } from 'vue';
2268
- import { PluginI18nEngine, CoreLanguageCode } from '@digitaldefiance/i18n-lib';
2269
-
2270
- const i18nPlugin: Plugin = {
2271
- install(app, options) {
2272
- const engine = PluginI18nEngine.getInstance<CoreLanguageCode>();
2273
-
2274
- app.config.globalProperties.$t = (
2275
- componentId: string,
2276
- key: string,
2277
- vars?: any
2278
- ) => {
2279
- return engine.translate(componentId, key, vars);
2280
- };
2281
-
2282
- app.config.globalProperties.$i18n = engine;
2283
- }
2284
- };
292
+ - `createInstance<TLanguage>(key: string, languages: LanguageDefinition[], config?: RegistryConfig)` - Create named instance
293
+ - `getInstance<TLanguage>(key?: string)` - Get instance by key
294
+ - `hasInstance(key?: string)` - Check if instance exists
295
+ - `removeInstance(key?: string)` - Remove instance
296
+ - `resetAll()` - Reset all instances
2285
297
 
2286
- export default i18nPlugin;
298
+ **Instance Methods**
2287
299
 
2288
- // Usage in component
2289
- // <template>
2290
- // <div>{{ $t('core', 'System_Welcome') }}</div>
2291
- // </template>
2292
- ```
300
+ - `registerComponent(registration: ComponentRegistration)` - Register component
301
+ - `translate(componentId: string, key: string, variables?, language?)` - Translate string
302
+ - `safeTranslate(componentId: string, key: string, variables?, language?)` - Safe translate with fallback
303
+ - `t(template: string, variables?, language?)` - Process template string
304
+ - `setLanguage(language: TLanguage)` - Set current language
305
+ - `setAdminLanguage(language: TLanguage)` - Set admin language
306
+ - `getCurrentLanguage()` - Get current language
307
+ - `getLanguages()` - Get all languages
308
+ - `hasLanguage(language: TLanguage)` - Check if language exists
309
+ - `switchToAdmin()` - Switch to admin context
310
+ - `switchToUser()` - Switch to user context
311
+ - `validate()` - Validate all components
2293
312
 
2294
- ## Troubleshooting
313
+ ### Core Functions
2295
314
 
2296
- ### Common Issues
315
+ - `getCoreI18nEngine()` - Get core engine with system strings
316
+ - `createCoreI18nEngine(instanceKey?)` - Create core engine instance
317
+ - `getCoreTranslation(stringKey, variables?, language?, instanceKey?)` - Get core translation
318
+ - `safeCoreTranslation(stringKey, variables?, language?, instanceKey?)` - Safe core translation
319
+ - `getCoreLanguageCodes()` - Get array of core language codes
320
+ - `getCoreLanguageDefinitions()` - Get core language definitions
2297
321
 
2298
- **Issue: "Instance with key 'X' not found"**
322
+ ## Testing
2299
323
 
2300
324
  ```typescript
2301
- // Solution: Create instance before using
2302
- const engine = PluginI18nEngine.createInstance('myapp', languages);
2303
- // Or use default instance
2304
- const engine = PluginI18nEngine.getInstance(); // Uses 'default' key
2305
- ```
2306
-
2307
- **Issue: "Component 'X' not found"**
325
+ import { PluginI18nEngine } from '@digitaldefiance/i18n-lib';
2308
326
 
2309
- ```typescript
2310
- // Solution: Register component before translating
2311
- engine.registerComponent(myComponentRegistration);
2312
- const text = engine.translate('my-component', 'key');
2313
- ```
327
+ describe('My Tests', () => {
328
+ beforeEach(() => {
329
+ PluginI18nEngine.resetAll();
330
+ });
2314
331
 
2315
- **Issue: "Language 'X' not found"**
332
+ afterEach(() => {
333
+ PluginI18nEngine.resetAll();
334
+ });
2316
335
 
2317
- ```typescript
2318
- // Solution: Register language before using
2319
- engine.registerLanguage(createLanguageDefinition('fr', 'Français', 'fr'));
2320
- engine.setLanguage('fr');
336
+ it('should translate', () => {
337
+ const engine = PluginI18nEngine.createInstance('test', languages);
338
+ engine.registerComponent(registration);
339
+ expect(engine.translate('app', 'hello')).toBe('Hello');
340
+ });
341
+ });
2321
342
  ```
2322
343
 
2323
- **Issue: Missing translations in production**
2324
-
2325
- ```typescript
2326
- // Solution: Use validation to catch missing translations
2327
- const validation = engine.validateAllComponents();
2328
- if (!validation.isValid) {
2329
- console.error('Missing translations:', validation.errors);
2330
- // Fix translations before deploying
2331
- }
2332
- ```
344
+ ## TypeScript Support
2333
345
 
2334
- **Issue: Type errors with string keys**
346
+ Full TypeScript support with generic types:
2335
347
 
2336
348
  ```typescript
2337
- // Solution: Use enum values, not strings
2338
- enum MyKeys {
2339
- Welcome = 'welcome'
2340
- }
349
+ // Type-safe language codes
350
+ type MyLanguages = 'en-US' | 'fr' | 'es';
351
+ const engine = PluginI18nEngine.createInstance<MyLanguages>('app', languages);
2341
352
 
2342
- // BAD
2343
- engine.translate('component', 'welcome'); // Type error
353
+ // Type-safe string keys
354
+ enum MyStringKeys {
355
+ Welcome = 'welcome',
356
+ Goodbye = 'goodbye'
357
+ }
2344
358
 
2345
- // GOOD
2346
- engine.translate('component', MyKeys.Welcome);
359
+ // Type-safe component registration
360
+ const registration: ComponentRegistration<MyStringKeys, MyLanguages> = {
361
+ component: {
362
+ id: 'app',
363
+ name: 'App',
364
+ stringKeys: Object.values(MyStringKeys)
365
+ },
366
+ strings: {
367
+ 'en-US': {
368
+ [MyStringKeys.Welcome]: 'Welcome',
369
+ [MyStringKeys.Goodbye]: 'Goodbye'
370
+ },
371
+ 'fr': {
372
+ [MyStringKeys.Welcome]: 'Bienvenue',
373
+ [MyStringKeys.Goodbye]: 'Au revoir'
374
+ }
375
+ }
376
+ };
2347
377
  ```
2348
378
 
2349
- ## License
379
+ ## Browser Support
2350
380
 
2351
- MIT License - See LICENSE file for details
381
+ - Chrome/Edge: Latest 2 versions
382
+ - Firefox: Latest 2 versions
383
+ - Safari: Latest 2 versions
384
+ - Node.js: 18+
2352
385
 
2353
- ## Repository
386
+ ## License
2354
387
 
2355
- Part of the [DigitalBurnbag](https://github.com/Digital-Defiance/DigitalBurnbag) project - a secure file sharing and automated protocol system with zero-knowledge encryption.
388
+ MIT License - See LICENSE file for details
2356
389
 
2357
390
  ## Contributing
2358
391
 
2359
- Contributions are welcome! Please:
392
+ Contributions welcome! Please:
2360
393
 
2361
394
  1. Fork the repository
2362
395
  2. Create a feature branch
2363
396
  3. Add tests for new functionality
2364
- 4. Ensure all tests pass
397
+ 4. Ensure all tests pass (`npm test`)
2365
398
  5. Submit a pull request
2366
399
 
2367
- ### Development Setup
400
+ ## Support
2368
401
 
2369
- ```bash
2370
- # Clone repository
2371
- git clone https://github.com/Digital-Defiance/DigitalBurnbag.git
2372
- cd DigitalBurnbag/packages/digitaldefiance-i18n-lib
402
+ - **Issues**: <https://github.com/Digital-Defiance/i18n-lib/issues>
403
+ - **Documentation**: See docs/ directory
404
+ - **Examples**: See tests/ directory
2373
405
 
2374
- # Install dependencies
2375
- yarn install
406
+ ## ChangeLog
2376
407
 
2377
- # Run tests
2378
- yarn test
408
+ ### Version 2.0.0
2379
409
 
2380
- # Build
2381
- yarn build
2382
- ```
410
+ **Major Release** - Architecture improvements and bug fixes
2383
411
 
2384
- ## Support
412
+ **Fixed**:
2385
413
 
2386
- For issues, questions, or contributions:
414
+ - Fixed `RegistryError.createWithEngine` to use correct parameter order (removed redundant componentId parameter)
415
+ - Fixed `PluginTranslatableGenericError.withEngine` to accept engine as first parameter instead of instanceKey
416
+ - Fixed `LanguageRegistry.getMatchingCode` to check and return language codes instead of IDs
417
+ - Fixed `createTranslationAdapter` to support both full TranslationEngine interface and simplified bound interface
418
+ - Fixed `TypedError` constructor to use correct componentId parameter
419
+ - Added missing `Common_Test` to CoreStringKey enum
420
+ - Fixed mock TranslationEngine signatures in tests to include componentId parameter
2387
421
 
2388
- - GitHub Issues: <https://github.com/Digital-Defiance/DigitalBurnbag/issues>
2389
- - Documentation: See README.md and inline code documentation
2390
- - Examples: See `examples/` directory in repository
422
+ **Improved**:
2391
423
 
2392
- ## ChangeLog
424
+ - Enhanced `createTranslationAdapter` with dual interface support (4-param and 3-param calling conventions)
425
+ - Improved JSDoc documentation for `createTranslationAdapter` with usage examples
426
+ - Added French language support to typed-error tests
427
+ - Updated all test files to use v2 LanguageRegistry APIs
428
+ - Added `PluginI18nEngine.resetAll()` calls in test beforeEach hooks for better isolation
429
+
430
+ **Internal**:
431
+
432
+ - Refactored core-i18n.ts to use lazy initialization pattern with Proxy for backward compatibility
433
+ - Updated error classes to use `getCoreI18nEngine()` instead of direct singleton import
434
+ - Fixed duplicate LanguageRegistry exports by commenting out v1 export
435
+ - Added v1 compatibility methods to v2 LanguageRegistry
436
+ - Changed `getDefault()` to return `LanguageDefinition | null` instead of throwing
437
+
438
+ **Testing**:
439
+
440
+ - All 511 tests passing
441
+ - Fixed registry-error.spec.ts mock expectations
442
+ - Fixed language-registry.spec.ts to handle null default language
443
+ - Fixed create-translation-adapter.spec.ts parameter detection
444
+ - Fixed typed-error.spec.ts to import and use PluginI18nEngine
2393
445
 
2394
446
  ### Version 1.3.27
2395
447
 
@@ -2478,7 +530,7 @@ For issues, questions, or contributions:
2478
530
 
2479
531
  ### Version 1.2.5
2480
532
 
2481
- - Sat Oct 25 2025 15:0100 GMT-0700 (Pacific Daylight Time)
533
+ - Sat Oct 25 2025 15:01:00 GMT-0700 (Pacific Daylight Time)
2482
534
 
2483
535
  #### Added
2484
536
 
@@ -2487,6 +539,7 @@ For issues, questions, or contributions:
2487
539
  - Provides graceful error handling with fallback to key strings
2488
540
  - Zero overhead - direct delegation to underlying `PluginI18nEngine`
2489
541
  - Comprehensive test coverage (19 tests)
542
+ - Supports both full TranslationEngine interface and simplified bound interface
2490
543
 
2491
544
  #### Benefits
2492
545
 
@@ -2501,7 +554,8 @@ Packages using custom translation adapters can now replace them with:
2501
554
  ```typescript
2502
555
  import { createTranslationAdapter } from '@digitaldefiance/i18n-lib';
2503
556
  const adapter = createTranslationAdapter(pluginEngine, 'component-id');
2504
-
557
+ ```
558
+
2505
559
  ### Version 1.2.4
2506
560
 
2507
561
  - Sat Oct 25 2025 14:29:00 GMT-0700 (Pacific Daylight Time)
@@ -2525,6 +579,7 @@ const adapter = createTranslationAdapter(pluginEngine, 'component-id');
2525
579
  - Updated test mocks to implement both required `translate` and `safeTranslate` methods
2526
580
 
2527
581
  **Breaking Changes:**
582
+
2528
583
  - Any code implementing `TranslationEngine` must now provide both `translate` and `safeTranslate` methods (previously optional)
2529
584
  - `TranslationEngine` now requires explicit type parameter when used (e.g., `TranslationEngine<EciesStringKey>`)
2530
585
 
@@ -2538,6 +593,7 @@ const adapter = createTranslationAdapter(pluginEngine, 'component-id');
2538
593
  - Thu Oct 23 2025 14:13:00 GMT-0700 (Pacific Daylight Time)
2539
594
 
2540
595
  #### Breaking Changes
596
+
2541
597
  - **Removed `CoreLanguage` enum** - Replaced with `CoreLanguageCode` type and `LanguageCodes` constants
2542
598
  - **Language identifiers now use BCP 47 codes** - Changed from descriptive names (e.g., `'English (US)'`) to standard codes (e.g., `'en-US'`)
2543
599
  - **API changes**:
@@ -2546,6 +602,7 @@ const adapter = createTranslationAdapter(pluginEngine, 'component-id');
2546
602
  - All language references updated to use `LanguageCodes` constants
2547
603
 
2548
604
  #### Added
605
+
2549
606
  - **`LanguageCodes` constants object** - Provides standard BCP 47 language codes:
2550
607
  - `EN_US`, `EN_GB`, `FR`, `ES`, `DE`, `ZH_CN`, `JA`, `UK`
2551
608
  - **`LanguageDisplayNames` mapping** - Maps language codes to human-readable names
@@ -2554,11 +611,13 @@ const adapter = createTranslationAdapter(pluginEngine, 'component-id');
2554
611
  - **Custom language code support** - Any string can now be used as a language code
2555
612
 
2556
613
  #### Changed
614
+
2557
615
  - **Language code format** - All language identifiers now use BCP 47 standard (e.g., `'en-US'` instead of `'English (US)'`)
2558
616
  - **Type system** - Languages are now string-based types instead of enums, allowing custom language codes
2559
617
  - **Documentation** - Updated README with new API usage examples and language code constants
2560
618
 
2561
619
  #### Migration Guide
620
+
2562
621
  ```typescript
2563
622
  // Before (v1.1.x)
2564
623
  import { CoreLanguage } from '@digitaldefiance/i18n-lib';
@@ -2592,12 +651,7 @@ const myEngine = PluginI18nEngine.createInstance<MyLanguageCodes>('custom', lang
2592
651
 
2593
652
  - Wed Oct 15 2025 16:13:00 GMT-0700 (Pacific Daylight Time)
2594
653
  **Fixed:**
2595
- - Corrected safeCoreTranslation fallback format to use
2596
-
2597
- ```plaintext
2598
- [CoreStringKey.${stringKey}]
2599
- ```
2600
-
654
+ - Corrected safeCoreTranslation fallback format to use `[CoreStringKey.${stringKey}]`
2601
655
  - Fixed import issues with DefaultInstanceKey
2602
656
  **Added:**
2603
657
  - New TranslatableGenericError class for generic translatable errors across any component
@@ -2605,12 +659,7 @@ const myEngine = PluginI18nEngine.createInstance<MyLanguageCodes>('custom', lang
2605
659
  - 130+ new tests for comprehensive coverage
2606
660
  - Complete usage documentation
2607
661
  **Changed:**
2608
- - Standardized all fallback formats to use square brackets
2609
-
2610
- ```plaintext
2611
- [componentId.stringKey]
2612
- ```
2613
-
662
+ - Standardized all fallback formats to use square brackets `[componentId.stringKey]`
2614
663
  - Refactored to use CoreI18nComponentId constant
2615
664
  All tests pass and backward compatibility is maintained.
2616
665
 
@@ -2662,3 +711,86 @@ const myEngine = PluginI18nEngine.createInstance<MyLanguageCodes>('custom', lang
2662
711
 
2663
712
  - Wed Sep 24 2025 15:20:07 GMT-0700 (Pacific Daylight Time)
2664
713
  - Initial release of the TypeScript internationalization library with enum translation, template processing, context management, and currency formatting.
714
+ PluginI18nEngine.resetAll();
715
+ });
716
+
717
+ afterEach(() => {
718
+ PluginI18nEngine.resetAll();
719
+ });
720
+
721
+ it('should translate', () => {
722
+ const engine = PluginI18nEngine.createInstance('test', languages);
723
+ engine.registerComponent(registration);
724
+ expect(engine.translate('app', 'hello')).toBe('Hello');
725
+ });
726
+ });
727
+
728
+ ```
729
+
730
+ ## TypeScript Support
731
+
732
+ Full TypeScript support with generic types:
733
+
734
+ ```typescript
735
+ // Type-safe language codes
736
+ type MyLanguages = 'en-US' | 'fr' | 'es';
737
+ const engine = PluginI18nEngine.createInstance<MyLanguages>('app', languages);
738
+
739
+ // Type-safe string keys
740
+ enum MyStringKeys {
741
+ Welcome = 'welcome',
742
+ Goodbye = 'goodbye'
743
+ }
744
+
745
+ // Type-safe component registration
746
+ const registration: ComponentRegistration<MyStringKeys, MyLanguages> = {
747
+ component: {
748
+ id: 'app',
749
+ name: 'App',
750
+ stringKeys: Object.values(MyStringKeys)
751
+ },
752
+ strings: {
753
+ 'en-US': {
754
+ [MyStringKeys.Welcome]: 'Welcome',
755
+ [MyStringKeys.Goodbye]: 'Goodbye'
756
+ },
757
+ 'fr': {
758
+ [MyStringKeys.Welcome]: 'Bienvenue',
759
+ [MyStringKeys.Goodbye]: 'Au revoir'
760
+ }
761
+ }
762
+ };
763
+ ```
764
+
765
+ ## Browser Support
766
+
767
+ - Chrome/Edge: Latest 2 versions
768
+ - Firefox: Latest 2 versions
769
+ - Safari: Latest 2 versions
770
+ - Node.js: 18+
771
+
772
+ ## License
773
+
774
+ MIT License - See LICENSE file for details
775
+
776
+ ## Contributing
777
+
778
+ Contributions welcome! Please:
779
+
780
+ 1. Fork the repository
781
+ 2. Create a feature branch
782
+ 3. Add tests for new functionality
783
+ 4. Ensure all tests pass (`npm test`)
784
+ 5. Submit a pull request
785
+
786
+ ## Support
787
+
788
+ - **Issues**: <https://github.com/Digital-Defiance/i18n-lib/issues>
789
+ - **Documentation**: See docs/ directory
790
+ - **Examples**: See tests/ directory
791
+
792
+ ---
793
+
794
+ **Version**: 2.0.0
795
+ **Status**: Production Ready
796
+ **Bundle Size**: ~25KB (minified + gzipped)