Redesign translation overrides: keyed, structural, hash-anchored
Replace the old "patches" format (path:lang composite keys, ordered hunk lists, text-anchored matching) with Data.overrides: path -> lang -> LangEdits, keyed throughout so a save's database diff touches only the edited chunks. The old "patches" key is ignored on decode, discarding legacy data without a migration. - Whole-paragraph additions/deletions are structural: a drop flag on the original chunk hash, and additions in their own dict anchored from the neighboring chunks' before/after (first live referrer wins), so they stay in place across retranslation and one-sided original edits. - Within-paragraph edits (up to a full paragraph rewrite or split) are full-chunk replace patches applied by chunk hash alone: a retranslation is overridden wholesale, so user edits survive AI re-runs; editing the original changes the hash and orphans the patch. The old search-matching staleness gate is gone. - Saving a translation on a page without original chunks is rejected (REST 400 / WS error); emptying the original afterwards renders the translation empty, with the orphaned overrides inert.
This commit is contained in:
@@ -7,7 +7,7 @@
|
||||
// Flag clicks toggle and save immediately; the settings round-trip
|
||||
// re-reads the payload, so this tab only ever changes translate_langs. The
|
||||
// settings write's invalidation hook kicks the translation dispatcher. The
|
||||
// refresh button drops all machine translations (user patches are kept),
|
||||
// refresh button drops all machine translations (user overrides are kept),
|
||||
// making the dispatcher re-translate everything. Translator keys are
|
||||
// managed inline (➕ add, name edit, ✕ delete); new keys are generated
|
||||
// here in the server's format and everything rides the settings
|
||||
@@ -100,7 +100,7 @@ async function toggle(code) {
|
||||
|
||||
// Delete all machine translations server-side; the dispatcher re-fills
|
||||
// them (a connected translator starts getting jobs right away). User
|
||||
// patches survive — they are edits, not machine output.
|
||||
// overrides survive — they are edits, not machine output.
|
||||
const refreshing = ref(false)
|
||||
async function refresh() {
|
||||
if (refreshing.value) return
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
// edited as its effective (hybrid) Markdown; the hybrid the session
|
||||
// started from is kept as a shadow copy (shadowBase) and sent along at
|
||||
// save time, so the server diffs the user's changes only and stores them
|
||||
// as a patch — edits to a translation never touch the original, while
|
||||
// as per-chunk overrides — edits to a translation never touch the original, while
|
||||
// edits to the primary language re-chunk the original (and thereby
|
||||
// invalidate the affected translation fragments). The live preview always
|
||||
// renders the version being edited, whichever language the page itself
|
||||
@@ -208,7 +208,7 @@ function save() {
|
||||
if (lang.value) {
|
||||
msg.lang = lang.value
|
||||
// The shadow copy this session started from: the server diffs base →
|
||||
// markdown and stores only the user's changes as a patch.
|
||||
// markdown and stores only the user's changes as overrides.
|
||||
msg.base = shadowBase
|
||||
// An untouched title field is not sent: it holds the served
|
||||
// translation, which a save must not freeze into an override fragment.
|
||||
|
||||
Reference in New Issue
Block a user