@lokascript/i18n 2.9.3 → 2.10.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 (40) hide show
  1. package/dist/browser.cjs +74 -10
  2. package/dist/browser.cjs.map +1 -1
  3. package/dist/browser.d.cts +2 -2
  4. package/dist/browser.d.ts +2 -2
  5. package/dist/browser.js +74 -10
  6. package/dist/browser.js.map +1 -1
  7. package/dist/dictionaries/index.cjs +18 -10
  8. package/dist/dictionaries/index.cjs.map +1 -1
  9. package/dist/dictionaries/index.js +18 -10
  10. package/dist/dictionaries/index.js.map +1 -1
  11. package/dist/index.cjs +74 -10
  12. package/dist/index.cjs.map +1 -1
  13. package/dist/index.d.cts +1 -1
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.js +74 -10
  16. package/dist/index.js.map +1 -1
  17. package/dist/lokascript-i18n.min.js +1 -1
  18. package/dist/lokascript-i18n.min.js.map +1 -1
  19. package/dist/lokascript-i18n.mjs +110 -10
  20. package/dist/lokascript-i18n.mjs.map +1 -1
  21. package/dist/plugins/vite.cjs +18 -10
  22. package/dist/plugins/vite.cjs.map +1 -1
  23. package/dist/plugins/vite.js +18 -10
  24. package/dist/plugins/vite.js.map +1 -1
  25. package/dist/plugins/webpack.cjs +18 -10
  26. package/dist/plugins/webpack.cjs.map +1 -1
  27. package/dist/plugins/webpack.js +18 -10
  28. package/dist/plugins/webpack.js.map +1 -1
  29. package/dist/{transformer--W46PGaW.d.cts → transformer-CsOeqayN.d.cts} +18 -1
  30. package/dist/{transformer-FU06J6kG.d.ts → transformer-DWCTG1DQ.d.ts} +18 -1
  31. package/package.json +4 -4
  32. package/src/dictionaries/de.ts +3 -1
  33. package/src/dictionaries/es.ts +2 -2
  34. package/src/dictionaries/pl.ts +2 -1
  35. package/src/dictionaries/pt.ts +5 -2
  36. package/src/dictionaries/ru.ts +2 -2
  37. package/src/dictionaries/uk.ts +2 -2
  38. package/src/grammar/grammar.test.ts +87 -0
  39. package/src/grammar/transformer.ts +106 -0
  40. package/src/grammar/types.ts +4 -0
