# 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`, чтобы не подтягивался старый перевод из кэша.