загружаю

This commit is contained in:
2026-09-07 19:07:40 +03:00
commit 1485819556
16 changed files with 1526 additions and 0 deletions
+254
View File
@@ -0,0 +1,254 @@
# 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`, чтобы не подтягивался старый перевод из кэша.