@lokascript/hyperscript-adapter 2.9.2 → 2.9.3

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 (45) hide show
  1. package/dist/hyperscript-i18n-ar.global.js +6 -5
  2. package/dist/hyperscript-i18n-bn.global.js +6 -5
  3. package/dist/hyperscript-i18n-de.global.js +6 -5
  4. package/dist/hyperscript-i18n-east-asian.global.js +6 -5
  5. package/dist/hyperscript-i18n-en.global.js +8 -7
  6. package/dist/hyperscript-i18n-es.global.js +6 -5
  7. package/dist/hyperscript-i18n-fr.global.js +6 -5
  8. package/dist/hyperscript-i18n-he.global.js +6 -5
  9. package/dist/hyperscript-i18n-hi.global.js +6 -5
  10. package/dist/hyperscript-i18n-id.global.js +6 -5
  11. package/dist/hyperscript-i18n-it.global.js +6 -5
  12. package/dist/hyperscript-i18n-ja.global.js +6 -5
  13. package/dist/hyperscript-i18n-ko.global.js +6 -5
  14. package/dist/hyperscript-i18n-lite.global.js +2 -1
  15. package/dist/hyperscript-i18n-ms.global.js +6 -5
  16. package/dist/hyperscript-i18n-pl.global.js +6 -5
  17. package/dist/hyperscript-i18n-pt.global.js +6 -5
  18. package/dist/hyperscript-i18n-qu.global.js +6 -5
  19. package/dist/hyperscript-i18n-ru.global.js +6 -5
  20. package/dist/hyperscript-i18n-slavic.global.js +6 -5
  21. package/dist/hyperscript-i18n-south-asian.global.js +6 -5
  22. package/dist/hyperscript-i18n-southeast-asian.global.js +6 -5
  23. package/dist/hyperscript-i18n-sw.global.js +6 -5
  24. package/dist/hyperscript-i18n-th.global.js +6 -5
  25. package/dist/hyperscript-i18n-tl.global.js +6 -5
  26. package/dist/hyperscript-i18n-tr.global.js +6 -5
  27. package/dist/hyperscript-i18n-uk.global.js +6 -5
  28. package/dist/hyperscript-i18n-vi.global.js +6 -5
  29. package/dist/hyperscript-i18n-western.global.js +6 -5
  30. package/dist/hyperscript-i18n-zh.global.js +6 -5
  31. package/dist/hyperscript-i18n.global.js +28 -28
  32. package/dist/hyperscript-i18n.global.js.map +1 -1
  33. package/dist/index.cjs +48 -7
  34. package/dist/index.cjs.map +1 -1
  35. package/dist/index.d.cts +2 -2
  36. package/dist/index.d.ts +2 -2
  37. package/dist/index.js +48 -7
  38. package/dist/index.js.map +1 -1
  39. package/package.json +2 -2
  40. package/src/attribute-translator.ts +96 -0
  41. package/src/browser-lite.ts +3 -11
  42. package/src/browser.ts +5 -36
  43. package/src/bundles/shared.ts +4 -35
  44. package/src/plugin.ts +5 -25
  45. package/src/slim-plugin.ts +3 -11
package/dist/index.cjs CHANGED
@@ -149,15 +149,56 @@ function translateCompound(statements, lang, threshold) {
149
149
  return translated.join(" then ");
150
150
  }
151
151
 
152
+ // src/attribute-translator.ts
153
+ var TRANSLATED_MARKER = "data-hyperscript-i18n-translated";
154
+ function scriptAttributeNames(hs) {
155
+ const raw = hs.config?.attributes ?? "_, script, data-script";
156
+ return raw.split(",").map((s) => s.trim()).filter(Boolean);
157
+ }
158
+ function findScriptAttribute(elt, attrNames) {
159
+ for (const name of attrNames) {
160
+ if (elt.hasAttribute(name)) return name;
161
+ }
162
+ return null;
163
+ }
164
+ function installAttributeTranslator(hs, translate2) {
165
+ if (typeof hs.addBeforeProcessHook !== "function") {
166
+ console.warn(
167
+ "[hyperscript-i18n] _hyperscript.addBeforeProcessHook is unavailable \u2014 this build of _hyperscript.org is not supported. Load a current version from https://unpkg.com/hyperscript.org."
168
+ );
169
+ return;
170
+ }
171
+ const attrNames = scriptAttributeNames(hs);
172
+ const selector = [...attrNames.map((a) => `[${a}]`), 'script[type="text/hyperscript"]'].join(", ");
173
+ const translateOne = (elt) => {
174
+ if (elt.hasAttribute(TRANSLATED_MARKER)) return;
175
+ if (elt instanceof HTMLScriptElement && elt.type === "text/hyperscript") {
176
+ const src2 = elt.textContent ?? "";
177
+ if (!src2) return;
178
+ const english2 = translate2(src2, elt);
179
+ elt.setAttribute(TRANSLATED_MARKER, "");
180
+ if (english2 != null && english2 !== src2) elt.textContent = english2;
181
+ return;
182
+ }
183
+ const attr = findScriptAttribute(elt, attrNames);
184
+ if (!attr) return;
185
+ const src = elt.getAttribute(attr);
186
+ if (!src) return;
187
+ const english = translate2(src, elt);
188
+ elt.setAttribute(TRANSLATED_MARKER, "");
189
+ if (english != null && english !== src) elt.setAttribute(attr, english);
190
+ };
191
+ hs.addBeforeProcessHook((root) => {
192
+ if (!root || typeof root.querySelectorAll !== "function") return;
193
+ if (typeof root.matches === "function" && root.matches(selector)) translateOne(root);
194
+ root.querySelectorAll(selector).forEach(translateOne);
195
+ });
196
+ }
197
+
152
198
  // src/plugin.ts
153
199
  function hyperscriptI18n(options = {}) {
154
200
  return function plugin(hs) {
155
- const { internals } = hs;
156
- const runtime = internals.runtime;
157
- const originalGetScript = runtime.getScript.bind(runtime);
158
- runtime.getScript = function(elt) {
159
- const src = originalGetScript(elt);
160
- if (!src) return null;
201
+ installAttributeTranslator(hs, (src, elt) => {
161
202
  const lang = resolveLanguageWithOptions(elt, options);
162
203
  if (!lang || lang === "en") return src;
163
204
  const english = preprocessToEnglish(src, lang, options);
@@ -171,7 +212,7 @@ function hyperscriptI18n(options = {}) {
171
212
  );
172
213
  }
173
214
  return english;
174
- };
215
+ });
175
216
  };
176
217
  }
