
Новости / ONI Games
Как мы собрали игровой сайт на 300 страниц на 16 языках одним скриптом на Python
Без CMS и фреймворков: один файл на Python пишет каждую страницу onirealms.com на 16 языках, а небольшой Cloudflare Worker их отдаёт.
Мы делаем бесплатные игры для Android: две idle-RPG, блочную головоломку и тайкун с фабрикой роботов. Каждой игре нужна своя страница, и каждая страница должна существовать на всех языках, на которых в эти игры играют. Это 16 версий: английский, немецкий, испанский (Испания и Латинская Америка), французский, хорватский, индонезийский, итальянский, японский, корейский, португальский (Бразилия и Португалия), русский, словенский, турецкий и традиционный китайский.
CMS показалась нам слишком тяжёлой для сайта, который меняется, когда меняется игра. Поэтому весь onirealms.com пишет один скрипт на Python в виде обычных статических файлов, а перед ними стоит небольшой Cloudflare Worker.
Как это устроено
Скрипт site.py - это один файл примерно на 3 200 строк. В нём есть словарь GAMES с записью для каждой игры: имя пакета, тема, ключевой арт, художественные полосы между разделами, жанры, возрастные рейтинги и вышла ли игра. Всё остальное выводится отсюда.
Тексты лежат в обычных словарях Python по языкам, по модулю на раздел: главная, каждая игра, FAQ, SEO-заголовки и описания, гайды и новости. Страница игры - это функция, которая получает игру и язык и возвращает HTML. Четыре игры на 16 языках плюс главная, новости, гайды, справочник киборгов, политика конфиденциальности и пресс-кит - больше 300 страниц. Полная сборка занимает около 26 секунд.

В итоге три вещи оказались важнее самого генератора.
1. hreflang должен быть полным, иначе он бесполезен
Каждая страница ссылается на все свои языковые версии, включая саму себя, плюс x-default. Карта сайта повторяет те же группы. Если пропустить хотя бы одну, поисковики считают страницы несвязанными почти-дубликатами. Скрипт строит эти ссылки из того же списка языков, по которому пишет страницы, поэтому они не могут разойтись.
2. Языковые редиректы не должны запирать поисковых роботов
Worker отправляет посетителя, попавшего на английскую страницу, на ту же страницу на языке его браузера. Игрокам это удобно, а для поиска губительно, если сделать наивно: робот всегда видел бы только редирект. Поэтому ботов не перенаправляют, посетителя, пришедшего с другой страницы сайта (он выбрал английский намеренно), тоже, редирект отключается, если в адресе есть ?lang=, и cookie не используются. Это 302 с Vary: Accept-Language, никогда не 301.
3. Неизменяемым файлам нужны новые имена
Всё в /assets/ отдаётся с Cache-Control: max-age=31536000, immutable. Это правильная настройка - до тех пор, пока вы не поменяете картинку и её никто не увидит целый год. Поэтому скрипты для графики записывают каждую новую версию под новым именем файла, а не перезаписывают старую.

Мелочи, которые окупаются
- QR-коды на каждой странице игры, созданные при сборке. Они ведут на короткую ссылку
/go/<game>в Worker, которая перенаправляет в Google Play с меткой источника, и Play Console показывает установки по источникам. - Аналитика с согласием. Тег стартует со всеми видами хранения данных запрещёнными (consent mode v2) и включается только после того, как посетитель согласится в баннере cookie.
- IndexNow после каждого деплоя, чтобы Bing и Яндекс видели изменения в тот же день.
- RSS-лента для каждого языка, чтобы читалки и агрегаторы подхватывали новости.
- Пресс-киты в ZIP-архивах, которые собирает скрипт из игровой графики.
Что бы мы сделали иначе
Писать список языков и таблицы текстов до HTML, а не после. Встраивать 16 языков в разметку, рассчитанную на английский, оказалось самой медленной частью всего проекта: длина строк, запасные шрифты для японского, корейского и китайского и запасной кириллический шрифт для русского потребовали отдельного прохода каждый.
Наши игры: Idle Ronin: Cyber RPG, Idle Summoner: Pixel RPG, Block Brain и Idle AI Factory: Robot Tycoon, все бесплатно в Google Play.







