A re-delivery could leave a stale row behind for an utterance the new
delivery does not cover: the upsert only touches rows the delivery mentions.
--replace clears the language's draft rows first, so what the reviewer sees
is what the delivery actually says.
Confirmed rows are never deleted. A confirmed translation is an approval, and
the import skips confirmed rows anyway, so the approved text survives a
re-delivery.
2 new tests.
UmbraTrans delivers one row per utterance per target language, in the shape
translations.import_translations already accepts. There was no script to run
that ingest, so the delivery side of the contract existed only as a function.
scripts/import_translations.py runs it: report-only by default, --write to
store, --confirm to store as confirmed rather than draft. It prints the
coverage before and after, so a delivery can be checked without writing it.
Two bugs in the ingest, both found by running a real delivery:
* import_translations folded the target language with lang.normalize, which
does not know script subtags. A delivery labelled "zh-Hans" - which is
what UmbraTrans writes - matched nothing, so every row was dropped and
reported as a problem. It now folds with canonical_code, and an unknown
language is still stored: the text is reviewable even when the render
will be refused.
* coverage and not_voiced_rows compared the stored code to the requested
code as strings, so a row stored as "zh-Hans" read as "nothing delivered"
when the reviewer asked about "zh". The coverage report was wrong in the
direction that makes a reviewer distrust it.
Measured on the real Deal Hunter Chinese delivery: 4,300 rows ingested, the
review UI's resolver then renders Chinese for 12/12 utterances of chapter 1
and 415/416 of chapter 6 - the missing one being the spoken chapter
announcement, which is deliberately not part of the reading edition.
spec 27 records what the shipped samples actually are: single-pass drafts
made against a hand-cleaned paraphrase of the English, matching the
speaker-verified database text in 0 of 12 utterances. The production Chinese
edition on oldbox does use the intent-based process; what it lacked was the
alignment back onto the utterances, which is now implemented in UmbraTrans.
7 new tests. Suite: 890 passed, 8 failed (the same 8 pre-existing failures).