@digitaldefiance/i18n-lib 1.0.22 → 1.0.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -5,12 +5,15 @@ A comprehensive TypeScript internationalization library with enum translation su
5
5
  ## Features
6
6
 
7
7
  - **Type-Safe Translations**: Full TypeScript support with generic types for strings and languages
8
+ - **Configuration Validation**: Automatic validation ensures all languages have complete string collections
9
+ - **Localized Error Messages**: Error messages can be translated using the engine's own translation system
8
10
  - **Enum Translation Registry**: Translate enum values with complete type safety
9
11
  - **Template Processing**: Advanced template system with `{{EnumName.EnumKey}}` patterns and variable replacement
10
12
  - **Context Management**: Admin vs user translation contexts with automatic language switching
11
13
  - **Singleton Pattern**: Efficient instance management with named instances
12
14
  - **Currency Formatting**: Built-in currency formatting utilities with locale support
13
15
  - **Fallback System**: Graceful degradation when translations are missing
16
+ - **Extensible Configuration**: Module augmentation support for layered library extension
14
17
  - **Zero Dependencies**: Lightweight with no external dependencies
15
18
 
16
19
  ## Installation
@@ -22,7 +25,7 @@ npm install @digitaldefiance/i18n-lib
22
25
  ## Quick Start
23
26
 
24
27
  ```typescript
25
- import { I18nEngine, I18nConfig, createContext } from '@digitaldefiance/i18n-lib';
28
+ import { I18nEngine, I18nConfig, createContext, CurrencyCode, Timezone } from '@digitaldefiance/i18n-lib';
26
29
 
27
30
  // Define your enums
28
31
  enum MyStrings {
@@ -49,12 +52,15 @@ const config: I18nConfig<MyStrings, MyLanguages> = {
49
52
  }
50
53
  },
51
54
  defaultLanguage: MyLanguages.English,
52
- defaultContext: 'user',
55
+ defaultTranslationContext: 'user',
56
+ defaultCurrencyCode: new CurrencyCode('USD'),
53
57
  languageCodes: {
54
58
  [MyLanguages.English]: 'en',
55
59
  [MyLanguages.Spanish]: 'es'
56
60
  },
57
- languages: Object.values(MyLanguages)
61
+ languages: Object.values(MyLanguages),
62
+ timezone: new Timezone('UTC'),
63
+ adminTimezone: new Timezone('UTC')
58
64
  };
59
65
 
60
66
  // Create engine
@@ -216,7 +222,7 @@ I18nEngine.removeInstance('main');
216
222
  #### Translation Methods
217
223
  - `translate(key, vars?, language?, fallbackLanguage?)` - Translate string with optional variables
218
224
  - `translateEnum(enumObj, value, language)` - Translate enum value
219
- - `t(templateString, language?, ...vars)` - Process template with enum patterns
225
+ - `t(templateString, language?, ...vars)` - Process template string with `{{EnumName.EnumKey}}` patterns
220
226
 
221
227
  #### Registration
222
228
  - `registerEnum(enumObj, translations, enumName)` - Register enum translations
@@ -266,16 +272,19 @@ type EnumLanguageTranslation<T extends string | number, TLanguage extends string
266
272
  [L in TLanguage]: EnumTranslation<T>;
267
273
  }>;
268
274
 
269
- interface I18nConfig<TStringKey, TLanguage, TConstants?, TContext?> {
275
+ interface I18nConfig<TStringKey, TLanguage, TConstants?, TTranslationContext?> {
270
276
  stringNames: TStringKey[];
271
277
  strings: MasterStringsCollection<TStringKey, TLanguage>;
272
278
  defaultLanguage: TLanguage;
273
- defaultContext: TContext;
279
+ defaultTranslationContext: TTranslationContext;
280
+ defaultCurrencyCode: CurrencyCode;
274
281
  languageCodes: LanguageCodeCollection<TLanguage>;
275
282
  languages: TLanguage[];
276
283
  constants?: TConstants;
277
284
  enumName?: string;
278
285
  enumObj?: Record<string, TStringKey>;
286
+ timezone: Timezone;
287
+ adminTimezone: Timezone;
279
288
  }
280
289
  ```