@@ -1,5 +1,5 @@
1
- import { K as KeywordProvider } from './transformer--W46PGaW.cjs';
2
- export { A as AdpositionType, E as ENGLISH_COMMANDS, a as ENGLISH_KEYWORDS, G as GrammarRule, b as GrammarTransformer, c as GrammaticalMarker, d as KeywordProviderOptions, L as LANGUAGE_FAMILY_DEFAULTS, e as LanguageProfile, f as LocaleManager, M as MorphologyType, P as ParsedElement, g as ParsedStatement, h as PatternMatcher, i as PatternTransform, S as SemanticRole, U as UNIVERSAL_ENGLISH_KEYWORDS, j as UNIVERSAL_PATTERNS, W as WordOrder, k as ar, k as arDictionary, l as arKeywords, m as arabicProfile, n as bn, n as bnDictionary, o as bnKeywords, p as chineseProfile, q as createEnglishProvider, r as createKeywordProvider, s as de, s as deDictionary, t as deKeywords, u as detectBrowserLocale, v as englishProfile, w as es, w as esDictionary, x as esKeywords, y as fr, y as frDictionary, z as frKeywords, B as frenchProfile, C as germanProfile, D as getProfile, F as getSupportedLocales, H as grammarExamples, I as hebrewProfile, J as hiDictionary, N as hiKeywords, J as hindiDictionary, O as id, O as idDictionary, Q as idKeywords, R as indonesianProfile, T as insertMarkers, V as it, V as itDictionary, X as itKeywords, Y as ja, Y as jaDictionary, Z as jaKeywords, _ as japaneseProfile, $ as joinTokens, a0 as ko, a0 as koDictionary, a1 as koKeywords, a2 as koreanProfile, a3 as malayProfile, a4 as ms, a4 as msDictionary, a5 as msKeywords, a6 as parseStatement, a7 as pl, a7 as plDictionary, a8 as plKeywords, a9 as portugueseProfile, aa as profiles, ab as pt, ab as ptDictionary, ac as ptKeywords, ad as qu, ad as quDictionary, ae as quKeywords, af as quechuaProfile, ag as reorderRoles, ah as ruDictionary, ai as ruKeywords, ah as russianDictionary, aj as spanishProfile, ak as sw, ak as swDictionary, al as swKeywords, am as swahiliProfile, an as th, an as thDictionary, ao as thKeywords, ap as tl, ap as tlDictionary, aq as tlKeywords, ar as toEnglish, as as toLocale, at as tr, at as trDictionary, au as trKeywords, av as transformStatement, aw as translate, ax as turkishProfile, ay as ukDictionary, az as ukKeywords, ay as ukrainianDictionary, aA as vi, aA as viDictionary, aB as viKeywords, aC as zh, aC as zhDictionary, aD as zhKeywords } from './transformer--W46PGaW.cjs';
1
+ import { K as KeywordProvider } from './transformer-CsOeqayN.cjs';
2
+ export { A as AdpositionType, E as ENGLISH_COMMANDS, a as ENGLISH_KEYWORDS, G as GrammarRule, b as GrammarTransformer, c as GrammaticalMarker, d as KeywordProviderOptions, L as LANGUAGE_FAMILY_DEFAULTS, e as LanguageProfile, f as LocaleManager, M as MorphologyType, P as ParsedElement, g as ParsedStatement, h as PatternMatcher, i as PatternTransform, S as SemanticRole, U as UNIVERSAL_ENGLISH_KEYWORDS, j as UNIVERSAL_PATTERNS, W as WordOrder, k as ar, k as arDictionary, l as arKeywords, m as arabicProfile, n as bn, n as bnDictionary, o as bnKeywords, p as chineseProfile, q as createEnglishProvider, r as createKeywordProvider, s as de, s as deDictionary, t as deKeywords, u as detectBrowserLocale, v as englishProfile, w as es, w as esDictionary, x as esKeywords, y as fr, y as frDictionary, z as frKeywords, B as frenchProfile, C as germanProfile, D as getProfile, F as getSupportedLocales, H as grammarExamples, I as hebrewProfile, J as hiDictionary, N as hiKeywords, J as hindiDictionary, O as id, O as idDictionary, Q as idKeywords, R as indonesianProfile, T as insertMarkers, V as it, V as itDictionary, X as itKeywords, Y as ja, Y as jaDictionary, Z as jaKeywords, _ as japaneseProfile, $ as joinTokens, a0 as ko, a0 as koDictionary, a1 as koKeywords, a2 as koreanProfile, a3 as malayProfile, a4 as ms, a4 as msDictionary, a5 as msKeywords, a6 as parseStatement, a7 as pl, a7 as plDictionary, a8 as plKeywords, a9 as portugueseProfile, aa as profiles, ab as pt, ab as ptDictionary, ac as ptKeywords, ad as qu, ad as quDictionary, ae as quKeywords, af as quechuaProfile, ag as reorderRoles, ah as ruDictionary, ai as ruKeywords, ah as russianDictionary, aj as spanishProfile, ak as sw, ak as swDictionary, al as swKeywords, am as swahiliProfile, an as th, an as thDictionary, ao as thKeywords, ap as tl, ap as tlDictionary, aq as tlKeywords, ar as toEnglish, as as toLocale, at as tr, at as trDictionary, au as trKeywords, av as transformStatement, aw as translate, ax as turkishProfile, ay as ukDictionary, az as ukKeywords, ay as ukrainianDictionary, aA as vi, aA as viDictionary, aB as viKeywords, aC as zh, aC as zhDictionary, aD as zhKeywords } from './transformer-CsOeqayN.cjs';
3
3
  import { D as Dictionary } from './types-BYtpqGq3.cjs';
4
4
 
5
5
  declare const he: Dictionary;
