@digitaldefiance/i18n-lib 1.0.23 → 1.0.25
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 +123 -0
- package/dist/default-config.d.ts +31 -0
- package/dist/default-config.js +67 -0
- package/dist/i18n-engine.d.ts +15 -0
- package/dist/i18n-engine.js +51 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/package.json +52 -0
- package/dist/src/active-context.d.ts +29 -0
- package/dist/src/active-context.js +2 -0
- package/dist/src/context-manager.d.ts +33 -0
- package/dist/src/context-manager.js +61 -0
- package/dist/src/context.d.ts +43 -0
- package/dist/src/context.js +69 -0
- package/dist/src/currency-code.d.ts +19 -0
- package/dist/src/currency-code.js +36 -0
- package/dist/src/currency-format.d.ts +10 -0
- package/dist/src/currency-format.js +2 -0
- package/dist/src/currency.d.ts +11 -0
- package/dist/src/currency.js +48 -0
- package/dist/src/enum-registry.d.ts +35 -0
- package/dist/src/enum-registry.js +67 -0
- package/dist/src/i18n-engine.d.ts +156 -0
- package/dist/src/i18n-engine.js +267 -0
- package/dist/src/index.d.ts +13 -0
- package/dist/src/index.js +32 -0
- package/dist/src/template.d.ts +12 -0
- package/dist/src/template.js +30 -0
- package/dist/src/timezone.d.ts +11 -0
- package/dist/src/timezone.js +22 -0
- package/dist/src/types.d.ts +78 -0
- package/dist/src/types.js +14 -0
- package/dist/src/utils.d.ts +41 -0
- package/dist/src/utils.js +85 -0
- package/dist/utils.d.ts +3 -3
- package/dist/utils.js +11 -7
- package/package.json +1 -1
- package/dist/global-active-context.d.ts +0 -22
- package/dist/global-active-context.js +0 -73
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
|
|
@@ -299,6 +302,124 @@ yarn test enum-registry.spec.ts
|
|
|
299
302
|
yarn test i18n-engine.spec.ts
|
|
300
303
|
```
|
|
301
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
|
+
|
|
302
423
|
## Best Practices
|
|
303
424
|
|
|
304
425
|
1. **Complete Translations**: EnumTranslation requires all enum values to be translated
|
|
@@ -306,6 +427,8 @@ yarn test i18n-engine.spec.ts
|
|
|
306
427
|
3. **Context Separation**: Use different contexts for admin and user interfaces
|
|
307
428
|
4. **Instance Management**: Use named instances for different parts of your application
|
|
308
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
|
|
309
432
|
|
|
310
433
|
## License
|
|
311
434
|
|
|
@@ -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;
|
package/dist/i18n-engine.d.ts
CHANGED
|
@@ -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
|
}
|
package/dist/i18n-engine.js
CHANGED
|
@@ -16,6 +16,7 @@ class I18nEngine {
|
|
|
16
16
|
* @throws Error if an instance with the same key already exists
|
|
17
17
|
*/
|
|
18
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
22
|
this._context = newContext();
|
|
@@ -251,6 +252,48 @@ class I18nEngine {
|
|
|
251
252
|
I18nEngine._instances.size > 0 && nextKey ? nextKey : null;
|
|
252
253
|
}
|
|
253
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
|
+
}
|
|
254
297
|
}
|
|
255
298
|
exports.I18nEngine = I18nEngine;
|
|
256
299
|
/**
|
|
@@ -265,3 +308,11 @@ I18nEngine._defaultKey = null;
|
|
|
265
308
|
* Default instance key if none is provided
|
|
266
309
|
*/
|
|
267
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
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,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;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { CurrencyCode } from './currency-code';
|
|
2
|
+
import { Timezone } from './timezone';
|
|
3
|
+
import { I18nContext, LanguageContext } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* Creates a new I18n context with default values.
|
|
6
|
+
* @param defaultLanguage - The default language for the context
|
|
7
|
+
* @param defaultContext - The default language context
|
|
8
|
+
* @param defaultCurrencyCode - The default currency code (defaults to USD)
|
|
9
|
+
* @param defaultTimezone - The default timezone (defaults to UTC)
|
|
10
|
+
* @param defaultAdminTimezone - The default admin timezone (defaults to UTC)
|
|
11
|
+
* @returns A new I18nContext instance
|
|
12
|
+
*/
|
|
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
|
+
/**
|
|
15
|
+
* Sets the language for the given I18n context.
|
|
16
|
+
* @param context - The I18n context to modify
|
|
17
|
+
* @param language - The language to set
|
|
18
|
+
*/
|
|
19
|
+
export declare function setLanguage<TLanguage extends string, TContext extends string = LanguageContext>(context: I18nContext<TLanguage, TContext>, language: TLanguage): void;
|
|
20
|
+
/**
|
|
21
|
+
* Sets the admin language for the given I18n context.
|
|
22
|
+
* @param context - The I18n context to modify
|
|
23
|
+
* @param language - The admin language to set
|
|
24
|
+
*/
|
|
25
|
+
export declare function setAdminLanguage<TLanguage extends string, TContext extends string = LanguageContext>(context: I18nContext<TLanguage, TContext>, language: TLanguage): void;
|
|
26
|
+
/**
|
|
27
|
+
* Sets the current context for the given I18n context.
|
|
28
|
+
* @param context - The I18n context to modify
|
|
29
|
+
* @param languageContext - The language context to set
|
|
30
|
+
*/
|
|
31
|
+
export declare function setContext<TLanguage extends string, TContext extends string = LanguageContext>(context: I18nContext<TLanguage, TContext>, languageContext: TContext): void;
|
|
32
|
+
/**
|
|
33
|
+
* Sets the timezone for the given I18n context.
|
|
34
|
+
* @param context - The I18n context to modify
|
|
35
|
+
* @param timezone - The timezone to set
|
|
36
|
+
*/
|
|
37
|
+
export declare function setTimezone<TLanguage extends string, TContext extends string = LanguageContext>(context: I18nContext<TLanguage, TContext>, timezone: Timezone): void;
|
|
38
|
+
/**
|
|
39
|
+
* Sets the admin timezone for the given I18n context.
|
|
40
|
+
* @param context - The I18n context to modify
|
|
41
|
+
* @param timezone - The admin timezone to set
|
|
42
|
+
*/
|
|
43
|
+
export declare function setAdminTimezone<TLanguage extends string, TContext extends string = LanguageContext>(context: I18nContext<TLanguage, TContext>, timezone: Timezone): void;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createContext = createContext;
|
|
4
|
+
exports.setLanguage = setLanguage;
|
|
5
|
+
exports.setAdminLanguage = setAdminLanguage;
|
|
6
|
+
exports.setContext = setContext;
|
|
7
|
+
exports.setTimezone = setTimezone;
|
|
8
|
+
exports.setAdminTimezone = setAdminTimezone;
|
|
9
|
+
const currency_code_1 = require("./currency-code");
|
|
10
|
+
const timezone_1 = require("./timezone");
|
|
11
|
+
/**
|
|
12
|
+
* Creates a new I18n context with default values.
|
|
13
|
+
* @param defaultLanguage - The default language for the context
|
|
14
|
+
* @param defaultContext - The default language context
|
|
15
|
+
* @param defaultCurrencyCode - The default currency code (defaults to USD)
|
|
16
|
+
* @param defaultTimezone - The default timezone (defaults to UTC)
|
|
17
|
+
* @param defaultAdminTimezone - The default admin timezone (defaults to UTC)
|
|
18
|
+
* @returns A new I18nContext instance
|
|
19
|
+
*/
|
|
20
|
+
function createContext(defaultLanguage, defaultContext, defaultCurrencyCode = new currency_code_1.CurrencyCode('USD'), defaultTimezone = new timezone_1.Timezone('UTC'), defaultAdminTimezone = new timezone_1.Timezone('UTC')) {
|
|
21
|
+
return {
|
|
22
|
+
language: defaultLanguage,
|
|
23
|
+
adminLanguage: defaultLanguage,
|
|
24
|
+
currencyCode: defaultCurrencyCode,
|
|
25
|
+
currentContext: defaultContext,
|
|
26
|
+
timezone: defaultTimezone,
|
|
27
|
+
adminTimezone: defaultAdminTimezone,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Sets the language for the given I18n context.
|
|
32
|
+
* @param context - The I18n context to modify
|
|
33
|
+
* @param language - The language to set
|
|
34
|
+
*/
|
|
35
|
+
function setLanguage(context, language) {
|
|
36
|
+
context.language = language;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Sets the admin language for the given I18n context.
|
|
40
|
+
* @param context - The I18n context to modify
|
|
41
|
+
* @param language - The admin language to set
|
|
42
|
+
*/
|
|
43
|
+
function setAdminLanguage(context, language) {
|
|
44
|
+
context.adminLanguage = language;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Sets the current context for the given I18n context.
|
|
48
|
+
* @param context - The I18n context to modify
|
|
49
|
+
* @param languageContext - The language context to set
|
|
50
|
+
*/
|
|
51
|
+
function setContext(context, languageContext) {
|
|
52
|
+
context.currentContext = languageContext;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Sets the timezone for the given I18n context.
|
|
56
|
+
* @param context - The I18n context to modify
|
|
57
|
+
* @param timezone - The timezone to set
|
|
58
|
+
*/
|
|
59
|
+
function setTimezone(context, timezone) {
|
|
60
|
+
context.timezone = timezone;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Sets the admin timezone for the given I18n context.
|
|
64
|
+
* @param context - The I18n context to modify
|
|
65
|
+
* @param timezone - The admin timezone to set
|
|
66
|
+
*/
|
|
67
|
+
function setAdminTimezone(context, timezone) {
|
|
68
|
+
context.adminTimezone = timezone;
|
|
69
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Class representing a validated currency code.
|
|
3
|
+
*/
|
|
4
|
+
export declare class CurrencyCode {
|
|
5
|
+
private _value;
|
|
6
|
+
/**
|
|
7
|
+
* Gets the currency code value.
|
|
8
|
+
*/
|
|
9
|
+
get value(): string;
|
|
10
|
+
/**
|
|
11
|
+
* Sets the currency code value after validating it.
|
|
12
|
+
*/
|
|
13
|
+
set value(value: string);
|
|
14
|
+
/**
|
|
15
|
+
* Gets the list of all valid currency codes.
|
|
16
|
+
*/
|
|
17
|
+
static get values(): string[];
|
|
18
|
+
constructor(value?: string);
|
|
19
|
+
}
|