@lokascript/i18n 2.5.1 → 2.7.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.
Files changed (73) hide show
  1. package/dist/browser.cjs +1274 -154
  2. package/dist/browser.cjs.map +1 -1
  3. package/dist/browser.d.cts +3 -3
  4. package/dist/browser.d.ts +3 -3
  5. package/dist/browser.js +1274 -155
  6. package/dist/browser.js.map +1 -1
  7. package/dist/dictionaries/index.cjs +364 -133
  8. package/dist/dictionaries/index.cjs.map +1 -1
  9. package/dist/dictionaries/index.d.cts +1 -1
  10. package/dist/dictionaries/index.d.ts +1 -1
  11. package/dist/dictionaries/index.js +364 -133
  12. package/dist/dictionaries/index.js.map +1 -1
  13. package/dist/index.cjs +8860 -7740
  14. package/dist/index.cjs.map +1 -1
  15. package/dist/index.d.cts +3 -3
  16. package/dist/index.d.ts +3 -3
  17. package/dist/index.js +8860 -7741
  18. package/dist/index.js.map +1 -1
  19. package/dist/lokascript-i18n.min.js +1 -1
  20. package/dist/lokascript-i18n.min.js.map +1 -1
  21. package/dist/lokascript-i18n.mjs +1697 -146
  22. package/dist/lokascript-i18n.mjs.map +1 -1
  23. package/dist/plugins/vite.cjs +359 -132
  24. package/dist/plugins/vite.cjs.map +1 -1
  25. package/dist/plugins/vite.js +359 -132
  26. package/dist/plugins/vite.js.map +1 -1
  27. package/dist/plugins/webpack.cjs +359 -132
  28. package/dist/plugins/webpack.cjs.map +1 -1
  29. package/dist/plugins/webpack.js +359 -132
  30. package/dist/plugins/webpack.js.map +1 -1
  31. package/dist/{transformer-BLv389qz.d.ts → transformer-BA8YX9u1.d.ts} +120 -2
  32. package/dist/{transformer-PFsYELrc.d.cts → transformer-DM7iplCU.d.cts} +120 -2
  33. package/dist/{types-BcALAO6h.d.cts → types-BYtpqGq3.d.cts} +1 -1
  34. package/dist/{types-BcALAO6h.d.ts → types-BYtpqGq3.d.ts} +1 -1
  35. package/package.json +9 -6
  36. package/src/browser.ts +1 -0
  37. package/src/command-primary-roles.test.ts +59 -0
  38. package/src/constants.ts +51 -0
  39. package/src/dictionaries/ar.ts +8 -3
  40. package/src/dictionaries/bn.ts +3 -1
  41. package/src/dictionaries/de.ts +19 -4
  42. package/src/dictionaries/derive.ts +4 -0
  43. package/src/dictionaries/en.ts +9 -0
  44. package/src/dictionaries/es.ts +2 -2
  45. package/src/dictionaries/fr.ts +6 -2
  46. package/src/dictionaries/he.ts +7 -1
  47. package/src/dictionaries/hi.ts +17 -10
  48. package/src/dictionaries/id.ts +15 -8
  49. package/src/dictionaries/it.ts +6 -3
  50. package/src/dictionaries/ja.ts +10 -4
  51. package/src/dictionaries/ko.ts +9 -4
  52. package/src/dictionaries/ms.ts +2 -1
  53. package/src/dictionaries/pl.ts +11 -3
  54. package/src/dictionaries/pt.ts +10 -4
  55. package/src/dictionaries/qu.ts +46 -22
  56. package/src/dictionaries/ru.ts +14 -7
  57. package/src/dictionaries/sw.ts +37 -10
  58. package/src/dictionaries/th.ts +3 -1
  59. package/src/dictionaries/tl.ts +14 -7
  60. package/src/dictionaries/tr.ts +22 -9
  61. package/src/dictionaries/uk.ts +13 -7
  62. package/src/dictionaries/vi.ts +3 -3
  63. package/src/dictionaries/zh.ts +10 -2
  64. package/src/examples/new-languages.ts +1 -1
  65. package/src/grammar/grammar.test.ts +1242 -1
  66. package/src/grammar/index.ts +1 -0
  67. package/src/grammar/profiles/index.ts +358 -12
  68. package/src/grammar/transformer.ts +1053 -16
  69. package/src/grammar/types.ts +22 -1
  70. package/src/index.ts +1 -0
  71. package/src/new-languages.test.ts +7 -3
  72. package/src/parser/parser-integration.test.ts +5 -1
  73. package/src/positional-keyword-drift.test.ts +138 -0
package/dist/browser.js CHANGED
@@ -10,6 +10,28 @@ var ENGLISH_MODIFIER_ROLES = {
10
10
  over: "duration",
11
11
  for: "duration"
12
12
  };
