Files
pgames-manager/backend/tests/test_translation.py
T
mrleo1nidandClaude Sonnet 5.5 7fa88e4025
CI / frontend (push) Failing after 27s
CI / backend (push) Failing after 49s
feat: language picker with flags, paging and follow mode, translation run in the floating window
Language picker with search and SVG flags, real pagination with locate, one global machine translation run shown next to uploads.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-25 19:24:20 +03:00

99 lines
4.9 KiB
Python

import json
import pytest
from app import translation as tr
from app.config import get_settings
from app.converter.pipeline import convert_archive
from tests.make_sample import build
@pytest.fixture
def manifest(tmp_path, monkeypatch):
monkeypatch.setenv("DATA_DIR", str(tmp_path / "data"))
get_settings.cache_clear()
archive = build(tmp_path / "sample.zip")
yield convert_archive(archive, "sample.zip", tmp_path / "work", lambda _: None).manifest
get_settings.cache_clear()
def test_index_has_context(manifest):
idx = tr.text_index(manifest)
first = idx.lines[0]
assert first.label == "start" and first.scene == "images/bg room.webp"
assert idx.lines[1].who == "Эйлин" and idx.lines[1].prev == first.source
assert idx.strings["Пойдём гулять"] == "menu" and idx.strings["Эйлин"] == "name"
def test_edit_new_language_and_outdated(manifest):
idx = tr.text_index(manifest)
tid = idx.lines[0].tid
m = tr.apply_changes(manifest, "de", lines={tid: {"t": "Willkommen", "state": "approved"}},
strings={"Эйлин": {"t": "Eileen"}}, meta={"glossary": [{"src": "Эйлин", "t": "Eileen"}]})
lines, strings, meta = tr.locale_tables(m, "de")
assert lines[tid]["t"] == "Willkommen" and lines[tid]["state"] == "translated" # no approve right
assert strings["Эйлин"]["t"] == "Eileen" and meta["glossary"][0]["t"] == "Eileen"
game = tr.file_json(m, "game.json")
assert game["locales"]["de"]["renpy"] == "german" and game["locales"]["de"]["lines"] == 1
# the original line changed in a mod: the translation is kept but marked outdated
info = idx.by_tid[tid]
changed = tr.LineInfo(**{**info.__dict__, "source": "другой текст"})
assert tr.line_state(lines[tid], changed) == "outdated"
# removing an entry
m2 = tr.apply_changes(m, "de", lines={tid: None})
assert tid not in tr.locale_tables(m2, "de")[0]
with pytest.raises(ValueError):
tr.apply_changes(manifest, "ru", lines={tid: {"t": "x"}}) # source language
def test_merge_languages_keeps_target_untouched(manifest):
tid = tr.text_index(manifest).lines[0].tid
fork = tr.apply_changes(manifest, "de", lines={tid: {"t": "Hallo"}})
assert tr.changed_languages(fork, manifest) == ["de"]
merged = tr.merge_languages(manifest, fork, ["de"])
assert tr.locale_tables(merged, "de")[0][tid]["t"] == "Hallo"
assert tr.locale_tables(merged, "en")[0] == tr.locale_tables(manifest, "en")[0]
assert "de" not in (tr.file_json(manifest, "game.json").get("locales") or {})
def test_three_way_publication_keeps_independent_lines(manifest):
a, b = [line.tid for line in tr.text_index(manifest).lines[:2]]
left = tr.apply_changes(manifest, "de", lines={a: {"t": "One"}})
right = tr.apply_changes(manifest, "de", lines={b: {"t": "Two"}})
merged, conflicts = tr.merge_translation(manifest, left, right)
assert not conflicts
lines = tr.locale_tables(merged, "de")[0]
assert lines[a]["t"] == "One" and lines[b]["t"] == "Two"
conflict = tr.apply_changes(manifest, "de", lines={a: {"t": "Other"}})
_, conflicts = tr.merge_translation(manifest, left, conflict)
assert conflicts and any(a in c["path"] for c in conflicts)
def test_po_and_csv_roundtrip(manifest):
tid = tr.text_index(manifest).lines[0].tid
m = tr.apply_changes(manifest, "de", lines={tid: {"t": "Zeile 1\nZeile \"2\""}},
strings={"Эйлин": {"t": "Eileen"}})
lines, strings = tr.parse_po(tr.export_po(m, "de", "Test"))
assert lines[tid]["t"] == "Zeile 1\nZeile \"2\"" and strings["Эйлин"]["t"] == "Eileen"
lines, strings = tr.parse_csv(tr.export_csv(m, "de"))
assert lines[tid]["t"] == "Zeile 1\nZeile \"2\"" and strings["Эйлин"]["t"] == "Eileen"
st = tr.stats(m, "de")
assert st["translated"] == 1 and st["stringsTranslated"] == 1
assert json.dumps(st)
def test_entries_page_locates_a_line_in_the_filtered_list(manifest):
idx = tr.text_index(manifest)
first, second = idx.lines[0], idx.lines[1]
page = tr.entries_page(manifest, "ru", state="untranslated", limit=1, locate=second.tid)
assert page["located"] == 1 and page["total"] == len(idx.lines) and len(page["items"]) == 1
m = tr.apply_changes(manifest, "ru", lines={first.tid: {"t": "Привет", "state": "machine"}})
# a translated line leaves the "untranslated" list, so the next one moves up
assert tr.entries_page(m, "ru", state="untranslated", locate=second.tid)["located"] == 0
assert tr.entries_page(m, "ru", state="untranslated", locate=first.tid)["located"] is None
# ...but stays in "todo" because machine translations still await a review
assert tr.entries_page(m, "ru", state="todo", locate=first.tid)["located"] == 0
assert tr.entries_page(m, "ru", locate="")["located"] is None
assert tr.entries_page(m, "ru", kind="strings", locate="Эйлин")["located"] is not None