Localization stage 1: stable IDs, import of tl/ translations, language picker

- every script node gets a permanent id, say nodes a Ren'Py-style tid
- all languages from the game's tl/ are imported at conversion: dialogue blocks
  matched by their source-location comment or by label + original text,
  `translate strings` become a per-language strings table
- source language from config.language or detected from the dialogue
- manifest stores locales/<lang>/<module>.json and strings.json; game.json
  lists sourceLang and available locales with progress
- player: language picker in main menu and settings, fallback to source,
  __(), [var!t] and _preferences.language
- parser: `default` with init priority, and one bad statement no longer drops
  the whole file
- docs/LOCALIZATION.md: design for translations, publishing and mods

Line endings of several converter/player files go back to LF: an earlier
commit accidentally stored them as CRLF.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mrleo1nidandClaude Opus 5.5 committed 2026-10-25 03:39:40 +03:00
1 parent 5d9ddb6376
commit e1d7b29f77
16 files changed
+4280 -3530

No files matched your search

+8 -2
View File
@@ -1,5 +1,6 @@
import "./player.css";
import { Engine, type SaveApi } from "./engine";
import { loadTranslator, preferredLanguage } from "./i18n";
import type { GameData, Manifest, SaveData, ScriptModule } from "./types";
async function getJSON<T>(url: string): Promise<T> {
@@ -44,9 +45,14 @@ 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 modules = await Promise.all(game.scripts.map((s) => getJSON<ScriptModule>(blob(manifest, s))));
const lang = preferredLanguage(game);
const [modules, tr] = await Promise.all([
Promise.all(game.scripts.map((s) => getJSON<ScriptModule>(blob(manifest, s)))),
loadTranslator(game, manifest, lang),
]);
const engine = new Engine(root, game, manifest, modules, saveApi(versionId),
`pgvn:persistent:${versionId}`, onExit);
`pgvn:persistent:${versionId}`, onExit, tr,
(code) => loadTranslator(game, manifest, code));
engine.start();
return engine;
}