@digitaldefiance/i18n-lib 1.3.12 → 1.3.13

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 (148) hide show
  1. package/README.md +4 -0
  2. package/package.json +12 -27
  3. package/src/active-context.ts +30 -0
  4. package/src/component-definition.ts +11 -0
  5. package/src/component-registration.ts +13 -0
  6. package/src/component-registry.ts +392 -0
  7. package/src/context-error-type.ts +3 -0
  8. package/src/context-error.ts +16 -0
  9. package/src/context-manager.ts +71 -0
  10. package/src/context.ts +90 -0
  11. package/src/core-i18n.ts +609 -0
  12. package/src/core-string-key.ts +49 -0
  13. package/src/create-translation-adapter.ts +47 -0
  14. package/src/currency-code.ts +35 -0
  15. package/{dist/currency-format.d.ts → src/currency-format.ts} +5 -4
  16. package/src/currency.ts +52 -0
  17. package/src/default-config.ts +199 -0
  18. package/src/enum-registry.ts +138 -0
  19. package/src/global-active-context.ts +255 -0
  20. package/src/handleable.ts +79 -0
  21. package/src/i-global-active-context.ts +59 -0
  22. package/src/i-handleable-error-options.ts +6 -0
  23. package/src/i-handleable.ts +5 -0
  24. package/src/i18n-config.ts +29 -0
  25. package/{dist/i18n-context.d.ts → src/i18n-context.ts} +7 -6
  26. package/src/i18n-engine.ts +491 -0
  27. package/{dist/index.d.ts → src/index.ts} +10 -1
  28. package/{dist/language-codes.d.ts → src/language-codes.ts} +23 -11
  29. package/src/language-definition.ts +13 -0
  30. package/src/language-registry.ts +292 -0
  31. package/src/plugin-i18n-engine.ts +520 -0
  32. package/src/plugin-translatable-generic-error.ts +106 -0
  33. package/src/plugin-translatable-handleable-generic.ts +60 -0
  34. package/src/plugin-typed-handleable.ts +77 -0
  35. package/src/registry-config.ts +15 -0
  36. package/src/registry-error-type.ts +12 -0
  37. package/src/registry-error.ts +74 -0
  38. package/src/strict-types.ts +35 -0
  39. package/src/template.ts +63 -0
  40. package/src/timezone.ts +20 -0
  41. package/src/translatable.ts +15 -0
  42. package/src/translation-engine.ts +8 -0
  43. package/src/translation-request.ts +12 -0
  44. package/src/translation-response.ts +8 -0
  45. package/src/typed-error.ts +384 -0
  46. package/src/typed-handleable.ts +70 -0
  47. package/{dist/types.d.ts → src/types.ts} +75 -20
  48. package/src/unified-translator.ts +96 -0
  49. package/src/utils.ts +213 -0
  50. package/src/validation-config.ts +11 -0
  51. package/src/validation-result.ts +12 -0
  52. package/dist/active-context.d.ts +0 -29
  53. package/dist/active-context.js +0 -2
  54. package/dist/component-definition.d.ts +0 -11
  55. package/dist/component-definition.js +0 -2
  56. package/dist/component-registration.d.ts +0 -9
  57. package/dist/component-registration.js +0 -2
  58. package/dist/component-registry.d.ts +0 -68
  59. package/dist/component-registry.js +0 -245
  60. package/dist/context-error-type.d.ts +0 -3
  61. package/dist/context-error-type.js +0 -7
  62. package/dist/context-error.d.ts +0 -6
  63. package/dist/context-error.js +0 -15
  64. package/dist/context-manager.d.ts +0 -33
  65. package/dist/context-manager.js +0 -61
  66. package/dist/context.d.ts +0 -44
  67. package/dist/context.js +0 -69
  68. package/dist/core-i18n.d.ts +0 -62
  69. package/dist/core-i18n.js +0 -477
  70. package/dist/core-string-key.d.ts +0 -42
  71. package/dist/core-string-key.js +0 -50
  72. package/dist/create-translation-adapter.d.ts +0 -20
  73. package/dist/create-translation-adapter.js +0 -36
  74. package/dist/currency-code.d.ts +0 -19
  75. package/dist/currency-code.js +0 -36
  76. package/dist/currency-format.js +0 -2
  77. package/dist/currency.d.ts +0 -11
  78. package/dist/currency.js +0 -48
  79. package/dist/default-config.d.ts +0 -32
  80. package/dist/default-config.js +0 -101
  81. package/dist/enum-registry.d.ts +0 -44
  82. package/dist/enum-registry.js +0 -100
  83. package/dist/global-active-context.d.ts +0 -50
  84. package/dist/global-active-context.js +0 -177
  85. package/dist/handleable.d.ts +0 -13
  86. package/dist/handleable.js +0 -56
  87. package/dist/i-global-active-context.d.ts +0 -22
  88. package/dist/i-global-active-context.js +0 -2
  89. package/dist/i-handleable-error-options.d.ts +0 -6
  90. package/dist/i-handleable-error-options.js +0 -2
  91. package/dist/i-handleable.d.ts +0 -5
  92. package/dist/i-handleable.js +0 -2
  93. package/dist/i18n-config.d.ts +0 -20
  94. package/dist/i18n-config.js +0 -2
  95. package/dist/i18n-context.js +0 -2
  96. package/dist/i18n-engine.d.ts +0 -178
  97. package/dist/i18n-engine.js +0 -338
  98. package/dist/index.js +0 -83
  99. package/dist/language-codes.js +0 -31
  100. package/dist/language-definition.d.ts +0 -13
  101. package/dist/language-definition.js +0 -2
  102. package/dist/language-registry.d.ts +0 -113
  103. package/dist/language-registry.js +0 -216
  104. package/dist/plugin-i18n-engine.d.ts +0 -146
  105. package/dist/plugin-i18n-engine.js +0 -360
  106. package/dist/plugin-translatable-generic-error.d.ts +0 -29
  107. package/dist/plugin-translatable-generic-error.js +0 -66
  108. package/dist/plugin-translatable-handleable-generic.d.ts +0 -28
  109. package/dist/plugin-translatable-handleable-generic.js +0 -40
  110. package/dist/plugin-typed-handleable.d.ts +0 -14
  111. package/dist/plugin-typed-handleable.js +0 -45
  112. package/dist/registry-config.d.ts +0 -14
  113. package/dist/registry-config.js +0 -2
  114. package/dist/registry-error-type.d.ts +0 -12
  115. package/dist/registry-error-type.js +0 -16
  116. package/dist/registry-error.d.ts +0 -18
  117. package/dist/registry-error.js +0 -45
  118. package/dist/strict-types.d.ts +0 -18
  119. package/dist/strict-types.js +0 -17
  120. package/dist/template.d.ts +0 -12
  121. package/dist/template.js +0 -30
  122. package/dist/timezone.d.ts +0 -11
  123. package/dist/timezone.js +0 -22
  124. package/dist/translatable-generic-error.d.ts +0 -29
  125. package/dist/translatable-generic-error.js +0 -66
  126. package/dist/translatable-handleable-generic.d.ts +0 -28
  127. package/dist/translatable-handleable-generic.js +0 -40
  128. package/dist/translatable.d.ts +0 -5
  129. package/dist/translatable.js +0 -11
  130. package/dist/translation-engine.d.ts +0 -8
  131. package/dist/translation-engine.js +0 -2
  132. package/dist/translation-request.d.ts +0 -9
  133. package/dist/translation-request.js +0 -2
  134. package/dist/translation-response.d.ts +0 -8
  135. package/dist/translation-response.js +0 -2
  136. package/dist/typed-error.d.ts +0 -72
  137. package/dist/typed-error.js +0 -251
  138. package/dist/typed-handleable.d.ts +0 -14
  139. package/dist/typed-handleable.js +0 -40
  140. package/dist/types.js +0 -18
  141. package/dist/unified-translator.d.ts +0 -30
  142. package/dist/unified-translator.js +0 -68
  143. package/dist/utils.d.ts +0 -64
  144. package/dist/utils.js +0 -130
  145. package/dist/validation-config.d.ts +0 -11
  146. package/dist/validation-config.js +0 -2
  147. package/dist/validation-result.d.ts +0 -12
  148. package/dist/validation-result.js +0 -2
