跳到正文
DEV Community· Nikolai Fedotenko·· 2 小時前AI 評分53

locsmith 以本機模型預翻譯遊戲字串並校驗格式

I built a localization pass that never lets an unreleased game script leave the laptop

AI 導讀

開發者 Nikolai Fedotenko 打造 locsmith 命令列工具,以本機運行的開放權重模型預翻譯遊戲字串,並檢查佔位符、富文本標籤、換行和字串長度。

正文

This is a submission for the Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle

What I Built

locsmith is a command-line tool that pre-translates a game's string table with an open-weight model running locally, then checks every line before it can break the build.

I work in game development, and the localization step is where small studios get hurt. The script of an unannounced game is under NDA, so pasting it into a cloud chatbot is off the table. An agency pass on every content update costs more than many indie teams make. And raw machine translation breaks things in ways that only show up in QA: a {price} placeholder that became {цена}, a </color> tag that disappeared, or a button label that's twice as wide as the button.

locsmith handles the boring 80% before a human translator sees the file:

  • It batches strings to a local model with context hints ("Blacksmith NPC, gruff but friendly") and the glossary terms relevant to that batch.
  • It validates every result: placeholders and rich-text tags must match the source exactly (reordering is allowed), line breaks are preserved, per-string max_len is respected, and glossary terms are used.
  • If a string fails, it goes back to the model with the specific problem ("lost placeholder {price}", "too long: 32 > 28") and is retried. Strings that still fail are marked review instead of silently shipping.
  • Reruns only translate new or changed strings, so it fits a daily-build workflow. A non-zero exit code means "human needed", which lets it gate a CI step.

Who would pay: solo devs and 2–10 person studios shipping to Steam in several languages. They'd pay for a one-time license or a small subscription that turns "we'll localize after launch" into "every build has a draft translation and a list of the 20 lines a human must look at".

Demo

One command against the bundled sample (13 strings, English → Russian, with a glossary):

$ python3 locsmith.py samples/strings.csv --to ru -g samples/glossary.ru.csv
translated 13/13

gemma4:12b @ http://localhost:11434/v1
strings: 13  ok: 11  needs review: 2  auto-fixed on retry: 0  cached: 0
time: 200.2s  (15.40s per new string)
  REVIEW hud.stamina_low: too long: 41 > 24 chars
  REVIEW shop.buy: too long: 32 > 28 chars
written: samples/strings.ru.csv

The output CSV is ready to drop into the build; the two review rows are flagged, not shipped:

key source target status issues
shop.buy Buy for {price} Aether Shards Купить за {price} Осколков Эфира review too long: 32 > 28 chars
shop.not_enough You need {0} more Aether Shards. Вам нужно еще {0} Осколков Эфира. ok
loot.picked You picked up %d x %s Вы подобрали %d x %s ok
quest.reward [b]Reward:[/b] {xp} XP and {shards} Aether Shards [b]Награда:[/b] {xp} ОЗП и {shards} Осколков Эфира ok
npc.smith.greet Back again, Warden? ... Снова ты, Страж? Кузница не спит, и я тоже. ok

Every placeholder ({price}, {0}, %d, %s, {xp}, {shards}, {key_dodge}), rich-text tag (<color=#ff4040>, [b]) and \n survived, and the glossary terms came through inflected — Осколок Эфира → Осколков Эфира, Warden → Страж. The only two flags are genuine UI-length overflows a human (or a shorter rephrase) needs to resolve.

Code

GitHub logo nfedotenko / locsmith

Local, private, validated pre-translation of game strings with an open-weight model (Gemma via Ollama)

locsmith

Local, private pre-translation of game strings with an open-weight model.

Indie teams can't send an unannounced game's script to a cloud API (NDAs, leaks) and can't afford an agency pass on every build. locsmith runs a first translation pass on your own machine then validates every string so the output is safe to drop into the build:

  • placeholders and tags survive untouched: {0}, {name}, %s, %1$s, <color=#f00>, [b], \n
  • UI length limits (max_len per string)
  • glossary terms (character names, currencies) used consistently; inflected languages supported via stems, strict for exact names
  • broken strings are sent back to the model with the exact problem, up to --retries times; the rest are flagged review
  • reruns only translate new or changed strings

Works with any OpenAI-compatible endpoint: Ollama, llama.cpp llama-server, vLLM, LM Studio. Standard library only.

Gemma 4 and other reasoning models "think" before…

How I Built It

  • Model: Gemma 4 (gemma4:12b, Q4_K_M) served by Ollama on a MacBook Pro (Apple M1, 16 GB). The tool talks to any OpenAI-compatible endpoint, so llama.cpp's llama-server, vLLM or LM Studio work too. Switching models is a single flag.
  • Reasoning off by default. Gemma 4 is a thinking model: left alone it spent ~1,700–3,000 tokens reasoning about a 13-string batch before emitting any JSON, which on a 16 GB laptop meant minutes per batch. locsmith sends reasoning_effort: none, which drops the same batch to ~310 generated tokens with identical output quality — about a 10x cut. --reasoning low|medium|high turns deliberation back on when you want it.
  • Zero dependencies: a single Python file using only the standard library, so a studio's build machine doesn't need another package manager.
  • Validation is the product. A single regex tokenizer recognizes {0}/{name}, printf (%s, %1$s, %.2f), HTML-like tags (<color=#f00>), BBCode ([b]) and literal escapes. Source and translation are compared as multisets, so a language can move %s before %d but can't lose or invent one.
  • Glossary in inflected languages: in Russian, "Aether Shard" can appear as осколок, осколков or осколками. Exact matching produces endless false alarms, so by default each glossary word is matched by its stem. Proper names can be marked strict instead.
  • Tests cover the validators, JSON recovery from chatty model output, the retry loop and the incremental cache.

Results on the bundled sample (13 strings, en → ru, gemma4:12b via Ollama):

  • passed validation on the first try: 11 / 13
  • auto-fixed on retry: 0
  • left for human review: 2 (both genuine UI-length overflows, not model mistakes)
  • time: 200 s total, ~15 s/string on a 16 GB M1 — memory-bound, since a 12B model barely fits; the first-pass batch of all 13 runs in ~31 s warm, and the retries for the two length failures dominate the rest. A machine with more RAM, or a smaller Gemma, is considerably faster.

Why Does Open Innovation Matter?

For this product, an open model is a requirement, not just a preference.

  1. Data never leaves the machine. An unannounced game's dialogue, item names and plot twists are what the NDA protects. With a local open-weight model, the leak surface is the developer's own laptop. I can say that to a studio with a straight face. "We send it to an API, but they promise not to train on it" doesn't pass a publisher's legal review.
  2. The unit economics work. A mid-size indie game has tens of thousands of strings, and every patch adds more. With local inference, the marginal cost of a rerun is electricity. That's what makes "translate every build" a feature instead of a budget meeting.
  3. No lock-in, room to specialize. Games have their own vocabulary and tone. With open weights, a studio can swap in a bigger model for the story and a fast one for UI, or fine-tune on its previously approved translations. Nobody can deprecate the model under them mid-production.
  4. Works offline. Build machines, trains, conventions, game jams.

The trade-off is that a local 12B model makes more mistakes than a frontier API. That's exactly why the validation and retry loop exists: the tool doesn't trust the model, it checks it.

Prize Categories

  • Best Use of Gemma

來源:DEV Community · dev.to