@lokascript/i18n 1.0.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 +286 -0
- package/dist/browser.cjs +7669 -0
- package/dist/browser.cjs.map +1 -0
- package/dist/browser.d.cts +50 -0
- package/dist/browser.d.ts +50 -0
- package/dist/browser.js +7592 -0
- package/dist/browser.js.map +1 -0
- package/dist/hyperfixi-i18n.min.js +2 -0
- package/dist/hyperfixi-i18n.min.js.map +1 -0
- package/dist/hyperfixi-i18n.mjs +8558 -0
- package/dist/hyperfixi-i18n.mjs.map +1 -0
- package/dist/index.cjs +14205 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +947 -0
- package/dist/index.d.ts +947 -0
- package/dist/index.js +14095 -0
- package/dist/index.js.map +1 -0
- package/dist/transformer-Ckask-yw.d.cts +1041 -0
- package/dist/transformer-Ckask-yw.d.ts +1041 -0
- package/package.json +84 -0
- package/src/browser.ts +122 -0
- package/src/compatibility/browser-tests/grammar-demo.spec.ts +169 -0
- package/src/constants.ts +366 -0
- package/src/dictionaries/ar.ts +233 -0
- package/src/dictionaries/bn.ts +156 -0
- package/src/dictionaries/de.ts +233 -0
- package/src/dictionaries/derive.ts +515 -0
- package/src/dictionaries/en.ts +237 -0
- package/src/dictionaries/es.ts +233 -0
- package/src/dictionaries/fr.ts +233 -0
- package/src/dictionaries/hi.ts +270 -0
- package/src/dictionaries/id.ts +233 -0
- package/src/dictionaries/index.ts +238 -0
- package/src/dictionaries/it.ts +233 -0
- package/src/dictionaries/ja.ts +233 -0
- package/src/dictionaries/ko.ts +233 -0
- package/src/dictionaries/ms.ts +276 -0
- package/src/dictionaries/pl.ts +239 -0
- package/src/dictionaries/pt.ts +237 -0
- package/src/dictionaries/qu.ts +233 -0
- package/src/dictionaries/ru.ts +270 -0
- package/src/dictionaries/sw.ts +233 -0
- package/src/dictionaries/th.ts +156 -0
- package/src/dictionaries/tl.ts +276 -0
- package/src/dictionaries/tr.ts +233 -0
- package/src/dictionaries/uk.ts +270 -0
- package/src/dictionaries/vi.ts +210 -0
- package/src/dictionaries/zh.ts +233 -0
- package/src/enhanced-i18n.test.ts +454 -0
- package/src/enhanced-i18n.ts +713 -0
- package/src/examples/new-languages.ts +326 -0
- package/src/formatting.test.ts +213 -0
- package/src/formatting.ts +416 -0
- package/src/grammar/direct-mappings.ts +353 -0
- package/src/grammar/grammar.test.ts +1053 -0
- package/src/grammar/index.ts +59 -0
- package/src/grammar/profiles/index.ts +860 -0
- package/src/grammar/transformer.ts +1318 -0
- package/src/grammar/types.ts +630 -0
- package/src/index.ts +202 -0
- package/src/new-languages.test.ts +389 -0
- package/src/parser/analyze-conflicts.test.ts +229 -0
- package/src/parser/ar.ts +40 -0
- package/src/parser/create-provider.ts +309 -0
- package/src/parser/de.ts +36 -0
- package/src/parser/es.ts +31 -0
- package/src/parser/fr.ts +31 -0
- package/src/parser/id.ts +34 -0
- package/src/parser/index.ts +50 -0
- package/src/parser/ja.ts +36 -0
- package/src/parser/ko.ts +37 -0
- package/src/parser/locale-manager.test.ts +198 -0
- package/src/parser/locale-manager.ts +197 -0
- package/src/parser/parser-integration.test.ts +439 -0
- package/src/parser/pt.ts +37 -0
- package/src/parser/qu.ts +37 -0
- package/src/parser/sw.ts +37 -0
- package/src/parser/tr.ts +38 -0
- package/src/parser/types.ts +113 -0
- package/src/parser/zh.ts +38 -0
- package/src/plugins/vite.ts +224 -0
- package/src/plugins/webpack.ts +124 -0
- package/src/pluralization.test.ts +197 -0
- package/src/pluralization.ts +393 -0
- package/src/runtime.ts +441 -0
- package/src/ssr-integration.ts +225 -0
- package/src/test-setup.ts +195 -0
- package/src/translation-validation.test.ts +171 -0
- package/src/translator.test.ts +252 -0
- package/src/translator.ts +297 -0
- package/src/types.ts +209 -0
- package/src/utils/locale.ts +190 -0
- package/src/utils/tokenizer-adapter.ts +469 -0
- package/src/utils/tokenizer.ts +19 -0
- package/src/validators/index.ts +174 -0
- package/src/validators/schema.ts +129 -0
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Dictionary Derivation from Semantic Profiles
|
|
3
|
+
*
|
|
4
|
+
* Creates Dictionary objects from LanguageProfile keywords,
|
|
5
|
+
* eliminating keyword duplication between packages.
|
|
6
|
+
*
|
|
7
|
+
* The semantic profile is the single source of truth for keyword translations.
|
|
8
|
+
* This adapter extracts the `primary` value from each KeywordTranslation.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import type { Dictionary, DictionaryCategory } from '../types';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* KeywordTranslation interface (matches @lokascript/semantic)
|
|
15
|
+
*/
|
|
16
|
+
interface KeywordTranslation {
|
|
17
|
+
readonly primary: string;
|
|
18
|
+
readonly alternatives?: string[];
|
|
19
|
+
readonly normalized?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* LanguageProfile interface (subset needed for derivation)
|
|
24
|
+
*/
|
|
25
|
+
interface LanguageProfile {
|
|
26
|
+
readonly code: string;
|
|
27
|
+
readonly keywords: Record<string, KeywordTranslation>;
|
|
28
|
+
readonly references?: Record<string, string>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// =============================================================================
|
|
32
|
+
// Keyword Category Mappings
|
|
33
|
+
// =============================================================================
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Keywords that belong to the 'commands' category.
|
|
37
|
+
*/
|
|
38
|
+
const COMMAND_KEYWORDS = [
|
|
39
|
+
// Event handling
|
|
40
|
+
'on',
|
|
41
|
+
'tell',
|
|
42
|
+
'trigger',
|
|
43
|
+
'send',
|
|
44
|
+
// DOM manipulation
|
|
45
|
+
'take',
|
|
46
|
+
'put',
|
|
47
|
+
'set',
|
|
48
|
+
'get',
|
|
49
|
+
'add',
|
|
50
|
+
'remove',
|
|
51
|
+
'toggle',
|
|
52
|
+
'hide',
|
|
53
|
+
'show',
|
|
54
|
+
// Control flow
|
|
55
|
+
'if',
|
|
56
|
+
'unless',
|
|
57
|
+
'repeat',
|
|
58
|
+
'for',
|
|
59
|
+
'while',
|
|
60
|
+
'until',
|
|
61
|
+
'continue',
|
|
62
|
+
'break',
|
|
63
|
+
'halt',
|
|
64
|
+
// Async
|
|
65
|
+
'wait',
|
|
66
|
+
'fetch',
|
|
67
|
+
'call',
|
|
68
|
+
'return',
|
|
69
|
+
// Other commands
|
|
70
|
+
'make',
|
|
71
|
+
'log',
|
|
72
|
+
'throw',
|
|
73
|
+
'catch',
|
|
74
|
+
'measure',
|
|
75
|
+
'transition',
|
|
76
|
+
// Data commands
|
|
77
|
+
'increment',
|
|
78
|
+
'decrement',
|
|
79
|
+
'bind',
|
|
80
|
+
'default',
|
|
81
|
+
'persist',
|
|
82
|
+
// Navigation
|
|
83
|
+
'go',
|
|
84
|
+
'pushUrl',
|
|
85
|
+
'replaceUrl',
|
|
86
|
+
// Utility
|
|
87
|
+
'copy',
|
|
88
|
+
'pick',
|
|
89
|
+
'beep',
|
|
90
|
+
// Advanced
|
|
91
|
+
'js',
|
|
92
|
+
'async',
|
|
93
|
+
'render',
|
|
94
|
+
// Animation
|
|
95
|
+
'swap',
|
|
96
|
+
'morph',
|
|
97
|
+
'settle',
|
|
98
|
+
// Content
|
|
99
|
+
'append',
|
|
100
|
+
'prepend',
|
|
101
|
+
'clone',
|
|
102
|
+
// Control
|
|
103
|
+
'exit',
|
|
104
|
+
// Behaviors
|
|
105
|
+
'install',
|
|
106
|
+
'behavior',
|
|
107
|
+
'init',
|
|
108
|
+
// Focus
|
|
109
|
+
'focus',
|
|
110
|
+
'blur',
|
|
111
|
+
] as const;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Keywords that belong to the 'modifiers' category.
|
|
115
|
+
*/
|
|
116
|
+
const MODIFIER_KEYWORDS = [
|
|
117
|
+
'to',
|
|
118
|
+
'from',
|
|
119
|
+
'into',
|
|
120
|
+
'with',
|
|
121
|
+
'at',
|
|
122
|
+
'in',
|
|
123
|
+
'of',
|
|
124
|
+
'as',
|
|
125
|
+
'by',
|
|
126
|
+
'before',
|
|
127
|
+
'after',
|
|
128
|
+
'over',
|
|
129
|
+
'under',
|
|
130
|
+
'between',
|
|
131
|
+
'through',
|
|
132
|
+
'without',
|
|
133
|
+
] as const;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Keywords that belong to the 'logical' category.
|
|
137
|
+
*/
|
|
138
|
+
const LOGICAL_KEYWORDS = [
|
|
139
|
+
'and',
|
|
140
|
+
'or',
|
|
141
|
+
'not',
|
|
142
|
+
'is',
|
|
143
|
+
'exists',
|
|
144
|
+
'matches',
|
|
145
|
+
'contains',
|
|
146
|
+
'includes',
|
|
147
|
+
'equals',
|
|
148
|
+
'then',
|
|
149
|
+
'else',
|
|
150
|
+
'otherwise',
|
|
151
|
+
'end',
|
|
152
|
+
] as const;
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Keywords that belong to the 'expressions' category.
|
|
156
|
+
*/
|
|
157
|
+
const EXPRESSION_KEYWORDS = [
|
|
158
|
+
// Positional
|
|
159
|
+
'first',
|
|
160
|
+
'last',
|
|
161
|
+
'next',
|
|
162
|
+
'previous',
|
|
163
|
+
'prev',
|
|
164
|
+
'random',
|
|
165
|
+
// DOM traversal
|
|
166
|
+
'closest',
|
|
167
|
+
'parent',
|
|
168
|
+
'children',
|
|
169
|
+
'within',
|
|
170
|
+
// Emptiness/existence
|
|
171
|
+
'no',
|
|
172
|
+
'empty',
|
|
173
|
+
'some',
|
|
174
|
+
] as const;
|
|
175
|
+
|
|
176
|
+
// =============================================================================
|
|
177
|
+
// Derivation Functions
|
|
178
|
+
// =============================================================================
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Extract primary translations for a list of keywords from the profile.
|
|
182
|
+
* Returns a Record<string, string> mapping English key to translated value.
|
|
183
|
+
*/
|
|
184
|
+
function extractCategory(
|
|
185
|
+
keywords: Record<string, KeywordTranslation>,
|
|
186
|
+
keys: readonly string[]
|
|
187
|
+
): Record<string, string> {
|
|
188
|
+
const result: Record<string, string> = {};
|
|
189
|
+
for (const key of keys) {
|
|
190
|
+
const translation = keywords[key];
|
|
191
|
+
if (translation) {
|
|
192
|
+
result[key] = translation.primary;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
return result;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Extract references from the profile (me, it, you, etc.).
|
|
200
|
+
*/
|
|
201
|
+
function extractReferences(references: Record<string, string> | undefined): Record<string, string> {
|
|
202
|
+
if (!references) return {};
|
|
203
|
+
return { ...references };
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Merge base dictionary with profile-derived values.
|
|
208
|
+
* Profile values take precedence (they're the source of truth).
|
|
209
|
+
*/
|
|
210
|
+
function mergeWithFallback(
|
|
211
|
+
derived: Record<string, string>,
|
|
212
|
+
fallback: Record<string, string>
|
|
213
|
+
): Record<string, string> {
|
|
214
|
+
return { ...fallback, ...derived };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// =============================================================================
|
|
218
|
+
// Fallback Dictionaries
|
|
219
|
+
// =============================================================================
|
|
220
|
+
|
|
221
|
+
/**
|
|
222
|
+
* Event names - these are typically not in profiles since they're
|
|
223
|
+
* DOM standard events. Localization is optional.
|
|
224
|
+
*/
|
|
225
|
+
const EVENT_FALLBACK: Record<string, string> = {
|
|
226
|
+
click: 'click',
|
|
227
|
+
dblclick: 'dblclick',
|
|
228
|
+
mousedown: 'mousedown',
|
|
229
|
+
mouseup: 'mouseup',
|
|
230
|
+
mouseenter: 'mouseenter',
|
|
231
|
+
mouseleave: 'mouseleave',
|
|
232
|
+
mouseover: 'mouseover',
|
|
233
|
+
mouseout: 'mouseout',
|
|
234
|
+
mousemove: 'mousemove',
|
|
235
|
+
keydown: 'keydown',
|
|
236
|
+
keyup: 'keyup',
|
|
237
|
+
keypress: 'keypress',
|
|
238
|
+
focus: 'focus',
|
|
239
|
+
blur: 'blur',
|
|
240
|
+
change: 'change',
|
|
241
|
+
input: 'input',
|
|
242
|
+
submit: 'submit',
|
|
243
|
+
reset: 'reset',
|
|
244
|
+
load: 'load',
|
|
245
|
+
unload: 'unload',
|
|
246
|
+
resize: 'resize',
|
|
247
|
+
scroll: 'scroll',
|
|
248
|
+
touchstart: 'touchstart',
|
|
249
|
+
touchend: 'touchend',
|
|
250
|
+
touchmove: 'touchmove',
|
|
251
|
+
touchcancel: 'touchcancel',
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Temporal keywords - time units.
|
|
256
|
+
*/
|
|
257
|
+
const TEMPORAL_FALLBACK: Record<string, string> = {
|
|
258
|
+
seconds: 'seconds',
|
|
259
|
+
second: 'second',
|
|
260
|
+
milliseconds: 'milliseconds',
|
|
261
|
+
millisecond: 'millisecond',
|
|
262
|
+
minutes: 'minutes',
|
|
263
|
+
minute: 'minute',
|
|
264
|
+
hours: 'hours',
|
|
265
|
+
hour: 'hour',
|
|
266
|
+
ms: 'ms',
|
|
267
|
+
s: 's',
|
|
268
|
+
min: 'min',
|
|
269
|
+
h: 'h',
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Value keywords - literals and references.
|
|
274
|
+
*/
|
|
275
|
+
const VALUES_FALLBACK: Record<string, string> = {
|
|
276
|
+
true: 'true',
|
|
277
|
+
false: 'false',
|
|
278
|
+
null: 'null',
|
|
279
|
+
undefined: 'undefined',
|
|
280
|
+
element: 'element',
|
|
281
|
+
window: 'window',
|
|
282
|
+
document: 'document',
|
|
283
|
+
value: 'value',
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Attribute keywords.
|
|
288
|
+
*/
|
|
289
|
+
const ATTRIBUTES_FALLBACK: Record<string, string> = {
|
|
290
|
+
class: 'class',
|
|
291
|
+
classes: 'classes',
|
|
292
|
+
style: 'style',
|
|
293
|
+
styles: 'styles',
|
|
294
|
+
attribute: 'attribute',
|
|
295
|
+
attributes: 'attributes',
|
|
296
|
+
property: 'property',
|
|
297
|
+
properties: 'properties',
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
/**
|
|
301
|
+
* Expression keywords fallback.
|
|
302
|
+
*/
|
|
303
|
+
const EXPRESSIONS_FALLBACK: Record<string, string> = {
|
|
304
|
+
at: 'at',
|
|
305
|
+
'starts with': 'starts with',
|
|
306
|
+
'ends with': 'ends with',
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
// =============================================================================
|
|
310
|
+
// Main Derivation Function
|
|
311
|
+
// =============================================================================
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Options for dictionary derivation.
|
|
315
|
+
*/
|
|
316
|
+
export interface DeriveOptions {
|
|
317
|
+
/**
|
|
318
|
+
* Override specific categories with custom values.
|
|
319
|
+
* Useful for categories not fully covered by the profile.
|
|
320
|
+
*/
|
|
321
|
+
overrides?: Partial<Dictionary>;
|
|
322
|
+
|
|
323
|
+
/**
|
|
324
|
+
* Whether to include English fallbacks for missing entries.
|
|
325
|
+
* Default: true
|
|
326
|
+
*/
|
|
327
|
+
includeFallbacks?: boolean;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Derive a Dictionary from a LanguageProfile.
|
|
332
|
+
*
|
|
333
|
+
* This is the main adapter function that creates dictionaries from
|
|
334
|
+
* semantic profiles, eliminating keyword duplication.
|
|
335
|
+
*
|
|
336
|
+
* @param profile - The language profile to derive from
|
|
337
|
+
* @param options - Derivation options
|
|
338
|
+
* @returns A complete Dictionary object
|
|
339
|
+
*
|
|
340
|
+
* @example
|
|
341
|
+
* ```typescript
|
|
342
|
+
* import { japaneseProfile } from '@lokascript/semantic';
|
|
343
|
+
* import { deriveFromProfile } from './derive';
|
|
344
|
+
*
|
|
345
|
+
* export const ja = deriveFromProfile(japaneseProfile);
|
|
346
|
+
* ```
|
|
347
|
+
*/
|
|
348
|
+
export function deriveFromProfile(
|
|
349
|
+
profile: LanguageProfile,
|
|
350
|
+
options: DeriveOptions = {}
|
|
351
|
+
): Dictionary {
|
|
352
|
+
const { overrides = {}, includeFallbacks = true } = options;
|
|
353
|
+
const { keywords, references } = profile;
|
|
354
|
+
|
|
355
|
+
// Extract from profile keywords
|
|
356
|
+
const derivedCommands = extractCategory(keywords, COMMAND_KEYWORDS);
|
|
357
|
+
const derivedModifiers = extractCategory(keywords, MODIFIER_KEYWORDS);
|
|
358
|
+
const derivedLogical = extractCategory(keywords, LOGICAL_KEYWORDS);
|
|
359
|
+
const derivedExpressions = extractCategory(keywords, EXPRESSION_KEYWORDS);
|
|
360
|
+
|
|
361
|
+
// Extract references (me, it, you, etc.) and add to values
|
|
362
|
+
const derivedReferences = extractReferences(references);
|
|
363
|
+
|
|
364
|
+
// Build the dictionary with fallbacks
|
|
365
|
+
const dictionary: Dictionary = {
|
|
366
|
+
commands: includeFallbacks ? mergeWithFallback(derivedCommands, {}) : derivedCommands,
|
|
367
|
+
|
|
368
|
+
modifiers: includeFallbacks ? mergeWithFallback(derivedModifiers, {}) : derivedModifiers,
|
|
369
|
+
|
|
370
|
+
events: includeFallbacks ? { ...EVENT_FALLBACK } : {},
|
|
371
|
+
|
|
372
|
+
logical: includeFallbacks ? mergeWithFallback(derivedLogical, {}) : derivedLogical,
|
|
373
|
+
|
|
374
|
+
temporal: includeFallbacks ? { ...TEMPORAL_FALLBACK } : {},
|
|
375
|
+
|
|
376
|
+
values: includeFallbacks
|
|
377
|
+
? mergeWithFallback(derivedReferences, VALUES_FALLBACK)
|
|
378
|
+
: derivedReferences,
|
|
379
|
+
|
|
380
|
+
attributes: includeFallbacks ? { ...ATTRIBUTES_FALLBACK } : {},
|
|
381
|
+
|
|
382
|
+
expressions: includeFallbacks
|
|
383
|
+
? mergeWithFallback(derivedExpressions, EXPRESSIONS_FALLBACK)
|
|
384
|
+
: derivedExpressions,
|
|
385
|
+
};
|
|
386
|
+
|
|
387
|
+
// Apply overrides (for language-specific customizations)
|
|
388
|
+
if (overrides.commands) {
|
|
389
|
+
dictionary.commands = { ...dictionary.commands, ...overrides.commands };
|
|
390
|
+
}
|
|
391
|
+
if (overrides.modifiers) {
|
|
392
|
+
dictionary.modifiers = { ...dictionary.modifiers, ...overrides.modifiers };
|
|
393
|
+
}
|
|
394
|
+
if (overrides.events) {
|
|
395
|
+
dictionary.events = { ...dictionary.events, ...overrides.events };
|
|
396
|
+
}
|
|
397
|
+
if (overrides.logical) {
|
|
398
|
+
dictionary.logical = { ...dictionary.logical, ...overrides.logical };
|
|
399
|
+
}
|
|
400
|
+
if (overrides.temporal) {
|
|
401
|
+
dictionary.temporal = { ...dictionary.temporal, ...overrides.temporal };
|
|
402
|
+
}
|
|
403
|
+
if (overrides.values) {
|
|
404
|
+
dictionary.values = { ...dictionary.values, ...overrides.values };
|
|
405
|
+
}
|
|
406
|
+
if (overrides.attributes) {
|
|
407
|
+
dictionary.attributes = { ...dictionary.attributes, ...overrides.attributes };
|
|
408
|
+
}
|
|
409
|
+
if (overrides.expressions) {
|
|
410
|
+
dictionary.expressions = { ...dictionary.expressions, ...overrides.expressions };
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return dictionary;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/**
|
|
417
|
+
* Create an English dictionary (identity mapping).
|
|
418
|
+
* Special case since English keywords map to themselves.
|
|
419
|
+
*/
|
|
420
|
+
export function createEnglishDictionary(): Dictionary {
|
|
421
|
+
const identity = (keys: readonly string[]): Record<string, string> => {
|
|
422
|
+
const result: Record<string, string> = {};
|
|
423
|
+
for (const key of keys) {
|
|
424
|
+
result[key] = key;
|
|
425
|
+
}
|
|
426
|
+
return result;
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
return {
|
|
430
|
+
commands: identity(COMMAND_KEYWORDS),
|
|
431
|
+
modifiers: identity(MODIFIER_KEYWORDS),
|
|
432
|
+
events: { ...EVENT_FALLBACK },
|
|
433
|
+
logical: identity(LOGICAL_KEYWORDS),
|
|
434
|
+
temporal: { ...TEMPORAL_FALLBACK },
|
|
435
|
+
values: {
|
|
436
|
+
...VALUES_FALLBACK,
|
|
437
|
+
it: 'it',
|
|
438
|
+
its: 'its',
|
|
439
|
+
me: 'me',
|
|
440
|
+
my: 'my',
|
|
441
|
+
myself: 'myself',
|
|
442
|
+
you: 'you',
|
|
443
|
+
your: 'your',
|
|
444
|
+
yourself: 'yourself',
|
|
445
|
+
target: 'target',
|
|
446
|
+
detail: 'detail',
|
|
447
|
+
event: 'event',
|
|
448
|
+
body: 'body',
|
|
449
|
+
result: 'result',
|
|
450
|
+
},
|
|
451
|
+
attributes: { ...ATTRIBUTES_FALLBACK },
|
|
452
|
+
expressions: {
|
|
453
|
+
...identity(EXPRESSION_KEYWORDS),
|
|
454
|
+
...EXPRESSIONS_FALLBACK,
|
|
455
|
+
},
|
|
456
|
+
};
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
// =============================================================================
|
|
460
|
+
// Validation Utilities
|
|
461
|
+
// =============================================================================
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* Compare a derived dictionary against an original to find missing entries.
|
|
465
|
+
* Useful for validating that derivation is complete.
|
|
466
|
+
*/
|
|
467
|
+
export function validateDictionary(
|
|
468
|
+
derived: Dictionary,
|
|
469
|
+
original: Dictionary
|
|
470
|
+
): { missing: Record<DictionaryCategory, string[]>; coverage: number } {
|
|
471
|
+
const categories: DictionaryCategory[] = [
|
|
472
|
+
'commands',
|
|
473
|
+
'modifiers',
|
|
474
|
+
'events',
|
|
475
|
+
'logical',
|
|
476
|
+
'temporal',
|
|
477
|
+
'values',
|
|
478
|
+
'attributes',
|
|
479
|
+
'expressions',
|
|
480
|
+
];
|
|
481
|
+
|
|
482
|
+
const missing: Record<DictionaryCategory, string[]> = {
|
|
483
|
+
commands: [],
|
|
484
|
+
modifiers: [],
|
|
485
|
+
events: [],
|
|
486
|
+
logical: [],
|
|
487
|
+
temporal: [],
|
|
488
|
+
values: [],
|
|
489
|
+
attributes: [],
|
|
490
|
+
expressions: [],
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
let totalOriginal = 0;
|
|
494
|
+
let totalDerived = 0;
|
|
495
|
+
|
|
496
|
+
for (const category of categories) {
|
|
497
|
+
const origKeys = Object.keys(original[category]);
|
|
498
|
+
const derivedKeys = new Set(Object.keys(derived[category]));
|
|
499
|
+
|
|
500
|
+
totalOriginal += origKeys.length;
|
|
501
|
+
|
|
502
|
+
for (const key of origKeys) {
|
|
503
|
+
if (derivedKeys.has(key)) {
|
|
504
|
+
totalDerived++;
|
|
505
|
+
} else {
|
|
506
|
+
missing[category].push(key);
|
|
507
|
+
}
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
return {
|
|
512
|
+
missing,
|
|
513
|
+
coverage: totalOriginal > 0 ? totalDerived / totalOriginal : 1,
|
|
514
|
+
};
|
|
515
|
+
}
|