The review UI's language dropdown did nothing: every selection kept rendering the English text. Two independent causes, both fixed. 1. The translations were never being read. The pipeline's `utterance_translations` table holds one row in Deal Hunter, and that row stores the *English* text under a `zh-Hans` code. The real translations live in the on-disk sample files under `samples/translations/`. `umbraaudio/translations.py` now resolves both sources and merges them by (chapter_id, utterance_index): the table wins where it holds a real translation, the sample files fill the gaps. A candidate whose text is identical to the source is a placeholder, not a translation, so it is ignored - otherwise the page would show English under a Chinese heading and claim it was translated. 2. The query that read them was broken. `language_routes.py` selected `u.index` and `u.speaker`; the columns are `utterance_index` and `speaker_name`, so every read raised `sqlite3.OperationalError: near "index": syntax error` and `/language-mockup` returned 500. It also opened `<project>/project.db` - a 0-byte decoy - instead of `<project>/.umbra/project.db`, the file that actually holds the data. Also in this commit: * The chapter review page honours `?lang=`, remembers the choice in a cookie (set by a middleware, so it survives on every page), and shows the selected language's *name* in the "has not been translated yet" message. * The page now shows the translation, not the source, and the edit box is bound to the source text so saving cannot overwrite the English with the Chinese. * The stray language selector that sat after `</html>` in base.html is gone; the chapters-list page's dropdown had zero options because the route never passed the language list. * The language tag is wired into generation: `utterance.language_code` (NULL = project source language) reaches `engine.generate()` and the staleness marker, so a retagged line is re-rendered instead of keeping English phonology behind an unchanged text. The marker records the language; a marker written before the tag existed means English, so the ~4,300 existing clips are not invalidated. * `project.source_language` (spec 31 section 13.1) records the book's own language, with a migration for existing project DBs. * A scene-break marker deliberately records no language: it is silence, identical in every language, and tagging it would claim a phonology the audio does not have. Tests: 883 passed / 8 failed - the same 8 pre-existing failures (2 test_pronunciation, 6 voice_profile_manager accent). 26 new tests cover the resolver and the generation wiring. ruff introduces no new findings.
Deal Hunter sample chapter - translated
Sample chapter 1 (the voice-introduction sample) in all 10 languages
Qwen3-TTS produces. Files are named <base>_<LANG>.json.
These are LLM-produced samples from a single pass. They are not the multi-pass semantic translation the production pipeline is meant to do (see specs/27-translation-pipeline-multipass.md). They exist to evaluate the audio stage per language and to agree the file format early. Native-speaker review required.
Register
Casual/informal register throughout, matching the English source. Korean uses 해요체 (everyday polite), NOT 합쇼체 (formal written) and NOT 해체 (반말, which would be socially wrong between these characters). T/V languages use the informal form (du/tu/tú). Russian keeps вы, because ты is too intimate for narration. Japanese stays polite-but-warm.
Files
deal_hunter_sample_ch1_DE.jsondeal_hunter_sample_ch1_EN.jsondeal_hunter_sample_ch1_ES.jsondeal_hunter_sample_ch1_FR.jsondeal_hunter_sample_ch1_IT.jsondeal_hunter_sample_ch1_JA.jsondeal_hunter_sample_ch1_KO.jsondeal_hunter_sample_ch1_PT.jsondeal_hunter_sample_ch1_RU.jsondeal_hunter_sample_ch1_ZH.json
Source issues found while translating
- utt 3: 'Kianda's aunt' — 'Kianda' is a typo for 'Kainda'.
- utt 5: 'the AI the runs the ship' — 'the' should be 'that'.
- utt 8: 'I trade salvage' — double space.
- The narrator's cues are telegraphic ('Princess Kainda say hello'), which a translator has to resolve by guessing. The production pipeline should decide whether to normalise these at the source.
- Every utterance in this chapter is typed 'narration' even where a character speaks in the first person. Same class of finding as the speaker-review audit; it matters because emotion and pace are set per utterance.
Decisions to confirm
Deal HunterandUmbra Audiokept in English in every language. Deal Hunter is both the title and the ship AI's name, so translating it would break the referent inside the story. Localisation decision, yours.- Character names transliterated by sound, not translated by meaning.