package/dist/browser.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { K as KeywordProvider } from './transformer-FU06J6kG.js';
2
- export { A as AdpositionType, E as ENGLISH_COMMANDS, a as ENGLISH_KEYWORDS, G as GrammarRule, b as GrammarTransformer, c as GrammaticalMarker, d as KeywordProviderOptions, L as LANGUAGE_FAMILY_DEFAULTS, e as LanguageProfile, f as LocaleManager, M as MorphologyType, P as ParsedElement, g as ParsedStatement, h as PatternMatcher, i as PatternTransform, S as SemanticRole, U as UNIVERSAL_ENGLISH_KEYWORDS, j as UNIVERSAL_PATTERNS, W as WordOrder, k as ar, k as arDictionary, l as arKeywords, m as arabicProfile, n as bn, n as bnDictionary, o as bnKeywords, p as chineseProfile, q as createEnglishProvider, r as createKeywordProvider, s as de, s as deDictionary, t as deKeywords, u as detectBrowserLocale, v as englishProfile, w as es, w as esDictionary, x as esKeywords, y as fr, y as frDictionary, z as frKeywords, B as frenchProfile, C as germanProfile, D as getProfile, F as getSupportedLocales, H as grammarExamples, I as hebrewProfile, J as hiDictionary, N as hiKeywords, J as hindiDictionary, O as id, O as idDictionary, Q as idKeywords, R as indonesianProfile, T as insertMarkers, V as it, V as itDictionary, X as itKeywords, Y as ja, Y as jaDictionary, Z as jaKeywords, _ as japaneseProfile, $ as joinTokens, a0 as ko, a0 as koDictionary, a1 as koKeywords, a2 as koreanProfile, a3 as malayProfile, a4 as ms, a4 as msDictionary, a5 as msKeywords, a6 as parseStatement, a7 as pl, a7 as plDictionary, a8 as plKeywords, a9 as portugueseProfile, aa as profiles, ab as pt, ab as ptDictionary, ac as ptKeywords, ad as qu, ad as quDictionary, ae as quKeywords, af as quechuaProfile, ag as reorderRoles, ah as ruDictionary, ai as ruKeywords, ah as russianDictionary, aj as spanishProfile, ak as sw, ak as swDictionary, al as swKeywords, am as swahiliProfile, an as th, an as thDictionary, ao as thKeywords, ap as tl, ap as tlDictionary, aq as tlKeywords, ar as toEnglish, as as toLocale, at as tr, at as trDictionary, au as trKeywords, av as transformStatement, aw as translate, ax as turkishProfile, ay as ukDictionary, az as ukKeywords, ay as ukrainianDictionary, aA as vi, aA as viDictionary, aB as viKeywords, aC as zh, aC as zhDictionary, aD as zhKeywords } from './transformer-FU06J6kG.js';
1
+ import { K as KeywordProvider } from './transformer-DWCTG1DQ.js';
2
+ export { A as AdpositionType, E as ENGLISH_COMMANDS, a as ENGLISH_KEYWORDS, G as GrammarRule, b as GrammarTransformer, c as GrammaticalMarker, d as KeywordProviderOptions, L as LANGUAGE_FAMILY_DEFAULTS, e as LanguageProfile, f as LocaleManager, M as MorphologyType, P as ParsedElement, g as ParsedStatement, h as PatternMatcher, i as PatternTransform, S as SemanticRole, U as UNIVERSAL_ENGLISH_KEYWORDS, j as UNIVERSAL_PATTERNS, W as WordOrder, k as ar, k as arDictionary, l as arKeywords, m as arabicProfile, n as bn, n as bnDictionary, o as bnKeywords, p as chineseProfile, q as createEnglishProvider, r as createKeywordProvider, s as de, s as deDictionary, t as deKeywords, u as detectBrowserLocale, v as englishProfile, w as es, w as esDictionary, x as esKeywords, y as fr, y as frDictionary, z as frKeywords, B as frenchProfile, C as germanProfile, D as getProfile, F as getSupportedLocales, H as grammarExamples, I as hebrewProfile, J as hiDictionary, N as hiKeywords, J as hindiDictionary, O as id, O as idDictionary, Q as idKeywords, R as indonesianProfile, T as insertMarkers, V as it, V as itDictionary, X as itKeywords, Y as ja, Y as jaDictionary, Z as jaKeywords, _ as japaneseProfile, $ as joinTokens, a0 as ko, a0 as koDictionary, a1 as koKeywords, a2 as koreanProfile, a3 as malayProfile, a4 as ms, a4 as msDictionary, a5 as msKeywords, a6 as parseStatement, a7 as pl, a7 as plDictionary, a8 as plKeywords, a9 as portugueseProfile, aa as profiles, ab as pt, ab as ptDictionary, ac as ptKeywords, ad as qu, ad as quDictionary, ae as quKeywords, af as quechuaProfile, ag as reorderRoles, ah as ruDictionary, ai as ruKeywords, ah as russianDictionary, aj as spanishProfile, ak as sw, ak as swDictionary, al as swKeywords, am as swahiliProfile, an as th, an as thDictionary, ao as thKeywords, ap as tl, ap as tlDictionary, aq as tlKeywords, ar as toEnglish, as as toLocale, at as tr, at as trDictionary, au as trKeywords, av as transformStatement, aw as translate, ax as turkishProfile, ay as ukDictionary, az as ukKeywords, ay as ukrainianDictionary, aA as vi, aA as viDictionary, aB as viKeywords, aC as zh, aC as zhDictionary, aD as zhKeywords } from './transformer-DWCTG1DQ.js';
3
3
  import { D as Dictionary } from './types-BYtpqGq3.js';
