@digitaldefiance/i18n-lib 4.1.2 → 4.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -924,6 +924,176 @@ const allValues = getStringKeyValues(AllKeys);
924
924
 
925
925
  For complete migration guide, see [BRANDED_ENUM_MIGRATION.md](docs/BRANDED_ENUM_MIGRATION.md).
926
926
 
927
+ ### Branded Enum Translation
928
+
929
+ The enum translation system supports branded enums from `@digitaldefiance/branded-enum`, enabling automatic name inference and type-safe enum value translations.
930
+
931
+ #### Why Use Branded Enums for Enum Translation?
932
+
933
+ - **Automatic Name Inference**: No need to provide an explicit enum name - it's extracted from the branded enum's component ID
934
+ - **Type Safety**: Full TypeScript support for enum values and translations
935
+ - **Consistent Naming**: Enum names in error messages match your component IDs
936
+ - **Unified Pattern**: Use the same branded enum pattern for both string keys and enum translations
937
+
938
+ #### Registering Branded Enums for Translation
939
+
940
+ ```typescript
941
+ import { createBrandedEnum } from '@digitaldefiance/branded-enum';
942
+ import { I18nEngine, LanguageCodes } from '@digitaldefiance/i18n-lib';
943
+
944
+ // Create a branded enum
945
+ const Status = createBrandedEnum('status', {
946
+ Active: 'active',
947
+ Inactive: 'inactive',
948
+ Pending: 'pending',
949
+ });
950
+
951
+ // Create engine
952
+ const engine = new I18nEngine([
953
+ { id: LanguageCodes.EN_US, name: 'English', code: 'en-US', isDefault: true },
954
+ { id: LanguageCodes.ES, name: 'Spanish', code: 'es' },
955
+ ]);
956
+
957
+ // Register branded enum - name is automatically inferred as 'status'
958
+ engine.registerEnum(Status, {
959
+ [LanguageCodes.EN_US]: {
960
+ active: 'Active',
961
+ inactive: 'Inactive',
962
+ pending: 'Pending',
963
+ },
964
+ [LanguageCodes.ES]: {
965
+ active: 'Activo',
966
+ inactive: 'Inactivo',
967
+ pending: 'Pendiente',
968
+ },
969
+ });
970
+
971
+ // Translate enum values
972
+ engine.translateEnum(Status, Status.Active, LanguageCodes.EN_US); // 'Active'
973
+ engine.translateEnum(Status, Status.Active, LanguageCodes.ES); // 'Activo'
974
+ ```
975
+
976
+ #### Comparison: Traditional vs Branded Enum Registration
977
+
978
+ ```typescript
979
+ // Traditional enum - requires explicit name
980
+ enum TraditionalStatus {
981
+ Active = 'active',
982
+ Inactive = 'inactive',
983
+ }
984
+ engine.registerEnum(TraditionalStatus, translations, 'Status'); // Name required
985
+
986
+ // Branded enum - name inferred from component ID
987
+ const BrandedStatus = createBrandedEnum('status', {
988
+ Active: 'active',
989
+ Inactive: 'inactive',
990
+ });
991
+ engine.registerEnum(BrandedStatus, translations); // Name 'status' inferred automatically
992
+ ```
993
+
994
+ #### Using with PluginI18nEngine
995
+
996
+ The `PluginI18nEngine` also supports branded enum translation with language validation:
997
+
998
+ ```typescript
999
+ import { PluginI18nEngine, LanguageCodes } from '@digitaldefiance/i18n-lib';
1000
+ import { createBrandedEnum } from '@digitaldefiance/branded-enum';
1001
+
1002
+ const Priority = createBrandedEnum('priority', {
1003
+ High: 'high',
1004
+ Medium: 'medium',
1005
+ Low: 'low',
1006
+ });
1007
+
1008
+ const engine = PluginI18nEngine.createInstance('myapp', [
1009
+ { id: LanguageCodes.EN_US, name: 'English', code: 'en-US', isDefault: true },
1010
+ { id: LanguageCodes.FR, name: 'French', code: 'fr' },
1011
+ ]);
1012
+
1013
+ // Register with automatic name inference
1014
+ engine.registerEnum(Priority, {
1015
+ [LanguageCodes.EN_US]: {
1016
+ high: 'High Priority',
1017
+ medium: 'Medium Priority',
1018
+ low: 'Low Priority',
1019
+ },
1020
+ [LanguageCodes.FR]: {
1021
+ high: 'Haute Priorité',
1022
+ medium: 'Priorité Moyenne',
1023
+ low: 'Basse Priorité',
1024
+ },
1025
+ });
1026
+
1027
+ // Translate
1028
+ engine.translateEnum(Priority, Priority.High); // Uses current language
1029
+ engine.translateEnum(Priority, Priority.High, LanguageCodes.FR); // 'Haute Priorité'
1030
+ ```
1031
+
1032
+ #### Utility Functions
1033
+
1034
+ The library provides utility functions for working with branded enums:
1035
+
1036
+ ```typescript
1037
+ import {
1038
+ isBrandedEnum,
1039
+ getBrandedEnumComponentId,
1040
+ getBrandedEnumId
1041
+ } from '@digitaldefiance/i18n-lib';
1042
+
1043
+ // Check if an enum is branded
1044
+ if (isBrandedEnum(Status)) {
1045
+ // TypeScript knows Status is a branded enum here
1046
+ const componentId = getBrandedEnumComponentId(Status);
1047
+ console.log(componentId); // 'status'
1048
+ }
1049
+
1050
+ // Get the raw brand ID (includes 'i18n:' prefix for i18n string keys)
1051
+ const rawId = getBrandedEnumId(Status); // 'status'
1052
+ ```
1053
+
1054
+ #### Error Messages with Branded Enums
1055
+
1056
+ When translation errors occur, the enum name in error messages is automatically derived from the branded enum's component ID:
1057
+
1058
+ ```typescript
1059
+ const Status = createBrandedEnum('user-status', { Active: 'active' });
1060
+ engine.registerEnum(Status, { en: { active: 'Active' } });
1061
+
1062
+ // If translation fails, error message includes the inferred name:
1063
+ // "No translations found for enum: user-status"
1064
+ // "Translation missing for enum value 'unknown' in language 'en'"
1065
+ ```
1066
+
1067
+ #### Type Definitions
1068
+
1069
+ New types are available for working with branded enum translations:
1070
+
1071
+ ```typescript
1072
+ import type {
1073
+ BrandedEnumTranslation,
1074
+ RegisterableEnum,
1075
+ TranslatableEnumValue,
1076
+ } from '@digitaldefiance/i18n-lib';
1077
+
1078
+ // Type for branded enum translation maps
1079
+ type MyTranslations = BrandedEnumTranslation<typeof Status, 'en' | 'es'>;
1080
+
1081
+ // Union type accepting both traditional and branded enums
1082
+ function registerAnyEnum<T extends string | number>(
1083
+ enumObj: RegisterableEnum<T>,
1084
+ translations: Record<string, Record<string, string>>,
1085
+ ): void {
1086
+ // Works with both enum types
1087
+ }
1088
+
1089
+ // Union type for translatable values
1090
+ function translateAnyValue<T extends string | number>(
1091
+ value: TranslatableEnumValue<T>,
1092
+ ): string {
1093
+ // Works with both traditional and branded enum values
1094
+ }
1095
+ ```
1096
+
927
1097
  ## Browser Support
