waybetter 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -2
- package/bin/waybetter.mjs +2 -5
- package/cli/buildenv.mjs +1 -1
- package/cli/release.mjs +11 -18
- package/cli/releasecheck.mjs +15 -0
- package/cli/remote.mjs +4 -4
- package/cli/store.mjs +80 -28
- package/cli/strings.mjs +2 -0
- package/dist/runtime/stringPackHook.js +2 -2
- package/dist/runtime/stringPackHook.js.map +1 -1
- package/dist/runtime/stringPackTranslate.d.ts +3 -1
- package/dist/runtime/stringPackTranslate.js +6 -3
- package/dist/runtime/stringPackTranslate.js.map +1 -1
- package/dist/runtime/stringPacks.d.ts +53 -5
- package/dist/runtime/stringPacks.js +161 -25
- package/dist/runtime/stringPacks.js.map +1 -1
- package/dist/runtime/themeBar.d.ts +28 -0
- package/dist/runtime/themeBar.js +82 -0
- package/dist/runtime/themeBar.js.map +1 -0
- package/dist/runtime/themeComponents.js +16 -3
- package/dist/runtime/themeComponents.js.map +1 -1
- package/dist/runtime/themeDecor.js +3 -1
- package/dist/runtime/themeDecor.js.map +1 -1
- package/dist/runtime/themeDerive.d.ts +69 -0
- package/dist/runtime/themeDerive.js +117 -0
- package/dist/runtime/themeDerive.js.map +1 -0
- package/dist/runtime/themeDoctor.d.ts +44 -3
- package/dist/runtime/themeDoctor.js +86 -25
- package/dist/runtime/themeDoctor.js.map +1 -1
- package/dist/runtime/themeGuards.d.ts +60 -0
- package/dist/runtime/themeGuards.js +235 -0
- package/dist/runtime/themeGuards.js.map +1 -0
- package/dist/runtime/themeIcons.d.ts +1 -1
- package/dist/runtime/themeIcons.js +13 -5
- package/dist/runtime/themeIcons.js.map +1 -1
- package/dist/runtime/themeInk.d.ts +37 -0
- package/dist/runtime/themeInk.js +111 -0
- package/dist/runtime/themeInk.js.map +1 -0
- package/dist/runtime/themeInvariants.d.ts +39 -0
- package/dist/runtime/themeInvariants.js +273 -0
- package/dist/runtime/themeInvariants.js.map +1 -0
- package/dist/runtime/themeRuntime.d.ts +15 -1
- package/dist/runtime/themeRuntime.js +78 -5
- package/dist/runtime/themeRuntime.js.map +1 -1
- package/dist/runtime/themeSpec.d.ts +5 -1
- package/dist/runtime/themeSpec.js +9 -13
- package/dist/runtime/themeSpec.js.map +1 -1
- package/dist/shell/InstallDialog.js +14 -8
- package/dist/shell/InstallDialog.js.map +1 -1
- package/dist/shell/StoreView.d.ts +12 -0
- package/dist/shell/StoreView.js +9 -4
- package/dist/shell/StoreView.js.map +1 -1
- package/dist/shell/frame/AppMenu.d.ts +26 -3
- package/dist/shell/frame/AppMenu.js +106 -27
- package/dist/shell/frame/AppMenu.js.map +1 -1
- package/dist/shell/frame/FindBar.js +1 -1
- package/dist/shell/frame/FindBar.js.map +1 -1
- package/dist/shell/frame/Shell.d.ts +1 -0
- package/dist/shell/frame/Shell.js +10 -2
- package/dist/shell/frame/Shell.js.map +1 -1
- package/dist/shell/frame/Titlebar.d.ts +2 -1
- package/dist/shell/frame/Titlebar.js +5 -3
- package/dist/shell/frame/Titlebar.js.map +1 -1
- package/dist/shell/frame/WebPanel.js +4 -1
- package/dist/shell/frame/WebPanel.js.map +1 -1
- package/dist/shell/frame/accountSwitcher.css +10 -10
- package/dist/shell/frame/appMenu.css +19 -13
- package/dist/shell/frame/boot.js +2 -0
- package/dist/shell/frame/boot.js.map +1 -1
- package/dist/shell/frame/chromeTheme.d.ts +19 -1
- package/dist/shell/frame/chromeTheme.js +51 -14
- package/dist/shell/frame/chromeTheme.js.map +1 -1
- package/dist/shell/frame/deck.css +30 -30
- package/dist/shell/frame/edges.css +159 -0
- package/dist/shell/frame/index.d.ts +2 -2
- package/dist/shell/frame/index.js +1 -1
- package/dist/shell/frame/index.js.map +1 -1
- package/dist/shell/frame/mediaModel.d.ts +13 -0
- package/dist/shell/frame/mediaModel.js +14 -0
- package/dist/shell/frame/mediaModel.js.map +1 -1
- package/dist/shell/frame/menuMessages.en.json +43 -1
- package/dist/shell/frame/menuModel.d.ts +42 -0
- package/dist/shell/frame/menuModel.js +8 -0
- package/dist/shell/frame/menuModel.js.map +1 -1
- package/dist/shell/frame/nativeMenu.d.ts +5 -0
- package/dist/shell/frame/nativeMenu.js +178 -0
- package/dist/shell/frame/nativeMenu.js.map +1 -0
- package/dist/shell/frame/nativeMenuModel.d.ts +102 -0
- package/dist/shell/frame/nativeMenuModel.js +186 -0
- package/dist/shell/frame/nativeMenuModel.js.map +1 -0
- package/dist/shell/frame/paletteModel.js +3 -1
- package/dist/shell/frame/paletteModel.js.map +1 -1
- package/dist/shell/frame/paneTheme.css +1 -1
- package/dist/shell/frame/reportModel.d.ts +1 -1
- package/dist/shell/frame/reportModel.js +1 -1
- package/dist/shell/frame/reportModel.js.map +1 -1
- package/dist/shell/frame/shell.css +56 -51
- package/dist/shell/frame/state.d.ts +2 -0
- package/dist/shell/frame/state.js +3 -1
- package/dist/shell/frame/state.js.map +1 -1
- package/dist/shell/frame/themeStore.js +21 -6
- package/dist/shell/frame/themeStore.js.map +1 -1
- package/dist/shell/frame/workspaces.css +12 -12
- package/dist/shell/installController.d.ts +16 -1
- package/dist/shell/installController.js +31 -5
- package/dist/shell/installController.js.map +1 -1
- package/dist/shell/panel/Appearance.js +12 -3
- package/dist/shell/panel/Appearance.js.map +1 -1
- package/dist/shell/panel/Settings.d.ts +1 -1
- package/dist/shell/panel/Settings.js +4 -3
- package/dist/shell/panel/Settings.js.map +1 -1
- package/dist/shell/panel/StoreSection.d.ts +2 -1
- package/dist/shell/panel/StoreSection.js +31 -1
- package/dist/shell/panel/StoreSection.js.map +1 -1
- package/dist/shell/panel/StringPacks.d.ts +12 -3
- package/dist/shell/panel/StringPacks.js +33 -8
- package/dist/shell/panel/StringPacks.js.map +1 -1
- package/dist/shell/panel/backup.css +1 -1
- package/dist/shell/panel/builder.css +48 -40
- package/dist/shell/panel/make.css +8 -7
- package/dist/shell/panel/panel.css +401 -401
- package/dist/shell/panel/panelMessages.d.ts +41 -1
- package/dist/shell/panel/panelMessages.js +13 -1
- package/dist/shell/panel/panelMessages.js.map +1 -1
- package/dist/shell/panel/plugins.css +5 -5
- package/dist/shell/panel/strings.css +1 -1
- package/dist/shell/store.css +34 -14
- package/dist/shell/storeMessages.d.ts +24 -0
- package/dist/shell/storeMessages.js +24 -0
- package/dist/shell/storeMessages.js.map +1 -1
- package/dist/shell/storeModel.d.ts +31 -7
- package/dist/shell/storeModel.js +15 -2
- package/dist/shell/storeModel.js.map +1 -1
- package/dist/shell/stringPacksModel.js +7 -2
- package/dist/shell/stringPacksModel.js.map +1 -1
- package/icons.schema.json +9 -0
- package/locales/ar.json +109 -2
- package/locales/bg.json +109 -2
- package/locales/bn.json +109 -2
- package/locales/ca.json +109 -2
- package/locales/cs.json +109 -2
- package/locales/da.json +109 -2
- package/locales/de.json +109 -2
- package/locales/el.json +109 -2
- package/locales/en.json +108 -2
- package/locales/es.json +109 -2
- package/locales/eu.json +109 -2
- package/locales/fa.json +109 -2
- package/locales/fi.json +109 -2
- package/locales/fil.json +109 -2
- package/locales/fr.json +109 -2
- package/locales/ga.json +109 -2
- package/locales/gl.json +109 -2
- package/locales/gu.json +109 -2
- package/locales/ha.json +109 -2
- package/locales/he.json +109 -2
- package/locales/hi.json +109 -2
- package/locales/hr.json +109 -2
- package/locales/hu.json +110 -2
- package/locales/id.json +109 -2
- package/locales/ig.json +109 -2
- package/locales/it.json +109 -2
- package/locales/ja.json +109 -2
- package/locales/kn.json +109 -2
- package/locales/ko.json +109 -2
- package/locales/mr.json +109 -2
- package/locales/ms.json +109 -2
- package/locales/nb.json +109 -2
- package/locales/nl.json +109 -2
- package/locales/pl.json +109 -2
- package/locales/pt.json +109 -2
- package/locales/ro.json +109 -2
- package/locales/ru.json +109 -2
- package/locales/sk.json +109 -2
- package/locales/sr.json +109 -2
- package/locales/sv.json +109 -2
- package/locales/ta.json +109 -2
- package/locales/th.json +109 -2
- package/locales/tr.json +109 -2
- package/locales/uk.json +109 -2
- package/locales/ur.json +109 -2
- package/locales/vi.json +109 -2
- package/locales/yo.json +109 -2
- package/locales/zh-Hans.json +109 -2
- package/locales/zh-Hant.json +109 -2
- package/package.json +2 -3
- package/store-catalog.schema.json +18 -5
- package/theme.schema.json +9 -0
- package/windows/installer.nsi +22 -2
- package/cli/privacy.mjs +0 -768
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stringPackTranslate.js","sourceRoot":"","sources":["../../../runtime/src/stringPackTranslate.ts"],"names":[],"mappings":"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","sourcesContent":["// Translate mode for string packs (published as `waybetter/runtime/stringPackTranslate`): the site's\n// strings carry an invisible marker naming their key, so clicking a word on the page tells which\n// key it is. The author types a replacement (checked with the pack rules right there), the edits\n// collect in the tab as a draft pack that applies on the next reload, and Export copies the pack\n// file. Nothing leaves the page; the draft lives in the tab's session storage.\n//\n// The markers change the site's text (invisible characters at the end of each string), so exact\n// comparisons in the site's own code may notice: translate mode is for authors, never on by default,\n// and leaving it reloads the page without markers.\nimport { compilePack, inventoryLookup, validateString, type InventoryLookup, type PageInventory, type StringPack } from './stringPacks.ts';\n\nconst START = '';\nconst DIGITS = ['', '', '', ''];\nconst MARKED = /([]+)/g;\n\nexport type Marker = {\n /** The value with its key's marker (strings, and functions whose result is a string). */\n mark(id: string, value: unknown): unknown;\n /** The keys marked in a text, in order. */\n ids(text: string): string[];\n /** The text without markers. */\n strip(text: string): string;\n};\n\nexport function createMarker(): Marker {\n const ids: string[] = [];\n const index = new Map<string, number>();\n const code = (id: string) => {\n let n = index.get(id);\n if (n === undefined) { n = ids.length; ids.push(id); index.set(id, n); }\n let digits = '';\n do { digits = DIGITS[n % 4] + digits; n = Math.floor(n / 4); } while (n);\n return START + digits + START;\n };\n return {\n mark(id, value) {\n if (typeof value === 'string') return value + code(id);\n if (typeof value === 'function') {\n const original = value as (...args: unknown[]) => unknown;\n return function (this: unknown, ...args: unknown[]) {\n const out = original.apply(this, args);\n return typeof out === 'string' ? out + code(id) : out;\n };\n }\n return value;\n },\n ids(text) {\n const out: string[] = [];\n for (const match of text.matchAll(MARKED)) {\n let n = 0;\n for (const ch of match[1]!) n = n * 4 + DIGITS.indexOf(ch);\n const id = ids[n];\n if (id && !out.includes(id)) out.push(id);\n }\n return out;\n },\n strip: text => text.replace(MARKED, ''),\n };\n}\n\n/** The author's edits in this tab. */\nexport type TranslateDraft = { language: string; name: string; strings: Record<string, string> };\n\nexport const DRAFT_KEY = 'wayBetter.translateDraft';\n\nexport function readDraft(storage: Pick<Storage, 'getItem'>): TranslateDraft | null {\n try {\n const value = JSON.parse(storage.getItem(DRAFT_KEY) ?? 'null') as TranslateDraft | null;\n return value && typeof value === 'object' && typeof value.strings === 'object' && value.strings !== null ? value : null;\n } catch {\n return null;\n }\n}\n\n/** The draft as a pack document for `site`/`catalog`. */\nexport function draftPack(draft: TranslateDraft, site: string, catalog: string): StringPack {\n return {\n format: 1, site, catalog, language: draft.language || 'en-x-draft', name: draft.name || 'My strings', authors: ['Me'],\n version: '0.1.0', strings: { ...draft.strings },\n };\n}\n\n/** Checks one edit against the key's shape; `null` when it can be used. */\nexport function checkEdit(lookup: InventoryLookup, id: string, value: string): string | null {\n const shape = lookup(id);\n if (!shape) return 'this key is not in the inventory';\n const result = validateString(shape, value);\n return 'reason' in result ? result.reason : null;\n}\n\nexport type TranslateWords = {\n bar: string; hint: string; edits(count: number): string; exportPack: string; copied: string; exit: string; reload: string;\n key: string; original: string; parameters: string; save: string; cancel: string; remove: string; notTranslatable: string; choose: string;\n};\n\n/** English defaults (the app passes its own words). */\nexport const TRANSLATE_WORDS: TranslateWords = {\n bar: 'Translate mode', hint: 'Click a word to change it.', edits: count => `${count} ${count === 1 ? 'edit' : 'edits'}`,\n exportPack: 'Copy pack file', copied: 'Pack file copied', exit: 'Leave', reload: 'Reload to see it',\n key: 'Key', original: 'Now', parameters: 'Parameters', save: 'Save', cancel: 'Cancel', remove: 'Remove edit',\n notTranslatable: 'This string cannot be changed by a pack yet.', choose: 'Which one?',\n};\n\nexport type TranslateUiOptions = {\n inventory: PageInventory;\n marker: Marker;\n site: string;\n catalog: string;\n words?: Partial<TranslateWords>;\n /** Leaves translate mode (the app drops the flag and reloads). */\n exit(): void;\n};\n\nconst CSS = `\n:host { all: initial; }\n.bar { position: fixed; left: 50%; bottom: 16px; transform: translateX(-50%); z-index: 2147483646; display: flex; gap: 10px; align-items: center;\n padding: 8px 12px; border-radius: 12px; background: #111827; color: #f9fafb; font: 13px/1.3 system-ui, sans-serif; box-shadow: 0 8px 24px rgba(0,0,0,.35); }\n.bar b { font-weight: 600; } .bar span { color: #cbd5e1; }\nbutton { font: inherit; border: 0; border-radius: 8px; padding: 6px 10px; cursor: pointer; background: #334155; color: #f9fafb; }\nbutton.primary { background: #2563eb; }\n.pop { position: fixed; z-index: 2147483647; width: 360px; max-width: calc(100vw - 24px); padding: 12px; border-radius: 12px; background: #fff; color: #111827;\n font: 13px/1.4 system-ui, sans-serif; box-shadow: 0 12px 32px rgba(0,0,0,.3); display: grid; gap: 8px; }\n.pop small { color: #475569; } .pop code { font-size: 12px; color: #334155; }\n.pop textarea { width: 100%; box-sizing: border-box; min-height: 64px; font: inherit; padding: 6px; border: 1px solid #cbd5e1; border-radius: 8px; }\n.pop .row { display: flex; gap: 8px; justify-content: flex-end; } .pop .err { color: #b91c1c; }\n.pop .pick { display: grid; gap: 4px; } .pop .pick button { text-align: left; background: #e2e8f0; color: #111827; }\n`;\n\n/**\n * The page part of translate mode: a bar (edits, copy the pack file, leave) and an editor for the\n * string under a click. Clicks on marked text are the editor's while the mode is on.\n */\nexport function installTranslateUi(win: Window, options: TranslateUiOptions): { dispose(): void } {\n const doc = win.document;\n const words = { ...TRANSLATE_WORDS, ...options.words };\n const lookup = inventoryLookup(options.inventory);\n const storage = win.sessionStorage;\n const draft = (): TranslateDraft => readDraft(storage) ?? { language: '', name: '', strings: {} };\n const save = (next: TranslateDraft) => { try { storage.setItem(DRAFT_KEY, JSON.stringify(next)); } catch { /* storage full or blocked */ } };\n\n const host = doc.createElement('wb-translate');\n const root = host.attachShadow({ mode: 'open' });\n const style = doc.createElement('style');\n style.textContent = CSS;\n root.append(style);\n const bar = doc.createElement('div');\n bar.className = 'bar';\n root.append(bar);\n let pop: HTMLElement | null = null;\n let changed = false;\n\n const button = (label: string, onClick: () => void, primary = false) => {\n const el = doc.createElement('button');\n el.textContent = label;\n if (primary) el.className = 'primary';\n el.addEventListener('click', event => { event.preventDefault(); event.stopPropagation(); onClick(); });\n return el;\n };\n const renderBar = () => {\n bar.replaceChildren();\n const title = doc.createElement('b');\n title.textContent = words.bar;\n const hint = doc.createElement('span');\n hint.textContent = `${words.hint} · ${words.edits(Object.keys(draft().strings).length)}`;\n bar.append(title, hint);\n if (changed) bar.append(button(words.reload, () => win.location.reload(), true));\n bar.append(button(words.exportPack, () => {\n const text = JSON.stringify(draftPack(draft(), options.site, options.catalog), null, 2);\n void win.navigator.clipboard?.writeText(text).then(() => { hint.textContent = words.copied; }).catch(() => {});\n }));\n bar.append(button(words.exit, options.exit));\n };\n const closePop = () => { pop?.remove(); pop = null; };\n\n const edit = (id: string, x: number, y: number, now: string) => {\n closePop();\n pop = doc.createElement('div');\n pop.className = 'pop';\n pop.style.left = `${Math.max(12, Math.min(x, win.innerWidth - 372))}px`;\n pop.style.top = `${Math.max(12, Math.min(y + 12, win.innerHeight - 260))}px`;\n const shape = lookup(id);\n const head = doc.createElement('small');\n head.textContent = `${words.key}: `;\n const code = doc.createElement('code');\n code.textContent = id;\n head.append(code);\n const current = doc.createElement('div');\n current.textContent = `${words.original}: ${now}`;\n pop.append(head, current);\n if (!shape || (shape.kind !== 'text' && shape.kind !== 'message')) {\n const note = doc.createElement('div');\n note.textContent = words.notTranslatable;\n pop.append(note, button(words.cancel, closePop));\n root.append(pop);\n return;\n }\n if (shape.params?.length) {\n const params = doc.createElement('small');\n params.textContent = `${words.parameters}: ${shape.params.map(name => `{${name}}${shape.plural?.includes(name) ? ' (plural)' : ''}`).join(', ')}`;\n pop.append(params);\n }\n const input = doc.createElement('textarea');\n input.value = draft().strings[id] ?? now;\n const error = doc.createElement('div');\n error.className = 'err';\n const row = doc.createElement('div');\n row.className = 'row';\n if (draft().strings[id] !== undefined) {\n row.append(button(words.remove, () => { const next = draft(); delete next.strings[id]; save(next); changed = true; closePop(); renderBar(); }));\n }\n row.append(button(words.cancel, closePop), button(words.save, () => {\n const value = input.value.trim();\n const problem = checkEdit(lookup, id, value);\n if (problem) { error.textContent = problem; return; }\n const next = draft();\n next.strings[id] = value;\n save(next);\n changed = true;\n closePop();\n renderBar();\n }, true));\n pop.append(input, error, row);\n root.append(pop);\n input.focus();\n };\n\n /** The marked keys at a click: the element's own text and labels, then up to three parents. */\n const keysAt = (target: Element | null) => {\n for (let el = target, depth = 0; el && depth < 4; el = el.parentElement, depth += 1) {\n const texts = [el.textContent ?? '', el.getAttribute('aria-label') ?? '', el.getAttribute('placeholder') ?? '', el.getAttribute('title') ?? ''];\n const found = texts.flatMap(text => options.marker.ids(text).map(id => ({ id, text: options.marker.strip(text) })));\n if (found.length) return found.filter((item, i) => found.findIndex(other => other.id === item.id) === i).slice(0, 8);\n }\n return [];\n };\n const onClick = (event: MouseEvent) => {\n if (event.composedPath().includes(host)) return;\n const found = keysAt(event.target as Element | null);\n if (!found.length) return;\n event.preventDefault();\n event.stopPropagation();\n if (found.length === 1) { edit(found[0]!.id, event.clientX, event.clientY, found[0]!.text); return; }\n closePop();\n pop = doc.createElement('div');\n pop.className = 'pop';\n pop.style.left = `${Math.max(12, Math.min(event.clientX, win.innerWidth - 372))}px`;\n pop.style.top = `${Math.max(12, Math.min(event.clientY + 12, win.innerHeight - 260))}px`;\n const pick = doc.createElement('div');\n pick.className = 'pick';\n const label = doc.createElement('small');\n label.textContent = words.choose;\n pick.append(label, ...found.map(item => button(`${item.text.slice(0, 60)} · ${item.id}`, () => edit(item.id, event.clientX, event.clientY, item.text))));\n pop.append(pick, button(words.cancel, closePop));\n root.append(pop);\n };\n\n const mount = () => { (doc.body ?? doc.documentElement).append(host); renderBar(); };\n if (doc.body) mount(); else doc.addEventListener('DOMContentLoaded', mount, { once: true });\n win.addEventListener('click', onClick, true);\n return { dispose() { win.removeEventListener('click', onClick, true); host.remove(); } };\n}\n\n/** The draft as the last pack of a delivery (it applies over the others), when it has any string. */\nexport function withDraft(packs: readonly unknown[], storage: Pick<Storage, 'getItem'>, site: string, catalog: string, inventory: PageInventory): unknown[] {\n const draft = readDraft(storage);\n if (!draft || !Object.keys(draft.strings).length) return [...packs];\n const pack = draftPack(draft, site, catalog);\n try { compilePack(pack, inventory); } catch { return [...packs]; }\n return [...packs, pack];\n}\n"]}
|
|
1
|
+
{"version":3,"file":"stringPackTranslate.js","sourceRoot":"","sources":["../../../runtime/src/stringPackTranslate.ts"],"names":[],"mappings":"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","sourcesContent":["// Translate mode for string packs (published as `waybetter/runtime/stringPackTranslate`): the site's\n// strings carry an invisible marker naming their key, so clicking a word on the page tells which\n// key it is. The author types a replacement (checked with the pack rules right there), the edits\n// collect in the tab as a draft pack that applies on the next reload, and Export copies the pack\n// file. Nothing leaves the page; the draft lives in the tab's session storage.\n//\n// The markers change the site's text (invisible characters at the end of each string), so exact\n// comparisons in the site's own code may notice: translate mode is for authors, never on by default,\n// and leaving it reloads the page without markers.\nimport { compilePack, inventoryLookup, validateString, type InventoryLookup, type PageInventory, type StringPack } from './stringPacks.ts';\n\nconst START = '';\nconst DIGITS = ['', '', '', ''];\nconst MARKED = /([]+)/g;\n\nexport type Marker = {\n /** The value with its key's marker (strings, and functions whose result is a string). */\n mark(id: string, value: unknown): unknown;\n /** The keys marked in a text, in order. */\n ids(text: string): string[];\n /** The text without markers. */\n strip(text: string): string;\n};\n\nexport function createMarker(): Marker {\n const ids: string[] = [];\n const index = new Map<string, number>();\n const code = (id: string) => {\n let n = index.get(id);\n if (n === undefined) { n = ids.length; ids.push(id); index.set(id, n); }\n let digits = '';\n do { digits = DIGITS[n % 4] + digits; n = Math.floor(n / 4); } while (n);\n return START + digits + START;\n };\n return {\n mark(id, value) {\n if (typeof value === 'string') return value + code(id);\n if (typeof value === 'function') {\n const original = value as (...args: unknown[]) => unknown;\n return function (this: unknown, ...args: unknown[]) {\n const out = original.apply(this, args);\n return typeof out === 'string' ? out + code(id) : out;\n };\n }\n return value;\n },\n ids(text) {\n const out: string[] = [];\n for (const match of text.matchAll(MARKED)) {\n let n = 0;\n for (const ch of match[1]!) n = n * 4 + DIGITS.indexOf(ch);\n const id = ids[n];\n if (id && !out.includes(id)) out.push(id);\n }\n return out;\n },\n strip: text => text.replace(MARKED, ''),\n };\n}\n\n/** The author's edits in this tab. */\nexport type TranslateDraft = { language: string; name: string; strings: Record<string, string> };\n\nexport const DRAFT_KEY = 'wayBetter.translateDraft';\n\nexport function readDraft(storage: Pick<Storage, 'getItem'>): TranslateDraft | null {\n try {\n const value = JSON.parse(storage.getItem(DRAFT_KEY) ?? 'null') as TranslateDraft | null;\n return value && typeof value === 'object' && typeof value.strings === 'object' && value.strings !== null ? value : null;\n } catch {\n return null;\n }\n}\n\n/** The draft as a pack document for `site`/`catalog`. */\nexport function draftPack(draft: TranslateDraft, site: string, catalog: string): StringPack {\n return {\n format: 1, site, catalog, language: draft.language || 'en-x-draft', name: draft.name || 'My strings', authors: ['Me'],\n version: '0.1.0', strings: { ...draft.strings },\n };\n}\n\n/** Checks one edit against the key's shape; `null` when it can be used. */\nexport function checkEdit(lookup: InventoryLookup, id: string, value: string): string | null {\n const shape = lookup(id);\n if (!shape) return 'this key is not in the inventory';\n const result = validateString(shape, value);\n return 'reason' in result ? result.reason : null;\n}\n\nexport type TranslateWords = {\n bar: string; hint: string; edits(count: number): string; exportPack: string; copied: string; exit: string; reload: string;\n key: string; original: string; parameters: string; plural: string; save: string; cancel: string; remove: string; notTranslatable: string; choose: string;\n};\n\n/** English defaults (the app passes its own words). */\nexport const TRANSLATE_WORDS: TranslateWords = {\n bar: 'Translate mode', hint: 'Click a word to change it.', edits: count => `${count} ${count === 1 ? 'edit' : 'edits'}`,\n exportPack: 'Copy pack file', copied: 'Pack file copied', exit: 'Leave', reload: 'Reload to see it',\n key: 'Key', original: 'Now', parameters: 'Parameters', plural: 'plural', save: 'Save', cancel: 'Cancel', remove: 'Remove edit',\n notTranslatable: 'This string cannot be changed by a pack yet.', choose: 'Which one?',\n};\n\nexport type TranslateUiOptions = {\n inventory: PageInventory;\n marker: Marker;\n site: string;\n catalog: string;\n /** The bar's and the editor's words; a function is asked again each time they are drawn (the app's language may change). */\n words?: Partial<TranslateWords> | (() => Partial<TranslateWords>);\n /** Leaves translate mode (the app drops the flag and reloads). */\n exit(): void;\n};\n\nconst CSS = `\n:host { all: initial; }\n.bar { position: fixed; left: 50%; bottom: 16px; transform: translateX(-50%); z-index: 2147483646; display: flex; gap: 10px; align-items: center;\n padding: 8px 12px; border-radius: 12px; background: #111827; color: #f9fafb; font: 13px/1.3 system-ui, sans-serif; box-shadow: 0 8px 24px rgba(0,0,0,.35); }\n.bar b { font-weight: 600; } .bar span { color: #cbd5e1; }\nbutton { font: inherit; border: 0; border-radius: 8px; padding: 6px 10px; cursor: pointer; background: #334155; color: #f9fafb; }\nbutton.primary { background: #2563eb; }\n.pop { position: fixed; z-index: 2147483647; width: 360px; max-width: calc(100vw - 24px); padding: 12px; border-radius: 12px; background: #fff; color: #111827;\n font: 13px/1.4 system-ui, sans-serif; box-shadow: 0 12px 32px rgba(0,0,0,.3); display: grid; gap: 8px; }\n.pop small { color: #475569; } .pop code { font-size: 12px; color: #334155; }\n.pop textarea { width: 100%; box-sizing: border-box; min-height: 64px; font: inherit; padding: 6px; border: 1px solid #cbd5e1; border-radius: 8px; }\n.pop .row { display: flex; gap: 8px; justify-content: flex-end; } .pop .err { color: #b91c1c; }\n.pop .pick { display: grid; gap: 4px; } .pop .pick button { text-align: left; background: #e2e8f0; color: #111827; }\n`;\n\n/**\n * The page part of translate mode: a bar (edits, copy the pack file, leave) and an editor for the\n * string under a click. Clicks on marked text are the editor's while the mode is on.\n */\nexport function installTranslateUi(win: Window, options: TranslateUiOptions): { dispose(): void } {\n const doc = win.document;\n const currentWords = (): TranslateWords => ({ ...TRANSLATE_WORDS, ...(typeof options.words === 'function' ? options.words() : options.words) });\n const lookup = inventoryLookup(options.inventory);\n const storage = win.sessionStorage;\n const draft = (): TranslateDraft => readDraft(storage) ?? { language: '', name: '', strings: {} };\n const save = (next: TranslateDraft) => { try { storage.setItem(DRAFT_KEY, JSON.stringify(next)); } catch { /* storage full or blocked */ } };\n\n const host = doc.createElement('wb-translate');\n const root = host.attachShadow({ mode: 'open' });\n const style = doc.createElement('style');\n style.textContent = CSS;\n root.append(style);\n const bar = doc.createElement('div');\n bar.className = 'bar';\n root.append(bar);\n let pop: HTMLElement | null = null;\n let changed = false;\n\n const button = (label: string, onClick: () => void, primary = false) => {\n const el = doc.createElement('button');\n el.textContent = label;\n if (primary) el.className = 'primary';\n el.addEventListener('click', event => { event.preventDefault(); event.stopPropagation(); onClick(); });\n return el;\n };\n const renderBar = () => {\n const words = currentWords();\n bar.replaceChildren();\n const title = doc.createElement('b');\n title.textContent = words.bar;\n const hint = doc.createElement('span');\n hint.textContent = `${words.hint} · ${words.edits(Object.keys(draft().strings).length)}`;\n bar.append(title, hint);\n if (changed) bar.append(button(words.reload, () => win.location.reload(), true));\n bar.append(button(words.exportPack, () => {\n const text = JSON.stringify(draftPack(draft(), options.site, options.catalog), null, 2);\n void win.navigator.clipboard?.writeText(text).then(() => { hint.textContent = words.copied; }).catch(() => {});\n }));\n bar.append(button(words.exit, options.exit));\n };\n const closePop = () => { pop?.remove(); pop = null; };\n\n const edit = (id: string, x: number, y: number, now: string) => {\n const words = currentWords();\n closePop();\n pop = doc.createElement('div');\n pop.className = 'pop';\n pop.style.left = `${Math.max(12, Math.min(x, win.innerWidth - 372))}px`;\n pop.style.top = `${Math.max(12, Math.min(y + 12, win.innerHeight - 260))}px`;\n const shape = lookup(id);\n const head = doc.createElement('small');\n head.textContent = `${words.key}: `;\n const code = doc.createElement('code');\n code.textContent = id;\n head.append(code);\n const current = doc.createElement('div');\n current.textContent = `${words.original}: ${now}`;\n pop.append(head, current);\n if (!shape || (shape.kind !== 'text' && shape.kind !== 'message')) {\n const note = doc.createElement('div');\n note.textContent = words.notTranslatable;\n pop.append(note, button(words.cancel, closePop));\n root.append(pop);\n return;\n }\n if (shape.params?.length) {\n const params = doc.createElement('small');\n params.textContent = `${words.parameters}: ${shape.params.map(name => `{${name}}${shape.plural?.includes(name) ? ` (${words.plural})` : ''}`).join(', ')}`;\n pop.append(params);\n }\n const input = doc.createElement('textarea');\n input.value = draft().strings[id] ?? now;\n const error = doc.createElement('div');\n error.className = 'err';\n const row = doc.createElement('div');\n row.className = 'row';\n if (draft().strings[id] !== undefined) {\n row.append(button(words.remove, () => { const next = draft(); delete next.strings[id]; save(next); changed = true; closePop(); renderBar(); }));\n }\n row.append(button(words.cancel, closePop), button(words.save, () => {\n const value = input.value.trim();\n const problem = checkEdit(lookup, id, value);\n if (problem) { error.textContent = problem; return; }\n const next = draft();\n next.strings[id] = value;\n save(next);\n changed = true;\n closePop();\n renderBar();\n }, true));\n pop.append(input, error, row);\n root.append(pop);\n input.focus();\n };\n\n /** The marked keys at a click: the element's own text and labels, then up to three parents. */\n const keysAt = (target: Element | null) => {\n for (let el = target, depth = 0; el && depth < 4; el = el.parentElement, depth += 1) {\n const texts = [el.textContent ?? '', el.getAttribute('aria-label') ?? '', el.getAttribute('placeholder') ?? '', el.getAttribute('title') ?? ''];\n const found = texts.flatMap(text => options.marker.ids(text).map(id => ({ id, text: options.marker.strip(text) })));\n if (found.length) return found.filter((item, i) => found.findIndex(other => other.id === item.id) === i).slice(0, 8);\n }\n return [];\n };\n const onClick = (event: MouseEvent) => {\n const words = currentWords();\n if (event.composedPath().includes(host)) return;\n const found = keysAt(event.target as Element | null);\n if (!found.length) return;\n event.preventDefault();\n event.stopPropagation();\n if (found.length === 1) { edit(found[0]!.id, event.clientX, event.clientY, found[0]!.text); return; }\n closePop();\n pop = doc.createElement('div');\n pop.className = 'pop';\n pop.style.left = `${Math.max(12, Math.min(event.clientX, win.innerWidth - 372))}px`;\n pop.style.top = `${Math.max(12, Math.min(event.clientY + 12, win.innerHeight - 260))}px`;\n const pick = doc.createElement('div');\n pick.className = 'pick';\n const label = doc.createElement('small');\n label.textContent = words.choose;\n pick.append(label, ...found.map(item => button(`${item.text.slice(0, 60)} · ${item.id}`, () => edit(item.id, event.clientX, event.clientY, item.text))));\n pop.append(pick, button(words.cancel, closePop));\n root.append(pop);\n };\n\n const mount = () => { (doc.body ?? doc.documentElement).append(host); renderBar(); };\n if (doc.body) mount(); else doc.addEventListener('DOMContentLoaded', mount, { once: true });\n win.addEventListener('click', onClick, true);\n return { dispose() { win.removeEventListener('click', onClick, true); host.remove(); } };\n}\n\n/** The draft as the last pack of a delivery (it applies over the others), when it has any string. */\nexport function withDraft(packs: readonly unknown[], storage: Pick<Storage, 'getItem'>, site: string, catalog: string, inventory: PageInventory): unknown[] {\n const draft = readDraft(storage);\n if (!draft || !Object.keys(draft.strings).length) return [...packs];\n const pack = draftPack(draft, site, catalog);\n try { compilePack(pack, inventory); } catch { return [...packs]; }\n return [...packs, pack];\n}\n"]}
|
|
@@ -4,9 +4,11 @@ type Parts = ReturnType<typeof parseMessage>;
|
|
|
4
4
|
* What the site's value for a key is:
|
|
5
5
|
* - `text`: a plain string;
|
|
6
6
|
* - `message`: a function of a parameter object that returns a string;
|
|
7
|
-
* - `rich`: text with element slots (
|
|
7
|
+
* - `rich`: text with element slots (a link or a button inside a sentence). It can be replaced when
|
|
8
|
+
* the inventory knows its slot count (`slots`): the pack value then places the site's own
|
|
9
|
+
* elements with `{e0}`, `{e1}`, …, each exactly once (the last may be left out); the value itself is still plain text;
|
|
8
10
|
* - `format`: a compiled number/date/currency formatter (not text).
|
|
9
|
-
* Only `text` and `
|
|
11
|
+
* Only `text`, `message` and `rich` with known `slots` can be replaced by a pack.
|
|
10
12
|
*/
|
|
11
13
|
export type StringKind = 'text' | 'message' | 'rich' | 'format';
|
|
12
14
|
export type InventoryEntry = {
|
|
@@ -17,6 +19,8 @@ export type InventoryEntry = {
|
|
|
17
19
|
plural?: string[];
|
|
18
20
|
/** Parameters the site's value selects on. */
|
|
19
21
|
select?: string[];
|
|
22
|
+
/** Element slots of a `rich` value (`{e0}` … `{e<slots-1>}` in a pack value; the last may be left out). Without it the key is closed. */
|
|
23
|
+
slots?: number;
|
|
20
24
|
/** Tag slot names, for catalogs whose values carry `<tag>…</tag>` slots. */
|
|
21
25
|
tags?: string[];
|
|
22
26
|
/** Hash of the base-language value (`hashText`): drift detection; never the text itself. */
|
|
@@ -35,6 +39,24 @@ export type StringInventory = {
|
|
|
35
39
|
build?: string;
|
|
36
40
|
entries: Record<string, InventoryEntry>;
|
|
37
41
|
};
|
|
42
|
+
/**
|
|
43
|
+
* When a pack applies. Without a condition it always does. `languages`: only while the site shows
|
|
44
|
+
* one of these languages (a tag matches itself and its regional forms: `en` matches `en-GB`), for
|
|
45
|
+
* example a rewording of English that must not touch a Turkish page.
|
|
46
|
+
*/
|
|
47
|
+
export type PackCondition = {
|
|
48
|
+
languages?: string[];
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* A pack's rewording rule: whole words replaced inside the site's own text, wherever it appears
|
|
52
|
+
* (`{ "Post": "Tweet", "posts": "tweets" }`). It carries none of the site's sentences, so it keeps
|
|
53
|
+
* working when the site words a string differently, and it applies to a value of the site (or of an
|
|
54
|
+
* earlier pack) that no later pack replaced. Words are matched exactly (case as written), never
|
|
55
|
+
* inside a longer word, and never in a parameter's value (a user named "Post" stays "Post").
|
|
56
|
+
*/
|
|
57
|
+
export type PackRewrite = {
|
|
58
|
+
words: Record<string, string>;
|
|
59
|
+
};
|
|
38
60
|
/** A string pack document: metadata and `key id -> text`. */
|
|
39
61
|
export type StringPack = {
|
|
40
62
|
format: 1;
|
|
@@ -46,6 +68,10 @@ export type StringPack = {
|
|
|
46
68
|
authors: string[];
|
|
47
69
|
/** A language whose plural rules the pack follows when the runtime does not know `language`. */
|
|
48
70
|
base?: string;
|
|
71
|
+
/** The pack applies only when this holds (default: always). */
|
|
72
|
+
when?: PackCondition;
|
|
73
|
+
/** Rewords the site's text instead of (or besides) replacing keys. */
|
|
74
|
+
rewrite?: PackRewrite;
|
|
49
75
|
version?: string;
|
|
50
76
|
description?: string;
|
|
51
77
|
strings: Record<string, string>;
|
|
@@ -57,12 +83,14 @@ export declare const STRING_PACK_LIMITS: Readonly<{
|
|
|
57
83
|
authors: 16;
|
|
58
84
|
}>;
|
|
59
85
|
export declare const MAIN_GROUP = "main";
|
|
60
|
-
/** Whether a pack can replace a value of this kind. */
|
|
61
|
-
export declare function translatable(kind: StringKind): boolean;
|
|
86
|
+
/** Whether a pack can replace a value of this kind (a `rich` one only with known `slots`: pass the shape). */
|
|
87
|
+
export declare function translatable(kind: StringKind | Pick<EntryShape, 'kind' | 'slots'>): boolean;
|
|
88
|
+
/** Whether `language` (the site's) satisfies a pack's `when`. */
|
|
89
|
+
export declare function conditionHolds(when: PackCondition | undefined, language: string | undefined): boolean;
|
|
62
90
|
/** 53-bit hash of a text as 11 base-36 characters. Same result in every runtime. */
|
|
63
91
|
export declare function hashText(text: string): string;
|
|
64
92
|
/** The facts the validator and the engine need about one key. */
|
|
65
|
-
export type EntryShape = Pick<InventoryEntry, 'kind' | 'params' | 'plural' | 'select' | 'tags'>;
|
|
93
|
+
export type EntryShape = Pick<InventoryEntry, 'kind' | 'params' | 'plural' | 'select' | 'tags' | 'slots'>;
|
|
66
94
|
export type InventoryLookup = (id: string) => EntryShape | undefined;
|
|
67
95
|
/**
|
|
68
96
|
* The inventory as it ships inside a page runtime: ids and shapes only, no hashes, a fraction of
|
|
@@ -75,6 +103,8 @@ export type PageInventory = {
|
|
|
75
103
|
text: string;
|
|
76
104
|
message: Record<string, string>;
|
|
77
105
|
closed: string;
|
|
106
|
+
/** Rich entries a pack can replace: id -> slot count. */
|
|
107
|
+
rich?: Record<string, number>;
|
|
78
108
|
/** A slice: only the keys some pack names. A key missing from a slice is not a new key of the site. */
|
|
79
109
|
partial?: boolean;
|
|
80
110
|
};
|
|
@@ -105,10 +135,20 @@ export declare function validateString(shape: EntryShape, value: unknown): {
|
|
|
105
135
|
} | {
|
|
106
136
|
reason: string;
|
|
107
137
|
};
|
|
138
|
+
/**
|
|
139
|
+
* A replaced `rich` value in the shape X's own renderer reads: piece `n` is shown, then the site's
|
|
140
|
+
* element `order[n]` (the last piece has no element after it).
|
|
141
|
+
*/
|
|
142
|
+
export type RichTemplate = {
|
|
143
|
+
readonly pieces: readonly string[];
|
|
144
|
+
readonly order: readonly number[];
|
|
145
|
+
};
|
|
108
146
|
export type CompiledValue = {
|
|
109
147
|
text: string;
|
|
110
148
|
} | {
|
|
111
149
|
parts: Parts;
|
|
150
|
+
} | {
|
|
151
|
+
rich: RichTemplate;
|
|
112
152
|
};
|
|
113
153
|
export type PackReport = {
|
|
114
154
|
/** Entries in the pack. */
|
|
@@ -127,6 +167,9 @@ export type CompiledPack = {
|
|
|
127
167
|
name: string;
|
|
128
168
|
language: string;
|
|
129
169
|
base?: string;
|
|
170
|
+
when?: PackCondition;
|
|
171
|
+
/** The pack's word rewriting, ready to apply to a text. */
|
|
172
|
+
rewrite?: (text: string) => string;
|
|
130
173
|
site: string;
|
|
131
174
|
catalog: string;
|
|
132
175
|
values: ReadonlyMap<string, CompiledValue>;
|
|
@@ -158,6 +201,11 @@ export type StringEngine = {
|
|
|
158
201
|
* Feeding a value this engine produced back in returns the same value (idempotent).
|
|
159
202
|
*/
|
|
160
203
|
resolve(id: string, original: unknown): unknown;
|
|
204
|
+
/**
|
|
205
|
+
* The language the site shows (what packs with a `when.languages` condition are checked
|
|
206
|
+
* against). Set before the first `resolve`; until then conditional packs do not apply.
|
|
207
|
+
*/
|
|
208
|
+
setLanguage(language: string): void;
|
|
161
209
|
/** The site's own values of the keys a pack changed: what restoring needs. */
|
|
162
210
|
originals(): ReadonlyMap<string, unknown>;
|
|
163
211
|
stats(): EngineStats;
|
|
@@ -22,10 +22,23 @@ export const MAIN_GROUP = 'main';
|
|
|
22
22
|
const TAG = /^[A-Za-z]{2,8}(?:-[A-Za-z0-9]{1,8})*$/;
|
|
23
23
|
const IDENT = /^[a-z][a-z0-9-]{0,31}$/;
|
|
24
24
|
const CONTROL = /[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f--]/;
|
|
25
|
+
const REWRITE_FROM = /^[\p{L}\p{N}]{1,40}$/u;
|
|
26
|
+
const REWRITE_TO = /^[\p{L}\p{N}][\p{L}\p{N}'’-]{0,39}$/u;
|
|
27
|
+
const MAX_REWRITES = 64;
|
|
25
28
|
const CATEGORIES = new Set(['zero', 'one', 'two', 'few', 'many', 'other']);
|
|
26
|
-
/** Whether a pack can replace a value of this kind. */
|
|
29
|
+
/** Whether a pack can replace a value of this kind (a `rich` one only with known `slots`: pass the shape). */
|
|
27
30
|
export function translatable(kind) {
|
|
28
|
-
|
|
31
|
+
const { kind: k, slots } = typeof kind === 'string' ? { kind, slots: undefined } : kind;
|
|
32
|
+
return k === 'text' || k === 'message' || (k === 'rich' && slots !== undefined);
|
|
33
|
+
}
|
|
34
|
+
/** Whether `language` (the site's) satisfies a pack's `when`. */
|
|
35
|
+
export function conditionHolds(when, language) {
|
|
36
|
+
if (!when?.languages)
|
|
37
|
+
return true;
|
|
38
|
+
if (!language)
|
|
39
|
+
return false;
|
|
40
|
+
const have = language.toLowerCase();
|
|
41
|
+
return when.languages.some(tag => { const want = tag.toLowerCase(); return have === want || have.startsWith(`${want}-`); });
|
|
29
42
|
}
|
|
30
43
|
/** 53-bit hash of a text as 11 base-36 characters. Same result in every runtime. */
|
|
31
44
|
export function hashText(text) {
|
|
@@ -44,8 +57,11 @@ function compact(site, catalog, shapes) {
|
|
|
44
57
|
const text = [];
|
|
45
58
|
const closed = [];
|
|
46
59
|
const message = {};
|
|
60
|
+
const rich = {};
|
|
47
61
|
for (const [id, entry] of shapes) {
|
|
48
|
-
if (entry.kind === '
|
|
62
|
+
if (entry.kind === 'rich' && entry.slots !== undefined && !entry.tags?.length)
|
|
63
|
+
rich[id] = entry.slots;
|
|
64
|
+
else if (entry.kind === 'text' && !entry.tags?.length)
|
|
49
65
|
text.push(id);
|
|
50
66
|
else if (entry.kind === 'message' && !entry.tags?.length) {
|
|
51
67
|
message[id] = (entry.params ?? []).map(name => `${name}${entry.plural?.includes(name) ? '*' : ''}${entry.select?.includes(name) ? '?' : ''}`).join(' ');
|
|
@@ -53,7 +69,7 @@ function compact(site, catalog, shapes) {
|
|
|
53
69
|
else
|
|
54
70
|
closed.push(id);
|
|
55
71
|
}
|
|
56
|
-
return { site, catalog, text: text.join(' '), message, closed: closed.join(' ') };
|
|
72
|
+
return { site, catalog, text: text.join(' '), message, closed: closed.join(' '), ...(Object.keys(rich).length ? { rich } : {}) };
|
|
57
73
|
}
|
|
58
74
|
export function packInventory(inventory) {
|
|
59
75
|
return compact(inventory.site, inventory.catalog, Object.entries(inventory.entries));
|
|
@@ -81,6 +97,7 @@ export function isPageInventory(input) {
|
|
|
81
97
|
return typeof input.site === 'string' && IDENT.test(input.site) && typeof input.catalog === 'string' && IDENT.test(input.catalog)
|
|
82
98
|
&& ids(input.text) && ids(input.closed) && isRecord(input.message)
|
|
83
99
|
&& Object.entries(input.message).every(([id, names]) => /^[\w$.-]+$/.test(id) && typeof names === 'string' && /^[\w$*? ]*$/.test(names))
|
|
100
|
+
&& (input.rich === undefined || (isRecord(input.rich) && Object.entries(input.rich).every(([id, n]) => /^[\w$.-]+$/.test(id) && Number.isInteger(n) && n >= 0 && n <= 32)))
|
|
84
101
|
&& (input.partial === undefined || typeof input.partial === 'boolean');
|
|
85
102
|
}
|
|
86
103
|
const TEXT_SHAPE = Object.freeze({ kind: 'text' });
|
|
@@ -100,6 +117,9 @@ export function inventoryLookup(inventory) {
|
|
|
100
117
|
if (inventory.closed)
|
|
101
118
|
for (const id of inventory.closed.split(' '))
|
|
102
119
|
map.set(id, CLOSED_SHAPE);
|
|
120
|
+
if (inventory.rich)
|
|
121
|
+
for (const [id, slots] of Object.entries(inventory.rich))
|
|
122
|
+
map.set(id, { kind: 'rich', slots });
|
|
103
123
|
for (const [id, spec] of Object.entries(inventory.message)) {
|
|
104
124
|
const names = spec ? spec.split(' ') : [];
|
|
105
125
|
map.set(id, {
|
|
@@ -137,6 +157,22 @@ export function checkPackShape(input) {
|
|
|
137
157
|
|| input.authors.some(author => typeof author !== 'string' || !author.trim() || author.length > STRING_PACK_LIMITS.name || CONTROL.test(author) || /[<>]/.test(author))) {
|
|
138
158
|
error(`"authors" must be a list of at most ${STRING_PACK_LIMITS.authors} plain-text names`);
|
|
139
159
|
}
|
|
160
|
+
if (input.when !== undefined) {
|
|
161
|
+
const when = input.when;
|
|
162
|
+
if (!isRecord(when) || Object.keys(when).some(key => key !== 'languages'))
|
|
163
|
+
error('"when" is an object with "languages"');
|
|
164
|
+
else if (when.languages !== undefined && (!Array.isArray(when.languages) || !when.languages.length || when.languages.length > 16
|
|
165
|
+
|| when.languages.some(tag => typeof tag !== 'string' || !TAG.test(tag))))
|
|
166
|
+
error('"when.languages" must be a list of 1 to 16 language tags');
|
|
167
|
+
}
|
|
168
|
+
if (input.rewrite !== undefined) {
|
|
169
|
+
const rewrite = input.rewrite;
|
|
170
|
+
const words = isRecord(rewrite) ? rewrite.words : undefined;
|
|
171
|
+
if (!isRecord(rewrite) || Object.keys(rewrite).some(key => key !== 'words') || !isRecord(words) || !Object.keys(words).length || Object.keys(words).length > MAX_REWRITES
|
|
172
|
+
|| Object.entries(words).some(([from, to]) => !REWRITE_FROM.test(from) || typeof to !== 'string' || !REWRITE_TO.test(to))) {
|
|
173
|
+
error(`"rewrite" is { "words": { "Word": "Replacement" } } with 1 to ${MAX_REWRITES} plain words`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
140
176
|
for (const field of ['version', 'description']) {
|
|
141
177
|
const value = input[field];
|
|
142
178
|
if (value !== undefined && (typeof value !== 'string' || value.length > 400 || CONTROL.test(value) || /[<>]/.test(value)))
|
|
@@ -146,7 +182,7 @@ export function checkPackShape(input) {
|
|
|
146
182
|
error('"strings" must be an object of key id -> text');
|
|
147
183
|
else if (Object.keys(input.strings).length > STRING_PACK_LIMITS.strings)
|
|
148
184
|
error(`"strings" has more than ${STRING_PACK_LIMITS.strings} entries`);
|
|
149
|
-
const known = new Set(['$schema', 'format', 'site', 'catalog', 'language', 'base', 'name', 'authors', 'version', 'description', 'strings']);
|
|
185
|
+
const known = new Set(['$schema', 'format', 'site', 'catalog', 'language', 'base', 'when', 'rewrite', 'name', 'authors', 'version', 'description', 'strings']);
|
|
150
186
|
for (const key of Object.keys(input))
|
|
151
187
|
if (!known.has(key))
|
|
152
188
|
issues.push({ level: 'warning', message: `unknown field "${key}" is ignored` });
|
|
@@ -224,7 +260,7 @@ function checkParts(parts, shape) {
|
|
|
224
260
|
* reason it is refused (the original then stays).
|
|
225
261
|
*/
|
|
226
262
|
export function validateString(shape, value) {
|
|
227
|
-
if (!translatable(shape
|
|
263
|
+
if (!translatable(shape))
|
|
228
264
|
return { reason: `not translatable yet (${shape.kind})` };
|
|
229
265
|
if (typeof value !== 'string')
|
|
230
266
|
return { reason: 'the value must be text' };
|
|
@@ -244,6 +280,23 @@ export function validateString(shape, value) {
|
|
|
244
280
|
catch (error) {
|
|
245
281
|
return { reason: `invalid message syntax: ${error instanceof Error ? error.message : String(error)}` };
|
|
246
282
|
}
|
|
283
|
+
if (shape.kind === 'rich') {
|
|
284
|
+
// Element slots: `{e0}` … `{e<slots-1>}`, each exactly once (the last may be left out); no plural, select or other parameter.
|
|
285
|
+
const slots = Array.from({ length: shape.slots ?? 0 }, (_, index) => `e${index}`);
|
|
286
|
+
const bad = checkParts(parts, { kind: 'message', params: slots });
|
|
287
|
+
if (bad)
|
|
288
|
+
return { reason: bad };
|
|
289
|
+
const used = parts.flatMap(part => (typeof part !== 'string' && part.kind === 'arg' ? [part.name] : []));
|
|
290
|
+
for (const name of slots) {
|
|
291
|
+
const count = used.filter(item => item === name).length;
|
|
292
|
+
// The element after the last piece is not always there: that one slot may be left out.
|
|
293
|
+
if (count === 0 && name === slots[slots.length - 1] && slots.length > 0)
|
|
294
|
+
continue;
|
|
295
|
+
if (count !== 1)
|
|
296
|
+
return { reason: count ? `{${name}} (the original's element) must appear once, not ${count} times` : `the original's element {${name}} is missing` };
|
|
297
|
+
}
|
|
298
|
+
return { parts };
|
|
299
|
+
}
|
|
247
300
|
const problem = checkParts(parts, shape.kind === 'message' ? shape : { kind: 'text' });
|
|
248
301
|
return problem ? { reason: problem } : { parts };
|
|
249
302
|
}
|
|
@@ -251,6 +304,31 @@ export function validateString(shape, value) {
|
|
|
251
304
|
function literal(parts) {
|
|
252
305
|
return parts.map(part => (typeof part === 'string' ? part : '')).join('');
|
|
253
306
|
}
|
|
307
|
+
/** The word-for-word rewriting of a validated `rewrite` rule. */
|
|
308
|
+
function compileRewrite(rule) {
|
|
309
|
+
const words = new Map(Object.entries(rule.words));
|
|
310
|
+
// Words are letters and digits only (checked), so they need no escaping.
|
|
311
|
+
const alternatives = [...words.keys()].sort((a, b) => b.length - a.length).join('|');
|
|
312
|
+
const pattern = new RegExp(String.raw `(?<![\p{L}\p{N}_])(?:${alternatives})(?![\p{L}\p{N}_])`, 'gu');
|
|
313
|
+
return text => text.replace(pattern, word => words.get(word) ?? word);
|
|
314
|
+
}
|
|
315
|
+
/** A validated rich value as pieces and element order. */
|
|
316
|
+
function richTemplate(parts) {
|
|
317
|
+
const pieces = [];
|
|
318
|
+
const order = [];
|
|
319
|
+
let current = '';
|
|
320
|
+
for (const part of parts) {
|
|
321
|
+
if (typeof part === 'string')
|
|
322
|
+
current += part;
|
|
323
|
+
else if (part.kind === 'arg') {
|
|
324
|
+
pieces.push(current);
|
|
325
|
+
order.push(Number(part.name.slice(1)));
|
|
326
|
+
current = '';
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
pieces.push(current);
|
|
330
|
+
return Object.freeze({ pieces: Object.freeze(pieces), order: Object.freeze(order) });
|
|
331
|
+
}
|
|
254
332
|
/**
|
|
255
333
|
* Validates a pack against an inventory. Throws on a malformed envelope or a pack for another
|
|
256
334
|
* site/catalog; every other problem is per key and lands in `report`.
|
|
@@ -278,10 +356,10 @@ export function compilePack(input, inventory) {
|
|
|
278
356
|
report.rejected.push({ id, reason: result.reason });
|
|
279
357
|
continue;
|
|
280
358
|
}
|
|
281
|
-
values.set(id, shape.kind === 'text' ? { text: literal(result.parts) } : { parts: result.parts });
|
|
359
|
+
values.set(id, shape.kind === 'text' ? { text: literal(result.parts) } : shape.kind === 'rich' ? { rich: richTemplate(result.parts) } : { parts: result.parts });
|
|
282
360
|
report.valid += 1;
|
|
283
361
|
}
|
|
284
|
-
return { name: pack.name, language: pack.language, base: pack.base, site: pack.site, catalog: pack.catalog, values, report };
|
|
362
|
+
return { name: pack.name, language: pack.language, base: pack.base, when: pack.when, rewrite: pack.rewrite ? compileRewrite(pack.rewrite) : undefined, site: pack.site, catalog: pack.catalog, values, report };
|
|
285
363
|
}
|
|
286
364
|
// -------------------------------------------------------------------------------------------
|
|
287
365
|
// Interpreting a message (closures over the parsed parts; nothing is evaluated)
|
|
@@ -343,6 +421,49 @@ export function renderMessage(parts, params, languages, pound = undefined) {
|
|
|
343
421
|
}
|
|
344
422
|
return out;
|
|
345
423
|
}
|
|
424
|
+
/** The site's (or a pack's) value of one key with `rewrite` applied; a message keeps its parameters' values as they are. */
|
|
425
|
+
function rewriteValue(shape, value, rewrite) {
|
|
426
|
+
if (typeof value === 'string')
|
|
427
|
+
return rewrite(value);
|
|
428
|
+
if (Array.isArray(value)) {
|
|
429
|
+
const mapped = value.map(piece => (typeof piece === 'string' ? rewrite(piece) : piece));
|
|
430
|
+
return mapped.some((piece, index) => piece !== value[index]) ? mapped : value;
|
|
431
|
+
}
|
|
432
|
+
if (typeof value === 'function') {
|
|
433
|
+
const message = value;
|
|
434
|
+
const plural = new Set(shape.plural ?? []);
|
|
435
|
+
return function (params) {
|
|
436
|
+
const input = isRecord(params) ? params : {};
|
|
437
|
+
// The parameters go through as private markers, so only the site's own words are reworded.
|
|
438
|
+
const forward = { ...input };
|
|
439
|
+
const held = [];
|
|
440
|
+
for (const name of shape.params ?? []) {
|
|
441
|
+
if (plural.has(name) || !(name in input))
|
|
442
|
+
continue;
|
|
443
|
+
const marker = `\uE000${held.length}\uE001`;
|
|
444
|
+
held.push([marker, input[name]]);
|
|
445
|
+
forward[name] = marker;
|
|
446
|
+
}
|
|
447
|
+
try {
|
|
448
|
+
const out = message.call(this, forward);
|
|
449
|
+
if (typeof out !== 'string')
|
|
450
|
+
return message.call(this, params);
|
|
451
|
+
let text = rewrite(out);
|
|
452
|
+
for (const [marker, original] of held)
|
|
453
|
+
text = text.split(marker).join(String(original));
|
|
454
|
+
return text;
|
|
455
|
+
}
|
|
456
|
+
catch {
|
|
457
|
+
return message.call(this, params);
|
|
458
|
+
}
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
if (typeof value === 'object' && value !== null && 'pieces' in value) {
|
|
462
|
+
const template = value;
|
|
463
|
+
return Object.freeze({ pieces: Object.freeze(template.pieces.map(rewrite)), order: template.order });
|
|
464
|
+
}
|
|
465
|
+
return value;
|
|
466
|
+
}
|
|
346
467
|
const MAX_LISTED = 200;
|
|
347
468
|
/** `packs` in application order: a later pack wins over an earlier one for the same key. */
|
|
348
469
|
export function createStringEngine(inventory, packs) {
|
|
@@ -355,55 +476,70 @@ export function createStringEngine(inventory, packs) {
|
|
|
355
476
|
const seen = new Set();
|
|
356
477
|
const unlisted = new Set();
|
|
357
478
|
const mismatched = new Set();
|
|
479
|
+
let siteLanguage;
|
|
358
480
|
const winner = (id) => {
|
|
359
481
|
for (let index = packs.length - 1; index >= 0; index -= 1) {
|
|
482
|
+
if (!conditionHolds(packs[index].when, siteLanguage))
|
|
483
|
+
continue;
|
|
360
484
|
const value = packs[index].values.get(id);
|
|
361
485
|
if (value)
|
|
362
|
-
return { pack: packs[index], value };
|
|
486
|
+
return { pack: packs[index], value, index };
|
|
363
487
|
}
|
|
364
488
|
return null;
|
|
365
489
|
};
|
|
366
490
|
const resolve = (id, incoming) => {
|
|
367
491
|
let original = incoming;
|
|
368
492
|
// A value of ours coming back in (a second application): work from the site's own value again.
|
|
369
|
-
if (typeof incoming === 'function' && produced.has(incoming))
|
|
493
|
+
if ((typeof incoming === 'function' || (typeof incoming === 'object' && incoming !== null)) && produced.has(incoming))
|
|
370
494
|
original = produced.get(incoming);
|
|
371
495
|
else if (typeof incoming === 'string' && applied.has(id) && applied.get(id) === incoming)
|
|
372
496
|
original = originals.get(id);
|
|
373
497
|
seen.add(id);
|
|
374
498
|
const found = winner(id);
|
|
375
|
-
|
|
499
|
+
// Word rewriting of the packs after the one whose text won (all of them when none did).
|
|
500
|
+
const rewrites = packs.flatMap((pack, index) => (index > (found?.index ?? -1) && pack.rewrite && conditionHolds(pack.when, siteLanguage) ? [pack.rewrite] : []));
|
|
501
|
+
if (!found && !rewrites.length) {
|
|
376
502
|
if (whole && unlisted.size < MAX_LISTED && !lookup(id))
|
|
377
503
|
unlisted.add(id);
|
|
378
504
|
return original;
|
|
379
505
|
}
|
|
380
506
|
const shape = lookup(id);
|
|
381
|
-
if (!shape || !translatable(shape
|
|
507
|
+
if (!shape || shape.kind === 'format' || (found && !translatable(shape)))
|
|
382
508
|
return original;
|
|
383
|
-
if (shape.kind === 'text' ? typeof original !== 'string' : typeof original !== 'function') {
|
|
509
|
+
if (shape.kind === 'text' ? typeof original !== 'string' : shape.kind === 'rich' ? !Array.isArray(original) : typeof original !== 'function') {
|
|
384
510
|
if (mismatched.size < MAX_LISTED)
|
|
385
511
|
mismatched.add(id);
|
|
386
512
|
return original;
|
|
387
513
|
}
|
|
388
514
|
if (originals.has(id) && originals.get(id) === original)
|
|
389
515
|
return applied.get(id);
|
|
390
|
-
let next;
|
|
391
|
-
if (
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
516
|
+
let next = original;
|
|
517
|
+
if (found) {
|
|
518
|
+
if ('text' in found.value) {
|
|
519
|
+
next = found.value.text;
|
|
520
|
+
}
|
|
521
|
+
else if ('rich' in found.value) {
|
|
522
|
+
next = found.value.rich;
|
|
523
|
+
}
|
|
524
|
+
else {
|
|
525
|
+
const parts = found.value.parts;
|
|
526
|
+
const languages = [found.pack.language, found.pack.base];
|
|
527
|
+
next = (params) => renderMessage(parts, isRecord(params) ? params : {}, languages);
|
|
528
|
+
}
|
|
400
529
|
}
|
|
530
|
+
for (const rewrite of rewrites)
|
|
531
|
+
next = rewriteValue(shape, next, rewrite);
|
|
532
|
+
if (next === original)
|
|
533
|
+
return original;
|
|
534
|
+
if (typeof next === 'function' || typeof next === 'object')
|
|
535
|
+
produced.set(next, original);
|
|
401
536
|
originals.set(id, original);
|
|
402
537
|
applied.set(id, next);
|
|
403
538
|
return next;
|
|
404
539
|
};
|
|
405
540
|
return {
|
|
406
541
|
resolve,
|
|
542
|
+
setLanguage(language) { siteLanguage = language; },
|
|
407
543
|
originals: () => originals,
|
|
408
544
|
stats: () => ({ seen: seen.size, applied: applied.size, unlisted: [...unlisted], mismatched: [...mismatched] }),
|
|
409
545
|
};
|
|
@@ -432,7 +568,7 @@ export function coverage(inventory, pack) {
|
|
|
432
568
|
for (const [id, entry] of Object.entries(inventory.entries)) {
|
|
433
569
|
const group = (out[entry.group ?? MAIN_GROUP] ??= { translatable: 0, translated: 0, closed: 0 });
|
|
434
570
|
for (const bucket of [group, out.all]) {
|
|
435
|
-
if (!translatable(entry
|
|
571
|
+
if (!translatable(entry)) {
|
|
436
572
|
bucket.closed += 1;
|
|
437
573
|
continue;
|
|
438
574
|
}
|
|
@@ -457,7 +593,7 @@ export function driftReport(inventory, observed, loaded) {
|
|
|
457
593
|
report.added.push(id);
|
|
458
594
|
continue;
|
|
459
595
|
}
|
|
460
|
-
if (before.kind !== now.kind || !sameList(before.params, now.params) || !sameList(before.plural, now.plural) || !sameList(before.select, now.select) || !sameList(before.tags, now.tags)) {
|
|
596
|
+
if (before.kind !== now.kind || before.slots !== now.slots || !sameList(before.params, now.params) || !sameList(before.plural, now.plural) || !sameList(before.select, now.select) || !sameList(before.tags, now.tags)) {
|
|
461
597
|
report.reshaped.push(id);
|
|
462
598
|
}
|
|
463
599
|
else if (before.hash !== now.hash) {
|