@lokascript/i18n 2.5.1 → 2.6.0

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