928
1098
 
929
1099
  - Chrome/Edge: Latest 2 versions
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@digitaldefiance/i18n-lib",
3
- "version": "4.1.2",
3
+ "version": "4.2.0",
4
4
  "description": "i18n library with enum translation support",
5
5
  "homepage": "https://github.com/Digital-Defiance/i18n-lib",
6
6
  "repository": {
@@ -0,0 +1,226 @@
1
+ /**
2
+ * Branded Enum Utilities for i18n enum translation support
3
+ *
4
+ * This module provides utility functions for detecting and extracting
5
+ * information from branded enums created with `@digitaldefiance/branded-enum`.
6
+ * These utilities enable the i18n library to seamlessly support both traditional
7
+ * TypeScript enums and branded enums for enum translations.
8
+ *
9
+ * ## Overview
10
+ *
11
+ * Branded enums carry runtime metadata (a unique component ID) that enables:
12
+ * - Automatic enum name inference during registration
13
+ * - Runtime identification for debugging and logging
14
+ * - Type-safe translation lookups
15
+ *
16
+ * ## Key Functions
17
+ *
18
+ * - {@link isBrandedEnum} - Type guard to detect branded enums
19
+ * - {@link getBrandedEnumComponentId} - Extract component ID (prefix stripped)
20
+ * - {@link getBrandedEnumId} - Get raw brand ID (for debugging)
21
+ *
22
+ * ## Usage with Enum Translation
23
+ *
24
+ * These utilities are used internally by `EnumRegistry` and `EnumTranslationRegistry`
25
+ * to provide automatic name inference and proper translation lookups for branded enums.
26
+ *
27
+ * @example
28
+ * ```typescript
29
+ * import { createBrandedEnum } from '@digitaldefiance/branded-enum';
30
+ * import { isBrandedEnum, getBrandedEnumComponentId } from '@digitaldefiance/i18n-lib';
31
+ *
32
+ * const Status = createBrandedEnum('status', { Active: 'active', Inactive: 'inactive' });
33
+ *
34
+ * if (isBrandedEnum(Status)) {
35
+ * const componentId = getBrandedEnumComponentId(Status);
36
+ * console.log(componentId); // 'status'
37
+ * }
38
+ * ```
39
+ *
40
+ * @module branded-enum-utils
41
+ * @see {@link EnumRegistry} - V2 enum registry with branded enum support
42
+ * @see {@link EnumTranslationRegistry} - Legacy registry with branded enum support
43
+ */
44
+ import type { AnyBrandedEnum } from '@digitaldefiance/branded-enum';
45
+ /**
46
+ * Type guard to check if an object is a branded enum from `@digitaldefiance/branded-enum`.
47
+ *
48
+ * Branded enums are identified by having a valid enum ID retrievable via `getEnumId()`.
49
+ * This function safely checks for this property without throwing errors, making it
50
+ * suitable for use in conditional logic and type narrowing.
51
+ *
52
+ * ## When to Use
53
+ *
54
+ * Use this function when you need to:
55
+ * - Determine if an enum object is branded or traditional
56
+ * - Conditionally apply branded enum-specific logic
57
+ * - Type-narrow an unknown enum object
58
+ *
59
+ * ## Implementation Details
60
+ *
61
+ * The function checks:
62
+ * 1. The object is not null or undefined
63
+ * 2. The object is of type 'object'
64
+ * 3. The object has a valid enum ID (via `getEnumId()`)
65
+ *
66
+ * @param obj - The object to check (can be any value)
67
+ * @returns `true` if the object is a branded enum, `false` otherwise
68
+ *
69
+ * @example Basic usage
70
+ * ```typescript
71
+ * import { createBrandedEnum } from '@digitaldefiance/branded-enum';
72
+ * import { isBrandedEnum } from '@digitaldefiance/i18n-lib';
73
+ *
74
+ * const BrandedStatus = createBrandedEnum('status', { Active: 'active' });
75
+ * const TraditionalStatus = { Active: 'active' };
76
+ *
77
+ * isBrandedEnum(BrandedStatus); // true
78
+ * isBrandedEnum(TraditionalStatus); // false
79
+ * ```
80
+ *
81
+ * @example Type narrowing
82
+ * ```typescript
83
+ * function processEnum(enumObj: object) {
84
+ * if (isBrandedEnum(enumObj)) {
85
+ * // TypeScript knows enumObj is AnyBrandedEnum here
86
+ * const id = getBrandedEnumComponentId(enumObj);
87
+ * console.log(`Processing branded enum: ${id}`);
88
+ * } else {
89
+ * console.log('Processing traditional enum');
90
+ * }
91
+ * }
92
+ * ```
93
+ *
94
+ * @example Edge cases
95
+ * ```typescript
96
+ * isBrandedEnum(null); // false
97
+ * isBrandedEnum(undefined); // false
98
+ * isBrandedEnum('string'); // false
99
+ * isBrandedEnum(123); // false
100
+ * isBrandedEnum({}); // false
101
+ * ```
102
+ *
103
+ * @see {@link getBrandedEnumComponentId} - Extract component ID from branded enum
104
+ * @see {@link getBrandedEnumId} - Get raw brand ID from branded enum
105
+ */
106
+ export declare function isBrandedEnum(obj: unknown): obj is AnyBrandedEnum;
107
+ /**
108
+ * Extracts the component ID from a branded enum.
109
+ *
110
+ * For branded enums created with `createI18nStringKeys()`, the brand ID
111
+ * is prefixed with `'i18n:'`. This function strips that prefix to return
112
+ * just the component ID, which is suitable for use as an enum name in
113
+ * registration and error messages.
114
+ *
115
+ * ## Prefix Stripping
116
+ *
117
+ * | Input Brand ID | Returned Component ID |
118
+ * |----------------|----------------------|
119
+ * | `'i18n:my-component'` | `'my-component'` |
120
+ * | `'my-component'` | `'my-component'` |
121
+ * | `'status'` | `'status'` |
122
+ *
123
+ * ## When to Use
124
+ *
125
+ * Use this function when you need:
126
+ * - The component ID for automatic enum name inference
127
+ * - A clean identifier without the `i18n:` prefix
128
+ * - The default name for error messages
129
+ *
130
+ * For debugging purposes where you need the full brand ID including prefix,
131
+ * use {@link getBrandedEnumId} instead.
132
+ *
133
+ * @param enumObj - The object to extract the component ID from
134
+ * @returns The component ID without the `'i18n:'` prefix, or `null` if not a branded enum
135
+ *
136
+ * @example Basic usage
137
+ * ```typescript
138
+ * import { createI18nStringKeys } from '@digitaldefiance/i18n-lib';
139
+ *
140
+ * const MyKeys = createI18nStringKeys('my-component', { A: 'a' });
141
+ *
142
+ * getBrandedEnumComponentId(MyKeys); // 'my-component'
143
+ * getBrandedEnumComponentId({ A: 'a' }); // null (not a branded enum)
144
+ * ```
145
+ *
146
+ * @example With createBrandedEnum (no prefix)
147
+ * ```typescript
148
+ * import { createBrandedEnum } from '@digitaldefiance/branded-enum';
149
+ *
150
+ * const Status = createBrandedEnum('status', { Active: 'active' });
151
+ *
152
+ * getBrandedEnumComponentId(Status); // 'status'
153
+ * ```
154
+ *
155
+ * @example Automatic name inference in registration
156
+ * ```typescript
157
+ * const MyKeys = createI18nStringKeys('user-profile', { Title: 'title' });
158
+ *
159
+ * // When registering without explicit name, component ID is used
160
+ * registry.register(MyKeys, translations);
161
+ * // Internally uses: getBrandedEnumComponentId(MyKeys) → 'user-profile'
162
+ * ```
163
+ *
164
+ * @see {@link isBrandedEnum} - Check if an object is a branded enum
165
+ * @see {@link getBrandedEnumId} - Get raw brand ID without prefix stripping
166
+ */
167
+ export declare function getBrandedEnumComponentId(enumObj: unknown): string | null;
168
+ /**
169
+ * Gets the raw brand/ID from a branded enum without prefix stripping.
170
+ *
171
+ * Unlike {@link getBrandedEnumComponentId}, this function returns the full
172
+ * brand ID including any prefixes (such as `'i18n:'`). This is useful for
173
+ * debugging, logging, and cases where you need the exact brand identifier.
174
+ *
175
+ * ## Comparison with getBrandedEnumComponentId
176
+ *
177
+ * | Function | Input Brand | Output |
178
+ * |----------|-------------|--------|
179
+ * | `getBrandedEnumId` | `'i18n:my-component'` | `'i18n:my-component'` |
180
+ * | `getBrandedEnumComponentId` | `'i18n:my-component'` | `'my-component'` |
181
+ *
182
+ * ## When to Use
183
+ *
184
+ * Use this function when you need:
185
+ * - The exact brand ID for debugging or logging
186
+ * - To distinguish between different branded enum sources
187
+ * - The full identifier including any prefixes
188
+ *
189
+ * For registration and user-facing names, use {@link getBrandedEnumComponentId}
190
+ * which strips the `i18n:` prefix.
191
+ *
192
+ * @param enumObj - The object to get the brand ID from
193
+ * @returns The raw brand ID, or `null` if not a branded enum
194
+ *
195
+ * @example Debugging and logging
196
+ * ```typescript
197
+ * import { createI18nStringKeys } from '@digitaldefiance/i18n-lib';
198
+ *
199
+ * const MyKeys = createI18nStringKeys('my-component', { A: 'a' });
200
+ *
201
+ * // For debugging, get the full brand ID
202
+ * console.log(`Brand ID: ${getBrandedEnumId(MyKeys)}`);
203
+ * // Output: 'Brand ID: i18n:my-component'
204
+ *
205
+ * // For user-facing names, use component ID
206
+ * console.log(`Component: ${getBrandedEnumComponentId(MyKeys)}`);
207
+ * // Output: 'Component: my-component'
208
+ * ```
209
+ *
210
+ * @example Comparing branded enums
211
+ * ```typescript
212
+ * import { createBrandedEnum } from '@digitaldefiance/branded-enum';
213
+ * import { createI18nStringKeys } from '@digitaldefiance/i18n-lib';
214
+ *
215
+ * const RegularBranded = createBrandedEnum('status', { A: 'a' });
216
+ * const I18nBranded = createI18nStringKeys('status', { A: 'a' });
217
+ *
218
+ * getBrandedEnumId(RegularBranded); // 'status'
219
+ * getBrandedEnumId(I18nBranded); // 'i18n:status'
220
+ * ```
221
+ *
222
+ * @see {@link isBrandedEnum} - Check if an object is a branded enum
223
+ * @see {@link getBrandedEnumComponentId} - Get component ID with prefix stripped
224
+ */
225
+ export declare function getBrandedEnumId(enumObj: unknown): string | null;
226
+ //# sourceMappingURL=branded-enum-utils.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branded-enum-utils.d.ts","sourceRoot":"","sources":["../../../../packages/digitaldefiance-i18n-lib/src/branded-enum-utils.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAGpE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,OAAO,GAAG,GAAG,IAAI,cAAc,CAOjE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAUzE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,GAAG,IAAI,CAMhE"}
@@ -0,0 +1,254 @@
1
+ "use strict";
2
+ /**
3
+ * Branded Enum Utilities for i18n enum translation support
4
+ *
5
+ * This module provides utility functions for detecting and extracting
6
+ * information from branded enums created with `@digitaldefiance/branded-enum`.
7
+ * These utilities enable the i18n library to seamlessly support both traditional
8
+ * TypeScript enums and branded enums for enum translations.
9
+ *
10
+ * ## Overview
11
+ *
12
+ * Branded enums carry runtime metadata (a unique component ID) that enables:
13
+ * - Automatic enum name inference during registration
14
+ * - Runtime identification for debugging and logging
15
+ * - Type-safe translation lookups
16
+ *
17
+ * ## Key Functions
18
+ *
19
+ * - {@link isBrandedEnum} - Type guard to detect branded enums
20
+ * - {@link getBrandedEnumComponentId} - Extract component ID (prefix stripped)
21
+ * - {@link getBrandedEnumId} - Get raw brand ID (for debugging)
22
+ *
23
+ * ## Usage with Enum Translation
24
+ *
25
+ * These utilities are used internally by `EnumRegistry` and `EnumTranslationRegistry`
26
+ * to provide automatic name inference and proper translation lookups for branded enums.
27
+ *
28
+ * @example
29
+ * ```typescript
30
+ * import { createBrandedEnum } from '@digitaldefiance/branded-enum';
31
+ * import { isBrandedEnum, getBrandedEnumComponentId } from '@digitaldefiance/i18n-lib';
32
+ *
33
+ * const Status = createBrandedEnum('status', { Active: 'active', Inactive: 'inactive' });
34
+ *
35
+ * if (isBrandedEnum(Status)) {
36
+ * const componentId = getBrandedEnumComponentId(Status);
37
+ * console.log(componentId); // 'status'
38
+ * }
39
+ * ```
40
+ *
41
+ * @module branded-enum-utils
42
+ * @see {@link EnumRegistry} - V2 enum registry with branded enum support
43
+ * @see {@link EnumTranslationRegistry} - Legacy registry with branded enum support
44
+ */
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.isBrandedEnum = isBrandedEnum;
47
+ exports.getBrandedEnumComponentId = getBrandedEnumComponentId;
48
+ exports.getBrandedEnumId = getBrandedEnumId;
49
+ const branded_enum_1 = require("@digitaldefiance/branded-enum");
50
+ /**
51
+ * Type guard to check if an object is a branded enum from `@digitaldefiance/branded-enum`.
52
+ *
53
+ * Branded enums are identified by having a valid enum ID retrievable via `getEnumId()`.
54
+ * This function safely checks for this property without throwing errors, making it
55
+ * suitable for use in conditional logic and type narrowing.
56
+ *
57
+ * ## When to Use
58
+ *
59
+ * Use this function when you need to:
60
+ * - Determine if an enum object is branded or traditional
61
+ * - Conditionally apply branded enum-specific logic
62
+ * - Type-narrow an unknown enum object
63
+ *
64
+ * ## Implementation Details
65
+ *
66
+ * The function checks:
67
+ * 1. The object is not null or undefined
68
+ * 2. The object is of type 'object'
69
+ * 3. The object has a valid enum ID (via `getEnumId()`)
70
+ *
71
+ * @param obj - The object to check (can be any value)
72
+ * @returns `true` if the object is a branded enum, `false` otherwise
73
+ *
74
+ * @example Basic usage
75
+ * ```typescript
76
+ * import { createBrandedEnum } from '@digitaldefiance/branded-enum';
77
+ * import { isBrandedEnum } from '@digitaldefiance/i18n-lib';
78
+ *
79
+ * const BrandedStatus = createBrandedEnum('status', { Active: 'active' });
80
+ * const TraditionalStatus = { Active: 'active' };
81
+ *
82
+ * isBrandedEnum(BrandedStatus); // true
83
+ * isBrandedEnum(TraditionalStatus); // false
84
+ * ```
85
+ *
86
+ * @example Type narrowing
87
+ * ```typescript
88
+ * function processEnum(enumObj: object) {
89
+ * if (isBrandedEnum(enumObj)) {
90
+ * // TypeScript knows enumObj is AnyBrandedEnum here
91
+ * const id = getBrandedEnumComponentId(enumObj);
92
+ * console.log(`Processing branded enum: ${id}`);
93
+ * } else {
94
+ * console.log('Processing traditional enum');
95
+ * }
96
+ * }
97
+ * ```
98
+ *
99
+ * @example Edge cases
100
+ * ```typescript
101
+ * isBrandedEnum(null); // false
102
+ * isBrandedEnum(undefined); // false
103
+ * isBrandedEnum('string'); // false
104
+ * isBrandedEnum(123); // false
105
+ * isBrandedEnum({}); // false
106
+ * ```
107
+ *
108
+ * @see {@link getBrandedEnumComponentId} - Extract component ID from branded enum
109
+ * @see {@link getBrandedEnumId} - Get raw brand ID from branded enum
110
+ */
111
+ function isBrandedEnum(obj) {
112
+ if (obj === null || obj === undefined || typeof obj !== 'object') {
113
+ return false;
114
+ }
115
+ // Branded enums have an enum ID retrievable via getEnumId
116
+ const enumId = (0, branded_enum_1.getEnumId)(obj);
117
+ return enumId !== undefined;
118
+ }
119
+ /**
120
+ * Extracts the component ID from a branded enum.
121
+ *
122
+ * For branded enums created with `createI18nStringKeys()`, the brand ID
123
+ * is prefixed with `'i18n:'`. This function strips that prefix to return
124
+ * just the component ID, which is suitable for use as an enum name in
125
+ * registration and error messages.
126
+ *
127
+ * ## Prefix Stripping
128
+ *
129
+ * | Input Brand ID | Returned Component ID |
130
+ * |----------------|----------------------|
131
+ * | `'i18n:my-component'` | `'my-component'` |
132
+ * | `'my-component'` | `'my-component'` |
133
+ * | `'status'` | `'status'` |
134
+ *
135
+ * ## When to Use
136
+ *
137
+ * Use this function when you need:
138
+ * - The component ID for automatic enum name inference
139
+ * - A clean identifier without the `i18n:` prefix
140
+ * - The default name for error messages
141
+ *
142
+ * For debugging purposes where you need the full brand ID including prefix,
143
+ * use {@link getBrandedEnumId} instead.
144
+ *
145
+ * @param enumObj - The object to extract the component ID from
146
+ * @returns The component ID without the `'i18n:'` prefix, or `null` if not a branded enum
147
+ *
148
+ * @example Basic usage
149
+ * ```typescript
150
+ * import { createI18nStringKeys } from '@digitaldefiance/i18n-lib';
151
+ *
152
+ * const MyKeys = createI18nStringKeys('my-component', { A: 'a' });
153
+ *
154
+ * getBrandedEnumComponentId(MyKeys); // 'my-component'
155
+ * getBrandedEnumComponentId({ A: 'a' }); // null (not a branded enum)
156
+ * ```
157
+ *
158
+ * @example With createBrandedEnum (no prefix)
159
+ * ```typescript
160
+ * import { createBrandedEnum } from '@digitaldefiance/branded-enum';
161
+ *
162
+ * const Status = createBrandedEnum('status', { Active: 'active' });
163
+ *
164
+ * getBrandedEnumComponentId(Status); // 'status'
165
+ * ```
166
+ *
167
+ * @example Automatic name inference in registration
168
+ * ```typescript
169
+ * const MyKeys = createI18nStringKeys('user-profile', { Title: 'title' });
170
+ *
171
+ * // When registering without explicit name, component ID is used
172
+ * registry.register(MyKeys, translations);
173
+ * // Internally uses: getBrandedEnumComponentId(MyKeys) → 'user-profile'
174
+ * ```
175
+ *
176
+ * @see {@link isBrandedEnum} - Check if an object is a branded enum
177
+ * @see {@link getBrandedEnumId} - Get raw brand ID without prefix stripping
178
+ */
179
+ function getBrandedEnumComponentId(enumObj) {
180
+ if (!isBrandedEnum(enumObj)) {
181
+ return null;
182
+ }
183
+ const brand = (0, branded_enum_1.getEnumId)(enumObj);
184
+ if (brand === undefined) {
185
+ return null;
186
+ }
187
+ // Remove 'i18n:' prefix if present (used by createI18nStringKeys)
188
+ return brand.startsWith('i18n:') ? brand.slice(5) : brand;
189
+ }
190
+ /**
191
+ * Gets the raw brand/ID from a branded enum without prefix stripping.
192
+ *
193
+ * Unlike {@link getBrandedEnumComponentId}, this function returns the full
194
+ * brand ID including any prefixes (such as `'i18n:'`). This is useful for
195
+ * debugging, logging, and cases where you need the exact brand identifier.
196
+ *
197
+ * ## Comparison with getBrandedEnumComponentId
198
+ *
199
+ * | Function | Input Brand | Output |
200
+ * |----------|-------------|--------|
201
+ * | `getBrandedEnumId` | `'i18n:my-component'` | `'i18n:my-component'` |
202
+ * | `getBrandedEnumComponentId` | `'i18n:my-component'` | `'my-component'` |
203
+ *
204
+ * ## When to Use
205
+ *
206
+ * Use this function when you need:
207
+ * - The exact brand ID for debugging or logging
208
+ * - To distinguish between different branded enum sources
209
+ * - The full identifier including any prefixes
210
+ *
211
+ * For registration and user-facing names, use {@link getBrandedEnumComponentId}
212
+ * which strips the `i18n:` prefix.
213
+ *
214
+ * @param enumObj - The object to get the brand ID from
215
+ * @returns The raw brand ID, or `null` if not a branded enum
216
+ *
217
+ * @example Debugging and logging
218
+ * ```typescript
219
+ * import { createI18nStringKeys } from '@digitaldefiance/i18n-lib';
220
+ *
221
+ * const MyKeys = createI18nStringKeys('my-component', { A: 'a' });
222
+ *
223
+ * // For debugging, get the full brand ID
224
+ * console.log(`Brand ID: ${getBrandedEnumId(MyKeys)}`);
225
+ * // Output: 'Brand ID: i18n:my-component'
226
+ *
227
+ * // For user-facing names, use component ID
228
+ * console.log(`Component: ${getBrandedEnumComponentId(MyKeys)}`);
229
+ * // Output: 'Component: my-component'
230
+ * ```
231
+ *
232
+ * @example Comparing branded enums
233
+ * ```typescript
234
+ * import { createBrandedEnum } from '@digitaldefiance/branded-enum';
235
+ * import { createI18nStringKeys } from '@digitaldefiance/i18n-lib';
236
+ *
237
+ * const RegularBranded = createBrandedEnum('status', { A: 'a' });
238
+ * const I18nBranded = createI18nStringKeys('status', { A: 'a' });
239
+ *
240
+ * getBrandedEnumId(RegularBranded); // 'status'
241
+ * getBrandedEnumId(I18nBranded); // 'i18n:status'
242
+ * ```
243
+ *
244
+ * @see {@link isBrandedEnum} - Check if an object is a branded enum
245
+ * @see {@link getBrandedEnumComponentId} - Get component ID with prefix stripped
246
+ */
247
+ function getBrandedEnumId(enumObj) {
248
+ if (!isBrandedEnum(enumObj)) {
249
+ return null;
250
+ }
251
+ const brand = (0, branded_enum_1.getEnumId)(enumObj);
252
+ return brand ?? null;
253
+ }
254
+ //# sourceMappingURL=branded-enum-utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"branded-enum-utils.js","sourceRoot":"","sources":["../../../../packages/digitaldefiance-i18n-lib/src/branded-enum-utils.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA0CG;;AAkEH,sCAOC;AA8DD,8DAUC;AA2DD,4CAMC;AA/MD,gEAA0D;AAE1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4DG;AACH,SAAgB,aAAa,CAAC,GAAY;IACxC,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QACjE,OAAO,KAAK,CAAC;IACf,CAAC;IACD,0DAA0D;IAC1D,MAAM,MAAM,GAAG,IAAA,wBAAS,EAAC,GAAqB,CAAC,CAAC;IAChD,OAAO,MAAM,KAAK,SAAS,CAAC;AAC9B,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2DG;AACH,SAAgB,yBAAyB,CAAC,OAAgB;IACxD,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,KAAK,GAAG,IAAA,wBAAS,EAAC,OAAO,CAAC,CAAC;IACjC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,OAAO,IAAI,CAAC;IACd,CAAC;IACD,kEAAkE;IAClE,OAAO,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC;AAC5D,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AACH,SAAgB,gBAAgB,CAAC,OAAgB;IAC/C,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC;QAC5B,OAAO,IAAI,CAAC;IACd,CAAC;IACD,MAAM,KAAK,GAAG,IAAA,wBAAS,EAAC,OAAO,CAAC,CAAC;IACjC,OAAO,KAAK,IAAI,IAAI,CAAC;AACvB,CAAC"}