"""Speaker handling for the WolfDawn translation module. WolfDawn attributes a speaker to every extracted line (``speaker`` / ``speaker_src`` fields). For the two "first-line" formats the speaker name is baked into line 1 of the ``source`` text, e.g.:: "市民\nおぉっ!来た!帰ってきたぞ!" speaker_src = literal_line1_lowconf "セルリア\nほーら、ローザも手を振って。" speaker_src = literal_line1 The shared translation prompt (``data/prompt.txt``) is built around the RPG Maker ``[Speaker]: line`` convention and already knows to translate the speaker tag ("Always translate speaker tags to English: ``[クロネ]:`` -> ``[Kurone]:``"). So when translating we reshape those lines into ``[Speaker]: body`` and, on write-back, restore WOLF's native ``Speaker\nbody`` structure so injection stays byte-faithful to the original layout. Which formats are reshaped is configurable (``data/wolf_speakers.json``) so the workflow can toggle the high-confidence nameplate format and the lower-confidence first-line guess independently. """ from __future__ import annotations import json import re from util.paths import DATA_DIR from util.speaker_prefix import SPEAKER_TAG_RE, strip_speaker_prefix # speaker_src values whose name is baked into line 1 of the source text. FIRSTLINE_SRCS = ("literal_line1", "literal_line1_lowconf") # Default: reshape both first-line formats. WolfDawn already gates the # low-confidence one (short line 1, no control codes, no sentence punctuation), # so it is safe enough to enable by default. DEFAULT_CONFIG = { "literal_line1": True, "literal_line1_lowconf": True, } CONFIG_PATH = DATA_DIR / "wolf_speakers.json" # Optional leading window-option prefix (``@