@digitaldefiance/i18n-lib 1.0.13 → 1.0.14

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.
@@ -18,7 +18,7 @@ class ContextManager {
18
18
  }
19
19
  }
20
20
  notifyChange(property, oldValue, newValue) {
21
- this.listeners.forEach(listener => {
21
+ this.listeners.forEach((listener) => {
22
22
  try {
23
23
  listener(property, oldValue, newValue);
24
24
  }
@@ -35,7 +35,7 @@ class ContextManager {
35
35
  target[property] = value;
36
36
  manager.notifyChange(property, oldValue, value);
37
37
  return true;
38
- }
38
+ },
39
39
  });
40
40
  }
41
41
  }
package/dist/context.js CHANGED
@@ -8,7 +8,7 @@ function createContext(defaultLanguage) {
8
8
  return {
9
9
  language: defaultLanguage,
10
10
  adminLanguage: defaultLanguage,
11
- currentContext: 'user'
11
+ currentContext: 'user',
12
12
  };
13
13
  }
14
14
  function setLanguage(context, language) {
@@ -21,7 +21,7 @@ class EnumTranslationRegistry {
21
21
  }
22
22
  let result = langTranslations[value];
23
23
  if (!result && typeof value === 'number') {
24
- const stringKey = Object.keys(enumObj).find(key => enumObj[key] === value);
24
+ const stringKey = Object.keys(enumObj).find((key) => enumObj[key] === value);
25
25
  if (stringKey) {
26
26
  result = langTranslations[stringKey];
27
27
  }
@@ -1,13 +1,132 @@
1
- import { I18nConfig, I18nContext, LanguageContext, EnumLanguageTranslation } from './types';
2
- export declare class I18nEngine<TStringKey extends string, TLanguage extends string, TConstants = Record<string, string | number>, TContext extends string = LanguageContext> {
3
- private config;
4
- private enumRegistry;
5
- constructor(config: I18nConfig<TStringKey, TLanguage, TConstants>);
6
- translate(key: TStringKey, context: I18nContext<TLanguage, TContext>, vars?: Record<string, string | number>, language?: TLanguage, fallbackLanguage?: TLanguage): string;
1
+ import { EnumTranslationRegistry } from './enum-registry';
2
+ import { EnumLanguageTranslation, I18nConfig, I18nContext } from './types';
3
+ export declare class I18nEngine<TStringKey extends string, TLanguage extends string, TConstants extends Record<string, any> = Record<string, any>, TContext extends string = string> {
4
+ /**
5
+ * Registry for enum translations
6
+ */
7
+ protected enumRegistry: EnumTranslationRegistry<TLanguage>;
8
+ /**
9
+ * Configuration for the i18n engine
10
+ */
11
+ protected config: I18nConfig<TStringKey, TLanguage, TConstants>;
12
+ /**
13
+ * Static instances for semi-singleton pattern
14
+ */
15
+ private static _instances;
16
+ /**
17
+ * Default instance key (first created instance)
18
+ */
19
+ private static _defaultKey;
20
+ /**
21
+ * Default instance key if none is provided
22
+ */
23
+ protected static readonly DefaultInstanceKey = "default";
24
+ /**
25
+ * Global context for translations (used if no context is provided) for this instance
26
+ */
27
+ private _context;
28
+ /**
29
+ * Default template processor instance
30
+ */
31
+ readonly t: (key: TStringKey, vars?: Record<string, string | number>, language?: TLanguage) => string;
32
+ /**
33
+ * Creates a new I18nEngine instance
34
+ * @param config The i18n configuration
35
+ * @param defaultContext The default context for translations
36
+ * @param key Optional instance key for the semi-singleton pattern
37
+ * @throws Error if an instance with the same key already exists
38
+ */
39
+ constructor(config: I18nConfig<TStringKey, TLanguage, TConstants>, defaultContext: TContext, key?: string);
40
+ /**
41
+ * Gets an instance of the I18nEngine by key. If no key is provided, the default instance is returned.
42
+ * @param key The key of the instance to retrieve
43
+ * @returns The I18nEngine instance
44
+ * @throws Error if the instance with the provided key does not exist
45
+ */
46
+ static getInstance<T extends I18nEngine<any, any, any, any>>(key?: string): T;
47
+ /**
48
+ * Gets a translation for the provided error key using the specified instance (or default instance if none is provided).
49
+ * @param errorKey The error key to translate
50
+ * @param vars Variables to replace in the translation string
51
+ * @param instanceKey The key of the I18nEngine instance to use
52
+ * @param language The language to translate to
53
+ * @param fallbackLanguage The fallback language if the translation is not found
54
+ * @returns The translated error message
55
+ */
56
+ static getErrorMessage(errorKey: string, vars?: Record<string, string | number>, instanceKey?: string, language?: string, fallbackLanguage?: string): string;
57
+ /**
58
+ * Throws an error with a translated message using the specified instance (or default instance if none is provided).
59
+ * @param errorKey The error key to translate
60
+ * @param vars Variables to replace in the translation string
61
+ * @param instanceKey The key of the I18nEngine instance to use
62
+ * @throws Error with translated message
63
+ */
64
+ static throwError(errorKey: string, vars?: Record<string, string | number>, instanceKey?: string): never;
65
+ /**
66
+ * Gets the global context for translations
67
+ * @returns The global context object
68
+ */
69
+ get context(): I18nContext<TLanguage, TContext>;
70
+ /**
71
+ * Sets the global context for translations (used if no context is provided) for this instance
72
+ * @param context The context to set
73
+ */
74
+ set context(context: Partial<I18nContext<TLanguage, TContext>>);
75
+ /**
76
+ * Translates a string key into the specified language, replacing any variables as needed.
77
+ * @param key The string key to translate
78
+ * @param vars Variables to replace in the translation string
79
+ * @param language The language to translate to
80
+ * @param fallbackLanguage The fallback language if the translation is not found
81
+ * @returns The translated string
82
+ */
83
+ translate(key: TStringKey, vars?: Record<string, string | number>, language?: TLanguage, fallbackLanguage?: TLanguage): string;
84
+ /**
85
+ * Translates an enumeration value into the specified language.
86
+ * @param enumObj The enumeration object
87
+ * @param value The enumeration value to translate
88
+ * @param language The language to translate to
89
+ * @returns The translated enumeration value
90
+ */
7
91
  translateEnum<TEnum extends string | number>(enumObj: Record<string, TEnum>, value: TEnum, language: TLanguage): string;
92
+ /**
93
+ * Registers an enumeration and its translations with the engine.
94
+ * @param enumObj The enumeration object
95
+ * @param translations The translations for the enumeration
96
+ * @param enumName The name of the enumeration (for error messages)
97
+ */
8
98
  registerEnum<TEnum extends string | number>(enumObj: Record<string, TEnum> | Record<string | number, string | number>, translations: EnumLanguageTranslation<TEnum, TLanguage>, enumName: string): void;
99
+ /**
100
+ * Safe translation that prevents infinite recursion for error messages
101
+ * @param key The string key to translate
102
+ * @param vars Variables to replace in the translation string
103
+ * @param language The language to translate to
104
+ * @returns The translated string or the key if translation fails
105
+ */
106
+ private safeTranslate;
107
+ /**
108
+ * Retrieves the string for the given language and key, throwing an error if not found.
109
+ * @param language The language to get the string for
110
+ * @param key The string key to retrieve
111
+ * @returns The string value
112
+ * @throws Error if the language or string key is not found
113
+ */
9
114
  private getString;
115
+ /**
116
+ * Gets the language code for the specified language.
117
+ * @param language The language to get the code for
118
+ * @returns The language code
119
+ */
10
120
  getLanguageCode(language: TLanguage): string;
121
+ /**
122
+ * Gets the language for the specified language code.
123
+ * @param code The language code to look up
124
+ * @returns The language, or undefined if not found
125
+ */
11
126
  getLanguageFromCode(code: string): TLanguage | undefined;
127
+ /**
128
+ * Gets all language codes.
129
+ * @returns A record of all language codes
130
+ */
12
131
  getAllLanguageCodes(): Record<TLanguage, string>;
13
132
  }
@@ -2,15 +2,108 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.I18nEngine = void 0;
4
4
  const enum_registry_1 = require("./enum-registry");
5
+ const template_1 = require("./template");
5
6
  const utils_1 = require("./utils");
6
7
  class I18nEngine {
7
- constructor(config) {
8
+ /**
9
+ * Creates a new I18nEngine instance
10
+ * @param config The i18n configuration
11
+ * @param defaultContext The default context for translations
12
+ * @param key Optional instance key for the semi-singleton pattern
13
+ * @throws Error if an instance with the same key already exists
14
+ */
15
+ constructor(config, defaultContext, key) {
8
16
  this.config = config;
9
17
  this.enumRegistry = new enum_registry_1.EnumTranslationRegistry();
18
+ this._context = {
19
+ language: config.defaultLanguage,
20
+ adminLanguage: config.defaultLanguage,
21
+ currentContext: defaultContext,
22
+ };
23
+ const instanceKey = key || I18nEngine.DefaultInstanceKey;
24
+ if (I18nEngine._instances.has(instanceKey)) {
25
+ const existing = I18nEngine._instances.get(instanceKey);
26
+ throw new Error(existing.translate('Error_InstanceAlreadyExistsTemplate', {
27
+ key: instanceKey,
28
+ }));
29
+ }
30
+ I18nEngine._instances.set(instanceKey, this);
31
+ if (!I18nEngine._defaultKey) {
32
+ I18nEngine._defaultKey = instanceKey;
33
+ }
34
+ // Initialize the default template processor
35
+ this.t = (0, template_1.createTemplateProcessor)(this.config.enumObj || {}, (key, vars, language) => this.translate(key, vars, language), this.config.enumName || 'StringKey');
36
+ }
37
+ /**
38
+ * Gets an instance of the I18nEngine by key. If no key is provided, the default instance is returned.
39
+ * @param key The key of the instance to retrieve
40
+ * @returns The I18nEngine instance
41
+ * @throws Error if the instance with the provided key does not exist
42
+ */
43
+ static getInstance(key) {
44
+ const instanceKey = key || I18nEngine._defaultKey || I18nEngine.DefaultInstanceKey;
45
+ if (!instanceKey || !I18nEngine._instances.has(instanceKey)) {
46
+ throw new Error(I18nEngine.getErrorMessage('Error_InstanceNotFoundTemplate', {
47
+ key: instanceKey,
48
+ }));
49
+ }
50
+ return I18nEngine._instances.get(instanceKey);
51
+ }
52
+ /**
53
+ * Gets a translation for the provided error key using the specified instance (or default instance if none is provided).
54
+ * @param errorKey The error key to translate
55
+ * @param vars Variables to replace in the translation string
56
+ * @param instanceKey The key of the I18nEngine instance to use
57
+ * @param language The language to translate to
58
+ * @param fallbackLanguage The fallback language if the translation is not found
59
+ * @returns The translated error message
60
+ */
61
+ static getErrorMessage(errorKey, vars, instanceKey, language, fallbackLanguage) {
62
+ try {
63
+ const instance = I18nEngine.getInstance(instanceKey);
64
+ return instance.translate(errorKey, vars, language, fallbackLanguage);
65
+ }
66
+ catch {
67
+ return `${errorKey}: ${JSON.stringify(vars || {})}`;
68
+ }
69
+ }
70
+ /**
71
+ * Throws an error with a translated message using the specified instance (or default instance if none is provided).
72
+ * @param errorKey The error key to translate
73
+ * @param vars Variables to replace in the translation string
74
+ * @param instanceKey The key of the I18nEngine instance to use
75
+ * @throws Error with translated message
76
+ */
77
+ static throwError(errorKey, vars, instanceKey) {
78
+ throw new Error(I18nEngine.getErrorMessage(errorKey, vars, instanceKey));
79
+ }
80
+ /**
81
+ * Gets the global context for translations
82
+ * @returns The global context object
83
+ */
84
+ get context() {
85
+ return this._context;
10
86
  }
11
- translate(key, context, vars, language, fallbackLanguage) {
87
+ /**
88
+ * Sets the global context for translations (used if no context is provided) for this instance
89
+ * @param context The context to set
90
+ */
91
+ set context(context) {
92
+ this._context = { ...this._context, ...context };
93
+ }
94
+ /**
95
+ * Translates a string key into the specified language, replacing any variables as needed.
96
+ * @param key The string key to translate
97
+ * @param vars Variables to replace in the translation string
98
+ * @param language The language to translate to
99
+ * @param fallbackLanguage The fallback language if the translation is not found
100
+ * @returns The translated string
101
+ */
102
+ translate(key, vars, language, fallbackLanguage) {
12
103
  const lang = language ??
13
- (context.currentContext === 'admin' ? context.adminLanguage : context.language);
104
+ (this._context.currentContext === 'admin'
105
+ ? this._context.adminLanguage
106
+ : this._context.language);
14
107
  const fallback = fallbackLanguage ?? this.config.defaultLanguage;
15
108
  try {
16
109
  const stringValue = this.getString(lang, key);
@@ -33,26 +126,80 @@ class I18nEngine {
33
126
  return key;
34
127
  }
35
128
  }
129
+ /**
130
+ * Translates an enumeration value into the specified language.
131
+ * @param enumObj The enumeration object
132
+ * @param value The enumeration value to translate
133
+ * @param language The language to translate to
134
+ * @returns The translated enumeration value
135
+ */
36
136
  translateEnum(enumObj, value, language) {
37
137
  return this.enumRegistry.translate(enumObj, value, language);
38
138
  }
139
+ /**
140
+ * Registers an enumeration and its translations with the engine.
141
+ * @param enumObj The enumeration object
142
+ * @param translations The translations for the enumeration
143
+ * @param enumName The name of the enumeration (for error messages)
144
+ */
39
145
  registerEnum(enumObj, translations, enumName) {
40
146
  this.enumRegistry.register(enumObj, translations, enumName);
41
147
  }
148
+ /**
149
+ * Safe translation that prevents infinite recursion for error messages
150
+ * @param key The string key to translate
151
+ * @param vars Variables to replace in the translation string
152
+ * @param language The language to translate to
153
+ * @returns The translated string or the key if translation fails
154
+ */
155
+ safeTranslate(key, vars, language) {
156
+ try {
157
+ const lang = language ?? this.config.defaultLanguage;
158
+ const strings = this.config.strings[lang];
159
+ if (!strings?.[key])
160
+ return key;
161
+ const stringValue = strings[key];
162
+ return (0, utils_1.isTemplate)(key)
163
+ ? (0, utils_1.replaceVariables)(stringValue, vars, this.config.constants)
164
+ : stringValue;
165
+ }
166
+ catch {
167
+ return key;
168
+ }
169
+ }
170
+ /**
171
+ * Retrieves the string for the given language and key, throwing an error if not found.
172
+ * @param language The language to get the string for
173
+ * @param key The string key to retrieve
174
+ * @returns The string value
175
+ * @throws Error if the language or string key is not found
176
+ */
42
177
  getString(language, key) {
43
178
  const strings = this.config.strings[language];
44
179
  if (!strings) {
45
- throw new Error(`Language not found: ${language}`);
180
+ throw new Error(this.safeTranslate('Error_LanguageNotFound', { language }) ||
181
+ `Language not found: ${language}`);
46
182
  }
47
183
  const value = strings[key];
48
184
  if (!value) {
49
- throw new Error(`String not found: ${key}`);
185
+ throw new Error(this.safeTranslate('Error_StringNotFound', { key }) ||
186
+ `String not found: ${key}`);
50
187
  }
51
188
  return value;
52
189
  }
190
+ /**
191
+ * Gets the language code for the specified language.
192
+ * @param language The language to get the code for
193
+ * @returns The language code
194
+ */
53
195
  getLanguageCode(language) {
54
196
  return this.config.languageCodes[language] || language;
55
197
  }
198
+ /**
199
+ * Gets the language for the specified language code.
200
+ * @param code The language code to look up
201
+ * @returns The language, or undefined if not found
202
+ */
56
203
  getLanguageFromCode(code) {
57
204
  for (const [lang, langCode] of Object.entries(this.config.languageCodes)) {
58
205
  if (langCode === code) {
@@ -61,8 +208,24 @@ class I18nEngine {
61
208
  }
62
209
  return undefined;
63
210
  }
211
+ /**
212
+ * Gets all language codes.
213
+ * @returns A record of all language codes
214
+ */
64
215
  getAllLanguageCodes() {
65
216
  return this.config.languageCodes;
66
217
  }
67
218
  }
68
219
  exports.I18nEngine = I18nEngine;
220
+ /**
221
+ * Static instances for semi-singleton pattern
222
+ */
223
+ I18nEngine._instances = new Map();
224
+ /**
225
+ * Default instance key (first created instance)
226
+ */
227
+ I18nEngine._defaultKey = null;
228
+ /**
229
+ * Default instance key if none is provided
230
+ */
231
+ I18nEngine.DefaultInstanceKey = 'default';
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
- export * from './types';
2
- export * from './i18n-engine';
3
- export * from './enum-registry';
4
1
  export * from './context';
5
- export * from './utils';
6
- export * from './template';
7
2
  export * from './context-manager';
8
3
  export * from './currency';
4
+ export * from './enum-registry';
5
+ export * from './i18n-engine';
6
+ export * from './template';
7
+ export * from './types';
8
+ export * from './utils';
9
9
  export { I18nEngine as I18n } from './i18n-engine';
package/dist/index.js CHANGED
@@ -15,14 +15,14 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
17
  exports.I18n = void 0;
18
- __exportStar(require("./types"), exports);
19
- __exportStar(require("./i18n-engine"), exports);
20
- __exportStar(require("./enum-registry"), exports);
21
18
  __exportStar(require("./context"), exports);
22
- __exportStar(require("./utils"), exports);
23
- __exportStar(require("./template"), exports);
24
19
  __exportStar(require("./context-manager"), exports);
25
20
  __exportStar(require("./currency"), exports);
21
+ __exportStar(require("./enum-registry"), exports);
22
+ __exportStar(require("./i18n-engine"), exports);
23
+ __exportStar(require("./template"), exports);
24
+ __exportStar(require("./types"), exports);
25
+ __exportStar(require("./utils"), exports);
26
26
  // Re-export for convenience
27
27
  var i18n_engine_1 = require("./i18n-engine");
28
28
  Object.defineProperty(exports, "I18n", { enumerable: true, get: function () { return i18n_engine_1.I18nEngine; } });
package/dist/template.js CHANGED
@@ -17,7 +17,9 @@ function createTemplateProcessor(enumObj, translateFn, enumName) {
17
17
  if (!enumValue) {
18
18
  return match; // Return original if enum key not found
19
19
  }
20
- const needsVars = enumValue.toLowerCase().endsWith('template');
20
+ const needsVars = enumValue
21
+ .toLowerCase()
22
+ .endsWith('template');
21
23
  const vars = needsVars ? otherVars[varIndex++] ?? {} : {};
22
24
  return translateFn(enumValue, vars, language);
23
25
  });
package/dist/types.d.ts CHANGED
@@ -4,11 +4,13 @@ export type StringsCollection<TStringKey extends string> = Partial<Record<TStrin
4
4
  export type MasterStringsCollection<TStringKey extends string, TLanguage extends string> = Partial<Record<TLanguage, StringsCollection<TStringKey>>>;
5
5
  export type LanguageCodeCollection<TLanguage extends string> = Partial<Record<TLanguage, string>>;
6
6
  export type EnumTranslationMap<TEnum extends string | number, TLanguage extends string> = Partial<Record<TLanguage, Partial<Record<TEnum, string>>>>;
7
- export interface I18nConfig<TStringKey extends string, TLanguage extends string, TConstants = Record<string, string | number>> {
7
+ export interface I18nConfig<TStringKey extends string, TLanguage extends string, TConstants extends Record<string, any> = Record<string, any>> {
8
8
  strings: MasterStringsCollection<TStringKey, TLanguage>;
9
9
  defaultLanguage: TLanguage;
10
10
  languageCodes: LanguageCodeCollection<TLanguage>;
11
11
  constants?: TConstants;
12
+ enumName?: string;
13
+ enumObj?: Record<string, TStringKey>;
12
14
  }
13
15
  export interface I18nContext<TLanguage extends string, TContext extends string = LanguageContext> {
14
16
  language: TLanguage;
package/package.json CHANGED
@@ -1,19 +1,31 @@
1
1
  {
2
2
  "name": "@digitaldefiance/i18n-lib",
3
- "version": "1.0.13",
3
+ "version": "1.0.14",
4
4
  "description": "Generic i18n library with enum translation support",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
7
7
  "scripts": {
8
8
  "build": "yarn tsc",
9
9
  "test": "yarn jest",
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",
10
15
  "prepublishOnly": "yarn build",
11
16
  "publish:public": "npm publish --access public"
12
17
  },
13
18
  "devDependencies": {
14
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-prettier": "^5.3.1",
15
25
  "jest": "^29.0.0",
16
26
  "jest-util": "^30.0.5",
27
+ "prettier": "^2.6.2",
28
+ "prettier-plugin-organize-imports": "^4.1.0",
17
29
  "ts-jest": "^29.0.0",
18
30
  "typescript": "^5.9.2"
19
31
  },