13
+ var COMMAND_PRIMARY_ROLES = {
14
+ set: "destination",
15
+ on: "event",
16
+ trigger: "event",
17
+ send: "event",
18
+ wait: "duration",
19
+ fetch: "source",
20
+ get: "source",
21
+ if: "condition",
22
+ unless: "condition",
23
+ while: "condition",
24
+ repeat: "loopType",
25
+ go: "destination",
26
+ scroll: "destination",
27
+ tell: "destination",
28
+ default: "destination",
29
+ swap: "destination",
30
+ // morph deliberately absent: its schema primaryRole is `patient` (the
31
+ // element being morphed — aligned with the transformer's patient marking
32
+ // in the session-9 role-layout swap), and patient is the default.
33
+ bind: "destination"
34
+ };
13
35
  var ENGLISH_MODIFIERS = /* @__PURE__ */ new Set([
14
36
  "to",
15
37
  "from",
@@ -88,7 +110,14 @@ var ENGLISH_COMMANDS = /* @__PURE__ */ new Set([
88
110
  "transition",
89
111
  "trigger",
90
112
  "unless",
91
- "wait"
113
+ "wait",
114
+ // v2.x reactive + realtime + service-worker DSL commands
115
+ "live",
116
+ "bind",
117
+ "eventsource",
118
+ "socket",
119
+ "worker",
120
+ "intercept"
92
121
  ]);
93
122
  var ENGLISH_KEYWORDS = /* @__PURE__ */ new Set([
94
123
  // Flow control
@@ -527,7 +556,8 @@ var es = {
527
556
  swap: "intercambiar",
528
557
  morph: "transformar",
529
558
  settle: "estabilizar",
530
- append: "a\xF1adir",
559
+ append: "anexar",
560
+ // profile primary; 'añadir' is add's word (parsed as add)
531
561
  exit: "salir",
532
562
  install: "instalar",
533
563
  breakpoint: "punto-interrupci\xF3n",
@@ -660,7 +690,7 @@ var es = {
660
690
  prev: "ant",
661
691
  at: "en",
662
692
  random: "aleatorio",
663
- closest: "m\xE1scercano",
693
+ closest: "cercano",
664
694
  parent: "padre",
665
695
  children: "hijos",
666
696
  within: "dentro",
@@ -699,7 +729,8 @@ var ja = {
699
729
  hide: "\u96A0\u3059",
700
730
  show: "\u8868\u793A",
701
731
  if: "\u3082\u3057",
702
- unless: "\u3067\u306A\u3051\u308C\u3070",
732
+ unless: "\u306A\u3044\u9650\u308A",
733
+ // not でなければ — leading で particle shatters the marker
703
734
  repeat: "\u7E70\u308A\u8FD4\u3057",
704
735
  for: "\u305F\u3081\u306B",
705
736
  while: "\u306E\u9593",
@@ -708,7 +739,7 @@ var ja = {
708
739
  break: "\u4E2D\u65AD",
709
740
  halt: "\u505C\u6B62",
710
741
  wait: "\u5F85\u3064",
711
- fetch: "\u53D6\u5F97",
742
+ fetch: "\u30D5\u30A7\u30C3\u30C1",
712
743
  call: "\u547C\u3073\u51FA\u3057",
713
744
  return: "\u623B\u308B",
714
745
  make: "\u4F5C\u308B",
@@ -733,7 +764,11 @@ var ja = {
733
764
  morph: "\u5909\u5F62",
734
765
  settle: "\u5B89\u5B9A",
735
766
  append: "\u672B\u5C3E\u8FFD\u52A0",
736
- exit: "\u7D42\u4E86",
767
+ // 終了 → 退出: realigned to the semantic profile primary (ja.ts `exit: 退出`).
768
+ // 終了 was the historic exit/end collision word (see isEndKeyword's ja note);
769
+ // rendered as 終了 it tokenized as a plain identifier, so a guard-clause
770
+ // `exit` inside `if … end` never parsed (behavior-sortable ja).
771
+ exit: "\u9000\u51FA",
737
772
  install: "\u30A4\u30F3\u30B9\u30C8\u30FC\u30EB",
738
773
  breakpoint: "\u30D6\u30EC\u30FC\u30AF\u30DD\u30A4\u30F3\u30C8",
739
774
  clear: "\u30AF\u30EA\u30A2",
@@ -741,7 +776,8 @@ var ja = {
741
776
  open: "\u958B\u304F",
742
777
  select: "\u9078\u629E",
743
778
  clone: "\u8907\u88FD",
744
- prepend: "\u5148\u982D\u8FFD\u52A0"
779
+ prepend: "\u5148\u982D\u8FFD\u52A0",
780
+ socket: "\u30BD\u30B1\u30C3\u30C8"
745
781
  },
746
782
  modifiers: {
747
783
  to: "\u306B",
@@ -790,7 +826,7 @@ var ja = {
790
826
  touchcancel: "\u30BF\u30C3\u30C1\u30AD\u30E3\u30F3\u30BB\u30EB"
791
827
  },
792
828
  logical: {
793
- when: "\u3068\u304D",
829
+ when: "\u6642",
794
830
  where: "\u3069\u3053",
795
831
  and: "\u305D\u3057\u3066",
796
832
  or: "\u307E\u305F\u306F",
@@ -808,6 +844,7 @@ var ja = {
808
844
  otherwise: "\u305D\u3046\u3067\u306A\u3051\u308C\u3070",
809
845
  end: "\u7D42\u308F\u308A",
810
846
  live: "\u30E9\u30A4\u30D6",
847
+ bind: "\u30D0\u30A4\u30F3\u30C9",
811
848
  changes: "\u5909\u308F\u3063\u305F\u3089"
812
849
  },
813
850
  temporal: {
@@ -901,6 +938,9 @@ var fr = {
901
938
  add: "ajouter",
902
939
  remove: "supprimer",
903
940
  toggle: "basculer",
941
+ // command blur (the events section has the blur EVENT word; without this
942
+ // entry the transformer fell back to it and no profile read it as the verb)
943
+ blur: "d\xE9focaliser",
904
944
  hide: "cacher",
905
945
  show: "montrer",
906
946
  if: "si",
@@ -937,7 +977,8 @@ var fr = {
937
977
  swap: "\xE9changer",
938
978
  morph: "transformer",
939
979
  settle: "stabiliser",
940
- append: "ajouter",
980
+ append: "annexer",
981
+ // profile primary; 'ajouter' is add's word (parsed as add)
941
982
  exit: "sortir",
942
983
  install: "installer",
943
984
  breakpoint: "point-arr\xEAt",
@@ -946,7 +987,8 @@ var fr = {
946
987
  open: "ouvrir",
947
988
  select: "s\xE9lectionner",
948
989
  clone: "cloner",
949
- prepend: "pr\xE9fixer"
990
+ prepend: "pr\xE9fixer",
991
+ focus: "focaliser"
950
992
  },
951
993
  modifiers: {
952
994
  to: "\xE0",
@@ -1070,7 +1112,7 @@ var fr = {
1070
1112
  prev: "pr\xE9c",
1071
1113
  at: "\xE0",
1072
1114
  random: "al\xE9atoire",
1073
- closest: "plusproche",
1115
+ closest: "proche",
1074
1116
  parent: "parent",
1075
1117
  children: "enfants",
1076
1118
  within: "dans",
@@ -1106,9 +1148,17 @@ var de = {
1106
1148
  add: "hinzuf\xFCgen",
1107
1149
  remove: "entfernen",
1108
1150
  toggle: "umschalten",
1151
+ // command blur (the events section has the blur EVENT word; without this
1152
+ // entry the transformer fell back to it and no profile read it as the verb)
1153
+ blur: "defokussieren",
1109
1154
  hide: "verstecken",
1110
1155
  show: "zeigen",
1111
- if: "wenn",
1156
+ // Align with the semantic German profile's `if` primary (`falls`). The previous
1157
+ // `wenn` collides with the profile's `when` keyword (German `wenn` = both
1158
+ // "if" and "when"), so a transformed `if` resolved to `when` and the conditional
1159
+ // wrapper never formed (`if`/`put` dropped). Same dict↔profile alignment as id
1160
+ // `toggle` / get-value. `when` is unaffected (separate dict entry).
1161
+ if: "falls",
1112
1162
  unless: "wennnicht",
1113
1163
  repeat: "wiederholen",
1114
1164
  for: "f\xFCr",
@@ -1118,7 +1168,7 @@ var de = {
1118
1168
  break: "unterbrechen",
1119
1169
  halt: "anhalten",
1120
1170
  wait: "warten",
1121
- fetch: "holen",
1171
+ fetch: "abrufen",
1122
1172
  call: "aufrufen",
1123
1173
  return: "zur\xFCckgeben",
1124
1174
  make: "erstellen",
@@ -1146,12 +1196,14 @@ var de = {
1146
1196
  exit: "beenden",
1147
1197
  install: "installieren",
1148
1198
  breakpoint: "haltepunkt",
1149
- clear: "l\xF6schen",
1199
+ clear: "bereinigen",
1200
+ // löschen is a remove alternative in the profile
1150
1201
  close: "schlie\xDFen",
1151
1202
  open: "\xF6ffnen",
1152
1203
  select: "ausw\xE4hlen",
1153
1204
  clone: "klonen",
1154
- prepend: "voranstellen"
1205
+ prepend: "voranstellen",
1206
+ focus: "fokussieren"
1155
1207
  },
1156
1208
  modifiers: {
1157
1209
  to: "zu",
@@ -1275,7 +1327,13 @@ var de = {
1275
1327
  prev: "vorh",
1276
1328
  at: "bei",
1277
1329
  random: "zuf\xE4llig",
1278
- closest: "n\xE4chste",
1330
+ // `nächste` already serves `next` (line above). German `nächste` is
1331
+ // genuinely ambiguous (next/nearest), so emitting it for `closest` too
1332
+ // collided: the semantic tokenizer normalizes `nächste`→next (a deliberate
1333
+ // last-wins choice — see german.ts), and a closest reading is impossible to
1334
+ // recover. Use the unambiguous `nächstgelegene` ("nearest-located") for
1335
+ // closest so the two readings stay distinct end-to-end.
1336
+ closest: "n\xE4chstgelegene",
1279
1337
  parent: "elternteil",
1280
1338
  children: "kinder",
1281
1339
  within: "innerhalb",
@@ -1314,7 +1372,10 @@ var ar = {
1314
1372
  hide: "\u0627\u062E\u0641",
1315
1373
  show: "\u0627\u0638\u0647\u0631",
1316
1374
  if: "\u0625\u0630\u0627",
1317
- unless: "\u0625\u0644\u0627 \u0625\u0630\u0627",
1375
+ // Single-token form: the semantic ar tokenizer splits the two-word `إلا إذا`
1376
+ // into `إلا` + `إذا`(if), which derails the `unless` block. `إلا` alone is the
1377
+ // conventional Arabic shorthand and matches the ar profile's unless keyword.
1378
+ unless: "\u0625\u0644\u0627",
1318
1379
  repeat: "\u0643\u0631\u0631",
1319
1380
  for: "\u0644\u0643\u0644",
1320
1381
  while: "\u0628\u064A\u0646\u0645\u0627",
@@ -1344,19 +1405,22 @@ var ar = {
1344
1405
  js: "\u062C\u0627\u0641\u0627\u0633\u0643\u0631\u0628\u062A",
1345
1406
  async: "\u0645\u062A\u0632\u0627\u0645\u0646",
1346
1407
  render: "\u0627\u0631\u0633\u0645",
1347
- swap: "\u0628\u062F\u0651\u0644",
1408
+ swap: "\u0627\u0633\u062A\u0628\u062F\u0644",
1409
+ // profile primary; 'بدّل' is toggle's word (parsed as toggle)
1348
1410
  morph: "\u062D\u0648\u0651\u0644",
1349
1411
  settle: "\u0627\u0633\u062A\u0642\u0631",
1350
1412
  append: "\u0623\u0644\u062D\u0642",
1351
1413
  exit: "\u0627\u062E\u0631\u062C",
1352
1414
  install: "\u062B\u0628\u0651\u062A",
1353
1415
  breakpoint: "\u0646\u0642\u0637\u0629-\u062A\u0648\u0642\u0641",
1354
- clear: "\u0627\u0645\u0633\u062D",
1416
+ clear: "\u0646\u0638\u0651\u0641",
1417
+ // امسح is a remove alternative in the profile
1355
1418
  close: "\u0623\u063A\u0644\u0642",
1356
1419
  open: "\u0627\u0641\u062A\u062D",
1357
1420
  select: "\u0627\u062E\u062A\u0631",
1358
1421
  clone: "\u0627\u0633\u062A\u0646\u0633\u062E",
1359
- prepend: "\u0633\u0628\u0642"
1422
+ prepend: "\u0633\u0628\u0642",
1423
+ socket: "\u0645\u0642\u0628\u0633"
1360
1424
  },
1361
1425
  modifiers: {
1362
1426
  to: "\u0625\u0644\u0649",
@@ -1423,6 +1487,7 @@ var ar = {
1423
1487
  otherwise: "\u062E\u0644\u0627\u0641 \u0630\u0644\u0643",
1424
1488
  end: "\u0627\u0644\u0646\u0647\u0627\u064A\u0629",
1425
1489
  live: "\u062D\u064A",
1490
+ bind: "\u0627\u0631\u0628\u0637",
1426
1491
  changes: "\u064A\u062A\u063A\u064A\u0631"
1427
1492
  },
1428
1493
  temporal: {
@@ -1519,9 +1584,11 @@ var ko = {
1519
1584
  hide: "\uC228\uAE30\uB2E4",
1520
1585
  show: "\uBCF4\uC774\uB2E4",
1521
1586
  if: "\uB9CC\uC57D",
1522
- unless: "\uC544\uB2C8\uBA74",
1587
+ unless: "\uC544\uB2C8\uB77C\uBA74",
1588
+ // distinct from else `아니면` (line ~131) — was a homonym
1523
1589
  repeat: "\uBC18\uBCF5",
1524
- for: "\uB3D9\uC548",
1590
+ for: "\uAC01\uAC01",
1591
+ // profile's for word — 동안 is while (collision)
1525
1592
  while: "\uB3D9\uC548",
1526
1593
  until: "\uAE4C\uC9C0",
1527
1594
  continue: "\uACC4\uC18D",
@@ -1552,8 +1619,12 @@ var ko = {
1552
1619
  swap: "\uAD50\uD658",
1553
1620
  morph: "\uBCC0\uD615",
1554
1621
  settle: "\uC548\uC815",
1555
- append: "\uCD94\uAC00",
1556
- exit: "\uC885\uB8CC",
1622
+ append: "\uB367\uBD99\uC774\uB2E4",
1623
+ // profile primary; '추가' is add's word (parsed as add)
1624
+ // 종료 → 나가기: realigned to the semantic profile primary (ko.ts `exit: 나가기`,
1625
+ // whose comment already flags the 종료 end-collision). Rendered as 종료 it
1626
+ // tokenized as a plain identifier, so a guard-clause `exit` never parsed.
1627
+ exit: "\uB098\uAC00\uAE30",
1557
1628
  install: "\uC124\uCE58",
1558
1629
  breakpoint: "\uC911\uB2E8\uC810",
1559
1630
  clear: "\uC9C0\uC6B0\uAE30",
@@ -1561,7 +1632,8 @@ var ko = {
1561
1632
  open: "\uC5F4\uAE30",
1562
1633
  select: "\uC120\uD0DD",
1563
1634
  clone: "\uBCF5\uC81C",
1564
- prepend: "\uC55E\uC5D0\uCD94\uAC00"
1635
+ prepend: "\uC55E\uC5D0\uCD94\uAC00",
1636
+ socket: "\uC18C\uCF13"
1565
1637
  },
1566
1638
  modifiers: {
1567
1639
  to: "\uC5D0",
@@ -1628,6 +1700,7 @@ var ko = {
1628
1700
  otherwise: "\uADF8\uB807\uC9C0\uC54A\uC73C\uBA74",
1629
1701
  end: "\uB05D",
1630
1702
  live: "\uB77C\uC774\uBE0C",
1703
+ bind: "\uBC14\uC778\uB4DC",
1631
1704
  changes: "\uBCC0\uACBD\uB418\uBA74"
1632
1705
  },
1633
1706
  temporal: {
@@ -1714,7 +1787,11 @@ var zh = {
1714
1787
  tell: "\u544A\u8BC9",
1715
1788
  trigger: "\u89E6\u53D1",
1716
1789
  send: "\u53D1\u9001",
1717
- take: "\u83B7\u53D6",
1790
+ // `拿取` (the semantic zh profile's take primary), not `获取` — `获取` is the
1791
+ // profile's GET primary, so emitting it for take made `take …` parse as get
1792
+ // (take-class-from-siblings: phantom get, take dropped). Exposed by the get-zh-ba
1793
+ // pattern; same dict↔profile homonym family as pl get/fetch.
1794
+ take: "\u62FF\u53D6",
1718
1795
  put: "\u653E\u7F6E",
1719
1796
  set: "\u8BBE\u7F6E",
1720
1797
  get: "\u83B7\u5F97",
@@ -1733,7 +1810,10 @@ var zh = {
1733
1810
  break: "\u4E2D\u65AD",
1734
1811
  halt: "\u505C\u6B62",
1735
1812
  wait: "\u7B49\u5F85",
1736
- fetch: "\u83B7\u53D6",
1813
+ // 抓取 (zhuāqǔ, "grab/scrape") is the semantic zh profile's `fetch` primary;
1814
+ // 获取 (huòqǔ) is its `get` primary, so emitting 获取 here made the semantic
1815
+ // parser read a transformed `fetch` as `get`. See ZH_BLOCK_BODY_SCOPE.md #3.
1816
+ fetch: "\u6293\u53D6",
1737
1817
  call: "\u8C03\u7528",
1738
1818
  return: "\u8FD4\u56DE",
1739
1819
  make: "\u5236\u4F5C",
@@ -1833,6 +1913,7 @@ var zh = {
1833
1913
  otherwise: "\u8981\u4E0D\u7136",
1834
1914
  end: "\u7ED3\u675F",
1835
1915
  live: "\u5B9E\u65F6",
1916
+ bind: "\u7ED1\u5B9A",
1836
1917
  changes: "\u6539\u53D8\u65F6"
1837
1918
  },
1838
1919
  temporal: {
@@ -1919,7 +2000,8 @@ var tr = {
1919
2000
  tell: "s\xF6yle",
1920
2001
  trigger: "tetikle",
1921
2002
  send: "g\xF6nder",
1922
- take: "al",
2003
+ take: "tut",
2004
+ // auditor: dict emitted a word the profile reads differently
1923
2005
  put: "koy",
1924
2006
  set: "ayarla",
1925
2007
  get: "al",
@@ -1932,7 +2014,11 @@ var tr = {
1932
2014
  unless: "de\u011Filse",
1933
2015
  repeat: "tekrarla",
1934
2016
  for: "i\xE7in",
1935
- while: "iken",
2017
+ // `süresince` (the semantic tr profile's while primary), not `iken`: `iken`
2018
+ // is the profile's WHEN primary (logical.when below also emits it), so a
2019
+ // fronted repeat-while head tokenized as `when` and the condition dropped
2020
+ // wholesale. Same dict↔profile homonym disambiguation as pl get/pobierz.
2021
+ while: "s\xFCresince",
1936
2022
  until: "kadar",
1937
2023
  continue: "devam",
1938
2024
  break: "dur",
@@ -1962,7 +2048,8 @@ var tr = {
1962
2048
  swap: "takas",
1963
2049
  morph: "d\xF6n\xFC\u015Ft\xFCr",
1964
2050
  settle: "sabitlen",
1965
- append: "ekle",
2051
+ append: "ili\u015Ftir",
2052
+ // profile primary; 'ekle' is add's word (parsed as add)
1966
2053
  exit: "\xE7\u0131k",
1967
2054
  install: "kur",
1968
2055
  breakpoint: "kesme-noktas\u0131",
@@ -1971,7 +2058,8 @@ var tr = {
1971
2058
  open: "a\xE7",
1972
2059
  select: "se\xE7",
1973
2060
  clone: "klonla",
1974
- prepend: "ba\u015F\u0131naekle"
2061
+ prepend: "ba\u015F\u0131naekle",
2062
+ socket: "soket"
1975
2063
  },
1976
2064
  modifiers: {
1977
2065
  to: "e",
@@ -1994,8 +2082,11 @@ var tr = {
1994
2082
  events: {
1995
2083
  click: "t\u0131klama",
1996
2084
  dblclick: "\xE7ift_t\u0131klama",
1997
- mousedown: "fare_bas",
1998
- mouseup: "fare_b\u0131rak",
2085
+ // Fused (no `_`): the semantic tokenizer splits on `_`, breaking event
2086
+ // recognition (mousedown → "bas" / mouseup mis-captured). Mirrors the #535
2087
+ // ru/uk fuse + #510 resize fix.
2088
+ mousedown: "farebas",
2089
+ mouseup: "fareb\u0131rak",
1999
2090
  mouseenter: "fare_gir",
2000
2091
  mouseleave: "fare_\xE7\u0131k",
2001
2092
  mouseover: "fare_\xFCst\xFC",
@@ -2012,7 +2103,7 @@ var tr = {
2012
2103
  reset: "s\u0131f\u0131rla",
2013
2104
  load: "y\xFCkle",
2014
2105
  unload: "y\xFCkle_kald\u0131r",
2015
- resize: "boyut_de\u011Fi\u015Ftir",
2106
+ resize: "boyutland\u0131rma",
2016
2107
  scroll: "kayd\u0131r",
2017
2108
  touchstart: "dokunma_ba\u015Fla",
2018
2109
  touchend: "dokunma_bitir",
@@ -2033,7 +2124,7 @@ var tr = {
2033
2124
  equals: "e\u015Fittir",
2034
2125
  has: "var",
2035
2126
  have: "var",
2036
- then: "sonra",
2127
+ then: "ard\u0131ndan",
2037
2128
  else: "yoksa",
2038
2129
  otherwise: "aksi_halde",
2039
2130
  end: "son",
@@ -2089,13 +2180,18 @@ var tr = {
2089
2180
  },
2090
2181
  expressions: {
2091
2182
  first: "ilk",
2092
- last: "son",
2183
+ // 'son' is also the END keyword emission (block terminator), and the
2184
+ // parser's end-recognizers match it by value — so a positional `last`
2185
+ // emission of 'son' terminated the enclosing block mid-condition
2186
+ // (focus-trap's `eşleşir son <button/>` dropped the branch body; the sw
2187
+ // mwisho class). 'sonuncu' ("the last one") is unambiguous.
2188
+ last: "sonuncu",
2093
2189
  next: "sonraki",
2094
2190
  previous: "\xF6nceki",
2095
2191
  prev: "\xF6nc",
2096
2192
  at: "de",
2097
2193
  random: "rastgele",
2098
- closest: "en_yak\u0131n",
2194
+ closest: "enyak\u0131n",
2099
2195
  parent: "ebeveyn",
2100
2196
  children: "\xE7ocuklar",
2101
2197
  within: "i\xE7inde",
@@ -2130,7 +2226,7 @@ var id = {
2130
2226
  get: "dapatkan",
2131
2227
  add: "tambah",
2132
2228
  remove: "hapus",
2133
- toggle: "ganti",
2229
+ toggle: "alihkan",
2134
2230
  hide: "sembunyikan",
2135
2231
  show: "tampilkan",
2136
2232
  if: "jika",
@@ -2143,7 +2239,10 @@ var id = {
2143
2239
  break: "hentikan",
2144
2240
  halt: "berhenti",
2145
2241
  wait: "tunggu",
2146
- fetch: "ambil",
2242
+ // profile primary; 'ambil' is take's word — emitting it for fetch made every
2243
+ // fetch parse as take once the fused take-event pattern came alive (the de
2244
+ // holen/abrufen bug class)
2245
+ fetch: "muat",
2147
2246
  call: "panggil",
2148
2247
  return: "kembali",
2149
2248
  make: "buat",
@@ -2152,7 +2251,11 @@ var id = {
2152
2251
  catch: "tangkap",
2153
2252
  measure: "ukur",
2154
2253
  transition: "transisi",
2155
- increment: "tambahkan",
2254
+ // naikkan (raise), not tambahkan — tambahkan is the semantic profile's
2255
+ // `add` alternative, so emitting it made increment-counter parse as ADD
2256
+ // (the #373 collision family). naikkan is the profile's increment
2257
+ // alternative, pairing naturally with the decrement side.
2258
+ increment: "naikkan",
2156
2259
  decrement: "kurangi",
2157
2260
  default: "bawaan",
2158
2261
  go: "pergi",
@@ -2163,15 +2266,20 @@ var id = {
2163
2266
  beep: "bunyi",
2164
2267
  js: "js",
2165
2268
  async: "asinkron",
2166
- render: "tampilkan",
2269
+ render: "olah",
2270
+ // auditor: dict emitted a word the profile reads differently
2167
2271
  swap: "tukar",
2168
- morph: "ubah_bentuk",
2169
- settle: "stabil",
2170
- append: "tambah_akhir",
2272
+ morph: "ubah",
2273
+ // auditor: dict emitted a word the profile reads differently
2274
+ settle: "stabilkan",
2275
+ // auditor: dict emitted a word the profile reads differently
2276
+ append: "sisipkan",
2277
+ // profile primary; 'tambah_akhir' splits and is unrecognized
2171
2278
  exit: "keluar",
2172
2279
  install: "pasang",
2173
2280
  breakpoint: "titik-henti",
2174
- clear: "hapus",
2281
+ clear: "bersihkan",
2282
+ // hapus is the remove PRIMARY in the profile
2175
2283
  close: "tutup",
2176
2284
  open: "buka",
2177
2285
  select: "pilih",
@@ -2329,20 +2437,34 @@ var qu = {
2329
2437
  tell: "niy",
2330
2438
  trigger: "kichay",
2331
2439
  send: "kachay",
2332
- take: "hurquy",
2440
+ take: "hapiy",
2441
+ // auditor: dict emitted a word the profile reads differently
2333
2442
  put: "churay",
2334
- set: "churay",
2335
- get: "chaskiy",
2443
+ // churay is the semantic qu profile's `put` primary; `set` is churanay. Emitting
2444
+ // churay for set made the semantic parser read a transformed `set` as `put`.
2445
+ // See ZH_BLOCK_BODY_SCOPE.md (#2 sweep — keyword realignment, cf. zh fetch).
2446
+ set: "churanay",
2447
+ // Align with the semantic quechua profile's `get` primary (`taripay`); the
2448
+ // previous `chaskiy` had no profile entry, so the transformed `get` dropped
2449
+ // (get-value parsed degenerate). Same dict↔profile alignment as id `toggle`.
2450
+ get: "taripay",
2336
2451
  add: "yapay",
2337
2452
  remove: "qichuy",
2338
2453
  toggle: "tikray",
2339
2454
  hide: "pakay",
2340
2455
  show: "rikuchiy",
2341
2456
  if: "sichus",
2342
- unless: "mana_sichus",
2343
- repeat: "kutichiy",
2344
- for: "rayku",
2345
- while: "kay_kaq",
2457
+ unless: "mana sichus",
2458
+ // spaced phrase ("if not"); `_` form split to `mana`+`sichus`(=if) at parse time
2459
+ repeat: "kutipay",
2460
+ for: "sapankaq",
2461
+ // profile's for word — rayku doubles as `by`
2462
+ // `kaykamaqa` (the semantic qu profile's while primary), not `kay_kaq`: the
2463
+ // `_` form is unknown to the profile (and `_` words split at parse time, cf.
2464
+ // qhipaman_yapay), so the fronted repeat-while head never formed a `while`
2465
+ // node — the condition dropped wholesale. Same dict↔profile alignment as
2466
+ // vi render / de abrufen.
2467
+ while: "kaykamaqa",
2346
2468
  until: "hayk_akama",
2347
2469
  continue: "purichiy",
2348
2470
  break: "p_akiy",
@@ -2352,12 +2474,18 @@ var qu = {
2352
2474
  call: "qayay",
2353
2475
  return: "kutimuy",
2354
2476
  make: "ruray",
2355
- log: "qillqay",
2477
+ // qillqay is the semantic qu profile's `copy` primary; `log` is qillqakuy.
2478
+ // Emitting qillqay for log made the parser read every transformed log as
2479
+ // copy (log-value/log-element/get-value lossy) — same realign as set above.
2480
+ log: "qillqakuy",
2356
2481
  throw: "wikchuy",
2357
2482
  catch: "hapsiy",
2358
2483
  measure: "tupuy",
2359
- transition: "tikray",
2360
- increment: "yapay",
2484
+ // pasay, not tikray: the semantic qu profile's toggle keyword is
2485
+ // t'ikray/tikray, so a tikray render collided with toggle and the
2486
+ // transition command never anchored (sw kama precedent, #569).
2487
+ transition: "pasay",
2488
+ increment: "yapachiy",
2361
2489
  decrement: "pisiyachiy",
2362
2490
  default: "\xF1awpaq_kaq",
2363
2491
  go: "riy",
@@ -2368,20 +2496,26 @@ var qu = {
2368
2496
  beep: "waqay",
2369
2497
  js: "js",
2370
2498
  async: "mana_suyaspa",
2371
- render: "rikuchiy",
2372
- swap: "rantin_tikray",
2373
- morph: "tikrachiy",
2374
- settle: "tiyay",
2375
- append: "qhipaman_yapay",
2499
+ render: "rikurichiy",
2500
+ // auditor: dict emitted a word the profile reads differently
2501
+ swap: "t'inkuy",
2502
+ // profile primary; 'rantin_tikray' splits, 'tikray' is toggle's word
2503
+ morph: "tukunay",
2504
+ // auditor: dict emitted a word the profile reads differently
2505
+ settle: "tiyakuy",
2506
+ // auditor: dict emitted a word the profile reads differently
2507
+ append: "qatichiy",
2508
+ // profile's append primary (single token); `qhipaman_yapay` `_`-split to qhipaman+yapay(=add) at parse time
2376
2509
  exit: "lluqsiy",
2377
- install: "churay",
2510
+ install: "tarpuy",
2378
2511
  breakpoint: "sayachinay",
2379
2512
  clear: "pichay",
2380
2513
  close: "wichqay",
2381
2514
  open: "kichay",
2382
2515
  select: "akllay",
2383
2516
  clone: "kikinchay",
2384
- prepend: "\xF1awpachiy"
2517
+ prepend: "\xF1awpachiy",
2518
+ socket: "tinkina"
2385
2519
  },
2386
2520
  modifiers: {
2387
2521
  to: "man",
@@ -2404,8 +2538,10 @@ var qu = {
2404
2538
  events: {
2405
2539
  click: "\xF1itiy",
2406
2540
  dblclick: "iskay_\xF1itiy",
2407
- mousedown: "rat_\xF1itiy",
2408
- mouseup: "rat_huqariy",
2541
+ // Fused (no `_`): the semantic tokenizer splits on `_` — and `rat_ñitiy` then
2542
+ // mis-captured as the `click` event (ñitiy→click). Mirrors the #535 ru/uk fuse.
2543
+ mousedown: "rat\xF1itiy",
2544
+ mouseup: "rathuqariy",
2409
2545
  mouseenter: "rat_yaykuy",
2410
2546
  mouseleave: "rat_lluqsiy",
2411
2547
  mouseover: "rat_hawapi",
@@ -2444,7 +2580,11 @@ var qu = {
2444
2580
  has: "kachkan",
2445
2581
  have: "kachkani",
2446
2582
  then: "chayqa",
2447
- else: "mana_chayqa",
2583
+ // Align to the semantic profile's else word `manachus`. The old `mana_chayqa`
2584
+ // tokenized as `mana`(false) + `_` + `chayqa`(then) — no else keyword formed,
2585
+ // so qu conditionals never split their else branch. `manachus` is a single
2586
+ // token the profile-derived keyword map reads as `else` (dict↔profile align).
2587
+ else: "manachus",
2448
2588
  otherwise: "huk_kaqpi",
2449
2589
  end: "tukuy",
2450
2590
  live: "kawsay",
@@ -2467,7 +2607,7 @@ var qu = {
2467
2607
  values: {
2468
2608
  true: "cheqaq",
2469
2609
  false: "llulla",
2470
- null: "ch_usaq",
2610
+ null: "chusaq",
2471
2611
  undefined: "mana_riqsisqa",
2472
2612
  it: "chay",
2473
2613
  its: "chaypaq",
@@ -2505,12 +2645,12 @@ var qu = {
2505
2645
  prev: "\xF1awpaq",
2506
2646
  at: "pi",
2507
2647
  random: "imaymanata",
2508
- closest: "aswan_kaylla",
2648
+ closest: "kaylla",
2509
2649
  parent: "mama_tayta",
2510
2650
  children: "wawakuna",
2511
2651
  within: "ukupi",
2512
2652
  no: "mana_kanchu",
2513
- empty: "ch_usaq",
2653
+ empty: "chusaq",
2514
2654
  some: "wakin",
2515
2655
  "starts with": "qallarisqa wan",
2516
2656
  "ends with": "tukusqa wan",
@@ -2536,11 +2676,17 @@ var sw = {
2536
2676
  send: "tuma",
2537
2677
  take: "chukua",
2538
2678
  put: "weka",
2539
- set: "weka",
2679
+ // weka is the semantic sw profile's `put` primary; `set` is seti. Emitting weka
2680
+ // for set made the semantic parser read a transformed `set` as `put`.
2681
+ // See ZH_BLOCK_BODY_SCOPE.md (#2 sweep — keyword realignment, cf. zh fetch).
2682
+ set: "seti",
2540
2683
  get: "pata",
2541
2684
  add: "ongeza",
2542
2685
  remove: "ondoa",
2543
2686
  toggle: "badilisha",
2687
+ // command blur (the events section has the blur EVENT word; without this
2688
+ // entry the transformer fell back to it and no profile read it as the verb)
2689
+ blur: "blur",
2544
2690
  hide: "ficha",
2545
2691
  show: "onyesha",
2546
2692
  if: "kama",
@@ -2556,13 +2702,14 @@ var sw = {
2556
2702
  fetch: "leta",
2557
2703
  call: "ita",
2558
2704
  return: "rudi",
2559
- make: "fanya",
2705
+ make: "tengeneza",
2706
+ // auditor: dict emitted a word the profile reads differently
2560
2707
  log: "andika",
2561
2708
  throw: "tupa",
2562
2709
  catch: "shika",
2563
2710
  measure: "pima",
2564
2711
  transition: "mpito",
2565
- increment: "ongeza",
2712
+ increment: "ongezeko",
2566
2713
  decrement: "punguza",
2567
2714
  default: "msingi",
2568
2715
  go: "nenda",
@@ -2575,18 +2722,23 @@ var sw = {
2575
2722
  async: "sainkroni",
2576
2723
  render: "chora",
2577
2724
  swap: "badilishana",
2578
- morph: "badilishaUmbo",
2725
+ morph: "geuza",
2726
+ // auditor: dict emitted a word the profile reads differently
2579
2727
  settle: "tulia",
2580
- append: "ongezaMwisho",
2728
+ append: "ambatanisha",
2729
+ // profile primary; 'ongezaMwisho' is unrecognized
2581
2730
  exit: "toka",
2582
2731
  install: "sakinisha",
2583
2732
  breakpoint: "nukta-simama",
2584
- clear: "futa",
2733
+ clear: "safisha",
2734
+ // futa is a remove alternative in the profile
2585
2735
  close: "funga",
2586
2736
  open: "fungua",
2587
2737
  select: "chagua",
2588
2738
  clone: "nakili",
2589
- prepend: "tanguliza"
2739
+ prepend: "tanguliza",
2740
+ focus: "lenga",
2741
+ socket: "soketi"
2590
2742
  },
2591
2743
  modifiers: {
2592
2744
  to: "kwa",
@@ -2596,7 +2748,14 @@ var sw = {
2596
2748
  at: "katika",
2597
2749
  in: "ndani",
2598
2750
  of: "ya",
2599
- as: "kama",
2751
+ // `kuwa` ("to be/become" — the conversion sense, cf. `badilisha kuwa`), not
2752
+ // `kama`: sw `kama` is the IF keyword (commands.if above and the semantic sw
2753
+ // profile's if primary), so emitting it for `as` grew a phantom `if` command
2754
+ // out of every transformed `as <Type>` tail (`kama JSON`, `kama Number`) when
2755
+ // the semantic parser split event-body statements. Same dict↔profile homonym
2756
+ // disambiguation as pl get/pobierz. The semantic fetch-sw pattern still
2757
+ // tolerates hand-written `kama` in as-marker position.
2758
+ as: "kuwa",
2600
2759
  by: "na",
2601
2760
  before: "kabla",
2602
2761
  after: "baada",
@@ -2652,7 +2811,7 @@ var sw = {
2652
2811
  else: "sivyo",
2653
2812
  otherwise: "vinginevyo",
2654
2813
  end: "mwisho",
2655
- live: "moja_kwa_moja",
2814
+ live: "mubashara",
2656
2815
  changes: "inabadilika"
2657
2816
  },
2658
2817
  temporal: {
@@ -2704,13 +2863,25 @@ var sw = {
2704
2863
  },
2705
2864
  expressions: {
2706
2865
  first: "kwanza",
2707
- last: "mwisho",
2866
+ // 'mwisho' is the END keyword (block terminator) — the tokenizer's keyword
2867
+ // map is last-wins, so a positional `last` emission of 'mwisho' reads as
2868
+ // `end` and terminates the enclosing block mid-condition (focus-trap's
2869
+ // `… inafanana mwisho <button/>` dropped the whole branch body). Emit the
2870
+ // concatenated adjective form instead (wa mwisho — the saufsi/wennnicht/
2871
+ // enyakın single-token class); the tokenizer reads it as `last`.
2872
+ last: "wamwisho",
2708
2873
  next: "ijayo",
2709
2874
  previous: "iliyopita",
2710
2875
  prev: "awali",
2711
2876
  at: "katika",
2712
2877
  random: "nasibu",
2713
- closest: "karibu_zaidi",
2878
+ // Natural Swahili: `karibu` ("near/close") is the ordinary word and already
2879
+ // maps to closest. The earlier `karibu_zaidi` ("nearer") underscore-split to
2880
+ // karibu/_/zaidi (the stray `_ zaidi` broke positional capture); the fused
2881
+ // `karibuzaidi` parsed but isn't real Swahili. `karibu` alone is both natural
2882
+ // and correct (the sw tokenizer is single-word-only, so a spaced `karibu
2883
+ // zaidi` would strand `zaidi` regardless).
2884
+ closest: "karibu",
2714
2885
  parent: "mzazi",
2715
2886
  children: "watoto",
2716
2887
  within: "ndani_ya",
@@ -2746,17 +2917,22 @@ var pt = {
2746
2917
  add: "adicionar",
2747
2918
  remove: "remover",
2748
2919
  toggle: "alternar",
2920
+ // command blur (the events section has the blur EVENT word; without this
2921
+ // entry the transformer fell back to it and no profile read it as the verb)
2922
+ blur: "desfocar",
2749
2923
  hide: "esconder",
2750
2924
  show: "mostrar",
2751
2925
  if: "se",
2752
- unless: "a_menos",
2926
+ unless: "salvo",
2927
+ // a_menos splits at _ in the pt extractor (see profile)
2753
2928
  repeat: "repetir",
2754
2929
  for: "para",
2755
2930
  while: "enquanto",
2756
2931
  until: "at\xE9",
2757
2932
  continue: "continuar",
2758
- break: "parar",
2759
- halt: "interromper",
2933
+ // un-crossed: the profile/tokenizer read parar=halt, interromper=break
2934
+ break: "interromper",
2935
+ halt: "parar",
2760
2936
  wait: "esperar",
2761
2937
  fetch: "buscar",
2762
2938
  call: "chamar",
@@ -2791,7 +2967,9 @@ var pt = {
2791
2967
  open: "abrir",
2792
2968
  select: "selecionar",
2793
2969
  clone: "clonar",
2794
- prepend: "preceder"
2970
+ prepend: "preceder",
2971
+ focus: "focar",
2972
+ socket: "soquete"
2795
2973
  },
2796
2974
  modifiers: {
2797
2975
  to: "para",
@@ -2915,7 +3093,7 @@ var pt = {
2915
3093
  prev: "ant",
2916
3094
  at: "em",
2917
3095
  random: "aleat\xF3rio",
2918
- closest: "mais_pr\xF3ximo",
3096
+ closest: "maispr\xF3ximo",
2919
3097
  parent: "pai",
2920
3098
  children: "filhos",
2921
3099
  within: "dentro",
@@ -2954,7 +3132,7 @@ var it = {
2954
3132
  hide: "nascondere",
2955
3133
  show: "mostrare",
2956
3134
  if: "se",
2957
- unless: "a meno che",
3135
+ unless: "salvo",
2958
3136
  repeat: "ripetere",
2959
3137
  for: "per",
2960
3138
  while: "mentre",
@@ -2987,7 +3165,8 @@ var it = {
2987
3165
  swap: "scambiare",
2988
3166
  morph: "trasformare",
2989
3167
  settle: "stabilizzare",
2990
- append: "aggiungere",
3168
+ append: "accodare",
3169
+ // profile primary; 'aggiungere' is add's word (parsed as add)
2991
3170
  exit: "uscire",
2992
3171
  install: "installare",
2993
3172
  breakpoint: "punto-interruzione",
@@ -2996,7 +3175,10 @@ var it = {
2996
3175
  open: "aprire",
2997
3176
  select: "selezionare",
2998
3177
  clone: "clonare",
2999
- prepend: "anteporre"
3178
+ prepend: "anteporre",
3179
+ // profile primary; without a commands entry the transformer fell back to
3180
+ // the EVENTS noun 'fuoco', which generated focus patterns can't match
3181
+ focus: "focalizzare"
3000
3182
  },
3001
3183
  modifiers: {
3002
3184
  to: "a",
@@ -3120,7 +3302,7 @@ var it = {
3120
3302
  prev: "prec",
3121
3303
  at: "a",
3122
3304
  random: "casuale",
3123
- closest: "pi\xF9vicino",
3305
+ closest: "vicino",
3124
3306
  parent: "genitore",
3125
3307
  children: "figli",
3126
3308
  within: "dentro",
@@ -3188,7 +3370,8 @@ var vi = {
3188
3370
  beep: "beep",
3189
3371
  js: "js",
3190
3372
  async: "b\u1EA5t \u0111\u1ED3ng b\u1ED9",
3191
- render: "hi\u1EC3n th\u1ECB",
3373
+ render: "k\u1EBFt xu\u1EA5t",
3374
+ // 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)
3192
3375
  swap: "ho\xE1n \u0111\u1ED5i",
3193
3376
  morph: "bi\u1EBFn \u0111\u1ED5i",
3194
3377
  settle: "\u1ED5n \u0111\u1ECBnh",
@@ -3203,7 +3386,8 @@ var vi = {
3203
3386
  prepend: "th\xEAm \u0111\u1EA7u",
3204
3387
  else: "kh\xF4ng th\xEC",
3205
3388
  breakpoint: "\u0111i\u1EC3m-d\u1EEBng",
3206
- clear: "x\xF3a",
3389
+ clear: "t\u1EA9y",
3390
+ // xóa is the remove PRIMARY in the profile
3207
3391
  close: "\u0111\xF3ng",
3208
3392
  open: "m\u1EDF",
3209
3393
  select: "ch\u1ECDn"
@@ -3267,7 +3451,7 @@ var vi = {
3267
3451
  else: "kh\xF4ng th\xEC",
3268
3452
  end: "k\u1EBFt th\xFAc",
3269
3453
  then: "r\u1ED3i",
3270
- live: "tr\u1EF1c ti\u1EBFp",
3454
+ live: "live",
3271
3455
  changes: "thay \u0111\u1ED5i"
3272
3456
  },
3273
3457
  temporal: {
@@ -3354,15 +3538,23 @@ var pl = {
3354
3538
  commands: {
3355
3539
  on: "gdy",
3356
3540
  tell: "powiedz",
3357
- trigger: "wywo\u0142aj",
3541
+ trigger: "wyzw\xF3l",
3542
+ // auditor: dict emitted a word the profile reads differently
3358
3543
  send: "wy\u015Blij",
3359
3544
  take: "we\u017A",
3360
3545
  put: "umie\u015B\u0107",
3361
3546
  set: "ustaw",
3362
- get: "pobierz",
3547
+ // `uzyskaj` (the semantic pl profile's get primary), not `pobierz`: pl `pobierz`
3548
+ // ("download") is the profile's FETCH primary, so emitting it for get made the
3549
+ // parser read every transformed get as fetch (get-value lossy + a phantom fetch).
3550
+ // Same dict↔profile homonym disambiguation as tr load/install, qu get.
3551
+ get: "uzyskaj",
3363
3552
  add: "dodaj",
3364
3553
  remove: "usu\u0144",
3365
3554
  toggle: "prze\u0142\u0105cz",
3555
+ // command blur (the events section has the blur EVENT word; without this
3556
+ // entry the transformer fell back to it and no profile read it as the verb)
3557
+ blur: "rozmyj",
3366
3558
  hide: "ukryj",
3367
3559
  show: "poka\u017C",
3368
3560
  if: "je\u015Bli",
@@ -3403,12 +3595,14 @@ var pl = {
3403
3595
  exit: "wyjd\u017A",
3404
3596
  install: "zainstaluj",
3405
3597
  breakpoint: "punkt-przerwania",
3406
- clear: "wyczy\u015B\u0107",
3598
+ clear: "zeruj",
3599
+ // wyczyść is a remove alternative in the profile
3407
3600
  close: "zamknij",
3408
3601
  open: "otw\xF3rz",
3409
3602
  select: "wybierz",
3410
3603
  clone: "sklonuj",
3411
- prepend: "poprzed\u017A"
3604
+ prepend: "poprzed\u017A",
3605
+ focus: "skup"
3412
3606
  },
3413
3607
  modifiers: {
3414
3608
  to: "do",
@@ -3559,7 +3753,8 @@ var russianDictionary = {
3559
3753
  commands: {
3560
3754
  on: "\u043F\u0440\u0438",
3561
3755
  tell: "\u0441\u043A\u0430\u0437\u0430\u0442\u044C",
3562
- trigger: "\u0432\u044B\u0437\u0432\u0430\u0442\u044C",
3756
+ trigger: "\u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u044C",
3757
+ // auditor: dict emitted a word the profile reads differently
3563
3758
  send: "\u043E\u0442\u043F\u0440\u0430\u0432\u0438\u0442\u044C",
3564
3759
  take: "\u0432\u0437\u044F\u0442\u044C",
3565
3760
  put: "\u043F\u043E\u043B\u043E\u0436\u0438\u0442\u044C",
@@ -3570,8 +3765,12 @@ var russianDictionary = {
3570
3765
  toggle: "\u043F\u0435\u0440\u0435\u043A\u043B\u044E\u0447\u0438\u0442\u044C",
3571
3766
  hide: "\u0441\u043A\u0440\u044B\u0442\u044C",
3572
3767
  show: "\u043F\u043E\u043A\u0430\u0437\u0430\u0442\u044C",
3768
+ // command verb; the events section keeps the noun прокрутка for `on scroll`.
3769
+ // Without this entry the transformer fell back to the event noun, which the
3770
+ // parser doesn't read as the scroll command (#321 focus family).
3771
+ scroll: "\u043F\u0440\u043E\u043A\u0440\u0443\u0442\u0438\u0442\u044C",
3573
3772
  if: "\u0435\u0441\u043B\u0438",
3574
- unless: "\u0435\u0441\u043B\u0438_\u043D\u0435",
3773
+ unless: "\u043A\u0440\u043E\u043C\u0435",
3575
3774
  repeat: "\u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u044C",
3576
3775
  for: "\u0434\u043B\u044F",
3577
3776
  while: "\u043F\u043E\u043A\u0430",
@@ -3604,10 +3803,12 @@ var russianDictionary = {
3604
3803
  swap: "\u043F\u043E\u043C\u0435\u043D\u044F\u0442\u044C",
3605
3804
  morph: "\u0442\u0440\u0430\u043D\u0441\u0444\u043E\u0440\u043C\u0438\u0440\u043E\u0432\u0430\u0442\u044C",
3606
3805
  settle: "\u0441\u0442\u0430\u0431\u0438\u043B\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u0442\u044C",
3607
- append: "\u0434\u043E\u0431\u0430\u0432\u0438\u0442\u044C_\u0432_\u043A\u043E\u043D\u0435\u0446",
3806
+ append: "\u0434\u043E\u043F\u0438\u0441\u0430\u0442\u044C",
3807
+ // profile alternative; the compound splits and добавить parses as add
3608
3808
  exit: "\u0432\u044B\u0439\u0442\u0438",
3609
3809
  else: "\u0438\u043D\u0430\u0447\u0435",
3610
- install: "\u0443\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C",
3810
+ install: "\u0438\u043D\u0441\u0442\u0430\u043B\u043B\u0438\u0440\u043E\u0432\u0430\u0442\u044C",
3811
+ // loanword — `установить` is `set` (homonym); see russian profile
3611
3812
  init: "\u0438\u043D\u0438\u0446\u0438\u0430\u043B\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u0442\u044C",
3612
3813
  behavior: "\u043F\u043E\u0432\u0435\u0434\u0435\u043D\u0438\u0435",
3613
3814
  focus: "\u0441\u0444\u043E\u043A\u0443\u0441\u0438\u0440\u043E\u0432\u0430\u0442\u044C",
@@ -3642,8 +3843,11 @@ var russianDictionary = {
3642
3843
  events: {
3643
3844
  click: "\u043A\u043B\u0438\u043A",
3644
3845
  dblclick: "\u0434\u0432\u043E\u0439\u043D\u043E\u0439_\u043A\u043B\u0438\u043A",
3645
- mousedown: "\u043C\u044B\u0448\u044C_\u0432\u043D\u0438\u0437",
3646
- mouseup: "\u043C\u044B\u0448\u044C_\u0432\u0432\u0435\u0440\u0445",
3846
+ // Fused (no `_`): the semantic tokenizer splits on `_`, so an underscore form
3847
+ // breaks event recognition (mousedown → bare expression). Mirrors the #510 tr
3848
+ // resize fix (boyut_değiştir → boyutlandırma) and the enyakın superlative.
3849
+ mousedown: "\u043C\u044B\u0448\u044C\u0432\u043D\u0438\u0437",
3850
+ mouseup: "\u043C\u044B\u0448\u044C\u0432\u0432\u0435\u0440\u0445",
3647
3851
  mouseenter: "\u043C\u044B\u0448\u044C_\u0432\u0445\u043E\u0434",
3648
3852
  mouseleave: "\u043C\u044B\u0448\u044C_\u0432\u044B\u0445\u043E\u0434",
3649
3853
  mouseover: "\u043D\u0430\u0432\u0435\u0434\u0435\u043D\u0438\u0435",
@@ -3660,7 +3864,8 @@ var russianDictionary = {
3660
3864
  reset: "\u0441\u0431\u0440\u043E\u0441",
3661
3865
  load: "\u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0430",
3662
3866
  unload: "\u0432\u044B\u0433\u0440\u0443\u0437\u043A\u0430",
3663
- resize: "\u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0435_\u0440\u0430\u0437\u043C\u0435\u0440\u0430",
3867
+ resize: "\u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u0435\u0440\u0430\u0437\u043C\u0435\u0440\u0430",
3868
+ // fused (no `_`) — see mousedown note above
3664
3869
  scroll: "\u043F\u0440\u043E\u043A\u0440\u0443\u0442\u043A\u0430",
3665
3870
  touchstart: "\u043A\u0430\u0441\u0430\u043D\u0438\u0435_\u043D\u0430\u0447\u0430\u043B\u043E",
3666
3871
  touchend: "\u043A\u0430\u0441\u0430\u043D\u0438\u0435_\u043A\u043E\u043D\u0435\u0446",
@@ -3790,7 +3995,8 @@ var ukrainianDictionary = {
3790
3995
  commands: {
3791
3996
  on: "\u043F\u0440\u0438",
3792
3997
  tell: "\u0441\u043A\u0430\u0437\u0430\u0442\u0438",
3793
- trigger: "\u0432\u0438\u043A\u043B\u0438\u043A\u0430\u0442\u0438",
3998
+ trigger: "\u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u0438",
3999
+ // auditor: dict emitted a word the profile reads differently
3794
4000
  send: "\u043D\u0430\u0434\u0456\u0441\u043B\u0430\u0442\u0438",
3795
4001
  take: "\u0432\u0437\u044F\u0442\u0438",
3796
4002
  put: "\u043F\u043E\u043A\u043B\u0430\u0441\u0442\u0438",
@@ -3801,8 +4007,12 @@ var ukrainianDictionary = {
3801
4007
  toggle: "\u043F\u0435\u0440\u0435\u043C\u043A\u043D\u0443\u0442\u0438",
3802
4008
  hide: "\u0441\u0445\u043E\u0432\u0430\u0442\u0438",
3803
4009
  show: "\u043F\u043E\u043A\u0430\u0437\u0430\u0442\u0438",
4010
+ // command verb; the events section keeps the noun прокрутка for `on scroll`.
4011
+ // Without this entry the transformer fell back to the event noun, which the
4012
+ // parser doesn't read as the scroll command (#321 focus family).
4013
+ scroll: "\u043F\u0440\u043E\u043A\u0440\u0443\u0442\u0438\u0442\u0438",
3804
4014
  if: "\u044F\u043A\u0449\u043E",
3805
- unless: "\u044F\u043A\u0449\u043E_\u043D\u0435",
4015
+ unless: "\u043A\u0440\u0456\u043C",
3806
4016
  repeat: "\u043F\u043E\u0432\u0442\u043E\u0440\u0438\u0442\u0438",
3807
4017
  for: "\u0434\u043B\u044F",
3808
4018
  while: "\u043F\u043E\u043A\u0438",
@@ -3835,10 +4045,12 @@ var ukrainianDictionary = {
3835
4045
  swap: "\u043F\u043E\u043C\u0456\u043D\u044F\u0442\u0438",
3836
4046
  morph: "\u0442\u0440\u0430\u043D\u0441\u0444\u043E\u0440\u043C\u0443\u0432\u0430\u0442\u0438",
3837
4047
  settle: "\u0441\u0442\u0430\u0431\u0456\u043B\u0456\u0437\u0443\u0432\u0430\u0442\u0438",
3838
- append: "\u0434\u043E\u0434\u0430\u0442\u0438_\u0432_\u043A\u0456\u043D\u0435\u0446\u044C",
4048
+ append: "\u0434\u043E\u043F\u0438\u0441\u0430\u0442\u0438",
4049
+ // profile alternative; the compound splits and додати parses as add
3839
4050
  exit: "\u0432\u0438\u0439\u0442\u0438",
3840
4051
  else: "\u0456\u043D\u0430\u043A\u0448\u0435",
3841
- install: "\u0432\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u0438",
4052
+ install: "\u0456\u043D\u0441\u0442\u0430\u043B\u044E\u0432\u0430\u0442\u0438",
4053
+ // loanword — `встановити` is `set` (homonym); see ukrainian profile
3842
4054
  init: "\u0456\u043D\u0456\u0446\u0456\u0430\u043B\u0456\u0437\u0443\u0432\u0430\u0442\u0438",
3843
4055
  behavior: "\u043F\u043E\u0432\u0435\u0434\u0456\u043D\u043A\u0430",
3844
4056
  focus: "\u0441\u0444\u043E\u043A\u0443\u0441\u0443\u0432\u0430\u0442\u0438",
@@ -3873,8 +4085,10 @@ var ukrainianDictionary = {
3873
4085
  events: {
3874
4086
  click: "\u043A\u043B\u0456\u043A",
3875
4087
  dblclick: "\u043F\u043E\u0434\u0432\u0456\u0439\u043D\u0438\u0439_\u043A\u043B\u0456\u043A",
3876
- mousedown: "\u043C\u0438\u0448\u0430_\u0432\u043D\u0438\u0437",
3877
- mouseup: "\u043C\u0438\u0448\u0430_\u0432\u0433\u043E\u0440\u0443",
4088
+ // Fused (no `_`): the semantic tokenizer splits on `_`, so an underscore form
4089
+ // breaks event recognition. Mirrors the #510 tr resize fix + enyakın.
4090
+ mousedown: "\u043C\u0438\u0448\u0430\u0432\u043D\u0438\u0437",
4091
+ mouseup: "\u043C\u0438\u0448\u0430\u0432\u0433\u043E\u0440\u0443",
3878
4092
  mouseenter: "\u043C\u0438\u0448\u0430_\u0432\u0445\u0456\u0434",
3879
4093
  mouseleave: "\u043C\u0438\u0448\u0430_\u0432\u0438\u0445\u0456\u0434",
3880
4094
  mouseover: "\u043D\u0430\u0432\u0435\u0434\u0435\u043D\u043D\u044F",
@@ -3891,7 +4105,8 @@ var ukrainianDictionary = {
3891
4105
  reset: "\u0441\u043A\u0438\u0434\u0430\u043D\u043D\u044F",
3892
4106
  load: "\u0437\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F",
3893
4107
  unload: "\u0432\u0438\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F",
3894
- resize: "\u0437\u043C\u0456\u043D\u0430_\u0440\u043E\u0437\u043C\u0456\u0440\u0443",
4108
+ resize: "\u0437\u043C\u0456\u043D\u0430\u0440\u043E\u0437\u043C\u0456\u0440\u0443",
4109
+ // fused (no `_`) — see mousedown note above
3895
4110
  scroll: "\u043F\u0440\u043E\u043A\u0440\u0443\u0442\u043A\u0430",
3896
4111
  touchstart: "\u0434\u043E\u0442\u0438\u043A_\u043F\u043E\u0447\u0430\u0442\u043E\u043A",
3897
4112
  touchend: "\u0434\u043E\u0442\u0438\u043A_\u043A\u0456\u043D\u0435\u0446\u044C",
@@ -4033,10 +4248,12 @@ var hindiDictionary = {
4033
4248
  hide: "\u091B\u093F\u092A\u093E\u090F\u0902",
4034
4249
  show: "\u0926\u093F\u0916\u093E\u090F\u0902",
4035
4250
  if: "\u0905\u0917\u0930",
4036
- unless: "\u091C\u092C_\u0924\u0915_\u0928\u0939\u0940\u0902",
4251
+ unless: "\u091C\u092C \u0924\u0915 \u0928\u0939\u0940\u0902",
4252
+ // spaced (not जब_तक_नहीं) → one multi-word `unless` token
4037
4253
  repeat: "\u0926\u094B\u0939\u0930\u093E\u090F\u0902",
4038
- for: "\u0915\u0947_\u0932\u093F\u090F",
4039
- while: "\u091C\u092C_\u0924\u0915",
4254
+ for: "\u0939\u0947\u0924\u0941",
4255
+ // single token — के_लिए splits at _ in the hi extractor
4256
+ while: "\u091C\u092C \u0924\u0915",
4040
4257
  until: "\u0924\u0915",
4041
4258
  continue: "\u091C\u093E\u0930\u0940",
4042
4259
  break: "\u0930\u094B\u0915\u0947\u0902",
@@ -4063,10 +4280,10 @@ var hindiDictionary = {
4063
4280
  js: "\u091C\u0947\u090F\u0938",
4064
4281
  async: "\u0905\u0938\u093F\u0902\u0915",
4065
4282
  render: "\u0930\u0947\u0902\u0921\u0930",
4066
- swap: "\u092C\u0926\u0932\u0947\u0902",
4283
+ swap: "\u0935\u093F\u0928\u093F\u092E\u092F",
4067
4284
  morph: "\u0930\u0942\u092A\u093E\u0902\u0924\u0930",
4068
4285
  settle: "\u0938\u094D\u0925\u093F\u0930",
4069
- append: "\u091C\u094B\u0921\u093C\u0947\u0902_\u0905\u0902\u0924",
4286
+ append: "\u0938\u0902\u0932\u0917\u094D\u0928",
4070
4287
  exit: "\u092C\u093E\u0939\u0930",
4071
4288
  else: "\u0935\u0930\u0928\u093E",
4072
4289
  install: "\u0907\u0902\u0938\u094D\u091F\u0949\u0932",
@@ -4080,7 +4297,8 @@ var hindiDictionary = {
4080
4297
  clear: "\u0938\u093E\u092B\u093C-\u0915\u0930\u0947\u0902",
4081
4298
  close: "\u092C\u0902\u0926-\u0915\u0930\u0947\u0902",
4082
4299
  open: "\u0916\u094B\u0932\u0947\u0902",
4083
- select: "\u091A\u0941\u0928\u0947\u0902"
4300
+ select: "\u091A\u0941\u0928\u0947\u0902",
4301
+ socket: "\u0938\u0949\u0915\u0947\u091F"
4084
4302
  },
4085
4303
  modifiers: {
4086
4304
  to: "\u0915\u094B",
@@ -4092,8 +4310,8 @@ var hindiDictionary = {
4092
4310
  of: "\u0915\u093E",
4093
4311
  as: "\u0915\u0947_\u0930\u0942\u092A_\u092E\u0947\u0902",
4094
4312
  by: "\u0926\u094D\u0935\u093E\u0930\u093E",
4095
- before: "\u0938\u0947_\u092A\u0939\u0932\u0947",
4096
- after: "\u0915\u0947_\u092C\u093E\u0926",
4313
+ before: "\u0938\u0947 \u092A\u0939\u0932\u0947",
4314
+ after: "\u0915\u0947 \u092C\u093E\u0926",
4097
4315
  over: "\u0915\u0947_\u090A\u092A\u0930",
4098
4316
  under: "\u0915\u0947_\u0928\u0940\u091A\u0947",
4099
4317
  between: "\u0915\u0947_\u092C\u0940\u091A",
@@ -4104,8 +4322,10 @@ var hindiDictionary = {
4104
4322
  events: {
4105
4323
  click: "\u0915\u094D\u0932\u093F\u0915",
4106
4324
  dblclick: "\u0921\u092C\u0932_\u0915\u094D\u0932\u093F\u0915",
4107
- mousedown: "\u092E\u093E\u0909\u0938_\u0928\u0940\u091A\u0947",
4108
- mouseup: "\u092E\u093E\u0909\u0938_\u090A\u092A\u0930",
4325
+ // Fused (no `_`): the semantic tokenizer splits on `_`, breaking event
4326
+ // recognition (mousedown → "नीचे"). Mirrors the #535 ru/uk fuse.
4327
+ mousedown: "\u092E\u093E\u0909\u0938\u0928\u0940\u091A\u0947",
4328
+ mouseup: "\u092E\u093E\u0909\u0938\u090A\u092A\u0930",
4109
4329
  mouseenter: "\u092E\u093E\u0909\u0938_\u092A\u094D\u0930\u0935\u0947\u0936",
4110
4330
  mouseleave: "\u092E\u093E\u0909\u0938_\u092C\u093E\u0939\u0930",
4111
4331
  mouseover: "\u092E\u093E\u0909\u0938_\u0913\u0935\u0930",
@@ -4137,7 +4357,11 @@ var hindiDictionary = {
4137
4357
  not: "\u0928\u0939\u0940\u0902",
4138
4358
  is: "\u0939\u0948",
4139
4359
  exists: "\u092E\u094C\u091C\u0942\u0926",
4140
- matches: "\u092E\u0947\u0932_\u0916\u093E\u0924\u093E",
4360
+ // Natural spaced phrase `मेल खाता` ("matches") — how Hindi writes it. The base
4361
+ // tokenizer now matches multi-word profile keywords, so the hindi profile maps
4362
+ // `मेल खाता`→matches without the underscore (`मेल_खाता` split) or the
4363
+ // concatenated `मेलखाता` (parsed, but unnatural) workarounds.
4364
+ matches: "\u092E\u0947\u0932 \u0916\u093E\u0924\u093E",
4141
4365
  contains: "\u0936\u093E\u092E\u093F\u0932",
4142
4366
  includes: "\u092E\u0947\u0902_\u0939\u0948",
4143
4367
  equals: "\u092C\u0930\u093E\u092C\u0930",
@@ -4279,7 +4503,7 @@ var bengaliDictionary = {
4279
4503
  throw: "\u09A8\u09BF\u0995\u09CD\u09B7\u09C7\u09AA",
4280
4504
  continue: "\u099A\u09BE\u09B2\u09BF\u09AF\u09BC\u09C7 \u09AF\u09BE\u09A8",
4281
4505
  make: "\u09A4\u09C8\u09B0\u09BF \u0995\u09B0\u09C1\u09A8",
4282
- append: "\u09B6\u09C7\u09B7\u09C7 \u09AF\u09CB\u0997",
4506
+ append: "\u099C\u09C1\u09A1\u09BC\u09C1\u09A8",
4283
4507
  prepend: "\u09B6\u09C1\u09B0\u09C1\u09A4\u09C7 \u09AF\u09CB\u0997",
4284
4508
  focus: "\u09AB\u09CB\u0995\u09BE\u09B8",
4285
4509
  blur: "\u099D\u09BE\u09AA\u09B8\u09BE",
@@ -4307,7 +4531,8 @@ var bengaliDictionary = {
4307
4531
  clear: "\u09AA\u09B0\u09BF\u09B7\u09CD\u0995\u09BE\u09B0",
4308
4532
  close: "\u09AC\u09A8\u09CD\u09A7",
4309
4533
  open: "\u0996\u09C1\u09B2\u09C1\u09A8",
4310
- select: "\u09A8\u09BF\u09B0\u09CD\u09AC\u09BE\u099A\u09A8"
4534
+ select: "\u09A8\u09BF\u09B0\u09CD\u09AC\u09BE\u099A\u09A8",
4535
+ socket: "\u09B8\u0995\u09C7\u099F"
4311
4536
  },
4312
4537
  modifiers: {
4313
4538
  to: "\u0995\u09C7",
@@ -4366,6 +4591,7 @@ var bengaliDictionary = {
4366
4591
  end: "\u09B6\u09C7\u09B7",
4367
4592
  then: "\u09A4\u09BE\u09B0\u09AA\u09B0",
4368
4593
  live: "\u09B2\u09BE\u0987\u09AD",
4594
+ bind: "\u09AC\u09BE\u0987\u09A8\u09CD\u09A1",
4369
4595
  changes: "\u09AA\u09B0\u09BF\u09AC\u09B0\u09CD\u09A4\u09BF\u09A4 \u09B9\u09B2\u09C7"
4370
4596
  },
4371
4597
  temporal: {
@@ -4465,7 +4691,9 @@ var thaiDictionary = {
4465
4691
  prepend: "\u0E40\u0E1E\u0E34\u0E48\u0E21\u0E2B\u0E19\u0E49\u0E32",
4466
4692
  focus: "\u0E42\u0E1F\u0E01\u0E31\u0E2A",
4467
4693
  blur: "\u0E40\u0E1A\u0E25\u0E2D",
4468
- transition: "\u0E40\u0E1B\u0E25\u0E35\u0E48\u0E22\u0E19",
4694
+ // เปลี่ยนผ่าน, not เปลี่ยน: the semantic th profile's `change` keyword is
4695
+ // เปลี่ยน, so a เปลี่ยน render could never anchor a transition command.
4696
+ transition: "\u0E40\u0E1B\u0E25\u0E35\u0E48\u0E22\u0E19\u0E1C\u0E48\u0E32\u0E19",
4469
4697
  settle: "\u0E04\u0E07\u0E17\u0E35\u0E48",
4470
4698
  measure: "\u0E27\u0E31\u0E14",
4471
4699
  async: "\u0E2D\u0E30\u0E0B\u0E34\u0E07\u0E04\u0E4C",
@@ -4672,10 +4900,13 @@ var malayDictionary = {
4672
4900
  init: "mula",
4673
4901
  clone: "klon",
4674
4902
  breakpoint: "titik-henti",
4675
- clear: "padam",
4903
+ clear: "bersihkan",
4904
+ // padam is a remove alternative in the profile
4676
4905
  close: "tutup",
4677
4906
  open: "buka",
4678
- select: "pilih"
4907
+ select: "pilih",
4908
+ socket: "soket"
4909
+ // derived from the ms profile (socket.primary); see generate-i18n-dictionaries
4679
4910
  },
4680
4911
  modifiers: {
4681
4912
  to: "ke",
@@ -4852,10 +5083,14 @@ var tagalogDictionary = {
4852
5083
  tell: "sabihin",
4853
5084
  trigger: "palitawin",
4854
5085
  send: "ipadala",
4855
- take: "kunin",
5086
+ take: "kumuha",
5087
+ // auditor: dict emitted a word the profile reads differently
4856
5088
  put: "ilagay",
4857
5089
  set: "itakda",
4858
- get: "kuhanin",
5090
+ // Align with the semantic tl profile's `get` primary (`kunin`); the previous
5091
+ // `kuhanin` is the base of fetch's `kuhanin_mula`, so the transformed `get`
5092
+ // dropped (get-value parsed degenerate). Same dict↔profile alignment as id `toggle`.
5093
+ get: "kunin",
4859
5094
  add: "idagdag",
4860
5095
  remove: "alisin",
4861
5096
  toggle: "palitan",
@@ -4879,7 +5114,7 @@ var tagalogDictionary = {
4879
5114
  throw: "ihagis",
4880
5115
  catch: "hulihin",
4881
5116
  measure: "sukatin",
4882
- transition: "baguhin",
5117
+ transition: "lumipat",
4883
5118
  increment: "dagdagan",
4884
5119
  decrement: "bawasan",
4885
5120
  default: "pamantayan",
@@ -4891,10 +5126,13 @@ var tagalogDictionary = {
4891
5126
  beep: "tunog",
4892
5127
  js: "js",
4893
5128
  async: "sabay",
4894
- render: "ipakita",
5129
+ render: "irender",
5130
+ // auditor: dict emitted a word the profile reads differently
4895
5131
  swap: "palitan_pwesto",
4896
- morph: "baguhin_hugis",
4897
- settle: "ayusin",
5132
+ morph: "baguhin",
5133
+ // auditor: dict emitted a word the profile reads differently
5134
+ settle: "magpatahimik",
5135
+ // auditor: dict emitted a word the profile reads differently
4898
5136
  append: "idagdag_sa_dulo",
4899
5137
  prepend: "idagdag_sa_simula",
4900
5138
  exit: "lumabas",
@@ -4922,7 +5160,11 @@ var tagalogDictionary = {
4922
5160
  as: "bilang",
4923
5161
  by: "sa_pamamagitan_ng",
4924
5162
  before: "bago",
4925
- after: "pagkatapos",
5163
+ // `matapos` (not `pagkatapos`) for the positional "after": `pagkatapos` is
5164
+ // also Tagalog "then", so emitting it for the position modifier collides with
5165
+ // the sequence connector. `matapos` is the unambiguous positional form and
5166
+ // matches the semantic tl profile's `after`.
5167
+ after: "matapos",
4926
5168
  over: "sa_ibabaw",
4927
5169
  under: "sa_ilalim",
4928
5170
  between: "sa_pagitan",
@@ -5088,6 +5330,8 @@ var he = {
5088
5330
  beep: "\u05D1\u05D9\u05E4",
5089
5331
  break: "\u05E9\u05D1\u05D5\u05E8",
5090
5332
  call: "\u05E7\u05E8\u05D0",
5333
+ clear: "\u05E0\u05E7\u05D4",
5334
+ // profile's clear word — was untranslated (parsed as nothing)
5091
5335
  continue: "\u05D4\u05DE\u05E9\u05DA",
5092
5336
  copy: "\u05D4\u05E2\u05EA\u05E7",
5093
5337
  decrement: "\u05D4\u05E7\u05D8\u05DF",
@@ -5100,6 +5344,7 @@ var he = {
5100
5344
  halt: "\u05E2\u05E6\u05D5\u05E8",
5101
5345
  hide: "\u05D4\u05E1\u05EA\u05E8",
5102
5346
  if: "\u05D0\u05DD",
5347
+ unless: "\u05D0\u05DC\u05D0",
5103
5348
  increment: "\u05D4\u05D2\u05D3\u05DC",
5104
5349
  install: "\u05D4\u05EA\u05E7\u05DF",
5105
5350
  js: "js",
@@ -5114,6 +5359,7 @@ var he = {
5114
5359
  render: "\u05E8\u05E0\u05D3\u05E8",
5115
5360
  repeat: "\u05D7\u05D6\u05D5\u05E8",
5116
5361
  return: "\u05D4\u05D7\u05D6\u05E8",
5362
+ scroll: "\u05D2\u05DC\u05D5\u05DC",
5117
5363
  send: "\u05E9\u05DC\u05D7",
5118
5364
  set: "\u05E7\u05D1\u05E2",
5119
5365
  settle: "\u05D4\u05EA\u05D9\u05D9\u05E6\u05D1",
@@ -5151,6 +5397,7 @@ var he = {
5151
5397
  end: "\u05E1\u05D5\u05E3",
5152
5398
  then: "\u05D0\u05D6",
5153
5399
  live: "\u05D7\u05D9",
5400
+ bind: "\u05E7\u05E9\u05D5\u05E8",
5154
5401
  changes: "\u05DE\u05E9\u05EA\u05E0\u05D4",
5155
5402
  when: "\u05DB\u05D0\u05E9\u05E8",
5156
5403
  where: "\u05D0\u05D9\u05E4\u05D4"
@@ -5169,7 +5416,9 @@ var he = {
5169
5416
  your: "\u05E9\u05DC\u05DA"
5170
5417
  },
5171
5418
  attributes: {},
5172
- expressions: {}
5419
+ expressions: {
5420
+ last: "\u05D0\u05D7\u05E8\u05D5\u05DF"
5421
+ }
5173
5422
  };
5174
5423
 
5175
5424
  // src/parser/he.ts
@@ -5350,6 +5599,13 @@ var en = {
5350
5599
  increment: "increment",
5351
5600
  decrement: "decrement",
5352
5601
  default: "default",
5602
+ // Reactivity Commands
5603
+ bind: "bind",
5604
+ // Realtime / service-worker Commands
5605
+ eventsource: "eventsource",
5606
+ socket: "socket",
5607
+ worker: "worker",
5608
+ intercept: "intercept",
5353
5609
  // Navigation Commands
5354
5610
  go: "go",
5355
5611
  pushUrl: "pushUrl",
@@ -5787,7 +6043,13 @@ function reorderRoles(roles, targetOrder) {
5787
6043
  function insertMarkers(elements, markers, adpositionType) {
5788
6044
  const result = [];
5789
6045
  for (const element of elements) {
5790
- const marker = markers.find((m) => m.role === element.role);
6046
+ let marker = markers.find((m) => m.role === element.role);
6047
+ if (marker?.role === "event" && /^[#.<@[]/.test(element.translated || element.value)) {
6048
+ marker = void 0;
6049
+ }
6050
+ if (marker?.role === "duration" && /^(me|you|it)$/i.test(element.value)) {
6051
+ marker = void 0;
6052
+ }
5791
6053
  if (marker) {
5792
6054
  if (adpositionType === "preposition") {
5793
6055
  if (marker.form) result.push(marker.form);
@@ -5911,6 +6173,41 @@ var japaneseProfile = {
5911
6173
  roleOrder: ["patient", "destination", "action"],
5912
6174
  insertMarkers: true
5913
6175
  }
6176
+ },
6177
+ {
6178
+ name: "set-to",
6179
+ description: "Transform set X to Y to Japanese verb-final order",
6180
+ priority: 90,
6181
+ match: {
6182
+ commands: ["set", "\u8A2D\u5B9A"],
6183
+ requiredRoles: ["action", "patient", "destination"],
6184
+ // Skip a set whose value swept up a trailing block terminator (`set X to Y
6185
+ // end` — the body of an inline `if … then set … end`): verb-final reorder
6186
+ // would push the verb past `end` and break the block. These parse fine
6187
+ // verb-medial (the default).
6188
+ predicate: (parsed) => !/(^|\s)end$/i.test((parsed.original ?? "").trim())
6189
+ },
6190
+ transform: {
6191
+ // X を Y に 設定 — verb-final, matching the generated SOV set pattern
6192
+ // (markerOverride: を on the destination / に on the value). Default reorder
6193
+ // emitted verb-MEDIAL (`X を 設定 Y に`), unmatched by any set pattern.
6194
+ roleOrder: ["patient", "destination", "action"],
6195
+ insertMarkers: true
6196
+ }
6197
+ },
6198
+ {
6199
+ name: "bind-to",
6200
+ description: "Transform bind $var to #el to Japanese verb-final order",
6201
+ priority: 90,
6202
+ match: {
6203
+ commands: ["bind", "\u30D0\u30A4\u30F3\u30C9"],
6204
+ requiredRoles: ["action", "patient", "destination"]
6205
+ },
6206
+ transform: {
6207
+ // $var を #el に バインド
6208
+ roleOrder: ["patient", "destination", "action"],
6209
+ insertMarkers: true
6210
+ }
5914
6211
  }
5915
6212
  ]
5916
6213
  };
@@ -5927,6 +6224,10 @@ var koreanProfile = {
5927
6224
  { form: "\uB97C", role: "patient", position: "postposition", required: true, alternatives: ["\uC744"] },
5928
6225
  { form: "\uC5D0", role: "destination", position: "postposition", required: true },
5929
6226
  { form: "\uC5D0\uC11C", role: "source", position: "postposition", required: true },
6227
+ // Event marker — the semantic *-event-ko-sov-* patterns anchor on 할 때
6228
+ // (the generator profile's eventMarker); without it every ko handler
6229
+ // emitted a bare event name no fused pattern could anchor.
6230
+ { form: "\uD560 \uB54C", role: "event", position: "postposition", required: true },
5930
6231
  {
5931
6232
  form: "\uB85C",
5932
6233
  role: "style",
@@ -5957,6 +6258,56 @@ var koreanProfile = {
5957
6258
  roleOrder: ["patient", "event", "action"],
5958
6259
  insertMarkers: true
5959
6260
  }
6261
+ },
6262
+ {
6263
+ name: "put-into",
6264
+ description: "Transform put X into Y to Korean verb-final order",
6265
+ priority: 90,
6266
+ match: {
6267
+ commands: ["put", "\uB123\uB2E4"],
6268
+ requiredRoles: ["action", "patient", "destination"],
6269
+ // Standalone put only (e.g. a then-chain clause): an event handler whose
6270
+ // action is `put` must keep the event mid-stream, so don't verb-final it.
6271
+ predicate: (parsed) => !parsed.roles.has("event")
6272
+ },
6273
+ transform: {
6274
+ // X 를 Y 에 넣다 (patient, destination, verb-last)
6275
+ roleOrder: ["patient", "destination", "action"],
6276
+ insertMarkers: true
6277
+ }
6278
+ },
6279
+ {
6280
+ name: "set-to",
6281
+ description: "Transform set X to Y to Korean verb-final order",
6282
+ priority: 90,
6283
+ match: {
6284
+ commands: ["set", "\uC124\uC815"],
6285
+ requiredRoles: ["action", "patient", "destination"],
6286
+ // Skip a set whose value swept up a trailing block terminator (`set X to Y
6287
+ // end` — the body of an inline `if … then set … end`): verb-final reorder
6288
+ // would push the verb past `end` and break the block. These parse fine
6289
+ // verb-medial (the default).
6290
+ predicate: (parsed) => !/(^|\s)end$/i.test((parsed.original ?? "").trim())
6291
+ },
6292
+ transform: {
6293
+ // X 를 Y 에 설정 — verb-final, matching the generated SOV set pattern.
6294
+ roleOrder: ["patient", "destination", "action"],
6295
+ insertMarkers: true
6296
+ }
6297
+ },
6298
+ {
6299
+ name: "bind-to",
6300
+ description: "Transform bind $var to #el to Korean verb-final order",
6301
+ priority: 90,
6302
+ match: {
6303
+ commands: ["bind", "\uBC14\uC778\uB4DC"],
6304
+ requiredRoles: ["action", "patient", "destination"]
6305
+ },
6306
+ transform: {
6307
+ // $var 를 #el 에 바인드
6308
+ roleOrder: ["patient", "destination", "action"],
6309
+ insertMarkers: true
6310
+ }
5960
6311
  }
5961
6312
  ]
5962
6313
  };
@@ -5992,18 +6343,23 @@ var chineseProfile = {
5992
6343
  requiredRoles: ["event", "action"]
5993
6344
  },
5994
6345
  transform: {
5995
- // 当 点击 时 增加 #count
6346
+ // 当 EVENT 时 ACTION [patient] [destination] [source] [style] [method] ...
5996
6347
  roleOrder: ["event", "action", "patient"],
5997
6348
  insertMarkers: true,
5998
- custom: (parsed, _profile) => {
6349
+ custom: (parsed, profile) => {
5999
6350
  const event = parsed.roles.get("event");
6000
6351
  const action = parsed.roles.get("action");
6001
- const patient = parsed.roles.get("patient");
6002
- const parts = ["\u5F53", event?.translated || event?.value, "\u65F6"];
6003
- parts.push(action?.translated || action?.value || "");
6004
- if (patient) {
6005
- parts.push(patient.translated || patient.value);
6006
- }
6352
+ const remaining = new Map(parsed.roles);
6353
+ remaining.delete("event");
6354
+ remaining.delete("action");
6355
+ const remainingOrder = (profile.canonicalOrder || []).filter(
6356
+ (r) => r !== "event" && r !== "action"
6357
+ );
6358
+ const reordered = reorderRoles(remaining, remainingOrder);
6359
+ const markedTokens = insertMarkers(reordered, profile.markers, profile.adpositionType);
6360
+ const parts = ["\u5F53", event?.translated || event?.value || "", "\u65F6"];
6361
+ if (action) parts.push(action.translated || action.value);
6362
+ parts.push(...markedTokens);
6007
6363
  return parts.filter(Boolean).join(" ");
6008
6364
  }
6009
6365
  }
@@ -6021,6 +6377,30 @@ var chineseProfile = {
6021
6377
  roleOrder: ["patient", "action", "destination"],
6022
6378
  insertMarkers: true
6023
6379
  }
6380
+ },
6381
+ {
6382
+ name: "bind-to",
6383
+ description: "Bind binds a variable to an element: \u7ED1\u5B9A X \u5230 Y (no \u628A)",
6384
+ priority: 90,
6385
+ match: {
6386
+ commands: ["bind", "\u7ED1\u5B9A"],
6387
+ requiredRoles: ["action", "patient", "destination"]
6388
+ },
6389
+ transform: {
6390
+ // Plain SVO without the 把 object-fronting: the bound variable is the
6391
+ // semantic destination (not a fronted patient), and `到` marks the
6392
+ // target element. Custom so the patient does not pick up 把.
6393
+ roleOrder: ["action", "patient", "destination"],
6394
+ custom: (parsed) => {
6395
+ const action = parsed.roles.get("action");
6396
+ const patient = parsed.roles.get("patient");
6397
+ const destination = parsed.roles.get("destination");
6398
+ const verb = action?.translated || action?.value || "";
6399
+ const v = patient?.translated || patient?.value || "";
6400
+ const d = destination?.translated || destination?.value || "";
6401
+ return [verb, v, "\u5230", d].filter(Boolean).join(" ");
6402
+ }
6403
+ }
6024
6404
  }
6025
6405
  ]
6026
6406
  };
@@ -6112,6 +6492,56 @@ var turkishProfile = {
6112
6492
  roleOrder: ["patient", "event", "action"],
6113
6493
  insertMarkers: true
6114
6494
  }
6495
+ },
6496
+ {
6497
+ name: "put-into",
6498
+ description: "Transform put X into Y to Turkish verb-final order",
6499
+ priority: 90,
6500
+ match: {
6501
+ commands: ["put", "koy"],
6502
+ requiredRoles: ["action", "patient", "destination"],
6503
+ // Standalone put only (e.g. a then-chain clause): an event handler whose
6504
+ // action is `put` must keep the event mid-stream, so don't verb-final it.
6505
+ predicate: (parsed) => !parsed.roles.has("event")
6506
+ },
6507
+ transform: {
6508
+ // X i Y e koy (patient, destination, verb-last)
6509
+ roleOrder: ["patient", "destination", "action"],
6510
+ insertMarkers: true
6511
+ }
6512
+ },
6513
+ {
6514
+ name: "set-to",
6515
+ description: "Transform set X to Y to Turkish verb-final order",
6516
+ priority: 90,
6517
+ match: {
6518
+ commands: ["set", "ayarla"],
6519
+ requiredRoles: ["action", "patient", "destination"],
6520
+ // Skip a set whose value swept up a trailing block terminator (`set X to Y
6521
+ // end` — the body of an inline `if … then set … end`): verb-final reorder
6522
+ // would push the verb past `end` and break the block. These parse fine
6523
+ // verb-medial (the default).
6524
+ predicate: (parsed) => !/(^|\s)end$/i.test((parsed.original ?? "").trim())
6525
+ },
6526
+ transform: {
6527
+ // X i Y e ayarla — verb-final, matching the generated SOV set pattern.
6528
+ roleOrder: ["patient", "destination", "action"],
6529
+ insertMarkers: true
6530
+ }
6531
+ },
6532
+ {
6533
+ name: "bind-to",
6534
+ description: "Transform bind $var to #el to Turkish verb-final order",
6535
+ priority: 90,
6536
+ match: {
6537
+ commands: ["bind"],
6538
+ requiredRoles: ["action", "patient", "destination"]
6539
+ },
6540
+ transform: {
6541
+ // $var i #el e bağla
6542
+ roleOrder: ["patient", "destination", "action"],
6543
+ insertMarkers: true
6544
+ }
6115
6545
  }
6116
6546
  ]
6117
6547
  };
@@ -6208,6 +6638,25 @@ var indonesianProfile = {
6208
6638
  { form: "sebagai", role: "method", position: "preposition", required: false }
6209
6639
  ]
6210
6640
  };
6641
+ var malayProfile = {
6642
+ code: "ms",
6643
+ name: "Bahasa Melayu",
6644
+ wordOrder: "SVO",
6645
+ adpositionType: "preposition",
6646
+ morphology: "agglutinative",
6647
+ direction: "ltr",
6648
+ canonicalOrder: ["event", "action", "patient", "destination"],
6649
+ markers: [
6650
+ // ms marks the event clause with `apabila` ("when/on") — matching the semantic
6651
+ // ms event-handler pattern (`apabila {event} {body}`) and the ms dict's
6652
+ // `on: apabila`. (Indonesian uses `pada` here; the two diverge on the event head.)
6653
+ { form: "apabila", role: "event", position: "preposition", required: true },
6654
+ { form: "ke", role: "destination", position: "preposition", required: false },
6655
+ { form: "dari", role: "source", position: "preposition", required: false },
6656
+ { form: "dengan", role: "style", position: "preposition", required: false },
6657
+ { form: "sebagai", role: "method", position: "preposition", required: false }
6658
+ ]
6659
+ };
6211
6660
  var quechuaProfile = {
6212
6661
  code: "qu",
6213
6662
  name: "Runasimi",
@@ -6243,8 +6692,11 @@ var swahiliProfile = {
6243
6692
  { form: "kwa", role: "destination", position: "preposition", required: false },
6244
6693
  { form: "kutoka", role: "source", position: "preposition", required: false },
6245
6694
  { form: "na", role: "style", position: "preposition", required: false },
6246
- { form: "kama", role: "method", position: "preposition", required: false }
6247
- // "as/like"
6695
+ // `kuwa`, not `kama`: sw `kama` is the IF keyword — rendering the method
6696
+ // role ("as X") with it grew a phantom `if` at semantic parse time. See the
6697
+ // matching comment in dictionaries/sw.ts (modifiers.as).
6698
+ { form: "kuwa", role: "method", position: "preposition", required: false }
6699
+ // "as/become"
6248
6700
  ]
6249
6701
  };
6250
6702
  var bengaliProfile = {
@@ -6281,6 +6733,56 @@ var bengaliProfile = {
6281
6733
  roleOrder: ["patient", "event", "action"],
6282
6734
  insertMarkers: true
6283
6735
  }
6736
+ },
6737
+ {
6738
+ name: "put-into",
6739
+ description: "Transform put X into Y to Bengali verb-final order",
6740
+ priority: 90,
6741
+ match: {
6742
+ commands: ["put", "\u09B0\u09BE\u0996\u09C1\u09A8"],
6743
+ requiredRoles: ["action", "patient", "destination"],
6744
+ // Standalone put only (e.g. a then-chain clause): an event handler whose
6745
+ // action is `put` must keep the event mid-stream, so don't verb-final it.
6746
+ predicate: (parsed) => !parsed.roles.has("event")
6747
+ },
6748
+ transform: {
6749
+ // X কে Y তে রাখুন (patient, destination, verb-last)
6750
+ roleOrder: ["patient", "destination", "action"],
6751
+ insertMarkers: true
6752
+ }
6753
+ },
6754
+ {
6755
+ name: "set-to",
6756
+ description: "Transform set X to Y to Bengali verb-final order",
6757
+ priority: 90,
6758
+ match: {
6759
+ commands: ["set", "\u09B8\u09C7\u099F"],
6760
+ requiredRoles: ["action", "patient", "destination"],
6761
+ // Skip a set whose value swept up a trailing block terminator (`set X to Y
6762
+ // end` — the body of an inline `if … then set … end`): verb-final reorder
6763
+ // would push the verb past `end` and break the block. These parse fine
6764
+ // verb-medial (the default).
6765
+ predicate: (parsed) => !/(^|\s)end$/i.test((parsed.original ?? "").trim())
6766
+ },
6767
+ transform: {
6768
+ // X কে Y তে সেট — verb-final, matching the generated SOV set pattern.
6769
+ roleOrder: ["patient", "destination", "action"],
6770
+ insertMarkers: true
6771
+ }
6772
+ },
6773
+ {
6774
+ name: "bind-to",
6775
+ description: "Transform bind $var to #el to Bengali verb-final order",
6776
+ priority: 90,
6777
+ match: {
6778
+ commands: ["bind", "\u09AC\u09BE\u0987\u09A8\u09CD\u09A1"],
6779
+ requiredRoles: ["action", "patient", "destination"]
6780
+ },
6781
+ transform: {
6782
+ // $var কে #el তে বাইন্ড
6783
+ roleOrder: ["patient", "destination", "action"],
6784
+ insertMarkers: true
6785
+ }
6284
6786
  }
6285
6787
  ]
6286
6788
  };
@@ -6398,6 +6900,61 @@ var hindiProfile = {
6398
6900
  roleOrder: ["patient", "event", "action"],
6399
6901
  insertMarkers: true
6400
6902
  }
6903
+ },
6904
+ {
6905
+ name: "put-into",
6906
+ description: "Transform put X into Y to Hindi verb-final order",
6907
+ priority: 90,
6908
+ match: {
6909
+ commands: ["put", "\u0930\u0916\u0947\u0902"],
6910
+ requiredRoles: ["action", "patient", "destination"]
6911
+ },
6912
+ transform: {
6913
+ // "hi" को #out में रखें
6914
+ roleOrder: ["patient", "destination", "action"],
6915
+ insertMarkers: true
6916
+ }
6917
+ },
6918
+ {
6919
+ name: "set-to",
6920
+ description: "Transform set X to Y to Hindi verb-final order",
6921
+ priority: 90,
6922
+ match: {
6923
+ commands: ["set", "\u0938\u0947\u091F"],
6924
+ requiredRoles: ["action", "patient", "destination"],
6925
+ // Skip a set whose value swept up a trailing block terminator (`set X to Y
6926
+ // end` — the body of an inline `if … then set … end`): verb-final reorder
6927
+ // would push the verb past `end` and break the block. These parse fine
6928
+ // verb-medial (the default).
6929
+ predicate: (parsed) => !/(^|\s)end$/i.test((parsed.original ?? "").trim())
6930
+ },
6931
+ transform: {
6932
+ // #x.innerText को इसका.name में सेट — verb-final, matching the generated
6933
+ // SOV set pattern (set schema markerOverride puts को on the destination /
6934
+ // में on the value). The default reorder emitted verb-MEDIAL
6935
+ // (`#x को सेट … में`), which no set pattern matched → bare-event fallback.
6936
+ roleOrder: ["patient", "destination", "action"],
6937
+ insertMarkers: true
6938
+ }
6939
+ },
6940
+ {
6941
+ name: "bind-to",
6942
+ description: "Transform bind $var to #el to Hindi verb-final order",
6943
+ priority: 90,
6944
+ match: {
6945
+ commands: ["bind", "\u092C\u093E\u0907\u0902\u0921", "\u092C\u093E\u0902\u0927\u0947\u0902"],
6946
+ requiredRoles: ["action", "patient", "destination"]
6947
+ },
6948
+ transform: {
6949
+ // $greeting को #name-input में bind — verb-final, matching the generated SOV
6950
+ // bind pattern (`{destination} को {source} में bind`). The default reorder
6951
+ // emitted verb-MEDIAL (`$greeting को bind #name-input में`), which the
6952
+ // verb-final pattern never matched → the bare-event fallback mis-anchored
6953
+ // the fronted `$greeting` as a phantom `on` event (the rf=0.00 bind residue).
6954
+ // ja/ko/zh/tr/bn already carry this rule; hi was the only SOV gap.
6955
+ roleOrder: ["patient", "destination", "action"],
6956
+ insertMarkers: true
6957
+ }
6401
6958
  }
6402
6959
  ]
6403
6960
  };
@@ -6413,7 +6970,11 @@ var tagalogProfile = {
6413
6970
  markers: [
6414
6971
  { form: "kapag", role: "event", position: "preposition", required: true },
6415
6972
  { form: "sa", role: "destination", position: "preposition", required: false },
6416
- { form: "mula sa", role: "source", position: "preposition", required: false },
6973
+ // mula_sa (underscore) matches the tl dict and the semantic profile's
6974
+ // source marker — the spaced form emitted two tokens the generated
6975
+ // patterns' single mula_sa literal could never match, so `remove X
6976
+ // mula sa Y` lost its source and the schema default fabricated me.
6977
+ { form: "mula_sa", role: "source", position: "preposition", required: false },
6417
6978
  { form: "nang", role: "style", position: "preposition", required: false },
6418
6979
  { form: "bilang", role: "method", position: "preposition", required: false }
6419
6980
  ],
@@ -6496,6 +7057,32 @@ var hebrewProfile = {
6496
7057
  { form: "\u05DE\u05DF", role: "source", position: "preposition", required: false },
6497
7058
  { form: "\u05E2\u05DD", role: "style", position: "preposition", required: false },
6498
7059
  { form: "\u05DB", role: "method", position: "preposition", required: false }
7060
+ ],
7061
+ rules: [
7062
+ {
7063
+ name: "bind-to",
7064
+ description: "Bind binds a variable to an element: \u05E7\u05E9\u05D5\u05E8 X \u05DC Y",
7065
+ priority: 90,
7066
+ match: {
7067
+ commands: ["bind", "\u05E7\u05E9\u05D5\u05E8"],
7068
+ requiredRoles: ["action", "patient", "destination"]
7069
+ },
7070
+ transform: {
7071
+ // The bound variable is the semantic destination (kept bare, no את
7072
+ // object marker), and ל marks the target element. Custom so the
7073
+ // variable does not pick up את and the element uses ל (not על).
7074
+ roleOrder: ["action", "patient", "destination"],
7075
+ custom: (parsed) => {
7076
+ const action = parsed.roles.get("action");
7077
+ const patient = parsed.roles.get("patient");
7078
+ const destination = parsed.roles.get("destination");
7079
+ const verb = action?.translated || action?.value || "";
7080
+ const v = patient?.translated || patient?.value || "";
7081
+ const d = destination?.translated || destination?.value || "";
7082
+ return [verb, v, "\u05DC", d].filter(Boolean).join(" ");
7083
+ }
7084
+ }
7085
+ }
6499
7086
  ]
6500
7087
  };
6501
7088
  var profiles = {
@@ -6510,6 +7097,7 @@ var profiles = {
6510
7097
  fr: frenchProfile,
6511
7098
  pt: portugueseProfile,
6512
7099
  id: indonesianProfile,
7100
+ ms: malayProfile,
6513
7101
  qu: quechuaProfile,
6514
7102
  sw: swahiliProfile,
6515
7103
  bn: bengaliProfile,
@@ -6906,6 +7494,51 @@ function getCommandKeywordsForLocale(locale) {
6906
7494
  }
6907
7495
  return keywords;
6908
7496
  }
7497
+ var EN_COPULAS = ["is", "are", "was", "were", "am", "be"];
7498
+ function getCopulasForLocale(locale) {
7499
+ const copulas = new Set(EN_COPULAS);
7500
+ if (locale !== "en") {
7501
+ for (const form of EN_COPULAS) {
7502
+ copulas.add(translateWord(form, "en", locale).toLowerCase());
7503
+ }
7504
+ }
7505
+ return copulas;
7506
+ }
7507
+ function isPredicateAdjectivePosition(tokens, i, copulas) {
7508
+ const prev = tokens[i - 1]?.toLowerCase();
7509
+ return !!prev && copulas.has(prev);
7510
+ }
7511
+ function getForLoopWordsForLocale(locale) {
7512
+ const forWords = /* @__PURE__ */ new Set(["for"]);
7513
+ const inWords = /* @__PURE__ */ new Set(["in"]);
7514
+ if (locale !== "en") {
7515
+ forWords.add(translateWord("for", "en", locale).toLowerCase());
7516
+ inWords.add(translateWord("in", "en", locale).toLowerCase());
7517
+ }
7518
+ return { forWords, inWords };
7519
+ }
7520
+ function isLoopHeadFor(tokens, i, inWords, commandKeywords) {
7521
+ for (let j = i + 1; j < tokens.length; j++) {
7522
+ const lt = tokens[j].toLowerCase();
7523
+ if (inWords.has(lt)) return true;
7524
+ if (commandKeywords.has(lt)) return false;
7525
+ }
7526
+ return false;
7527
+ }
7528
+ function repairHebrewFrontedAccusative(text) {
7529
+ const ACC = "\u05D0\u05EA";
7530
+ const verbs = getCommandKeywordsForLocale("he");
7531
+ const tokens = text.split(/\s+/);
7532
+ let changed = false;
7533
+ for (let i = 0; i + 1 < tokens.length; i++) {
7534
+ if (tokens[i] === ACC && verbs.has(tokens[i + 1].toLowerCase())) {
7535
+ [tokens[i], tokens[i + 1]] = [tokens[i + 1], tokens[i]];
7536
+ changed = true;
7537
+ i++;
7538
+ }
7539
+ }
7540
+ return changed ? tokens.join(" ") : text;
7541
+ }
6909
7542
  function extractBlockStructure(input, sourceLocale) {
6910
7543
  const tokens = input.split(/\s+/);
6911
7544
  const head = tokens[0]?.toLowerCase();
@@ -6943,9 +7576,10 @@ function extractBlockStructure(input, sourceLocale) {
6943
7576
  return null;
6944
7577
  }
6945
7578
  const commands = getCommandKeywordsForLocale(sourceLocale);
7579
+ const copulas = getCopulasForLocale(sourceLocale);
6946
7580
  let bodyStart = -1;
6947
7581
  for (let i = 0; i < inner.length; i++) {
6948
- if (commands.has(inner[i].toLowerCase())) {
7582
+ if (commands.has(inner[i].toLowerCase()) && !isPredicateAdjectivePosition(inner, i, copulas)) {
6949
7583
  bodyStart = i;
6950
7584
  break;
6951
7585
  }
@@ -7072,9 +7706,40 @@ var BOUNDARY_MODIFIERS = /* @__PURE__ */ new Set([
7072
7706
  "of",
7073
7707
  "over"
7074
7708
  ]);
7709
+ var boundaryModifiersCache = /* @__PURE__ */ new Map();
7710
+ function getBoundaryModifiersForLocale(locale) {
7711
+ const cached = boundaryModifiersCache.get(locale);
7712
+ if (cached) return cached;
7713
+ const modifiers = new Set(BOUNDARY_MODIFIERS);
7714
+ const profile = getProfile(locale);
7715
+ profile?.markers.forEach((marker) => {
7716
+ const form = marker.form.replace(/^-|-$/g, "").toLowerCase();
7717
+ if (form) modifiers.add(form);
7718
+ marker.alternatives?.forEach((alt) => {
7719
+ const altForm = alt.replace(/^-|-$/g, "").toLowerCase();
7720
+ if (altForm) modifiers.add(altForm);
7721
+ });
7722
+ });
7723
+ boundaryModifiersCache.set(locale, modifiers);
7724
+ return modifiers;
7725
+ }
7726
+ var ON_TARGET_COMMANDS = /* @__PURE__ */ new Set(["toggle", "add", "remove", "trigger", "send"]);
7727
+ function commandVerbOf(tokens, commandKeywords) {
7728
+ for (const token of tokens) {
7729
+ const lt = token.toLowerCase();
7730
+ if (commandKeywords.has(lt) && !BOUNDARY_MODIFIERS.has(lt) && !EVENT_KEYWORDS.has(lt)) {
7731
+ return lt;
7732
+ }
7733
+ }
7734
+ return null;
7735
+ }
7075
7736
  var BLOCK_HEAD_KEYWORDS = /* @__PURE__ */ new Set(["live", "when", "unless"]);
7737
+ var BLOCK_BODY_KEYWORDS = /* @__PURE__ */ new Set(["if", "repeat", "unless", "while", "for"]);
7738
+ var UNLESS_GUARD_OBJECT_MARKING_LOCALES = /* @__PURE__ */ new Set(["he", "zh"]);
7076
7739
  function splitOnCommandBoundaries(input, sourceLocale) {
7077
7740
  const commandKeywords = getCommandKeywordsForLocale(sourceLocale);
7741
+ const boundaryModifiers = getBoundaryModifiersForLocale(sourceLocale);
7742
+ const { forWords, inWords } = getForLoopWordsForLocale(sourceLocale);
7078
7743
  const tokens = input.split(/\s+/);
7079
7744
  if (tokens.length === 0) return [input];
7080
7745
  const parts = [];
@@ -7103,7 +7768,27 @@ function splitOnCommandBoundaries(input, sourceLocale) {
7103
7768
  currentPart.push(token);
7104
7769
  continue;
7105
7770
  }
7106
- if (!BOUNDARY_MODIFIERS.has(prevLower) && !commandKeywords.has(prevLower)) {
7771
+ if (forWords.has(lowerToken) && !isLoopHeadFor(tokens, i, inWords, commandKeywords)) {
7772
+ currentPart.push(token);
7773
+ continue;
7774
+ }
7775
+ if (BOUNDARY_MODIFIERS.has(lowerToken)) {
7776
+ const verb = commandVerbOf(currentPart, commandKeywords);
7777
+ if (verb && ON_TARGET_COMMANDS.has(verb)) {
7778
+ currentPart.push(token);
7779
+ continue;
7780
+ }
7781
+ if (lowerToken === "on" && verb === "set") {
7782
+ const nextTok = tokens[i + 1];
7783
+ const nextLower = nextTok?.toLowerCase();
7784
+ const scopeLike = !!nextTok && (/^[#.<@[]/.test(nextTok) || nextLower === "me" || nextLower === "it" || nextLower === "you");
7785
+ if (scopeLike) {
7786
+ currentPart.push(token);
7787
+ continue;
7788
+ }
7789
+ }
7790
+ }
7791
+ if (!boundaryModifiers.has(prevLower) && !commandKeywords.has(prevLower)) {
7107
7792
  parts.push(currentPart.join(" "));
7108
7793
  currentPart = [token];
7109
7794
  continue;
@@ -7157,6 +7842,15 @@ function deriveEventKeywordsFromProfiles() {
7157
7842
  return keywords;
7158
7843
  }
7159
7844
  var EVENT_KEYWORDS = deriveEventKeywordsFromProfiles();
7845
+ var EVENT_CONJUNCTIONS = /* @__PURE__ */ new Set(["or"]);
7846
+ var BODY_MODIFIER_KEYWORDS = /* @__PURE__ */ new Set([
7847
+ "async",
7848
+ "once",
7849
+ "debounced",
7850
+ "debounce",
7851
+ "throttled",
7852
+ "throttle"
7853
+ ]);
7160
7854
  function generateModifierMap(profile) {
7161
7855
  const map = {};
7162
7856
  profile.markers.forEach((marker) => {
@@ -7178,6 +7872,18 @@ function generateModifierMap(profile) {
7178
7872
  }
7179
7873
  return map;
7180
7874
  }
7875
+ function buildArgumentModifierMap(profile, actionVerb) {
7876
+ const map = generateModifierMap(profile);
7877
+ const verb = actionVerb?.toLowerCase();
7878
+ if (profile.wordOrder !== "SVO" || !verb || !ON_TARGET_COMMANDS.has(verb)) {
7879
+ return map;
7880
+ }
7881
+ const remapped = {};
7882
+ for (const [form, role] of Object.entries(map)) {
7883
+ remapped[form] = role === "event" ? "destination" : role;
7884
+ }
7885
+ return remapped;
7886
+ }
7181
7887
  function parseStatement(input, sourceLocale = "en") {
7182
7888
  const profile = getProfile(sourceLocale);
7183
7889
  if (!profile) return null;
@@ -7248,6 +7954,8 @@ function tokenize(input, profile) {
7248
7954
  let current = "";
7249
7955
  let inSelector = false;
7250
7956
  let selectorDepth = 0;
7957
+ let bracketDepth = 0;
7958
+ let parenDepth = 0;
7251
7959
  for (let i = 0; i < input.length; i++) {
7252
7960
  const char = input[i];
7253
7961
  if (char === "<") {
@@ -7257,7 +7965,17 @@ function tokenize(input, profile) {
7257
7965
  selectorDepth--;
7258
7966
  if (selectorDepth === 0) inSelector = false;
7259
7967
  }
7260
- if (/\s/.test(char) && !inSelector) {
7968
+ if (char === "[") {
7969
+ bracketDepth++;
7970
+ } else if (char === "]" && bracketDepth > 0) {
7971
+ bracketDepth--;
7972
+ }
7973
+ if (char === "(") {
7974
+ parenDepth++;
7975
+ } else if (char === ")" && parenDepth > 0) {
7976
+ parenDepth--;
7977
+ }
7978
+ if (/\s/.test(char) && !inSelector && bracketDepth === 0 && parenDepth === 0) {
7261
7979
  if (current) {
7262
7980
  tokens.push(current);
7263
7981
  current = "";
@@ -7290,11 +8008,16 @@ function parseEventHandler(tokens, profile) {
7290
8008
  const roles = /* @__PURE__ */ new Map();
7291
8009
  let startIndex = EVENT_KEYWORDS.has(tokens[0]?.toLowerCase()) ? 1 : 0;
7292
8010
  if (tokens[startIndex]) {
8011
+ const eventTokens = [tokens[startIndex]];
8012
+ startIndex++;
8013
+ while (tokens[startIndex] && EVENT_CONJUNCTIONS.has(tokens[startIndex].toLowerCase()) && tokens[startIndex + 1]) {
8014
+ eventTokens.push(tokens[startIndex], tokens[startIndex + 1]);
8015
+ startIndex += 2;
8016
+ }
7293
8017
  roles.set("event", {
7294
8018
  role: "event",
7295
- value: tokens[startIndex]
8019
+ value: eventTokens.join(" ")
7296
8020
  });
7297
- startIndex++;
7298
8021
  }
7299
8022
  if (tokens[startIndex] && tokens[startIndex].toLowerCase() === "from" && tokens[startIndex + 1]) {
7300
8023
  startIndex++;
@@ -7321,7 +8044,7 @@ function parseEventHandler(tokens, profile) {
7321
8044
  startIndex++;
7322
8045
  }
7323
8046
  if (tokens[startIndex]) {
7324
- const modifierMap = generateModifierMap(profile);
8047
+ const modifierMap = buildArgumentModifierMap(profile, roles.get("action")?.value);
7325
8048
  let currentRole = "patient";
7326
8049
  let currentValue = [];
7327
8050
  for (let i = startIndex; i < tokens.length; i++) {
@@ -7366,7 +8089,7 @@ function parseCommand(tokens, profile) {
7366
8089
  role: "action",
7367
8090
  value: tokens[0]
7368
8091
  });
7369
- const modifierMap = generateModifierMap(profile);
8092
+ const modifierMap = buildArgumentModifierMap(profile, tokens[0]);
7370
8093
  let currentRole = "patient";
7371
8094
  let currentValue = [];
7372
8095
  for (let i = 1; i < tokens.length; i++) {
@@ -7401,6 +8124,22 @@ function parseCommand(tokens, profile) {
7401
8124
  original: tokens.join(" ")
7402
8125
  };
7403
8126
  }
8127
+ var LITERAL_PRIMARY_ROLES = /* @__PURE__ */ new Set([
8128
+ "duration",
8129
+ "quantity"
8130
+ ]);
8131
+ function applyPrimaryRole(parsed, targetProfile) {
8132
+ if (parsed.type !== "command") return;
8133
+ const action = parsed.roles.get("action")?.value;
8134
+ if (!action) return;
8135
+ const primaryRole = COMMAND_PRIMARY_ROLES[action.toLowerCase()];
8136
+ if (!primaryRole || !LITERAL_PRIMARY_ROLES.has(primaryRole)) return;
8137
+ const patientEl = parsed.roles.get("patient");
8138
+ if (!patientEl || parsed.roles.has(primaryRole)) return;
8139
+ if (targetProfile.markers.some((m) => m.role === primaryRole)) return;
8140
+ parsed.roles.delete("patient");
8141
+ parsed.roles.set(primaryRole, { ...patientEl, role: primaryRole });
8142
+ }
7404
8143
  function parseConditional(tokens, _profile) {
7405
8144
  const roles = /* @__PURE__ */ new Map();
7406
8145
  roles.set("action", {
@@ -7414,6 +8153,11 @@ function parseConditional(tokens, _profile) {
7414
8153
  role: "condition",
7415
8154
  value: conditionValue
7416
8155
  });
8156
+ } else if (thenIndex === -1 && tokens.length > 1) {
8157
+ roles.set("condition", {
8158
+ role: "condition",
8159
+ value: tokens.slice(1).join(" ")
8160
+ });
7417
8161
  }
7418
8162
  return {
7419
8163
  type: "conditional",
@@ -7428,6 +8172,15 @@ function translateWord(word, sourceLocale, targetLocale) {
7428
8172
  if (/^\d+/.test(word)) {
7429
8173
  return word;
7430
8174
  }
8175
+ if (/\s/.test(word) && word.startsWith("(")) {
8176
+ return word.split(/\s+/).map((w) => translateWord(w, sourceLocale, targetLocale)).join(" ");
8177
+ }
8178
+ if (word.length > 1 && (word.startsWith("(") || word.endsWith(")"))) {
8179
+ const m = word.match(/^(\(*)([^()]+)(\)*)$/);
8180
+ if (m && (m[1] || m[3])) {
8181
+ return m[1] + translateWord(m[2], sourceLocale, targetLocale) + m[3];
8182
+ }
8183
+ }
7431
8184
  const sourceDict = sourceLocale === "en" ? null : dictionaries[sourceLocale];
7432
8185
  const targetDict = dictionaries[targetLocale];
7433
8186
  if (!targetDict) return word;
@@ -7451,9 +8204,21 @@ var POSSESSIVE_MARKERS = {
7451
8204
  ko: { type: "suffix", marker: "\uC758" },
7452
8205
  zh: { type: "suffix", marker: "\u7684" },
7453
8206
  ar: { type: "preposition", marker: "\u0644\u0640" },
7454
- tr: { type: "suffix", marker: "'\u0131n" },
8207
+ // Spaced genitive particle (not the glued `'ın`), so the tokenizer can split
8208
+ // it off the selector — consistent with Turkish's other spaced case markers.
8209
+ tr: { type: "particle", marker: "\u0131n" },
7455
8210
  id: { type: "preposition", marker: "dari" },
7456
- qu: { type: "suffix", marker: "-pa" },
8211
+ // Latin-script genitive: must be a *spaced* particle (`#picker pa`), since a
8212
+ // glued `#pickerpa` can't be split from the selector by the tokenizer the
8213
+ // way a non-Latin suffix (の/의/র) can.
8214
+ qu: { type: "particle", marker: "pa" },
8215
+ // Bengali SOV postposition genitive, like ja/ko — a spaced suffix the
8216
+ // tokenizer splits off as a particle. Previously absent, so it fell back to
8217
+ // the English `'s` marker and its possessive property paths never parsed.
8218
+ // (Hindi `का` is intentionally omitted: its `bind` lacks a verb-final
8219
+ // grammar rule, so fixing its possessive alone yields a wrong `on` parse —
8220
+ // tracked as separate follow-up.)
8221
+ bn: { type: "suffix", marker: "\u09B0" },
7457
8222
  sw: { type: "preposition", marker: "ya" }
7458
8223
  };
7459
8224
  function translatePossessive(token, sourceLocale, targetLocale) {
@@ -7477,6 +8242,8 @@ function translatePossessive(token, sourceLocale, targetLocale) {
7477
8242
  switch (targetMarker.type) {
7478
8243
  case "suffix":
7479
8244
  return `${translatedOwner}${targetMarker.marker}`;
8245
+ case "particle":
8246
+ return `${translatedOwner} ${targetMarker.marker}`;
7480
8247
  case "preposition":
7481
8248
  return `__POSS__${targetMarker.marker}__${translatedOwner}__POSS__`;
7482
8249
  default:
@@ -7512,6 +8279,17 @@ function translatePossessiveDotNotation(value, sourceLocale, targetLocale) {
7512
8279
  return null;
7513
8280
  }
7514
8281
  function translateMultiWordValue(value, sourceLocale, targetLocale) {
8282
+ if (value.includes("[")) {
8283
+ const guards = [];
8284
+ const masked = value.replace(/\[[^\]]*\]/g, (match) => {
8285
+ guards.push(match);
8286
+ return `\uE000${guards.length - 1}\uE001`;
8287
+ });
8288
+ if (guards.length > 0) {
8289
+ const translated2 = translateMultiWordValue(masked, sourceLocale, targetLocale);
8290
+ return translated2.replace(/(\d+)/g, (_, n) => guards[Number(n)]);
8291
+ }
8292
+ }
7515
8293
  if (!value.includes(" ")) {
7516
8294
  if (value.includes("'s")) {
7517
8295
  return translatePossessive(value, sourceLocale, targetLocale);
@@ -7579,6 +8357,24 @@ function translateElements(parsed, sourceLocale, targetLocale) {
7579
8357
  }
7580
8358
  }
7581
8359
  }
8360
+ var CARET_SCOPE_OPEN = "\uE000";
8361
+ var CARET_SCOPE_CLOSE = "\uE001";
8362
+ var CARET_SCOPE_RE = /(\^[A-Za-z_][\w-]*)(\s+on\s+(?:[#.][\w-]+|<[^>]*\/>|\[[^\]]+\]))/g;
8363
+ function maskCaretScopes(input) {
8364
+ const scopes = [];
8365
+ const masked = input.replace(CARET_SCOPE_RE, (_m, varTok, scope) => {
8366
+ const idx = scopes.length;
8367
+ scopes.push(scope);
8368
+ return `${varTok}${CARET_SCOPE_OPEN}${idx}${CARET_SCOPE_CLOSE}`;
8369
+ });
8370
+ return scopes.length > 0 ? { masked, scopes } : null;
8371
+ }
8372
+ function restoreCaretScopes(input, scopes) {
8373
+ return input.replace(
8374
+ new RegExp(`${CARET_SCOPE_OPEN}(\\d+)${CARET_SCOPE_CLOSE}`, "g"),
8375
+ (_m, n) => scopes[Number(n)] ?? ""
8376
+ );
8377
+ }
7582
8378
  var GrammarTransformer = class {
7583
8379
  constructor(sourceLocale = "en", targetLocale) {
7584
8380
  const source = getProfile(sourceLocale);
@@ -7596,7 +8392,25 @@ var GrammarTransformer = class {
7596
8392
  * For multi-line input, preserves line structure (indentation, blank lines).
7597
8393
  */
7598
8394
  transform(input) {
8395
+ const out = this.transformInternal(input);
8396
+ return this.targetProfile.code === "he" ? repairHebrewFrontedAccusative(out) : out;
8397
+ }
8398
+ transformInternal(input) {
8399
+ const caret = maskCaretScopes(input);
8400
+ if (caret) {
8401
+ return restoreCaretScopes(this.transform(caret.masked), caret.scopes);
8402
+ }
7599
8403
  const targetThen = getTargetThenKeyword(this.targetProfile.code);
8404
+ if (!input.includes("\n")) {
8405
+ const jsBlock = this.tryTransformJsBlock(input);
8406
+ if (jsBlock !== null) return jsBlock;
8407
+ const eventModifier = this.tryTransformEventWithModifierBody(input);
8408
+ if (eventModifier !== null) return eventModifier;
8409
+ const eventBlock = this.tryTransformEventWithBlockBody(input);
8410
+ if (eventBlock !== null) return eventBlock;
8411
+ const eventGuard = this.tryTransformEventWithUnlessGuard(input);
8412
+ if (eventGuard !== null) return eventGuard;
8413
+ }
7600
8414
  const hasMultiLineStructure = input.includes("\n");
7601
8415
  if (hasMultiLineStructure) {
7602
8416
  const { parts: parts2, lineMetadata, partToLineIndex } = splitCompoundStatementWithMetadata(
@@ -7626,10 +8440,15 @@ var GrammarTransformer = class {
7626
8440
  if (block) {
7627
8441
  return this.transformBlock(block);
7628
8442
  }
8443
+ const setScope = this.transformSetWithScope(input);
8444
+ if (setScope !== null) {
8445
+ return setScope;
8446
+ }
7629
8447
  const parsed = parseStatement(input, this.sourceProfile.code);
7630
8448
  if (!parsed) {
7631
8449
  return input;
7632
8450
  }
8451
+ applyPrimaryRole(parsed, this.targetProfile);
7633
8452
  translateElements(parsed, this.sourceProfile.code, this.targetProfile.code);
7634
8453
  const rule = this.findRule(parsed);
7635
8454
  if (rule?.transform.custom) {
@@ -7648,6 +8467,306 @@ var GrammarTransformer = class {
7648
8467
  }
7649
8468
  return joinTokens(reordered.map((e) => e.translated || e.value));
7650
8469
  }
8470
+ /**
8471
+ * Detect and transform an inline JS block (`[on <event>] js <raw js> end`).
8472
+ *
8473
+ * The `js ... end` body is raw JavaScript: it must not be tokenized,
8474
+ * translated, or word-order reordered. We mask the whole block with a single
8475
+ * opaque placeholder, run the surrounding statement (the event-handler head,
8476
+ * if any) through the normal reorder pipeline so the placeholder lands in the
8477
+ * correct action position, then substitute the translated `js`/`end` keywords
8478
+ * around the verbatim body.
8479
+ *
8480
+ * Returns `null` (fall through to the normal path) when there is no js block,
8481
+ * no matching `end`, or trailing content after `end` (kept tight on purpose).
8482
+ */
8483
+ tryTransformJsBlock(input) {
8484
+ const src = this.sourceProfile.code;
8485
+ const dst = this.targetProfile.code;
8486
+ const sourceJs = translateWord("js", "en", src);
8487
+ const sourceEnd = translateWord("end", "en", src).toLowerCase();
8488
+ const tokens = input.split(/\s+/).filter((t) => t.length > 0);
8489
+ const escapedJs = sourceJs.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
8490
+ const jsRe = new RegExp(`^${escapedJs}(\\(.*\\))?$`, "i");
8491
+ const jsIdx = tokens.findIndex((t) => jsRe.test(t));
8492
+ if (jsIdx === -1) return null;
8493
+ let endIdx = -1;
8494
+ for (let i = jsIdx + 1; i < tokens.length; i++) {
8495
+ if (tokens[i].toLowerCase() === sourceEnd) {
8496
+ endIdx = i;
8497
+ break;
8498
+ }
8499
+ }
8500
+ if (endIdx === -1) return null;
8501
+ if (endIdx !== tokens.length - 1) return null;
8502
+ const jsToken = tokens[jsIdx];
8503
+ const jsParen = jsToken.match(jsRe)?.[1] ?? "";
8504
+ const jsKeywordRaw = jsParen ? jsToken.slice(0, jsToken.length - jsParen.length) : jsToken;
8505
+ const body = tokens.slice(jsIdx + 1, endIdx).join(" ");
8506
+ const targetJs = translateWord(jsKeywordRaw, src, dst) + jsParen;
8507
+ const targetEnd = translateWord(tokens[endIdx], src, dst);
8508
+ const replacement = [targetJs, body, targetEnd].filter((s) => s.length > 0).join(" ");
8509
+ const before = tokens.slice(0, jsIdx);
8510
+ if (before.length === 0) return replacement;
8511
+ const placeholder = "JSBLOCKPLACEHOLDER";
8512
+ const reordered = this.transformSingle([...before, placeholder].join(" "));
8513
+ if (!reordered.includes(placeholder)) return null;
8514
+ return reordered.replace(placeholder, replacement);
8515
+ }
8516
+ /**
8517
+ * Transform an event handler whose body is a block command
8518
+ * (`on <event> [from <src>] {if|repeat|unless|while|for} … end`).
8519
+ *
8520
+ * `parseEventHandler` would treat the block keyword as the action and sweep the
8521
+ * condition/body into role values, then reorder them — shredding the block
8522
+ * (`if event.shiftKey call submitAndContinue() end` → scattered tokens). Instead
8523
+ * we mask the whole block as an opaque action placeholder, reorder the event
8524
+ * head normally, transform the block as a self-contained unit, and restitch.
8525
+ *
8526
+ * Returns `null` (fall through) when the input isn't an event handler, has no
8527
+ * block-keyword body, or has no closing `end`.
8528
+ */
8529
+ tryTransformEventWithBlockBody(input) {
8530
+ const tokens = tokenize(input, this.sourceProfile);
8531
+ if (tokens.length === 0) return null;
8532
+ if (!EVENT_KEYWORDS.has(tokens[0]?.toLowerCase())) return null;
8533
+ let blockIdx = -1;
8534
+ for (let i = 1; i < tokens.length; i++) {
8535
+ if (BLOCK_BODY_KEYWORDS.has(tokens[i].toLowerCase())) {
8536
+ blockIdx = i;
8537
+ break;
8538
+ }
8539
+ }
8540
+ if (blockIdx <= 0) return null;
8541
+ if (tokens[tokens.length - 1].toLowerCase() !== "end") return null;
8542
+ const eventHead = tokens.slice(0, blockIdx);
8543
+ const blockTokens = tokens.slice(blockIdx);
8544
+ if (this.targetProfile.wordOrder === "VSO" && eventHead.some((t) => t.toLowerCase() === "from")) {
8545
+ return null;
8546
+ }
8547
+ const placeholder = "EVENTBLOCKPLACEHOLDER";
8548
+ const headOut = this.transformSingle([...eventHead, placeholder].join(" "));
8549
+ if (!headOut.includes(placeholder)) return null;
8550
+ const blockOut = this.transformBlockBody(blockTokens);
8551
+ const eventClause = headOut.replace(placeholder, "").replace(/\s+/g, " ").trim();
8552
+ return [eventClause, blockOut].filter((s) => s.length > 0).join(" ");
8553
+ }
8554
+ /**
8555
+ * Transform an event handler whose body is an inline `unless` guard with NO
8556
+ * `end` (`on <event> unless <cond> <body>` — the `unless-condition` shape).
8557
+ *
8558
+ * Object-marking SVO targets (he, zh). `parseEventHandler` reads `unless` as the
8559
+ * action and sweeps the whole `<cond> <body>` tail into a single `patient` blob;
8560
+ * the target then prefixes that blob with its object marker — Hebrew's accusative
8561
+ * את (`… אלא את I match .disabled מתג .selected`) or Chinese's BA particle 把
8562
+ * (`… 除非 把 I match .disabled 切换 .selected`) — and the inner toggle loses its
8563
+ * own marker. The semantic parser can't recover the guard from that: the marker
8564
+ * ahead of the condition blocks the `unless` pattern AND the now-markerless body
8565
+ * command fails its object-marked toggle pattern, so the body collapses (`unless`
8566
+ * dropped). Marker-less languages (de/it/ar/pl) tolerate the same role-blob and
8567
+ * stay faithful, so this is an object-marker artifact, not a general parse gap.
8568
+ *
8569
+ * The standalone `unless <cond> <body>` path already produces the correct shape
8570
+ * (`extractBlockStructure` → `transformBlock`: condition kept marker-free, body
8571
+ * command keeps its marker — he `אלא I match .disabled מתג את .selected`, zh
8572
+ * `除非 I match .disabled 切换 把 .selected`). So we split the event head off,
8573
+ * transform the guard through that path, and emit the event clause first (he and
8574
+ * zh are both SVO — event leads). Returns `null` (fall through) when the input
8575
+ * isn't an object-marking event handler with an un-terminated inline `unless`
8576
+ * guard.
8577
+ */
8578
+ tryTransformEventWithUnlessGuard(input) {
8579
+ if (!UNLESS_GUARD_OBJECT_MARKING_LOCALES.has(this.targetProfile.code)) return null;
8580
+ const tokens = tokenize(input, this.sourceProfile);
8581
+ if (tokens.length === 0) return null;
8582
+ if (!EVENT_KEYWORDS.has(tokens[0]?.toLowerCase())) return null;
8583
+ let guardIdx = -1;
8584
+ for (let i = 1; i < tokens.length; i++) {
8585
+ if (tokens[i].toLowerCase() === "unless") {
8586
+ guardIdx = i;
8587
+ break;
8588
+ }
8589
+ }
8590
+ if (guardIdx <= 0) return null;
8591
+ if (tokens[tokens.length - 1].toLowerCase() === "end") return null;
8592
+ const eventHead = tokens.slice(0, guardIdx);
8593
+ const guard = tokens.slice(guardIdx).join(" ");
8594
+ const guardOut = this.transform(guard);
8595
+ if (!guardOut) return null;
8596
+ const placeholder = "EVENTGUARDPLACEHOLDER";
8597
+ const headOut = this.transformSingle([...eventHead, placeholder].join(" "));
8598
+ if (!headOut.includes(placeholder)) return null;
8599
+ const eventClause = headOut.replace(placeholder, "").replace(/\s+/g, " ").trim();
8600
+ return [eventClause, guardOut].filter((s) => s.length > 0).join(" ");
8601
+ }
8602
+ /**
8603
+ * Transform an event handler whose body leads with a command-modifier
8604
+ * (`on <event> [from <src>] {async|once|debounced [at N]|throttled [at N]} <body>`).
8605
+ *
8606
+ * `parseEventHandler` reads the first token after the event as the **action**, so
8607
+ * a leading modifier is mistaken for the verb and the real verb (`fetch`/`add`) is
8608
+ * swept into the patient. For SOV targets the reorder then surfaces that verb
8609
+ * **first** (`取得 /api/data を クリック …`), and the semantic parser matches the
8610
+ * leading `<verb> <patient>` with the low-priority `*-generated-verb-first`
8611
+ * command pattern — returning a bare command and discarding the event + the rest
8612
+ * of the body (degenerate parse).
8613
+ *
8614
+ * Instead, lift the modifier out, transform the modifier-free handler through the
8615
+ * normal path (which keeps the body in canonical patient-first SOV order so the
8616
+ * event sits mid-stream and the existing SOV event-extraction recovers it), then
8617
+ * re-emit the modifier as a **leading English literal**. The semantic parser
8618
+ * strips a leading `once`/`debounced`/`throttled` (`extractStandaloneModifiers`)
8619
+ * and an `async` anywhere (`stripAsyncModifier`) before parsing, so the modifier
8620
+ * is consumed as handler metadata rather than shadowing the body.
8621
+ *
8622
+ * Returns `null` (fall through) when the input isn't an event handler or the body
8623
+ * doesn't lead with a modifier — leaving simple/Mode-B handlers byte-identical.
8624
+ */
8625
+ tryTransformEventWithModifierBody(input) {
8626
+ if (this.targetProfile.wordOrder !== "SOV") return null;
8627
+ const tokens = tokenize(input, this.sourceProfile);
8628
+ if (tokens.length === 0) return null;
8629
+ if (!EVENT_KEYWORDS.has(tokens[0]?.toLowerCase())) return null;
8630
+ let i = 1;
8631
+ if (!tokens[i]) return null;
8632
+ i++;
8633
+ while (tokens[i] && EVENT_CONJUNCTIONS.has(tokens[i].toLowerCase()) && tokens[i + 1]) {
8634
+ i += 2;
8635
+ }
8636
+ if (tokens[i]?.toLowerCase() === "from" && tokens[i + 1]) {
8637
+ i++;
8638
+ while (tokens[i] && !ENGLISH_COMMANDS.has(tokens[i].toLowerCase()) && !BODY_MODIFIER_KEYWORDS.has(tokens[i].toLowerCase())) {
8639
+ i++;
8640
+ }
8641
+ }
8642
+ const modWord = tokens[i]?.toLowerCase();
8643
+ if (!modWord || !BODY_MODIFIER_KEYWORDS.has(modWord)) return null;
8644
+ const modStart = i;
8645
+ let modEnd = i + 1;
8646
+ if (modWord !== "async" && modWord !== "once") {
8647
+ if (tokens[modEnd]?.toLowerCase() === "at") modEnd++;
8648
+ if (tokens[modEnd] && /^\d+(ms|s|m)?$/.test(tokens[modEnd])) modEnd++;
8649
+ }
8650
+ const modifierPhrase = tokens.slice(modStart, modEnd).join(" ");
8651
+ const rebuilt = [...tokens.slice(0, modStart), ...tokens.slice(modEnd)].join(" ");
8652
+ if (tokens.length - (modEnd - modStart) <= 2) return null;
8653
+ const bodyOut = this.transform(rebuilt);
8654
+ return [modifierPhrase, bodyOut].filter((s) => s.length > 0).join(" ");
8655
+ }
8656
+ /**
8657
+ * Transform a `set <stuff> on <scope>` clause (S1 tabs-aria). The trailing
8658
+ * `on <scope>` is the element(s) the attribute is set on — kept attached by
8659
+ * splitOnCommandBoundaries. The semantic parser captures it as a `scope` role
8660
+ * via the passthrough literal `on` (setSchema's scope markerOverride is `on`
8661
+ * in every language), so `on` is emitted verbatim and only the scope *value*
8662
+ * is translated (selectors pass through; `me`/`it`/`you` translate to the
8663
+ * native reference, which the parser also accepts).
8664
+ *
8665
+ * Positioning matches where the set patterns expect the scope: at the clause
8666
+ * end for verb-first orders (SVO/VSO), and immediately before the clause-final
8667
+ * verb for SOV (the generated SOV pattern is `{dest} {patient} on {scope}
8668
+ * {verb}`). Returns null (fall through) when there is no trailing `on <scope>`.
8669
+ *
8670
+ * Source is English in the sync-translations pipeline, so the `set` verb and
8671
+ * `on` marker are matched as English literals.
8672
+ */
8673
+ transformSetWithScope(input) {
8674
+ const src = this.sourceProfile.code;
8675
+ const dst = this.targetProfile.code;
8676
+ const m = input.match(/^(.*\bset\b.*\S)\s+on\s+([#.<@[]\S*|me|it|you)\s*$/i);
8677
+ if (!m) return null;
8678
+ const head = m[1];
8679
+ const scopeRaw = m[2];
8680
+ const headOut = this.transformSingle(head);
8681
+ const scopeT = /^[#.<@[]/.test(scopeRaw) ? scopeRaw : translateWord(scopeRaw, src, dst);
8682
+ if (this.targetProfile.wordOrder === "SOV") {
8683
+ const verb = translateWord("set", "en", dst);
8684
+ const firstTok = input.trim().split(/\s+/)[0]?.toLowerCase();
8685
+ const isEventHandler = !!firstTok && EVENT_KEYWORDS.has(firstTok);
8686
+ const toks = headOut.split(/\s+/).filter(Boolean);
8687
+ if (!isEventHandler) {
8688
+ const vIdx = toks.indexOf(verb);
8689
+ if (vIdx >= 0) {
8690
+ toks.splice(vIdx, 1);
8691
+ toks.push("on", scopeT, verb);
8692
+ return toks.join(" ");
8693
+ }
8694
+ } else if (toks.length > 0 && toks[toks.length - 1] === verb) {
8695
+ toks.splice(toks.length - 1, 0, "on", scopeT);
8696
+ return toks.join(" ");
8697
+ }
8698
+ return `${headOut} on ${scopeT}`;
8699
+ }
8700
+ return `${headOut} on ${scopeT}`;
8701
+ }
8702
+ /**
8703
+ * Transform a self-contained block command (`{head} {clause?} {body} {end}`),
8704
+ * where head ∈ {if, repeat, unless, …}. The clause (condition / `until event …`)
8705
+ * runs up to the first command verb and is translated word-by-word; the body is
8706
+ * recursively transformed (so its inner commands reorder for the target); the
8707
+ * head/tail keywords are translated. The block is never word-order reordered as
8708
+ * a whole — delimiters stay at the edges regardless of target word order.
8709
+ */
8710
+ transformBlockBody(blockTokens) {
8711
+ const src = this.sourceProfile.code;
8712
+ const dst = this.targetProfile.code;
8713
+ const head = blockTokens[0];
8714
+ const hasEnd = blockTokens[blockTokens.length - 1]?.toLowerCase() === "end";
8715
+ const tail = hasEnd ? blockTokens[blockTokens.length - 1] : "";
8716
+ const inner = blockTokens.slice(1, hasEnd ? -1 : void 0);
8717
+ const commands = getCommandKeywordsForLocale(src);
8718
+ const copulas = getCopulasForLocale(src);
8719
+ let bodyStart = inner.findIndex(
8720
+ (t, i) => commands.has(t.toLowerCase()) && !isPredicateAdjectivePosition(inner, i, copulas)
8721
+ );
8722
+ if (bodyStart < 0) bodyStart = inner.length;
8723
+ const clause = inner.slice(0, bodyStart).join(" ");
8724
+ const bodyTokens = inner.slice(bodyStart);
8725
+ const headT = translateWord(head, src, dst);
8726
+ const tailT = tail ? translateWord(tail, src, dst) : "";
8727
+ const clauseT = clause ? translateMultiWordValue(clause, src, dst) : "";
8728
+ const bodyT = this.transformConditionalBody(bodyTokens);
8729
+ return [headT, clauseT, bodyT, tailT].filter((s) => s.length > 0).join(" ");
8730
+ }
8731
+ /**
8732
+ * Transform an `if`/`unless` block body, splitting it at a top-level `else` into
8733
+ * a then-branch and an else-branch so each is reordered as a self-contained unit
8734
+ * and the `else` keyword itself is translated. Without this, the body is reordered
8735
+ * as one stream: `else` rides along glued to the preceding clause (and, when that
8736
+ * clause begins with a selector, is marked a selector and left *untranslated*),
8737
+ * and a spurious `then` is inserted around it — both of which break the target
8738
+ * text and the downstream parse. The split is depth-aware so an `else` belonging
8739
+ * to a nested block is not mistaken for this block's separator. Bodies without an
8740
+ * `else` transform exactly as before.
8741
+ */
8742
+ transformConditionalBody(bodyTokens) {
8743
+ const src = this.sourceProfile.code;
8744
+ const dst = this.targetProfile.code;
8745
+ const sourceElse = translateWord("else", "en", src).toLowerCase();
8746
+ let depth = 0;
8747
+ let elseIdx = -1;
8748
+ for (let i = 0; i < bodyTokens.length; i++) {
8749
+ const t = bodyTokens[i].toLowerCase();
8750
+ if (BLOCK_BODY_KEYWORDS.has(t)) depth++;
8751
+ else if (t === "end" && depth > 0) depth--;
8752
+ else if (t === sourceElse && depth === 0) {
8753
+ elseIdx = i;
8754
+ break;
8755
+ }
8756
+ }
8757
+ if (elseIdx === -1) {
8758
+ const body = bodyTokens.join(" ");
8759
+ return body ? this.transform(body) : "";
8760
+ }
8761
+ const thenBranch = bodyTokens.slice(0, elseIdx).join(" ");
8762
+ const elseBranch = bodyTokens.slice(elseIdx + 1).join(" ");
8763
+ const elseT = translateWord(bodyTokens[elseIdx], src, dst);
8764
+ return [
8765
+ thenBranch ? this.transform(thenBranch) : "",
8766
+ elseT,
8767
+ elseBranch ? this.transform(elseBranch) : ""
8768
+ ].filter((s) => s.length > 0).join(" ");
8769
+ }
7651
8770
  /**
7652
8771
  * Translate a reactive block by translating the head/tail/connector
7653
8772
  * via the dictionary, recursively transforming the body through the
@@ -7771,6 +8890,6 @@ var examples = {
7771
8890
  }
7772
8891
  };
7773
8892
 
7774
- export { ENGLISH_COMMANDS, ENGLISH_KEYWORDS, GrammarTransformer, LANGUAGE_FAMILY_DEFAULTS, LocaleManager, UNIVERSAL_ENGLISH_KEYWORDS, UNIVERSAL_PATTERNS, ar, ar as arDictionary, arKeywords, arabicProfile, bn, bn as bnDictionary, bnKeywords, chineseProfile, createEnglishProvider, createKeywordProvider, de, de as deDictionary, deKeywords, detectBrowserLocale, directMappings, en, englishProfile, es, es as esDictionary, esKeywords, fr, fr as frDictionary, frKeywords, frenchProfile, germanProfile, getDirectMapping, getProfile, getSupportedDirectPairs, getSupportedLocales, examples as grammarExamples, hasDirectMapping, he, he as heDictionary, heKeywords, hebrewProfile, hindiDictionary as hiDictionary, hiKeywords, hindiDictionary, id, id as idDictionary, idKeywords, indonesianProfile, insertMarkers, it, it as itDictionary, itKeywords, ja, ja as jaDictionary, jaKeywords, japaneseProfile, joinTokens, ko, ko as koDictionary, koKeywords, koreanProfile, ms, ms as msDictionary, msKeywords, parseStatement, pl, pl as plDictionary, plKeywords, portugueseProfile, profiles, pt, pt as ptDictionary, ptKeywords, qu, qu as quDictionary, quKeywords, quechuaProfile, reorderRoles, russianDictionary as ruDictionary, ruKeywords, russianDictionary, spanishProfile, sw, sw as swDictionary, swKeywords, swahiliProfile, th, th as thDictionary, thKeywords, tl, tl as tlDictionary, tlKeywords, toEnglish, toLocale, tr, tr as trDictionary, trKeywords, transformStatement, translate, translateWordDirect, turkishProfile, ukrainianDictionary as ukDictionary, ukKeywords, ukrainianDictionary, vi, vi as viDictionary, viKeywords, zh, zh as zhDictionary, zhKeywords };
8893
+ export { ENGLISH_COMMANDS, ENGLISH_KEYWORDS, GrammarTransformer, LANGUAGE_FAMILY_DEFAULTS, LocaleManager, UNIVERSAL_ENGLISH_KEYWORDS, UNIVERSAL_PATTERNS, ar, ar as arDictionary, arKeywords, arabicProfile, bn, bn as bnDictionary, bnKeywords, chineseProfile, createEnglishProvider, createKeywordProvider, de, de as deDictionary, deKeywords, detectBrowserLocale, directMappings, en, englishProfile, es, es as esDictionary, esKeywords, fr, fr as frDictionary, frKeywords, frenchProfile, germanProfile, getDirectMapping, getProfile, getSupportedDirectPairs, getSupportedLocales, examples as grammarExamples, hasDirectMapping, he, he as heDictionary, heKeywords, hebrewProfile, hindiDictionary as hiDictionary, hiKeywords, hindiDictionary, id, id as idDictionary, idKeywords, indonesianProfile, insertMarkers, it, it as itDictionary, itKeywords, ja, ja as jaDictionary, jaKeywords, japaneseProfile, joinTokens, ko, ko as koDictionary, koKeywords, koreanProfile, malayProfile, ms, ms as msDictionary, msKeywords, parseStatement, pl, pl as plDictionary, plKeywords, portugueseProfile, profiles, pt, pt as ptDictionary, ptKeywords, qu, qu as quDictionary, quKeywords, quechuaProfile, reorderRoles, russianDictionary as ruDictionary, ruKeywords, russianDictionary, spanishProfile, sw, sw as swDictionary, swKeywords, swahiliProfile, th, th as thDictionary, thKeywords, tl, tl as tlDictionary, tlKeywords, toEnglish, toLocale, tr, tr as trDictionary, trKeywords, transformStatement, translate, translateWordDirect, turkishProfile, ukrainianDictionary as ukDictionary, ukKeywords, ukrainianDictionary, vi, vi as viDictionary, viKeywords, zh, zh as zhDictionary, zhKeywords };
7775
8894
  //# sourceMappingURL=browser.js.map
7776
8895
  //# sourceMappingURL=browser.js.map