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>
This commit is contained in:
mrleo1nidandClaude Opus 5.5 committed 2026-10-25 03:56:19 +03:00
1 parent 4e047f3aa0
commit ac164d1782
20 files changed
+2039 -298

No files matched your search

+20
View File
@@ -76,6 +76,11 @@ class VersionOut(ORM):
progress: str
size_bytes: int
created_at: datetime
visibility: str = "published"
kind: str = "original"
# from the version's game.json (filled by the game detail endpoint)
source_lang: str | None = None
languages: list[dict[str, Any]] = []
class VersionDetailOut(VersionOut):
@@ -90,6 +95,7 @@ class GameOut(ORM):
engine: str
cover_hash: str | None
created_at: datetime
main_version_id: int | None = None
# version counts visible to the requesting user (filled by the list endpoint)
ready_versions: int = 0
pending_versions: int = 0
@@ -117,6 +123,20 @@ class VersionIn(BaseModel):
description: str = ""
class ForkIn(VersionIn):
kind: str = Field(default="mod", pattern="^(mod|translation)$")
class PublishIn(BaseModel):
make_main: bool = False
class HistoryOut(ORM):
id: int
note: str
created_at: datetime
class SaveIn(BaseModel):
title: str = ""
data: dict[str, Any]