Игра «Мокша» была доступна только на хинди. В README прямо было написано: «For now its only for hindi users who deeply understand hindu philosophy». Но это ограничение, а не фича. Игроки, которые не читают на хинди, не могли прикоснуться к философии, заложенной в механике: прарабдха, вайрагья, самарпан. Добавление английского языка превратилось в целый квест — с архитектурными решениями, десятком багов и неожиданными открытиями.
Правила перевода: что нельзя переводить
Самый первый вопрос был не техническим, а смысловым: что вообще переводить? Если перевести всё подряд, философия рассыплется. Поэтому установили три правила.
Шлоки не переводятся
Стихи на санскрите — не просто текст, они несут силу сами по себе. В английской версии шлока остаётся в деванагари, а внизу добавляется только пояснение. Пример:
नाम जपत मंगल दिसि दसहूँ॥
By chanting the Nāma, auspiciousness arises in all ten directions.
— Rāmacaritamānasa
Сначала переводчик написал «Auspiciousness in all ten directions» — и это неправильно. Пропал субъект действия: смысл в том, что именно от повторения имени наступает благость, а не просто «благость есть». Пришлось переделывать.
Термины — транслитерация, а не перевод
«Папа» — это не «бад карма». Это не моральное зло, а загрязнённость, долг. Если перевести «папа» как «грех», смысл исказится. Поэтому все ключевые понятия оставили в транслитерации — с пояснением в скобках.
| Хинди | Неправильный перевод | Правильно |
|---|---|---|
| पाप | Bad Karma | Pāpa |
| पुण्य | Good Karma | Puṇya |
| प्रारब्ध | Past-life karma | Prārabdha |
| कृपा | Grace | Kṛpā (grace) |
| ॐ | Om | ॐ (не переводится никогда) |
Слова «grace» недостаточно — оно несёт христианский подтекст.
Архитектура i18n: как устроить переключение языка без боли
Весь код разделили на три части.
Модуль i18n.js отвечает за языковые ключи. Он не зависит от DOM, поэтому его можно безопасно импортировать из движка и рендера. Методы простые: initLang() читает язык из localStorage, setLang() меняет и сохраняет, t(key, params) возвращает строку с подстановкой параметров.
Важная деталь — fallback chain: текущий язык → хинди → ключ. Если какого-то ключа нет, игра не падает, а показывает хинди. Английский режим остаётся читаемым.
Late binding — ключевое решение
В очереди оповещений игры хранятся не переведённые строки, а ключи. Перевод выполняется в момент отрисовки кадра. Это значит: если игрок переключит язык во время игры, старые уведомления тоже рендерятся на новом языке. Никакого смешения языков.
// В очереди алертов: { titleKey: 'alert.chetana.title', params: null, icon: '👁️' } // В render.js при каждом кадре: const { icon, title, subtitle } = resolveAlert(a);Побочный эффект: эмодзи теперь хранятся в коде, а не в строках. Поэтому слой, который раньше вырезал эмодзи из текста регулярками, стал не нужен.
Сакральные тексты — отдельно
Для текстов шактры создали отдельный модуль shastra.js. Эти тексты не похожи на UI-строки: они длинные, содержат разметку и видны только в оверлее, а не каждый кадр. Шлоки хранятся в словаре и подключаются по идентификатору — один раз написаны, используются в обеих языковых версиях.
| UI-строки | Шактра | |
|---|---|---|
| Формат | Короткие, разбросанные, с плейсхолдерами | Длинный связный текст с разметкой |
| Разметка | Отсутствует | <strong>, <em>, шлоки |
| Использование | Каждый кадр | Один раз при открытии оверлея |
Баги, которые нашлись благодаря локализации
Перевод вскрыл кучу давних проблем. Некоторые видны сразу, другие давно молчали.
Видимые баги
- laguage-toggle — опечатка в id: переключение языка просто не работало, а опциональная цепочка ?. молча глотала ошибку.
- last.Time = ... — после нажатия клавиши «R» весь обработчик клавиш умирал.
- _tutorialClickRead — туториал не закрывался по клику.
- info.alert вызывался дважды — при сборе каждой раковины появлялось два алерта, один пустой.
Молчаливые ошибки
Эти проявились бы только при локализации. aria-describedby="language-choicce-status" — два «c» в значении атрибута. Screen reader никогда не узнавал о смене языка. Опечатка в CSS: padding:10px, 28px — запятая делает всю декларацию недействительной. letter-spacing:0.5pxl — несуществующая единица. Тихие поломки.
Отдельная история — шрифт. Orbitron не содержит деванагари. Когда игрок видел хинди, браузер подставлял системный шрифт, из-за чего строки выглядели криво. Плюс CARD_H был фиксированным (290px). Английский текст длиннее хинди на 25%, и туториал вылезал за пределы карточки. Пришлось вычислять высоту по содержимому.
Философский баг
В index.html было написано «сарáтхи (ум)», а в коде спрайта — cachedBuddhiSprite. Код знал правильный ответ, а документация — нет. В Катхопанишаде (1.3.3) сказано: буддхи — возница, ум — вожжи. Исправили и добавили этот стих на страницу. Игра стала честнее.
Уроки, которые вынесем с собой
- Считайте ключи не строками, а фактом: grep -c считает строки, а не ключи. Пришлось написать скрипт, который считает вхождения по регулярке и находит несбалансированность.
- Синтаксическая проверка не спасает от ошибок имён: node --check прошёл, а ReferenceError появился только в рантайме.
- Самый лучший предохранитель — git diff | grep "^-". Именно так поймали два регресса, включая удалённую декларацию tutorialBtn.
- Заглядывайте в git status. Два новых файла — i18n.js и shastra.js — оказались untracked. Если бы закоммитили, все, кто клонирует репозиторий, получили бы сломанную игру, а на локальной машине всё работало бы.
Итоговые цифры
134 ключа на два языка, полностью сбалансированы. 40+ мест, где вызывались алерты, переведены на ключи. Два новых модуля. И 25+ дополнительных багов, многие из которых были невидимы для пользователя.
Что дальше
В планах — углубление философской части на обоих языках, унификация транслитерации IAST, решить, как отображать деванагари-цифры. И третья языковая версия: архитектура уже готова.
Локализация — это не перевод слов. Это перевод смысла. И, как показала «Мокша», она способна вскрыть такие ошибки, о которых не знал даже автор.
Комментарии (0)
Войдите, чтобы комментировать.
Пока нет комментариев. Будьте первым.