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

+7 -2
View File
@@ -45,7 +45,10 @@ export async function launch(root: HTMLElement, versionId: number, onExit: () =>
const manifest = await getJSON<Manifest>(`/api/versions/${versionId}/manifest`);
const game = await getJSON<GameData>(blob(manifest, "game.json"));
onProgress(`Загрузка скриптов (${game.scripts.length})`);
const lang = preferredLanguage(game);
// ?lang= and ?at= come from the translation editor ("play from here")
const url = new URLSearchParams(location.search);
const asked = url.get("lang");
const lang = asked && (asked === game.sourceLang || game.locales?.[asked]) ? asked : preferredLanguage(game);
const [modules, tr] = await Promise.all([
Promise.all(game.scripts.map((s) => getJSON<ScriptModule>(blob(manifest, s)))),
loadTranslator(game, manifest, lang),
@@ -53,6 +56,8 @@ export async function launch(root: HTMLElement, versionId: number, onExit: () =>
const engine = new Engine(root, game, manifest, modules, saveApi(versionId),
`pgvn:persistent:${versionId}`, onExit, tr,
(code) => loadTranslator(game, manifest, code));
engine.start();
const at = url.get("at");
if (at) engine.startAt(at);
else engine.start();
return engine;
}