Files
cs2-easy-translate-tool/README.md
T
2026-09-07 19:01:26 +03:00

17 KiB
Raw Blame History

translate_util

Набор скриптов для перевода визуальной новеллы на движке CatSystem2.

Полный путь: игра → распаковка архивов → декомпиляция скриптов → перевод → сборка обратно → компиляция для игры.

Требования

  • Python 3.9+
  • Windows (нужны .exe-утилиты в папке tool/)
  • Ключ доступа к любому OpenAI-совместимому API (OpenAI, OpenRouter, локальный сервер и т.д.)

Python-зависимости

Всё, кроме одного пакета, — стандартная библиотека Python (json, re, csv, pathlib, subprocess, urllib, logging и т.д.), ничего дополнительно ставить не нужно.

Единственная внешняя зависимость — официальная реализация формата NSV:

pip install nsv

Это готовый пакет с PyPI (исходники: nsv-format/nsv-python), под Windows ставится как готовый wheel — компилятор не нужен. Без него convert.py, names.py и translate.py не запустятся (упадут с ModuleNotFoundError: No module named 'nsv').

Наш файл nsv_io.py — тонкая обёртка над этим пакетом: сам он не парсит и не экранирует NSV, а только переводит между "строка формата NSV" и "запись перевода с полями line/speaker/pcm/text/translated_text", специфичными для этого проекта. nsv_io.py должен лежать рядом с convert.py, names.py, translate.py — они делают from nsv_io import ....

Проверить, что Python установлен и виден из консоли:

python --version

Если команда не найдена — установите Python с python.org (при установке на Windows отметьте галочку "Add python.exe to PATH").

Сторонние утилиты (не Python-пакеты)

Эти .exe — не часть репозитория, их нужно положить в папку tool/ самостоятельно (взять из готового тулкита для CatSystem2 или найти по названию):

Файл Для чего Используется в
exkifint_v3.exe распаковка .int-архивов игры extract.py
cs2_decompile.exe декомпиляция .cst.txt decompile.py
mc.exe компиляция .txt.cst обратно compile.py

Без них соответствующие шаги пайплайна работать не будут — остальные шаги (Txt to NSV, Names, Перевод, NSV to Txt) от них не зависят и работают на чистом Python.

Структура папок

translate_util/
├── start.py            запускать отсюда
├── extract.py
├── decompile.py
├── convert.py
├── names.py
├── translate.py
├── compile.py
├── nsv_io.py            обёртка над пакетом nsv под схему записей проекта
├── config.json          настройки API и перевода
├── glossary.md          глоссарий (имена, термины, заметки по стилю)
├── prompt.txt           системный промпт для LLM
├── namestable.csv        создаётся скриптом names.py
├── tool/                 exkifint_v3.exe, cs2_decompile.exe, mc.exe
├── txt/                  декомпилированные .txt (шаг 2)
├── json/                 .nsv файлы для перевода (шаг 3-5)
├── txt_translated/       собранные переведённые .txt (шаг 6)
├── update00/, update01/  оригинальные и переведённые архивы игры
└── _translate_data/      кэш переводов и логи (создаётся автоматически)

Быстрый старт

python start.py

Покажет меню на 7 пунктов — можно проходить шаги по порядку, каждый раз возвращаясь в меню.

1. Extract     — достать файлы из архива игры (.int → .cst/.cstl)
2. Decompile   — .cst → .txt
3. Txt to NSV  — .txt → .nsv (подготовка к переводу)
4. Names       — собрать/применить перевод имён персонажей
5. Перевод     — прогнать текст через LLM
6. NSV to Txt  — .nsv → .txt (сборка перевода обратно)
7. Compile     — .txt → .cst (готово для игры)

Каждый скрипт можно запускать и напрямую (python decompile.py, python translate.py и т.д.) — меню лишь избавляет от необходимости помнить порядок и имена файлов.

Шаг за шагом

1. Extract — достать файлы из игры

python extract.py

Спросит путь к папке с игрой и .exe игры (для определения версии движка). Извлекает .cst (тексты) и .cstl (уже локализованные, если есть) файлы из всех архивов (scene.int, fes.int, config.int, update00.int...update19.int), учитывая, что апдейты перезаписывают базовые файлы. Результат кладётся в папку, которую вы укажете (по умолчанию extracted_texts/text и extracted_texts/localized_text).

2. Decompile — .cst → .txt

python decompile.py

Спросит папку с .cst (по умолчанию update00). Декомпилирует все файлы через tool/cs2_decompile.exe в папку txt (кодировка Shift-JIS/CP932).

3. Txt to NSV — подготовка к переводу

python convert.py --txt2nsv

Спросит папку с .txt (по умолчанию txt) и папку для результата (по умолчанию json). Разбирает каждый .txt, находит строки с текстом (отличает их от игровых команд), и сохраняет в компактном текстовом формате .nsv — по одной записи на реплику, с полями: номер строки, спикер, id войсовера (pcm), оригинальный текст, перевод (изначально пустой).

.nsv — обычный текстовый файл, его можно открыть и посмотреть в любом редакторе. Сам формат — спецификация NSV, парсится пакетом nsv; маппинг на конкретные поля (line/speaker/pcm/text/translated_text) описан в nsv_io.py.

4. Names — перевод имён персонажей

python names.py

Меню на 2 пункта:

  • 1 — собрать имена: проходит по всем .nsv в указанной папке, собирает все уникальные значения поля "спикер" и складывает их в namestable.csv (в корне translate_util) с пустой колонкой Translated.
  • 2 — применить перевод: читает namestable.csv, и для каждой строки, где колонка Translated заполнена, заменяет спикера во всех .nsv файлах.

