Files
2026-09-07 19:07:40 +03:00

255 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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](https://pypi.org/project/nsv/) (исходники: [nsv-format/nsv-python](https://github.com/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](https://www.python.org/downloads/) (при установке на 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](https://github.com/nsv-format/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.
Пример содержимого:
```csv
Original,Translated
Michiru,Мичиру
Sachi,Сачи
Fan|A,Фанат|А
```
Строки с пустым `Translated` игнорируются — можно переводить имена постепенно.
### 5. Перевод текста через LLM
Перед первым запуском настройте `config.json`:
```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` — впишите имена персонажей, термины и заметки по стилю перевода. Формат:
```markdown
## 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
Простой текстовый формат ([спецификация](https://github.com/nsv-format/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`, чтобы не подтягивался старый перевод из кэша.