@@ -0,0 +1,47 @@
1
+ import { PluginI18nEngine } from './plugin-i18n-engine';
2
+ import { TranslationEngine } from './translation-engine';
3
+
4
+ /**
5
+ * Creates a TranslationEngine adapter from a PluginI18nEngine for a specific component.
6
+ * This allows PluginI18nEngine to be used where TranslationEngine interface is expected.
7
+ *
8
+ * @param pluginEngine - The PluginI18nEngine instance to wrap
9
+ * @param componentId - The component ID to use for translations
10
+ * @returns A TranslationEngine that delegates to the PluginI18nEngine
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * const pluginEngine = getMyPluginI18nEngine();
15
+ * const adapter = createTranslationAdapter(pluginEngine, 'my-component');
16
+ *
17
+ * // Now can be used where TranslationEngine is expected
18
+ * const error = new MyError(errorType, adapter);
19
+ * ```
20
+ */
21
+ export function createTranslationAdapter<TStringKey extends string, TLanguage extends string>(
22
+ pluginEngine: PluginI18nEngine<TLanguage>,
23
+ componentId: string,
24
+ ): TranslationEngine<TStringKey> {
25
+ return {
26
+ translate: (
27
+ key: TStringKey,
28
+ vars?: Record<string, string | number>,
29
+ lang?: TLanguage,
30
+ ): string => {
31
+ try {
32
+ return pluginEngine.translate(componentId, key, vars, lang);
33
+ } catch (error) {
34
+ // Fallback to key if translation fails
35
+ return String(key);
36
+ }
37
+ },
38
+
39
+ safeTranslate: (
40
+ key: TStringKey,
41
+ vars?: Record<string, string | number>,
42
+ lang?: TLanguage,
43
+ ): string => {
44
+ return pluginEngine.safeTranslate(componentId, key, vars, lang);
45
+ },
46
+ };
47
+ }
@@ -0,0 +1,35 @@
1
+ import { codes } from 'currency-codes';
2
+ import { DefaultCurrencyCode } from './types';
3
+
4
+ /**
5
+ * Class representing a validated currency code.
6
+ */
7
+ export class CurrencyCode {
8
+ private _value: string = DefaultCurrencyCode;
9
+ /**
10
+ * Gets the currency code value.
11
+ */
12
+ public get value(): string {
13
+ return this._value;
14
+ }
15
+ /**
16
+ * Sets the currency code value after validating it.
17
+ */
18
+ public set value(value: string) {
19
+ if (!CurrencyCode.values.includes(value)) {
20
+ throw new Error('Invalid currency code');
21
+ }
22
+ this._value = value;
23
+ }
24
+
25
+ /**
26
+ * Gets the list of all valid currency codes.
27
+ */
28
+ public static get values(): string[] {
29
+ return codes();
30
+ }
31
+
32
+ constructor(value: string = DefaultCurrencyCode) {
33
+ this.value = value;
34
+ }
35
+ }
@@ -1,10 +1,11 @@
1
1
  import { CurrencyPosition } from './types';
