Masked translation round-trip; named translator keys

- pagerite/masking.py: technical spans (code, URLs, {placeholders}, attrs,
  footnote/link labels, container names, HTML tags) become numbered sentinels
  for the LLM round trip; results are restored by number and rejected when a
  sentinel is mangled (skipped for the rest of the run, stays pending).
  Chunks with no prose left after masking are never dispatched.
- Data.translate_key -> translate_keys dict (key -> name); the first key is
  generated at bootstrap, result transactions record the key as user=, and
  startup logs the service URL(s) via translate.log_service_urls.
- Fix /_translate proxying through the Vite dev server (missing slash).
This commit is contained in:
2026-09-02 19:56:54 +00:00
parent af76277d68
commit 4c886eda86
11 changed files with 329 additions and 46 deletions
+7 -5
View File
@@ -52,9 +52,9 @@ class Node(msgspec.Struct, omit_defaults=True):
class Data(msgspec.Struct):
...
#: API key gating the translator service WebSocket (/_translate/{key});
#: generated lazily at startup (see the lifespan in app.py).
translate_key: str = ""
#: API keys gating the translator service WebSocket (/_translate/{key}):
#: key -> display name; the first is generated at bootstrap (app.py).
translate_keys: dict[str, str] = {}
#: Wanted target languages for the translator service (presence-keys);
#: jobs are offered only in these ∩ a connection's capabilities.
translate_langs: dict[str, True] = {}
@@ -81,8 +81,10 @@ Notes:
`trans.get(hash(node.title), {}).get(lang)`. No separate title storage;
editing a title invalidates its translations automatically.
- **Per-hunk options** live in two places: *inherent* options are derived at
chunking time (code fences and HTML blocks are marked no-translate without
storing anything); *editor-set* flags are `node.no_trans` (keyed by chunk
chunking time (code fences, HTML blocks and prose-free chunks are
no-translate without storing anything — `needs_translation`, see
docs/localization.md "Masking"); *editor-set* flags are `node.no_trans`
(keyed by chunk
hash, so a heavy edit silently drops the flag — acceptable and
self-healing).
- **Patch payloads stay inline** in `Patch.hunks` — patches are small by