Solo Developer Builds 8-Language Game Site: A Practical Review of Python + Docker + Free LLM

A technical breakdown and boundary reflection on how a Korean indie developer used Python, Docker, and a free LLM API to bring 8-language support to a browser game site.
Yeoul Games is a browser game aggregation site run by a Korean indie developer, originally Korean-only and later expanded to eight languages. The technical approach has two paths: fixed UI copy is stored in a JSON translation table and replaced as whole sentences via an HTML response filter; game descriptions are auto-translated by calling Groq's free LLM API, with SHA256 fingerprint validation to keep translations in sync with source text. During development, free-tier tokens-per-minute limits caused 413/429 errors, solved by reducing request granularity, limiting max_tokens, and throttling with 70-second intervals, plus adding character-set validation to discard incomplete translations. The article also discusses the tiered translation strategy and its boundaries: translation coverage should align with what the product can actually deliver.
A Korean indie developer used Python, Docker, and a free LLM API to bring an 8-language experience to a personal browser game site. This article breaks down his technical approach, the pitfalls he hit, and the boundaries of automated translation in a real product.

From Korean-Only to Eight Languages: The Internationalization Motivation for a Small Site
Yeoul Games is a browser game aggregation site where you can play without registering or installing anything, run single-handedly by a Korean indie developer. The site started as a showcase page for his own games and a Korean-language text MUD, and later other Korean indie developers began listing their work there.
As traffic sources diversified, he expanded the interface to eight languages—Korean, English, Japanese, Portuguese, Spanish, Vietnamese, Turkish, and German—switchable via query parameters like ?lang=en. The entire service runs in Docker containers on a home Synology NAS, with a backend web server written in Python. This is a very cost-effective choice: no cloud bills and no complex ops chain, at the cost of limited resources—which directly caused failures in the translation pipeline later on.
Technical Approach Breakdown: Two Translation Paths
Fixed UI Text: JSON Translation Table + Response Filter
All UI strings live in a JSON table. Every HTML response passes through a filter that replaces Korean strings with the corresponding language's translation. One detail worth noting: when a sentence is split by HTML tags, the translation table's key preserves the complete sentence to ensure replacement is an exact match—rather than fragment-based replacement that would scramble word order.
This is a classic "static internationalization" approach: zero runtime cost and predictable results, but limited to UI copy and unable to handle user-generated content.
Response Filter and the HTML Tag-Splitting Problem
A response filter is a middleware mechanism in web frameworks: after the server generates HTML but before it is sent to the client, it performs unified processing on the response body. For internationalization, it iterates over the response text and replaces source-language strings registered in the translation table with target-language translations. The difficulty is that a natural-language sentence in HTML is often split into multiple segments by inline tags (such as <strong>, <a>). If you match against the fragments after tag splitting, the translation table would need entries for every segmentation combination—unmaintainable and prone to missed replacements. Using the complete sentence as the key and doing whole-sentence matching during the filter stage sidesteps this problem, but it requires that the HTML structure not break sentence continuity, and that replacement order avoids nested accidental replacements.
Game Descriptions: Automatic Translation via Free LLM API
When developers upload a game, they check the languages the game itself supports; but regardless of what they check, the description text is sent to Groq's free LLM API and translated into the other seven languages. This way, a Brazilian visitor can at least understand what a Korean game is about.
To ensure translations don't go stale, a background thread computes a fingerprint of the source field. The code uses hashlib.sha256 to hash the normalized JSON and takes the first 16 characters as the _src fingerprint. When rendering the page, a translation is only used if the stored fingerprint matches the current text. As soon as a developer edits the description, the page falls back to displaying the Korean original until the new translation arrives. This design solves an easily overlooked problem: a stale translation is more dangerous than no translation, because it describes a changed product with incorrect information.
How Groq and Free LLM APIs Work
Groq is a cloud platform providing LLM inference services, known for using its custom LPU (Language Processing Unit) hardware to accelerate inference, with a focus on low-latency generation. Developers can call open-source models hosted on it (such as the Llama series, Mixtral, etc.) through an OpenAI-compatible API. A so-called "free API" usually refers to the platform's free tier: you can call it at limited rates without binding a payment method, but tokens-per-minute and requests-per-minute limits apply. Once exceeded, the server returns status codes such as 429 (Too Many Requests) rather than silently degrading. For personal projects, this free tier is enough to support low-frequency, batch translation tasks, but you must design your own throttling and retry logic and cannot assume the API is always available.
Pitfall Log: The Real Cost of Free Quotas
The free tier's tokens-per-minute limit was the most direct obstacle in this development. The author started receiving HTTP 413 and 429 errors. His fix strategy had three steps: reduce the amount per request (process only one game's one language group at a time), limit max_tokens, and wait 70 seconds between requests (240 seconds when idle).
Even so, some translations came back incomplete—for example, a Japanese sentence with Korean characters mixed into the ending. He fixed these manually at first, then added an auto_ok validation function: if the translation contains Korean characters not present in the source text (matched via the regex [가-힣]+), the translation is discarded. This idea can be generalized to any translation QA scenario where "source-language characters should not appear in the target language."
According to the author's description in the original article, these limitations weren't solved all at once; instead, through a polling rhythm of "one game × one language group per round," requests were pushed below the free quota. This is a typical strategy of trading time for cost—suitable for personal projects, but not for latency-sensitive products.
The Difference Between HTTP 413 and 429
413 (Payload Too Large) means the request body exceeds the size the server allows; in LLM API scenarios, this is often related to text that is too long in a single request or a max_tokens setting that is too high. 429 (Too Many Requests) means the rate limit has been triggered, indicating that the number of requests or tokens per unit time exceeds the quota. The causes differ, and so do the fixes: 413 requires reducing the input or output size per request, while 429 requires lengthening the request interval or reducing concurrency. Conflating the two easily leads to ineffective debugging where "you only add waits but still get 413." Understanding the semantics of status codes is the first step in troubleshooting such issues.
Translation Boundaries: What Should and Shouldn't Be Translated
The author tiers content, and this judgment deserves separate discussion:
- Announcements and rules: Written by the author in Korean; non-Korean pages are machine-translated but always include a link to the Korean original, which remains authoritative.
- Other users' posts: Kept completely as-is, not translated.
- Game descriptions: Fully auto-translated.
- Game content itself: Not translated.
This tiering reflects a pragmatic product judgment: rule-type content needs accuracy, so the authoritative original is retained; user content involves the right to expression and should not be rewritten without permission; description-type content aims for "understandable," for which machine translation suffices.
Non-Korean pages also offer a "playable in my language" filter, based on the language support checked by developers. But since the vast majority of games are currently Korean-only, this filter actually returns very few results. This is an honest presentation of the status quo: the feature exists, but supply hasn't caught up.
Current Status and Open Questions
The site currently has 42 games, almost all from Korean indie developers, and the games themselves remain primarily in Korean. Translation covers only the interface and descriptions, and translation quality still has flaws in some scenarios.
The question the author himself raises is valuable: are auto-translated descriptions actually helping users, or are they setting the wrong expectations? If an English-speaking user reads a fluent English description, clicks in, and finds the game interface entirely in Korean, that gap may be more frustrating than seeing a Korean description. There is no standard answer to this question, but it points to a core contradiction in internationalized products—the boundaries of translation should align with the boundaries of what the product can actually deliver.
For other browser game developers, the reusable takeaways from this case are: use a translation table for static copy, use an LLM plus fingerprint validation for dynamic content, trade throttling for stability under free quotas, and use character-set validation as a quality backstop. This combination costs nearly zero and is suitable for the early stages of a personal project.
Site address: https://yeoulgame.com/?lang=en
One-line Summary
A technical breakdown and boundary reflection on how a Korean indie developer used Python, Docker, and a free LLM API to bring 8-language support to a browser game site.
Paragraph Summary
Yeoul Games is a browser game aggregation site run by a Korean indie developer, originally Korean-only and later expanded to eight languages. The technical approach has two paths: fixed UI copy is stored in a JSON translation table and replaced as whole sentences via an HTML response filter; game descriptions are auto-translated by calling Groq's free LLM API, with SHA256 fingerprint validation to keep translations in sync with source text. During development, free-tier tokens-per-minute limits caused 413/429 errors, solved by reducing request granularity, limiting max_tokens, and throttling with 70-second intervals, plus adding character-set validation to discard incomplete translations. The article also discusses the tiered translation strategy and its boundaries: translation coverage should align with what the product can actually deliver.
Related articles

Chain of Thought Isn't Just for AI: How Humans Can Use CoT to Improve Thinking Quality
Chain of Thought isn't just a prompt trick for large models. Starting from a Hacker News discussion, this article analyzes why CoT works for human thinking too, breaks down actionable ways to transfer it, and highlights the key differences between human CoT and machine CoT.
Cloudflare Teams Up with OpenAI: Closi…
Cloudflare Teams Up with OpenAI: Closing the Loop from Vulnerability Discovery to Defense with AI
Cloudflare uses OpenAI Daybreak to investigate security vulnerabilities and generate reviewable remediation plans, while developers use Codex to boost coding efficiency and quality.

Orca Multi-Agent Orchestration in Practice: A Guide to Parallel Collaboration with Claude and Codex
Orca is Stable AI's free open-source agent workbench supporting 34 agents like Claude Code and Codex in parallel. Learn its Git worktree isolation, multi-model orchestration, and mobile management.