@lokascript/hyperscript-adapter 2.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/LICENSE +21 -0
- package/README.md +147 -0
- package/dist/hyperscript-i18n-ar.global.js +4 -0
- package/dist/hyperscript-i18n-bn.global.js +4 -0
- package/dist/hyperscript-i18n-de.global.js +4 -0
- package/dist/hyperscript-i18n-east-asian.global.js +4 -0
- package/dist/hyperscript-i18n-en.global.js +6 -0
- package/dist/hyperscript-i18n-es.global.js +4 -0
- package/dist/hyperscript-i18n-fr.global.js +4 -0
- package/dist/hyperscript-i18n-he.global.js +4 -0
- package/dist/hyperscript-i18n-hi.global.js +4 -0
- package/dist/hyperscript-i18n-id.global.js +4 -0
- package/dist/hyperscript-i18n-it.global.js +4 -0
- package/dist/hyperscript-i18n-ja.global.js +4 -0
- package/dist/hyperscript-i18n-ko.global.js +4 -0
- package/dist/hyperscript-i18n-lite.global.js +1 -0
- package/dist/hyperscript-i18n-ms.global.js +4 -0
- package/dist/hyperscript-i18n-pl.global.js +4 -0
- package/dist/hyperscript-i18n-pt.global.js +4 -0
- package/dist/hyperscript-i18n-qu.global.js +4 -0
- package/dist/hyperscript-i18n-ru.global.js +4 -0
- package/dist/hyperscript-i18n-slavic.global.js +4 -0
- package/dist/hyperscript-i18n-south-asian.global.js +4 -0
- package/dist/hyperscript-i18n-southeast-asian.global.js +4 -0
- package/dist/hyperscript-i18n-sw.global.js +4 -0
- package/dist/hyperscript-i18n-th.global.js +4 -0
- package/dist/hyperscript-i18n-tl.global.js +4 -0
- package/dist/hyperscript-i18n-tr.global.js +4 -0
- package/dist/hyperscript-i18n-uk.global.js +4 -0
- package/dist/hyperscript-i18n-vi.global.js +4 -0
- package/dist/hyperscript-i18n-western.global.js +4 -0
- package/dist/hyperscript-i18n-zh.global.js +4 -0
- package/dist/hyperscript-i18n.global.js +8 -0
- package/dist/hyperscript-i18n.global.js.map +1 -0
- package/dist/index.cjs +203 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +99 -0
- package/dist/index.d.ts +99 -0
- package/dist/index.js +179 -0
- package/dist/index.js.map +1 -0
- package/package.json +169 -0
- package/src/browser-lite.ts +159 -0
- package/src/browser.ts +74 -0
- package/src/bundles/ar.ts +6 -0
- package/src/bundles/bn.ts +6 -0
- package/src/bundles/de.ts +6 -0
- package/src/bundles/east-asian.ts +8 -0
- package/src/bundles/en.ts +6 -0
- package/src/bundles/es.ts +6 -0
- package/src/bundles/fr.ts +6 -0
- package/src/bundles/he.ts +6 -0
- package/src/bundles/hi.ts +6 -0
- package/src/bundles/id.ts +6 -0
- package/src/bundles/it.ts +6 -0
- package/src/bundles/ja.ts +6 -0
- package/src/bundles/ko.ts +6 -0
- package/src/bundles/ms.ts +6 -0
- package/src/bundles/pl.ts +6 -0
- package/src/bundles/pt.ts +6 -0
- package/src/bundles/qu.ts +6 -0
- package/src/bundles/ru.ts +6 -0
- package/src/bundles/shared.ts +65 -0
- package/src/bundles/slavic.ts +8 -0
- package/src/bundles/south-asian.ts +7 -0
- package/src/bundles/southeast-asian.ts +10 -0
- package/src/bundles/sw.ts +6 -0
- package/src/bundles/th.ts +6 -0
- package/src/bundles/tl.ts +6 -0
- package/src/bundles/tr.ts +6 -0
- package/src/bundles/uk.ts +6 -0
- package/src/bundles/vi.ts +6 -0
- package/src/bundles/western.ts +9 -0
- package/src/bundles/zh.ts +6 -0
- package/src/derive-syntax.ts +126 -0
- package/src/generated/syntax-table.ts +61 -0
- package/src/hyperscript-renderer.ts +150 -0
- package/src/index.ts +27 -0
- package/src/language-resolver.ts +41 -0
- package/src/plugin.ts +119 -0
- package/src/preprocessor.ts +251 -0
- package/src/slim-plugin.ts +60 -0
- package/src/slim-preprocessor.ts +190 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Self-contained: Turkish (renders to English via custom renderer) */
|
|
2
|
+
import '@lokascript/semantic/languages/tr';
|
|
3
|
+
export { plugin, preprocess, resolveLanguage } from './shared';
|
|
4
|
+
export const supportedLanguages = ['tr'];
|
|
5
|
+
import { autoRegister } from './shared';
|
|
6
|
+
autoRegister();
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Self-contained: Ukrainian (renders to English via custom renderer) */
|
|
2
|
+
import '@lokascript/semantic/languages/uk';
|
|
3
|
+
export { plugin, preprocess, resolveLanguage } from './shared';
|
|
4
|
+
export const supportedLanguages = ['uk'];
|
|
5
|
+
import { autoRegister } from './shared';
|
|
6
|
+
autoRegister();
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Self-contained: Vietnamese (renders to English via custom renderer) */
|
|
2
|
+
import '@lokascript/semantic/languages/vi';
|
|
3
|
+
export { plugin, preprocess, resolveLanguage } from './shared';
|
|
4
|
+
export const supportedLanguages = ['vi'];
|
|
5
|
+
import { autoRegister } from './shared';
|
|
6
|
+
autoRegister();
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** Self-contained: Western/European languages (es, pt, fr, de) */
|
|
2
|
+
import '@lokascript/semantic/languages/es';
|
|
3
|
+
import '@lokascript/semantic/languages/pt';
|
|
4
|
+
import '@lokascript/semantic/languages/fr';
|
|
5
|
+
import '@lokascript/semantic/languages/de';
|
|
6
|
+
export { plugin, preprocess, resolveLanguage } from './shared';
|
|
7
|
+
export const supportedLanguages = ['es', 'pt', 'fr', 'de'];
|
|
8
|
+
import { autoRegister } from './shared';
|
|
9
|
+
autoRegister();
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/** Self-contained: Chinese (renders to English via custom renderer) */
|
|
2
|
+
import '@lokascript/semantic/languages/zh';
|
|
3
|
+
export { plugin, preprocess, resolveLanguage } from './shared';
|
|
4
|
+
export const supportedLanguages = ['zh'];
|
|
5
|
+
import { autoRegister } from './shared';
|
|
6
|
+
autoRegister();
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Derive SYNTAX tables from CommandSchema definitions.
|
|
3
|
+
*
|
|
4
|
+
* Instead of maintaining a hand-coded SYNTAX table that can drift from
|
|
5
|
+
* the canonical schemas, this module generates it from the semantic
|
|
6
|
+
* package's command-schemas + language profile.
|
|
7
|
+
*
|
|
8
|
+
* The generated table maps each command to ordered [role, preposition]
|
|
9
|
+
* tuples for rendering.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { CommandSchema, RoleSpec, LanguageProfile } from '@lokascript/semantic';
|
|
13
|
+
import type { RoleMarker } from '@lokascript/semantic/core';
|
|
14
|
+
|
|
15
|
+
// ---------------------------------------------------------------------------
|
|
16
|
+
// Types
|
|
17
|
+
// ---------------------------------------------------------------------------
|
|
18
|
+
|
|
19
|
+
export type SyntaxEntry = readonly [string, string][];
|
|
20
|
+
export type SyntaxTable = Record<string, SyntaxEntry>;
|
|
21
|
+
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Overrides for commands whose rendering doesn't cleanly derive from
|
|
24
|
+
// schema roles (different role names, simplified structure, etc.)
|
|
25
|
+
// ---------------------------------------------------------------------------
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Commands where the SYNTAX rendering uses a different mapping
|
|
29
|
+
* than what derives from schema roles + language profile.
|
|
30
|
+
*
|
|
31
|
+
* - repeat: Schema has loopType/quantity/event/source, but English renders
|
|
32
|
+
* as "repeat N" or "repeat until <condition>" using simplified roles
|
|
33
|
+
*/
|
|
34
|
+
const RENDER_OVERRIDES: SyntaxTable = {
|
|
35
|
+
repeat: [
|
|
36
|
+
['quantity', ''],
|
|
37
|
+
['condition', 'until'],
|
|
38
|
+
],
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
// Derivation
|
|
43
|
+
// ---------------------------------------------------------------------------
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Resolve the preposition for a role in a specific command and language.
|
|
47
|
+
*
|
|
48
|
+
* Priority:
|
|
49
|
+
* 1. Render-specific override for this language (renderOverride[lang])
|
|
50
|
+
* 2. Command-specific marker override for this language (markerOverride[lang])
|
|
51
|
+
* 3. Language profile's default roleMarker for this role
|
|
52
|
+
* 4. '' (no preposition)
|
|
53
|
+
*/
|
|
54
|
+
function resolvePreposition(roleSpec: RoleSpec, profile: LanguageProfile, lang: string): string {
|
|
55
|
+
// 1. Render-time override for this language
|
|
56
|
+
if (roleSpec.renderOverride?.[lang] !== undefined) {
|
|
57
|
+
return roleSpec.renderOverride[lang];
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 2. Command-specific marker override for this language
|
|
61
|
+
if (roleSpec.markerOverride?.[lang] !== undefined) {
|
|
62
|
+
return roleSpec.markerOverride[lang];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// 3. Profile default for this role
|
|
66
|
+
const roleMarker = (profile.roleMarkers as Record<string, RoleMarker | undefined>)[roleSpec.role];
|
|
67
|
+
if (roleMarker) {
|
|
68
|
+
return roleMarker.primary;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 4. No preposition
|
|
72
|
+
return '';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Derive the SYNTAX table from command schemas and a language profile.
|
|
77
|
+
*
|
|
78
|
+
* For each command:
|
|
79
|
+
* - Sort roles by position (svoPosition for SVO/VSO, sovPosition for SOV)
|
|
80
|
+
* - Resolve each role's preposition for the given language
|
|
81
|
+
* - Return [role, preposition] tuples
|
|
82
|
+
*
|
|
83
|
+
* Commands in RENDER_OVERRIDES use their manual entry instead (English only).
|
|
84
|
+
*/
|
|
85
|
+
export function deriveSyntax(
|
|
86
|
+
schemas: Record<string, CommandSchema>,
|
|
87
|
+
profile: LanguageProfile,
|
|
88
|
+
lang: string
|
|
89
|
+
): SyntaxTable {
|
|
90
|
+
const syntax: Record<string, [string, string][]> = {};
|
|
91
|
+
const isSov = profile.wordOrder === 'SOV';
|
|
92
|
+
|
|
93
|
+
for (const [action, schema] of Object.entries(schemas)) {
|
|
94
|
+
// Use manual override for commands that don't derive cleanly (English only)
|
|
95
|
+
if (lang === 'en' && RENDER_OVERRIDES[action]) {
|
|
96
|
+
syntax[action] = RENDER_OVERRIDES[action] as [string, string][];
|
|
97
|
+
continue;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Sort roles by position appropriate for word order
|
|
101
|
+
const sortedRoles = [...schema.roles].sort((a, b) => {
|
|
102
|
+
const posA = isSov ? (a.sovPosition ?? 99) : (a.svoPosition ?? 99);
|
|
103
|
+
const posB = isSov ? (b.sovPosition ?? 99) : (b.svoPosition ?? 99);
|
|
104
|
+
return posA - posB;
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
// Derive [role, preposition] tuples
|
|
108
|
+
syntax[action] = sortedRoles.map(roleSpec => [
|
|
109
|
+
roleSpec.role,
|
|
110
|
+
resolvePreposition(roleSpec, profile, lang),
|
|
111
|
+
]);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return syntax;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Derive the English SYNTAX table.
|
|
119
|
+
* Convenience wrapper that calls deriveSyntax with 'en'.
|
|
120
|
+
*/
|
|
121
|
+
export function deriveEnglishSyntax(
|
|
122
|
+
schemas: Record<string, CommandSchema>,
|
|
123
|
+
englishProfile: LanguageProfile
|
|
124
|
+
): SyntaxTable {
|
|
125
|
+
return deriveSyntax(schemas, englishProfile, 'en');
|
|
126
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
// @generated — do not edit manually.
|
|
2
|
+
// Regenerate with: npm run generate:syntax
|
|
3
|
+
//
|
|
4
|
+
// Derived from command schemas + English profile via deriveEnglishSyntax().
|
|
5
|
+
|
|
6
|
+
export const SYNTAX: Record<string, readonly [string, string][]> = {
|
|
7
|
+
add: [['patient', ''], ['destination', 'to']],
|
|
8
|
+
append: [['patient', ''], ['destination', 'to']],
|
|
9
|
+
async: [],
|
|
10
|
+
beep: [['patient', '']],
|
|
11
|
+
behavior: [['patient', '']],
|
|
12
|
+
blur: [['patient', '']],
|
|
13
|
+
break: [],
|
|
14
|
+
call: [['patient', '']],
|
|
15
|
+
clone: [['patient', ''], ['destination', 'into']],
|
|
16
|
+
compound: [],
|
|
17
|
+
continue: [],
|
|
18
|
+
copy: [['patient', '']],
|
|
19
|
+
decrement: [['patient', ''], ['quantity', 'by']],
|
|
20
|
+
default: [['destination', ''], ['patient', 'to']],
|
|
21
|
+
else: [],
|
|
22
|
+
exit: [],
|
|
23
|
+
fetch: [['source', ''], ['responseType', 'as'], ['method', 'via'], ['destination', 'on']],
|
|
24
|
+
focus: [['patient', '']],
|
|
25
|
+
for: [['patient', ''], ['source', 'in']],
|
|
26
|
+
get: [['source', ''], ['destination', 'on']],
|
|
27
|
+
go: [['destination', '']],
|
|
28
|
+
halt: [['patient', '']],
|
|
29
|
+
hide: [['patient', ''], ['style', 'with']],
|
|
30
|
+
if: [['condition', '']],
|
|
31
|
+
increment: [['patient', ''], ['quantity', 'by']],
|
|
32
|
+
init: [],
|
|
33
|
+
install: [['patient', ''], ['destination', 'on']],
|
|
34
|
+
js: [['patient', '']],
|
|
35
|
+
log: [['patient', '']],
|
|
36
|
+
make: [['patient', '']],
|
|
37
|
+
measure: [['patient', ''], ['source', 'of']],
|
|
38
|
+
morph: [['destination', ''], ['patient', 'to']],
|
|
39
|
+
on: [['event', ''], ['source', 'from']],
|
|
40
|
+
pick: [['patient', ''], ['source', 'from']],
|
|
41
|
+
prepend: [['patient', ''], ['destination', 'to']],
|
|
42
|
+
put: [['patient', ''], ['destination', 'into']],
|
|
43
|
+
remove: [['patient', ''], ['source', 'from']],
|
|
44
|
+
render: [['patient', ''], ['style', 'with']],
|
|
45
|
+
repeat: [['quantity', ''], ['condition', 'until']],
|
|
46
|
+
return: [['patient', '']],
|
|
47
|
+
send: [['event', ''], ['destination', 'to']],
|
|
48
|
+
set: [['destination', ''], ['patient', 'to']],
|
|
49
|
+
settle: [['patient', '']],
|
|
50
|
+
show: [['patient', ''], ['style', 'with']],
|
|
51
|
+
swap: [['method', ''], ['destination', 'of'], ['patient', 'with']],
|
|
52
|
+
take: [['patient', ''], ['source', 'from']],
|
|
53
|
+
tell: [['destination', '']],
|
|
54
|
+
throw: [['patient', '']],
|
|
55
|
+
toggle: [['patient', ''], ['destination', 'on']],
|
|
56
|
+
transition: [['patient', ''], ['goal', 'to'], ['destination', 'on'], ['duration', 'over'], ['style', 'with']],
|
|
57
|
+
trigger: [['event', ''], ['destination', 'on']],
|
|
58
|
+
unless: [['condition', '']],
|
|
59
|
+
wait: [['patient', '']],
|
|
60
|
+
while: [['condition', '']],
|
|
61
|
+
};
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hyperscript English Renderer
|
|
3
|
+
*
|
|
4
|
+
* Purpose-built renderer that converts SemanticNodes to standard English
|
|
5
|
+
* _hyperscript syntax. Unlike the semantic package's renderer (which uses
|
|
6
|
+
* pattern matching and requires English patterns/tokenizer/profile),
|
|
7
|
+
* this is a deterministic mapping — no registry lookups needed.
|
|
8
|
+
*
|
|
9
|
+
* This eliminates ~35 KB of English language data from per-language bundles.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type {
|
|
13
|
+
SemanticNode,
|
|
14
|
+
SemanticValue,
|
|
15
|
+
SemanticRole,
|
|
16
|
+
EventHandlerSemanticNode,
|
|
17
|
+
CompoundSemanticNode,
|
|
18
|
+
} from '@lokascript/semantic/core';
|
|
19
|
+
|
|
20
|
+
// ---------------------------------------------------------------------------
|
|
21
|
+
// Per-command syntax: ordered [role, preposition] tuples.
|
|
22
|
+
// Empty string = no preposition (direct object).
|
|
23
|
+
//
|
|
24
|
+
// Generated from command schemas. Do not edit manually.
|
|
25
|
+
// Regenerate with: npm run generate:syntax
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
import { SYNTAX } from './generated/syntax-table';
|
|
29
|
+
export { SYNTAX };
|
|
30
|
+
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// Main entry point
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Render a SemanticNode to standard English _hyperscript syntax.
|
|
37
|
+
*/
|
|
38
|
+
export function renderToHyperscript(node: SemanticNode): string {
|
|
39
|
+
switch (node.kind) {
|
|
40
|
+
case 'event-handler':
|
|
41
|
+
return renderEventHandler(node as EventHandlerSemanticNode);
|
|
42
|
+
case 'compound':
|
|
43
|
+
return renderCompound(node as CompoundSemanticNode);
|
|
44
|
+
default:
|
|
45
|
+
return renderCommand(node);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Node-kind renderers
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
function renderEventHandler(node: EventHandlerSemanticNode): string {
|
|
54
|
+
const parts: string[] = ['on'];
|
|
55
|
+
|
|
56
|
+
// Event name
|
|
57
|
+
const event = node.roles.get('event');
|
|
58
|
+
if (event) {
|
|
59
|
+
parts.push(renderValue(event));
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Event source (from #element)
|
|
63
|
+
const source = node.roles.get('source');
|
|
64
|
+
if (source) {
|
|
65
|
+
parts.push('from', renderValue(source));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Body commands
|
|
69
|
+
if (node.body && node.body.length > 0) {
|
|
70
|
+
const bodyParts = node.body.map(renderToHyperscript);
|
|
71
|
+
parts.push(bodyParts.join(' then '));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return parts.join(' ');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function renderCompound(node: CompoundSemanticNode): string {
|
|
78
|
+
const chainWord = node.chainType === 'async' ? 'async' : node.chainType;
|
|
79
|
+
return node.statements.map(renderToHyperscript).join(` ${chainWord} `);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function renderCommand(node: SemanticNode): string {
|
|
83
|
+
const syntax = SYNTAX[node.action];
|
|
84
|
+
|
|
85
|
+
// Known command: use syntax table
|
|
86
|
+
if (syntax) {
|
|
87
|
+
const parts: string[] = [node.action];
|
|
88
|
+
for (const [role, prep] of syntax) {
|
|
89
|
+
const value = node.roles.get(role as SemanticRole);
|
|
90
|
+
if (!value) continue;
|
|
91
|
+
// Skip implicit "me" destination (default in _hyperscript)
|
|
92
|
+
if (role === 'destination' && value.type === 'reference' && value.value === 'me') continue;
|
|
93
|
+
if (prep) parts.push(prep);
|
|
94
|
+
parts.push(renderValue(value));
|
|
95
|
+
}
|
|
96
|
+
return parts.join(' ');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Unknown command: generic fallback — action then all roles
|
|
100
|
+
const parts: string[] = [node.action];
|
|
101
|
+
for (const [, value] of node.roles) {
|
|
102
|
+
parts.push(renderValue(value));
|
|
103
|
+
}
|
|
104
|
+
return parts.join(' ');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ---------------------------------------------------------------------------
|
|
108
|
+
// Value renderers
|
|
109
|
+
// ---------------------------------------------------------------------------
|
|
110
|
+
|
|
111
|
+
function renderValue(value: SemanticValue): string {
|
|
112
|
+
switch (value.type) {
|
|
113
|
+
case 'literal':
|
|
114
|
+
if (typeof value.value === 'string' && value.dataType === 'string') {
|
|
115
|
+
return `"${value.value}"`;
|
|
116
|
+
}
|
|
117
|
+
return String(value.value);
|
|
118
|
+
|
|
119
|
+
case 'selector':
|
|
120
|
+
return value.value;
|
|
121
|
+
|
|
122
|
+
case 'reference':
|
|
123
|
+
return value.value;
|
|
124
|
+
|
|
125
|
+
case 'property-path':
|
|
126
|
+
return renderPropertyPath(value);
|
|
127
|
+
|
|
128
|
+
case 'expression':
|
|
129
|
+
return value.raw;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function renderPropertyPath(value: SemanticValue & { type: 'property-path' }): string {
|
|
134
|
+
const objectStr = renderValue(value.object);
|
|
135
|
+
const property = value.property;
|
|
136
|
+
|
|
137
|
+
// English possessive special forms
|
|
138
|
+
if (value.object.type === 'reference') {
|
|
139
|
+
switch (value.object.value) {
|
|
140
|
+
case 'me':
|
|
141
|
+
return `my ${property}`;
|
|
142
|
+
case 'it':
|
|
143
|
+
return `its ${property}`;
|
|
144
|
+
case 'you':
|
|
145
|
+
return `your ${property}`;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
return `${objectStr}'s ${property}`;
|
|
150
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @lokascript/hyperscript-adapter
|
|
3
|
+
*
|
|
4
|
+
* Multilingual adapter plugin for the original _hyperscript.
|
|
5
|
+
* Enables writing hyperscript in any of 24 supported languages.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* import { hyperscriptI18n } from '@lokascript/hyperscript-adapter';
|
|
9
|
+
* _hyperscript.use(hyperscriptI18n());
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* // With options
|
|
13
|
+
* _hyperscript.use(hyperscriptI18n({
|
|
14
|
+
* defaultLanguage: 'ja',
|
|
15
|
+
* debug: true,
|
|
16
|
+
* }));
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* // Standalone preprocessing (for _hyperscript.evaluate() calls)
|
|
20
|
+
* import { preprocess } from '@lokascript/hyperscript-adapter';
|
|
21
|
+
* const english = preprocess('トグル .active', 'ja');
|
|
22
|
+
* _hyperscript(english);
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
export { hyperscriptI18n, preprocess, type PluginOptions } from './plugin';
|
|
26
|
+
export { preprocessToEnglish, type PreprocessorConfig } from './preprocessor';
|
|
27
|
+
export { resolveLanguage } from './language-resolver';
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Language Resolver
|
|
3
|
+
*
|
|
4
|
+
* Determines the language of hyperscript code on a given element
|
|
5
|
+
* using a cascading resolution strategy.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Resolve the language for a hyperscript attribute on an element.
|
|
10
|
+
*
|
|
11
|
+
* Resolution order:
|
|
12
|
+
* 1. `data-lang` attribute on the element itself
|
|
13
|
+
* 2. `data-hyperscript-lang` attribute on the element or closest ancestor
|
|
14
|
+
* 3. `lang` attribute on `<html>` element
|
|
15
|
+
* 4. null (assume English, no preprocessing needed)
|
|
16
|
+
*/
|
|
17
|
+
export function resolveLanguage(elt: Element): string | null {
|
|
18
|
+
// 1. Explicit per-element
|
|
19
|
+
const dataLang = elt.getAttribute('data-lang');
|
|
20
|
+
if (dataLang) return normalizeLangCode(dataLang);
|
|
21
|
+
|
|
22
|
+
// 2. Inherited from ancestor
|
|
23
|
+
const hsLang =
|
|
24
|
+
elt.getAttribute('data-hyperscript-lang') ??
|
|
25
|
+
elt.closest?.('[data-hyperscript-lang]')?.getAttribute('data-hyperscript-lang');
|
|
26
|
+
if (hsLang) return normalizeLangCode(hsLang);
|
|
27
|
+
|
|
28
|
+
// 3. Document-level lang
|
|
29
|
+
const htmlLang = typeof document !== 'undefined' ? document.documentElement?.lang : null;
|
|
30
|
+
if (htmlLang && htmlLang !== 'en') return normalizeLangCode(htmlLang);
|
|
31
|
+
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Normalize a BCP-47 language tag to an ISO 639-1 code.
|
|
37
|
+
* e.g., "ja-JP" → "ja", "zh-Hans" → "zh", "pt-BR" → "pt"
|
|
38
|
+
*/
|
|
39
|
+
function normalizeLangCode(lang: string): string {
|
|
40
|
+
return lang.split('-')[0].toLowerCase();
|
|
41
|
+
}
|
package/src/plugin.ts
ADDED
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* _hyperscript Plugin
|
|
3
|
+
*
|
|
4
|
+
* Registers with _hyperscript.use() to intercept getScript() and
|
|
5
|
+
* preprocess non-English hyperscript into English before parsing.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { resolveLanguage } from './language-resolver';
|
|
9
|
+
import { preprocessToEnglish, type PreprocessorConfig } from './preprocessor';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* The _hyperscript global object shape (minimal subset we need).
|
|
13
|
+
*/
|
|
14
|
+
interface HyperscriptGlobal {
|
|
15
|
+
internals: {
|
|
16
|
+
runtime: HyperscriptRuntime;
|
|
17
|
+
};
|
|
18
|
+
config: Record<string, unknown>;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface HyperscriptRuntime {
|
|
22
|
+
getScript(elt: Element): string | null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface PluginOptions extends Partial<PreprocessorConfig> {
|
|
26
|
+
/** Default language for all elements (overridable per-element). */
|
|
27
|
+
defaultLanguage?: string;
|
|
28
|
+
/** Custom attribute name for per-element language. Default: "data-lang" */
|
|
29
|
+
languageAttribute?: string;
|
|
30
|
+
/** Enable debug logging to console. Default: false */
|
|
31
|
+
debug?: boolean;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Create a _hyperscript plugin that enables multilingual hyperscript.
|
|
36
|
+
*
|
|
37
|
+
* @example
|
|
38
|
+
* // Basic usage
|
|
39
|
+
* _hyperscript.use(hyperscriptI18n());
|
|
40
|
+
*
|
|
41
|
+
* @example
|
|
42
|
+
* // With options
|
|
43
|
+
* _hyperscript.use(hyperscriptI18n({
|
|
44
|
+
* defaultLanguage: 'ja',
|
|
45
|
+
* confidenceThreshold: 0.6,
|
|
46
|
+
* debug: true,
|
|
47
|
+
* }));
|
|
48
|
+
*/
|
|
49
|
+
export function hyperscriptI18n(options: PluginOptions = {}) {
|
|
50
|
+
return function plugin(hs: unknown): void {
|
|
51
|
+
const { internals } = hs as HyperscriptGlobal;
|
|
52
|
+
const runtime = internals.runtime;
|
|
53
|
+
const originalGetScript = runtime.getScript.bind(runtime);
|
|
54
|
+
|
|
55
|
+
runtime.getScript = function (elt: Element): string | null {
|
|
56
|
+
const src = originalGetScript(elt);
|
|
57
|
+
if (!src) return null;
|
|
58
|
+
|
|
59
|
+
// Resolve language
|
|
60
|
+
const lang = resolveLanguageWithOptions(elt, options);
|
|
61
|
+
|
|
62
|
+
// If English or no language detected, pass through
|
|
63
|
+
if (!lang || lang === 'en') return src;
|
|
64
|
+
|
|
65
|
+
// Preprocess to English
|
|
66
|
+
const english = preprocessToEnglish(src, lang, options);
|
|
67
|
+
|
|
68
|
+
if (english !== src) {
|
|
69
|
+
if (options.debug) {
|
|
70
|
+
console.log(`[hyperscript-i18n] ${lang}: "${src}" → "${english}"`);
|
|
71
|
+
}
|
|
72
|
+
} else {
|
|
73
|
+
// Translation produced no change — likely a failure
|
|
74
|
+
console.warn(
|
|
75
|
+
`[hyperscript-i18n] Translation unchanged for lang="${lang}": "${src.length > 60 ? src.slice(0, 60) + '…' : src}". ` +
|
|
76
|
+
'The input may not match any known pattern, or the language may not be registered. ' +
|
|
77
|
+
'Original text will be passed to _hyperscript as-is.'
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return english;
|
|
82
|
+
};
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Resolve language with plugin options factored in.
|
|
88
|
+
*/
|
|
89
|
+
function resolveLanguageWithOptions(elt: Element, options: PluginOptions): string | null {
|
|
90
|
+
// Check custom attribute name
|
|
91
|
+
if (options.languageAttribute) {
|
|
92
|
+
const custom = elt.getAttribute(options.languageAttribute);
|
|
93
|
+
if (custom) return custom.split('-')[0].toLowerCase();
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// Standard resolution
|
|
97
|
+
const resolved = resolveLanguage(elt);
|
|
98
|
+
if (resolved) return resolved;
|
|
99
|
+
|
|
100
|
+
// Fall back to default language
|
|
101
|
+
return options.defaultLanguage ?? null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Standalone preprocessing function for programmatic use.
|
|
106
|
+
* Call this when using _hyperscript.evaluate() or _hyperscript("code") directly.
|
|
107
|
+
*
|
|
108
|
+
* @example
|
|
109
|
+
* const english = preprocess("トグル .active", "ja");
|
|
110
|
+
* _hyperscript(english);
|
|
111
|
+
*/
|
|
112
|
+
export function preprocess(
|
|
113
|
+
src: string,
|
|
114
|
+
lang: string,
|
|
115
|
+
config: Partial<PreprocessorConfig> = {}
|
|
116
|
+
): string {
|
|
117
|
+
if (lang === 'en') return src;
|
|
118
|
+
return preprocessToEnglish(src, lang, config);
|
|
119
|
+
}
|