Откройте namestable.csv (Excel, Google Таблицы, любой редактор с поддержкой UTF-8) и заполните колонку Translated для нужных имён, затем запустите пункт 2.

Пример содержимого:

Original,Translated
Michiru,Мичиру
Sachi,Сачи
Fan|A,Фанат|А

Строки с пустым Translated игнорируются — можно переводить имена постепенно.

5. Перевод текста через LLM

Перед первым запуском настройте config.json:

{
  "api": {
    "base_url": "https://api.openai.com/v1",
    "api_key": "sk-ваш-ключ",
    "model": "gpt-4o",
    "temperature": 0.3,
    "max_tokens": 4092,
    "timeout_seconds": 120
  },
  "translation": {
    "source_language": "English",
    "target_language": "Russian",
    "batch_size": 10,
    "context_before": 2,
    "context_after": 2,
    "max_retries": 3,
    "retry_delay_seconds": 5
  },
  "paths": {
    "json_dir": "json",
    "glossary_file": "glossary.md"
  }
}
Параметр Что значит
base_url адрес API — для OpenAI, OpenRouter или локального сервера (LM Studio, Ollama и т.д.)
api_key ваш ключ
model название модели у выбранного провайдера
temperature ниже — переводит более предсказуемо и буквально, выше — вольнее
max_tokens лимит длины ответа на один запрос
reasoning_effort необязательное поле, если модель поддерживает режимы рассуждения (например "low"/"medium"/"high")
batch_size сколько реплик отправляется в LLM за один запрос
context_before / context_after сколько соседних реплик показывать модели для контекста (до — уже переведённые, после — оригинал)
max_retries / retry_delay_seconds сколько раз и с какой паузой повторять запрос при сбое

Отредактируйте glossary.md — впишите имена персонажей, термины и заметки по стилю перевода. Формат:

## Characters
- Michiru: Мичиру

## Terms
- Idol: Айдол

## Notes
- Michiru обращается к другим неформально.

Запуск:

python translate.py

Спросит, какие .nsv файлы переводить — имена файлов через запятую (без расширения) или all для всех. Дальше работает автоматически:

  • бьёт текст на пачки и отправляет в LLM вместе с глоссарием и контекстом соседних реплик;
  • пишет перевод сразу в файл после каждой пачки — можно прервать (Ctrl+C) и продолжить позже, ничего не потеряется;
  • строки, у которых перевод уже есть, пропускаются — safe перезапускать сколько угодно раз;
  • при сбое или странном ответе модели — повторяет запрос; если не получилось после всех попыток — идёт дальше, а в конце выводит список проблемных мест для повторного прогона;
  • одинаковый исходный текст переводится один раз и берётся из кэша при повторных встречах (файл _translate_data/translation_cache.json) — экономит и деньги, и время.

Логи каждого запуска — в _translate_data/translate_logs/, сырые запросы/ответы API — в _translate_data/raw_api_logs/ (полезно для отладки, если модель ведёт себя странно).

Если после прогона остались проблемные пачки — просто запустите python translate.py ещё раз для тех же файлов, уже переведённое не тронется.

6. NSV to Txt — сборка перевода обратно

python convert.py --nsv2txt

Спросит:

  • папку с оригинальными .txt (по умолчанию txt) — используется как "скелет", все игровые команды и структура берутся отсюда;
  • папку с переведёнными .nsv (по умолчанию json);
  • папку для результата (по умолчанию txt_translated);
  • какое поле использовать — text (оригинал, для проверки, что ничего не сломалось) или translated_text (реальный перевод, стандартный выбор).

Соберёт .txt-файлы, готовые к компиляции, подставив перевод только в текстовые строки и не тронув остальное.

7. Compile — .txt → .cst

python compile.py

Спросит папку с переведёнными .txt (по умолчанию txt_translated) и папку для результата (по умолчанию update01). Компилирует всё через tool/mc.exe.

После этого update01 можно класть обратно в игру (или собирать в архив — зависит от того, как игра подхватывает патчи).

Формат .nsv

Простой текстовый формат (спецификация, реализация — пакет nsv): одна запись — 5 строк подряд (номер, спикер, pcm, оригинал, перевод), затем пустая строка-разделитель. Пустое значение поля — одиночный \. Внутри текста \ экранируется как \\, настоящий перенос строки — как \n.

Файл можно открыть в любом текстовом редакторе. LLM никогда не видит это экранирование напрямую — при отправке в модель текст уже "распакован" в обычный читаемый вид, а при сохранении на диск запаковывается заново автоматически.

Частые проблемы

translate.py падает с ошибкой про кодировку/JSON при чтении файла — убедитесь, что используете .nsv файлы, созданные текущей версией convert.py --txt2nsv, а не старые .json от прежних версий скриптов.

В игре после компиляции вместо буквы стоит ? — где-то в переводе оказался символ, не входящий в Shift-JIS/CP932 (например длинное тире , , эмодзи). convert.py при сборке (--nsv2txt) старается автоматически заменить самые частые проблемные символы, но если что-то новое проскочило — compile.py/convert.py покажут ошибку кодировки в консоли, укажут на файл, надо будет поправить перевод вручную в .nsv.

Перевод потерял [слова] [в] [скобках] или \-теги — это значит модель их не сохранила, несмотря на инструкцию в prompt.txt. Найдите строку в .nsv, поправьте вручную или сотрите translated_text у этой записи и запустите translate.py заново — тогда LLM переведёт её ещё раз.

Хочу начать перевод заново с нуля — очистите поле translated_text в нужных .nsv файлах (или пересоздайте их через convert.py --txt2nsv), и при желании удалите _translate_data/translation_cache.json, чтобы не подтягивался старый перевод из кэша.