177
218
  function resolveLanguageWithOptions(elt, options) {
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/language-resolver.ts","../src/preprocessor.ts","../src/plugin.ts"],"sourcesContent":["/**\n * @lokascript/hyperscript-adapter\n *\n * Multilingual adapter plugin for the original _hyperscript.\n * Enables writing hyperscript in any of 24 supported languages.\n *\n * @example\n * import { hyperscriptI18n } from '@lokascript/hyperscript-adapter';\n * _hyperscript.use(hyperscriptI18n());\n *\n * @example\n * // With options\n * _hyperscript.use(hyperscriptI18n({\n * defaultLanguage: 'ja',\n * debug: true,\n * }));\n *\n * @example\n * // Standalone preprocessing (for _hyperscript.evaluate() calls)\n * import { preprocess } from '@lokascript/hyperscript-adapter';\n * const english = preprocess('トグル .active', 'ja');\n * _hyperscript(english);\n */\n\nexport { hyperscriptI18n, preprocess, type PluginOptions } from './plugin';\nexport { preprocessToEnglish, type PreprocessorConfig } from './preprocessor';\nexport { resolveLanguage } from './language-resolver';\n","/**\n * Language Resolver\n *\n * Determines the language of hyperscript code on a given element\n * using a cascading resolution strategy.\n */\n\n/**\n * Resolve the language for a hyperscript attribute on an element.\n *\n * Resolution order:\n * 1. `data-lang` attribute on the element itself\n * 2. `data-hyperscript-lang` attribute on the element or closest ancestor\n * 3. `lang` attribute on `<html>` element\n * 4. null (assume English, no preprocessing needed)\n */\nexport function resolveLanguage(elt: Element): string | null {\n // 1. Explicit per-element\n const dataLang = elt.getAttribute('data-lang');\n if (dataLang) return normalizeLangCode(dataLang);\n\n // 2. Inherited from ancestor\n const hsLang =\n elt.getAttribute('data-hyperscript-lang') ??\n elt.closest?.('[data-hyperscript-lang]')?.getAttribute('data-hyperscript-lang');\n if (hsLang) return normalizeLangCode(hsLang);\n\n // 3. Document-level lang\n const htmlLang = typeof document !== 'undefined' ? document.documentElement?.lang : null;\n if (htmlLang && htmlLang !== 'en') return normalizeLangCode(htmlLang);\n\n return null;\n}\n\n/**\n * Normalize a BCP-47 language tag to an ISO 639-1 code.\n * e.g., \"ja-JP\" → \"ja\", \"zh-Hans\" → \"zh\", \"pt-BR\" → \"pt\"\n */\nfunction normalizeLangCode(lang: string): string {\n return lang.split('-')[0].toLowerCase();\n}\n","/**\n * Preprocessor\n *\n * Translates non-English hyperscript to English using the semantic parser\n * (with optional i18n fallback), so the original _hyperscript can parse it.\n */\n\nimport {\n translate,\n render,\n parseSemantic,\n isLanguageRegistered,\n tryGetProfile,\n} from '@lokascript/semantic';\n\nexport interface PreprocessorConfig {\n /**\n * Minimum confidence threshold for semantic parsing (0-1). Default: 0.5.\n * Can be a single number (applies to all languages) or a per-language map.\n * Per-language thresholds are useful because SOV languages (ja, ko, tr) produce\n * inherently lower confidence scores than SVO languages (es, fr, de).\n *\n * @example\n * // Single threshold\n * { confidenceThreshold: 0.5 }\n *\n * @example\n * // Per-language thresholds\n * { confidenceThreshold: { es: 0.7, ja: 0.1, ko: 0.05, '*': 0.5 } }\n */\n confidenceThreshold: number | Record<string, number>;\n /** Strategy: 'semantic' (default), 'i18n', or 'auto' (semantic then i18n) */\n strategy: 'semantic' | 'i18n' | 'auto';\n /** Whether to return original text on failure. Default: true */\n fallbackToOriginal: boolean;\n /** Optional i18n toEnglish function (loaded dynamically if available) */\n i18nToEnglish?: (input: string, locale: string) => string;\n}\n\nconst DEFAULT_THRESHOLD = 0.5;\n\nconst DEFAULT_CONFIG: PreprocessorConfig = {\n confidenceThreshold: DEFAULT_THRESHOLD,\n strategy: 'semantic',\n fallbackToOriginal: true,\n};\n\n/**\n * Resolve the confidence threshold for a specific language.\n * Supports both a single number and a per-language map with '*' as default.\n */\nfunction resolveThreshold(threshold: number | Record<string, number>, lang: string): number {\n if (typeof threshold === 'number') return threshold;\n return threshold[lang] ?? threshold['*'] ?? DEFAULT_THRESHOLD;\n}\n\n/**\n * Preprocess non-English hyperscript into English.\n *\n * Uses the semantic parser to parse the input in the source language,\n * then renders back to English. Falls through to i18n if configured.\n *\n * Handles _hyperscript feature prefixes (e.g. \"on click\", \"on every keyup\")\n * by stripping them, translating only the command portion, then reassembling.\n */\nexport function preprocessToEnglish(\n src: string,\n lang: string,\n config: Partial<PreprocessorConfig> = {}\n): string {\n // English→English is identity; skip semantic parsing which may mangle the input\n if (lang === 'en') return src;\n\n const cfg = { ...DEFAULT_CONFIG, ...config };\n\n // Try translating the full string first\n const fullResult = tryTranslateWithStrategies(src, lang, cfg);\n if (fullResult !== null) return fullResult;\n\n // If full translation failed, try stripping event/feature prefix.\n // _hyperscript attributes often contain \"on <event> <commands>\" — the semantic\n // parser only understands command syntax, not event declarations.\n const stripped = stripEventPrefix(src);\n if (stripped) {\n const translated = tryTranslateWithStrategies(stripped.commands, lang, cfg);\n if (translated !== null) return stripped.prefix + translated;\n }\n\n // Fallback: return original\n return cfg.fallbackToOriginal ? src : src;\n}\n\n/**\n * Try all configured translation strategies on the input.\n * Returns null if none succeed.\n */\nfunction tryTranslateWithStrategies(\n src: string,\n lang: string,\n cfg: PreprocessorConfig\n): string | null {\n // Strategy: semantic-only\n if (cfg.strategy === 'semantic' || cfg.strategy === 'auto') {\n const threshold = resolveThreshold(cfg.confidenceThreshold, lang);\n const result = trySemanticTranslation(src, lang, threshold);\n if (result !== null) return result;\n }\n\n // Strategy: i18n fallback (auto mode or i18n-only)\n if ((cfg.strategy === 'auto' || cfg.strategy === 'i18n') && cfg.i18nToEnglish) {\n const result = tryI18nTranslation(src, lang, cfg.i18nToEnglish);\n if (result !== null) return result;\n }\n\n return null;\n}\n\n/**\n * Match _hyperscript event handler prefix: \"on [every] <event>[filter][.modifiers] \"\n *\n * Examples:\n * \"on click toggle .active\" → prefix: \"on click \", commands: \"toggle .active\"\n * \"on every click toggle .active\" → prefix: \"on every click \", commands: \"toggle .active\"\n * \"on click.debounce(300) toggle .x\" → prefix: \"on click.debounce(300) \", commands: \"toggle .x\"\n * \"on keyup[key=='Enter'] set x to 1\" → prefix: \"on keyup[key=='Enter'] \", commands: \"set x to 1\"\n * \"on click from body toggle .active\" → prefix: \"on click from body \", commands: \"toggle .active\"\n */\nconst EVENT_PREFIX_RE =\n /^(on\\s+(?:every\\s+)?[\\w-]+(?:\\[.*?\\])?(?:\\.[\\w-]+(?:\\([^)]*\\))?)*(?:\\s+from\\s+\\S+)?(?:\\s+queue\\s+\\w+)?\\s+)/;\n\nfunction stripEventPrefix(src: string): { prefix: string; commands: string } | null {\n const match = src.match(EVENT_PREFIX_RE);\n if (!match) return null;\n const prefix = match[1];\n const commands = src.slice(prefix.length);\n if (!commands) return null;\n return { prefix, commands };\n}\n\n/**\n * Try semantic translation: parse in source language, render to English.\n * Returns null if confidence is below threshold.\n */\nfunction trySemanticTranslation(src: string, lang: string, threshold: number): string | null {\n try {\n // Handle compound statements (split on \"then\" boundaries and newlines)\n const statements = splitStatements(src, lang);\n if (statements.length > 1) {\n return translateCompound(statements, lang, threshold);\n }\n\n // Check if the semantic parser can handle this\n if (!isLanguageRegistered(lang)) return null;\n\n const result = parseSemantic(src, lang);\n if (result.confidence < threshold || !result.node) {\n // If we got SOME confidence but no node, fall back to translate()\n if (result.confidence >= threshold) {\n return translate(src, lang, 'en');\n }\n return null;\n }\n\n // Render the semantic node to English\n return render(result.node, 'en');\n } catch {\n return null;\n }\n}\n\n/**\n * Try i18n grammar transformation to English.\n * Returns null if the result is identical to input (no translation happened).\n */\nfunction tryI18nTranslation(\n src: string,\n lang: string,\n toEnglish: (input: string, locale: string) => string\n): string | null {\n try {\n const result = toEnglish(src, lang);\n return result !== src ? result : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Get all \"then\" keyword forms for a language (primary + alternatives).\n * Falls back to English \"then\" if profile is unavailable.\n */\nfunction getThenKeywords(lang: string): string[] {\n const keywords = ['then']; // Always include English\n const profile = tryGetProfile(lang);\n if (profile?.keywords?.then) {\n const kw = profile.keywords.then;\n if (kw.primary && kw.primary !== 'then') keywords.push(kw.primary);\n if (kw.alternatives) {\n for (const alt of kw.alternatives) {\n if (!keywords.includes(alt)) keywords.push(alt);\n }\n }\n }\n return keywords;\n}\n\n/**\n * Split a hyperscript source string into individual statements.\n * Splits on language-specific \"then\" keywords and newlines.\n */\nfunction splitStatements(src: string, lang: string): string[] {\n const thenWords = getThenKeywords(lang);\n // Escape regex special chars and build pattern\n const escaped = thenWords.map(w => w.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'));\n const pattern = new RegExp(`\\\\s+(?:${escaped.join('|')})\\\\s+`, 'i');\n\n // Split on newlines first\n const lines = src\n .split('\\n')\n .map(l => l.trim())\n .filter(l => l.length > 0);\n const result: string[] = [];\n for (const line of lines) {\n const parts = line.split(pattern);\n result.push(...parts);\n }\n return result;\n}\n\n/**\n * Translate compound statements individually and rejoin with \" then \".\n */\nfunction translateCompound(statements: string[], lang: string, threshold: number): string | null {\n const translated: string[] = [];\n for (const stmt of statements) {\n const result = trySemanticTranslation(stmt.trim(), lang, threshold);\n if (result === null) return null; // If any part fails, fail the whole compound\n translated.push(result);\n }\n return translated.join(' then ');\n}\n","/**\n * _hyperscript Plugin\n *\n * Registers with _hyperscript.use() to intercept getScript() and\n * preprocess non-English hyperscript into English before parsing.\n */\n\nimport { resolveLanguage } from './language-resolver';\nimport { preprocessToEnglish, type PreprocessorConfig } from './preprocessor';\n\n/**\n * The _hyperscript global object shape (minimal subset we need).\n */\ninterface HyperscriptGlobal {\n internals: {\n runtime: HyperscriptRuntime;\n };\n config: Record<string, unknown>;\n}\n\ninterface HyperscriptRuntime {\n getScript(elt: Element): string | null;\n}\n\nexport interface PluginOptions extends Partial<PreprocessorConfig> {\n /** Default language for all elements (overridable per-element). */\n defaultLanguage?: string;\n /** Custom attribute name for per-element language. Default: \"data-lang\" */\n languageAttribute?: string;\n /** Enable debug logging to console. Default: false */\n debug?: boolean;\n}\n\n/**\n * Create a _hyperscript plugin that enables multilingual hyperscript.\n *\n * @example\n * // Basic usage\n * _hyperscript.use(hyperscriptI18n());\n *\n * @example\n * // With options\n * _hyperscript.use(hyperscriptI18n({\n * defaultLanguage: 'ja',\n * confidenceThreshold: 0.6,\n * debug: true,\n * }));\n */\nexport function hyperscriptI18n(options: PluginOptions = {}) {\n return function plugin(hs: unknown): void {\n const { internals } = hs as HyperscriptGlobal;\n const runtime = internals.runtime;\n const originalGetScript = runtime.getScript.bind(runtime);\n\n runtime.getScript = function (elt: Element): string | null {\n const src = originalGetScript(elt);\n if (!src) return null;\n\n // Resolve language\n const lang = resolveLanguageWithOptions(elt, options);\n\n // If English or no language detected, pass through\n if (!lang || lang === 'en') return src;\n\n // Preprocess to English\n const english = preprocessToEnglish(src, lang, options);\n\n if (english !== src) {\n if (options.debug) {\n console.log(`[hyperscript-i18n] ${lang}: \"${src}\" → \"${english}\"`);\n }\n } else {\n // Translation produced no change — likely a failure\n console.warn(\n `[hyperscript-i18n] Translation unchanged for lang=\"${lang}\": \"${src.length > 60 ? src.slice(0, 60) + '…' : src}\". ` +\n 'The input may not match any known pattern, or the language may not be registered. ' +\n 'Original text will be passed to _hyperscript as-is.'\n );\n }\n\n return english;\n };\n };\n}\n\n/**\n * Resolve language with plugin options factored in.\n */\nfunction resolveLanguageWithOptions(elt: Element, options: PluginOptions): string | null {\n // Check custom attribute name\n if (options.languageAttribute) {\n const custom = elt.getAttribute(options.languageAttribute);\n if (custom) return custom.split('-')[0].toLowerCase();\n }\n\n // Standard resolution\n const resolved = resolveLanguage(elt);\n if (resolved) return resolved;\n\n // Fall back to default language\n return options.defaultLanguage ?? null;\n}\n\n/**\n * Standalone preprocessing function for programmatic use.\n * Call this when using _hyperscript.evaluate() or _hyperscript(\"code\") directly.\n *\n * @example\n * const english = preprocess(\"トグル .active\", \"ja\");\n * _hyperscript(english);\n */\nexport function preprocess(\n src: string,\n lang: string,\n config: Partial<PreprocessorConfig> = {}\n): string {\n if (lang === 'en') return src;\n return preprocessToEnglish(src, lang, config);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/index.ts","../src/language-resolver.ts","../src/preprocessor.ts","../src/attribute-translator.ts","../src/plugin.ts"],"sourcesContent":["/**\n * @lokascript/hyperscript-adapter\n *\n * Multilingual adapter plugin for the original _hyperscript.\n * Enables writing hyperscript in any of 24 supported languages.\n *\n * @example\n * import { hyperscriptI18n } from '@lokascript/hyperscript-adapter';\n * _hyperscript.use(hyperscriptI18n());\n *\n * @example\n * // With options\n * _hyperscript.use(hyperscriptI18n({\n * defaultLanguage: 'ja',\n * debug: true,\n * }));\n *\n * @example\n * // Standalone preprocessing (for _hyperscript.evaluate() calls)\n * import { preprocess } from '@lokascript/hyperscript-adapter';\n * const english = preprocess('トグル .active', 'ja');\n * _hyperscript(english);\n */\n\nexport { hyperscriptI18n, preprocess, type PluginOptions } from './plugin';\nexport { preprocessToEnglish, type PreprocessorConfig } from './preprocessor';\nexport { resolveLanguage } from './language-resolver';\n","/**\n * Language Resolver\n *\n * Determines the language of hyperscript code on a given element\n * using a cascading resolution strategy.\n */\n\n/**\n * Resolve the language for a hyperscript attribute on an element.\n *\n * Resolution order:\n * 1. `data-lang` attribute on the element itself\n * 2. `data-hyperscript-lang` attribute on the element or closest ancestor\n * 3. `lang` attribute on `<html>` element\n * 4. null (assume English, no preprocessing needed)\n */\nexport function resolveLanguage(elt: Element): string | null {\n // 1. Explicit per-element\n const dataLang = elt.getAttribute('data-lang');\n if (dataLang) return normalizeLangCode(dataLang);\n\n // 2. Inherited from ancestor\n const hsLang =\n elt.getAttribute('data-hyperscript-lang') ??\n elt.closest?.('[data-hyperscript-lang]')?.getAttribute('data-hyperscript-lang');\n if (hsLang) return normalizeLangCode(hsLang);\n\n // 3. Document-level lang\n const htmlLang = typeof document !== 'undefined' ? document.documentElement?.lang : null;\n if (htmlLang && htmlLang !== 'en') return normalizeLangCode(htmlLang);\n\n return null;\n}\n\n/**\n * Normalize a BCP-47 language tag to an ISO 639-1 code.\n * e.g., \"ja-JP\" → \"ja\", \"zh-Hans\" → \"zh\", \"pt-BR\" → \"pt\"\n */\nfunction normalizeLangCode(lang: string): string {\n return lang.split('-')[0].toLowerCase();\n}\n","/**\n * Preprocessor\n *\n * Translates non-English hyperscript to English using the semantic parser\n * (with optional i18n fallback), so the original _hyperscript can parse it.\n */\n\nimport {\n translate,\n render,\n parseSemantic,\n isLanguageRegistered,\n tryGetProfile,\n} from '@lokascript/semantic';\n\nexport interface PreprocessorConfig {\n /**\n * Minimum confidence threshold for semantic parsing (0-1). Default: 0.5.\n * Can be a single number (applies to all languages) or a per-language map.\n * Per-language thresholds are useful because SOV languages (ja, ko, tr) produce\n * inherently lower confidence scores than SVO languages (es, fr, de).\n *\n * @example\n * // Single threshold\n * { confidenceThreshold: 0.5 }\n *\n * @example\n * // Per-language thresholds\n * { confidenceThreshold: { es: 0.7, ja: 0.1, ko: 0.05, '*': 0.5 } }\n */\n confidenceThreshold: number | Record<string, number>;\n /** Strategy: 'semantic' (default), 'i18n', or 'auto' (semantic then i18n) */\n strategy: 'semantic' | 'i18n' | 'auto';\n /** Whether to return original text on failure. Default: true */\n fallbackToOriginal: boolean;\n /** Optional i18n toEnglish function (loaded dynamically if available) */\n i18nToEnglish?: (input: string, locale: string) => string;\n}\n\nconst DEFAULT_THRESHOLD = 0.5;\n\nconst DEFAULT_CONFIG: PreprocessorConfig = {\n confidenceThreshold: DEFAULT_THRESHOLD,\n strategy: 'semantic',\n fallbackToOriginal: true,\n};\n\n/**\n * Resolve the confidence threshold for a specific language.\n * Supports both a single number and a per-language map with '*' as default.\n */\nfunction resolveThreshold(threshold: number | Record<string, number>, lang: string): number {\n if (typeof threshold === 'number') return threshold;\n return threshold[lang] ?? threshold['*'] ?? DEFAULT_THRESHOLD;\n}\n\n/**\n * Preprocess non-English hyperscript into English.\n *\n * Uses the semantic parser to parse the input in the source language,\n * then renders back to English. Falls through to i18n if configured.\n *\n * Handles _hyperscript feature prefixes (e.g. \"on click\", \"on every keyup\")\n * by stripping them, translating only the command portion, then reassembling.\n */\nexport function preprocessToEnglish(\n src: string,\n lang: string,\n config: Partial<PreprocessorConfig> = {}\n): string {\n // English→English is identity; skip semantic parsing which may mangle the input\n if (lang === 'en') return src;\n\n const cfg = { ...DEFAULT_CONFIG, ...config };\n\n // Try translating the full string first\n const fullResult = tryTranslateWithStrategies(src, lang, cfg);\n if (fullResult !== null) return fullResult;\n\n // If full translation failed, try stripping event/feature prefix.\n // _hyperscript attributes often contain \"on <event> <commands>\" — the semantic\n // parser only understands command syntax, not event declarations.\n const stripped = stripEventPrefix(src);\n if (stripped) {\n const translated = tryTranslateWithStrategies(stripped.commands, lang, cfg);\n if (translated !== null) return stripped.prefix + translated;\n }\n\n // Fallback: return original\n return cfg.fallbackToOriginal ? src : src;\n}\n\n/**\n * Try all configured translation strategies on the input.\n * Returns null if none succeed.\n */\nfunction tryTranslateWithStrategies(\n src: string,\n lang: string,\n cfg: PreprocessorConfig\n): string | null {\n // Strategy: semantic-only\n if (cfg.strategy === 'semantic' || cfg.strategy === 'auto') {\n const threshold = resolveThreshold(cfg.confidenceThreshold, lang);\n const result = trySemanticTranslation(src, lang, threshold);\n if (result !== null) return result;\n }\n\n // Strategy: i18n fallback (auto mode or i18n-only)\n if ((cfg.strategy === 'auto' || cfg.strategy === 'i18n') && cfg.i18nToEnglish) {\n const result = tryI18nTranslation(src, lang, cfg.i18nToEnglish);\n if (result !== null) return result;\n }\n\n return null;\n}\n\n/**\n * Match _hyperscript event handler prefix: \"on [every] <event>[filter][.modifiers] \"\n *\n * Examples:\n * \"on click toggle .active\" → prefix: \"on click \", commands: \"toggle .active\"\n * \"on every click toggle .active\" → prefix: \"on every click \", commands: \"toggle .active\"\n * \"on click.debounce(300) toggle .x\" → prefix: \"on click.debounce(300) \", commands: \"toggle .x\"\n * \"on keyup[key=='Enter'] set x to 1\" → prefix: \"on keyup[key=='Enter'] \", commands: \"set x to 1\"\n * \"on click from body toggle .active\" → prefix: \"on click from body \", commands: \"toggle .active\"\n */\nconst EVENT_PREFIX_RE =\n /^(on\\s+(?:every\\s+)?[\\w-]+(?:\\[.*?\\])?(?:\\.[\\w-]+(?:\\([^)]*\\))?)*(?:\\s+from\\s+\\S+)?(?:\\s+queue\\s+\\w+)?\\s+)/;\n\nfunction stripEventPrefix(src: string): { prefix: string; commands: string } | null {\n const match = src.match(EVENT_PREFIX_RE);\n if (!match) return null;\n const prefix = match[1];\n const commands = src.slice(prefix.length);\n if (!commands) return null;\n return { prefix, commands };\n}\n\n/**\n * Try semantic translation: parse in source language, render to English.\n * Returns null if confidence is below threshold.\n */\nfunction trySemanticTranslation(src: string, lang: string, threshold: number): string | null {\n try {\n // Handle compound statements (split on \"then\" boundaries and newlines)\n const statements = splitStatements(src, lang);\n if (statements.length > 1) {\n return translateCompound(statements, lang, threshold);\n }\n\n // Check if the semantic parser can handle this\n if (!isLanguageRegistered(lang)) return null;\n\n const result = parseSemantic(src, lang);\n if (result.confidence < threshold || !result.node) {\n // If we got SOME confidence but no node, fall back to translate()\n if (result.confidence >= threshold) {\n return translate(src, lang, 'en');\n }\n return null;\n }\n\n // Render the semantic node to English\n return render(result.node, 'en');\n } catch {\n return null;\n }\n}\n\n/**\n * Try i18n grammar transformation to English.\n * Returns null if the result is identical to input (no translation happened).\n */\nfunction tryI18nTranslation(\n src: string,\n lang: string,\n toEnglish: (input: string, locale: string) => string\n): string | null {\n try {\n const result = toEnglish(src, lang);\n return result !== src ? result : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Get all \"then\" keyword forms for a language (primary + alternatives).\n * Falls back to English \"then\" if profile is unavailable.\n */\nfunction getThenKeywords(lang: string): string[] {\n const keywords = ['then']; // Always include English\n const profile = tryGetProfile(lang);\n if (profile?.keywords?.then) {\n const kw = profile.keywords.then;\n if (kw.primary && kw.primary !== 'then') keywords.push(kw.primary);\n if (kw.alternatives) {\n for (const alt of kw.alternatives) {\n if (!keywords.includes(alt)) keywords.push(alt);\n }\n }\n }\n return keywords;\n}\n\n/**\n * Split a hyperscript source string into individual statements.\n * Splits on language-specific \"then\" keywords and newlines.\n */\nfunction splitStatements(src: string, lang: string): string[] {\n const thenWords = getThenKeywords(lang);\n // Escape regex special chars and build pattern\n const escaped = thenWords.map(w => w.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'));\n const pattern = new RegExp(`\\\\s+(?:${escaped.join('|')})\\\\s+`, 'i');\n\n // Split on newlines first\n const lines = src\n .split('\\n')\n .map(l => l.trim())\n .filter(l => l.length > 0);\n const result: string[] = [];\n for (const line of lines) {\n const parts = line.split(pattern);\n result.push(...parts);\n }\n return result;\n}\n\n/**\n * Translate compound statements individually and rejoin with \" then \".\n */\nfunction translateCompound(statements: string[], lang: string, threshold: number): string | null {\n const translated: string[] = [];\n for (const stmt of statements) {\n const result = trySemanticTranslation(stmt.trim(), lang, threshold);\n if (result === null) return null; // If any part fails, fail the whole compound\n translated.push(result);\n }\n return translated.join(' then ');\n}\n","/**\n * Attribute translator\n *\n * _hyperscript.org's Runtime#getScript is a private class field (`#getScript`),\n * not a property reachable through `internals.runtime` — every plugin variant\n * in this package used to monkey-patch `internals.runtime.getScript`, which\n * silently no-ops against current _hyperscript.org builds (the assignment\n * creates a stray own-property that the runtime's internal `#getScript()`\n * calls never read, so translation never happens and errors are swallowed by\n * `runtime.getScript.bind` throwing before any hyperscript even parses).\n *\n * `addBeforeProcessHook` is the supported, public extension point instead: it\n * fires on the subtree root passed to `processNode()` before the runtime reads\n * whichever configured attribute (`_`, `script`, `data-script` by default) or\n * `<script type=\"text/hyperscript\">` body holds the source. Rewriting that\n * attribute/body in place — before the runtime's own scan reaches it — gets\n * the same \"translate before parse\" effect through a mechanism the runtime\n * actually calls.\n */\n\nexport interface HyperscriptHost {\n addBeforeProcessHook?: (fn: (elt: Element) => void) => void;\n config?: { attributes?: string };\n}\n\n/** Marks an element as already translated so a later `processNode()` call over\n * the same subtree (e.g. a sibling swap re-scanning a shared ancestor) doesn't\n * re-translate already-English text as if it were still the original language. */\nconst TRANSLATED_MARKER = 'data-hyperscript-i18n-translated';\n\nfunction scriptAttributeNames(hs: HyperscriptHost): string[] {\n const raw = hs.config?.attributes ?? '_, script, data-script';\n return raw\n .split(',')\n .map(s => s.trim())\n .filter(Boolean);\n}\n\nfunction findScriptAttribute(elt: Element, attrNames: string[]): string | null {\n for (const name of attrNames) {\n if (elt.hasAttribute(name)) return name;\n }\n return null;\n}\n\n/**\n * Install a translator that rewrites non-English script attributes to English\n * in place, before `_hyperscript.org` parses them.\n *\n * @param translate Given the raw source and its element, return the English\n * translation, or `null`/the same string to leave the element untouched\n * (English input, unresolved language, translation failure, etc).\n */\nexport function installAttributeTranslator(\n hs: HyperscriptHost,\n translate: (src: string, elt: Element) => string | null\n): void {\n if (typeof hs.addBeforeProcessHook !== 'function') {\n console.warn(\n '[hyperscript-i18n] _hyperscript.addBeforeProcessHook is unavailable — ' +\n 'this build of _hyperscript.org is not supported. Load a current version ' +\n 'from https://unpkg.com/hyperscript.org.'\n );\n return;\n }\n\n const attrNames = scriptAttributeNames(hs);\n const selector = [...attrNames.map(a => `[${a}]`), 'script[type=\"text/hyperscript\"]'].join(', ');\n\n const translateOne = (elt: Element): void => {\n if (elt.hasAttribute(TRANSLATED_MARKER)) return;\n\n if (elt instanceof HTMLScriptElement && elt.type === 'text/hyperscript') {\n const src = elt.textContent ?? '';\n if (!src) return;\n const english = translate(src, elt);\n elt.setAttribute(TRANSLATED_MARKER, '');\n if (english != null && english !== src) elt.textContent = english;\n return;\n }\n\n const attr = findScriptAttribute(elt, attrNames);\n if (!attr) return;\n const src = elt.getAttribute(attr);\n if (!src) return;\n const english = translate(src, elt);\n elt.setAttribute(TRANSLATED_MARKER, '');\n if (english != null && english !== src) elt.setAttribute(attr, english);\n };\n\n hs.addBeforeProcessHook((root: Element) => {\n if (!root || typeof root.querySelectorAll !== 'function') return;\n if (typeof root.matches === 'function' && root.matches(selector)) translateOne(root);\n root.querySelectorAll(selector).forEach(translateOne);\n });\n}\n","/**\n * _hyperscript Plugin\n *\n * Registers with _hyperscript.use() to rewrite non-English hyperscript\n * attributes into English before _hyperscript.org parses them.\n */\n\nimport { resolveLanguage } from './language-resolver';\nimport { preprocessToEnglish, type PreprocessorConfig } from './preprocessor';\nimport { installAttributeTranslator, type HyperscriptHost } from './attribute-translator';\n\nexport interface PluginOptions extends Partial<PreprocessorConfig> {\n /** Default language for all elements (overridable per-element). */\n defaultLanguage?: string;\n /** Custom attribute name for per-element language. Default: \"data-lang\" */\n languageAttribute?: string;\n /** Enable debug logging to console. Default: false */\n debug?: boolean;\n}\n\n/**\n * Create a _hyperscript plugin that enables multilingual hyperscript.\n *\n * @example\n * // Basic usage\n * _hyperscript.use(hyperscriptI18n());\n *\n * @example\n * // With options\n * _hyperscript.use(hyperscriptI18n({\n * defaultLanguage: 'ja',\n * confidenceThreshold: 0.6,\n * debug: true,\n * }));\n */\nexport function hyperscriptI18n(options: PluginOptions = {}) {\n return function plugin(hs: unknown): void {\n installAttributeTranslator(hs as HyperscriptHost, (src, elt) => {\n // Resolve language\n const lang = resolveLanguageWithOptions(elt, options);\n\n // If English or no language detected, pass through\n if (!lang || lang === 'en') return src;\n\n // Preprocess to English\n const english = preprocessToEnglish(src, lang, options);\n\n if (english !== src) {\n if (options.debug) {\n console.log(`[hyperscript-i18n] ${lang}: \"${src}\" → \"${english}\"`);\n }\n } else {\n // Translation produced no change — likely a failure\n console.warn(\n `[hyperscript-i18n] Translation unchanged for lang=\"${lang}\": \"${src.length > 60 ? src.slice(0, 60) + '…' : src}\". ` +\n 'The input may not match any known pattern, or the language may not be registered. ' +\n 'Original text will be passed to _hyperscript as-is.'\n );\n }\n\n return english;\n });\n };\n}\n\n/**\n * Resolve language with plugin options factored in.\n */\nfunction resolveLanguageWithOptions(elt: Element, options: PluginOptions): string | null {\n // Check custom attribute name\n if (options.languageAttribute) {\n const custom = elt.getAttribute(options.languageAttribute);\n if (custom) return custom.split('-')[0].toLowerCase();\n }\n\n // Standard resolution\n const resolved = resolveLanguage(elt);\n if (resolved) return resolved;\n\n // Fall back to default language\n return options.defaultLanguage ?? null;\n}\n\n/**\n * Standalone preprocessing function for programmatic use.\n * Call this when using _hyperscript.evaluate() or _hyperscript(\"code\") directly.\n *\n * @example\n * const english = preprocess(\"トグル .active\", \"ja\");\n * _hyperscript(english);\n */\nexport function preprocess(\n src: string,\n lang: string,\n config: Partial<PreprocessorConfig> = {}\n): string {\n if (lang === 'en') return src;\n return preprocessToEnglish(src, lang, config);\n}\n"],"mappings":"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","names":["translate","src","english"]}
package/dist/index.d.cts CHANGED
@@ -41,8 +41,8 @@ declare function preprocessToEnglish(src: string, lang: string, config?: Partial
41
41
  /**
42
42
  * _hyperscript Plugin
43
43
  *
44
- * Registers with _hyperscript.use() to intercept getScript() and
45
- * preprocess non-English hyperscript into English before parsing.
44
+ * Registers with _hyperscript.use() to rewrite non-English hyperscript
45
+ * attributes into English before _hyperscript.org parses them.
46
46
  */
47
47
 
48
48
  interface PluginOptions extends Partial<PreprocessorConfig> {
package/dist/index.d.ts CHANGED
@@ -41,8 +41,8 @@ declare function preprocessToEnglish(src: string, lang: string, config?: Partial
41
41
  /**
42
42
  * _hyperscript Plugin
43
43
  *
44
- * Registers with _hyperscript.use() to intercept getScript() and
45
- * preprocess non-English hyperscript into English before parsing.
44
+ * Registers with _hyperscript.use() to rewrite non-English hyperscript
45
+ * attributes into English before _hyperscript.org parses them.
46
46
  */
47
47
 
48
48
  interface PluginOptions extends Partial<PreprocessorConfig> {
package/dist/index.js CHANGED
@@ -126,15 +126,56 @@ function translateCompound(statements, lang, threshold) {
126
126
  return translated.join(" then ");
127
127
  }
128
128
 
129
+ // src/attribute-translator.ts
130
+ var TRANSLATED_MARKER = "data-hyperscript-i18n-translated";
131
+ function scriptAttributeNames(hs) {
132
+ const raw = hs.config?.attributes ?? "_, script, data-script";
133
+ return raw.split(",").map((s) => s.trim()).filter(Boolean);
134
+ }
135
+ function findScriptAttribute(elt, attrNames) {
136
+ for (const name of attrNames) {
137
+ if (elt.hasAttribute(name)) return name;
138
+ }
139
+ return null;
140
+ }
141
+ function installAttributeTranslator(hs, translate2) {
142
+ if (typeof hs.addBeforeProcessHook !== "function") {
143
+ console.warn(
144
+ "[hyperscript-i18n] _hyperscript.addBeforeProcessHook is unavailable \u2014 this build of _hyperscript.org is not supported. Load a current version from https://unpkg.com/hyperscript.org."
145
+ );
146
+ return;
147
+ }
148
+ const attrNames = scriptAttributeNames(hs);
149
+ const selector = [...attrNames.map((a) => `[${a}]`), 'script[type="text/hyperscript"]'].join(", ");
150
+ const translateOne = (elt) => {
151
+ if (elt.hasAttribute(TRANSLATED_MARKER)) return;
152
+ if (elt instanceof HTMLScriptElement && elt.type === "text/hyperscript") {
153
+ const src2 = elt.textContent ?? "";
154
+ if (!src2) return;
155
+ const english2 = translate2(src2, elt);
156
+ elt.setAttribute(TRANSLATED_MARKER, "");
157
+ if (english2 != null && english2 !== src2) elt.textContent = english2;
158
+ return;
159
+ }
160
+ const attr = findScriptAttribute(elt, attrNames);
161
+ if (!attr) return;
162
+ const src = elt.getAttribute(attr);
163
+ if (!src) return;
164
+ const english = translate2(src, elt);
165
+ elt.setAttribute(TRANSLATED_MARKER, "");
166
+ if (english != null && english !== src) elt.setAttribute(attr, english);
167
+ };
168
+ hs.addBeforeProcessHook((root) => {
169
+ if (!root || typeof root.querySelectorAll !== "function") return;
170
+ if (typeof root.matches === "function" && root.matches(selector)) translateOne(root);
171
+ root.querySelectorAll(selector).forEach(translateOne);
172
+ });
173
+ }
174
+
129
175
  // src/plugin.ts
130
176
  function hyperscriptI18n(options = {}) {
131
177
  return function plugin(hs) {
132
- const { internals } = hs;
133
- const runtime = internals.runtime;
134
- const originalGetScript = runtime.getScript.bind(runtime);
135
- runtime.getScript = function(elt) {
136
- const src = originalGetScript(elt);
137
- if (!src) return null;
178
+ installAttributeTranslator(hs, (src, elt) => {
138
179
  const lang = resolveLanguageWithOptions(elt, options);
139
180
  if (!lang || lang === "en") return src;
140
181
  const english = preprocessToEnglish(src, lang, options);
@@ -148,7 +189,7 @@ function hyperscriptI18n(options = {}) {
148
189
  );
149
190
  }
150
191
  return english;
151
- };
192
+ });
152
193
  };
153
194
  }
154
195
  function resolveLanguageWithOptions(elt, options) {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/language-resolver.ts","../src/preprocessor.ts","../src/plugin.ts"],"sourcesContent":["/**\n * Language Resolver\n *\n * Determines the language of hyperscript code on a given element\n * using a cascading resolution strategy.\n */\n\n/**\n * Resolve the language for a hyperscript attribute on an element.\n *\n * Resolution order:\n * 1. `data-lang` attribute on the element itself\n * 2. `data-hyperscript-lang` attribute on the element or closest ancestor\n * 3. `lang` attribute on `<html>` element\n * 4. null (assume English, no preprocessing needed)\n */\nexport function resolveLanguage(elt: Element): string | null {\n // 1. Explicit per-element\n const dataLang = elt.getAttribute('data-lang');\n if (dataLang) return normalizeLangCode(dataLang);\n\n // 2. Inherited from ancestor\n const hsLang =\n elt.getAttribute('data-hyperscript-lang') ??\n elt.closest?.('[data-hyperscript-lang]')?.getAttribute('data-hyperscript-lang');\n if (hsLang) return normalizeLangCode(hsLang);\n\n // 3. Document-level lang\n const htmlLang = typeof document !== 'undefined' ? document.documentElement?.lang : null;\n if (htmlLang && htmlLang !== 'en') return normalizeLangCode(htmlLang);\n\n return null;\n}\n\n/**\n * Normalize a BCP-47 language tag to an ISO 639-1 code.\n * e.g., \"ja-JP\" → \"ja\", \"zh-Hans\" → \"zh\", \"pt-BR\" → \"pt\"\n */\nfunction normalizeLangCode(lang: string): string {\n return lang.split('-')[0].toLowerCase();\n}\n","/**\n * Preprocessor\n *\n * Translates non-English hyperscript to English using the semantic parser\n * (with optional i18n fallback), so the original _hyperscript can parse it.\n */\n\nimport {\n translate,\n render,\n parseSemantic,\n isLanguageRegistered,\n tryGetProfile,\n} from '@lokascript/semantic';\n\nexport interface PreprocessorConfig {\n /**\n * Minimum confidence threshold for semantic parsing (0-1). Default: 0.5.\n * Can be a single number (applies to all languages) or a per-language map.\n * Per-language thresholds are useful because SOV languages (ja, ko, tr) produce\n * inherently lower confidence scores than SVO languages (es, fr, de).\n *\n * @example\n * // Single threshold\n * { confidenceThreshold: 0.5 }\n *\n * @example\n * // Per-language thresholds\n * { confidenceThreshold: { es: 0.7, ja: 0.1, ko: 0.05, '*': 0.5 } }\n */\n confidenceThreshold: number | Record<string, number>;\n /** Strategy: 'semantic' (default), 'i18n', or 'auto' (semantic then i18n) */\n strategy: 'semantic' | 'i18n' | 'auto';\n /** Whether to return original text on failure. Default: true */\n fallbackToOriginal: boolean;\n /** Optional i18n toEnglish function (loaded dynamically if available) */\n i18nToEnglish?: (input: string, locale: string) => string;\n}\n\nconst DEFAULT_THRESHOLD = 0.5;\n\nconst DEFAULT_CONFIG: PreprocessorConfig = {\n confidenceThreshold: DEFAULT_THRESHOLD,\n strategy: 'semantic',\n fallbackToOriginal: true,\n};\n\n/**\n * Resolve the confidence threshold for a specific language.\n * Supports both a single number and a per-language map with '*' as default.\n */\nfunction resolveThreshold(threshold: number | Record<string, number>, lang: string): number {\n if (typeof threshold === 'number') return threshold;\n return threshold[lang] ?? threshold['*'] ?? DEFAULT_THRESHOLD;\n}\n\n/**\n * Preprocess non-English hyperscript into English.\n *\n * Uses the semantic parser to parse the input in the source language,\n * then renders back to English. Falls through to i18n if configured.\n *\n * Handles _hyperscript feature prefixes (e.g. \"on click\", \"on every keyup\")\n * by stripping them, translating only the command portion, then reassembling.\n */\nexport function preprocessToEnglish(\n src: string,\n lang: string,\n config: Partial<PreprocessorConfig> = {}\n): string {\n // English→English is identity; skip semantic parsing which may mangle the input\n if (lang === 'en') return src;\n\n const cfg = { ...DEFAULT_CONFIG, ...config };\n\n // Try translating the full string first\n const fullResult = tryTranslateWithStrategies(src, lang, cfg);\n if (fullResult !== null) return fullResult;\n\n // If full translation failed, try stripping event/feature prefix.\n // _hyperscript attributes often contain \"on <event> <commands>\" — the semantic\n // parser only understands command syntax, not event declarations.\n const stripped = stripEventPrefix(src);\n if (stripped) {\n const translated = tryTranslateWithStrategies(stripped.commands, lang, cfg);\n if (translated !== null) return stripped.prefix + translated;\n }\n\n // Fallback: return original\n return cfg.fallbackToOriginal ? src : src;\n}\n\n/**\n * Try all configured translation strategies on the input.\n * Returns null if none succeed.\n */\nfunction tryTranslateWithStrategies(\n src: string,\n lang: string,\n cfg: PreprocessorConfig\n): string | null {\n // Strategy: semantic-only\n if (cfg.strategy === 'semantic' || cfg.strategy === 'auto') {\n const threshold = resolveThreshold(cfg.confidenceThreshold, lang);\n const result = trySemanticTranslation(src, lang, threshold);\n if (result !== null) return result;\n }\n\n // Strategy: i18n fallback (auto mode or i18n-only)\n if ((cfg.strategy === 'auto' || cfg.strategy === 'i18n') && cfg.i18nToEnglish) {\n const result = tryI18nTranslation(src, lang, cfg.i18nToEnglish);\n if (result !== null) return result;\n }\n\n return null;\n}\n\n/**\n * Match _hyperscript event handler prefix: \"on [every] <event>[filter][.modifiers] \"\n *\n * Examples:\n * \"on click toggle .active\" → prefix: \"on click \", commands: \"toggle .active\"\n * \"on every click toggle .active\" → prefix: \"on every click \", commands: \"toggle .active\"\n * \"on click.debounce(300) toggle .x\" → prefix: \"on click.debounce(300) \", commands: \"toggle .x\"\n * \"on keyup[key=='Enter'] set x to 1\" → prefix: \"on keyup[key=='Enter'] \", commands: \"set x to 1\"\n * \"on click from body toggle .active\" → prefix: \"on click from body \", commands: \"toggle .active\"\n */\nconst EVENT_PREFIX_RE =\n /^(on\\s+(?:every\\s+)?[\\w-]+(?:\\[.*?\\])?(?:\\.[\\w-]+(?:\\([^)]*\\))?)*(?:\\s+from\\s+\\S+)?(?:\\s+queue\\s+\\w+)?\\s+)/;\n\nfunction stripEventPrefix(src: string): { prefix: string; commands: string } | null {\n const match = src.match(EVENT_PREFIX_RE);\n if (!match) return null;\n const prefix = match[1];\n const commands = src.slice(prefix.length);\n if (!commands) return null;\n return { prefix, commands };\n}\n\n/**\n * Try semantic translation: parse in source language, render to English.\n * Returns null if confidence is below threshold.\n */\nfunction trySemanticTranslation(src: string, lang: string, threshold: number): string | null {\n try {\n // Handle compound statements (split on \"then\" boundaries and newlines)\n const statements = splitStatements(src, lang);\n if (statements.length > 1) {\n return translateCompound(statements, lang, threshold);\n }\n\n // Check if the semantic parser can handle this\n if (!isLanguageRegistered(lang)) return null;\n\n const result = parseSemantic(src, lang);\n if (result.confidence < threshold || !result.node) {\n // If we got SOME confidence but no node, fall back to translate()\n if (result.confidence >= threshold) {\n return translate(src, lang, 'en');\n }\n return null;\n }\n\n // Render the semantic node to English\n return render(result.node, 'en');\n } catch {\n return null;\n }\n}\n\n/**\n * Try i18n grammar transformation to English.\n * Returns null if the result is identical to input (no translation happened).\n */\nfunction tryI18nTranslation(\n src: string,\n lang: string,\n toEnglish: (input: string, locale: string) => string\n): string | null {\n try {\n const result = toEnglish(src, lang);\n return result !== src ? result : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Get all \"then\" keyword forms for a language (primary + alternatives).\n * Falls back to English \"then\" if profile is unavailable.\n */\nfunction getThenKeywords(lang: string): string[] {\n const keywords = ['then']; // Always include English\n const profile = tryGetProfile(lang);\n if (profile?.keywords?.then) {\n const kw = profile.keywords.then;\n if (kw.primary && kw.primary !== 'then') keywords.push(kw.primary);\n if (kw.alternatives) {\n for (const alt of kw.alternatives) {\n if (!keywords.includes(alt)) keywords.push(alt);\n }\n }\n }\n return keywords;\n}\n\n/**\n * Split a hyperscript source string into individual statements.\n * Splits on language-specific \"then\" keywords and newlines.\n */\nfunction splitStatements(src: string, lang: string): string[] {\n const thenWords = getThenKeywords(lang);\n // Escape regex special chars and build pattern\n const escaped = thenWords.map(w => w.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'));\n const pattern = new RegExp(`\\\\s+(?:${escaped.join('|')})\\\\s+`, 'i');\n\n // Split on newlines first\n const lines = src\n .split('\\n')\n .map(l => l.trim())\n .filter(l => l.length > 0);\n const result: string[] = [];\n for (const line of lines) {\n const parts = line.split(pattern);\n result.push(...parts);\n }\n return result;\n}\n\n/**\n * Translate compound statements individually and rejoin with \" then \".\n */\nfunction translateCompound(statements: string[], lang: string, threshold: number): string | null {\n const translated: string[] = [];\n for (const stmt of statements) {\n const result = trySemanticTranslation(stmt.trim(), lang, threshold);\n if (result === null) return null; // If any part fails, fail the whole compound\n translated.push(result);\n }\n return translated.join(' then ');\n}\n","/**\n * _hyperscript Plugin\n *\n * Registers with _hyperscript.use() to intercept getScript() and\n * preprocess non-English hyperscript into English before parsing.\n */\n\nimport { resolveLanguage } from './language-resolver';\nimport { preprocessToEnglish, type PreprocessorConfig } from './preprocessor';\n\n/**\n * The _hyperscript global object shape (minimal subset we need).\n */\ninterface HyperscriptGlobal {\n internals: {\n runtime: HyperscriptRuntime;\n };\n config: Record<string, unknown>;\n}\n\ninterface HyperscriptRuntime {\n getScript(elt: Element): string | null;\n}\n\nexport interface PluginOptions extends Partial<PreprocessorConfig> {\n /** Default language for all elements (overridable per-element). */\n defaultLanguage?: string;\n /** Custom attribute name for per-element language. Default: \"data-lang\" */\n languageAttribute?: string;\n /** Enable debug logging to console. Default: false */\n debug?: boolean;\n}\n\n/**\n * Create a _hyperscript plugin that enables multilingual hyperscript.\n *\n * @example\n * // Basic usage\n * _hyperscript.use(hyperscriptI18n());\n *\n * @example\n * // With options\n * _hyperscript.use(hyperscriptI18n({\n * defaultLanguage: 'ja',\n * confidenceThreshold: 0.6,\n * debug: true,\n * }));\n */\nexport function hyperscriptI18n(options: PluginOptions = {}) {\n return function plugin(hs: unknown): void {\n const { internals } = hs as HyperscriptGlobal;\n const runtime = internals.runtime;\n const originalGetScript = runtime.getScript.bind(runtime);\n\n runtime.getScript = function (elt: Element): string | null {\n const src = originalGetScript(elt);\n if (!src) return null;\n\n // Resolve language\n const lang = resolveLanguageWithOptions(elt, options);\n\n // If English or no language detected, pass through\n if (!lang || lang === 'en') return src;\n\n // Preprocess to English\n const english = preprocessToEnglish(src, lang, options);\n\n if (english !== src) {\n if (options.debug) {\n console.log(`[hyperscript-i18n] ${lang}: \"${src}\" → \"${english}\"`);\n }\n } else {\n // Translation produced no change — likely a failure\n console.warn(\n `[hyperscript-i18n] Translation unchanged for lang=\"${lang}\": \"${src.length > 60 ? src.slice(0, 60) + '…' : src}\". ` +\n 'The input may not match any known pattern, or the language may not be registered. ' +\n 'Original text will be passed to _hyperscript as-is.'\n );\n }\n\n return english;\n };\n };\n}\n\n/**\n * Resolve language with plugin options factored in.\n */\nfunction resolveLanguageWithOptions(elt: Element, options: PluginOptions): string | null {\n // Check custom attribute name\n if (options.languageAttribute) {\n const custom = elt.getAttribute(options.languageAttribute);\n if (custom) return custom.split('-')[0].toLowerCase();\n }\n\n // Standard resolution\n const resolved = resolveLanguage(elt);\n if (resolved) return resolved;\n\n // Fall back to default language\n return options.defaultLanguage ?? null;\n}\n\n/**\n * Standalone preprocessing function for programmatic use.\n * Call this when using _hyperscript.evaluate() or _hyperscript(\"code\") directly.\n *\n * @example\n * const english = preprocess(\"トグル .active\", \"ja\");\n * _hyperscript(english);\n */\nexport function preprocess(\n src: string,\n lang: string,\n config: Partial<PreprocessorConfig> = {}\n): string {\n if (lang === 'en') return src;\n return preprocessToEnglish(src, lang, config);\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/language-resolver.ts","../src/preprocessor.ts","../src/attribute-translator.ts","../src/plugin.ts"],"sourcesContent":["/**\n * Language Resolver\n *\n * Determines the language of hyperscript code on a given element\n * using a cascading resolution strategy.\n */\n\n/**\n * Resolve the language for a hyperscript attribute on an element.\n *\n * Resolution order:\n * 1. `data-lang` attribute on the element itself\n * 2. `data-hyperscript-lang` attribute on the element or closest ancestor\n * 3. `lang` attribute on `<html>` element\n * 4. null (assume English, no preprocessing needed)\n */\nexport function resolveLanguage(elt: Element): string | null {\n // 1. Explicit per-element\n const dataLang = elt.getAttribute('data-lang');\n if (dataLang) return normalizeLangCode(dataLang);\n\n // 2. Inherited from ancestor\n const hsLang =\n elt.getAttribute('data-hyperscript-lang') ??\n elt.closest?.('[data-hyperscript-lang]')?.getAttribute('data-hyperscript-lang');\n if (hsLang) return normalizeLangCode(hsLang);\n\n // 3. Document-level lang\n const htmlLang = typeof document !== 'undefined' ? document.documentElement?.lang : null;\n if (htmlLang && htmlLang !== 'en') return normalizeLangCode(htmlLang);\n\n return null;\n}\n\n/**\n * Normalize a BCP-47 language tag to an ISO 639-1 code.\n * e.g., \"ja-JP\" → \"ja\", \"zh-Hans\" → \"zh\", \"pt-BR\" → \"pt\"\n */\nfunction normalizeLangCode(lang: string): string {\n return lang.split('-')[0].toLowerCase();\n}\n","/**\n * Preprocessor\n *\n * Translates non-English hyperscript to English using the semantic parser\n * (with optional i18n fallback), so the original _hyperscript can parse it.\n */\n\nimport {\n translate,\n render,\n parseSemantic,\n isLanguageRegistered,\n tryGetProfile,\n} from '@lokascript/semantic';\n\nexport interface PreprocessorConfig {\n /**\n * Minimum confidence threshold for semantic parsing (0-1). Default: 0.5.\n * Can be a single number (applies to all languages) or a per-language map.\n * Per-language thresholds are useful because SOV languages (ja, ko, tr) produce\n * inherently lower confidence scores than SVO languages (es, fr, de).\n *\n * @example\n * // Single threshold\n * { confidenceThreshold: 0.5 }\n *\n * @example\n * // Per-language thresholds\n * { confidenceThreshold: { es: 0.7, ja: 0.1, ko: 0.05, '*': 0.5 } }\n */\n confidenceThreshold: number | Record<string, number>;\n /** Strategy: 'semantic' (default), 'i18n', or 'auto' (semantic then i18n) */\n strategy: 'semantic' | 'i18n' | 'auto';\n /** Whether to return original text on failure. Default: true */\n fallbackToOriginal: boolean;\n /** Optional i18n toEnglish function (loaded dynamically if available) */\n i18nToEnglish?: (input: string, locale: string) => string;\n}\n\nconst DEFAULT_THRESHOLD = 0.5;\n\nconst DEFAULT_CONFIG: PreprocessorConfig = {\n confidenceThreshold: DEFAULT_THRESHOLD,\n strategy: 'semantic',\n fallbackToOriginal: true,\n};\n\n/**\n * Resolve the confidence threshold for a specific language.\n * Supports both a single number and a per-language map with '*' as default.\n */\nfunction resolveThreshold(threshold: number | Record<string, number>, lang: string): number {\n if (typeof threshold === 'number') return threshold;\n return threshold[lang] ?? threshold['*'] ?? DEFAULT_THRESHOLD;\n}\n\n/**\n * Preprocess non-English hyperscript into English.\n *\n * Uses the semantic parser to parse the input in the source language,\n * then renders back to English. Falls through to i18n if configured.\n *\n * Handles _hyperscript feature prefixes (e.g. \"on click\", \"on every keyup\")\n * by stripping them, translating only the command portion, then reassembling.\n */\nexport function preprocessToEnglish(\n src: string,\n lang: string,\n config: Partial<PreprocessorConfig> = {}\n): string {\n // English→English is identity; skip semantic parsing which may mangle the input\n if (lang === 'en') return src;\n\n const cfg = { ...DEFAULT_CONFIG, ...config };\n\n // Try translating the full string first\n const fullResult = tryTranslateWithStrategies(src, lang, cfg);\n if (fullResult !== null) return fullResult;\n\n // If full translation failed, try stripping event/feature prefix.\n // _hyperscript attributes often contain \"on <event> <commands>\" — the semantic\n // parser only understands command syntax, not event declarations.\n const stripped = stripEventPrefix(src);\n if (stripped) {\n const translated = tryTranslateWithStrategies(stripped.commands, lang, cfg);\n if (translated !== null) return stripped.prefix + translated;\n }\n\n // Fallback: return original\n return cfg.fallbackToOriginal ? src : src;\n}\n\n/**\n * Try all configured translation strategies on the input.\n * Returns null if none succeed.\n */\nfunction tryTranslateWithStrategies(\n src: string,\n lang: string,\n cfg: PreprocessorConfig\n): string | null {\n // Strategy: semantic-only\n if (cfg.strategy === 'semantic' || cfg.strategy === 'auto') {\n const threshold = resolveThreshold(cfg.confidenceThreshold, lang);\n const result = trySemanticTranslation(src, lang, threshold);\n if (result !== null) return result;\n }\n\n // Strategy: i18n fallback (auto mode or i18n-only)\n if ((cfg.strategy === 'auto' || cfg.strategy === 'i18n') && cfg.i18nToEnglish) {\n const result = tryI18nTranslation(src, lang, cfg.i18nToEnglish);\n if (result !== null) return result;\n }\n\n return null;\n}\n\n/**\n * Match _hyperscript event handler prefix: \"on [every] <event>[filter][.modifiers] \"\n *\n * Examples:\n * \"on click toggle .active\" → prefix: \"on click \", commands: \"toggle .active\"\n * \"on every click toggle .active\" → prefix: \"on every click \", commands: \"toggle .active\"\n * \"on click.debounce(300) toggle .x\" → prefix: \"on click.debounce(300) \", commands: \"toggle .x\"\n * \"on keyup[key=='Enter'] set x to 1\" → prefix: \"on keyup[key=='Enter'] \", commands: \"set x to 1\"\n * \"on click from body toggle .active\" → prefix: \"on click from body \", commands: \"toggle .active\"\n */\nconst EVENT_PREFIX_RE =\n /^(on\\s+(?:every\\s+)?[\\w-]+(?:\\[.*?\\])?(?:\\.[\\w-]+(?:\\([^)]*\\))?)*(?:\\s+from\\s+\\S+)?(?:\\s+queue\\s+\\w+)?\\s+)/;\n\nfunction stripEventPrefix(src: string): { prefix: string; commands: string } | null {\n const match = src.match(EVENT_PREFIX_RE);\n if (!match) return null;\n const prefix = match[1];\n const commands = src.slice(prefix.length);\n if (!commands) return null;\n return { prefix, commands };\n}\n\n/**\n * Try semantic translation: parse in source language, render to English.\n * Returns null if confidence is below threshold.\n */\nfunction trySemanticTranslation(src: string, lang: string, threshold: number): string | null {\n try {\n // Handle compound statements (split on \"then\" boundaries and newlines)\n const statements = splitStatements(src, lang);\n if (statements.length > 1) {\n return translateCompound(statements, lang, threshold);\n }\n\n // Check if the semantic parser can handle this\n if (!isLanguageRegistered(lang)) return null;\n\n const result = parseSemantic(src, lang);\n if (result.confidence < threshold || !result.node) {\n // If we got SOME confidence but no node, fall back to translate()\n if (result.confidence >= threshold) {\n return translate(src, lang, 'en');\n }\n return null;\n }\n\n // Render the semantic node to English\n return render(result.node, 'en');\n } catch {\n return null;\n }\n}\n\n/**\n * Try i18n grammar transformation to English.\n * Returns null if the result is identical to input (no translation happened).\n */\nfunction tryI18nTranslation(\n src: string,\n lang: string,\n toEnglish: (input: string, locale: string) => string\n): string | null {\n try {\n const result = toEnglish(src, lang);\n return result !== src ? result : null;\n } catch {\n return null;\n }\n}\n\n/**\n * Get all \"then\" keyword forms for a language (primary + alternatives).\n * Falls back to English \"then\" if profile is unavailable.\n */\nfunction getThenKeywords(lang: string): string[] {\n const keywords = ['then']; // Always include English\n const profile = tryGetProfile(lang);\n if (profile?.keywords?.then) {\n const kw = profile.keywords.then;\n if (kw.primary && kw.primary !== 'then') keywords.push(kw.primary);\n if (kw.alternatives) {\n for (const alt of kw.alternatives) {\n if (!keywords.includes(alt)) keywords.push(alt);\n }\n }\n }\n return keywords;\n}\n\n/**\n * Split a hyperscript source string into individual statements.\n * Splits on language-specific \"then\" keywords and newlines.\n */\nfunction splitStatements(src: string, lang: string): string[] {\n const thenWords = getThenKeywords(lang);\n // Escape regex special chars and build pattern\n const escaped = thenWords.map(w => w.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&'));\n const pattern = new RegExp(`\\\\s+(?:${escaped.join('|')})\\\\s+`, 'i');\n\n // Split on newlines first\n const lines = src\n .split('\\n')\n .map(l => l.trim())\n .filter(l => l.length > 0);\n const result: string[] = [];\n for (const line of lines) {\n const parts = line.split(pattern);\n result.push(...parts);\n }\n return result;\n}\n\n/**\n * Translate compound statements individually and rejoin with \" then \".\n */\nfunction translateCompound(statements: string[], lang: string, threshold: number): string | null {\n const translated: string[] = [];\n for (const stmt of statements) {\n const result = trySemanticTranslation(stmt.trim(), lang, threshold);\n if (result === null) return null; // If any part fails, fail the whole compound\n translated.push(result);\n }\n return translated.join(' then ');\n}\n","/**\n * Attribute translator\n *\n * _hyperscript.org's Runtime#getScript is a private class field (`#getScript`),\n * not a property reachable through `internals.runtime` — every plugin variant\n * in this package used to monkey-patch `internals.runtime.getScript`, which\n * silently no-ops against current _hyperscript.org builds (the assignment\n * creates a stray own-property that the runtime's internal `#getScript()`\n * calls never read, so translation never happens and errors are swallowed by\n * `runtime.getScript.bind` throwing before any hyperscript even parses).\n *\n * `addBeforeProcessHook` is the supported, public extension point instead: it\n * fires on the subtree root passed to `processNode()` before the runtime reads\n * whichever configured attribute (`_`, `script`, `data-script` by default) or\n * `<script type=\"text/hyperscript\">` body holds the source. Rewriting that\n * attribute/body in place — before the runtime's own scan reaches it — gets\n * the same \"translate before parse\" effect through a mechanism the runtime\n * actually calls.\n */\n\nexport interface HyperscriptHost {\n addBeforeProcessHook?: (fn: (elt: Element) => void) => void;\n config?: { attributes?: string };\n}\n\n/** Marks an element as already translated so a later `processNode()` call over\n * the same subtree (e.g. a sibling swap re-scanning a shared ancestor) doesn't\n * re-translate already-English text as if it were still the original language. */\nconst TRANSLATED_MARKER = 'data-hyperscript-i18n-translated';\n\nfunction scriptAttributeNames(hs: HyperscriptHost): string[] {\n const raw = hs.config?.attributes ?? '_, script, data-script';\n return raw\n .split(',')\n .map(s => s.trim())\n .filter(Boolean);\n}\n\nfunction findScriptAttribute(elt: Element, attrNames: string[]): string | null {\n for (const name of attrNames) {\n if (elt.hasAttribute(name)) return name;\n }\n return null;\n}\n\n/**\n * Install a translator that rewrites non-English script attributes to English\n * in place, before `_hyperscript.org` parses them.\n *\n * @param translate Given the raw source and its element, return the English\n * translation, or `null`/the same string to leave the element untouched\n * (English input, unresolved language, translation failure, etc).\n */\nexport function installAttributeTranslator(\n hs: HyperscriptHost,\n translate: (src: string, elt: Element) => string | null\n): void {\n if (typeof hs.addBeforeProcessHook !== 'function') {\n console.warn(\n '[hyperscript-i18n] _hyperscript.addBeforeProcessHook is unavailable — ' +\n 'this build of _hyperscript.org is not supported. Load a current version ' +\n 'from https://unpkg.com/hyperscript.org.'\n );\n return;\n }\n\n const attrNames = scriptAttributeNames(hs);\n const selector = [...attrNames.map(a => `[${a}]`), 'script[type=\"text/hyperscript\"]'].join(', ');\n\n const translateOne = (elt: Element): void => {\n if (elt.hasAttribute(TRANSLATED_MARKER)) return;\n\n if (elt instanceof HTMLScriptElement && elt.type === 'text/hyperscript') {\n const src = elt.textContent ?? '';\n if (!src) return;\n const english = translate(src, elt);\n elt.setAttribute(TRANSLATED_MARKER, '');\n if (english != null && english !== src) elt.textContent = english;\n return;\n }\n\n const attr = findScriptAttribute(elt, attrNames);\n if (!attr) return;\n const src = elt.getAttribute(attr);\n if (!src) return;\n const english = translate(src, elt);\n elt.setAttribute(TRANSLATED_MARKER, '');\n if (english != null && english !== src) elt.setAttribute(attr, english);\n };\n\n hs.addBeforeProcessHook((root: Element) => {\n if (!root || typeof root.querySelectorAll !== 'function') return;\n if (typeof root.matches === 'function' && root.matches(selector)) translateOne(root);\n root.querySelectorAll(selector).forEach(translateOne);\n });\n}\n","/**\n * _hyperscript Plugin\n *\n * Registers with _hyperscript.use() to rewrite non-English hyperscript\n * attributes into English before _hyperscript.org parses them.\n */\n\nimport { resolveLanguage } from './language-resolver';\nimport { preprocessToEnglish, type PreprocessorConfig } from './preprocessor';\nimport { installAttributeTranslator, type HyperscriptHost } from './attribute-translator';\n\nexport interface PluginOptions extends Partial<PreprocessorConfig> {\n /** Default language for all elements (overridable per-element). */\n defaultLanguage?: string;\n /** Custom attribute name for per-element language. Default: \"data-lang\" */\n languageAttribute?: string;\n /** Enable debug logging to console. Default: false */\n debug?: boolean;\n}\n\n/**\n * Create a _hyperscript plugin that enables multilingual hyperscript.\n *\n * @example\n * // Basic usage\n * _hyperscript.use(hyperscriptI18n());\n *\n * @example\n * // With options\n * _hyperscript.use(hyperscriptI18n({\n * defaultLanguage: 'ja',\n * confidenceThreshold: 0.6,\n * debug: true,\n * }));\n */\nexport function hyperscriptI18n(options: PluginOptions = {}) {\n return function plugin(hs: unknown): void {\n installAttributeTranslator(hs as HyperscriptHost, (src, elt) => {\n // Resolve language\n const lang = resolveLanguageWithOptions(elt, options);\n\n // If English or no language detected, pass through\n if (!lang || lang === 'en') return src;\n\n // Preprocess to English\n const english = preprocessToEnglish(src, lang, options);\n\n if (english !== src) {\n if (options.debug) {\n console.log(`[hyperscript-i18n] ${lang}: \"${src}\" → \"${english}\"`);\n }\n } else {\n // Translation produced no change — likely a failure\n console.warn(\n `[hyperscript-i18n] Translation unchanged for lang=\"${lang}\": \"${src.length > 60 ? src.slice(0, 60) + '…' : src}\". ` +\n 'The input may not match any known pattern, or the language may not be registered. ' +\n 'Original text will be passed to _hyperscript as-is.'\n );\n }\n\n return english;\n });\n };\n}\n\n/**\n * Resolve language with plugin options factored in.\n */\nfunction resolveLanguageWithOptions(elt: Element, options: PluginOptions): string | null {\n // Check custom attribute name\n if (options.languageAttribute) {\n const custom = elt.getAttribute(options.languageAttribute);\n if (custom) return custom.split('-')[0].toLowerCase();\n }\n\n // Standard resolution\n const resolved = resolveLanguage(elt);\n if (resolved) return resolved;\n\n // Fall back to default language\n return options.defaultLanguage ?? null;\n}\n\n/**\n * Standalone preprocessing function for programmatic use.\n * Call this when using _hyperscript.evaluate() or _hyperscript(\"code\") directly.\n *\n * @example\n * const english = preprocess(\"トグル .active\", \"ja\");\n * _hyperscript(english);\n */\nexport function preprocess(\n src: string,\n lang: string,\n config: Partial<PreprocessorConfig> = {}\n): string {\n if (lang === 'en') return src;\n return preprocessToEnglish(src, lang, config);\n}\n"],"mappings":"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","names":["translate","src","english"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lokascript/hyperscript-adapter",
3
- "version": "2.9.2",
3
+ "version": "2.9.3",
4
4
  "description": "Multilingual adapter plugin for original _hyperscript — write hyperscript in 24 languages",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -142,7 +142,7 @@
142
142
  },
143
143
  "homepage": "https://github.com/codetalcott/hyperfixi/tree/main/packages/hyperscript-adapter#readme",
144
144
  "dependencies": {
145
- "@lokascript/semantic": "^2.9.2"
145
+ "@lokascript/semantic": "^2.9.3"
146
146
  },
147
147
  "optionalDependencies": {
148
148
  "@lokascript/i18n": "*"
@@ -0,0 +1,96 @@
1
+ /**
2
+ * Attribute translator
3
+ *
4
+ * _hyperscript.org's Runtime#getScript is a private class field (`#getScript`),
5
+ * not a property reachable through `internals.runtime` — every plugin variant
6
+ * in this package used to monkey-patch `internals.runtime.getScript`, which
7
+ * silently no-ops against current _hyperscript.org builds (the assignment
8
+ * creates a stray own-property that the runtime's internal `#getScript()`
9
+ * calls never read, so translation never happens and errors are swallowed by
10
+ * `runtime.getScript.bind` throwing before any hyperscript even parses).
11
+ *
12
+ * `addBeforeProcessHook` is the supported, public extension point instead: it
13
+ * fires on the subtree root passed to `processNode()` before the runtime reads
14
+ * whichever configured attribute (`_`, `script`, `data-script` by default) or
15
+ * `<script type="text/hyperscript">` body holds the source. Rewriting that
16
+ * attribute/body in place — before the runtime's own scan reaches it — gets
17
+ * the same "translate before parse" effect through a mechanism the runtime
18
+ * actually calls.
19
+ */
20
+
21
+ export interface HyperscriptHost {
22
+ addBeforeProcessHook?: (fn: (elt: Element) => void) => void;
23
+ config?: { attributes?: string };
24
+ }
25
+
26
+ /** Marks an element as already translated so a later `processNode()` call over
27
+ * the same subtree (e.g. a sibling swap re-scanning a shared ancestor) doesn't
28
+ * re-translate already-English text as if it were still the original language. */
29
+ const TRANSLATED_MARKER = 'data-hyperscript-i18n-translated';
30
+
31
+ function scriptAttributeNames(hs: HyperscriptHost): string[] {
32
+ const raw = hs.config?.attributes ?? '_, script, data-script';
33
+ return raw
34
+ .split(',')
35
+ .map(s => s.trim())
36
+ .filter(Boolean);
37
+ }
38
+
39
+ function findScriptAttribute(elt: Element, attrNames: string[]): string | null {
40
+ for (const name of attrNames) {
41
+ if (elt.hasAttribute(name)) return name;
42
+ }
43
+ return null;
44
+ }
45
+
46
+ /**
47
+ * Install a translator that rewrites non-English script attributes to English
48
+ * in place, before `_hyperscript.org` parses them.
49
+ *
50
+ * @param translate Given the raw source and its element, return the English
51
+ * translation, or `null`/the same string to leave the element untouched
52
+ * (English input, unresolved language, translation failure, etc).
53
+ */
54
+ export function installAttributeTranslator(
55
+ hs: HyperscriptHost,
56
+ translate: (src: string, elt: Element) => string | null
57
+ ): void {
58
+ if (typeof hs.addBeforeProcessHook !== 'function') {
59
+ console.warn(
60
+ '[hyperscript-i18n] _hyperscript.addBeforeProcessHook is unavailable — ' +
61
+ 'this build of _hyperscript.org is not supported. Load a current version ' +
62
+ 'from https://unpkg.com/hyperscript.org.'
63
+ );
64
+ return;
65
+ }
66
+
67
+ const attrNames = scriptAttributeNames(hs);
68
+ const selector = [...attrNames.map(a => `[${a}]`), 'script[type="text/hyperscript"]'].join(', ');
69
+
70
+ const translateOne = (elt: Element): void => {
71
+ if (elt.hasAttribute(TRANSLATED_MARKER)) return;
72
+
73
+ if (elt instanceof HTMLScriptElement && elt.type === 'text/hyperscript') {
74
+ const src = elt.textContent ?? '';
75
+ if (!src) return;
76
+ const english = translate(src, elt);
77
+ elt.setAttribute(TRANSLATED_MARKER, '');
78
+ if (english != null && english !== src) elt.textContent = english;
79
+ return;
80
+ }
81
+
82
+ const attr = findScriptAttribute(elt, attrNames);
83
+ if (!attr) return;
84
+ const src = elt.getAttribute(attr);
85
+ if (!src) return;
86
+ const english = translate(src, elt);
87
+ elt.setAttribute(TRANSLATED_MARKER, '');
88
+ if (english != null && english !== src) elt.setAttribute(attr, english);
89
+ };
90
+
91
+ hs.addBeforeProcessHook((root: Element) => {
92
+ if (!root || typeof root.querySelectorAll !== 'function') return;
93
+ if (typeof root.matches === 'function' && root.matches(selector)) translateOne(root);
94
+ root.querySelectorAll(selector).forEach(translateOne);
95
+ });
96
+ }
@@ -16,6 +16,7 @@
16
16
  import { resolveLanguage } from './language-resolver';
17
17
  import type { PluginOptions } from './plugin';
18
18
  import type { PreprocessorConfig } from './preprocessor';
19
+ import { installAttributeTranslator, type HyperscriptHost } from './attribute-translator';
19
20
 
20
21
  // ---------------------------------------------------------------------------
21
22
  // Detect semantic global
@@ -100,16 +101,7 @@ export function hyperscriptI18n(options: PluginOptions = {}) {
100
101
  return;
101
102
  }
102
103
 
103
- const { internals } = hs as {
104
- internals: { runtime: { getScript: (elt: Element) => string | null } };
105
- };
106
- const runtime = internals.runtime;
107
- const originalGetScript = runtime.getScript.bind(runtime);
108
-
109
- runtime.getScript = function (elt: Element): string | null {
110
- const src = originalGetScript(elt);
111
- if (!src) return null;
112
-
104
+ installAttributeTranslator(hs as HyperscriptHost, (src, elt) => {
113
105
  const lang = resolveLanguageWithOptions(elt, options);
114
106
  if (!lang || lang === 'en') return src;
115
107
 
@@ -120,7 +112,7 @@ export function hyperscriptI18n(options: PluginOptions = {}) {
120
112
  }
121
113
 
122
114
  return english;
123
- };
115
+ });
124
116
 
125
117
  if (options.debug) {
126
118
  const langs = semantic.getSupportedLanguages?.() ?? ['(unknown)'];
package/src/browser.ts CHANGED
@@ -31,44 +31,13 @@ export const supportedLanguages = getSupportedLanguages();
31
31
 
32
32
  export { hyperscriptI18n as plugin, preprocess, preprocessToEnglish, resolveLanguage };
33
33
 
34
- // Auto-register if _hyperscript is available globally
34
+ // Auto-register if _hyperscript is available globally. hyperscriptI18n()
35
+ // registers an addBeforeProcessHook, which _hyperscript.org's own initial
36
+ // page scan is guaranteed to run after (that scan is deferred to
37
+ // DOMContentLoaded/ready(), well after this synchronous <script> tag runs) —
38
+ // no separate reprocessing pass is needed.
35
39
  declare const _hyperscript: { use: (plugin: unknown) => void } | undefined;
36
40
 
37
41
  if (typeof _hyperscript !== 'undefined' && _hyperscript.use) {
38
42
  _hyperscript.use(hyperscriptI18n());
39
- reprocessInitializedElements();
40
- }
41
-
42
- /**
43
- * _hyperscript processes the DOM immediately on load (via browserInit()),
44
- * so elements are already marked as initialized with unparsed multilingual
45
- * text by the time the adapter registers. Clear the initialized flag on
46
- * all script-bearing elements and re-process them through the patched
47
- * getScript() pipeline.
48
- */
49
- function reprocessInitializedElements(): void {
50
- if (typeof document === 'undefined') return;
51
-
52
- const hs = _hyperscript as unknown as {
53
- internals?: {
54
- runtime?: {
55
- getInternalData: (el: Element) => { initialized?: boolean };
56
- processNode: (el: Element) => void;
57
- getScriptSelector: () => string;
58
- };
59
- };
60
- };
61
-
62
- const runtime = hs?.internals?.runtime;
63
- if (!runtime?.processNode || !runtime?.getInternalData) return;
64
-
65
- const selector = runtime.getScriptSelector?.() ?? '[_], [script], [data-script]';
66
- document.querySelectorAll(selector).forEach(el => {
67
- const data = runtime.getInternalData(el);
68
- if (data.initialized) {
69
- data.initialized = false;
70
- }
71
- });
72
-
73
- runtime.processNode(document.body);
74
43
  }
@@ -23,43 +23,12 @@ export { hyperscriptI18n as plugin, preprocess, resolveLanguage };
23
23
 
24
24
  declare const _hyperscript: { use: (plugin: unknown) => void } | undefined;
25
25
 
26
+ // hyperscriptI18n() registers an addBeforeProcessHook, which _hyperscript.org's
27
+ // own initial page scan is guaranteed to run after (that scan is deferred to
28
+ // DOMContentLoaded/ready(), well after this synchronous <script> tag runs) —
29
+ // no separate reprocessing pass is needed.
26
30
  export function autoRegister(): void {
27
31
  if (typeof _hyperscript !== 'undefined' && _hyperscript.use) {
28
32
  _hyperscript.use(hyperscriptI18n());
29
- reprocessInitializedElements();
30
33
  }
31
34
  }
32
-
33
- /**
34
- * _hyperscript processes the DOM immediately on load (via browserInit()),
35
- * so elements are already marked as initialized with unparsed multilingual
36
- * text by the time the adapter registers. Clear the initialized flag on
37
- * all script-bearing elements and re-process them through the patched
38
- * getScript() pipeline.
39
- */
40
- function reprocessInitializedElements(): void {
41
- if (typeof document === 'undefined') return;
42
-
43
- const hs = _hyperscript as unknown as {
44
- internals?: {
45
- runtime?: {
46
- getInternalData: (el: Element) => { initialized?: boolean };
47
- processNode: (el: Element) => void;
48
- getScriptSelector: () => string;
49
- };
50
- };
51
- };
52
-
53
- const runtime = hs?.internals?.runtime;
54
- if (!runtime?.processNode || !runtime?.getInternalData) return;
55
-
56
- const selector = runtime.getScriptSelector?.() ?? '[_], [script], [data-script]';
57
- document.querySelectorAll(selector).forEach(el => {
58
- const data = runtime.getInternalData(el);
59
- if (data.initialized) {
60
- data.initialized = false;
61
- }
62
- });
63
-
64
- runtime.processNode(document.body);
65
- }