2
+
2
3
  /**
3
4
  * Represents the format details for a specific currency in a given locale.
4
5
  */
5
6
  export interface CurrencyFormat {
6
- symbol: string;
7
- position: CurrencyPosition;
8
- groupSeparator: string;
9
- decimalSeparator: string;
7
+ symbol: string;
8
+ position: CurrencyPosition;
9
+ groupSeparator: string;
10
+ decimalSeparator: string;
10
11
  }
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Currency formatting utilities
3
+ */
4
+
5
+ import { CurrencyFormat } from './currency-format';
6
+ import { CurrencyPosition } from './types';
7
+
8
+ /**
9
+ * Get currency format details for a given locale and currency code.
10
+ * @param locale The locale string (e.g., 'en-US')
11
+ * @param currencyCode The ISO 4217 currency code (e.g., 'USD')
12
+ * @returns The currency format details
13
+ */
14
+ export function getCurrencyFormat(
15
+ locale: string,
16
+ currencyCode: string,
17
+ ): CurrencyFormat {
18
+ const formatter = new Intl.NumberFormat(locale, {
19
+ style: 'currency',
20
+ currency: currencyCode,
21
+ });
22
+
23
+ const parts = formatter.formatToParts(1234567.89);
24
+ const symbol = parts.find((part) => part.type === 'currency')?.value || '';
25
+ const symbolIndex = parts.findIndex((part) => part.type === 'currency');
26
+ const decimalIndex = parts.findIndex((part) => part.type === 'decimal');
27
+ const integerIndex = parts.findIndex((part) => part.type === 'integer');
28
+
29
+ let position: CurrencyPosition;
30
+ if (decimalIndex === -1) {
31
+ // No decimal separator
32
+ if (symbolIndex < integerIndex) {
33
+ position = 'prefix';
34
+ } else {
35
+ position = 'postfix';
36
+ }
37
+ } else if (symbolIndex < decimalIndex && symbolIndex < integerIndex) {
38
+ position = 'prefix';
39
+ } else if (symbolIndex > decimalIndex) {
40
+ position = 'postfix';
41
+ } else {
42
+ position = 'infix';
43
+ }
44
+
45
+ return {
46
+ symbol,
47
+ position,
48
+ groupSeparator: parts.find((part) => part.type === 'group')?.value || ',',
49
+ decimalSeparator:
50
+ parts.find((part) => part.type === 'decimal')?.value || '.',
51
+ };
52
+ }
@@ -0,0 +1,199 @@
1
+ import { CurrencyCode } from './currency-code';
2
+ import { I18nConfig } from './i18n-config';
3
+ import { I18nContext } from './i18n-context';
4
+ import { I18nEngine } from './i18n-engine';
5
+ import { LanguageCodes } from './language-codes';
6
+ import { Timezone } from './timezone';
7
+ import {
8
+ DefaultCurrencyCode,
9
+ LanguageCodeCollection,
10
+ LanguageContextSpace,
11
+ } from './types';
12
+
13
+ // Default enum types that can be augmented by consumers
14
+ export enum DefaultStringKey {
15
+ Common_Test = 'common_test',
16
+ Error_InstanceAlreadyExistsTemplate = 'error_instanceAlreadyExistsTemplate',
17
+ Error_InstanceNotFoundTemplate = 'error_instanceNotFoundTemplate',
18
+ Error_MissingStringCollectionTemplate = 'error_missingStringCollectionTemplate',
19
+ Error_MissingTranslationTemplate = 'error_missingTranslationTemplate',
20
+ Error_DefaultLanguageNoCollectionTemplate = 'error_defaultLanguageNoCollectionTemplate',
21
+ Error_MissingTranslationKeyTemplate = 'error_missingTranslationKeyTemplate',
22
+ }
23
+
24
+ // Default language codes used by the library
25
+ export type DefaultLanguageCode =
26
+ | typeof LanguageCodes.EN_US
27
+ | typeof LanguageCodes.EN_GB
28
+ | typeof LanguageCodes.FR
29
+ | typeof LanguageCodes.ES
30
+ | typeof LanguageCodes.ZH_CN
31
+ | typeof LanguageCodes.UK;
32
+
33
+ export const DefaultLanguageCodes: LanguageCodeCollection<DefaultLanguageCode> = {
34
+ [LanguageCodes.EN_US]: 'en-US',
35
+ [LanguageCodes.EN_GB]: 'en-GB',
36
+ [LanguageCodes.FR]: 'fr',
37
+ [LanguageCodes.ES]: 'es',
38
+ [LanguageCodes.ZH_CN]: 'zh-CN',
39
+ [LanguageCodes.UK]: 'uk',
40
+ };
41
+
42
+ // Global interface that can be augmented by consumers
43
+ declare global {
44
+ namespace I18n {
45
+ interface Config {
46
+ StringKey: DefaultStringKey;
47
+ Language: DefaultLanguageCode;
48
+ LanguageCodes: LanguageCodeCollection<DefaultLanguageCode>;
49
+ engine: I18nEngine<
50
+ I18n.Config['StringKey'],
51
+ I18n.Config['Language'],
52
+ Record<any, any>
53
+ >;
54
+ }
55
+ }
56
+ }
57
+
58
+ // Convenient type aliases that automatically pick up augmented types
59
+ export type StringKey = I18n.Config['StringKey'];
60
+ export type Language = I18n.Config['Language'];
61
+ export type Engine = I18n.Config['engine'];
62
+ export type DefaultLanguageCodesType = I18n.Config['LanguageCodes'];
63
+
64
+ // Singleton instance that uses the augmented types
65
+ export const getI18nEngine = (): Engine => I18nEngine.getInstance() as Engine;
66
+
67
+ const getConfig = <
68
+ TConstants extends Record<string, any>,
69
+ >(
70
+ constants: TConstants,
71
+ timezone?: Timezone,
72
+ adminTimezone?: Timezone,
73
+ ): I18nConfig<StringKey, Language, TConstants> => ({
74
+ strings: {
75
+ [LanguageCodes.EN_US]: {
76
+ [DefaultStringKey.Common_Test]: 'Test',
77
+ [DefaultStringKey.Error_InstanceAlreadyExistsTemplate]:
78
+ "Instance with key '{key}' already exists",
79
+ [DefaultStringKey.Error_InstanceNotFoundTemplate]:
80
+ "Instance with key '{key}' not found",
81
+ [DefaultStringKey.Error_MissingStringCollectionTemplate]:
82
+ 'Missing string collection for language: {language}',
83
+ [DefaultStringKey.Error_MissingTranslationTemplate]:
84
+ "Missing translation for key '{key}' in language '{language}'",
85
+ [DefaultStringKey.Error_DefaultLanguageNoCollectionTemplate]:
86
+ "Default language '{language}' has no string collection",
87
+ [DefaultStringKey.Error_MissingTranslationKeyTemplate]:
88
+ 'Missing translation key for type: {type}',
89
+ },
90
+ [LanguageCodes.EN_GB]: {
91
+ [DefaultStringKey.Common_Test]: 'Test',
92
+ [DefaultStringKey.Error_InstanceAlreadyExistsTemplate]:
93
+ "Instance with key '{key}' already exists",
94
+ [DefaultStringKey.Error_InstanceNotFoundTemplate]:
95
+ "Instance with key '{key}' not found",
96
+ [DefaultStringKey.Error_MissingStringCollectionTemplate]:
97
+ 'Missing string collection for language: {language}',
98
+ [DefaultStringKey.Error_MissingTranslationTemplate]:
99
+ "Missing translation for key '{key}' in language '{language}'",
100
+ [DefaultStringKey.Error_DefaultLanguageNoCollectionTemplate]:
101
+ "Default language '{language}' has no string collection",
102
+ [DefaultStringKey.Error_MissingTranslationKeyTemplate]:
103
+ 'Missing translation key for type: {type}',
104
+ },
105
+ [LanguageCodes.FR]: {
106
+ [DefaultStringKey.Common_Test]: 'Test',
107
+ [DefaultStringKey.Error_InstanceAlreadyExistsTemplate]:
108
+ "Instance avec clé '{key}' existe déjà",
109
+ [DefaultStringKey.Error_InstanceNotFoundTemplate]:
110
+ "Instance avec clé '{key}' introuvable",
111
+ [DefaultStringKey.Error_MissingStringCollectionTemplate]:
112
+ 'Collection de chaînes manquante pour la langue: {language}',
113
+ [DefaultStringKey.Error_MissingTranslationTemplate]:
114
+ "Traduction manquante pour la clé '{key}' dans la langue '{language}'",
115
+ [DefaultStringKey.Error_DefaultLanguageNoCollectionTemplate]:
116
+ "La langue par défaut '{language}' n'a pas de collection de chaînes",
117
+ [DefaultStringKey.Error_MissingTranslationKeyTemplate]:
118
+ 'Clé de traduction manquante pour le type: {type}',
119
+ },
120
+ [LanguageCodes.ZH_CN]: {
121
+ [DefaultStringKey.Common_Test]: '测试',
122
+ [DefaultStringKey.Error_InstanceAlreadyExistsTemplate]:
123
+ "键为'{key}'的实例已存在",
124
+ [DefaultStringKey.Error_InstanceNotFoundTemplate]:
125
+ "未找到键为'{key}'的实例",
126
+ [DefaultStringKey.Error_MissingStringCollectionTemplate]:
127
+ '缺少语言的字符串集合: {language}',
128
+ [DefaultStringKey.Error_MissingTranslationTemplate]:
129
+ "在语言'{language}'中缺少键'{key}'的翻译",
130
+ [DefaultStringKey.Error_DefaultLanguageNoCollectionTemplate]:
131
+ "默认语言'{language}'没有字符串集合",
132
+ [DefaultStringKey.Error_MissingTranslationKeyTemplate]:
133
+ '类型缺少翻译键: {type}',
134
+ },
135
+ [LanguageCodes.ES]: {
136
+ [DefaultStringKey.Common_Test]: 'Prueba',
137
+ [DefaultStringKey.Error_InstanceAlreadyExistsTemplate]:
138
+ "La instancia con clave '{key}' ya existe",
139
+ [DefaultStringKey.Error_InstanceNotFoundTemplate]:
140
+ "Instancia con clave '{key}' no encontrada",
141
+ [DefaultStringKey.Error_MissingStringCollectionTemplate]:
142
+ 'Falta colección de cadenas para el idioma: {language}',
143
+ [DefaultStringKey.Error_MissingTranslationTemplate]:
144
+ "Falta traducción para la clave '{key}' en el idioma '{language}'",
145
+ [DefaultStringKey.Error_DefaultLanguageNoCollectionTemplate]:
146
+ "El idioma predeterminado '{language}' no tiene colección de cadenas",
147
+ [DefaultStringKey.Error_MissingTranslationKeyTemplate]:
148
+ 'Falta clave de traducción para el tipo: {type}',
149
+ },
150
+ [LanguageCodes.UK]: {
151
+ [DefaultStringKey.Common_Test]: 'Тест',
152
+ [DefaultStringKey.Error_InstanceAlreadyExistsTemplate]:
153
+ "Екземпляр з ключем '{key}' вже існує",
154
+ [DefaultStringKey.Error_InstanceNotFoundTemplate]:
155
+ "Екземпляр з ключем '{key}' не знайдено",
156
+ [DefaultStringKey.Error_MissingStringCollectionTemplate]:
157
+ 'Відсутня колекція рядків для мови: {language}',
158
+ [DefaultStringKey.Error_MissingTranslationTemplate]:
159
+ "Відсутній переклад для ключа '{key}' в мові '{language}'",
160
+ [DefaultStringKey.Error_DefaultLanguageNoCollectionTemplate]:
161
+ "Мова за замовчуванням '{language}' не має колекції рядків",
162
+ [DefaultStringKey.Error_MissingTranslationKeyTemplate]:
163
+ 'Відсутній ключ перекладу для типу: {type}',
164
+ },
165
+ } as any,
166
+ stringNames: Object.values(DefaultStringKey),
167
+ defaultLanguage: LanguageCodes.EN_US,
168
+ defaultTranslationContext: 'user' as LanguageContextSpace,
169
+ defaultCurrencyCode: new CurrencyCode(DefaultCurrencyCode),
170
+ languageCodes: DefaultLanguageCodes,
171
+ languages: [LanguageCodes.EN_US, LanguageCodes.EN_GB, LanguageCodes.FR, LanguageCodes.ES, LanguageCodes.ZH_CN, LanguageCodes.UK],
172
+ constants: constants,
173
+ enumName: 'DefaultStringKey',
174
+ enumObj: DefaultStringKey as Record<string, DefaultStringKey>,
175
+ timezone: timezone ?? new Timezone('UTC'),
176
+ adminTimezone: adminTimezone ?? new Timezone('UTC'),
177
+ });
178
+
179
+ export const getDefaultI18nEngine = <
180
+ TConstants extends Record<string, any>,
181
+ TContext extends I18nContext<DefaultLanguageCode>,
182
+ >(
183
+ constants: TConstants,
184
+ timezone?: Timezone,
185
+ adminTimezone?: Timezone,
186
+ ) =>
187
+ new I18nEngine<
188
+ DefaultStringKey,
189
+ DefaultLanguageCode,
190
+ TConstants,
191
+ TContext
192
+ >(
193
+ getConfig<TConstants>(
194
+ constants,
195
+ timezone,
196
+ adminTimezone,
197
+ ),
198
+ 'user' as LanguageContextSpace,
199
+ );
@@ -0,0 +1,138 @@
1
+ import { EnumLanguageTranslation } from './types';
2
+
3
+ /**
4
+ * Registry for managing enum translations across multiple languages.
5
+ */
6
+ export class EnumTranslationRegistry<
7
+ TStringKey extends string,
8
+ TLanguage extends string,
9
+ > {
10
+ protected translations = new Map<
11
+ any,
12
+ EnumLanguageTranslation<any, TLanguage>
13
+ >();
14
+ protected enumNames = new WeakMap<any, string>();
15
+ protected availableLanguages: Set<TLanguage>;
16
+ protected translateFn?: (
17
+ key: TStringKey,
18
+ vars?: Record<string, any>,
19
+ ) => string;
20
+
21
+ constructor(
22
+ availableLanguages: TLanguage[],
23
+ translateFn?: (key: TStringKey, vars?: Record<string, any>) => string,
24
+ ) {
25
+ this.availableLanguages = new Set(availableLanguages);
26
+ this.translateFn = translateFn;
27
+ }
28
+
29
+ /**
30
+ * Registers an enumeration with its translations and a name.
31
+ * @param enumObj The enumeration object
32
+ * @param translations The translations for the enumeration
33
+ * @param enumName The name of the enumeration
34
+ */
35
+ public register<TEnum extends string | number>(
36
+ enumObj: Record<string, TEnum>,
37
+ translations: EnumLanguageTranslation<TEnum, TLanguage>,
38
+ enumName: string,
39
+ ): void {
40
+ this.validateTranslations(translations, enumName);
41
+ this.translations.set(enumObj, translations);
42
+ this.enumNames.set(enumObj, enumName);
43
+ }
44
+
45
+ /**
46
+ * Translates a value from the given enumeration to the specified language.
47
+ * @param enumObj The enumeration object
48
+ * @param value The value to translate
49
+ * @param language The target language for translation
50
+ * @returns The translated string
51
+ */
52
+ public translate<TEnum extends string | number>(
53
+ enumObj: Record<string, TEnum>,
54
+ value: TEnum,
55
+ language: TLanguage,
56
+ ): string {
57
+ const translations = this.translations.get(enumObj);
58
+ if (!translations) {
59
+ const message = this.translateFn
60
+ ? this.translateFn('Error_EnumNotFoundTemplate' as TStringKey, {
61
+ enumName: this.getEnumName(enumObj),
62
+ })
63
+ : `No translations found for enum: ${this.getEnumName(enumObj)}`;
64
+ throw new Error(message);
65
+ }
66
+
67
+ const langTranslations = translations[language];
68
+ if (!langTranslations) {
69
+ const message = this.translateFn
70
+ ? this.translateFn('Error_EnumLanguageNotFoundTemplate' as TStringKey, {
71
+ language,
72
+ })
73
+ : `No translations found for language: ${language}`;
74
+ throw new Error(message);
75
+ }
76
+
77
+ let result = langTranslations[value];
78
+ if (!result && typeof value === 'number') {
79
+ const stringKey = Object.keys(enumObj).find(
80
+ (key) => enumObj[key] === value,
81
+ );
82
+ if (stringKey) {
83
+ result = langTranslations[stringKey as TEnum];
84
+ }
85
+ }
86
+
87
+ if (!result) {
88
+ const message = this.translateFn
89
+ ? this.translateFn('Error_EnumValueNotFoundTemplate' as TStringKey, {
90
+ value: String(value),
91
+ })
92
+ : `No translation found for value: ${String(value)}`;
93
+ throw new Error(message);
94
+ }
95
+
96
+ return result;
97
+ }
98
+
99
+ /**
100
+ * Gets the name of the enumeration.
101
+ * @param enumObj The enumeration object
102
+ * @returns The name of the enumeration
103
+ */
104
+ private getEnumName(enumObj: any): string {
105
+ return this.enumNames.get(enumObj) || 'UnknownEnum';
106
+ }
107
+
108
+ /**
109
+ * Checks if the registry has translations for the given enumeration.
110
+ * @param enumObj The enumeration object
111
+ * @returns True if translations exist, false otherwise
112
+ */
113
+ has(enumObj: any): boolean {
114
+ return this.translations.has(enumObj);
115
+ }
116
+
117
+ /**
118
+ * Validates that enum translations only contain available languages.
119
+ * @param translations The translations to validate
120
+ * @param enumName The name of the enumeration for error messages
121
+ */
122
+ private validateTranslations<TEnum extends string | number>(
123
+ translations: EnumLanguageTranslation<TEnum, TLanguage>,
124
+ enumName: string,
125
+ ): void {
126
+ for (const language of Object.keys(translations) as TLanguage[]) {
127
+ if (!this.availableLanguages.has(language)) {
128
+ const message = this.translateFn
129
+ ? this.translateFn(
130
+ 'Error_EnumLanguageNotAvailableTemplate' as TStringKey,
131
+ { language, enumName },
132
+ )
133
+ : `Language '${language}' in enum '${enumName}' is not available in this engine instance`;
134
+ throw new Error(message);
135
+ }
136
+ }
137
+ }
138
+ }