4
4
 
5
5
  declare const he: Dictionary;
package/dist/browser.js CHANGED
@@ -589,12 +589,12 @@ var es = {
589
589
  events: {
590
590
  click: "clic",
591
591
  dblclick: "dobleclic",
592
- mousedown: "rat\xF3nabajo",
593
- mouseup: "rat\xF3narriba",
594
592
  mouseenter: "rat\xF3nentrar",
595
593
  mouseleave: "rat\xF3nsalir",
596
594
  // V3 Batch 2: fused forms split to the S5b/tokenizer-known forms (the fused
597
595
  // spellings captured event=expression:undefined — broken listeners)
596
+ mousedown: "rat\xF3n abajo",
597
+ mouseup: "rat\xF3n arriba",
598
598
  mouseover: "rat\xF3n encima",
599
599
  mouseout: "rat\xF3n fuera",
600
600
  mousemove: "rat\xF3nmover",
@@ -1278,7 +1278,9 @@ var de = {
1278
1278
  reset: "zur\xFCcksetzen",
1279
1279
  load: "laden",
1280
1280
  unload: "entladen",
1281
- resize: "gr\xF6\xDFe\xE4ndern",
1281
+ // 'größeändern' was a malformed compound: German either separates the verb
1282
+ // phrase ('Größe ändern') or compounds with the linking -n- ('Größenänderung').
1283
+ resize: "gr\xF6\xDFen\xE4nderung",
1282
1284
  scroll: "scrollen",
1283
1285
  touchstart: "ber\xFChrungstart",
1284
1286
  touchend: "ber\xFChrungend",
@@ -3117,12 +3119,15 @@ var pt = {
3117
3119
  events: {
3118
3120
  click: "clique",
3119
3121
  dblclick: "duploClique",
3120
- mousedown: "mouseBaixo",
3121
- mouseup: "mouseCima",
3122
3122
  mouseenter: "mouseEntrar",
3123
3123
  mouseleave: "mouseSair",
3124
3124
  // V3 Batch 2: camelCase forms split to the S5b/tokenizer-known forms (the
3125
3125
  // fused spellings captured event=expression:undefined — broken listeners)
3126
+ // pt-BR idiom is "pressionado"/"solto", not the spatial baixo/cima calque —
3127
+ // cf. MDN pt-BR ("um botão ... é pressionado") and Scratch pt-BR
3128
+ // ("mouse pressionado?"). The calques stay as profile parse alternatives.
3129
+ mousedown: "mouse pressionado",
3130
+ mouseup: "mouse solto",
3126
3131
  mouseover: "mouse sobre",
3127
3132
  mouseout: "mouse fora",
3128
3133
  mousemove: "mouseMover",
@@ -3800,7 +3805,8 @@ var pl = {
3800
3805
  // Batch 3: resetuj captured on.event as expression (broken listener) — profile word round-trips
3801
3806
  load: "za\u0142aduj",
3802
3807
  unload: "wy\u0142aduj",
3803
- resize: "zmie\u0144rozmiar",
3808
+ // 'zmieńrozmiar' was a fused two-word phrase; Polish writes 'zmiana rozmiaru'.
3809
+ resize: "zmiana rozmiaru",
3804
3810
  scroll: "przewi\u0144",
3805
3811
  touchstart: "dotykstart",
3806
3812
  touchend: "dotykkoniec",
@@ -3964,7 +3970,7 @@ var russianDictionary = {
3964
3970
  morph: "\u0442\u0440\u0430\u043D\u0441\u0444\u043E\u0440\u043C\u0438\u0440\u043E\u0432\u0430\u0442\u044C",
3965
3971
  settle: "\u0441\u0442\u0430\u0431\u0438\u043B\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u0442\u044C",
3966
3972
  append: "\u0434\u043E\u043F\u0438\u0441\u0430\u0442\u044C",
3967
- // profile alternative; the compound splits and добавить parses as add
3973
+ // matches the ru profile primary
3968
3974
  exit: "\u0432\u044B\u0439\u0442\u0438",
3969
3975
  else: "\u0438\u043D\u0430\u0447\u0435",
3970
3976
  install: "\u0438\u043D\u0441\u0442\u0430\u043B\u043B\u0438\u0440\u043E\u0432\u0430\u0442\u044C",
@@ -3974,7 +3980,8 @@ var russianDictionary = {
3974
3980
  focus: "\u0441\u0444\u043E\u043A\u0443\u0441\u0438\u0440\u043E\u0432\u0430\u0442\u044C",
3975
3981
  blur: "\u0440\u0430\u0437\u043C\u044B\u0442\u044C",
3976
3982
  clone: "\u043A\u043B\u043E\u043D\u0438\u0440\u043E\u0432\u0430\u0442\u044C",
3977
- prepend: "\u0434\u043E\u0431\u0430\u0432\u0438\u0442\u044C_\u0432_\u043D\u0430\u0447\u0430\u043B\u043E",
3983
+ prepend: "\u0434\u043E\u0431\u0430\u0432\u0438\u0442\u044C \u0432 \u043D\u0430\u0447\u0430\u043B\u043E",
3984
+ // natural form; the underscore compound was never natural Russian
3978
3985
  breakpoint: "\u0442\u043E\u0447\u043A\u0430-\u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0430",
3979
3986
  clear: "\u043E\u0447\u0438\u0441\u0442\u0438\u0442\u044C",
3980
3987
  close: "\u0437\u0430\u043A\u0440\u044B\u0442\u044C",
@@ -4211,7 +4218,7 @@ var ukrainianDictionary = {
4211
4218
  morph: "\u0442\u0440\u0430\u043D\u0441\u0444\u043E\u0440\u043C\u0443\u0432\u0430\u0442\u0438",
4212
4219
  settle: "\u0441\u0442\u0430\u0431\u0456\u043B\u0456\u0437\u0443\u0432\u0430\u0442\u0438",
4213
4220
  append: "\u0434\u043E\u043F\u0438\u0441\u0430\u0442\u0438",
4214
- // profile alternative; the compound splits and додати parses as add
4221
+ // matches the uk profile primary
4215
4222
  exit: "\u0432\u0438\u0439\u0442\u0438",
4216
4223
  else: "\u0456\u043D\u0430\u043A\u0448\u0435",
4217
4224
  install: "\u0456\u043D\u0441\u0442\u0430\u043B\u044E\u0432\u0430\u0442\u0438",
@@ -4221,7 +4228,8 @@ var ukrainianDictionary = {
4221
4228
  focus: "\u0441\u0444\u043E\u043A\u0443\u0441\u0443\u0432\u0430\u0442\u0438",
4222
4229
  blur: "\u0440\u043E\u0437\u0444\u043E\u043A\u0443\u0441\u0443\u0432\u0430\u0442\u0438",
4223
4230
  clone: "\u043A\u043B\u043E\u043D\u0443\u0432\u0430\u0442\u0438",
4224
- prepend: "\u0434\u043E\u0434\u0430\u0442\u0438_\u043D\u0430_\u043F\u043E\u0447\u0430\u0442\u043E\u043A",
4231
+ prepend: "\u0434\u043E\u0434\u0430\u0442\u0438 \u043D\u0430 \u043F\u043E\u0447\u0430\u0442\u043E\u043A",
4232
+ // natural form; see ru.ts
4225
4233
  breakpoint: "\u0442\u043E\u0447\u043A\u0430-\u0437\u0443\u043F\u0438\u043D\u043A\u0438",
4226
4234
  clear: "\u043E\u0447\u0438\u0441\u0442\u0438\u0442\u0438",
4227
4235
  close: "\u0437\u0430\u043A\u0440\u0438\u0442\u0438",
@@ -8809,6 +8817,25 @@ function restoreCaretScopes(input, scopes) {
8809
8817
  (_m, n) => scopes[Number(n)] ?? ""
8810
8818
  );
8811
8819
  }
8820
+ var VIEW_TAIL_OPEN = "\uE002";
8821
+ var VIEW_TAIL_CLOSE = "\uE003";
8822
+ var VIEW_TAIL_RE = /\busing\s+view\b(?:\s+(?!then\b)[A-Za-z][\w-]*)?/gi;
8823
+ var VIEW_TAIL_TOKEN_RE = new RegExp(`^${VIEW_TAIL_OPEN}(\\d+)${VIEW_TAIL_CLOSE}$`);
8824
+ function maskViewTransitionTails(input) {
8825
+ const tails = [];
8826
+ const masked = input.replace(VIEW_TAIL_RE, (match) => {
8827
+ const idx = tails.length;
8828
+ tails.push(match);
8829
+ return `${VIEW_TAIL_OPEN}${idx}${VIEW_TAIL_CLOSE}`;
8830
+ });
8831
+ return tails.length > 0 ? { masked, tails } : null;
8832
+ }
8833
+ function restoreViewTransitionTails(input, tails) {
8834
+ return input.replace(
8835
+ new RegExp(`${VIEW_TAIL_OPEN}(\\d+)${VIEW_TAIL_CLOSE}`, "g"),
8836
+ (_m, n) => tails[Number(n)] ?? ""
8837
+ );
8838
+ }
8812
8839
  var GrammarTransformer = class {
8813
8840
  constructor(sourceLocale = "en", targetLocale) {
8814
8841
  const source = getProfile(sourceLocale);
@@ -8830,6 +8857,10 @@ var GrammarTransformer = class {
8830
8857
  return this.targetProfile.code === "he" ? repairHebrewFrontedAccusative(out) : out;
8831
8858
  }
8832
8859
  transformInternal(input) {
8860
+ const viewTails = maskViewTransitionTails(input);
8861
+ if (viewTails) {
8862
+ return restoreViewTransitionTails(this.transformInternal(viewTails.masked), viewTails.tails);
8863
+ }
8833
8864
  const caret = maskCaretScopes(input);
8834
8865
  if (caret) {
8835
8866
  return restoreCaretScopes(this.transform(caret.masked), caret.scopes);
@@ -8882,6 +8913,10 @@ var GrammarTransformer = class {
8882
8913
  if (setScope !== null) {
8883
8914
  return setScope;
8884
8915
  }
8916
+ const viewTail = this.transformWithViewTransitionTail(input);
8917
+ if (viewTail !== null) {
8918
+ return viewTail;
8919
+ }
8885
8920
  const parsed = parseStatement(input, this.sourceProfile.code);
8886
8921
  if (!parsed) {
8887
8922
  return input;
@@ -8905,6 +8940,35 @@ var GrammarTransformer = class {
8905
8940
  }
8906
8941
  return joinTokens(reordered.map((e) => e.translated || e.value));
8907
8942
  }
8943
+ /**
8944
+ * Clause carrying a masked `using view transition` tail: strip the opaque
8945
+ * token, transform the clause alone, and re-append the token at the very end.
8946
+ *
8947
+ * The tail is a clause-final modifier in every word order the corpus emits:
8948
+ * the semantic side matches it as the literal `using view` marker plus a value
8949
+ * word, and the SOV/VSO event-handler patterns admit it as an optional
8950
+ * TRAILING group (after the with-marked operand). So the target position is
8951
+ * "end of the transformed clause" for all 24 languages — no per-profile
8952
+ * placement decision, which is what makes this a passthrough rather than a
8953
+ * role.
8954
+ *
8955
+ * Returns null when the clause carries no masked tail, or when the token is
8956
+ * not clause-final (nothing to reposition — leaving it in place still restores
8957
+ * verbatim English).
8958
+ */
8959
+ transformWithViewTransitionTail(input) {
8960
+ const trimmed = input.trim();
8961
+ const tokens = trimmed.split(/\s+/);
8962
+ if (tokens.length < 2) {
8963
+ return null;
8964
+ }
8965
+ if (!VIEW_TAIL_TOKEN_RE.test(tokens[tokens.length - 1])) {
8966
+ return null;
8967
+ }
8968
+ const tail = tokens[tokens.length - 1];
8969
+ const head = tokens.slice(0, -1).join(" ");
8970
+ return `${this.transformSingle(head)} ${tail}`;
8971
+ }
8908
8972
  /**
8909
8973
  * `<command …> end` fragments: transform the command without its stranded
8910
8974
  * terminator, then re-append the translated terminator as a standalone