@lokascript/i18n 2.5.1 → 2.6.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/dist/browser.cjs +1274 -154
- package/dist/browser.cjs.map +1 -1
- package/dist/browser.d.cts +3 -3
- package/dist/browser.d.ts +3 -3
- package/dist/browser.js +1274 -155
- package/dist/browser.js.map +1 -1
- package/dist/dictionaries/index.cjs +364 -133
- package/dist/dictionaries/index.cjs.map +1 -1
- package/dist/dictionaries/index.d.cts +1 -1
- package/dist/dictionaries/index.d.ts +1 -1
- package/dist/dictionaries/index.js +364 -133
- package/dist/dictionaries/index.js.map +1 -1
- package/dist/index.cjs +8860 -7740
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +37 -37
- package/dist/index.d.ts +37 -37
- package/dist/index.js +8860 -7741
- package/dist/index.js.map +1 -1
- package/dist/lokascript-i18n.min.js +1 -1
- package/dist/lokascript-i18n.min.js.map +1 -1
- package/dist/lokascript-i18n.mjs +1697 -146
- package/dist/lokascript-i18n.mjs.map +1 -1
- package/dist/plugins/vite.cjs +359 -132
- package/dist/plugins/vite.cjs.map +1 -1
- package/dist/plugins/vite.js +359 -132
- package/dist/plugins/vite.js.map +1 -1
- package/dist/plugins/webpack.cjs +359 -132
- package/dist/plugins/webpack.cjs.map +1 -1
- package/dist/plugins/webpack.js +359 -132
- package/dist/plugins/webpack.js.map +1 -1
- package/dist/{transformer-BLv389qz.d.ts → transformer-BA8YX9u1.d.ts} +120 -2
- package/dist/{transformer-PFsYELrc.d.cts → transformer-DM7iplCU.d.cts} +120 -2
- package/dist/{types-BcALAO6h.d.cts → types-BYtpqGq3.d.cts} +1 -1
- package/dist/{types-BcALAO6h.d.ts → types-BYtpqGq3.d.ts} +1 -1
- package/package.json +6 -6
- package/src/browser.ts +1 -0
- package/src/command-primary-roles.test.ts +59 -0
- package/src/constants.ts +51 -0
- package/src/dictionaries/ar.ts +8 -3
- package/src/dictionaries/bn.ts +3 -1
- package/src/dictionaries/de.ts +19 -4
- package/src/dictionaries/derive.ts +4 -0
- package/src/dictionaries/en.ts +9 -0
- package/src/dictionaries/es.ts +2 -2
- package/src/dictionaries/fr.ts +6 -2
- package/src/dictionaries/he.ts +7 -1
- package/src/dictionaries/hi.ts +17 -10
- package/src/dictionaries/id.ts +15 -8
- package/src/dictionaries/it.ts +6 -3
- package/src/dictionaries/ja.ts +10 -4
- package/src/dictionaries/ko.ts +9 -4
- package/src/dictionaries/ms.ts +2 -1
- package/src/dictionaries/pl.ts +11 -3
- package/src/dictionaries/pt.ts +10 -4
- package/src/dictionaries/qu.ts +46 -22
- package/src/dictionaries/ru.ts +14 -7
- package/src/dictionaries/sw.ts +37 -10
- package/src/dictionaries/th.ts +3 -1
- package/src/dictionaries/tl.ts +14 -7
- package/src/dictionaries/tr.ts +22 -9
- package/src/dictionaries/uk.ts +13 -7
- package/src/dictionaries/vi.ts +3 -3
- package/src/dictionaries/zh.ts +10 -2
- package/src/examples/new-languages.ts +1 -1
- package/src/grammar/grammar.test.ts +1242 -1
- package/src/grammar/index.ts +1 -0
- package/src/grammar/profiles/index.ts +358 -12
- package/src/grammar/transformer.ts +1053 -16
- package/src/grammar/types.ts +22 -1
- package/src/index.ts +1 -0
- package/src/new-languages.test.ts +7 -3
- package/src/parser/parser-integration.test.ts +5 -1
- package/src/positional-keyword-drift.test.ts +138 -0
package/dist/lokascript-i18n.mjs
CHANGED
|
@@ -17,6 +17,49 @@ const ENGLISH_MODIFIER_ROLES = {
|
|
|
17
17
|
over: 'duration',
|
|
18
18
|
for: 'duration',
|
|
19
19
|
};
|
|
20
|
+
/**
|
|
21
|
+
* Maps a command verb to its **primary** semantic role — the role of the
|
|
22
|
+
* command's first/leading argument when no explicit modifier keyword marks it.
|
|
23
|
+
*
|
|
24
|
+
* The generic argument parser in the transformer defaults the first unmarked
|
|
25
|
+
* argument to `patient`, which is correct for the majority of commands
|
|
26
|
+
* (`toggle .x`, `add .x`, …) but wrong for commands whose leading argument is a
|
|
27
|
+
* non-patient — e.g. `wait <duration>`. Marking a duration as a patient emits a
|
|
28
|
+
* spurious object-marker in the target language (Chinese `等待 把 1s`, ungrammatical;
|
|
29
|
+
* Japanese `1s を 待つ`; Korean `1s 를 대기`), which the semantic parser then fails to
|
|
30
|
+
* match — dropping the command.
|
|
31
|
+
*
|
|
32
|
+
* Only commands whose primary role is **not** `patient` are listed here (the
|
|
33
|
+
* `patient` default already covers the rest). Mirrors the `primaryRole` field of
|
|
34
|
+
* the semantic package's command schemas (`@lokascript/semantic`); kept in sync by
|
|
35
|
+
* `schema-alignment.test.ts` so this local copy can't drift without the bundle
|
|
36
|
+
* having to pull in the whole semantic graph.
|
|
37
|
+
*
|
|
38
|
+
* @see packages/i18n/src/schema-alignment.test.ts
|
|
39
|
+
* @see docs-internal/ZH_BLOCK_BODY_SCOPE.md (#1 — transformer role model)
|
|
40
|
+
*/
|
|
41
|
+
const COMMAND_PRIMARY_ROLES = {
|
|
42
|
+
set: 'destination',
|
|
43
|
+
on: 'event',
|
|
44
|
+
trigger: 'event',
|
|
45
|
+
send: 'event',
|
|
46
|
+
wait: 'duration',
|
|
47
|
+
fetch: 'source',
|
|
48
|
+
get: 'source',
|
|
49
|
+
if: 'condition',
|
|
50
|
+
unless: 'condition',
|
|
51
|
+
while: 'condition',
|
|
52
|
+
repeat: 'loopType',
|
|
53
|
+
go: 'destination',
|
|
54
|
+
scroll: 'destination',
|
|
55
|
+
tell: 'destination',
|
|
56
|
+
default: 'destination',
|
|
57
|
+
swap: 'destination',
|
|
58
|
+
// morph deliberately absent: its schema primaryRole is `patient` (the
|
|
59
|
+
// element being morphed — aligned with the transformer's patient marking
|
|
60
|
+
// in the session-9 role-layout swap), and patient is the default.
|
|
61
|
+
bind: 'destination',
|
|
62
|
+
};
|
|
20
63
|
/**
|
|
21
64
|
* English modifier keywords (derived from ENGLISH_MODIFIER_ROLES)
|
|
22
65
|
*/
|
|
@@ -96,6 +139,13 @@ const ENGLISH_COMMANDS = new Set([
|
|
|
96
139
|
'trigger',
|
|
97
140
|
'unless',
|
|
98
141
|
'wait',
|
|
142
|
+
// v2.x reactive + realtime + service-worker DSL commands
|
|
143
|
+
'live',
|
|
144
|
+
'bind',
|
|
145
|
+
'eventsource',
|
|
146
|
+
'socket',
|
|
147
|
+
'worker',
|
|
148
|
+
'intercept',
|
|
99
149
|
]);
|
|
100
150
|
// =============================================================================
|
|
101
151
|
// English Keywords (Non-Commands)
|
|
@@ -622,7 +672,7 @@ const es$1 = {
|
|
|
622
672
|
swap: 'intercambiar',
|
|
623
673
|
morph: 'transformar',
|
|
624
674
|
settle: 'estabilizar',
|
|
625
|
-
append: 'añadir'
|
|
675
|
+
append: 'anexar', // profile primary; 'añadir' is add's word (parsed as add)
|
|
626
676
|
exit: 'salir',
|
|
627
677
|
install: 'instalar',
|
|
628
678
|
breakpoint: 'punto-interrupción',
|
|
@@ -755,7 +805,7 @@ const es$1 = {
|
|
|
755
805
|
prev: 'ant',
|
|
756
806
|
at: 'en',
|
|
757
807
|
random: 'aleatorio',
|
|
758
|
-
closest: '
|
|
808
|
+
closest: 'cercano',
|
|
759
809
|
parent: 'padre',
|
|
760
810
|
children: 'hijos',
|
|
761
811
|
within: 'dentro',
|
|
@@ -814,7 +864,7 @@ const ja$1 = {
|
|
|
814
864
|
hide: '隠す',
|
|
815
865
|
show: '表示',
|
|
816
866
|
if: 'もし',
|
|
817
|
-
unless: '
|
|
867
|
+
unless: 'ない限り', // not でなければ — leading で particle shatters the marker
|
|
818
868
|
repeat: '繰り返し',
|
|
819
869
|
for: 'ために',
|
|
820
870
|
while: 'の間',
|
|
@@ -823,7 +873,7 @@ const ja$1 = {
|
|
|
823
873
|
break: '中断',
|
|
824
874
|
halt: '停止',
|
|
825
875
|
wait: '待つ',
|
|
826
|
-
fetch: '
|
|
876
|
+
fetch: 'フェッチ',
|
|
827
877
|
call: '呼び出し',
|
|
828
878
|
return: '戻る',
|
|
829
879
|
make: '作る',
|
|
@@ -848,7 +898,11 @@ const ja$1 = {
|
|
|
848
898
|
morph: '変形',
|
|
849
899
|
settle: '安定',
|
|
850
900
|
append: '末尾追加',
|
|
851
|
-
exit:
|
|
901
|
+
// 終了 → 退出: realigned to the semantic profile primary (ja.ts `exit: 退出`).
|
|
902
|
+
// 終了 was the historic exit/end collision word (see isEndKeyword's ja note);
|
|
903
|
+
// rendered as 終了 it tokenized as a plain identifier, so a guard-clause
|
|
904
|
+
// `exit` inside `if … end` never parsed (behavior-sortable ja).
|
|
905
|
+
exit: '退出',
|
|
852
906
|
install: 'インストール',
|
|
853
907
|
breakpoint: 'ブレークポイント',
|
|
854
908
|
clear: 'クリア',
|
|
@@ -857,6 +911,7 @@ const ja$1 = {
|
|
|
857
911
|
select: '選択',
|
|
858
912
|
clone: '複製',
|
|
859
913
|
prepend: '先頭追加',
|
|
914
|
+
socket: 'ソケット',
|
|
860
915
|
},
|
|
861
916
|
modifiers: {
|
|
862
917
|
to: 'に',
|
|
@@ -905,7 +960,7 @@ const ja$1 = {
|
|
|
905
960
|
touchcancel: 'タッチキャンセル',
|
|
906
961
|
},
|
|
907
962
|
logical: {
|
|
908
|
-
when: '
|
|
963
|
+
when: '時',
|
|
909
964
|
where: 'どこ',
|
|
910
965
|
and: 'そして',
|
|
911
966
|
or: 'または',
|
|
@@ -923,6 +978,7 @@ const ja$1 = {
|
|
|
923
978
|
otherwise: 'そうでなければ',
|
|
924
979
|
end: '終わり',
|
|
925
980
|
live: 'ライブ',
|
|
981
|
+
bind: 'バインド',
|
|
926
982
|
changes: '変わったら',
|
|
927
983
|
},
|
|
928
984
|
temporal: {
|
|
@@ -1041,6 +1097,9 @@ const fr$1 = {
|
|
|
1041
1097
|
add: 'ajouter',
|
|
1042
1098
|
remove: 'supprimer',
|
|
1043
1099
|
toggle: 'basculer',
|
|
1100
|
+
// command blur (the events section has the blur EVENT word; without this
|
|
1101
|
+
// entry the transformer fell back to it and no profile read it as the verb)
|
|
1102
|
+
blur: 'défocaliser',
|
|
1044
1103
|
hide: 'cacher',
|
|
1045
1104
|
show: 'montrer',
|
|
1046
1105
|
if: 'si',
|
|
@@ -1077,7 +1136,7 @@ const fr$1 = {
|
|
|
1077
1136
|
swap: 'échanger',
|
|
1078
1137
|
morph: 'transformer',
|
|
1079
1138
|
settle: 'stabiliser',
|
|
1080
|
-
append: 'ajouter'
|
|
1139
|
+
append: 'annexer', // profile primary; 'ajouter' is add's word (parsed as add)
|
|
1081
1140
|
exit: 'sortir',
|
|
1082
1141
|
install: 'installer',
|
|
1083
1142
|
breakpoint: 'point-arrêt',
|
|
@@ -1087,6 +1146,7 @@ const fr$1 = {
|
|
|
1087
1146
|
select: 'sélectionner',
|
|
1088
1147
|
clone: 'cloner',
|
|
1089
1148
|
prepend: 'préfixer',
|
|
1149
|
+
focus: 'focaliser',
|
|
1090
1150
|
},
|
|
1091
1151
|
modifiers: {
|
|
1092
1152
|
to: 'à',
|
|
@@ -1210,7 +1270,7 @@ const fr$1 = {
|
|
|
1210
1270
|
prev: 'préc',
|
|
1211
1271
|
at: 'à',
|
|
1212
1272
|
random: 'aléatoire',
|
|
1213
|
-
closest: '
|
|
1273
|
+
closest: 'proche',
|
|
1214
1274
|
parent: 'parent',
|
|
1215
1275
|
children: 'enfants',
|
|
1216
1276
|
within: 'dans',
|
|
@@ -1266,9 +1326,17 @@ const de$1 = {
|
|
|
1266
1326
|
add: 'hinzufügen',
|
|
1267
1327
|
remove: 'entfernen',
|
|
1268
1328
|
toggle: 'umschalten',
|
|
1329
|
+
// command blur (the events section has the blur EVENT word; without this
|
|
1330
|
+
// entry the transformer fell back to it and no profile read it as the verb)
|
|
1331
|
+
blur: 'defokussieren',
|
|
1269
1332
|
hide: 'verstecken',
|
|
1270
1333
|
show: 'zeigen',
|
|
1271
|
-
if
|
|
1334
|
+
// Align with the semantic German profile's `if` primary (`falls`). The previous
|
|
1335
|
+
// `wenn` collides with the profile's `when` keyword (German `wenn` = both
|
|
1336
|
+
// "if" and "when"), so a transformed `if` resolved to `when` and the conditional
|
|
1337
|
+
// wrapper never formed (`if`/`put` dropped). Same dict↔profile alignment as id
|
|
1338
|
+
// `toggle` / get-value. `when` is unaffected (separate dict entry).
|
|
1339
|
+
if: 'falls',
|
|
1272
1340
|
unless: 'wennnicht',
|
|
1273
1341
|
repeat: 'wiederholen',
|
|
1274
1342
|
for: 'für',
|
|
@@ -1278,7 +1346,7 @@ const de$1 = {
|
|
|
1278
1346
|
break: 'unterbrechen',
|
|
1279
1347
|
halt: 'anhalten',
|
|
1280
1348
|
wait: 'warten',
|
|
1281
|
-
fetch: '
|
|
1349
|
+
fetch: 'abrufen',
|
|
1282
1350
|
call: 'aufrufen',
|
|
1283
1351
|
return: 'zurückgeben',
|
|
1284
1352
|
make: 'erstellen',
|
|
@@ -1306,12 +1374,13 @@ const de$1 = {
|
|
|
1306
1374
|
exit: 'beenden',
|
|
1307
1375
|
install: 'installieren',
|
|
1308
1376
|
breakpoint: 'haltepunkt',
|
|
1309
|
-
clear: '
|
|
1377
|
+
clear: 'bereinigen', // löschen is a remove alternative in the profile
|
|
1310
1378
|
close: 'schließen',
|
|
1311
1379
|
open: 'öffnen',
|
|
1312
1380
|
select: 'auswählen',
|
|
1313
1381
|
clone: 'klonen',
|
|
1314
1382
|
prepend: 'voranstellen',
|
|
1383
|
+
focus: 'fokussieren',
|
|
1315
1384
|
},
|
|
1316
1385
|
modifiers: {
|
|
1317
1386
|
to: 'zu',
|
|
@@ -1435,7 +1504,13 @@ const de$1 = {
|
|
|
1435
1504
|
prev: 'vorh',
|
|
1436
1505
|
at: 'bei',
|
|
1437
1506
|
random: 'zufällig',
|
|
1438
|
-
|
|
1507
|
+
// `nächste` already serves `next` (line above). German `nächste` is
|
|
1508
|
+
// genuinely ambiguous (next/nearest), so emitting it for `closest` too
|
|
1509
|
+
// collided: the semantic tokenizer normalizes `nächste`→next (a deliberate
|
|
1510
|
+
// last-wins choice — see german.ts), and a closest reading is impossible to
|
|
1511
|
+
// recover. Use the unambiguous `nächstgelegene` ("nearest-located") for
|
|
1512
|
+
// closest so the two readings stay distinct end-to-end.
|
|
1513
|
+
closest: 'nächstgelegene',
|
|
1439
1514
|
parent: 'elternteil',
|
|
1440
1515
|
children: 'kinder',
|
|
1441
1516
|
within: 'innerhalb',
|
|
@@ -1499,7 +1574,10 @@ const ar$1 = {
|
|
|
1499
1574
|
hide: 'اخف',
|
|
1500
1575
|
show: 'اظهر',
|
|
1501
1576
|
if: 'إذا',
|
|
1502
|
-
|
|
1577
|
+
// Single-token form: the semantic ar tokenizer splits the two-word `إلا إذا`
|
|
1578
|
+
// into `إلا` + `إذا`(if), which derails the `unless` block. `إلا` alone is the
|
|
1579
|
+
// conventional Arabic shorthand and matches the ar profile's unless keyword.
|
|
1580
|
+
unless: 'إلا',
|
|
1503
1581
|
repeat: 'كرر',
|
|
1504
1582
|
for: 'لكل',
|
|
1505
1583
|
while: 'بينما',
|
|
@@ -1529,19 +1607,20 @@ const ar$1 = {
|
|
|
1529
1607
|
js: 'جافاسكربت',
|
|
1530
1608
|
async: 'متزامن',
|
|
1531
1609
|
render: 'ارسم',
|
|
1532
|
-
swap: 'بدّل'
|
|
1610
|
+
swap: 'استبدل', // profile primary; 'بدّل' is toggle's word (parsed as toggle)
|
|
1533
1611
|
morph: 'حوّل',
|
|
1534
1612
|
settle: 'استقر',
|
|
1535
1613
|
append: 'ألحق',
|
|
1536
1614
|
exit: 'اخرج',
|
|
1537
1615
|
install: 'ثبّت',
|
|
1538
1616
|
breakpoint: 'نقطة-توقف',
|
|
1539
|
-
clear: '
|
|
1617
|
+
clear: 'نظّف', // امسح is a remove alternative in the profile
|
|
1540
1618
|
close: 'أغلق',
|
|
1541
1619
|
open: 'افتح',
|
|
1542
1620
|
select: 'اختر',
|
|
1543
1621
|
clone: 'استنسخ',
|
|
1544
1622
|
prepend: 'سبق',
|
|
1623
|
+
socket: 'مقبس',
|
|
1545
1624
|
},
|
|
1546
1625
|
modifiers: {
|
|
1547
1626
|
to: 'إلى',
|
|
@@ -1608,6 +1687,7 @@ const ar$1 = {
|
|
|
1608
1687
|
otherwise: 'خلاف ذلك',
|
|
1609
1688
|
end: 'النهاية',
|
|
1610
1689
|
live: 'حي',
|
|
1690
|
+
bind: 'اربط',
|
|
1611
1691
|
changes: 'يتغير',
|
|
1612
1692
|
},
|
|
1613
1693
|
temporal: {
|
|
@@ -1733,9 +1813,9 @@ const ko$1 = {
|
|
|
1733
1813
|
hide: '숨기다',
|
|
1734
1814
|
show: '보이다',
|
|
1735
1815
|
if: '만약',
|
|
1736
|
-
unless: '
|
|
1816
|
+
unless: '아니라면', // distinct from else `아니면` (line ~131) — was a homonym
|
|
1737
1817
|
repeat: '반복',
|
|
1738
|
-
for: '
|
|
1818
|
+
for: '각각', // profile's for word — 동안 is while (collision)
|
|
1739
1819
|
while: '동안',
|
|
1740
1820
|
until: '까지',
|
|
1741
1821
|
continue: '계속',
|
|
@@ -1766,8 +1846,11 @@ const ko$1 = {
|
|
|
1766
1846
|
swap: '교환',
|
|
1767
1847
|
morph: '변형',
|
|
1768
1848
|
settle: '안정',
|
|
1769
|
-
append: '추가'
|
|
1770
|
-
exit:
|
|
1849
|
+
append: '덧붙이다', // profile primary; '추가' is add's word (parsed as add)
|
|
1850
|
+
// 종료 → 나가기: realigned to the semantic profile primary (ko.ts `exit: 나가기`,
|
|
1851
|
+
// whose comment already flags the 종료 end-collision). Rendered as 종료 it
|
|
1852
|
+
// tokenized as a plain identifier, so a guard-clause `exit` never parsed.
|
|
1853
|
+
exit: '나가기',
|
|
1771
1854
|
install: '설치',
|
|
1772
1855
|
breakpoint: '중단점',
|
|
1773
1856
|
clear: '지우기',
|
|
@@ -1776,6 +1859,7 @@ const ko$1 = {
|
|
|
1776
1859
|
select: '선택',
|
|
1777
1860
|
clone: '복제',
|
|
1778
1861
|
prepend: '앞에추가',
|
|
1862
|
+
socket: '소켓',
|
|
1779
1863
|
},
|
|
1780
1864
|
modifiers: {
|
|
1781
1865
|
to: '에',
|
|
@@ -1842,6 +1926,7 @@ const ko$1 = {
|
|
|
1842
1926
|
otherwise: '그렇지않으면',
|
|
1843
1927
|
end: '끝',
|
|
1844
1928
|
live: '라이브',
|
|
1929
|
+
bind: '바인드',
|
|
1845
1930
|
changes: '변경되면',
|
|
1846
1931
|
},
|
|
1847
1932
|
temporal: {
|
|
@@ -1954,7 +2039,11 @@ const zh$1 = {
|
|
|
1954
2039
|
tell: '告诉',
|
|
1955
2040
|
trigger: '触发',
|
|
1956
2041
|
send: '发送',
|
|
1957
|
-
take
|
|
2042
|
+
// `拿取` (the semantic zh profile's take primary), not `获取` — `获取` is the
|
|
2043
|
+
// profile's GET primary, so emitting it for take made `take …` parse as get
|
|
2044
|
+
// (take-class-from-siblings: phantom get, take dropped). Exposed by the get-zh-ba
|
|
2045
|
+
// pattern; same dict↔profile homonym family as pl get/fetch.
|
|
2046
|
+
take: '拿取',
|
|
1958
2047
|
put: '放置',
|
|
1959
2048
|
set: '设置',
|
|
1960
2049
|
get: '获得',
|
|
@@ -1973,7 +2062,10 @@ const zh$1 = {
|
|
|
1973
2062
|
break: '中断',
|
|
1974
2063
|
halt: '停止',
|
|
1975
2064
|
wait: '等待',
|
|
1976
|
-
fetch
|
|
2065
|
+
// 抓取 (zhuāqǔ, "grab/scrape") is the semantic zh profile's `fetch` primary;
|
|
2066
|
+
// 获取 (huòqǔ) is its `get` primary, so emitting 获取 here made the semantic
|
|
2067
|
+
// parser read a transformed `fetch` as `get`. See ZH_BLOCK_BODY_SCOPE.md #3.
|
|
2068
|
+
fetch: '抓取',
|
|
1977
2069
|
call: '调用',
|
|
1978
2070
|
return: '返回',
|
|
1979
2071
|
make: '制作',
|
|
@@ -2073,6 +2165,7 @@ const zh$1 = {
|
|
|
2073
2165
|
otherwise: '要不然',
|
|
2074
2166
|
end: '结束',
|
|
2075
2167
|
live: '实时',
|
|
2168
|
+
bind: '绑定',
|
|
2076
2169
|
changes: '改变时',
|
|
2077
2170
|
},
|
|
2078
2171
|
temporal: {
|
|
@@ -2186,7 +2279,7 @@ const tr$1 = {
|
|
|
2186
2279
|
tell: 'söyle',
|
|
2187
2280
|
trigger: 'tetikle',
|
|
2188
2281
|
send: 'gönder',
|
|
2189
|
-
take: '
|
|
2282
|
+
take: 'tut', // auditor: dict emitted a word the profile reads differently
|
|
2190
2283
|
put: 'koy',
|
|
2191
2284
|
set: 'ayarla',
|
|
2192
2285
|
get: 'al',
|
|
@@ -2199,7 +2292,11 @@ const tr$1 = {
|
|
|
2199
2292
|
unless: 'değilse',
|
|
2200
2293
|
repeat: 'tekrarla',
|
|
2201
2294
|
for: 'için',
|
|
2202
|
-
while
|
|
2295
|
+
// `süresince` (the semantic tr profile's while primary), not `iken`: `iken`
|
|
2296
|
+
// is the profile's WHEN primary (logical.when below also emits it), so a
|
|
2297
|
+
// fronted repeat-while head tokenized as `when` and the condition dropped
|
|
2298
|
+
// wholesale. Same dict↔profile homonym disambiguation as pl get/pobierz.
|
|
2299
|
+
while: 'süresince',
|
|
2203
2300
|
until: 'kadar',
|
|
2204
2301
|
continue: 'devam',
|
|
2205
2302
|
break: 'dur',
|
|
@@ -2229,7 +2326,7 @@ const tr$1 = {
|
|
|
2229
2326
|
swap: 'takas',
|
|
2230
2327
|
morph: 'dönüştür',
|
|
2231
2328
|
settle: 'sabitlen',
|
|
2232
|
-
append: 'ekle'
|
|
2329
|
+
append: 'iliştir', // profile primary; 'ekle' is add's word (parsed as add)
|
|
2233
2330
|
exit: 'çık',
|
|
2234
2331
|
install: 'kur',
|
|
2235
2332
|
breakpoint: 'kesme-noktası',
|
|
@@ -2239,6 +2336,7 @@ const tr$1 = {
|
|
|
2239
2336
|
select: 'seç',
|
|
2240
2337
|
clone: 'klonla',
|
|
2241
2338
|
prepend: 'başınaekle',
|
|
2339
|
+
socket: 'soket',
|
|
2242
2340
|
},
|
|
2243
2341
|
modifiers: {
|
|
2244
2342
|
to: 'e',
|
|
@@ -2261,8 +2359,11 @@ const tr$1 = {
|
|
|
2261
2359
|
events: {
|
|
2262
2360
|
click: 'tıklama',
|
|
2263
2361
|
dblclick: 'çift_tıklama',
|
|
2264
|
-
|
|
2265
|
-
mouseup
|
|
2362
|
+
// Fused (no `_`): the semantic tokenizer splits on `_`, breaking event
|
|
2363
|
+
// recognition (mousedown → "bas" / mouseup mis-captured). Mirrors the #535
|
|
2364
|
+
// ru/uk fuse + #510 resize fix.
|
|
2365
|
+
mousedown: 'farebas',
|
|
2366
|
+
mouseup: 'farebırak',
|
|
2266
2367
|
mouseenter: 'fare_gir',
|
|
2267
2368
|
mouseleave: 'fare_çık',
|
|
2268
2369
|
mouseover: 'fare_üstü',
|
|
@@ -2279,7 +2380,7 @@ const tr$1 = {
|
|
|
2279
2380
|
reset: 'sıfırla',
|
|
2280
2381
|
load: 'yükle',
|
|
2281
2382
|
unload: 'yükle_kaldır',
|
|
2282
|
-
resize: '
|
|
2383
|
+
resize: 'boyutlandırma',
|
|
2283
2384
|
scroll: 'kaydır',
|
|
2284
2385
|
touchstart: 'dokunma_başla',
|
|
2285
2386
|
touchend: 'dokunma_bitir',
|
|
@@ -2300,7 +2401,7 @@ const tr$1 = {
|
|
|
2300
2401
|
equals: 'eşittir',
|
|
2301
2402
|
has: 'var',
|
|
2302
2403
|
have: 'var',
|
|
2303
|
-
then: '
|
|
2404
|
+
then: 'ardından',
|
|
2304
2405
|
else: 'yoksa',
|
|
2305
2406
|
otherwise: 'aksi_halde',
|
|
2306
2407
|
end: 'son',
|
|
@@ -2356,13 +2457,18 @@ const tr$1 = {
|
|
|
2356
2457
|
},
|
|
2357
2458
|
expressions: {
|
|
2358
2459
|
first: 'ilk',
|
|
2359
|
-
|
|
2460
|
+
// 'son' is also the END keyword emission (block terminator), and the
|
|
2461
|
+
// parser's end-recognizers match it by value — so a positional `last`
|
|
2462
|
+
// emission of 'son' terminated the enclosing block mid-condition
|
|
2463
|
+
// (focus-trap's `eşleşir son <button/>` dropped the branch body; the sw
|
|
2464
|
+
// mwisho class). 'sonuncu' ("the last one") is unambiguous.
|
|
2465
|
+
last: 'sonuncu',
|
|
2360
2466
|
next: 'sonraki',
|
|
2361
2467
|
previous: 'önceki',
|
|
2362
2468
|
prev: 'önc',
|
|
2363
2469
|
at: 'de',
|
|
2364
2470
|
random: 'rastgele',
|
|
2365
|
-
closest: '
|
|
2471
|
+
closest: 'enyakın',
|
|
2366
2472
|
parent: 'ebeveyn',
|
|
2367
2473
|
children: 'çocuklar',
|
|
2368
2474
|
within: 'içinde',
|
|
@@ -2424,7 +2530,7 @@ const id$1 = {
|
|
|
2424
2530
|
get: 'dapatkan',
|
|
2425
2531
|
add: 'tambah',
|
|
2426
2532
|
remove: 'hapus',
|
|
2427
|
-
toggle: '
|
|
2533
|
+
toggle: 'alihkan',
|
|
2428
2534
|
hide: 'sembunyikan',
|
|
2429
2535
|
show: 'tampilkan',
|
|
2430
2536
|
if: 'jika',
|
|
@@ -2437,7 +2543,10 @@ const id$1 = {
|
|
|
2437
2543
|
break: 'hentikan',
|
|
2438
2544
|
halt: 'berhenti',
|
|
2439
2545
|
wait: 'tunggu',
|
|
2440
|
-
|
|
2546
|
+
// profile primary; 'ambil' is take's word — emitting it for fetch made every
|
|
2547
|
+
// fetch parse as take once the fused take-event pattern came alive (the de
|
|
2548
|
+
// holen/abrufen bug class)
|
|
2549
|
+
fetch: 'muat',
|
|
2441
2550
|
call: 'panggil',
|
|
2442
2551
|
return: 'kembali',
|
|
2443
2552
|
make: 'buat',
|
|
@@ -2446,7 +2555,11 @@ const id$1 = {
|
|
|
2446
2555
|
catch: 'tangkap',
|
|
2447
2556
|
measure: 'ukur',
|
|
2448
2557
|
transition: 'transisi',
|
|
2449
|
-
|
|
2558
|
+
// naikkan (raise), not tambahkan — tambahkan is the semantic profile's
|
|
2559
|
+
// `add` alternative, so emitting it made increment-counter parse as ADD
|
|
2560
|
+
// (the #373 collision family). naikkan is the profile's increment
|
|
2561
|
+
// alternative, pairing naturally with the decrement side.
|
|
2562
|
+
increment: 'naikkan',
|
|
2450
2563
|
decrement: 'kurangi',
|
|
2451
2564
|
default: 'bawaan',
|
|
2452
2565
|
go: 'pergi',
|
|
@@ -2457,15 +2570,15 @@ const id$1 = {
|
|
|
2457
2570
|
beep: 'bunyi',
|
|
2458
2571
|
js: 'js',
|
|
2459
2572
|
async: 'asinkron',
|
|
2460
|
-
render: '
|
|
2573
|
+
render: 'olah', // auditor: dict emitted a word the profile reads differently
|
|
2461
2574
|
swap: 'tukar',
|
|
2462
|
-
morph: '
|
|
2463
|
-
settle: '
|
|
2464
|
-
append: 'tambah_akhir'
|
|
2575
|
+
morph: 'ubah', // auditor: dict emitted a word the profile reads differently
|
|
2576
|
+
settle: 'stabilkan', // auditor: dict emitted a word the profile reads differently
|
|
2577
|
+
append: 'sisipkan', // profile primary; 'tambah_akhir' splits and is unrecognized
|
|
2465
2578
|
exit: 'keluar',
|
|
2466
2579
|
install: 'pasang',
|
|
2467
2580
|
breakpoint: 'titik-henti',
|
|
2468
|
-
clear: '
|
|
2581
|
+
clear: 'bersihkan', // hapus is the remove PRIMARY in the profile
|
|
2469
2582
|
close: 'tutup',
|
|
2470
2583
|
open: 'buka',
|
|
2471
2584
|
select: 'pilih',
|
|
@@ -2646,20 +2759,31 @@ const qu$1 = {
|
|
|
2646
2759
|
tell: 'niy',
|
|
2647
2760
|
trigger: 'kichay',
|
|
2648
2761
|
send: 'kachay',
|
|
2649
|
-
take: '
|
|
2762
|
+
take: 'hapiy', // auditor: dict emitted a word the profile reads differently
|
|
2650
2763
|
put: 'churay',
|
|
2651
|
-
|
|
2652
|
-
|
|
2764
|
+
// churay is the semantic qu profile's `put` primary; `set` is churanay. Emitting
|
|
2765
|
+
// churay for set made the semantic parser read a transformed `set` as `put`.
|
|
2766
|
+
// See ZH_BLOCK_BODY_SCOPE.md (#2 sweep — keyword realignment, cf. zh fetch).
|
|
2767
|
+
set: 'churanay',
|
|
2768
|
+
// Align with the semantic quechua profile's `get` primary (`taripay`); the
|
|
2769
|
+
// previous `chaskiy` had no profile entry, so the transformed `get` dropped
|
|
2770
|
+
// (get-value parsed degenerate). Same dict↔profile alignment as id `toggle`.
|
|
2771
|
+
get: 'taripay',
|
|
2653
2772
|
add: 'yapay',
|
|
2654
2773
|
remove: 'qichuy',
|
|
2655
2774
|
toggle: 'tikray',
|
|
2656
2775
|
hide: 'pakay',
|
|
2657
2776
|
show: 'rikuchiy',
|
|
2658
2777
|
if: 'sichus',
|
|
2659
|
-
unless: '
|
|
2660
|
-
repeat: '
|
|
2661
|
-
for: '
|
|
2662
|
-
while
|
|
2778
|
+
unless: 'mana sichus', // spaced phrase ("if not"); `_` form split to `mana`+`sichus`(=if) at parse time
|
|
2779
|
+
repeat: 'kutipay',
|
|
2780
|
+
for: 'sapankaq', // profile's for word — rayku doubles as `by`
|
|
2781
|
+
// `kaykamaqa` (the semantic qu profile's while primary), not `kay_kaq`: the
|
|
2782
|
+
// `_` form is unknown to the profile (and `_` words split at parse time, cf.
|
|
2783
|
+
// qhipaman_yapay), so the fronted repeat-while head never formed a `while`
|
|
2784
|
+
// node — the condition dropped wholesale. Same dict↔profile alignment as
|
|
2785
|
+
// vi render / de abrufen.
|
|
2786
|
+
while: 'kaykamaqa',
|
|
2663
2787
|
until: 'hayk_akama',
|
|
2664
2788
|
continue: 'purichiy',
|
|
2665
2789
|
break: 'p_akiy',
|
|
@@ -2669,12 +2793,18 @@ const qu$1 = {
|
|
|
2669
2793
|
call: 'qayay',
|
|
2670
2794
|
return: 'kutimuy',
|
|
2671
2795
|
make: 'ruray',
|
|
2672
|
-
|
|
2796
|
+
// qillqay is the semantic qu profile's `copy` primary; `log` is qillqakuy.
|
|
2797
|
+
// Emitting qillqay for log made the parser read every transformed log as
|
|
2798
|
+
// copy (log-value/log-element/get-value lossy) — same realign as set above.
|
|
2799
|
+
log: 'qillqakuy',
|
|
2673
2800
|
throw: 'wikchuy',
|
|
2674
2801
|
catch: 'hapsiy',
|
|
2675
2802
|
measure: 'tupuy',
|
|
2676
|
-
|
|
2677
|
-
|
|
2803
|
+
// pasay, not tikray: the semantic qu profile's toggle keyword is
|
|
2804
|
+
// t'ikray/tikray, so a tikray render collided with toggle and the
|
|
2805
|
+
// transition command never anchored (sw kama precedent, #569).
|
|
2806
|
+
transition: 'pasay',
|
|
2807
|
+
increment: 'yapachiy',
|
|
2678
2808
|
decrement: 'pisiyachiy',
|
|
2679
2809
|
default: 'ñawpaq_kaq',
|
|
2680
2810
|
go: 'riy',
|
|
@@ -2685,13 +2815,13 @@ const qu$1 = {
|
|
|
2685
2815
|
beep: 'waqay',
|
|
2686
2816
|
js: 'js',
|
|
2687
2817
|
async: 'mana_suyaspa',
|
|
2688
|
-
render: '
|
|
2689
|
-
swap: 'rantin_tikray',
|
|
2690
|
-
morph: '
|
|
2691
|
-
settle: '
|
|
2692
|
-
append: '
|
|
2818
|
+
render: 'rikurichiy', // auditor: dict emitted a word the profile reads differently
|
|
2819
|
+
swap: "t'inkuy", // profile primary; 'rantin_tikray' splits, 'tikray' is toggle's word
|
|
2820
|
+
morph: 'tukunay', // auditor: dict emitted a word the profile reads differently
|
|
2821
|
+
settle: 'tiyakuy', // auditor: dict emitted a word the profile reads differently
|
|
2822
|
+
append: 'qatichiy', // profile's append primary (single token); `qhipaman_yapay` `_`-split to qhipaman+yapay(=add) at parse time
|
|
2693
2823
|
exit: 'lluqsiy',
|
|
2694
|
-
install: '
|
|
2824
|
+
install: 'tarpuy',
|
|
2695
2825
|
breakpoint: 'sayachinay',
|
|
2696
2826
|
clear: 'pichay',
|
|
2697
2827
|
close: 'wichqay',
|
|
@@ -2699,6 +2829,7 @@ const qu$1 = {
|
|
|
2699
2829
|
select: 'akllay',
|
|
2700
2830
|
clone: 'kikinchay',
|
|
2701
2831
|
prepend: 'ñawpachiy',
|
|
2832
|
+
socket: 'tinkina',
|
|
2702
2833
|
},
|
|
2703
2834
|
modifiers: {
|
|
2704
2835
|
to: 'man',
|
|
@@ -2721,8 +2852,10 @@ const qu$1 = {
|
|
|
2721
2852
|
events: {
|
|
2722
2853
|
click: 'ñitiy',
|
|
2723
2854
|
dblclick: 'iskay_ñitiy',
|
|
2724
|
-
|
|
2725
|
-
|
|
2855
|
+
// Fused (no `_`): the semantic tokenizer splits on `_` — and `rat_ñitiy` then
|
|
2856
|
+
// mis-captured as the `click` event (ñitiy→click). Mirrors the #535 ru/uk fuse.
|
|
2857
|
+
mousedown: 'ratñitiy',
|
|
2858
|
+
mouseup: 'rathuqariy',
|
|
2726
2859
|
mouseenter: 'rat_yaykuy',
|
|
2727
2860
|
mouseleave: 'rat_lluqsiy',
|
|
2728
2861
|
mouseover: 'rat_hawapi',
|
|
@@ -2761,7 +2894,11 @@ const qu$1 = {
|
|
|
2761
2894
|
has: 'kachkan',
|
|
2762
2895
|
have: 'kachkani',
|
|
2763
2896
|
then: 'chayqa',
|
|
2764
|
-
else
|
|
2897
|
+
// Align to the semantic profile's else word `manachus`. The old `mana_chayqa`
|
|
2898
|
+
// tokenized as `mana`(false) + `_` + `chayqa`(then) — no else keyword formed,
|
|
2899
|
+
// so qu conditionals never split their else branch. `manachus` is a single
|
|
2900
|
+
// token the profile-derived keyword map reads as `else` (dict↔profile align).
|
|
2901
|
+
else: 'manachus',
|
|
2765
2902
|
otherwise: 'huk_kaqpi',
|
|
2766
2903
|
end: 'tukuy',
|
|
2767
2904
|
live: 'kawsay',
|
|
@@ -2784,7 +2921,7 @@ const qu$1 = {
|
|
|
2784
2921
|
values: {
|
|
2785
2922
|
true: 'cheqaq',
|
|
2786
2923
|
false: 'llulla',
|
|
2787
|
-
null: '
|
|
2924
|
+
null: 'chusaq',
|
|
2788
2925
|
undefined: 'mana_riqsisqa',
|
|
2789
2926
|
it: 'chay',
|
|
2790
2927
|
its: 'chaypaq',
|
|
@@ -2822,12 +2959,12 @@ const qu$1 = {
|
|
|
2822
2959
|
prev: 'ñawpaq',
|
|
2823
2960
|
at: 'pi',
|
|
2824
2961
|
random: 'imaymanata',
|
|
2825
|
-
closest: '
|
|
2962
|
+
closest: 'kaylla',
|
|
2826
2963
|
parent: 'mama_tayta',
|
|
2827
2964
|
children: 'wawakuna',
|
|
2828
2965
|
within: 'ukupi',
|
|
2829
2966
|
no: 'mana_kanchu',
|
|
2830
|
-
empty: '
|
|
2967
|
+
empty: 'chusaq',
|
|
2831
2968
|
some: 'wakin',
|
|
2832
2969
|
'starts with': 'qallarisqa wan',
|
|
2833
2970
|
'ends with': 'tukusqa wan',
|
|
@@ -2879,11 +3016,17 @@ const sw$1 = {
|
|
|
2879
3016
|
send: 'tuma',
|
|
2880
3017
|
take: 'chukua',
|
|
2881
3018
|
put: 'weka',
|
|
2882
|
-
set
|
|
3019
|
+
// weka is the semantic sw profile's `put` primary; `set` is seti. Emitting weka
|
|
3020
|
+
// for set made the semantic parser read a transformed `set` as `put`.
|
|
3021
|
+
// See ZH_BLOCK_BODY_SCOPE.md (#2 sweep — keyword realignment, cf. zh fetch).
|
|
3022
|
+
set: 'seti',
|
|
2883
3023
|
get: 'pata',
|
|
2884
3024
|
add: 'ongeza',
|
|
2885
3025
|
remove: 'ondoa',
|
|
2886
3026
|
toggle: 'badilisha',
|
|
3027
|
+
// command blur (the events section has the blur EVENT word; without this
|
|
3028
|
+
// entry the transformer fell back to it and no profile read it as the verb)
|
|
3029
|
+
blur: 'blur',
|
|
2887
3030
|
hide: 'ficha',
|
|
2888
3031
|
show: 'onyesha',
|
|
2889
3032
|
if: 'kama',
|
|
@@ -2899,13 +3042,13 @@ const sw$1 = {
|
|
|
2899
3042
|
fetch: 'leta',
|
|
2900
3043
|
call: 'ita',
|
|
2901
3044
|
return: 'rudi',
|
|
2902
|
-
make: '
|
|
3045
|
+
make: 'tengeneza', // auditor: dict emitted a word the profile reads differently
|
|
2903
3046
|
log: 'andika',
|
|
2904
3047
|
throw: 'tupa',
|
|
2905
3048
|
catch: 'shika',
|
|
2906
3049
|
measure: 'pima',
|
|
2907
3050
|
transition: 'mpito',
|
|
2908
|
-
increment: '
|
|
3051
|
+
increment: 'ongezeko',
|
|
2909
3052
|
decrement: 'punguza',
|
|
2910
3053
|
default: 'msingi',
|
|
2911
3054
|
go: 'nenda',
|
|
@@ -2918,18 +3061,20 @@ const sw$1 = {
|
|
|
2918
3061
|
async: 'sainkroni',
|
|
2919
3062
|
render: 'chora',
|
|
2920
3063
|
swap: 'badilishana',
|
|
2921
|
-
morph: '
|
|
3064
|
+
morph: 'geuza', // auditor: dict emitted a word the profile reads differently
|
|
2922
3065
|
settle: 'tulia',
|
|
2923
|
-
append: 'ongezaMwisho'
|
|
3066
|
+
append: 'ambatanisha', // profile primary; 'ongezaMwisho' is unrecognized
|
|
2924
3067
|
exit: 'toka',
|
|
2925
3068
|
install: 'sakinisha',
|
|
2926
3069
|
breakpoint: 'nukta-simama',
|
|
2927
|
-
clear: '
|
|
3070
|
+
clear: 'safisha', // futa is a remove alternative in the profile
|
|
2928
3071
|
close: 'funga',
|
|
2929
3072
|
open: 'fungua',
|
|
2930
3073
|
select: 'chagua',
|
|
2931
3074
|
clone: 'nakili',
|
|
2932
3075
|
prepend: 'tanguliza',
|
|
3076
|
+
focus: 'lenga',
|
|
3077
|
+
socket: 'soketi',
|
|
2933
3078
|
},
|
|
2934
3079
|
modifiers: {
|
|
2935
3080
|
to: 'kwa',
|
|
@@ -2939,7 +3084,14 @@ const sw$1 = {
|
|
|
2939
3084
|
at: 'katika',
|
|
2940
3085
|
in: 'ndani',
|
|
2941
3086
|
of: 'ya',
|
|
2942
|
-
|
|
3087
|
+
// `kuwa` ("to be/become" — the conversion sense, cf. `badilisha kuwa`), not
|
|
3088
|
+
// `kama`: sw `kama` is the IF keyword (commands.if above and the semantic sw
|
|
3089
|
+
// profile's if primary), so emitting it for `as` grew a phantom `if` command
|
|
3090
|
+
// out of every transformed `as <Type>` tail (`kama JSON`, `kama Number`) when
|
|
3091
|
+
// the semantic parser split event-body statements. Same dict↔profile homonym
|
|
3092
|
+
// disambiguation as pl get/pobierz. The semantic fetch-sw pattern still
|
|
3093
|
+
// tolerates hand-written `kama` in as-marker position.
|
|
3094
|
+
as: 'kuwa',
|
|
2943
3095
|
by: 'na',
|
|
2944
3096
|
before: 'kabla',
|
|
2945
3097
|
after: 'baada',
|
|
@@ -2995,7 +3147,7 @@ const sw$1 = {
|
|
|
2995
3147
|
else: 'sivyo',
|
|
2996
3148
|
otherwise: 'vinginevyo',
|
|
2997
3149
|
end: 'mwisho',
|
|
2998
|
-
live: '
|
|
3150
|
+
live: 'mubashara',
|
|
2999
3151
|
changes: 'inabadilika',
|
|
3000
3152
|
},
|
|
3001
3153
|
temporal: {
|
|
@@ -3047,13 +3199,25 @@ const sw$1 = {
|
|
|
3047
3199
|
},
|
|
3048
3200
|
expressions: {
|
|
3049
3201
|
first: 'kwanza',
|
|
3050
|
-
|
|
3202
|
+
// 'mwisho' is the END keyword (block terminator) — the tokenizer's keyword
|
|
3203
|
+
// map is last-wins, so a positional `last` emission of 'mwisho' reads as
|
|
3204
|
+
// `end` and terminates the enclosing block mid-condition (focus-trap's
|
|
3205
|
+
// `… inafanana mwisho <button/>` dropped the whole branch body). Emit the
|
|
3206
|
+
// concatenated adjective form instead (wa mwisho — the saufsi/wennnicht/
|
|
3207
|
+
// enyakın single-token class); the tokenizer reads it as `last`.
|
|
3208
|
+
last: 'wamwisho',
|
|
3051
3209
|
next: 'ijayo',
|
|
3052
3210
|
previous: 'iliyopita',
|
|
3053
3211
|
prev: 'awali',
|
|
3054
3212
|
at: 'katika',
|
|
3055
3213
|
random: 'nasibu',
|
|
3056
|
-
|
|
3214
|
+
// Natural Swahili: `karibu` ("near/close") is the ordinary word and already
|
|
3215
|
+
// maps to closest. The earlier `karibu_zaidi` ("nearer") underscore-split to
|
|
3216
|
+
// karibu/_/zaidi (the stray `_ zaidi` broke positional capture); the fused
|
|
3217
|
+
// `karibuzaidi` parsed but isn't real Swahili. `karibu` alone is both natural
|
|
3218
|
+
// and correct (the sw tokenizer is single-word-only, so a spaced `karibu
|
|
3219
|
+
// zaidi` would strand `zaidi` regardless).
|
|
3220
|
+
closest: 'karibu',
|
|
3057
3221
|
parent: 'mzazi',
|
|
3058
3222
|
children: 'watoto',
|
|
3059
3223
|
within: 'ndani_ya',
|
|
@@ -3115,17 +3279,21 @@ const pt$1 = {
|
|
|
3115
3279
|
add: 'adicionar',
|
|
3116
3280
|
remove: 'remover',
|
|
3117
3281
|
toggle: 'alternar',
|
|
3282
|
+
// command blur (the events section has the blur EVENT word; without this
|
|
3283
|
+
// entry the transformer fell back to it and no profile read it as the verb)
|
|
3284
|
+
blur: 'desfocar',
|
|
3118
3285
|
hide: 'esconder',
|
|
3119
3286
|
show: 'mostrar',
|
|
3120
3287
|
if: 'se',
|
|
3121
|
-
unless: '
|
|
3288
|
+
unless: 'salvo', // a_menos splits at _ in the pt extractor (see profile)
|
|
3122
3289
|
repeat: 'repetir',
|
|
3123
3290
|
for: 'para',
|
|
3124
3291
|
while: 'enquanto',
|
|
3125
3292
|
until: 'até',
|
|
3126
3293
|
continue: 'continuar',
|
|
3127
|
-
|
|
3128
|
-
|
|
3294
|
+
// un-crossed: the profile/tokenizer read parar=halt, interromper=break
|
|
3295
|
+
break: 'interromper',
|
|
3296
|
+
halt: 'parar',
|
|
3129
3297
|
wait: 'esperar',
|
|
3130
3298
|
fetch: 'buscar',
|
|
3131
3299
|
call: 'chamar',
|
|
@@ -3161,6 +3329,8 @@ const pt$1 = {
|
|
|
3161
3329
|
select: 'selecionar',
|
|
3162
3330
|
clone: 'clonar',
|
|
3163
3331
|
prepend: 'preceder',
|
|
3332
|
+
focus: 'focar',
|
|
3333
|
+
socket: 'soquete',
|
|
3164
3334
|
},
|
|
3165
3335
|
modifiers: {
|
|
3166
3336
|
to: 'para',
|
|
@@ -3284,7 +3454,7 @@ const pt$1 = {
|
|
|
3284
3454
|
prev: 'ant',
|
|
3285
3455
|
at: 'em',
|
|
3286
3456
|
random: 'aleatório',
|
|
3287
|
-
closest: '
|
|
3457
|
+
closest: 'maispróximo',
|
|
3288
3458
|
parent: 'pai',
|
|
3289
3459
|
children: 'filhos',
|
|
3290
3460
|
within: 'dentro',
|
|
@@ -3349,7 +3519,7 @@ const it$1 = {
|
|
|
3349
3519
|
hide: 'nascondere',
|
|
3350
3520
|
show: 'mostrare',
|
|
3351
3521
|
if: 'se',
|
|
3352
|
-
unless: '
|
|
3522
|
+
unless: 'salvo',
|
|
3353
3523
|
repeat: 'ripetere',
|
|
3354
3524
|
for: 'per',
|
|
3355
3525
|
while: 'mentre',
|
|
@@ -3382,7 +3552,7 @@ const it$1 = {
|
|
|
3382
3552
|
swap: 'scambiare',
|
|
3383
3553
|
morph: 'trasformare',
|
|
3384
3554
|
settle: 'stabilizzare',
|
|
3385
|
-
append: 'aggiungere'
|
|
3555
|
+
append: 'accodare', // profile primary; 'aggiungere' is add's word (parsed as add)
|
|
3386
3556
|
exit: 'uscire',
|
|
3387
3557
|
install: 'installare',
|
|
3388
3558
|
breakpoint: 'punto-interruzione',
|
|
@@ -3392,6 +3562,9 @@ const it$1 = {
|
|
|
3392
3562
|
select: 'selezionare',
|
|
3393
3563
|
clone: 'clonare',
|
|
3394
3564
|
prepend: 'anteporre',
|
|
3565
|
+
// profile primary; without a commands entry the transformer fell back to
|
|
3566
|
+
// the EVENTS noun 'fuoco', which generated focus patterns can't match
|
|
3567
|
+
focus: 'focalizzare',
|
|
3395
3568
|
},
|
|
3396
3569
|
modifiers: {
|
|
3397
3570
|
to: 'a',
|
|
@@ -3515,7 +3688,7 @@ const it$1 = {
|
|
|
3515
3688
|
prev: 'prec',
|
|
3516
3689
|
at: 'a',
|
|
3517
3690
|
random: 'casuale',
|
|
3518
|
-
closest: '
|
|
3691
|
+
closest: 'vicino',
|
|
3519
3692
|
parent: 'genitore',
|
|
3520
3693
|
children: 'figli',
|
|
3521
3694
|
within: 'dentro',
|
|
@@ -3583,7 +3756,7 @@ const vi$1 = {
|
|
|
3583
3756
|
beep: 'beep',
|
|
3584
3757
|
js: 'js',
|
|
3585
3758
|
async: 'bất đồng bộ',
|
|
3586
|
-
render: '
|
|
3759
|
+
render: 'kết xuất', // distinct from `show` (was `hiển thị`, which collides with show: the semantic profile reads `hiển thị` as show, `kết xuất` as render)
|
|
3587
3760
|
swap: 'hoán đổi',
|
|
3588
3761
|
morph: 'biến đổi',
|
|
3589
3762
|
settle: 'ổn định',
|
|
@@ -3598,7 +3771,7 @@ const vi$1 = {
|
|
|
3598
3771
|
prepend: 'thêm đầu',
|
|
3599
3772
|
else: 'không thì',
|
|
3600
3773
|
breakpoint: 'điểm-dừng',
|
|
3601
|
-
clear: '
|
|
3774
|
+
clear: 'tẩy', // xóa is the remove PRIMARY in the profile
|
|
3602
3775
|
close: 'đóng',
|
|
3603
3776
|
open: 'mở',
|
|
3604
3777
|
select: 'chọn',
|
|
@@ -3662,7 +3835,7 @@ const vi$1 = {
|
|
|
3662
3835
|
else: 'không thì',
|
|
3663
3836
|
end: 'kết thúc',
|
|
3664
3837
|
then: 'rồi',
|
|
3665
|
-
live: '
|
|
3838
|
+
live: 'live',
|
|
3666
3839
|
changes: 'thay đổi',
|
|
3667
3840
|
},
|
|
3668
3841
|
temporal: {
|
|
@@ -3749,15 +3922,22 @@ const pl$1 = {
|
|
|
3749
3922
|
commands: {
|
|
3750
3923
|
on: 'gdy',
|
|
3751
3924
|
tell: 'powiedz',
|
|
3752
|
-
trigger: '
|
|
3925
|
+
trigger: 'wyzwól', // auditor: dict emitted a word the profile reads differently
|
|
3753
3926
|
send: 'wyślij',
|
|
3754
3927
|
take: 'weź',
|
|
3755
3928
|
put: 'umieść',
|
|
3756
3929
|
set: 'ustaw',
|
|
3757
|
-
get
|
|
3930
|
+
// `uzyskaj` (the semantic pl profile's get primary), not `pobierz`: pl `pobierz`
|
|
3931
|
+
// ("download") is the profile's FETCH primary, so emitting it for get made the
|
|
3932
|
+
// parser read every transformed get as fetch (get-value lossy + a phantom fetch).
|
|
3933
|
+
// Same dict↔profile homonym disambiguation as tr load/install, qu get.
|
|
3934
|
+
get: 'uzyskaj',
|
|
3758
3935
|
add: 'dodaj',
|
|
3759
3936
|
remove: 'usuń',
|
|
3760
3937
|
toggle: 'przełącz',
|
|
3938
|
+
// command blur (the events section has the blur EVENT word; without this
|
|
3939
|
+
// entry the transformer fell back to it and no profile read it as the verb)
|
|
3940
|
+
blur: 'rozmyj',
|
|
3761
3941
|
hide: 'ukryj',
|
|
3762
3942
|
show: 'pokaż',
|
|
3763
3943
|
if: 'jeśli',
|
|
@@ -3798,12 +3978,13 @@ const pl$1 = {
|
|
|
3798
3978
|
exit: 'wyjdź',
|
|
3799
3979
|
install: 'zainstaluj',
|
|
3800
3980
|
breakpoint: 'punkt-przerwania',
|
|
3801
|
-
clear: '
|
|
3981
|
+
clear: 'zeruj', // wyczyść is a remove alternative in the profile
|
|
3802
3982
|
close: 'zamknij',
|
|
3803
3983
|
open: 'otwórz',
|
|
3804
3984
|
select: 'wybierz',
|
|
3805
3985
|
clone: 'sklonuj',
|
|
3806
3986
|
prepend: 'poprzedź',
|
|
3987
|
+
focus: 'skup',
|
|
3807
3988
|
},
|
|
3808
3989
|
modifiers: {
|
|
3809
3990
|
to: 'do',
|
|
@@ -3954,7 +4135,7 @@ const russianDictionary = {
|
|
|
3954
4135
|
commands: {
|
|
3955
4136
|
on: 'при',
|
|
3956
4137
|
tell: 'сказать',
|
|
3957
|
-
trigger: '
|
|
4138
|
+
trigger: 'запустить', // auditor: dict emitted a word the profile reads differently
|
|
3958
4139
|
send: 'отправить',
|
|
3959
4140
|
take: 'взять',
|
|
3960
4141
|
put: 'положить',
|
|
@@ -3965,8 +4146,12 @@ const russianDictionary = {
|
|
|
3965
4146
|
toggle: 'переключить',
|
|
3966
4147
|
hide: 'скрыть',
|
|
3967
4148
|
show: 'показать',
|
|
4149
|
+
// command verb; the events section keeps the noun прокрутка for `on scroll`.
|
|
4150
|
+
// Without this entry the transformer fell back to the event noun, which the
|
|
4151
|
+
// parser doesn't read as the scroll command (#321 focus family).
|
|
4152
|
+
scroll: 'прокрутить',
|
|
3968
4153
|
if: 'если',
|
|
3969
|
-
unless: '
|
|
4154
|
+
unless: 'кроме',
|
|
3970
4155
|
repeat: 'повторить',
|
|
3971
4156
|
for: 'для',
|
|
3972
4157
|
while: 'пока',
|
|
@@ -3999,10 +4184,10 @@ const russianDictionary = {
|
|
|
3999
4184
|
swap: 'поменять',
|
|
4000
4185
|
morph: 'трансформировать',
|
|
4001
4186
|
settle: 'стабилизировать',
|
|
4002
|
-
append: '
|
|
4187
|
+
append: 'дописать', // profile alternative; the compound splits and добавить parses as add
|
|
4003
4188
|
exit: 'выйти',
|
|
4004
4189
|
else: 'иначе',
|
|
4005
|
-
install: '
|
|
4190
|
+
install: 'инсталлировать', // loanword — `установить` is `set` (homonym); see russian profile
|
|
4006
4191
|
init: 'инициализировать',
|
|
4007
4192
|
behavior: 'поведение',
|
|
4008
4193
|
focus: 'сфокусировать',
|
|
@@ -4037,8 +4222,11 @@ const russianDictionary = {
|
|
|
4037
4222
|
events: {
|
|
4038
4223
|
click: 'клик',
|
|
4039
4224
|
dblclick: 'двойной_клик',
|
|
4040
|
-
|
|
4041
|
-
|
|
4225
|
+
// Fused (no `_`): the semantic tokenizer splits on `_`, so an underscore form
|
|
4226
|
+
// breaks event recognition (mousedown → bare expression). Mirrors the #510 tr
|
|
4227
|
+
// resize fix (boyut_değiştir → boyutlandırma) and the enyakın superlative.
|
|
4228
|
+
mousedown: 'мышьвниз',
|
|
4229
|
+
mouseup: 'мышьвверх',
|
|
4042
4230
|
mouseenter: 'мышь_вход',
|
|
4043
4231
|
mouseleave: 'мышь_выход',
|
|
4044
4232
|
mouseover: 'наведение',
|
|
@@ -4055,7 +4243,7 @@ const russianDictionary = {
|
|
|
4055
4243
|
reset: 'сброс',
|
|
4056
4244
|
load: 'загрузка',
|
|
4057
4245
|
unload: 'выгрузка',
|
|
4058
|
-
resize: '
|
|
4246
|
+
resize: 'изменениеразмера', // fused (no `_`) — see mousedown note above
|
|
4059
4247
|
scroll: 'прокрутка',
|
|
4060
4248
|
touchstart: 'касание_начало',
|
|
4061
4249
|
touchend: 'касание_конец',
|
|
@@ -4185,7 +4373,7 @@ const ukrainianDictionary = {
|
|
|
4185
4373
|
commands: {
|
|
4186
4374
|
on: 'при',
|
|
4187
4375
|
tell: 'сказати',
|
|
4188
|
-
trigger: '
|
|
4376
|
+
trigger: 'запустити', // auditor: dict emitted a word the profile reads differently
|
|
4189
4377
|
send: 'надіслати',
|
|
4190
4378
|
take: 'взяти',
|
|
4191
4379
|
put: 'покласти',
|
|
@@ -4196,8 +4384,12 @@ const ukrainianDictionary = {
|
|
|
4196
4384
|
toggle: 'перемкнути',
|
|
4197
4385
|
hide: 'сховати',
|
|
4198
4386
|
show: 'показати',
|
|
4387
|
+
// command verb; the events section keeps the noun прокрутка for `on scroll`.
|
|
4388
|
+
// Without this entry the transformer fell back to the event noun, which the
|
|
4389
|
+
// parser doesn't read as the scroll command (#321 focus family).
|
|
4390
|
+
scroll: 'прокрутити',
|
|
4199
4391
|
if: 'якщо',
|
|
4200
|
-
unless: '
|
|
4392
|
+
unless: 'крім',
|
|
4201
4393
|
repeat: 'повторити',
|
|
4202
4394
|
for: 'для',
|
|
4203
4395
|
while: 'поки',
|
|
@@ -4230,10 +4422,10 @@ const ukrainianDictionary = {
|
|
|
4230
4422
|
swap: 'поміняти',
|
|
4231
4423
|
morph: 'трансформувати',
|
|
4232
4424
|
settle: 'стабілізувати',
|
|
4233
|
-
append: '
|
|
4425
|
+
append: 'дописати', // profile alternative; the compound splits and додати parses as add
|
|
4234
4426
|
exit: 'вийти',
|
|
4235
4427
|
else: 'інакше',
|
|
4236
|
-
install: '
|
|
4428
|
+
install: 'інсталювати', // loanword — `встановити` is `set` (homonym); see ukrainian profile
|
|
4237
4429
|
init: 'ініціалізувати',
|
|
4238
4430
|
behavior: 'поведінка',
|
|
4239
4431
|
focus: 'сфокусувати',
|
|
@@ -4268,8 +4460,10 @@ const ukrainianDictionary = {
|
|
|
4268
4460
|
events: {
|
|
4269
4461
|
click: 'клік',
|
|
4270
4462
|
dblclick: 'подвійний_клік',
|
|
4271
|
-
|
|
4272
|
-
|
|
4463
|
+
// Fused (no `_`): the semantic tokenizer splits on `_`, so an underscore form
|
|
4464
|
+
// breaks event recognition. Mirrors the #510 tr resize fix + enyakın.
|
|
4465
|
+
mousedown: 'мишавниз',
|
|
4466
|
+
mouseup: 'мишавгору',
|
|
4273
4467
|
mouseenter: 'миша_вхід',
|
|
4274
4468
|
mouseleave: 'миша_вихід',
|
|
4275
4469
|
mouseover: 'наведення',
|
|
@@ -4286,7 +4480,7 @@ const ukrainianDictionary = {
|
|
|
4286
4480
|
reset: 'скидання',
|
|
4287
4481
|
load: 'завантаження',
|
|
4288
4482
|
unload: 'вивантаження',
|
|
4289
|
-
resize: '
|
|
4483
|
+
resize: 'змінарозміру', // fused (no `_`) — see mousedown note above
|
|
4290
4484
|
scroll: 'прокрутка',
|
|
4291
4485
|
touchstart: 'дотик_початок',
|
|
4292
4486
|
touchend: 'дотик_кінець',
|
|
@@ -4428,10 +4622,10 @@ const hindiDictionary = {
|
|
|
4428
4622
|
hide: 'छिपाएं',
|
|
4429
4623
|
show: 'दिखाएं',
|
|
4430
4624
|
if: 'अगर',
|
|
4431
|
-
unless: 'जब_तक_नहीं
|
|
4625
|
+
unless: 'जब तक नहीं', // spaced (not जब_तक_नहीं) → one multi-word `unless` token
|
|
4432
4626
|
repeat: 'दोहराएं',
|
|
4433
|
-
for: 'के_लिए
|
|
4434
|
-
while: 'जब
|
|
4627
|
+
for: 'हेतु', // single token — के_लिए splits at _ in the hi extractor
|
|
4628
|
+
while: 'जब तक',
|
|
4435
4629
|
until: 'तक',
|
|
4436
4630
|
continue: 'जारी',
|
|
4437
4631
|
break: 'रोकें',
|
|
@@ -4458,10 +4652,10 @@ const hindiDictionary = {
|
|
|
4458
4652
|
js: 'जेएस',
|
|
4459
4653
|
async: 'असिंक',
|
|
4460
4654
|
render: 'रेंडर',
|
|
4461
|
-
swap: '
|
|
4655
|
+
swap: 'विनिमय',
|
|
4462
4656
|
morph: 'रूपांतर',
|
|
4463
4657
|
settle: 'स्थिर',
|
|
4464
|
-
append: '
|
|
4658
|
+
append: 'संलग्न',
|
|
4465
4659
|
exit: 'बाहर',
|
|
4466
4660
|
else: 'वरना',
|
|
4467
4661
|
install: 'इंस्टॉल',
|
|
@@ -4476,6 +4670,7 @@ const hindiDictionary = {
|
|
|
4476
4670
|
close: 'बंद-करें',
|
|
4477
4671
|
open: 'खोलें',
|
|
4478
4672
|
select: 'चुनें',
|
|
4673
|
+
socket: 'सॉकेट',
|
|
4479
4674
|
},
|
|
4480
4675
|
modifiers: {
|
|
4481
4676
|
to: 'को',
|
|
@@ -4487,8 +4682,8 @@ const hindiDictionary = {
|
|
|
4487
4682
|
of: 'का',
|
|
4488
4683
|
as: 'के_रूप_में',
|
|
4489
4684
|
by: 'द्वारा',
|
|
4490
|
-
before: 'से
|
|
4491
|
-
after: 'के
|
|
4685
|
+
before: 'से पहले',
|
|
4686
|
+
after: 'के बाद',
|
|
4492
4687
|
over: 'के_ऊपर',
|
|
4493
4688
|
under: 'के_नीचे',
|
|
4494
4689
|
between: 'के_बीच',
|
|
@@ -4499,8 +4694,10 @@ const hindiDictionary = {
|
|
|
4499
4694
|
events: {
|
|
4500
4695
|
click: 'क्लिक',
|
|
4501
4696
|
dblclick: 'डबल_क्लिक',
|
|
4502
|
-
|
|
4503
|
-
|
|
4697
|
+
// Fused (no `_`): the semantic tokenizer splits on `_`, breaking event
|
|
4698
|
+
// recognition (mousedown → "नीचे"). Mirrors the #535 ru/uk fuse.
|
|
4699
|
+
mousedown: 'माउसनीचे',
|
|
4700
|
+
mouseup: 'माउसऊपर',
|
|
4504
4701
|
mouseenter: 'माउस_प्रवेश',
|
|
4505
4702
|
mouseleave: 'माउस_बाहर',
|
|
4506
4703
|
mouseover: 'माउस_ओवर',
|
|
@@ -4532,7 +4729,11 @@ const hindiDictionary = {
|
|
|
4532
4729
|
not: 'नहीं',
|
|
4533
4730
|
is: 'है',
|
|
4534
4731
|
exists: 'मौजूद',
|
|
4535
|
-
matches
|
|
4732
|
+
// Natural spaced phrase `मेल खाता` ("matches") — how Hindi writes it. The base
|
|
4733
|
+
// tokenizer now matches multi-word profile keywords, so the hindi profile maps
|
|
4734
|
+
// `मेल खाता`→matches without the underscore (`मेल_खाता` split) or the
|
|
4735
|
+
// concatenated `मेलखाता` (parsed, but unnatural) workarounds.
|
|
4736
|
+
matches: 'मेल खाता',
|
|
4536
4737
|
contains: 'शामिल',
|
|
4537
4738
|
includes: 'में_है',
|
|
4538
4739
|
equals: 'बराबर',
|
|
@@ -4674,7 +4875,7 @@ const bengaliDictionary = {
|
|
|
4674
4875
|
throw: 'নিক্ষেপ',
|
|
4675
4876
|
continue: 'চালিয়ে যান',
|
|
4676
4877
|
make: 'তৈরি করুন',
|
|
4677
|
-
append: '
|
|
4878
|
+
append: 'জুড়ুন',
|
|
4678
4879
|
prepend: 'শুরুতে যোগ',
|
|
4679
4880
|
focus: 'ফোকাস',
|
|
4680
4881
|
blur: 'ঝাপসা',
|
|
@@ -4703,6 +4904,7 @@ const bengaliDictionary = {
|
|
|
4703
4904
|
close: 'বন্ধ',
|
|
4704
4905
|
open: 'খুলুন',
|
|
4705
4906
|
select: 'নির্বাচন',
|
|
4907
|
+
socket: 'সকেট',
|
|
4706
4908
|
},
|
|
4707
4909
|
modifiers: {
|
|
4708
4910
|
to: 'কে',
|
|
@@ -4761,6 +4963,7 @@ const bengaliDictionary = {
|
|
|
4761
4963
|
end: 'শেষ',
|
|
4762
4964
|
then: 'তারপর',
|
|
4763
4965
|
live: 'লাইভ',
|
|
4966
|
+
bind: 'বাইন্ড',
|
|
4764
4967
|
changes: 'পরিবর্তিত হলে',
|
|
4765
4968
|
},
|
|
4766
4969
|
temporal: {
|
|
@@ -4860,7 +5063,9 @@ const thaiDictionary = {
|
|
|
4860
5063
|
prepend: 'เพิ่มหน้า',
|
|
4861
5064
|
focus: 'โฟกัส',
|
|
4862
5065
|
blur: 'เบลอ',
|
|
4863
|
-
|
|
5066
|
+
// เปลี่ยนผ่าน, not เปลี่ยน: the semantic th profile's `change` keyword is
|
|
5067
|
+
// เปลี่ยน, so a เปลี่ยน render could never anchor a transition command.
|
|
5068
|
+
transition: 'เปลี่ยนผ่าน',
|
|
4864
5069
|
settle: 'คงที่',
|
|
4865
5070
|
measure: 'วัด',
|
|
4866
5071
|
async: 'อะซิงค์',
|
|
@@ -5067,10 +5272,11 @@ const malayDictionary = {
|
|
|
5067
5272
|
init: 'mula',
|
|
5068
5273
|
clone: 'klon',
|
|
5069
5274
|
breakpoint: 'titik-henti',
|
|
5070
|
-
clear: '
|
|
5275
|
+
clear: 'bersihkan', // padam is a remove alternative in the profile
|
|
5071
5276
|
close: 'tutup',
|
|
5072
5277
|
open: 'buka',
|
|
5073
5278
|
select: 'pilih',
|
|
5279
|
+
socket: 'soket', // derived from the ms profile (socket.primary); see generate-i18n-dictionaries
|
|
5074
5280
|
},
|
|
5075
5281
|
modifiers: {
|
|
5076
5282
|
to: 'ke',
|
|
@@ -5247,10 +5453,13 @@ const tagalogDictionary = {
|
|
|
5247
5453
|
tell: 'sabihin',
|
|
5248
5454
|
trigger: 'palitawin',
|
|
5249
5455
|
send: 'ipadala',
|
|
5250
|
-
take: '
|
|
5456
|
+
take: 'kumuha', // auditor: dict emitted a word the profile reads differently
|
|
5251
5457
|
put: 'ilagay',
|
|
5252
5458
|
set: 'itakda',
|
|
5253
|
-
get
|
|
5459
|
+
// Align with the semantic tl profile's `get` primary (`kunin`); the previous
|
|
5460
|
+
// `kuhanin` is the base of fetch's `kuhanin_mula`, so the transformed `get`
|
|
5461
|
+
// dropped (get-value parsed degenerate). Same dict↔profile alignment as id `toggle`.
|
|
5462
|
+
get: 'kunin',
|
|
5254
5463
|
add: 'idagdag',
|
|
5255
5464
|
remove: 'alisin',
|
|
5256
5465
|
toggle: 'palitan',
|
|
@@ -5274,7 +5483,7 @@ const tagalogDictionary = {
|
|
|
5274
5483
|
throw: 'ihagis',
|
|
5275
5484
|
catch: 'hulihin',
|
|
5276
5485
|
measure: 'sukatin',
|
|
5277
|
-
transition: '
|
|
5486
|
+
transition: 'lumipat',
|
|
5278
5487
|
increment: 'dagdagan',
|
|
5279
5488
|
decrement: 'bawasan',
|
|
5280
5489
|
default: 'pamantayan',
|
|
@@ -5286,10 +5495,10 @@ const tagalogDictionary = {
|
|
|
5286
5495
|
beep: 'tunog',
|
|
5287
5496
|
js: 'js',
|
|
5288
5497
|
async: 'sabay',
|
|
5289
|
-
render: '
|
|
5498
|
+
render: 'irender', // auditor: dict emitted a word the profile reads differently
|
|
5290
5499
|
swap: 'palitan_pwesto',
|
|
5291
|
-
morph: '
|
|
5292
|
-
settle: '
|
|
5500
|
+
morph: 'baguhin', // auditor: dict emitted a word the profile reads differently
|
|
5501
|
+
settle: 'magpatahimik', // auditor: dict emitted a word the profile reads differently
|
|
5293
5502
|
append: 'idagdag_sa_dulo',
|
|
5294
5503
|
prepend: 'idagdag_sa_simula',
|
|
5295
5504
|
exit: 'lumabas',
|
|
@@ -5317,7 +5526,11 @@ const tagalogDictionary = {
|
|
|
5317
5526
|
as: 'bilang',
|
|
5318
5527
|
by: 'sa_pamamagitan_ng',
|
|
5319
5528
|
before: 'bago',
|
|
5320
|
-
after:
|
|
5529
|
+
// `matapos` (not `pagkatapos`) for the positional "after": `pagkatapos` is
|
|
5530
|
+
// also Tagalog "then", so emitting it for the position modifier collides with
|
|
5531
|
+
// the sequence connector. `matapos` is the unambiguous positional form and
|
|
5532
|
+
// matches the semantic tl profile's `after`.
|
|
5533
|
+
after: 'matapos',
|
|
5321
5534
|
over: 'sa_ibabaw',
|
|
5322
5535
|
under: 'sa_ilalim',
|
|
5323
5536
|
between: 'sa_pagitan',
|
|
@@ -5483,6 +5696,7 @@ const he$1 = {
|
|
|
5483
5696
|
beep: 'ביפ',
|
|
5484
5697
|
break: 'שבור',
|
|
5485
5698
|
call: 'קרא',
|
|
5699
|
+
clear: 'נקה', // profile's clear word — was untranslated (parsed as nothing)
|
|
5486
5700
|
continue: 'המשך',
|
|
5487
5701
|
copy: 'העתק',
|
|
5488
5702
|
decrement: 'הקטן',
|
|
@@ -5495,6 +5709,7 @@ const he$1 = {
|
|
|
5495
5709
|
halt: 'עצור',
|
|
5496
5710
|
hide: 'הסתר',
|
|
5497
5711
|
if: 'אם',
|
|
5712
|
+
unless: 'אלא',
|
|
5498
5713
|
increment: 'הגדל',
|
|
5499
5714
|
install: 'התקן',
|
|
5500
5715
|
js: 'js',
|
|
@@ -5509,6 +5724,7 @@ const he$1 = {
|
|
|
5509
5724
|
render: 'רנדר',
|
|
5510
5725
|
repeat: 'חזור',
|
|
5511
5726
|
return: 'החזר',
|
|
5727
|
+
scroll: 'גלול',
|
|
5512
5728
|
send: 'שלח',
|
|
5513
5729
|
set: 'קבע',
|
|
5514
5730
|
settle: 'התייצב',
|
|
@@ -5546,6 +5762,7 @@ const he$1 = {
|
|
|
5546
5762
|
end: 'סוף',
|
|
5547
5763
|
then: 'אז',
|
|
5548
5764
|
live: 'חי',
|
|
5765
|
+
bind: 'קשור',
|
|
5549
5766
|
changes: 'משתנה',
|
|
5550
5767
|
when: 'כאשר',
|
|
5551
5768
|
where: 'איפה',
|
|
@@ -5564,7 +5781,9 @@ const he$1 = {
|
|
|
5564
5781
|
your: 'שלך',
|
|
5565
5782
|
},
|
|
5566
5783
|
attributes: {},
|
|
5567
|
-
expressions: {
|
|
5784
|
+
expressions: {
|
|
5785
|
+
last: 'אחרון',
|
|
5786
|
+
},
|
|
5568
5787
|
};
|
|
5569
5788
|
|
|
5570
5789
|
// packages/i18n/src/parser/he.ts
|
|
@@ -5810,6 +6029,13 @@ const en$1 = {
|
|
|
5810
6029
|
increment: 'increment',
|
|
5811
6030
|
decrement: 'decrement',
|
|
5812
6031
|
default: 'default',
|
|
6032
|
+
// Reactivity Commands
|
|
6033
|
+
bind: 'bind',
|
|
6034
|
+
// Realtime / service-worker Commands
|
|
6035
|
+
eventsource: 'eventsource',
|
|
6036
|
+
socket: 'socket',
|
|
6037
|
+
worker: 'worker',
|
|
6038
|
+
intercept: 'intercept',
|
|
5813
6039
|
// Navigation Commands
|
|
5814
6040
|
go: 'go',
|
|
5815
6041
|
pushUrl: 'pushUrl',
|
|
@@ -6281,7 +6507,26 @@ function reorderRoles(roles, targetOrder) {
|
|
|
6281
6507
|
function insertMarkers(elements, markers, adpositionType) {
|
|
6282
6508
|
const result = [];
|
|
6283
6509
|
for (const element of elements) {
|
|
6284
|
-
|
|
6510
|
+
let marker = markers.find(m => m.role === element.role);
|
|
6511
|
+
// A selector-shaped "event" is never a trigger. It's the dangling target of
|
|
6512
|
+
// a locative `on` (`set @role to "alert" on #sr-announce`) that
|
|
6513
|
+
// splitOnCommandBoundaries splits off as a headless pseudo-handler (set/put
|
|
6514
|
+
// are deliberately NOT in ON_TARGET_COMMANDS — dual-destination collision).
|
|
6515
|
+
// Emitting the event marker after it (ko 할 때, ja で, bn এ, tr de) plants a
|
|
6516
|
+
// spurious mid-stream event anchor in the emission; suppress the marker and
|
|
6517
|
+
// emit the bare selector, which body parsers skip harmlessly.
|
|
6518
|
+
if (marker?.role === 'event' && /^[#.<@[]/.test(element.translated || element.value)) {
|
|
6519
|
+
marker = undefined;
|
|
6520
|
+
}
|
|
6521
|
+
// A pronoun-valued "duration" is never a time expression. It's the target
|
|
6522
|
+
// phrase of `take … for me` (en maps `for` → duration lexically), kept
|
|
6523
|
+
// in-clause by splitOnCommandBoundaries' loop-head guard. Emitting the
|
|
6524
|
+
// duration marker after it (bn জন্য — also bn's `for` loop keyword) anchors
|
|
6525
|
+
// a spurious `for` command in the semantic parse; suppress the marker and
|
|
6526
|
+
// emit the bare pronoun, which take-pattern matchers skip harmlessly.
|
|
6527
|
+
if (marker?.role === 'duration' && /^(me|you|it)$/i.test(element.value)) {
|
|
6528
|
+
marker = undefined;
|
|
6529
|
+
}
|
|
6285
6530
|
if (marker) {
|
|
6286
6531
|
if (adpositionType === 'preposition') {
|
|
6287
6532
|
// Marker before element: "to element"
|
|
@@ -6455,6 +6700,41 @@ const japaneseProfile = {
|
|
|
6455
6700
|
insertMarkers: true,
|
|
6456
6701
|
},
|
|
6457
6702
|
},
|
|
6703
|
+
{
|
|
6704
|
+
name: 'set-to',
|
|
6705
|
+
description: 'Transform set X to Y to Japanese verb-final order',
|
|
6706
|
+
priority: 90,
|
|
6707
|
+
match: {
|
|
6708
|
+
commands: ['set', '設定'],
|
|
6709
|
+
requiredRoles: ['action', 'patient', 'destination'],
|
|
6710
|
+
// Skip a set whose value swept up a trailing block terminator (`set X to Y
|
|
6711
|
+
// end` — the body of an inline `if … then set … end`): verb-final reorder
|
|
6712
|
+
// would push the verb past `end` and break the block. These parse fine
|
|
6713
|
+
// verb-medial (the default).
|
|
6714
|
+
predicate: parsed => !/(^|\s)end$/i.test((parsed.original ?? '').trim()),
|
|
6715
|
+
},
|
|
6716
|
+
transform: {
|
|
6717
|
+
// X を Y に 設定 — verb-final, matching the generated SOV set pattern
|
|
6718
|
+
// (markerOverride: を on the destination / に on the value). Default reorder
|
|
6719
|
+
// emitted verb-MEDIAL (`X を 設定 Y に`), unmatched by any set pattern.
|
|
6720
|
+
roleOrder: ['patient', 'destination', 'action'],
|
|
6721
|
+
insertMarkers: true,
|
|
6722
|
+
},
|
|
6723
|
+
},
|
|
6724
|
+
{
|
|
6725
|
+
name: 'bind-to',
|
|
6726
|
+
description: 'Transform bind $var to #el to Japanese verb-final order',
|
|
6727
|
+
priority: 90,
|
|
6728
|
+
match: {
|
|
6729
|
+
commands: ['bind', 'バインド'],
|
|
6730
|
+
requiredRoles: ['action', 'patient', 'destination'],
|
|
6731
|
+
},
|
|
6732
|
+
transform: {
|
|
6733
|
+
// $var を #el に バインド
|
|
6734
|
+
roleOrder: ['patient', 'destination', 'action'],
|
|
6735
|
+
insertMarkers: true,
|
|
6736
|
+
},
|
|
6737
|
+
},
|
|
6458
6738
|
],
|
|
6459
6739
|
};
|
|
6460
6740
|
// =============================================================================
|
|
@@ -6473,6 +6753,10 @@ const koreanProfile = {
|
|
|
6473
6753
|
{ form: '를', role: 'patient', position: 'postposition', required: true, alternatives: ['을'] },
|
|
6474
6754
|
{ form: '에', role: 'destination', position: 'postposition', required: true },
|
|
6475
6755
|
{ form: '에서', role: 'source', position: 'postposition', required: true },
|
|
6756
|
+
// Event marker — the semantic *-event-ko-sov-* patterns anchor on 할 때
|
|
6757
|
+
// (the generator profile's eventMarker); without it every ko handler
|
|
6758
|
+
// emitted a bare event name no fused pattern could anchor.
|
|
6759
|
+
{ form: '할 때', role: 'event', position: 'postposition', required: true },
|
|
6476
6760
|
{
|
|
6477
6761
|
form: '로',
|
|
6478
6762
|
role: 'style',
|
|
@@ -6503,6 +6787,56 @@ const koreanProfile = {
|
|
|
6503
6787
|
insertMarkers: true,
|
|
6504
6788
|
},
|
|
6505
6789
|
},
|
|
6790
|
+
{
|
|
6791
|
+
name: 'put-into',
|
|
6792
|
+
description: 'Transform put X into Y to Korean verb-final order',
|
|
6793
|
+
priority: 90,
|
|
6794
|
+
match: {
|
|
6795
|
+
commands: ['put', '넣다'],
|
|
6796
|
+
requiredRoles: ['action', 'patient', 'destination'],
|
|
6797
|
+
// Standalone put only (e.g. a then-chain clause): an event handler whose
|
|
6798
|
+
// action is `put` must keep the event mid-stream, so don't verb-final it.
|
|
6799
|
+
predicate: parsed => !parsed.roles.has('event'),
|
|
6800
|
+
},
|
|
6801
|
+
transform: {
|
|
6802
|
+
// X 를 Y 에 넣다 (patient, destination, verb-last)
|
|
6803
|
+
roleOrder: ['patient', 'destination', 'action'],
|
|
6804
|
+
insertMarkers: true,
|
|
6805
|
+
},
|
|
6806
|
+
},
|
|
6807
|
+
{
|
|
6808
|
+
name: 'set-to',
|
|
6809
|
+
description: 'Transform set X to Y to Korean verb-final order',
|
|
6810
|
+
priority: 90,
|
|
6811
|
+
match: {
|
|
6812
|
+
commands: ['set', '설정'],
|
|
6813
|
+
requiredRoles: ['action', 'patient', 'destination'],
|
|
6814
|
+
// Skip a set whose value swept up a trailing block terminator (`set X to Y
|
|
6815
|
+
// end` — the body of an inline `if … then set … end`): verb-final reorder
|
|
6816
|
+
// would push the verb past `end` and break the block. These parse fine
|
|
6817
|
+
// verb-medial (the default).
|
|
6818
|
+
predicate: parsed => !/(^|\s)end$/i.test((parsed.original ?? '').trim()),
|
|
6819
|
+
},
|
|
6820
|
+
transform: {
|
|
6821
|
+
// X 를 Y 에 설정 — verb-final, matching the generated SOV set pattern.
|
|
6822
|
+
roleOrder: ['patient', 'destination', 'action'],
|
|
6823
|
+
insertMarkers: true,
|
|
6824
|
+
},
|
|
6825
|
+
},
|
|
6826
|
+
{
|
|
6827
|
+
name: 'bind-to',
|
|
6828
|
+
description: 'Transform bind $var to #el to Korean verb-final order',
|
|
6829
|
+
priority: 90,
|
|
6830
|
+
match: {
|
|
6831
|
+
commands: ['bind', '바인드'],
|
|
6832
|
+
requiredRoles: ['action', 'patient', 'destination'],
|
|
6833
|
+
},
|
|
6834
|
+
transform: {
|
|
6835
|
+
// $var 를 #el 에 바인드
|
|
6836
|
+
roleOrder: ['patient', 'destination', 'action'],
|
|
6837
|
+
insertMarkers: true,
|
|
6838
|
+
},
|
|
6839
|
+
},
|
|
6506
6840
|
],
|
|
6507
6841
|
};
|
|
6508
6842
|
// =============================================================================
|
|
@@ -6537,19 +6871,29 @@ const chineseProfile = {
|
|
|
6537
6871
|
requiredRoles: ['event', 'action'],
|
|
6538
6872
|
},
|
|
6539
6873
|
transform: {
|
|
6540
|
-
// 当
|
|
6874
|
+
// 当 EVENT 时 ACTION [patient] [destination] [source] [style] [method] ...
|
|
6541
6875
|
roleOrder: ['event', 'action', 'patient'],
|
|
6542
6876
|
insertMarkers: true,
|
|
6543
|
-
custom: (parsed,
|
|
6544
|
-
// Handle 当...时 circumfix
|
|
6877
|
+
custom: (parsed, profile) => {
|
|
6878
|
+
// Handle 当...时 circumfix wrapping the event, then emit the action,
|
|
6879
|
+
// then any remaining roles in canonical order with their markers.
|
|
6880
|
+
// Earlier this function emitted only event/action/patient and silently
|
|
6881
|
+
// dropped destination, source, style, etc. — that produced truncated
|
|
6882
|
+
// output for `send X to Y`, `fetch /api with method:...`, etc.
|
|
6545
6883
|
const event = parsed.roles.get('event');
|
|
6546
6884
|
const action = parsed.roles.get('action');
|
|
6547
|
-
|
|
6548
|
-
|
|
6549
|
-
|
|
6550
|
-
|
|
6551
|
-
|
|
6552
|
-
|
|
6885
|
+
// Build the post-action role list using the canonical order minus
|
|
6886
|
+
// event/action, plus a safety net for any roles not in the order.
|
|
6887
|
+
const remaining = new Map(parsed.roles);
|
|
6888
|
+
remaining.delete('event');
|
|
6889
|
+
remaining.delete('action');
|
|
6890
|
+
const remainingOrder = (profile.canonicalOrder || []).filter(r => r !== 'event' && r !== 'action');
|
|
6891
|
+
const reordered = reorderRoles(remaining, remainingOrder);
|
|
6892
|
+
const markedTokens = insertMarkers(reordered, profile.markers, profile.adpositionType);
|
|
6893
|
+
const parts = ['当', event?.translated || event?.value || '', '时'];
|
|
6894
|
+
if (action)
|
|
6895
|
+
parts.push(action.translated || action.value);
|
|
6896
|
+
parts.push(...markedTokens);
|
|
6553
6897
|
return parts.filter(Boolean).join(' ');
|
|
6554
6898
|
},
|
|
6555
6899
|
},
|
|
@@ -6568,6 +6912,30 @@ const chineseProfile = {
|
|
|
6568
6912
|
insertMarkers: true,
|
|
6569
6913
|
},
|
|
6570
6914
|
},
|
|
6915
|
+
{
|
|
6916
|
+
name: 'bind-to',
|
|
6917
|
+
description: 'Bind binds a variable to an element: 绑定 X 到 Y (no 把)',
|
|
6918
|
+
priority: 90,
|
|
6919
|
+
match: {
|
|
6920
|
+
commands: ['bind', '绑定'],
|
|
6921
|
+
requiredRoles: ['action', 'patient', 'destination'],
|
|
6922
|
+
},
|
|
6923
|
+
transform: {
|
|
6924
|
+
// Plain SVO without the 把 object-fronting: the bound variable is the
|
|
6925
|
+
// semantic destination (not a fronted patient), and `到` marks the
|
|
6926
|
+
// target element. Custom so the patient does not pick up 把.
|
|
6927
|
+
roleOrder: ['action', 'patient', 'destination'],
|
|
6928
|
+
custom: parsed => {
|
|
6929
|
+
const action = parsed.roles.get('action');
|
|
6930
|
+
const patient = parsed.roles.get('patient');
|
|
6931
|
+
const destination = parsed.roles.get('destination');
|
|
6932
|
+
const verb = action?.translated || action?.value || '';
|
|
6933
|
+
const v = patient?.translated || patient?.value || '';
|
|
6934
|
+
const d = destination?.translated || destination?.value || '';
|
|
6935
|
+
return [verb, v, '到', d].filter(Boolean).join(' ');
|
|
6936
|
+
},
|
|
6937
|
+
},
|
|
6938
|
+
},
|
|
6571
6939
|
],
|
|
6572
6940
|
};
|
|
6573
6941
|
// =============================================================================
|
|
@@ -6664,6 +7032,56 @@ const turkishProfile = {
|
|
|
6664
7032
|
insertMarkers: true,
|
|
6665
7033
|
},
|
|
6666
7034
|
},
|
|
7035
|
+
{
|
|
7036
|
+
name: 'put-into',
|
|
7037
|
+
description: 'Transform put X into Y to Turkish verb-final order',
|
|
7038
|
+
priority: 90,
|
|
7039
|
+
match: {
|
|
7040
|
+
commands: ['put', 'koy'],
|
|
7041
|
+
requiredRoles: ['action', 'patient', 'destination'],
|
|
7042
|
+
// Standalone put only (e.g. a then-chain clause): an event handler whose
|
|
7043
|
+
// action is `put` must keep the event mid-stream, so don't verb-final it.
|
|
7044
|
+
predicate: parsed => !parsed.roles.has('event'),
|
|
7045
|
+
},
|
|
7046
|
+
transform: {
|
|
7047
|
+
// X i Y e koy (patient, destination, verb-last)
|
|
7048
|
+
roleOrder: ['patient', 'destination', 'action'],
|
|
7049
|
+
insertMarkers: true,
|
|
7050
|
+
},
|
|
7051
|
+
},
|
|
7052
|
+
{
|
|
7053
|
+
name: 'set-to',
|
|
7054
|
+
description: 'Transform set X to Y to Turkish verb-final order',
|
|
7055
|
+
priority: 90,
|
|
7056
|
+
match: {
|
|
7057
|
+
commands: ['set', 'ayarla'],
|
|
7058
|
+
requiredRoles: ['action', 'patient', 'destination'],
|
|
7059
|
+
// Skip a set whose value swept up a trailing block terminator (`set X to Y
|
|
7060
|
+
// end` — the body of an inline `if … then set … end`): verb-final reorder
|
|
7061
|
+
// would push the verb past `end` and break the block. These parse fine
|
|
7062
|
+
// verb-medial (the default).
|
|
7063
|
+
predicate: parsed => !/(^|\s)end$/i.test((parsed.original ?? '').trim()),
|
|
7064
|
+
},
|
|
7065
|
+
transform: {
|
|
7066
|
+
// X i Y e ayarla — verb-final, matching the generated SOV set pattern.
|
|
7067
|
+
roleOrder: ['patient', 'destination', 'action'],
|
|
7068
|
+
insertMarkers: true,
|
|
7069
|
+
},
|
|
7070
|
+
},
|
|
7071
|
+
{
|
|
7072
|
+
name: 'bind-to',
|
|
7073
|
+
description: 'Transform bind $var to #el to Turkish verb-final order',
|
|
7074
|
+
priority: 90,
|
|
7075
|
+
match: {
|
|
7076
|
+
commands: ['bind'],
|
|
7077
|
+
requiredRoles: ['action', 'patient', 'destination'],
|
|
7078
|
+
},
|
|
7079
|
+
transform: {
|
|
7080
|
+
// $var i #el e bağla
|
|
7081
|
+
roleOrder: ['patient', 'destination', 'action'],
|
|
7082
|
+
insertMarkers: true,
|
|
7083
|
+
},
|
|
7084
|
+
},
|
|
6667
7085
|
],
|
|
6668
7086
|
};
|
|
6669
7087
|
// =============================================================================
|
|
@@ -6773,6 +7191,30 @@ const indonesianProfile = {
|
|
|
6773
7191
|
],
|
|
6774
7192
|
};
|
|
6775
7193
|
// =============================================================================
|
|
7194
|
+
// Malay (SVO, Austronesian) — grammar mirrors Indonesian (shared function words:
|
|
7195
|
+
// pada / ke / dari / dengan / sebagai). Without this profile the transformer threw
|
|
7196
|
+
// "Unknown target locale: ms", so no ms translation could be generated at all.
|
|
7197
|
+
// =============================================================================
|
|
7198
|
+
const malayProfile = {
|
|
7199
|
+
code: 'ms',
|
|
7200
|
+
name: 'Bahasa Melayu',
|
|
7201
|
+
wordOrder: 'SVO',
|
|
7202
|
+
adpositionType: 'preposition',
|
|
7203
|
+
morphology: 'agglutinative',
|
|
7204
|
+
direction: 'ltr',
|
|
7205
|
+
canonicalOrder: ['event', 'action', 'patient', 'destination'],
|
|
7206
|
+
markers: [
|
|
7207
|
+
// ms marks the event clause with `apabila` ("when/on") — matching the semantic
|
|
7208
|
+
// ms event-handler pattern (`apabila {event} {body}`) and the ms dict's
|
|
7209
|
+
// `on: apabila`. (Indonesian uses `pada` here; the two diverge on the event head.)
|
|
7210
|
+
{ form: 'apabila', role: 'event', position: 'preposition', required: true },
|
|
7211
|
+
{ form: 'ke', role: 'destination', position: 'preposition', required: false },
|
|
7212
|
+
{ form: 'dari', role: 'source', position: 'preposition', required: false },
|
|
7213
|
+
{ form: 'dengan', role: 'style', position: 'preposition', required: false },
|
|
7214
|
+
{ form: 'sebagai', role: 'method', position: 'preposition', required: false },
|
|
7215
|
+
],
|
|
7216
|
+
};
|
|
7217
|
+
// =============================================================================
|
|
6776
7218
|
// Quechua (SOV, Quechuan)
|
|
6777
7219
|
// =============================================================================
|
|
6778
7220
|
const quechuaProfile = {
|
|
@@ -6811,7 +7253,10 @@ const swahiliProfile = {
|
|
|
6811
7253
|
{ form: 'kwa', role: 'destination', position: 'preposition', required: false },
|
|
6812
7254
|
{ form: 'kutoka', role: 'source', position: 'preposition', required: false },
|
|
6813
7255
|
{ form: 'na', role: 'style', position: 'preposition', required: false },
|
|
6814
|
-
|
|
7256
|
+
// `kuwa`, not `kama`: sw `kama` is the IF keyword — rendering the method
|
|
7257
|
+
// role ("as X") with it grew a phantom `if` at semantic parse time. See the
|
|
7258
|
+
// matching comment in dictionaries/sw.ts (modifiers.as).
|
|
7259
|
+
{ form: 'kuwa', role: 'method', position: 'preposition', required: false }, // "as/become"
|
|
6815
7260
|
],
|
|
6816
7261
|
};
|
|
6817
7262
|
// =============================================================================
|
|
@@ -6852,6 +7297,56 @@ const bengaliProfile = {
|
|
|
6852
7297
|
insertMarkers: true,
|
|
6853
7298
|
},
|
|
6854
7299
|
},
|
|
7300
|
+
{
|
|
7301
|
+
name: 'put-into',
|
|
7302
|
+
description: 'Transform put X into Y to Bengali verb-final order',
|
|
7303
|
+
priority: 90,
|
|
7304
|
+
match: {
|
|
7305
|
+
commands: ['put', 'রাখুন'],
|
|
7306
|
+
requiredRoles: ['action', 'patient', 'destination'],
|
|
7307
|
+
// Standalone put only (e.g. a then-chain clause): an event handler whose
|
|
7308
|
+
// action is `put` must keep the event mid-stream, so don't verb-final it.
|
|
7309
|
+
predicate: parsed => !parsed.roles.has('event'),
|
|
7310
|
+
},
|
|
7311
|
+
transform: {
|
|
7312
|
+
// X কে Y তে রাখুন (patient, destination, verb-last)
|
|
7313
|
+
roleOrder: ['patient', 'destination', 'action'],
|
|
7314
|
+
insertMarkers: true,
|
|
7315
|
+
},
|
|
7316
|
+
},
|
|
7317
|
+
{
|
|
7318
|
+
name: 'set-to',
|
|
7319
|
+
description: 'Transform set X to Y to Bengali verb-final order',
|
|
7320
|
+
priority: 90,
|
|
7321
|
+
match: {
|
|
7322
|
+
commands: ['set', 'সেট'],
|
|
7323
|
+
requiredRoles: ['action', 'patient', 'destination'],
|
|
7324
|
+
// Skip a set whose value swept up a trailing block terminator (`set X to Y
|
|
7325
|
+
// end` — the body of an inline `if … then set … end`): verb-final reorder
|
|
7326
|
+
// would push the verb past `end` and break the block. These parse fine
|
|
7327
|
+
// verb-medial (the default).
|
|
7328
|
+
predicate: parsed => !/(^|\s)end$/i.test((parsed.original ?? '').trim()),
|
|
7329
|
+
},
|
|
7330
|
+
transform: {
|
|
7331
|
+
// X কে Y তে সেট — verb-final, matching the generated SOV set pattern.
|
|
7332
|
+
roleOrder: ['patient', 'destination', 'action'],
|
|
7333
|
+
insertMarkers: true,
|
|
7334
|
+
},
|
|
7335
|
+
},
|
|
7336
|
+
{
|
|
7337
|
+
name: 'bind-to',
|
|
7338
|
+
description: 'Transform bind $var to #el to Bengali verb-final order',
|
|
7339
|
+
priority: 90,
|
|
7340
|
+
match: {
|
|
7341
|
+
commands: ['bind', 'বাইন্ড'],
|
|
7342
|
+
requiredRoles: ['action', 'patient', 'destination'],
|
|
7343
|
+
},
|
|
7344
|
+
transform: {
|
|
7345
|
+
// $var কে #el তে বাইন্ড
|
|
7346
|
+
roleOrder: ['patient', 'destination', 'action'],
|
|
7347
|
+
insertMarkers: true,
|
|
7348
|
+
},
|
|
7349
|
+
},
|
|
6855
7350
|
],
|
|
6856
7351
|
};
|
|
6857
7352
|
// =============================================================================
|
|
@@ -6984,6 +7479,61 @@ const hindiProfile = {
|
|
|
6984
7479
|
insertMarkers: true,
|
|
6985
7480
|
},
|
|
6986
7481
|
},
|
|
7482
|
+
{
|
|
7483
|
+
name: 'put-into',
|
|
7484
|
+
description: 'Transform put X into Y to Hindi verb-final order',
|
|
7485
|
+
priority: 90,
|
|
7486
|
+
match: {
|
|
7487
|
+
commands: ['put', 'रखें'],
|
|
7488
|
+
requiredRoles: ['action', 'patient', 'destination'],
|
|
7489
|
+
},
|
|
7490
|
+
transform: {
|
|
7491
|
+
// "hi" को #out में रखें
|
|
7492
|
+
roleOrder: ['patient', 'destination', 'action'],
|
|
7493
|
+
insertMarkers: true,
|
|
7494
|
+
},
|
|
7495
|
+
},
|
|
7496
|
+
{
|
|
7497
|
+
name: 'set-to',
|
|
7498
|
+
description: 'Transform set X to Y to Hindi verb-final order',
|
|
7499
|
+
priority: 90,
|
|
7500
|
+
match: {
|
|
7501
|
+
commands: ['set', 'सेट'],
|
|
7502
|
+
requiredRoles: ['action', 'patient', 'destination'],
|
|
7503
|
+
// Skip a set whose value swept up a trailing block terminator (`set X to Y
|
|
7504
|
+
// end` — the body of an inline `if … then set … end`): verb-final reorder
|
|
7505
|
+
// would push the verb past `end` and break the block. These parse fine
|
|
7506
|
+
// verb-medial (the default).
|
|
7507
|
+
predicate: parsed => !/(^|\s)end$/i.test((parsed.original ?? '').trim()),
|
|
7508
|
+
},
|
|
7509
|
+
transform: {
|
|
7510
|
+
// #x.innerText को इसका.name में सेट — verb-final, matching the generated
|
|
7511
|
+
// SOV set pattern (set schema markerOverride puts को on the destination /
|
|
7512
|
+
// में on the value). The default reorder emitted verb-MEDIAL
|
|
7513
|
+
// (`#x को सेट … में`), which no set pattern matched → bare-event fallback.
|
|
7514
|
+
roleOrder: ['patient', 'destination', 'action'],
|
|
7515
|
+
insertMarkers: true,
|
|
7516
|
+
},
|
|
7517
|
+
},
|
|
7518
|
+
{
|
|
7519
|
+
name: 'bind-to',
|
|
7520
|
+
description: 'Transform bind $var to #el to Hindi verb-final order',
|
|
7521
|
+
priority: 90,
|
|
7522
|
+
match: {
|
|
7523
|
+
commands: ['bind', 'बाइंड', 'बांधें'],
|
|
7524
|
+
requiredRoles: ['action', 'patient', 'destination'],
|
|
7525
|
+
},
|
|
7526
|
+
transform: {
|
|
7527
|
+
// $greeting को #name-input में bind — verb-final, matching the generated SOV
|
|
7528
|
+
// bind pattern (`{destination} को {source} में bind`). The default reorder
|
|
7529
|
+
// emitted verb-MEDIAL (`$greeting को bind #name-input में`), which the
|
|
7530
|
+
// verb-final pattern never matched → the bare-event fallback mis-anchored
|
|
7531
|
+
// the fronted `$greeting` as a phantom `on` event (the rf=0.00 bind residue).
|
|
7532
|
+
// ja/ko/zh/tr/bn already carry this rule; hi was the only SOV gap.
|
|
7533
|
+
roleOrder: ['patient', 'destination', 'action'],
|
|
7534
|
+
insertMarkers: true,
|
|
7535
|
+
},
|
|
7536
|
+
},
|
|
6987
7537
|
],
|
|
6988
7538
|
};
|
|
6989
7539
|
// =============================================================================
|
|
@@ -7001,7 +7551,11 @@ const tagalogProfile = {
|
|
|
7001
7551
|
markers: [
|
|
7002
7552
|
{ form: 'kapag', role: 'event', position: 'preposition', required: true },
|
|
7003
7553
|
{ form: 'sa', role: 'destination', position: 'preposition', required: false },
|
|
7004
|
-
|
|
7554
|
+
// mula_sa (underscore) matches the tl dict and the semantic profile's
|
|
7555
|
+
// source marker — the spaced form emitted two tokens the generated
|
|
7556
|
+
// patterns' single mula_sa literal could never match, so `remove X
|
|
7557
|
+
// mula sa Y` lost its source and the schema default fabricated me.
|
|
7558
|
+
{ form: 'mula_sa', role: 'source', position: 'preposition', required: false },
|
|
7005
7559
|
{ form: 'nang', role: 'style', position: 'preposition', required: false },
|
|
7006
7560
|
{ form: 'bilang', role: 'method', position: 'preposition', required: false },
|
|
7007
7561
|
],
|
|
@@ -7091,6 +7645,32 @@ const hebrewProfile = {
|
|
|
7091
7645
|
{ form: 'עם', role: 'style', position: 'preposition', required: false },
|
|
7092
7646
|
{ form: 'כ', role: 'method', position: 'preposition', required: false },
|
|
7093
7647
|
],
|
|
7648
|
+
rules: [
|
|
7649
|
+
{
|
|
7650
|
+
name: 'bind-to',
|
|
7651
|
+
description: 'Bind binds a variable to an element: קשור X ל Y',
|
|
7652
|
+
priority: 90,
|
|
7653
|
+
match: {
|
|
7654
|
+
commands: ['bind', 'קשור'],
|
|
7655
|
+
requiredRoles: ['action', 'patient', 'destination'],
|
|
7656
|
+
},
|
|
7657
|
+
transform: {
|
|
7658
|
+
// The bound variable is the semantic destination (kept bare, no את
|
|
7659
|
+
// object marker), and ל marks the target element. Custom so the
|
|
7660
|
+
// variable does not pick up את and the element uses ל (not על).
|
|
7661
|
+
roleOrder: ['action', 'patient', 'destination'],
|
|
7662
|
+
custom: parsed => {
|
|
7663
|
+
const action = parsed.roles.get('action');
|
|
7664
|
+
const patient = parsed.roles.get('patient');
|
|
7665
|
+
const destination = parsed.roles.get('destination');
|
|
7666
|
+
const verb = action?.translated || action?.value || '';
|
|
7667
|
+
const v = patient?.translated || patient?.value || '';
|
|
7668
|
+
const d = destination?.translated || destination?.value || '';
|
|
7669
|
+
return [verb, v, 'ל', d].filter(Boolean).join(' ');
|
|
7670
|
+
},
|
|
7671
|
+
},
|
|
7672
|
+
},
|
|
7673
|
+
],
|
|
7094
7674
|
};
|
|
7095
7675
|
// =============================================================================
|
|
7096
7676
|
// Profile Registry
|
|
@@ -7107,6 +7687,7 @@ const profiles = {
|
|
|
7107
7687
|
fr: frenchProfile,
|
|
7108
7688
|
pt: portugueseProfile,
|
|
7109
7689
|
id: indonesianProfile,
|
|
7690
|
+
ms: malayProfile,
|
|
7110
7691
|
qu: quechuaProfile,
|
|
7111
7692
|
sw: swahiliProfile,
|
|
7112
7693
|
bn: bengaliProfile,
|
|
@@ -7585,6 +8166,95 @@ function getCommandKeywordsForLocale(locale) {
|
|
|
7585
8166
|
}
|
|
7586
8167
|
return keywords;
|
|
7587
8168
|
}
|
|
8169
|
+
/**
|
|
8170
|
+
* English copula forms. A command keyword IMMEDIATELY after one of these is a
|
|
8171
|
+
* predicate adjective, not a command verb: in `if my value is empty add .error
|
|
8172
|
+
* to me` the `empty` belongs to the condition (`empty` is also a hyperscript
|
|
8173
|
+
* command, v0.9.90). Without this guard the condition/body scans cut the
|
|
8174
|
+
* condition at `is` and displace the adjective into the body's argument zone,
|
|
8175
|
+
* where it anchors a spurious `empty-{lang}-generated` parse AND steals a
|
|
8176
|
+
* neighboring role (the empty ×8 bn/hi/tr family). Source-locale copulas are
|
|
8177
|
+
* added via the dictionary in `isPredicateAdjectivePosition`.
|
|
8178
|
+
*/
|
|
8179
|
+
const EN_COPULAS = ['is', 'are', 'was', 'were', 'am', 'be'];
|
|
8180
|
+
function getCopulasForLocale(locale) {
|
|
8181
|
+
const copulas = new Set(EN_COPULAS);
|
|
8182
|
+
if (locale !== 'en') {
|
|
8183
|
+
for (const form of EN_COPULAS) {
|
|
8184
|
+
copulas.add(translateWord(form, 'en', locale).toLowerCase());
|
|
8185
|
+
}
|
|
8186
|
+
}
|
|
8187
|
+
return copulas;
|
|
8188
|
+
}
|
|
8189
|
+
/**
|
|
8190
|
+
* True when tokens[i] sits right after a copula — a predicate-adjective
|
|
8191
|
+
* position that must never be read as the start of a body command.
|
|
8192
|
+
*/
|
|
8193
|
+
function isPredicateAdjectivePosition(tokens, i, copulas) {
|
|
8194
|
+
const prev = tokens[i - 1]?.toLowerCase();
|
|
8195
|
+
return !!prev && copulas.has(prev);
|
|
8196
|
+
}
|
|
8197
|
+
/**
|
|
8198
|
+
* Source-locale surface forms of the `for` command keyword and the loop's
|
|
8199
|
+
* `in` preposition, for the loop-head test below.
|
|
8200
|
+
*/
|
|
8201
|
+
function getForLoopWordsForLocale(locale) {
|
|
8202
|
+
const forWords = new Set(['for']);
|
|
8203
|
+
const inWords = new Set(['in']);
|
|
8204
|
+
if (locale !== 'en') {
|
|
8205
|
+
forWords.add(translateWord('for', 'en', locale).toLowerCase());
|
|
8206
|
+
inWords.add(translateWord('in', 'en', locale).toLowerCase());
|
|
8207
|
+
}
|
|
8208
|
+
return { forWords, inWords };
|
|
8209
|
+
}
|
|
8210
|
+
/**
|
|
8211
|
+
* True when a `for` at tokens[i] heads a real loop. Hyperscript's only
|
|
8212
|
+
* statement-head `for` is `for <var> in <iterable>`, so a `for` with no `in`
|
|
8213
|
+
* among the tokens before the next command keyword is a ROLE PHRASE — take's
|
|
8214
|
+
* target (`take .active from .tab-button for me`) or a duration (`for 2s`) —
|
|
8215
|
+
* and must never split the statement: the split shattered the phrase into a
|
|
8216
|
+
* dangling `then for me` clause, which six SOV languages then parsed as a
|
|
8217
|
+
* spurious `for` loop with patient "me" (the take-class ×6 family).
|
|
8218
|
+
*/
|
|
8219
|
+
function isLoopHeadFor(tokens, i, inWords, commandKeywords) {
|
|
8220
|
+
for (let j = i + 1; j < tokens.length; j++) {
|
|
8221
|
+
const lt = tokens[j].toLowerCase();
|
|
8222
|
+
if (inWords.has(lt))
|
|
8223
|
+
return true;
|
|
8224
|
+
if (commandKeywords.has(lt))
|
|
8225
|
+
return false;
|
|
8226
|
+
}
|
|
8227
|
+
return false;
|
|
8228
|
+
}
|
|
8229
|
+
/**
|
|
8230
|
+
* Repair a FRONTED Hebrew accusative marker in transformed output.
|
|
8231
|
+
*
|
|
8232
|
+
* When an event-handler body leads with a command-modifier (`on click once add …`,
|
|
8233
|
+
* via {@link GrammarTransformer.tryTransformEventWithModifierBody}) or is a control
|
|
8234
|
+
* block (`on blur if … add … end`, via `tryTransformEventWithBlockBody`), the body
|
|
8235
|
+
* command's accusative marker את can be emitted AHEAD of its verb — `add .error to me`
|
|
8236
|
+
* renders `… את הוסף .error …` instead of the canonical `… הוסף את .error …`. את before
|
|
8237
|
+
* a verb is always ungrammatical Hebrew (it only ever marks a FOLLOWING definite
|
|
8238
|
+
* object), and the semantic parser drops the command in every parse path (fused-event,
|
|
8239
|
+
* multi-clause, conditional-body) when the marker is fronted but parses it when the
|
|
8240
|
+
* marker follows the verb. So an `<accusative-marker> <command-verb>` adjacency is a
|
|
8241
|
+
* pure transformer artifact: swap it back. Idempotent and safe — only touches `את
|
|
8242
|
+
* <verb>`, never the ~40 generated `<verb> את {patient}` patterns that embed את legitimately.
|
|
8243
|
+
*/
|
|
8244
|
+
function repairHebrewFrontedAccusative(text) {
|
|
8245
|
+
const ACC = 'את'; // hebrewProfile.markers patient marker
|
|
8246
|
+
const verbs = getCommandKeywordsForLocale('he');
|
|
8247
|
+
const tokens = text.split(/\s+/);
|
|
8248
|
+
let changed = false;
|
|
8249
|
+
for (let i = 0; i + 1 < tokens.length; i++) {
|
|
8250
|
+
if (tokens[i] === ACC && verbs.has(tokens[i + 1].toLowerCase())) {
|
|
8251
|
+
[tokens[i], tokens[i + 1]] = [tokens[i + 1], tokens[i]];
|
|
8252
|
+
changed = true;
|
|
8253
|
+
i++; // skip the marker we just moved
|
|
8254
|
+
}
|
|
8255
|
+
}
|
|
8256
|
+
return changed ? tokens.join(' ') : text;
|
|
8257
|
+
}
|
|
7588
8258
|
/**
|
|
7589
8259
|
* Detect a reactive block (`live ... end`, `when X changes Y [end]`,
|
|
7590
8260
|
* `unless X Y [end]`) and decompose it. Returns `null` when the input
|
|
@@ -7642,11 +8312,14 @@ function extractBlockStructure(input, sourceLocale) {
|
|
|
7642
8312
|
// `unless <cond> <body>`: condition runs up to the first command
|
|
7643
8313
|
// keyword in `inner`. Heuristic — works because hyperscript bodies
|
|
7644
8314
|
// always start with a command verb, and `unless` conditions rarely
|
|
7645
|
-
// contain bare command keywords as values.
|
|
8315
|
+
// contain bare command keywords as values. A candidate right after a
|
|
8316
|
+
// copula (`… is empty`) is a predicate adjective inside the condition,
|
|
8317
|
+
// not a body verb — skip it.
|
|
7646
8318
|
const commands = getCommandKeywordsForLocale(sourceLocale);
|
|
8319
|
+
const copulas = getCopulasForLocale(sourceLocale);
|
|
7647
8320
|
let bodyStart = -1;
|
|
7648
8321
|
for (let i = 0; i < inner.length; i++) {
|
|
7649
|
-
if (commands.has(inner[i].toLowerCase())) {
|
|
8322
|
+
if (commands.has(inner[i].toLowerCase()) && !isPredicateAdjectivePosition(inner, i, copulas)) {
|
|
7650
8323
|
bodyStart = i;
|
|
7651
8324
|
break;
|
|
7652
8325
|
}
|
|
@@ -7803,7 +8476,13 @@ function reconstructWithLineStructure(transformedParts, lineMetadata, partToLine
|
|
|
7803
8476
|
* - "on <event> <command>" stays together (event handler with first command)
|
|
7804
8477
|
* - Modifiers like "to", "from" don't trigger splits
|
|
7805
8478
|
*/
|
|
7806
|
-
/**
|
|
8479
|
+
/**
|
|
8480
|
+
* English modifier keywords that should not trigger command boundary splits.
|
|
8481
|
+
* These are the base set; localized equivalents (e.g. Japanese `に`, Spanish
|
|
8482
|
+
* `a`, Arabic `إلى`) are layered on per source locale by
|
|
8483
|
+
* `getBoundaryModifiersForLocale`, so a preposition in a non-English source
|
|
8484
|
+
* is also recognized as a modifier rather than a spurious command boundary.
|
|
8485
|
+
*/
|
|
7807
8486
|
const BOUNDARY_MODIFIERS = new Set([
|
|
7808
8487
|
'to',
|
|
7809
8488
|
'into',
|
|
@@ -7817,6 +8496,81 @@ const BOUNDARY_MODIFIERS = new Set([
|
|
|
7817
8496
|
'of',
|
|
7818
8497
|
'over',
|
|
7819
8498
|
]);
|
|
8499
|
+
/**
|
|
8500
|
+
* Boundary modifiers resolved for a given source locale: the English base set
|
|
8501
|
+
* (always kept, since input may mix English keywords) plus every grammatical
|
|
8502
|
+
* marker form declared by the locale's profile. Profile markers are the
|
|
8503
|
+
* surface realizations of semantic roles (destination, source, style, …) —
|
|
8504
|
+
* they always bind to a following value, so none should be treated as a
|
|
8505
|
+
* command boundary. Cached per locale because profiles are static.
|
|
8506
|
+
*/
|
|
8507
|
+
const boundaryModifiersCache = new Map();
|
|
8508
|
+
function getBoundaryModifiersForLocale(locale) {
|
|
8509
|
+
const cached = boundaryModifiersCache.get(locale);
|
|
8510
|
+
if (cached)
|
|
8511
|
+
return cached;
|
|
8512
|
+
const modifiers = new Set(BOUNDARY_MODIFIERS);
|
|
8513
|
+
const profile = getProfile(locale);
|
|
8514
|
+
profile?.markers.forEach(marker => {
|
|
8515
|
+
const form = marker.form.replace(/^-|-$/g, '').toLowerCase();
|
|
8516
|
+
if (form)
|
|
8517
|
+
modifiers.add(form);
|
|
8518
|
+
marker.alternatives?.forEach(alt => {
|
|
8519
|
+
const altForm = alt.replace(/^-|-$/g, '').toLowerCase();
|
|
8520
|
+
if (altForm)
|
|
8521
|
+
modifiers.add(altForm);
|
|
8522
|
+
});
|
|
8523
|
+
});
|
|
8524
|
+
boundaryModifiersCache.set(locale, modifiers);
|
|
8525
|
+
return modifiers;
|
|
8526
|
+
}
|
|
8527
|
+
/**
|
|
8528
|
+
* Commands for which a trailing `on <element>` is the TARGET the command acts
|
|
8529
|
+
* on (a destination), not a new event-handler clause. For these, the locative
|
|
8530
|
+
* `on` must neither split the statement (`splitOnCommandBoundaries`) nor be read
|
|
8531
|
+
* as a fresh `event` role (`buildArgumentModifierMap`) — see those call sites.
|
|
8532
|
+
*
|
|
8533
|
+
* Deliberately narrow. `on` is overloaded (event-handler head vs. locative
|
|
8534
|
+
* target), and the change has cross-language blast radius, so we only enable
|
|
8535
|
+
* the locative reading for the DOM class/attribute mutators where it's the
|
|
8536
|
+
* documented idiom (`toggle .open on #menu`, `toggle @hidden on #panel`).
|
|
8537
|
+
*
|
|
8538
|
+
* `trigger`/`send` — `trigger X on Y` / `send X on Y` fire an event on a target
|
|
8539
|
+
* element; the trailing `on Y` is that target (destination), not a new clause.
|
|
8540
|
+
* Previously excluded: keeping `on Y` attached produced `trigger X → #y` output
|
|
8541
|
+
* that was thought to destabilise the semantic parser's multi-line behaviour
|
|
8542
|
+
* fallback (behavior-sortable's `trigger sortable:start on me`). That concern is
|
|
8543
|
+
* stale — the recent semantic-parser body increments fold the surrounding block
|
|
8544
|
+
* cleanly, and splitting instead injected a spurious `then` (`disparar
|
|
8545
|
+
* sortable:start entonces en yo`) that glued the following `repeat until event`
|
|
8546
|
+
* loop into a then-chain and dropped it. Keeping `on Y` attached restores
|
|
8547
|
+
* behavior-sortable to faithful across the SVO languages.
|
|
8548
|
+
*
|
|
8549
|
+
* Excluded on purpose:
|
|
8550
|
+
* - `set` — `set @attr to V on Y` carries BOTH `to` (value) and `on` (target);
|
|
8551
|
+
* English marks both as `destination`, so remapping `on` would collide with
|
|
8552
|
+
* and clobber the value. Needs distinct value/target roles first (deferred).
|
|
8553
|
+
* - `put` — `put X on Y into Z` has the same dual-destination collision.
|
|
8554
|
+
*
|
|
8555
|
+
* `add`/`remove` use `to`/`from` for their target in practice (not `on`), so
|
|
8556
|
+
* their inclusion is a harmless no-op that documents intent.
|
|
8557
|
+
*/
|
|
8558
|
+
const ON_TARGET_COMMANDS = new Set(['toggle', 'add', 'remove', 'trigger', 'send']);
|
|
8559
|
+
/**
|
|
8560
|
+
* Find the command verb of a partially-collected statement: the first command
|
|
8561
|
+
* keyword that is neither the event-handler head (`on`/その他 event markers) nor
|
|
8562
|
+
* an argument-introducing preposition. For `on click toggle .open` → `toggle`;
|
|
8563
|
+
* for `trigger sortable:start` → `trigger`.
|
|
8564
|
+
*/
|
|
8565
|
+
function commandVerbOf(tokens, commandKeywords) {
|
|
8566
|
+
for (const token of tokens) {
|
|
8567
|
+
const lt = token.toLowerCase();
|
|
8568
|
+
if (commandKeywords.has(lt) && !BOUNDARY_MODIFIERS.has(lt) && !EVENT_KEYWORDS.has(lt)) {
|
|
8569
|
+
return lt;
|
|
8570
|
+
}
|
|
8571
|
+
}
|
|
8572
|
+
return null;
|
|
8573
|
+
}
|
|
7820
8574
|
/**
|
|
7821
8575
|
* Block-introducing keywords whose body should not be split at command
|
|
7822
8576
|
* boundaries by `splitOnCommandBoundaries`. Inputs starting with one of
|
|
@@ -7829,8 +8583,26 @@ const BOUNDARY_MODIFIERS = new Set([
|
|
|
7829
8583
|
* `splitOnThen` + `parseConditional` path.
|
|
7830
8584
|
*/
|
|
7831
8585
|
const BLOCK_HEAD_KEYWORDS = new Set(['live', 'when', 'unless']);
|
|
8586
|
+
/**
|
|
8587
|
+
* Source-language block-introducing command keywords whose body is a clause plus a
|
|
8588
|
+
* branch/loop body (harness source is English, so these are English-keyed). Used to
|
|
8589
|
+
* locate a block body inside an event handler and to track block depth when finding
|
|
8590
|
+
* a top-level `else`.
|
|
8591
|
+
*/
|
|
8592
|
+
const BLOCK_BODY_KEYWORDS = new Set(['if', 'repeat', 'unless', 'while', 'for']);
|
|
8593
|
+
/**
|
|
8594
|
+
* SVO targets that mark the object/patient with a particle (he את, zh 把) and so
|
|
8595
|
+
* mangle an inline `on <event> unless <cond> <body>` guard — the unless tail is
|
|
8596
|
+
* swept into one patient blob and the marker lands ahead of the condition. These
|
|
8597
|
+
* route through `tryTransformEventWithUnlessGuard`. SOV/VSO object-markers
|
|
8598
|
+
* (ja/ko/tr/ar) are excluded: their event does not lead, so the SVO event-first
|
|
8599
|
+
* emission there would be wrong (and they don't exhibit the artifact today).
|
|
8600
|
+
*/
|
|
8601
|
+
const UNLESS_GUARD_OBJECT_MARKING_LOCALES = new Set(['he', 'zh']);
|
|
7832
8602
|
function splitOnCommandBoundaries(input, sourceLocale) {
|
|
7833
8603
|
const commandKeywords = getCommandKeywordsForLocale(sourceLocale);
|
|
8604
|
+
const boundaryModifiers = getBoundaryModifiersForLocale(sourceLocale);
|
|
8605
|
+
const { forWords, inWords } = getForLoopWordsForLocale(sourceLocale);
|
|
7834
8606
|
const tokens = input.split(/\s+/);
|
|
7835
8607
|
if (tokens.length === 0)
|
|
7836
8608
|
return [input];
|
|
@@ -7876,7 +8648,48 @@ function splitOnCommandBoundaries(input, sourceLocale) {
|
|
|
7876
8648
|
currentPart.push(token);
|
|
7877
8649
|
continue;
|
|
7878
8650
|
}
|
|
7879
|
-
|
|
8651
|
+
// A `for` that doesn't head a real loop (`for <var> in <iterable>`) is a
|
|
8652
|
+
// role phrase of the current command — see isLoopHeadFor.
|
|
8653
|
+
if (forWords.has(lowerToken) && !isLoopHeadFor(tokens, i, inWords, commandKeywords)) {
|
|
8654
|
+
currentPart.push(token);
|
|
8655
|
+
continue;
|
|
8656
|
+
}
|
|
8657
|
+
// Locative `on` for a DOM-target command (`toggle X on Y`) is the target
|
|
8658
|
+
// element, NOT a new command. `on` lands in `commandKeywords` only
|
|
8659
|
+
// incidentally — the EN dictionary registers `commands.on = 'on'` for the
|
|
8660
|
+
// event-handler head — so without this guard the destination `on` split
|
|
8661
|
+
// the statement, the join re-inserted a spurious `then` (ثم/pagkatapos/…),
|
|
8662
|
+
// and the dangling `on Y` was misread as a second event handler. Keep it
|
|
8663
|
+
// attached so the role parser can assign it to `destination`. Restricted
|
|
8664
|
+
// to ON_TARGET_COMMANDS so `trigger X on me` etc. keep their prior split.
|
|
8665
|
+
if (BOUNDARY_MODIFIERS.has(lowerToken)) {
|
|
8666
|
+
const verb = commandVerbOf(currentPart, commandKeywords);
|
|
8667
|
+
if (verb && ON_TARGET_COMMANDS.has(verb)) {
|
|
8668
|
+
currentPart.push(token);
|
|
8669
|
+
continue;
|
|
8670
|
+
}
|
|
8671
|
+
// `set @attr to V on <scope>` (S1 tabs-aria): the trailing `on <scope>`
|
|
8672
|
+
// is the element(s) the attribute is set on, not a new command. `set` is
|
|
8673
|
+
// deliberately NOT in ON_TARGET_COMMANDS (its role parser would clobber
|
|
8674
|
+
// the value), so this is a dedicated guard — kept attached only when a
|
|
8675
|
+
// selector/reference scope follows, then positioned by transformSingle's
|
|
8676
|
+
// set-scope handler (transformSetWithScope). A `set` whose `on` is
|
|
8677
|
+
// followed by a verb is left to split as before.
|
|
8678
|
+
if (lowerToken === 'on' && verb === 'set') {
|
|
8679
|
+
const nextTok = tokens[i + 1];
|
|
8680
|
+
const nextLower = nextTok?.toLowerCase();
|
|
8681
|
+
const scopeLike = !!nextTok &&
|
|
8682
|
+
(/^[#.<@[]/.test(nextTok) ||
|
|
8683
|
+
nextLower === 'me' ||
|
|
8684
|
+
nextLower === 'it' ||
|
|
8685
|
+
nextLower === 'you');
|
|
8686
|
+
if (scopeLike) {
|
|
8687
|
+
currentPart.push(token);
|
|
8688
|
+
continue;
|
|
8689
|
+
}
|
|
8690
|
+
}
|
|
8691
|
+
}
|
|
8692
|
+
if (!boundaryModifiers.has(prevLower) && !commandKeywords.has(prevLower)) {
|
|
7880
8693
|
// This looks like a command boundary - save current part and start new one
|
|
7881
8694
|
parts.push(currentPart.join(' '));
|
|
7882
8695
|
currentPart = [token];
|
|
@@ -7963,6 +8776,29 @@ function deriveEventKeywordsFromProfiles() {
|
|
|
7963
8776
|
}
|
|
7964
8777
|
/** Event keywords derived from language profiles */
|
|
7965
8778
|
const EVENT_KEYWORDS = deriveEventKeywordsFromProfiles();
|
|
8779
|
+
/**
|
|
8780
|
+
* Conjunctions that join multiple events in an event handler head
|
|
8781
|
+
* (`on click or keypress ...`). Source hyperscript is English, so the
|
|
8782
|
+
* canonical form is `or`; localized equivalents are added defensively for
|
|
8783
|
+
* non-English sources.
|
|
8784
|
+
*/
|
|
8785
|
+
const EVENT_CONJUNCTIONS = new Set(['or']);
|
|
8786
|
+
/**
|
|
8787
|
+
* Command-modifier keywords that may lead an event handler body
|
|
8788
|
+
* (`on click async fetch …`, `on click once add …`). They modify the handler /
|
|
8789
|
+
* following command rather than acting as the verb, so the transformer must lift
|
|
8790
|
+
* them out before role assignment instead of treating them as the action.
|
|
8791
|
+
* Source hyperscript is English, so these are the canonical English forms — the
|
|
8792
|
+
* semantic parser recognizes the same literals when stripping them pre-parse.
|
|
8793
|
+
*/
|
|
8794
|
+
const BODY_MODIFIER_KEYWORDS = new Set([
|
|
8795
|
+
'async',
|
|
8796
|
+
'once',
|
|
8797
|
+
'debounced',
|
|
8798
|
+
'debounce',
|
|
8799
|
+
'throttled',
|
|
8800
|
+
'throttle',
|
|
8801
|
+
]);
|
|
7966
8802
|
// =============================================================================
|
|
7967
8803
|
// Helper: Dynamic Modifier Map
|
|
7968
8804
|
// =============================================================================
|
|
@@ -7996,6 +8832,48 @@ function generateModifierMap(profile) {
|
|
|
7996
8832
|
}
|
|
7997
8833
|
return map;
|
|
7998
8834
|
}
|
|
8835
|
+
/**
|
|
8836
|
+
* Modifier map for parsing the ARGUMENTS of a statement (everything after the
|
|
8837
|
+
* command verb), as opposed to the statement head.
|
|
8838
|
+
*
|
|
8839
|
+
* Why a separate map: a few languages reuse one word for both the event-handler
|
|
8840
|
+
* head marker and a locative/target preposition. English is the prime case —
|
|
8841
|
+
* `on` is the event head (`on click …`) AND the toggle/set target preposition
|
|
8842
|
+
* (`toggle .x on #y`). `generateModifierMap` maps `on → event` (from the EN
|
|
8843
|
+
* profile's event marker), so when the destination `on` was read in argument
|
|
8844
|
+
* position it overwrote the already-captured head event (the `click` got
|
|
8845
|
+
* dropped, e.g. `toggle @hidden on #panel` → `… عند #panel` with نقر/click gone).
|
|
8846
|
+
*
|
|
8847
|
+
* The fix: in argument position, remap the event role to `destination`. This is
|
|
8848
|
+
* safe because a trigger event only ever appears at the statement head (which is
|
|
8849
|
+
* consumed before argument parsing begins). Any "event marker" reached while
|
|
8850
|
+
* scanning arguments is therefore being used as a locative — i.e. the element
|
|
8851
|
+
* the command acts ON — which is exactly the `destination` role (the semantic
|
|
8852
|
+
* parser also models `toggle .x on #y` as patient `.x` + destination `#y`).
|
|
8853
|
+
*
|
|
8854
|
+
* Restricted to (a) SVO source profiles and (b) ON_TARGET_COMMANDS:
|
|
8855
|
+
* - SVO: in VSO/SOV languages the event marker can legitimately appear
|
|
8856
|
+
* mid-statement (Arabic VSO renders an event handler as `بدل X عند نقر`,
|
|
8857
|
+
* classified as a command), so remapping there would mis-read a real event
|
|
8858
|
+
* as a destination. English — and the en→lang gate path — is SVO.
|
|
8859
|
+
* - command: only the DOM class/attr mutators use a locative `on` target.
|
|
8860
|
+
* For other commands (`trigger X on me`, `set X to V on Y`) the remap is
|
|
8861
|
+
* either destabilising or collides with `to`/`into` — see ON_TARGET_COMMANDS.
|
|
8862
|
+
*
|
|
8863
|
+
* When neither applies the unmodified map is returned (event stays event).
|
|
8864
|
+
*/
|
|
8865
|
+
function buildArgumentModifierMap(profile, actionVerb) {
|
|
8866
|
+
const map = generateModifierMap(profile);
|
|
8867
|
+
const verb = actionVerb?.toLowerCase();
|
|
8868
|
+
if (profile.wordOrder !== 'SVO' || !verb || !ON_TARGET_COMMANDS.has(verb)) {
|
|
8869
|
+
return map;
|
|
8870
|
+
}
|
|
8871
|
+
const remapped = {};
|
|
8872
|
+
for (const [form, role] of Object.entries(map)) {
|
|
8873
|
+
remapped[form] = role === 'event' ? 'destination' : role;
|
|
8874
|
+
}
|
|
8875
|
+
return remapped;
|
|
8876
|
+
}
|
|
7999
8877
|
// =============================================================================
|
|
8000
8878
|
// Statement Parser
|
|
8001
8879
|
// =============================================================================
|
|
@@ -8102,6 +8980,8 @@ function tokenize(input, profile) {
|
|
|
8102
8980
|
let current = '';
|
|
8103
8981
|
let inSelector = false;
|
|
8104
8982
|
let selectorDepth = 0;
|
|
8983
|
+
let bracketDepth = 0;
|
|
8984
|
+
let parenDepth = 0;
|
|
8105
8985
|
for (let i = 0; i < input.length; i++) {
|
|
8106
8986
|
const char = input[i];
|
|
8107
8987
|
// Track CSS selector context
|
|
@@ -8114,8 +8994,41 @@ function tokenize(input, profile) {
|
|
|
8114
8994
|
if (selectorDepth === 0)
|
|
8115
8995
|
inSelector = false;
|
|
8116
8996
|
}
|
|
8117
|
-
//
|
|
8118
|
-
|
|
8997
|
+
// Track event-guard / attribute brackets so `[key is 'Escape']` (which has
|
|
8998
|
+
// internal spaces) stays a single token instead of splitting into
|
|
8999
|
+
// `[key` / `is` / `'Escape']` — which mis-assigns `is` as the action verb.
|
|
9000
|
+
if (char === '[') {
|
|
9001
|
+
bracketDepth++;
|
|
9002
|
+
}
|
|
9003
|
+
else if (char === ']' && bracketDepth > 0) {
|
|
9004
|
+
bracketDepth--;
|
|
9005
|
+
}
|
|
9006
|
+
// Track EVERY parenthesized group as a depth scope so it stays ONE token:
|
|
9007
|
+
//
|
|
9008
|
+
// - An ATTACHED `(` (a call or event destructure: `pointerdown(clientX,
|
|
9009
|
+
// clientY)`, `Resizable(a, b)`) must not split at the comma-space — the
|
|
9010
|
+
// event-handler-head reorder would separate the halves and drop the
|
|
9011
|
+
// whole handler (tl behavior-resizable degenerate).
|
|
9012
|
+
// - A STANDALONE `(` opening an expression (`to (the value of #price as
|
|
9013
|
+
// Number) * (my value as Number)`) must be OPAQUE to role segmentation:
|
|
9014
|
+
// left loose, its interior `of`/`as`/`from` keywords hit the argument
|
|
9015
|
+
// modifier map, so the parser split the expression across roles — the
|
|
9016
|
+
// R1 cluster E mangle (computed-value: the transformer reordered INSIDE
|
|
9017
|
+
// the parens, embedded the event phrase mid-expression, and dropped the
|
|
9018
|
+
// whole second operand in every language). Interior keywords still
|
|
9019
|
+
// translate IN PLACE: role values are re-split on whitespace by
|
|
9020
|
+
// translateMultiWordValue, and translateWord strips paren punctuation
|
|
9021
|
+
// before its dictionary lookup (`($count or 0)` → `($count o 0)` in tl —
|
|
9022
|
+
// the operator translates without the group being torn apart).
|
|
9023
|
+
if (char === '(') {
|
|
9024
|
+
parenDepth++;
|
|
9025
|
+
}
|
|
9026
|
+
else if (char === ')' && parenDepth > 0) {
|
|
9027
|
+
parenDepth--;
|
|
9028
|
+
}
|
|
9029
|
+
// Split on whitespace unless inside a selector, a bracket guard, or an
|
|
9030
|
+
// attached parenthesized argument list
|
|
9031
|
+
if (/\s/.test(char) && !inSelector && bracketDepth === 0 && parenDepth === 0) {
|
|
8119
9032
|
if (current) {
|
|
8120
9033
|
tokens.push(current);
|
|
8121
9034
|
current = '';
|
|
@@ -8165,11 +9078,26 @@ function parseEventHandler(tokens, profile) {
|
|
|
8165
9078
|
let startIndex = EVENT_KEYWORDS.has(tokens[0]?.toLowerCase()) ? 1 : 0;
|
|
8166
9079
|
// Next token is the event
|
|
8167
9080
|
if (tokens[startIndex]) {
|
|
9081
|
+
const eventTokens = [tokens[startIndex]];
|
|
9082
|
+
startIndex++;
|
|
9083
|
+
// Fold "or"-conjoined events into the event value (e.g.
|
|
9084
|
+
// "on click or keypress[...] toggle .active"). Without this, "or" would be
|
|
9085
|
+
// read as the action verb and the second event swept into role values,
|
|
9086
|
+
// hoisting "or <event2>" ahead of the command on reorder. Keeping the whole
|
|
9087
|
+
// "<event1> or <event2>" string in the event role lets it translate and
|
|
9088
|
+
// reorder as a single event clause. Only consumes "or" that immediately
|
|
9089
|
+
// follows the event, before any source/action — so a later "or" in a guard
|
|
9090
|
+
// or value is untouched.
|
|
9091
|
+
while (tokens[startIndex] &&
|
|
9092
|
+
EVENT_CONJUNCTIONS.has(tokens[startIndex].toLowerCase()) &&
|
|
9093
|
+
tokens[startIndex + 1]) {
|
|
9094
|
+
eventTokens.push(tokens[startIndex], tokens[startIndex + 1]);
|
|
9095
|
+
startIndex += 2;
|
|
9096
|
+
}
|
|
8168
9097
|
roles.set('event', {
|
|
8169
9098
|
role: 'event',
|
|
8170
|
-
value:
|
|
9099
|
+
value: eventTokens.join(' '),
|
|
8171
9100
|
});
|
|
8172
|
-
startIndex++;
|
|
8173
9101
|
}
|
|
8174
9102
|
// Check for event source modifier before the action (e.g., "from #source" in "on input from #firstName set ...")
|
|
8175
9103
|
// Only handle 'from' (event source) — other modifiers like circumfix markers (Chinese 时) should not be consumed.
|
|
@@ -8201,9 +9129,12 @@ function parseEventHandler(tokens, profile) {
|
|
|
8201
9129
|
startIndex++;
|
|
8202
9130
|
}
|
|
8203
9131
|
// Parse remaining tokens with modifier awareness (like parseCommand does)
|
|
8204
|
-
// This handles "by 3" in "on click increment #count by 3"
|
|
9132
|
+
// This handles "by 3" in "on click increment #count by 3".
|
|
9133
|
+
// Argument map: for DOM-target commands the head event is already captured, so
|
|
9134
|
+
// a locative `on` (`toggle @hidden on #panel`) maps to `destination` rather
|
|
9135
|
+
// than overwriting the head `event`.
|
|
8205
9136
|
if (tokens[startIndex]) {
|
|
8206
|
-
const modifierMap =
|
|
9137
|
+
const modifierMap = buildArgumentModifierMap(profile, roles.get('action')?.value);
|
|
8207
9138
|
let currentRole = 'patient';
|
|
8208
9139
|
let currentValue = [];
|
|
8209
9140
|
for (let i = startIndex; i < tokens.length; i++) {
|
|
@@ -8256,9 +9187,11 @@ function parseCommand(tokens, profile) {
|
|
|
8256
9187
|
role: 'action',
|
|
8257
9188
|
value: tokens[0],
|
|
8258
9189
|
});
|
|
8259
|
-
// Generate dynamic modifier map from language profile
|
|
8260
|
-
// This enables parsing non-English input (e.g., Japanese に, Korean 에, Arabic إلى)
|
|
8261
|
-
|
|
9190
|
+
// Generate dynamic modifier map from language profile (argument position).
|
|
9191
|
+
// This enables parsing non-English input (e.g., Japanese に, Korean 에, Arabic إلى).
|
|
9192
|
+
// For a DOM-target command a locative `on` (`toggle .active on me`) maps to
|
|
9193
|
+
// `destination` (SVO sources) rather than spawning a bogus `event` role.
|
|
9194
|
+
const modifierMap = buildArgumentModifierMap(profile, tokens[0]);
|
|
8262
9195
|
let currentRole = 'patient';
|
|
8263
9196
|
let currentValue = [];
|
|
8264
9197
|
for (let i = 1; i < tokens.length; i++) {
|
|
@@ -8296,6 +9229,72 @@ function parseCommand(tokens, profile) {
|
|
|
8296
9229
|
original: tokens.join(' '),
|
|
8297
9230
|
};
|
|
8298
9231
|
}
|
|
9232
|
+
/**
|
|
9233
|
+
* Re-assign a command's mis-marked primary argument from the default `patient`
|
|
9234
|
+
* role to its true primary role (e.g. `wait`'s leading argument is a `duration`,
|
|
9235
|
+
* not a fronted object).
|
|
9236
|
+
*
|
|
9237
|
+
* The generic argument parser in `parseCommand` / `parseEventHandler` defaults the
|
|
9238
|
+
* first unmarked argument to `patient`. For most commands that is correct, but for a
|
|
9239
|
+
* command whose primary role is a non-patient *and which the target language does not
|
|
9240
|
+
* give a marker* (a duration, a measure — never a BA/object construction), the
|
|
9241
|
+
* `patient` assignment makes `insertMarkers` emit a spurious object-marker (Chinese
|
|
9242
|
+
* `把`, Japanese `を`, Korean `를`). The marked form is ungrammatical and the semantic
|
|
9243
|
+
* parser fails to match it, dropping the command.
|
|
9244
|
+
*
|
|
9245
|
+
* The fix is deliberately scoped to **literal/measure** primary roles
|
|
9246
|
+
* ({@link LITERAL_PRIMARY_ROLES}: `duration`, `quantity`) — values that are *never*
|
|
9247
|
+
* the object of an object-marking construction in any supported language, so moving
|
|
9248
|
+
* them off `patient` can only ever *remove* a spurious marker. Marker-bearing
|
|
9249
|
+
* primaries are intentionally left alone:
|
|
9250
|
+
* - `set`→destination `到`, `fetch`→source `从` carry a *correct* marker; touching
|
|
9251
|
+
* them risks no benefit.
|
|
9252
|
+
* - `send`/`trigger`→event: in a language without an event marker (e.g. Korean has
|
|
9253
|
+
* no event particle) un-marking the leading argument makes the semantic parser
|
|
9254
|
+
* mis-read it as a bare event handler and emit a phantom `on` action — an
|
|
9255
|
+
* over-generation that recall-based fidelity would not catch. So `event`-primary
|
|
9256
|
+
* commands stay on the `patient` default.
|
|
9257
|
+
*
|
|
9258
|
+
* Roles are still reordered by the safety-net in `reorderRoles`, so no value is lost.
|
|
9259
|
+
* A belt-and-suspenders target-marker guard keeps the change inert should a profile
|
|
9260
|
+
* ever add a marker for one of these literal roles.
|
|
9261
|
+
*
|
|
9262
|
+
* Must run *before* `translateElements`, while the `action` value is still the
|
|
9263
|
+
* source-language (English) keyword, so the schema lookup resolves.
|
|
9264
|
+
*
|
|
9265
|
+
* @see docs-internal/ZH_BLOCK_BODY_SCOPE.md (#1 — transformer role model)
|
|
9266
|
+
*/
|
|
9267
|
+
const LITERAL_PRIMARY_ROLES = new Set([
|
|
9268
|
+
'duration',
|
|
9269
|
+
'quantity',
|
|
9270
|
+
]);
|
|
9271
|
+
function applyPrimaryRole(parsed, targetProfile) {
|
|
9272
|
+
// Only re-mark standalone command statements. In an event handler (`on click
|
|
9273
|
+
// wait 2s …`) a verb-final SOV language without an event particle (e.g. Korean)
|
|
9274
|
+
// relies on the leading argument's patient marker as the structural cue that
|
|
9275
|
+
// anchors the handler; un-marking it makes the semantic parser lose the event.
|
|
9276
|
+
// The block-body / then-chain `wait` clauses this fix targets are each parsed as
|
|
9277
|
+
// their own command statement, so they are still covered.
|
|
9278
|
+
if (parsed.type !== 'command')
|
|
9279
|
+
return;
|
|
9280
|
+
const action = parsed.roles.get('action')?.value;
|
|
9281
|
+
if (!action)
|
|
9282
|
+
return;
|
|
9283
|
+
const primaryRole = COMMAND_PRIMARY_ROLES[action.toLowerCase()];
|
|
9284
|
+
if (!primaryRole || !LITERAL_PRIMARY_ROLES.has(primaryRole))
|
|
9285
|
+
return;
|
|
9286
|
+
// Only act on a leading argument the generic parser defaulted to `patient`,
|
|
9287
|
+
// and only when the primary slot isn't already filled by an explicit marker.
|
|
9288
|
+
const patientEl = parsed.roles.get('patient');
|
|
9289
|
+
if (!patientEl || parsed.roles.has(primaryRole))
|
|
9290
|
+
return;
|
|
9291
|
+
// Guard: never introduce a marker that wasn't there. If the target language
|
|
9292
|
+
// marks the primary role, leave the command as-is.
|
|
9293
|
+
if (targetProfile.markers.some(m => m.role === primaryRole))
|
|
9294
|
+
return;
|
|
9295
|
+
parsed.roles.delete('patient');
|
|
9296
|
+
parsed.roles.set(primaryRole, { ...patientEl, role: primaryRole });
|
|
9297
|
+
}
|
|
8299
9298
|
/**
|
|
8300
9299
|
* Parse a conditional statement
|
|
8301
9300
|
*/
|
|
@@ -8315,6 +9314,18 @@ function parseConditional(tokens, _profile) {
|
|
|
8315
9314
|
value: conditionValue,
|
|
8316
9315
|
});
|
|
8317
9316
|
}
|
|
9317
|
+
else if (thenIndex === -1 && tokens.length > 1) {
|
|
9318
|
+
// Block-style `if <cond>` with the body on following lines (no inline
|
|
9319
|
+
// `then`). Capture everything after `if` as the condition; otherwise the
|
|
9320
|
+
// condition is silently dropped and the rendered block becomes a bare
|
|
9321
|
+
// `if`/`אם`/`如果`, which the semantic block parser then rejects (null
|
|
9322
|
+
// parse). This is the dominant failure for nested control-flow bodies in
|
|
9323
|
+
// non-Latin languages (he, zh).
|
|
9324
|
+
roles.set('condition', {
|
|
9325
|
+
role: 'condition',
|
|
9326
|
+
value: tokens.slice(1).join(' '),
|
|
9327
|
+
});
|
|
9328
|
+
}
|
|
8318
9329
|
return {
|
|
8319
9330
|
type: 'conditional',
|
|
8320
9331
|
roles,
|
|
@@ -8336,6 +9347,24 @@ function translateWord(word, sourceLocale, targetLocale) {
|
|
|
8336
9347
|
if (/^\d+/.test(word)) {
|
|
8337
9348
|
return word;
|
|
8338
9349
|
}
|
|
9350
|
+
// A whole parenthesized group fused by the tokenizer (`(the value of #price
|
|
9351
|
+
// as Number)`) reaching a single-token translate path: translate its
|
|
9352
|
+
// interior word-by-word IN ORDER — never reordered, never re-segmented.
|
|
9353
|
+
if (/\s/.test(word) && word.startsWith('(')) {
|
|
9354
|
+
return word
|
|
9355
|
+
.split(/\s+/)
|
|
9356
|
+
.map(w => translateWord(w, sourceLocale, targetLocale))
|
|
9357
|
+
.join(' ');
|
|
9358
|
+
}
|
|
9359
|
+
// A word carrying paren punctuation from a fused group after whitespace
|
|
9360
|
+
// re-splitting (`(my` / `valor)` / `(($x`): strip the parens for the
|
|
9361
|
+
// dictionary lookup and re-attach, so interior keywords still translate.
|
|
9362
|
+
if (word.length > 1 && (word.startsWith('(') || word.endsWith(')'))) {
|
|
9363
|
+
const m = word.match(/^(\(*)([^()]+)(\)*)$/);
|
|
9364
|
+
if (m && (m[1] || m[3])) {
|
|
9365
|
+
return m[1] + translateWord(m[2], sourceLocale, targetLocale) + m[3];
|
|
9366
|
+
}
|
|
9367
|
+
}
|
|
8339
9368
|
const sourceDict = sourceLocale === 'en' ? null : dictionaries[sourceLocale];
|
|
8340
9369
|
const targetDict = dictionaries[targetLocale];
|
|
8341
9370
|
if (!targetDict)
|
|
@@ -8366,9 +9395,21 @@ const POSSESSIVE_MARKERS = {
|
|
|
8366
9395
|
ko: { type: 'suffix', marker: '의' },
|
|
8367
9396
|
zh: { type: 'suffix', marker: '的' },
|
|
8368
9397
|
ar: { type: 'preposition', marker: 'لـ' },
|
|
8369
|
-
|
|
9398
|
+
// Spaced genitive particle (not the glued `'ın`), so the tokenizer can split
|
|
9399
|
+
// it off the selector — consistent with Turkish's other spaced case markers.
|
|
9400
|
+
tr: { type: 'particle', marker: 'ın' },
|
|
8370
9401
|
id: { type: 'preposition', marker: 'dari' },
|
|
8371
|
-
|
|
9402
|
+
// Latin-script genitive: must be a *spaced* particle (`#picker pa`), since a
|
|
9403
|
+
// glued `#pickerpa` can't be split from the selector by the tokenizer the
|
|
9404
|
+
// way a non-Latin suffix (の/의/র) can.
|
|
9405
|
+
qu: { type: 'particle', marker: 'pa' },
|
|
9406
|
+
// Bengali SOV postposition genitive, like ja/ko — a spaced suffix the
|
|
9407
|
+
// tokenizer splits off as a particle. Previously absent, so it fell back to
|
|
9408
|
+
// the English `'s` marker and its possessive property paths never parsed.
|
|
9409
|
+
// (Hindi `का` is intentionally omitted: its `bind` lacks a verb-final
|
|
9410
|
+
// grammar rule, so fixing its possessive alone yields a wrong `on` parse —
|
|
9411
|
+
// tracked as separate follow-up.)
|
|
9412
|
+
bn: { type: 'suffix', marker: 'র' },
|
|
8372
9413
|
sw: { type: 'preposition', marker: 'ya' },
|
|
8373
9414
|
};
|
|
8374
9415
|
/**
|
|
@@ -8405,6 +9446,10 @@ function translatePossessive(token, sourceLocale, targetLocale) {
|
|
|
8405
9446
|
case 'suffix':
|
|
8406
9447
|
// Japanese/Korean/Chinese: owner + marker (e.g., #buttonの, #button의)
|
|
8407
9448
|
return `${translatedOwner}${targetMarker.marker}`;
|
|
9449
|
+
case 'particle':
|
|
9450
|
+
// Latin-script spaced genitive (Quechua `pa`): owner + space + marker
|
|
9451
|
+
// so the tokenizer can separate it from the selector.
|
|
9452
|
+
return `${translatedOwner} ${targetMarker.marker}`;
|
|
8408
9453
|
case 'preposition':
|
|
8409
9454
|
// Will be handled by caller - return marker + owner format
|
|
8410
9455
|
// Store as special format to be processed later
|
|
@@ -8473,6 +9518,20 @@ function translatePossessiveDotNotation(value, sourceLocale, targetLocale) {
|
|
|
8473
9518
|
* Also handles possessive dot notation like "my.textContent" → "mi.textContent".
|
|
8474
9519
|
*/
|
|
8475
9520
|
function translateMultiWordValue(value, sourceLocale, targetLocale) {
|
|
9521
|
+
// Mask event-guard / attribute brackets (`[key is 'Escape']`): their contents
|
|
9522
|
+
// are expression syntax, not translatable keywords — translating `is` -> `ni`
|
|
9523
|
+
// etc. inside them breaks the guard. Restore verbatim after translation.
|
|
9524
|
+
if (value.includes('[')) {
|
|
9525
|
+
const guards = [];
|
|
9526
|
+
const masked = value.replace(/\[[^\]]*\]/g, match => {
|
|
9527
|
+
guards.push(match);
|
|
9528
|
+
return `${guards.length - 1}`;
|
|
9529
|
+
});
|
|
9530
|
+
if (guards.length > 0) {
|
|
9531
|
+
const translated = translateMultiWordValue(masked, sourceLocale, targetLocale);
|
|
9532
|
+
return translated.replace(/(\d+)/g, (_, n) => guards[Number(n)]);
|
|
9533
|
+
}
|
|
9534
|
+
}
|
|
8476
9535
|
// If it's a single word, check for possessive then translate
|
|
8477
9536
|
if (!value.includes(' ')) {
|
|
8478
9537
|
// Check for possessive 's
|
|
@@ -8563,6 +9622,35 @@ function translateElements(parsed, sourceLocale, targetLocale) {
|
|
|
8563
9622
|
}
|
|
8564
9623
|
}
|
|
8565
9624
|
// =============================================================================
|
|
9625
|
+
// Caret-scope masking (`^name on <selector>`)
|
|
9626
|
+
// =============================================================================
|
|
9627
|
+
/** Private-use sentinels bracketing a masked caret-scope index. */
|
|
9628
|
+
const CARET_SCOPE_OPEN = '\uE000';
|
|
9629
|
+
const CARET_SCOPE_CLOSE = '\uE001';
|
|
9630
|
+
/**
|
|
9631
|
+
* Match `^name on <selector>` and the scope's selector form (#id, .class,
|
|
9632
|
+
* <tag/>, [attr]). The `^name` is kept; only the ` on <selector>` scope is masked.
|
|
9633
|
+
*/
|
|
9634
|
+
const CARET_SCOPE_RE = /(\^[A-Za-z_][\w-]*)(\s+on\s+(?:[#.][\w-]+|<[^>]*\/>|\[[^\]]+\]))/g;
|
|
9635
|
+
/**
|
|
9636
|
+
* Mask the ` on <selector>` scope of every `^name on <selector>` read behind an
|
|
9637
|
+
* opaque token attached to `^name`, so the overloaded `on` doesn't reach the
|
|
9638
|
+
* splitter / event-handler parser. Returns null when there's nothing to mask.
|
|
9639
|
+
*/
|
|
9640
|
+
function maskCaretScopes(input) {
|
|
9641
|
+
const scopes = [];
|
|
9642
|
+
const masked = input.replace(CARET_SCOPE_RE, (_m, varTok, scope) => {
|
|
9643
|
+
const idx = scopes.length;
|
|
9644
|
+
scopes.push(scope);
|
|
9645
|
+
return `${varTok}${CARET_SCOPE_OPEN}${idx}${CARET_SCOPE_CLOSE}`;
|
|
9646
|
+
});
|
|
9647
|
+
return scopes.length > 0 ? { masked, scopes } : null;
|
|
9648
|
+
}
|
|
9649
|
+
/** Restore masked caret-scope tokens to their verbatim ` on <selector>` form. */
|
|
9650
|
+
function restoreCaretScopes(input, scopes) {
|
|
9651
|
+
return input.replace(new RegExp(`${CARET_SCOPE_OPEN}(\\d+)${CARET_SCOPE_CLOSE}`, 'g'), (_m, n) => scopes[Number(n)] ?? '');
|
|
9652
|
+
}
|
|
9653
|
+
// =============================================================================
|
|
8566
9654
|
// Main Transformer
|
|
8567
9655
|
// =============================================================================
|
|
8568
9656
|
class GrammarTransformer {
|
|
@@ -8584,7 +9672,54 @@ class GrammarTransformer {
|
|
|
8584
9672
|
* For multi-line input, preserves line structure (indentation, blank lines).
|
|
8585
9673
|
*/
|
|
8586
9674
|
transform(input) {
|
|
9675
|
+
const out = this.transformInternal(input);
|
|
9676
|
+
// Hebrew: repair a fronted accusative marker the body-split heuristics can emit
|
|
9677
|
+
// (`… את הוסף .x …` → `… הוסף את .x …`). Applied to the assembled output; idempotent
|
|
9678
|
+
// across the internal recursion. See repairHebrewFrontedAccusative.
|
|
9679
|
+
return this.targetProfile.code === 'he' ? repairHebrewFrontedAccusative(out) : out;
|
|
9680
|
+
}
|
|
9681
|
+
transformInternal(input) {
|
|
9682
|
+
// Caret-scoped variable reads (`^name on <selector>`) carry a second,
|
|
9683
|
+
// overloaded `on` that the splitter/event-parser would mistake for an event
|
|
9684
|
+
// or command boundary — mangling `put ^count on #host into me`. Mask the
|
|
9685
|
+
// ` on <selector>` scope behind an opaque token attached to `^name` so the
|
|
9686
|
+
// command reorders as if the patient were a single value, then restore it.
|
|
9687
|
+
// `on` is kept verbatim (the semantic caret-scope matcher accepts it by raw
|
|
9688
|
+
// value across languages — passthrough-alignment).
|
|
9689
|
+
const caret = maskCaretScopes(input);
|
|
9690
|
+
if (caret) {
|
|
9691
|
+
return restoreCaretScopes(this.transform(caret.masked), caret.scopes);
|
|
9692
|
+
}
|
|
8587
9693
|
const targetThen = getTargetThenKeyword(this.targetProfile.code);
|
|
9694
|
+
// Inline JS blocks (`... js <raw js> end`) must be masked BEFORE any
|
|
9695
|
+
// splitting/reordering: the body is raw JavaScript, not hyperscript, so it
|
|
9696
|
+
// must never be tokenized, translated, or word-order reordered. (Single-line
|
|
9697
|
+
// only here; multi-line js bodies are handled with the behavior work.)
|
|
9698
|
+
if (!input.includes('\n')) {
|
|
9699
|
+
const jsBlock = this.tryTransformJsBlock(input);
|
|
9700
|
+
if (jsBlock !== null)
|
|
9701
|
+
return jsBlock;
|
|
9702
|
+
// Event handler whose body leads with a command-modifier
|
|
9703
|
+
// (`on click async fetch …`, `on click once add …`): lift the modifier out
|
|
9704
|
+
// so the real verb isn't mistaken for the action and the SOV reorder keeps
|
|
9705
|
+
// the body patient-first (recoverable by the parser's SOV event extraction).
|
|
9706
|
+
const eventModifier = this.tryTransformEventWithModifierBody(input);
|
|
9707
|
+
if (eventModifier !== null)
|
|
9708
|
+
return eventModifier;
|
|
9709
|
+
// Event handler whose body is a block (`on <event> if/repeat/unless … end`):
|
|
9710
|
+
// the block body must be reordered as a self-contained unit, not shredded
|
|
9711
|
+
// across the event handler's role soup.
|
|
9712
|
+
const eventBlock = this.tryTransformEventWithBlockBody(input);
|
|
9713
|
+
if (eventBlock !== null)
|
|
9714
|
+
return eventBlock;
|
|
9715
|
+
// Event handler whose body is an un-terminated inline `unless` guard
|
|
9716
|
+
// (`on click unless I match .disabled toggle .selected`): route the guard
|
|
9717
|
+
// through the standalone block path so Hebrew's accusative marker lands on
|
|
9718
|
+
// the body command, not the condition. Hebrew-only; null elsewhere.
|
|
9719
|
+
const eventGuard = this.tryTransformEventWithUnlessGuard(input);
|
|
9720
|
+
if (eventGuard !== null)
|
|
9721
|
+
return eventGuard;
|
|
9722
|
+
}
|
|
8588
9723
|
// Check if input has multi-line structure worth preserving
|
|
8589
9724
|
const hasMultiLineStructure = input.includes('\n');
|
|
8590
9725
|
if (hasMultiLineStructure) {
|
|
@@ -8614,11 +9749,22 @@ class GrammarTransformer {
|
|
|
8614
9749
|
if (block) {
|
|
8615
9750
|
return this.transformBlock(block);
|
|
8616
9751
|
}
|
|
9752
|
+
// 0b. `set @attr to V on <scope>` (S1 tabs-aria): strip the trailing
|
|
9753
|
+
// `on <scope>`, transform the scope-less set normally, then re-insert
|
|
9754
|
+
// `on <scope>` where the semantic set patterns expect it.
|
|
9755
|
+
const setScope = this.transformSetWithScope(input);
|
|
9756
|
+
if (setScope !== null) {
|
|
9757
|
+
return setScope;
|
|
9758
|
+
}
|
|
8617
9759
|
// 1. Parse into semantic roles
|
|
8618
9760
|
const parsed = parseStatement(input, this.sourceProfile.code);
|
|
8619
9761
|
if (!parsed) {
|
|
8620
9762
|
return input; // Return unchanged if parsing fails
|
|
8621
9763
|
}
|
|
9764
|
+
// 1b. Re-assign a mis-marked primary argument (e.g. `wait`'s duration) off the
|
|
9765
|
+
// default `patient` role so the target doesn't emit a spurious object-marker.
|
|
9766
|
+
// Runs before translation while `action` is still the English keyword.
|
|
9767
|
+
applyPrimaryRole(parsed, this.targetProfile);
|
|
8622
9768
|
// 2. Translate individual words
|
|
8623
9769
|
translateElements(parsed, this.sourceProfile.code, this.targetProfile.code);
|
|
8624
9770
|
// 3. Find applicable rule
|
|
@@ -8640,6 +9786,411 @@ class GrammarTransformer {
|
|
|
8640
9786
|
// 7. Join without markers (still use joinTokens for consistency)
|
|
8641
9787
|
return joinTokens(reordered.map(e => e.translated || e.value));
|
|
8642
9788
|
}
|
|
9789
|
+
/**
|
|
9790
|
+
* Detect and transform an inline JS block (`[on <event>] js <raw js> end`).
|
|
9791
|
+
*
|
|
9792
|
+
* The `js ... end` body is raw JavaScript: it must not be tokenized,
|
|
9793
|
+
* translated, or word-order reordered. We mask the whole block with a single
|
|
9794
|
+
* opaque placeholder, run the surrounding statement (the event-handler head,
|
|
9795
|
+
* if any) through the normal reorder pipeline so the placeholder lands in the
|
|
9796
|
+
* correct action position, then substitute the translated `js`/`end` keywords
|
|
9797
|
+
* around the verbatim body.
|
|
9798
|
+
*
|
|
9799
|
+
* Returns `null` (fall through to the normal path) when there is no js block,
|
|
9800
|
+
* no matching `end`, or trailing content after `end` (kept tight on purpose).
|
|
9801
|
+
*/
|
|
9802
|
+
tryTransformJsBlock(input) {
|
|
9803
|
+
const src = this.sourceProfile.code;
|
|
9804
|
+
const dst = this.targetProfile.code;
|
|
9805
|
+
// The `js` / `end` keyword forms in the SOURCE language.
|
|
9806
|
+
const sourceJs = translateWord('js', 'en', src);
|
|
9807
|
+
const sourceEnd = translateWord('end', 'en', src).toLowerCase();
|
|
9808
|
+
const tokens = input.split(/\s+/).filter(t => t.length > 0);
|
|
9809
|
+
// The js command token, optionally with a `(locals)` suffix: `js`, `js(me)`.
|
|
9810
|
+
const escapedJs = sourceJs.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
9811
|
+
const jsRe = new RegExp(`^${escapedJs}(\\(.*\\))?$`, 'i');
|
|
9812
|
+
const jsIdx = tokens.findIndex(t => jsRe.test(t));
|
|
9813
|
+
if (jsIdx === -1)
|
|
9814
|
+
return null;
|
|
9815
|
+
// First `end` after the js keyword closes the block (raw JS never contains a
|
|
9816
|
+
// bare hyperscript `end` token).
|
|
9817
|
+
let endIdx = -1;
|
|
9818
|
+
for (let i = jsIdx + 1; i < tokens.length; i++) {
|
|
9819
|
+
if (tokens[i].toLowerCase() === sourceEnd) {
|
|
9820
|
+
endIdx = i;
|
|
9821
|
+
break;
|
|
9822
|
+
}
|
|
9823
|
+
}
|
|
9824
|
+
if (endIdx === -1)
|
|
9825
|
+
return null;
|
|
9826
|
+
// Trailing content after `end` — leave for the normal path.
|
|
9827
|
+
if (endIdx !== tokens.length - 1)
|
|
9828
|
+
return null;
|
|
9829
|
+
const jsToken = tokens[jsIdx];
|
|
9830
|
+
const jsParen = jsToken.match(jsRe)?.[1] ?? '';
|
|
9831
|
+
const jsKeywordRaw = jsParen ? jsToken.slice(0, jsToken.length - jsParen.length) : jsToken;
|
|
9832
|
+
const body = tokens.slice(jsIdx + 1, endIdx).join(' ');
|
|
9833
|
+
const targetJs = translateWord(jsKeywordRaw, src, dst) + jsParen;
|
|
9834
|
+
const targetEnd = translateWord(tokens[endIdx], src, dst);
|
|
9835
|
+
const replacement = [targetJs, body, targetEnd].filter(s => s.length > 0).join(' ');
|
|
9836
|
+
const before = tokens.slice(0, jsIdx);
|
|
9837
|
+
// Bare `js ... end` with no leading event-handler head: emit directly.
|
|
9838
|
+
if (before.length === 0)
|
|
9839
|
+
return replacement;
|
|
9840
|
+
// Mask the block as one opaque action token, reorder the surrounding
|
|
9841
|
+
// statement, then restore the verbatim block.
|
|
9842
|
+
const placeholder = 'JSBLOCKPLACEHOLDER';
|
|
9843
|
+
const reordered = this.transformSingle([...before, placeholder].join(' '));
|
|
9844
|
+
if (!reordered.includes(placeholder))
|
|
9845
|
+
return null; // unexpected — fall through
|
|
9846
|
+
return reordered.replace(placeholder, replacement);
|
|
9847
|
+
}
|
|
9848
|
+
/**
|
|
9849
|
+
* Transform an event handler whose body is a block command
|
|
9850
|
+
* (`on <event> [from <src>] {if|repeat|unless|while|for} … end`).
|
|
9851
|
+
*
|
|
9852
|
+
* `parseEventHandler` would treat the block keyword as the action and sweep the
|
|
9853
|
+
* condition/body into role values, then reorder them — shredding the block
|
|
9854
|
+
* (`if event.shiftKey call submitAndContinue() end` → scattered tokens). Instead
|
|
9855
|
+
* we mask the whole block as an opaque action placeholder, reorder the event
|
|
9856
|
+
* head normally, transform the block as a self-contained unit, and restitch.
|
|
9857
|
+
*
|
|
9858
|
+
* Returns `null` (fall through) when the input isn't an event handler, has no
|
|
9859
|
+
* block-keyword body, or has no closing `end`.
|
|
9860
|
+
*/
|
|
9861
|
+
tryTransformEventWithBlockBody(input) {
|
|
9862
|
+
const tokens = tokenize(input, this.sourceProfile);
|
|
9863
|
+
if (tokens.length === 0)
|
|
9864
|
+
return null;
|
|
9865
|
+
if (!EVENT_KEYWORDS.has(tokens[0]?.toLowerCase()))
|
|
9866
|
+
return null;
|
|
9867
|
+
let blockIdx = -1;
|
|
9868
|
+
for (let i = 1; i < tokens.length; i++) {
|
|
9869
|
+
if (BLOCK_BODY_KEYWORDS.has(tokens[i].toLowerCase())) {
|
|
9870
|
+
blockIdx = i;
|
|
9871
|
+
break;
|
|
9872
|
+
}
|
|
9873
|
+
}
|
|
9874
|
+
if (blockIdx <= 0)
|
|
9875
|
+
return null;
|
|
9876
|
+
// Only handle the explicitly-terminated form; un-terminated bodies
|
|
9877
|
+
// (`on click unless X toggle Y`) keep the existing path.
|
|
9878
|
+
if (tokens[tokens.length - 1].toLowerCase() !== 'end')
|
|
9879
|
+
return null;
|
|
9880
|
+
const eventHead = tokens.slice(0, blockIdx);
|
|
9881
|
+
const blockTokens = tokens.slice(blockIdx);
|
|
9882
|
+
// Event heads carrying a `from <source>` modifier (`on keydown[...] from
|
|
9883
|
+
// .modal if … end`): for SVO/SOV targets, route them through this block-body
|
|
9884
|
+
// path too — masking the block and emitting the event clause (incl. the
|
|
9885
|
+
// translated `from <source>`, which `transformSingle` → `parseEventHandler`
|
|
9886
|
+
// already reorders) first, then the transformed block. This keeps the if-block
|
|
9887
|
+
// body from being shredded across the event handler's roles and properly
|
|
9888
|
+
// translates its inner keywords (`in`/`focus`/`first`/positional); the old
|
|
9889
|
+
// exclusion left them English and mangled the order (this is what cleared
|
|
9890
|
+
// `focus-trap` in tr). VSO targets (ar, tl) are kept on the existing path:
|
|
9891
|
+
// there the event-first emission with a `from`-source reorders incorrectly and
|
|
9892
|
+
// regresses `focus-trap`/`window-keydown`, while the existing path already
|
|
9893
|
+
// parses them. So the `from`-source exclusion is scoped to VSO only.
|
|
9894
|
+
if (this.targetProfile.wordOrder === 'VSO' && eventHead.some(t => t.toLowerCase() === 'from')) {
|
|
9895
|
+
return null;
|
|
9896
|
+
}
|
|
9897
|
+
const placeholder = 'EVENTBLOCKPLACEHOLDER';
|
|
9898
|
+
const headOut = this.transformSingle([...eventHead, placeholder].join(' '));
|
|
9899
|
+
if (!headOut.includes(placeholder))
|
|
9900
|
+
return null;
|
|
9901
|
+
const blockOut = this.transformBlockBody(blockTokens);
|
|
9902
|
+
// Always emit the event clause first, then the block. An event handler's
|
|
9903
|
+
// event is a leading delimiter, and the semantic parser only matches a
|
|
9904
|
+
// block body when it follows the event — even in verb-first (VSO) languages
|
|
9905
|
+
// whose normal command order would push the event to the end. So strip the
|
|
9906
|
+
// placeholder out of the (possibly reordered) head and append the block,
|
|
9907
|
+
// rather than substituting in place.
|
|
9908
|
+
const eventClause = headOut.replace(placeholder, '').replace(/\s+/g, ' ').trim();
|
|
9909
|
+
return [eventClause, blockOut].filter(s => s.length > 0).join(' ');
|
|
9910
|
+
}
|
|
9911
|
+
/**
|
|
9912
|
+
* Transform an event handler whose body is an inline `unless` guard with NO
|
|
9913
|
+
* `end` (`on <event> unless <cond> <body>` — the `unless-condition` shape).
|
|
9914
|
+
*
|
|
9915
|
+
* Object-marking SVO targets (he, zh). `parseEventHandler` reads `unless` as the
|
|
9916
|
+
* action and sweeps the whole `<cond> <body>` tail into a single `patient` blob;
|
|
9917
|
+
* the target then prefixes that blob with its object marker — Hebrew's accusative
|
|
9918
|
+
* את (`… אלא את I match .disabled מתג .selected`) or Chinese's BA particle 把
|
|
9919
|
+
* (`… 除非 把 I match .disabled 切换 .selected`) — and the inner toggle loses its
|
|
9920
|
+
* own marker. The semantic parser can't recover the guard from that: the marker
|
|
9921
|
+
* ahead of the condition blocks the `unless` pattern AND the now-markerless body
|
|
9922
|
+
* command fails its object-marked toggle pattern, so the body collapses (`unless`
|
|
9923
|
+
* dropped). Marker-less languages (de/it/ar/pl) tolerate the same role-blob and
|
|
9924
|
+
* stay faithful, so this is an object-marker artifact, not a general parse gap.
|
|
9925
|
+
*
|
|
9926
|
+
* The standalone `unless <cond> <body>` path already produces the correct shape
|
|
9927
|
+
* (`extractBlockStructure` → `transformBlock`: condition kept marker-free, body
|
|
9928
|
+
* command keeps its marker — he `אלא I match .disabled מתג את .selected`, zh
|
|
9929
|
+
* `除非 I match .disabled 切换 把 .selected`). So we split the event head off,
|
|
9930
|
+
* transform the guard through that path, and emit the event clause first (he and
|
|
9931
|
+
* zh are both SVO — event leads). Returns `null` (fall through) when the input
|
|
9932
|
+
* isn't an object-marking event handler with an un-terminated inline `unless`
|
|
9933
|
+
* guard.
|
|
9934
|
+
*/
|
|
9935
|
+
tryTransformEventWithUnlessGuard(input) {
|
|
9936
|
+
// SVO object-marking targets only — these front the unless tail with an object
|
|
9937
|
+
// marker (he את / zh 把) that breaks the parse. Event-leads emission below
|
|
9938
|
+
// assumes SVO, so SOV/VSO object-markers (ja/ko/tr/ar) are intentionally out.
|
|
9939
|
+
if (!UNLESS_GUARD_OBJECT_MARKING_LOCALES.has(this.targetProfile.code))
|
|
9940
|
+
return null;
|
|
9941
|
+
const tokens = tokenize(input, this.sourceProfile);
|
|
9942
|
+
if (tokens.length === 0)
|
|
9943
|
+
return null;
|
|
9944
|
+
if (!EVENT_KEYWORDS.has(tokens[0]?.toLowerCase()))
|
|
9945
|
+
return null;
|
|
9946
|
+
let guardIdx = -1;
|
|
9947
|
+
for (let i = 1; i < tokens.length; i++) {
|
|
9948
|
+
if (tokens[i].toLowerCase() === 'unless') {
|
|
9949
|
+
guardIdx = i;
|
|
9950
|
+
break;
|
|
9951
|
+
}
|
|
9952
|
+
}
|
|
9953
|
+
if (guardIdx <= 0)
|
|
9954
|
+
return null;
|
|
9955
|
+
// The terminated form (`… unless … end`) is handled by
|
|
9956
|
+
// tryTransformEventWithBlockBody's masking path; only take the inline guard.
|
|
9957
|
+
if (tokens[tokens.length - 1].toLowerCase() === 'end')
|
|
9958
|
+
return null;
|
|
9959
|
+
const eventHead = tokens.slice(0, guardIdx);
|
|
9960
|
+
const guard = tokens.slice(guardIdx).join(' ');
|
|
9961
|
+
// `unless` is a BLOCK_HEAD keyword, so transform() routes the guard through
|
|
9962
|
+
// extractBlockStructure → transformBlock (the standalone shape that parses).
|
|
9963
|
+
const guardOut = this.transform(guard);
|
|
9964
|
+
if (!guardOut)
|
|
9965
|
+
return null;
|
|
9966
|
+
const placeholder = 'EVENTGUARDPLACEHOLDER';
|
|
9967
|
+
const headOut = this.transformSingle([...eventHead, placeholder].join(' '));
|
|
9968
|
+
if (!headOut.includes(placeholder))
|
|
9969
|
+
return null;
|
|
9970
|
+
const eventClause = headOut.replace(placeholder, '').replace(/\s+/g, ' ').trim();
|
|
9971
|
+
return [eventClause, guardOut].filter(s => s.length > 0).join(' ');
|
|
9972
|
+
}
|
|
9973
|
+
/**
|
|
9974
|
+
* Transform an event handler whose body leads with a command-modifier
|
|
9975
|
+
* (`on <event> [from <src>] {async|once|debounced [at N]|throttled [at N]} <body>`).
|
|
9976
|
+
*
|
|
9977
|
+
* `parseEventHandler` reads the first token after the event as the **action**, so
|
|
9978
|
+
* a leading modifier is mistaken for the verb and the real verb (`fetch`/`add`) is
|
|
9979
|
+
* swept into the patient. For SOV targets the reorder then surfaces that verb
|
|
9980
|
+
* **first** (`取得 /api/data を クリック …`), and the semantic parser matches the
|
|
9981
|
+
* leading `<verb> <patient>` with the low-priority `*-generated-verb-first`
|
|
9982
|
+
* command pattern — returning a bare command and discarding the event + the rest
|
|
9983
|
+
* of the body (degenerate parse).
|
|
9984
|
+
*
|
|
9985
|
+
* Instead, lift the modifier out, transform the modifier-free handler through the
|
|
9986
|
+
* normal path (which keeps the body in canonical patient-first SOV order so the
|
|
9987
|
+
* event sits mid-stream and the existing SOV event-extraction recovers it), then
|
|
9988
|
+
* re-emit the modifier as a **leading English literal**. The semantic parser
|
|
9989
|
+
* strips a leading `once`/`debounced`/`throttled` (`extractStandaloneModifiers`)
|
|
9990
|
+
* and an `async` anywhere (`stripAsyncModifier`) before parsing, so the modifier
|
|
9991
|
+
* is consumed as handler metadata rather than shadowing the body.
|
|
9992
|
+
*
|
|
9993
|
+
* Returns `null` (fall through) when the input isn't an event handler or the body
|
|
9994
|
+
* doesn't lead with a modifier — leaving simple/Mode-B handlers byte-identical.
|
|
9995
|
+
*/
|
|
9996
|
+
tryTransformEventWithModifierBody(input) {
|
|
9997
|
+
// The verb-first degenerate parse this works around is specific to SOV
|
|
9998
|
+
// reorder: only there does a leading modifier displace the patient-first
|
|
9999
|
+
// order and surface the verb first. SVO/VSO/V2/other targets keep the body
|
|
10000
|
+
// in an order the parser already handles, so leave them byte-identical.
|
|
10001
|
+
if (this.targetProfile.wordOrder !== 'SOV')
|
|
10002
|
+
return null;
|
|
10003
|
+
const tokens = tokenize(input, this.sourceProfile);
|
|
10004
|
+
if (tokens.length === 0)
|
|
10005
|
+
return null;
|
|
10006
|
+
if (!EVENT_KEYWORDS.has(tokens[0]?.toLowerCase()))
|
|
10007
|
+
return null;
|
|
10008
|
+
// Walk past the event clause head: event keyword, event token, any
|
|
10009
|
+
// `or`-conjoined events, and an optional `from <source>` modifier — mirroring
|
|
10010
|
+
// parseEventHandler's head parsing — to find where the body begins.
|
|
10011
|
+
let i = 1; // past the event keyword
|
|
10012
|
+
if (!tokens[i])
|
|
10013
|
+
return null;
|
|
10014
|
+
i++; // past the event token
|
|
10015
|
+
while (tokens[i] && EVENT_CONJUNCTIONS.has(tokens[i].toLowerCase()) && tokens[i + 1]) {
|
|
10016
|
+
i += 2;
|
|
10017
|
+
}
|
|
10018
|
+
if (tokens[i]?.toLowerCase() === 'from' && tokens[i + 1]) {
|
|
10019
|
+
i++; // skip 'from'
|
|
10020
|
+
// Collect source tokens until a command verb or a body modifier.
|
|
10021
|
+
while (tokens[i] &&
|
|
10022
|
+
!ENGLISH_COMMANDS.has(tokens[i].toLowerCase()) &&
|
|
10023
|
+
!BODY_MODIFIER_KEYWORDS.has(tokens[i].toLowerCase())) {
|
|
10024
|
+
i++;
|
|
10025
|
+
}
|
|
10026
|
+
}
|
|
10027
|
+
const modWord = tokens[i]?.toLowerCase();
|
|
10028
|
+
if (!modWord || !BODY_MODIFIER_KEYWORDS.has(modWord))
|
|
10029
|
+
return null;
|
|
10030
|
+
// Consume the modifier phrase. `debounced`/`throttled` may carry an optional
|
|
10031
|
+
// `at <duration>` (or a bare duration). `async`/`once` are single tokens.
|
|
10032
|
+
const modStart = i;
|
|
10033
|
+
let modEnd = i + 1;
|
|
10034
|
+
if (modWord !== 'async' && modWord !== 'once') {
|
|
10035
|
+
if (tokens[modEnd]?.toLowerCase() === 'at')
|
|
10036
|
+
modEnd++;
|
|
10037
|
+
if (tokens[modEnd] && /^\d+(ms|s|m)?$/.test(tokens[modEnd]))
|
|
10038
|
+
modEnd++;
|
|
10039
|
+
}
|
|
10040
|
+
const modifierPhrase = tokens.slice(modStart, modEnd).join(' ');
|
|
10041
|
+
const rebuilt = [...tokens.slice(0, modStart), ...tokens.slice(modEnd)].join(' ');
|
|
10042
|
+
// A handler with only a modifier and no body has nothing to keep patient-first.
|
|
10043
|
+
if (tokens.length - (modEnd - modStart) <= 2)
|
|
10044
|
+
return null;
|
|
10045
|
+
// Re-run the full transform on the modifier-free handler so then-chains and
|
|
10046
|
+
// juxtaposed bodies route through the existing (working) paths. The rebuilt
|
|
10047
|
+
// input no longer leads with a modifier, so this never re-enters here.
|
|
10048
|
+
const bodyOut = this.transform(rebuilt);
|
|
10049
|
+
return [modifierPhrase, bodyOut].filter(s => s.length > 0).join(' ');
|
|
10050
|
+
}
|
|
10051
|
+
/**
|
|
10052
|
+
* Transform a `set <stuff> on <scope>` clause (S1 tabs-aria). The trailing
|
|
10053
|
+
* `on <scope>` is the element(s) the attribute is set on — kept attached by
|
|
10054
|
+
* splitOnCommandBoundaries. The semantic parser captures it as a `scope` role
|
|
10055
|
+
* via the passthrough literal `on` (setSchema's scope markerOverride is `on`
|
|
10056
|
+
* in every language), so `on` is emitted verbatim and only the scope *value*
|
|
10057
|
+
* is translated (selectors pass through; `me`/`it`/`you` translate to the
|
|
10058
|
+
* native reference, which the parser also accepts).
|
|
10059
|
+
*
|
|
10060
|
+
* Positioning matches where the set patterns expect the scope: at the clause
|
|
10061
|
+
* end for verb-first orders (SVO/VSO), and immediately before the clause-final
|
|
10062
|
+
* verb for SOV (the generated SOV pattern is `{dest} {patient} on {scope}
|
|
10063
|
+
* {verb}`). Returns null (fall through) when there is no trailing `on <scope>`.
|
|
10064
|
+
*
|
|
10065
|
+
* Source is English in the sync-translations pipeline, so the `set` verb and
|
|
10066
|
+
* `on` marker are matched as English literals.
|
|
10067
|
+
*/
|
|
10068
|
+
transformSetWithScope(input) {
|
|
10069
|
+
const src = this.sourceProfile.code;
|
|
10070
|
+
const dst = this.targetProfile.code;
|
|
10071
|
+
const m = input.match(/^(.*\bset\b.*\S)\s+on\s+([#.<@[]\S*|me|it|you)\s*$/i);
|
|
10072
|
+
if (!m)
|
|
10073
|
+
return null;
|
|
10074
|
+
const head = m[1];
|
|
10075
|
+
const scopeRaw = m[2];
|
|
10076
|
+
// Transform the scope-less clause via the normal path; `head` no longer ends
|
|
10077
|
+
// in `on <scope>`, so this never re-enters transformSetWithScope.
|
|
10078
|
+
const headOut = this.transformSingle(head);
|
|
10079
|
+
const scopeT = /^[#.<@[]/.test(scopeRaw) ? scopeRaw : translateWord(scopeRaw, src, dst);
|
|
10080
|
+
// SOV needs the scope positioned per how the semantic set patterns match:
|
|
10081
|
+
// - Event-handler set (`on <event> set …`): the dest-first SOV event-handler
|
|
10082
|
+
// pattern is verb-MEDIAL and carries an optional trailing `[on {scope}]`,
|
|
10083
|
+
// so append the scope at the clause end.
|
|
10084
|
+
// - Standalone then-clause set: SOV emits verb-MEDIAL (`{dest} {verb}
|
|
10085
|
+
// {patient}`), but the only command set pattern with scope is verb-LAST
|
|
10086
|
+
// (`{dest} {patient} on {scope} {verb}`). Move the medial verb to the end
|
|
10087
|
+
// and place `on {scope}` before it, so the generated command pattern matches.
|
|
10088
|
+
if (this.targetProfile.wordOrder === 'SOV') {
|
|
10089
|
+
const verb = translateWord('set', 'en', dst);
|
|
10090
|
+
const firstTok = input.trim().split(/\s+/)[0]?.toLowerCase();
|
|
10091
|
+
const isEventHandler = !!firstTok && EVENT_KEYWORDS.has(firstTok);
|
|
10092
|
+
const toks = headOut.split(/\s+/).filter(Boolean);
|
|
10093
|
+
if (!isEventHandler) {
|
|
10094
|
+
// Standalone then-clause set: SOV emits verb-MEDIAL; move the verb to the
|
|
10095
|
+
// end and place `on {scope}` before it so the verb-last command set
|
|
10096
|
+
// pattern (scope before verb) matches.
|
|
10097
|
+
const vIdx = toks.indexOf(verb);
|
|
10098
|
+
if (vIdx >= 0) {
|
|
10099
|
+
toks.splice(vIdx, 1);
|
|
10100
|
+
toks.push('on', scopeT, verb);
|
|
10101
|
+
return toks.join(' ');
|
|
10102
|
+
}
|
|
10103
|
+
}
|
|
10104
|
+
else if (toks.length > 0 && toks[toks.length - 1] === verb) {
|
|
10105
|
+
// Event-handler set whose verb is clause-final (e.g. qu
|
|
10106
|
+
// `{dest} ta {patient} man {event} pi {verb}`): the parser extracts the
|
|
10107
|
+
// event and matches the body as a verb-last command, so the scope must
|
|
10108
|
+
// sit before the verb. Verb-MEDIAL SOV event handlers (ja/ko/tr/bn/hi)
|
|
10109
|
+
// fall through to the append branch, where their fused event-handler set
|
|
10110
|
+
// pattern carries the trailing `[on {scope}]` group.
|
|
10111
|
+
toks.splice(toks.length - 1, 0, 'on', scopeT);
|
|
10112
|
+
return toks.join(' ');
|
|
10113
|
+
}
|
|
10114
|
+
return `${headOut} on ${scopeT}`;
|
|
10115
|
+
}
|
|
10116
|
+
// Verb-first (SVO/VSO/V2): append `on <scope>` at the clause end, which the
|
|
10117
|
+
// trailing `[on {scope}]` group on the set patterns matches.
|
|
10118
|
+
return `${headOut} on ${scopeT}`;
|
|
10119
|
+
}
|
|
10120
|
+
/**
|
|
10121
|
+
* Transform a self-contained block command (`{head} {clause?} {body} {end}`),
|
|
10122
|
+
* where head ∈ {if, repeat, unless, …}. The clause (condition / `until event …`)
|
|
10123
|
+
* runs up to the first command verb and is translated word-by-word; the body is
|
|
10124
|
+
* recursively transformed (so its inner commands reorder for the target); the
|
|
10125
|
+
* head/tail keywords are translated. The block is never word-order reordered as
|
|
10126
|
+
* a whole — delimiters stay at the edges regardless of target word order.
|
|
10127
|
+
*/
|
|
10128
|
+
transformBlockBody(blockTokens) {
|
|
10129
|
+
const src = this.sourceProfile.code;
|
|
10130
|
+
const dst = this.targetProfile.code;
|
|
10131
|
+
const head = blockTokens[0];
|
|
10132
|
+
const hasEnd = blockTokens[blockTokens.length - 1]?.toLowerCase() === 'end';
|
|
10133
|
+
const tail = hasEnd ? blockTokens[blockTokens.length - 1] : '';
|
|
10134
|
+
const inner = blockTokens.slice(1, hasEnd ? -1 : undefined);
|
|
10135
|
+
// Skip predicate-adjective positions (`… is empty`): the adjective is
|
|
10136
|
+
// part of the condition clause, not the body's first verb — cutting there
|
|
10137
|
+
// displaced it into the next command's argument zone (empty ×8 bn/hi/tr).
|
|
10138
|
+
const commands = getCommandKeywordsForLocale(src);
|
|
10139
|
+
const copulas = getCopulasForLocale(src);
|
|
10140
|
+
let bodyStart = inner.findIndex((t, i) => commands.has(t.toLowerCase()) && !isPredicateAdjectivePosition(inner, i, copulas));
|
|
10141
|
+
if (bodyStart < 0)
|
|
10142
|
+
bodyStart = inner.length;
|
|
10143
|
+
const clause = inner.slice(0, bodyStart).join(' ');
|
|
10144
|
+
const bodyTokens = inner.slice(bodyStart);
|
|
10145
|
+
const headT = translateWord(head, src, dst);
|
|
10146
|
+
const tailT = tail ? translateWord(tail, src, dst) : '';
|
|
10147
|
+
const clauseT = clause ? translateMultiWordValue(clause, src, dst) : '';
|
|
10148
|
+
const bodyT = this.transformConditionalBody(bodyTokens);
|
|
10149
|
+
return [headT, clauseT, bodyT, tailT].filter(s => s.length > 0).join(' ');
|
|
10150
|
+
}
|
|
10151
|
+
/**
|
|
10152
|
+
* Transform an `if`/`unless` block body, splitting it at a top-level `else` into
|
|
10153
|
+
* a then-branch and an else-branch so each is reordered as a self-contained unit
|
|
10154
|
+
* and the `else` keyword itself is translated. Without this, the body is reordered
|
|
10155
|
+
* as one stream: `else` rides along glued to the preceding clause (and, when that
|
|
10156
|
+
* clause begins with a selector, is marked a selector and left *untranslated*),
|
|
10157
|
+
* and a spurious `then` is inserted around it — both of which break the target
|
|
10158
|
+
* text and the downstream parse. The split is depth-aware so an `else` belonging
|
|
10159
|
+
* to a nested block is not mistaken for this block's separator. Bodies without an
|
|
10160
|
+
* `else` transform exactly as before.
|
|
10161
|
+
*/
|
|
10162
|
+
transformConditionalBody(bodyTokens) {
|
|
10163
|
+
const src = this.sourceProfile.code;
|
|
10164
|
+
const dst = this.targetProfile.code;
|
|
10165
|
+
const sourceElse = translateWord('else', 'en', src).toLowerCase();
|
|
10166
|
+
let depth = 0;
|
|
10167
|
+
let elseIdx = -1;
|
|
10168
|
+
for (let i = 0; i < bodyTokens.length; i++) {
|
|
10169
|
+
const t = bodyTokens[i].toLowerCase();
|
|
10170
|
+
if (BLOCK_BODY_KEYWORDS.has(t))
|
|
10171
|
+
depth++;
|
|
10172
|
+
else if (t === 'end' && depth > 0)
|
|
10173
|
+
depth--;
|
|
10174
|
+
else if (t === sourceElse && depth === 0) {
|
|
10175
|
+
elseIdx = i;
|
|
10176
|
+
break;
|
|
10177
|
+
}
|
|
10178
|
+
}
|
|
10179
|
+
if (elseIdx === -1) {
|
|
10180
|
+
const body = bodyTokens.join(' ');
|
|
10181
|
+
return body ? this.transform(body) : '';
|
|
10182
|
+
}
|
|
10183
|
+
const thenBranch = bodyTokens.slice(0, elseIdx).join(' ');
|
|
10184
|
+
const elseBranch = bodyTokens.slice(elseIdx + 1).join(' ');
|
|
10185
|
+
const elseT = translateWord(bodyTokens[elseIdx], src, dst);
|
|
10186
|
+
return [
|
|
10187
|
+
thenBranch ? this.transform(thenBranch) : '',
|
|
10188
|
+
elseT,
|
|
10189
|
+
elseBranch ? this.transform(elseBranch) : '',
|
|
10190
|
+
]
|
|
10191
|
+
.filter(s => s.length > 0)
|
|
10192
|
+
.join(' ');
|
|
10193
|
+
}
|
|
8643
10194
|
/**
|
|
8644
10195
|
* Translate a reactive block by translating the head/tail/connector
|
|
8645
10196
|
* via the dictionary, recursively transforming the body through the
|
|
@@ -8806,5 +10357,5 @@ const examples = {
|
|
|
8806
10357
|
},
|
|
8807
10358
|
};
|
|
8808
10359
|
|
|
8809
|
-
export { ENGLISH_COMMANDS, ENGLISH_KEYWORDS, GrammarTransformer, LANGUAGE_FAMILY_DEFAULTS, LocaleManager, UNIVERSAL_ENGLISH_KEYWORDS, UNIVERSAL_PATTERNS, ar$1 as ar, ar$1 as arDictionary, arKeywords, arabicProfile, bn$1 as bn, bn$1 as bnDictionary, bnKeywords, chineseProfile, createEnglishProvider, createKeywordProvider, de$1 as de, de$1 as deDictionary, deKeywords, detectBrowserLocale, directMappings, en$1 as en, englishProfile, es$1 as es, es$1 as esDictionary, esKeywords, fr$1 as fr, fr$1 as frDictionary, frKeywords, frenchProfile, germanProfile, getDirectMapping, getProfile, getSupportedDirectPairs, getSupportedLocales, examples as grammarExamples, hasDirectMapping, he$1 as he, he$1 as heDictionary, heKeywords, hebrewProfile, hindiDictionary as hiDictionary, hiKeywords, hindiDictionary, id$1 as id, id$1 as idDictionary, idKeywords, indonesianProfile, insertMarkers, it$1 as it, it$1 as itDictionary, itKeywords, ja$1 as ja, ja$1 as jaDictionary, jaKeywords, japaneseProfile, joinTokens, ko$1 as ko, ko$1 as koDictionary, koKeywords, koreanProfile, ms$1 as ms, ms$1 as msDictionary, msKeywords, parseStatement, pl$1 as pl, pl$1 as plDictionary, plKeywords, portugueseProfile, profiles, pt$1 as pt, pt$1 as ptDictionary, ptKeywords, qu$1 as qu, qu$1 as quDictionary, quKeywords, quechuaProfile, reorderRoles, russianDictionary as ruDictionary, ruKeywords, russianDictionary, spanishProfile, sw$1 as sw, sw$1 as swDictionary, swKeywords, swahiliProfile, th$1 as th, th$1 as thDictionary, thKeywords, tl$1 as tl, tl$1 as tlDictionary, tlKeywords, toEnglish, toLocale, tr$1 as tr, tr$1 as trDictionary, trKeywords, transformStatement, translate, translateWordDirect, turkishProfile, ukrainianDictionary as ukDictionary, ukKeywords, ukrainianDictionary, vi$1 as vi, vi$1 as viDictionary, viKeywords, zh$1 as zh, zh$1 as zhDictionary, zhKeywords };
|
|
10360
|
+
export { ENGLISH_COMMANDS, ENGLISH_KEYWORDS, GrammarTransformer, LANGUAGE_FAMILY_DEFAULTS, LocaleManager, UNIVERSAL_ENGLISH_KEYWORDS, UNIVERSAL_PATTERNS, ar$1 as ar, ar$1 as arDictionary, arKeywords, arabicProfile, bn$1 as bn, bn$1 as bnDictionary, bnKeywords, chineseProfile, createEnglishProvider, createKeywordProvider, de$1 as de, de$1 as deDictionary, deKeywords, detectBrowserLocale, directMappings, en$1 as en, englishProfile, es$1 as es, es$1 as esDictionary, esKeywords, fr$1 as fr, fr$1 as frDictionary, frKeywords, frenchProfile, germanProfile, getDirectMapping, getProfile, getSupportedDirectPairs, getSupportedLocales, examples as grammarExamples, hasDirectMapping, he$1 as he, he$1 as heDictionary, heKeywords, hebrewProfile, hindiDictionary as hiDictionary, hiKeywords, hindiDictionary, id$1 as id, id$1 as idDictionary, idKeywords, indonesianProfile, insertMarkers, it$1 as it, it$1 as itDictionary, itKeywords, ja$1 as ja, ja$1 as jaDictionary, jaKeywords, japaneseProfile, joinTokens, ko$1 as ko, ko$1 as koDictionary, koKeywords, koreanProfile, malayProfile, ms$1 as ms, ms$1 as msDictionary, msKeywords, parseStatement, pl$1 as pl, pl$1 as plDictionary, plKeywords, portugueseProfile, profiles, pt$1 as pt, pt$1 as ptDictionary, ptKeywords, qu$1 as qu, qu$1 as quDictionary, quKeywords, quechuaProfile, reorderRoles, russianDictionary as ruDictionary, ruKeywords, russianDictionary, spanishProfile, sw$1 as sw, sw$1 as swDictionary, swKeywords, swahiliProfile, th$1 as th, th$1 as thDictionary, thKeywords, tl$1 as tl, tl$1 as tlDictionary, tlKeywords, toEnglish, toLocale, tr$1 as tr, tr$1 as trDictionary, trKeywords, transformStatement, translate, translateWordDirect, turkishProfile, ukrainianDictionary as ukDictionary, ukKeywords, ukrainianDictionary, vi$1 as vi, vi$1 as viDictionary, viKeywords, zh$1 as zh, zh$1 as zhDictionary, zhKeywords };
|
|
8810
10361
|
//# sourceMappingURL=lokascript-i18n.mjs.map
|