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