Как я перевел японскую игру за день с помощью своей GPU и Codex

Я давно хотел попробовать перевести старую японскую VN/адвенчуру не “ручным героизмом на полгода”, а нормальным инженерным пайплайном: вытащить текст, прогнать через локальную LLM на своей видеокарте, проверить байтовые лимиты, починить команды движка и собрать патч.

В итоге за день я получил рабочий английский патч для Luv Wave. Не идеальный литературный перевод уровня издателя, но полностью проходимый быстрый фанатский перевод, который запускается на оригинальных файлах игры.
Что за игра Luv Wave

Luv Wave — японская адвенчура / visual novel от C’s Ware, вышедшая на Windows в 1998 году. Это та самая эпоха, когда японские VN уже переезжали с PC-98 в Windows 95/98, но внутри всё ещё жили старые кодировки, странные движки, fixed-size строки и форматные ограничения из прошлого.

По жанру это не школьная романтика, а довольно мрачный киберпанк-триллер. Действие происходит в будущем, после крупного инцидента с компьютерным вирусом XINN, который подчинил себе подключенные к сети машины. Мир игры — это спецслужбы, корпоративные структуры, убийства, кибернетика, секретные операции и ощущение поздних 90-х, когда “сеть” ещё воспринималась почти как мистическая угроза.

Главный герой, Микогами Каору, связан с тайными операциями и заказными убийствами. Уже в начале игра бросает его в историю с покушением, VIP-комнатами, кодами доступа, подозрительными клиентами и людьми, которые могут быть не совсем теми, кем кажутся. Внутри много типичного для того времени техно-нуара: пароли, терминалы, вирусы, спецотделы, цифровые следы.

Да там именно так подается текст. Но он достаточно читабелен!
Да там именно так подается текст. Но он достаточно читабелен!

В чем была проблема

Игра старая, японская, под Windows, с текстом в CP932/Shift-JIS. Просто заменить строки нельзя: многие записи должны оставаться ровно того же размера в байтах. Если перевод длиннее оригинала, можно сломать структуру файла.

Плюс в тексте есть команды движка вроде:

@Pause
@CLW:SetBS(15)
@If(...)

Их нельзя переводить, переставлять или терять.

Отдельная боль — пункты выбора. Оказалось, что обычные UI-строки типа Load Data могут спокойно иметь пробелы, а сюжетные варианты выбора внутри скрипта парсятся как командные токены. Поэтому Let's do it now ломало игру, а Lets_do_it_now работало.

Что делал Codex

Codex был не “переводчиком”, а напарником-инженером:

  • разобрал формат GDE/Entis;
  • написал сканер CP932-строк;
  • сделал экспорт JSONL-батчей по 200 строк;
  • помог прогонять локальную Gemma через OpenAI-compatible API;
  • проверял, что перевод влезает в оригинальный размер;
  • ловил сломанные @xx команды;
  • собирал патч обратно;
  • сделал Rust UI/CLI;
  • потом вынес всё в отдельный универсальный тул OldVNKeeper.

То есть вместо “переведи мне файл” получился нормальный цикл:

scan -> export batches -> local LLM -> import -> validate -> build patch

Локальная LLM на своей GPU

Основной быстрый проход шел через локальную Gemma на моей видеокарте. Это важно: не надо отправлять тысячи строк в облако, можно гонять всё у себя.

Модель получала строки в JSONL с ограничениями:

{"source":" японская строка ","maxCp932Bytes":70,"commands":["@Pause"],"translation":""}

Задача модели была простая: заполнить только translation, не трогать команды, уложиться в байты.

После этого импорт проверял каждую строку. Если перевод слишком длинный, пустой, не CP932 или сломал команды, он не попадал в финальный патч.

Самое смешное: пробелы в меню

В какой-то момент игра начала показывать ошибку:

Bad choice item.

На экране были варианты выбора:

Let's do it now I'm nervous

Выглядело нормально, но движок считал это не одной командой, а несколькими значениями. Решение:

Lets_do_it_now Im_nervous

При этом Load Data в главном меню работал с пробелом. Почему? Потому что это другой тип строки: engine UI label, а не tab-prefixed story choice. Разница была прямо в байтах.

Итог

За день получилось:

  • вытащить весь текст;
  • сделать быстрый LLM-проход;
  • вручную добить проблемные строки;
  • собрать рабочий luvwave.en.gde;
  • сделать патчер;
  • выпустить релиз;
  • создать отдельный универсальный Rust-инструмент OldVNKeeper.

Финальный отчет патчера был примерно такой:

Translation candidate records: 18072
Fitted translations: 18072
Overlong translations: 0
Missing translations: 0
Commands changed: 0

Почему это круто

Мне кажется, вот это и есть правильное применение AI в фанатских переводах старых игр. Не “нейронка магически делает всё”, а связка:

человек + локальная модель + строгая валидация + инструменты

LLM быстро дает черновик. Codex помогает построить пайплайн и чинить форматные проблемы. Человек принимает решения, проверяет смысл, чинит странные места и не дает движку развалиться.

Старые японские игры очень часто недоступны западной аудитории не потому, что их невозможно перевести, а потому что вокруг перевода слишком много скучной технической работы. Теперь эту работу можно сильно ускорить.

И да, весь патч работает от пользовательских оригинальных файлов. Никаких игровых ресурсов в релизе, только инструменты, кеш переводов и сборщик патча.

Какую игру вы бы хотели перевести? Кстати недавним ранее я сделал автоматический переводчик в реальном времени для новелл с PC98, но это уже другая история

9