Начнём с вопроса, который звучит слегка глупо:

Какие части строки игрового текста вообще предназначены для чтения человеком?

Возьмём такую строку:

Welcome back, {player_name}!\nHP: %03d

Человек видит «Welcome back» и «HP».

Игра, разумеется, видит кое-что посложнее:

  • {player_name} — переменная. Во время игры она должна замениться именем игрока. Иначе всех игроков будут звать одинаково: {Player Name}.
  • \n — перенос строки, а не буква n. Пожалуйста, не делайте вид, будто не заметили перед ней обратную косую черту…
  • %03d — числовой плейсхолдер. Это не значит «три процента D», потому что игрок понятия не имеет, почему D должно составлять три процента от чего бы то ни было.

Всё это запихнуто в одну строку и выглядит как набор символов. Но по сути это совершенно разные виды содержимого.

Для переводчика всё это выглядит как одна строка текста.

Для игрового движка — маленький контракт со строгими условиями: одни части разрешено менять, а из-за одного пропущенного символа в других игра может рухнуть.

А потом мы целиком отправляем эту строку модели ИИ и очень серьёзно предупреждаем:

Переводи только текст, который видит человек. Всё остальное не трогай.

Модель вежливо соглашается: «Конечно, всё поняла».

Что именно она поняла и поняла ли вообще — вопрос отдельный.

01 ИИ не пытается ничего саботировать. Он просто не знает, что ему можно трогать

ИИ может распознать, что {player_name} похож на переменную, но вашу игру он на самом деле не запускал. Он не знает, что произойдёт, если исчезнет одна скобка. Модель по-прежнему воспринимает переменную как часть входного текста — а значит, может её переписать.

Поэтому каждый день происходят примерно такие вещи:

{player_name}  →  {Player Name}
%03d           →  %3d
\n             →  \\n
{b}...{/b}     →  {b}...{b}

Иногда модель переводит имя переменной. Иногда заботливо приводит в порядок пробелы и пунктуацию. А иногда решает, что два странных тега наверняка дублируют друг друга, — и с большим энтузиазмом удаляет один из них.

Даже если сказать ИИ, какой движок вы используете, он действительно может распознать большинство переменных и тегов. Одно дело — распознать правило. Совсем другое — безошибочно соблюдать его на следующих сорока тысячах строк.

Когда контекст становится достаточно длинным, давнее предупреждение «это не трогать» отправляется пылиться куда-то на задворки контекста. К тому же разработчики неизбежно добавляют собственные теги и необычный синтаксис. Вполне естественно, что ИИ раньше их никогда не видел.

В отличие от традиционной системы машинного перевода, ИИ ещё и «живой». Он может начать рассуждать самостоятельно:

«Этот пробел выглядит лишним. Удалить!»

«Так, разве эти два тега не одинаковые? Удалить!»

«Это точно имя переменной? Неважно. Переведу».

Так что да: правило он может знать. Ошибки всё равно просачиваются.

По отдельности ни одно из этих изменений не выглядит особенно драматично.

Но после возврата текста в игровой движок последствия могут включать, помимо прочего:

  • Имя игрока, бесследно растворившееся в воздухе.
  • Формат числа, превратившийся в полную бессмыслицу.
  • Буквальные символы \n прямо в интерфейсе.
  • Предложение, которое на полной скорости пробивает край диалогового окна и исчезает за горизонтом.
  • Несколько следующих тысяч слов, внезапно ставших жирными или красными.
  • Или самый прямолинейный исход: игра выдаёт ошибку и отказывается запускаться.

Причём такая путаница бывает не только у ИИ.

Глубокой ночью, где-нибудь на трёхтысячной строке, человеческая префронтальная кора тоже вполне способна штатно отключиться — и рука тут же дрогнет.

Инструмент перевода тоже может всё сломать, если неправильно обрабатывает escape-последовательности.

Когда дело касается рабочей памяти, человеческий мозг ненамного надёжнее большой языковой модели. ИИ галлюцинирует. Люди тоже — причём иногда с поразительной уверенностью в собственной правоте.

Ещё веселее становится, когда над одним переводом работают несколько человек. Фразы «А я думал, мы договорились делать вот так» и «О нет, я пропустил скобку» возникают с вероятностью примерно 100%.

Ещё до того, как я начал делать Goblin Forge, я работал над совместным проектом по переводу игры.

Однажды я потратил целый день, вручную проверяя больше 40 000 строк: не потерялась ли одна из скобок у маркера переноса и не превратился ли player_name снова в «Player Name».

Каждую строку я проверял с совершенно спокойным лицом, пока мозг орал:

Мы повторяли это бесчисленное количество раз. Как здесь до сих пор может быть столько пропусков?

Тогда я всё сильнее убеждался: «защиту кода» нельзя доверять ни ИИ, ни людям. После импорта текст проходит через редактирование, перевод, пакетную замену и экспорт — и повредить его можно на любом этапе.

Если никто из нас не отличается особой надёжностью, нужен другой подход.

02 Помогут ли дополнительные предупреждения в промпте?

Да, но недостаточно.

В промпте вполне можно написать:

Сохраняй все переменные, плейсхолдеры, теги и escape-последовательности без изменений.

И вероятность ошибки действительно снизится.

