Files
pgames-manager/backend/tests/test_translation.py
T
mrleo1nidandClaude Opus 5.5 ac164d1782 Localization stages 2-3: drafts, publishing, translation editor
- games.main_version_id, version visibility (draft/published) and kind
  (original/translation/mod), version_history; migration 0002
- permissions translations.edit and versions.publish
- "Translate" creates a translation draft; publishing merges the changed
  languages into the main version in place (players keep their saves), the
  previous manifest goes to the history and can be restored
- translation API: overview with per-state stats, entries with filters,
  search and scene context, save, glossary, locale font, PO/CSV export/import
- translation editor page: languages and progress, lines and strings,
  filters, autosave, approve/outdated/fuzzy actions, glossary highlighting,
  "play from here" with the scene restored
- player: ?lang= and ?at= from the editor, per-locale font

(models.py and permissions.py for this landed earlier in 9ee3e1f)

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

70 lines
3.2 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_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)