
新聞 / ONI Games
我們如何用一支 Python 腳本做出 16 種語言、300 頁的遊戲網站
不用 CMS,也不用框架:一個 Python 檔案產生 onirealms.com 每一頁的 16 種語言版本,再由小小的 Cloudflare Worker 提供服務。
我們為 Android 製作免費遊戲:兩款放置型 RPG、一款方塊益智遊戲,以及一款機器人工廠經營遊戲。每款遊戲都需要自己的頁面,而每個頁面都必須提供遊戲支援的所有語言。總共 16 個版本:英文、德文、西班牙文(西班牙與拉丁美洲)、法文、克羅埃西亞文、印尼文、義大利文、日文、韓文、葡萄牙文(巴西與葡萄牙)、俄文、斯洛維尼亞文、土耳其文和繁體中文。
對一個會隨遊戲更新而改變的網站來說,CMS 太過笨重。所以整個 onirealms.com 都由一支 Python 腳本產生純靜態檔案,前面再放一個小小的 Cloudflare Worker。
整體架構
腳本 site.py 是一個約 3,200 行的單一檔案。裡面有一個 GAMES 字典,每款遊戲一筆資料:套件名稱、主題、主視覺、區塊之間的美術橫幅、類型、年齡分級,以及遊戲是否已上架。其他所有內容都由此推導而來。
文字放在依語言區分的一般 Python 字典裡,每個區域一個模組:首頁、各款遊戲、常見問題、SEO 標題與描述、攻略和新聞。遊戲頁面就是一個函式,傳入遊戲和語言,回傳 HTML。四款遊戲乘以 16 種語言,再加上首頁、新聞、攻略、賽博格圖鑑、隱私權政策和媒體資料包,總共超過 300 頁。完整建置大約需要 26 秒。

最後發現,有三件事比產生器本身更重要。
1. hreflang 必須完整,否則毫無作用
每個頁面都會連到自己所有的語言版本(包含自己),再加上 x-default。網站地圖也重複同樣的群組。只要少一個,搜尋引擎就會把這些頁面當成互不相關、幾乎重複的頁面。腳本用產生頁面時同一份語言清單來建立這些連結,所以兩者不可能對不上。
2. 語言重新導向不能困住爬蟲
Worker 會把進到英文頁面的訪客導向同一頁面的瀏覽器語言版本。這對玩家很方便,但若做得太簡單,對搜尋就是災難,因為爬蟲只會看到重新導向。所以機器人不導向、從本站其他頁面過來的訪客不導向(他們是刻意選了英文)、網址帶有 ?lang= 時不導向,而且不使用 Cookie。這是帶有 Vary: Accept-Language 的 302,絕不是 301。
3. 不可變的資源需要新檔名
/assets/ 底下的一切都以 Cache-Control: max-age=31536000, immutable 提供。這是正確的設定,直到你換了一張圖,結果一整年都沒人看到。所以美術腳本每出一個新版本就寫入新檔名,而不是覆蓋舊檔。

小而實用的細節
- 每個遊戲頁面上的 QR Code 都在建置時產生。它們指向 Worker 上的短網址
/go/<game>,再帶著來源標記轉到 Google Play,因此 Play Console 能依來源顯示安裝數。 - 依同意啟用的數據分析。標記一開始會拒絕所有類型的儲存(consent mode v2),直到訪客在 Cookie 橫幅中同意後才啟用。
- 每次部署後發送 IndexNow,讓 Bing 和 Yandex 當天就看到變更。
- 每種語言一個 RSS 訂閱源,讓閱讀器和彙整服務收到新聞。
- 以 ZIP 檔提供的媒體資料包,由腳本從遊戲美術打包而成。
重來一次會怎麼做
先寫好語言清單和文字表格,再寫 HTML,而不是反過來。把 16 種語言硬塞進原本只為英文設計的標記,是整個專案最耗時的部分:行長、日文、韓文和中文的備用字型,以及俄文的西里爾字母備用字型,每一項都得單獨處理一次。
我們的遊戲有 Idle Ronin: Cyber RPG、Idle Summoner: Pixel RPG、Block Brain 和 Idle AI Factory: Robot Tycoon,全部都能在 Google Play 免費下載。