Проблема в том, что «вероятность ниже» не так полезна, как кажется человеку, когда фундамент игры настолько хрупок. Рано или поздно неучтённые факторы заставят модель выдать новый вариант — и она снова взорвёт фундамент, ничего вам не сказав.

Меняется модель — меняется вероятность ошибки.

Предложение стало длиннее — вероятность снова изменилась.

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

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

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

Когда ошибки настолько хаотичны, человек даже не понимает, с какого места начинать поиски.

03 Главная защита от взрыва: сделать код доступным только для чтения

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

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

В строке игрового текста редактировать на самом деле нужно только то, что предназначено для людей.

Переменные, плейсхолдеры, теги и переносы строк следует распознавать отдельно и защищать по правилам проекта.

Просто изменить их цвет недостаточно. Это всего лишь визуальное напоминание — причём в основном для человека, а не для ИИ.

Настоящая защита означает, что эти части доступны только для чтения.

Их можно видеть и перемещать целиком, но нельзя вскрыть и случайно изменить отдельный символ внутри конструкции.

Во время перевода они остаются ровно там, где должны быть. Люди редактируют человеческий язык. ИИ разрешено обрабатывать только человеческий язык.

В конце нужно ещё раз сравнить структуру исходника и перевода — как сверяют список деталей перед отправкой: всё необходимое на месте и у каждой детали сохранилось прежнее имя.

04 Но инструменты тоже ошибаются

У разных игровых движков разный синтаксис. Добавьте плагины и странные мини-языки, придуманные отдельными разработчиками, — и хаос наступит очень быстро.

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

Слишком широкое правило заблокирует текст, который нужно было перевести. Слишком узкое пропустит настоящий код.

Безопаснее сначала взять несколько десятков строк со всеми разновидностями странных символов и проверить их. Если всё выглядит правильно, можно переходить к проекту на тысячи или десятки тысяч строк.

Пожалуйста, сопротивляйтесь желанию с первой попытки скормить инструменту весь проект.

Если правило неверное, инструмент не обязательно пожалуется. Он может уверенно перевести для вас все сорок тысяч строк.

Когда придётся переделывать всё заново, страдания целиком достанутся вам. Откуда я это знаю — лучше не спрашивайте.

05 Каким бы инструментом вы ни пользовались, соблюдайте эти правила выживания

Что бы вы в итоге ни выбрали — таблицу, скрипт, чат-бота или продвинутый CAT-инструмент, — помните одно:

Не доверяйте своей памяти.

  • Всегда сначала делайте резервную копию. Если, конечно, вы не мечтаете провести ночь, обливаясь холодным потом перед программой для восстановления данных.
  • Перечислите всё, что нельзя трогать: переменные, плейсхолдеры, теги, escape-последовательности, команды скрипта и не только.
  • Сначала переведите небольшой образец — лучше всего 50–100 строк со всеми странными символами, которые сможете найти.
  • Сравните структуру исходника и перевода. Проверьте количество элементов, имена и парные конструкции.
  • Только после этого запускайте пакетную обработку всего проекта. Пожалуйста, не меняйте порядок, если только вы не считаете многократную переустановку игры развлечением.
  • И наконец, проверьте всё в самой игре.

Многие считают: если файл успешно записан и игра запускается, работа закончена.

В действительности картина обычно выглядит примерно так:

Все плейсхолдеры на месте. Игрок открывает диалоговое окно — и текст тут же вылетает за его пределы и пробивает атмосферу планеты.

Суровый мужчина средних лет открывает рот и начинает говорить как жеманная девочка-подросток.

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

Никогда не считайте «перевод закончен» и «тестирование закончено» одним и тем же.

Игру всё равно придётся открыть и посмотреть — скорее всего, не один раз. Иначе сэкономленное сейчас время вернётся с процентами, когда начнётся переделка.

06 Поэтому я встроил этот физический щит в Goblin Forge

После того как и ИИ, и человеческая рабочая память несколько раз размазали меня по полу, именно этой проблемой я занялся в Goblin Forge в первую очередь.

После импорта файла система автоматически распознаёт переменные, теги, плейсхолдеры и escape-последовательности, а затем упаковывает их в доступные только для чтения «капсулы кода».

В редакторе перевода каждая капсула — единый, неделимый объект. Можно увидеть исходные символы или переключить отображение и посмотреть, что они означают.

Целую капсулу можно переместить, добавить или удалить, но нельзя вскрыть и изменить отдельный символ внутри — если только вы намеренно не измените стандартные настройки и шаблоны.

Так физически предотвращаются ситуации вроде удаления одной скобки из пары или потери части тега.

Когда перевод завершён, Goblin Forge снова сравнивает структуру исходника и перевода — словно сверяет список деталей.

Если элемент пропал, продублировался или нарушил ожидаемую структуру, соответствующая строка помечается, чтобы при редактировании и проверке было понятно, куда смотреть.

Разумеется, у каждой игры есть свои особенности. Помимо встроенных распространённых форматов и шаблонов движков, для отдельного проекта можно добавить собственные правила защиты.

При первом использовании я всё равно советую проверить небольшой фрагмент настоящего текста и убедиться, что правила ничего не пропустили.

Распознавание при импорте, блокировка во время перевода и последующая проверка структуры связаны внутри Goblin Forge в единый процесс.

Я сделал всё это потому, что больше никогда не хочу тратить целый день на поиски пропавшей скобки в сорока тысячах строк текста.