281
290
 
@@ -293,6 +302,124 @@ yarn test enum-registry.spec.ts
293
302
  yarn test i18n-engine.spec.ts
294
303
  ```
295
304
 
305
+ ## Extensible Configuration
306
+
307
+ The library supports layered extension across multiple libraries using TypeScript module augmentation:
308
+
309
+ ### Base Library Setup
310
+
311
+ ```typescript
312
+ // In your base library
313
+ import { DefaultStringKey, DefaultLanguage } from '@digitaldefiance/i18n-lib';
314
+
315
+ enum MyLibStringKey {
316
+ Feature_Save = 'feature_save',
317
+ Feature_Load = 'feature_load',
318
+ }
319
+
320
+ // Extend the global configuration
321
+ declare global {
322
+ namespace I18n {
323
+ interface Config {
324
+ StringKey: DefaultStringKey | MyLibStringKey;
325
+ }
326
+ }
327
+ }
328
+ ```
329
+
330
+ ### Extending Another Library
331
+
332
+ ```typescript
333
+ // In a library that extends the base library
334
+ import { DefaultStringKey } from '@digitaldefiance/i18n-lib';
335
+ import { MyLibStringKey } from 'my-base-lib';
336
+
337
+ enum AdvancedStringKey {
338
+ Advanced_Export = 'advanced_export',
339
+ Advanced_Import = 'advanced_import',
340
+ }
341
+
342
+ // Extend with union types
343
+ declare global {
344
+ namespace I18n {
345
+ interface Config {
346
+ StringKey: DefaultStringKey | MyLibStringKey | AdvancedStringKey;
347
+ }
348
+ }
349
+ }
350
+ ```
351
+
352
+ ### Final Application
353
+
354
+ ```typescript
355
+ // In your final application
356
+ import { StringKey, Language, getI18nEngine } from 'my-extended-lib';
357
+
358
+ // All string keys from all layers are now available
359
+ const engine = getI18nEngine();
360
+ const translation = engine.translate('advanced_export' as StringKey);
361
+ ```
362
+
363
+ ### Default Configuration Helper
364
+
365
+ Use the default configuration helper for quick setup:
366
+
367
+ ```typescript
368
+ import { getDefaultI18nEngine } from '@digitaldefiance/i18n-lib';
369
+
370
+ // Create engine with default configuration
371
+ const engine = getDefaultI18nEngine(
372
+ { APP_NAME: 'MyApp' }, // constants
373
+ new Timezone('America/New_York'), // user timezone
374
+ new Timezone('UTC') // admin timezone
375
+ );
376
+ ```
377
+
378
+ ## Configuration Validation
379
+
380
+ The engine automatically validates configurations during construction to ensure completeness:
381
+
382
+ ### Validation Rules
383
+
384
+ 1. **All languages in `languageCodes` must have corresponding `strings` collections**
385
+ 2. **All `stringNames` must be present in every language's string collection**
386
+ 3. **The `defaultLanguage` must have a string collection**
387
+
388
+ ### Localized Error Messages
389
+
390
+ Validation errors can be localized by including error message keys in your configuration:
391
+
392
+ ```typescript
393
+ enum MyStrings {
394
+ Welcome = 'welcome',
395
+ // Error message keys
396
+ Error_MissingStringCollectionTemplate = 'error_missing_string_collection_template',
397
+ Error_MissingTranslationTemplate = 'error_missing_translation_template',
398
+ Error_DefaultLanguageNoCollectionTemplate = 'error_default_language_no_collection_template'
399
+ }
400
+
401
+ const config: I18nConfig<MyStrings, MyLanguages> = {
402
+ stringNames: Object.values(MyStrings),
403
+ strings: {
404
+ [MyLanguages.English]: {
405
+ [MyStrings.Welcome]: 'Welcome!',
406
+ [MyStrings.Error_MissingStringCollectionTemplate]: 'Missing translations for language: {language}',
407
+ [MyStrings.Error_MissingTranslationTemplate]: 'Key \'{key}\' not found in {language}',
408
+ [MyStrings.Error_DefaultLanguageNoCollectionTemplate]: 'Default language \'{language}\' has no translations'
409
+ }
410
+ },
411
+ // ... rest of config
412
+ };
413
+ ```
414
+
415
+ ### Fallback Error Messages
416
+
417
+ If localized error messages aren't provided, the engine falls back to English templates:
418
+
419
+ - `Missing string collection for language: {language}`
420
+ - `Missing translation for key '{key}' in language '{language}'`
421
+ - `Default language '{language}' has no string collection`
422
+
296
423
  ## Best Practices
297
424
 
298
425
  1. **Complete Translations**: EnumTranslation requires all enum values to be translated
@@ -300,6 +427,8 @@ yarn test i18n-engine.spec.ts
300
427
  3. **Context Separation**: Use different contexts for admin and user interfaces
301
428
  4. **Instance Management**: Use named instances for different parts of your application
302
429
  5. **Error Handling**: Handle missing translations gracefully with fallback languages
430
+ 6. **Layered Extension**: Use union types when extending configurations across libraries
431
+ 7. **Default Configuration**: Use `getDefaultI18nEngine()` for standard setups
303
432
 
304
433
  ## License
305
434
 
package/dist/context.d.ts CHANGED
@@ -10,7 +10,7 @@ import { I18nContext, LanguageContext } from './types';
10
10
  * @param defaultAdminTimezone - The default admin timezone (defaults to UTC)
11
11
  * @returns A new I18nContext instance
12
12
  */
13
- export declare function createContext<TLanguage extends string, TContext extends string = LanguageContext>(defaultLanguage: TLanguage, defaultContext: TContext, defaultCurrencyCode?: CurrencyCode, defaultTimezone?: Timezone, defaultAdminTimezone?: Timezone): I18nContext<TLanguage, TContext>;
13
+ export declare function createContext<TLanguage extends string, TTranslationContext extends string = LanguageContext, TContext extends I18nContext<TLanguage, TTranslationContext> = I18nContext<TLanguage, TTranslationContext>>(defaultLanguage: TLanguage, defaultContext: TTranslationContext, defaultCurrencyCode?: CurrencyCode, defaultTimezone?: Timezone, defaultAdminTimezone?: Timezone): TContext;
14
14
  /**
15
15
  * Sets the language for the given I18n context.
16
16
  * @param context - The I18n context to modify
@@ -0,0 +1,31 @@
1
+ import { I18nEngine } from './i18n-engine';
2
+ import { Timezone } from './timezone';
3
+ import { I18nContext, LanguageCodeCollection } from './types';
4
+ export declare enum DefaultStringKey {
5
+ Common_Test = "common_test"
6
+ }
7
+ export declare enum DefaultLanguage {
8
+ EnglishUS = "English (US)",
9
+ EnglishUK = "English (UK)",
10
+ French = "Fran\u00E7ais",
11
+ MandarinChinese = "\u4E2D\u6587",
12
+ Spanish = "Espa\u00F1ol",
13
+ Ukrainian = "\u0423\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0438\u0439"
14
+ }
15
+ export declare const DefaultLanguageCodes: LanguageCodeCollection<DefaultLanguage>;
16
+ declare global {
17
+ namespace I18n {
18
+ interface Config {
19
+ StringKey: DefaultStringKey;
20
+ Language: DefaultLanguage;
21
+ LanguageCodes: LanguageCodeCollection<DefaultLanguage>;
22
+ engine: I18nEngine<I18n.Config['StringKey'], I18n.Config['Language'], Record<any, any>, string>;
23
+ }
24
+ }
25
+ }
26
+ export type StringKey = I18n.Config['StringKey'];
27
+ export type Language = I18n.Config['Language'];
28
+ export type Engine = I18n.Config['engine'];
29
+ export type LanguageCodes = I18n.Config['LanguageCodes'];
30
+ export declare const getI18nEngine: () => Engine;
31
+ export declare const getDefaultI18nEngine: <TConstants extends Record<string, any>, TTranslationContext extends string, TContext extends I18nContext<DefaultLanguage, TTranslationContext>>(constants: TConstants, timezone?: Timezone, adminTimezone?: Timezone) => I18nEngine<DefaultStringKey, DefaultLanguage, TConstants, TTranslationContext, TContext>;
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.getDefaultI18nEngine = exports.getI18nEngine = exports.DefaultLanguageCodes = exports.DefaultLanguage = exports.DefaultStringKey = void 0;
4
+ const currency_code_1 = require("./currency-code");
5
+ const i18n_engine_1 = require("./i18n-engine");
6
+ const timezone_1 = require("./timezone");
7
+ const types_1 = require("./types");
8
+ // Default enum types that can be augmented by consumers
9
+ var DefaultStringKey;
10
+ (function (DefaultStringKey) {
11
+ DefaultStringKey["Common_Test"] = "common_test";
12
+ })(DefaultStringKey || (exports.DefaultStringKey = DefaultStringKey = {}));
13
+ var DefaultLanguage;
14
+ (function (DefaultLanguage) {
15
+ DefaultLanguage["EnglishUS"] = "English (US)";
16
+ DefaultLanguage["EnglishUK"] = "English (UK)";
17
+ DefaultLanguage["French"] = "Fran\u00E7ais";
18
+ DefaultLanguage["MandarinChinese"] = "\u4E2D\u6587";
19
+ DefaultLanguage["Spanish"] = "Espa\u00F1ol";
20
+ DefaultLanguage["Ukrainian"] = "\u0423\u043A\u0440\u0430\u0457\u043D\u0441\u044C\u043A\u0438\u0439";
21
+ })(DefaultLanguage || (exports.DefaultLanguage = DefaultLanguage = {}));
22
+ exports.DefaultLanguageCodes = {
23
+ [DefaultLanguage.EnglishUS]: 'en',
24
+ [DefaultLanguage.EnglishUK]: 'en-GB',
25
+ [DefaultLanguage.French]: 'fr',
26
+ [DefaultLanguage.MandarinChinese]: 'zh-CN',
27
+ [DefaultLanguage.Spanish]: 'es',
28
+ [DefaultLanguage.Ukrainian]: 'uk',
29
+ };
30
+ // Singleton instance that uses the augmented types
31
+ const getI18nEngine = () => i18n_engine_1.I18nEngine.getInstance();
32
+ exports.getI18nEngine = getI18nEngine;
33
+ const getConfig = (constants, timezone, adminTimezone) => ({
34
+ strings: {
35
+ [DefaultLanguage.EnglishUS]: {
36
+ [DefaultStringKey.Common_Test]: 'Test',
37
+ },
38
+ [DefaultLanguage.EnglishUK]: {
39
+ [DefaultStringKey.Common_Test]: 'Test',
40
+ },
41
+ [DefaultLanguage.French]: {
42
+ [DefaultStringKey.Common_Test]: 'Test',
43
+ },
44
+ [DefaultLanguage.MandarinChinese]: {
45
+ [DefaultStringKey.Common_Test]: '测试',
46
+ },
47
+ [DefaultLanguage.Spanish]: {
48
+ [DefaultStringKey.Common_Test]: 'Prueba',
49
+ },
50
+ [DefaultLanguage.Ukrainian]: {
51
+ [DefaultStringKey.Common_Test]: 'Тест',
52
+ },
53
+ },
54
+ stringNames: Object.values(DefaultStringKey),
55
+ defaultLanguage: DefaultLanguage.EnglishUS,
56
+ defaultTranslationContext: 'user',
57
+ defaultCurrencyCode: new currency_code_1.CurrencyCode(types_1.DefaultCurrencyCode),
58
+ languageCodes: exports.DefaultLanguageCodes,
59
+ languages: Object.values(DefaultLanguage),
60
+ constants: constants,
61
+ enumName: 'DefaultStringKey',
62
+ enumObj: DefaultStringKey,
63
+ timezone: timezone ?? new timezone_1.Timezone('UTC'),
64
+ adminTimezone: adminTimezone ?? new timezone_1.Timezone('UTC'),
65
+ });
66
+ const getDefaultI18nEngine = (constants, timezone, adminTimezone) => new i18n_engine_1.I18nEngine(getConfig(constants, timezone, adminTimezone), 'user');
67
+ exports.getDefaultI18nEngine = getDefaultI18nEngine;
@@ -3,7 +3,7 @@ import { EnumLanguageTranslation, I18nConfig, I18nContext } from './types';
3
3
  /**
4
4
  * Internationalization engine class
5
5
  */
6
- export declare class I18nEngine<TStringKey extends string, TLanguage extends string, TConstants extends Record<string, any> = Record<string, any>, TContext extends string = string> {
6
+ export declare class I18nEngine<TStringKey extends string, TLanguage extends string, TConstants extends Record<string, any> = Record<string, any>, TTranslationContext extends string = string, TContext extends I18nContext<TLanguage, TTranslationContext> = I18nContext<TLanguage, TTranslationContext>> {
7
7
  /**
8
8
  * Registry for enum translations
9
9
  */
@@ -11,7 +11,7 @@ export declare class I18nEngine<TStringKey extends string, TLanguage extends str
11
11
  /**
12
12
  * Configuration for the i18n engine
13
13
  */
14
- readonly config: I18nConfig<TStringKey, TLanguage, TConstants, TContext>;
14
+ readonly config: I18nConfig<TStringKey, TLanguage, TConstants, TTranslationContext>;
15
15
  /**
16
16
  * Static instances for semi-singleton pattern
17
17
  */
@@ -38,7 +38,7 @@ export declare class I18nEngine<TStringKey extends string, TLanguage extends str
38
38
  * @param key Optional instance key for the semi-singleton pattern
39
39
  * @throws Error if an instance with the same key already exists
40
40
  */
41
- constructor(config: I18nConfig<TStringKey, TLanguage, TConstants, TContext>, key?: string);
41
+ constructor(config: I18nConfig<TStringKey, TLanguage, TConstants, TTranslationContext>, key?: string, newContext?: () => TContext);
42
42
  /**
43
43
  * Gets an instance of the I18nEngine by key. If no key is provided, the default instance is returned.
44
44
  * @param key The key of the instance to retrieve
@@ -68,12 +68,12 @@ export declare class I18nEngine<TStringKey extends string, TLanguage extends str
68
68
  * Gets the global context for translations
69
69
  * @returns The global context object
70
70
  */
71
- get context(): I18nContext<TLanguage, TContext>;
71
+ get context(): TContext;
72
72
  /**
73
73
  * Sets the global context for translations (used if no context is provided) for this instance
74
74
  * @param context The context to set
75
75
  */
76
- set context(context: Partial<I18nContext<TLanguage, TContext>>);
76
+ set context(context: Partial<TContext>);
77
77
  /**
78
78
  * Translates a string key into the specified language, replacing any variables as needed.
79
79
  * @param key The string key to translate
@@ -153,4 +153,19 @@ export declare class I18nEngine<TStringKey extends string, TLanguage extends str
153
153
  * @internal
154
154
  */
155
155
  static removeInstance(key?: string): void;
156
+ /**
157
+ * Static error message templates for validation
158
+ */
159
+ private static readonly ErrorTemplates;
160
+ /**
161
+ * Validates the configuration to ensure all languages have string collections
162
+ * and all string keys are provided for each language
163
+ * @param config The configuration to validate
164
+ * @throws Error if validation fails
165
+ */
166
+ private validateConfig;
167
+ /**
168
+ * Gets validation error message, trying translation first, falling back to template
169
+ */
170
+ private getValidationError;
156
171
  }
@@ -15,10 +15,11 @@ class I18nEngine {
15
15
  * @param key Optional instance key for the semi-singleton pattern
16
16
  * @throws Error if an instance with the same key already exists
17
17
  */
18
- constructor(config, key) {
18
+ constructor(config, key, newContext = () => (0, context_1.createContext)(config.defaultLanguage, config.defaultTranslationContext, config.defaultCurrencyCode, config.timezone, config.adminTimezone)) {
19
+ this.validateConfig(config);
19
20
  this.config = config;
20
21
  this.enumRegistry = new enum_registry_1.EnumTranslationRegistry();
21
- this._context = (0, context_1.createContext)(config.defaultLanguage, config.defaultTranslationContext, config.defaultCurrencyCode, config.timezone, config.adminTimezone);
22
+ this._context = newContext();
22
23
  const instanceKey = key || I18nEngine.DefaultInstanceKey;
23
24
  if (I18nEngine._instances.has(instanceKey)) {
24
25
  const existing = I18nEngine._instances.get(instanceKey);
@@ -247,9 +248,52 @@ class I18nEngine {
247
248
  I18nEngine._instances.delete(instanceKey);
248
249
  if (I18nEngine._defaultKey === instanceKey) {
249
250
  const nextKey = I18nEngine._instances.keys().next().value;
250
- I18nEngine._defaultKey = I18nEngine._instances.size > 0 && nextKey ? nextKey : null;
251
+ I18nEngine._defaultKey =
252
+ I18nEngine._instances.size > 0 && nextKey ? nextKey : null;
251
253
  }
252
254
  }
255
+ /**
256
+ * Validates the configuration to ensure all languages have string collections
257
+ * and all string keys are provided for each language
258
+ * @param config The configuration to validate
259
+ * @throws Error if validation fails
260
+ */
261
+ validateConfig(config) {
262
+ // Check that all languages in languageCodes have string collections
263
+ for (const language of Object.keys(config.languageCodes)) {
264
+ if (!config.strings[language]) {
265
+ throw new Error(this.getValidationError('Error_MissingStringCollectionTemplate', { language }, config));
266
+ }
267
+ // Check that all string keys are provided for this language
268
+ const strings = config.strings[language];
269
+ for (const stringKey of config.stringNames) {
270
+ if (!strings[stringKey]) {
271
+ throw new Error(this.getValidationError('Error_MissingTranslationTemplate', { key: stringKey, language }, config));
272
+ }
273
+ }
274
+ }
275
+ // Check that default language exists
276
+ if (!config.strings[config.defaultLanguage]) {
277
+ throw new Error(this.getValidationError('Error_DefaultLanguageNoCollectionTemplate', { language: config.defaultLanguage }, config));
278
+ }
279
+ }
280
+ /**
281
+ * Gets validation error message, trying translation first, falling back to template
282
+ */
283
+ getValidationError(key, vars, config) {
284
+ try {
285
+ const strings = config.strings[config.defaultLanguage];
286
+ if (strings?.[key]) {
287
+ return (0, utils_1.replaceVariables)(strings[key], vars, config.constants);
288
+ }
289
+ }
290
+ catch { }
291
+ // Fallback to static templates
292
+ const template = key.includes('MissingStringCollection') ? I18nEngine.ErrorTemplates.MissingStringCollection :
293
+ key.includes('MissingTranslation') ? I18nEngine.ErrorTemplates.MissingTranslation :
294
+ I18nEngine.ErrorTemplates.DefaultLanguageNoCollection;
295
+ return (0, utils_1.replaceVariables)(template, vars);
296
+ }
253
297
  }
254
298
  exports.I18nEngine = I18nEngine;
255
299
  /**
@@ -264,3 +308,11 @@ I18nEngine._defaultKey = null;
264
308
  * Default instance key if none is provided
265
309
  */
266
310
  I18nEngine.DefaultInstanceKey = 'default';
311
+ /**
312
+ * Static error message templates for validation
313
+ */
314
+ I18nEngine.ErrorTemplates = {
315
+ MissingStringCollection: 'Missing string collection for language: {language}',
316
+ MissingTranslation: 'Missing translation for key \'{key}\' in language \'{language}\'',
317
+ DefaultLanguageNoCollection: 'Default language \'{language}\' has no string collection'
318
+ };
package/dist/index.d.ts CHANGED
@@ -5,6 +5,7 @@ export * from './currency';
5
5
  export * from './currency-code';
6
6
  export * from './currency-format';
7
7
  export * from './enum-registry';
8
+ export * from './default-config';
8
9
  export * from './i18n-engine';
9
10
  export * from './template';
10
11
  export * from './timezone';
package/dist/index.js CHANGED
@@ -22,6 +22,7 @@ __exportStar(require("./currency"), exports);
22
22
  __exportStar(require("./currency-code"), exports);
23
23
  __exportStar(require("./currency-format"), exports);
24
24
  __exportStar(require("./enum-registry"), exports);
25
+ __exportStar(require("./default-config"), exports);
25
26
  __exportStar(require("./i18n-engine"), exports);
26
27
  __exportStar(require("./template"), exports);
27
28
  __exportStar(require("./timezone"), exports);
@@ -0,0 +1,52 @@
1
+ {
2
+ "name": "@digitaldefiance/i18n-lib",
3
+ "version": "1.0.23",
4
+ "description": "Generic i18n library with enum translation support",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "scripts": {
8
+ "build": "yarn tsc",
9
+ "test": "yarn jest --detectOpenHandles",
10
+ "lint": "eslint src/**/*.ts tests/**/*.ts",
11
+ "lint:fix": "eslint src/**/*.ts tests/**/*.ts --fix",
12
+ "prettier:check": "prettier --check 'src/**/*.{ts,tsx}' 'tests/**/*.{ts,tsx}'",
13
+ "prettier:fix": "prettier --write 'src/**/*.{ts,tsx}' 'tests/**/*.{ts,tsx}'",
14
+ "format": "yarn prettier:fix && yarn lint:fix",
15
+ "prepublishOnly": "yarn build",
16
+ "publish:public": "npm publish --access public"
17
+ },
18
+ "devDependencies": {
19
+ "@types/jest": "^29.0.0",
20
+ "@typescript-eslint/eslint-plugin": "^8.31.1",
21
+ "@typescript-eslint/parser": "^8.31.1",
22
+ "eslint": "^9.8.0",
23
+ "eslint-config-prettier": "^10.1.2",
24
+ "eslint-plugin-import": "^2.32.0",
25
+ "eslint-plugin-prettier": "^5.3.1",
26
+ "jest": "^29.0.0",
27
+ "jest-util": "^30.0.5",
28
+ "prettier": "^2.6.2",
29
+ "prettier-plugin-organize-imports": "^4.1.0",
30
+ "ts-jest": "^29.0.0",
31
+ "typescript": "^5.9.2"
32
+ },
33
+ "files": [
34
+ "dist",
35
+ "README.md"
36
+ ],
37
+ "keywords": [
38
+ "i18n",
39
+ "internationalization",
40
+ "typescript",
41
+ "enum",
42
+ "translation"
43
+ ],
44
+ "author": "Digital Defiance",
45
+ "license": "MIT",
46
+ "packageManager": "yarn@4.10.3",
47
+ "dependencies": {
48
+ "moment": "^2.30.1",
49
+ "moment-timezone": "^0.6.0"
50
+ },
51
+ "type": "commonjs"
52
+ }
@@ -0,0 +1,29 @@
1
+ import { LanguageContext } from './types';
2
+ import { Timezone } from './timezone';
3
+ import { CurrencyCode } from './currency-code';
4
+ export interface IActiveContext<TLanguage extends string> {
5
+ /**
6
+ * The default language for the user facing application
7
+ */
8
+ language: TLanguage;
9
+ /**
10
+ * The default language for the admin interface
11
+ */
12
+ adminLanguage: TLanguage;
13
+ /**
14
+ * The default currency code for the user facing application
15
+ */
16
+ currencyCode: CurrencyCode;
17
+ /**
18
+ * The default language context for the current context
19
+ */
20
+ currentContext: LanguageContext;
21
+ /**
22
+ * The default timezone for the user facing application
23
+ */
24
+ timezone: Timezone;
25
+ /**
26
+ * The default timezone for the admin interface
27
+ */
28
+ adminTimezone: Timezone;
29
+ }
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Context change management for i18n systems
3
+ */
4
+ export type ContextChangeListener<T = any> = (property: string, oldValue: T, newValue: T) => void;
5
+ /**
6
+ * Manages context changes and notifies listeners.
7
+ */
8
+ export declare class ContextManager<TContext extends Record<string, any>> {
9
+ protected listeners: ContextChangeListener[];
10
+ /**
11
+ * Adds a listener to be notified of context changes.
12
+ * @param listener - The listener function to add
13
+ */
14
+ addListener(listener: ContextChangeListener): void;
15
+ /**
16
+ * Removes a listener from the notification list.
17
+ * @param listener - The listener function to remove
18
+ */
19
+ removeListener(listener: ContextChangeListener): void;
20
+ /**
21
+ * Notifies all listeners of a context change.
22
+ * @param property - The property that changed
23
+ * @param oldValue - The old value of the property
24
+ * @param newValue - The new value of the property
25
+ */
26
+ notifyChange<K extends keyof TContext>(property: K, oldValue: TContext[K], newValue: TContext[K]): void;
27
+ /**
28
+ * Creates a proxy for the given context to automatically notify listeners on changes.
29
+ * @param context - The context object to proxy
30
+ * @returns A proxied version of the context object
31
+ */
32
+ createProxy(context: TContext): TContext;
33
+ }
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ContextManager = void 0;
4
+ /**
5
+ * Manages context changes and notifies listeners.
6
+ */
7
+ class ContextManager {
8
+ constructor() {
9
+ this.listeners = [];
10
+ }
11
+ /**
12
+ * Adds a listener to be notified of context changes.
13
+ * @param listener - The listener function to add
14
+ */
15
+ addListener(listener) {
16
+ this.listeners.push(listener);
17
+ }
18
+ /**
19
+ * Removes a listener from the notification list.
20
+ * @param listener - The listener function to remove
21
+ */
22
+ removeListener(listener) {
23
+ const index = this.listeners.indexOf(listener);
24
+ if (index > -1) {
25
+ this.listeners.splice(index, 1);
26
+ }
27
+ }
28
+ /**
29
+ * Notifies all listeners of a context change.
30
+ * @param property - The property that changed
31
+ * @param oldValue - The old value of the property
32
+ * @param newValue - The new value of the property
33
+ */
34
+ notifyChange(property, oldValue, newValue) {
35
+ this.listeners.forEach((listener) => {
36
+ try {
37
+ listener(property, oldValue, newValue);
38
+ }
39
+ catch (error) {
40
+ console.error('Error in context change listener:', error);
41
+ }
42
+ });
43
+ }
44
+ /**
45
+ * Creates a proxy for the given context to automatically notify listeners on changes.
46
+ * @param context - The context object to proxy
47
+ * @returns A proxied version of the context object
48
+ */
49
+ createProxy(context) {
50
+ const manager = this;
51
+ return new Proxy(context, {
52
+ set(target, property, value) {
53
+ const oldValue = target[property];
54
+ target[property] = value;
55
+ manager.notifyChange(property, oldValue, value);
56
+ return true;
57
+ },
58
+ });
59
+ }
60
+ }
61
+ exports.ContextManager = ContextManager;