From game jam build to released game: fixing the text layer
Your jam entry got a better reception than you expected, and you have decided to build the real version. Most advice about that transition is about scope: what to cut, what to expand, how to avoid the trap of rebuilding everything from scratch. This article is about the least glamorous layer of the same job, the one that nobody puts on a roadmap — the words.
Jam text debt is a specific thing, different from ordinary technical debt. Text written at three in the morning to be understood by a couple of hundred jam voters, who read your entry page and knew the theme, now has to work for strangers who arrive from a store page with no context. It has to survive being read on a stream. It may eventually have to be translated. And there is about to be ten to thirty times more of it.
That last point is the reason to do this early rather than at the end. Straightening out three hundred strings is an afternoon's work. Straightening out six thousand, written over a year with the same habits, is a project you will keep postponing until it becomes a reason not to add languages at all.
What forty-eight hours does to your text
Nothing here is a criticism of jam code. These are the shortcuts that let the build exist at all, and they were the right call at the time. They are simply not the shortcuts you want to carry into a two-year project.
- Player-facing sentences typed directly into whatever function needed them, scattered across every script
- Placeholder copy that shipped — 'TODO: explain this', 'Item01', temporary names for characters that stuck
- Near-duplicate strings: the same warning written three slightly different ways in three places
- Sentences assembled from fragments at runtime, because concatenation was faster than a format string
- Debug and developer text reachable by the player, usually in an error path nobody tested
- Terminology drift: the same stat called Energy in the tutorial, Stamina on the HUD and SP in the item text
- Tone drift: a jokey line in the intro, a dry systems message on the next screen, a jam in-joke in the credits
Step 1: inventory every string that reaches the player
Do not start fixing. Start listing. You cannot plan work on text you have not found, and you will not find it all by memory — the strings that hide are exactly the ones on paths you rarely walk: error dialogs, empty states, the message when a save fails, the text on the last screen of a losing run.
Use three passes, because each finds things the others miss. Play the game deliberately, including every failure path you can trigger, and screenshot every screen. Then search the source tree for quoted string literals and skim the results, ignoring logs and debug output. Then list the text that lives outside the build entirely: the window title, the store or jam page description, the credits, the achievement names if you have any, the text in your screenshots and trailer.
The deliverable at the end of this step is a list, not a fix, and one number: the total word count. That number is your baseline. It tells you how big the cleanup is, it is the first thing anyone quoting a translation will ask for, and comparing it against the same count in six months tells you how fast your text is actually growing.
# Rough sweep for player-facing literals — adjust to your language and layout. # The point is to skim the output, not to trust the pattern. grep -rn '"' src/ --include='*.cs' | grep -viE 'log|debug|assert|print|path|http'
Step 2: give every string a key and a home
Now move them. Every player-facing string goes into one table or resource file under a stable key, and the code asks for it by key. This is the change that makes everything after it possible: you cannot count, review, spellcheck, proofread or translate text you cannot export.
Do it screen by screen rather than as one heroic refactor. Pick a scene, move all of its strings, verify the scene still reads correctly, commit, move on. This keeps the work interruptible and keeps the game runnable throughout, which matters because you are almost certainly also adding features at the same time.
Two things to fix while you are in there, because you will never have a better opportunity. First, unpick any sentence built by concatenation and replace it with one complete string containing named placeholders — this is the single change that most affects whether translation is possible later. Second, when you find near-duplicates, decide which wording is correct and delete the others; the moment they are all in one file, duplicates become obvious in a way they never are while scattered across scripts.
// Before: invisible to any export, word order fixed in code
ShowMessage("You need " + cost + " gold to buy " + itemName + ".");
// After: one complete message, named placeholders, one home
ShowMessage(T("shop.error.need_gold", cost, itemName));
// strings/en.csv
// shop.error.need_gold,"You need {cost} gold to buy {item}."Step 3: rewrite the copy for strangers, not for jam voters
Extraction is mechanical. This step is not, and it is where most of the quality difference between a jam build and a released game actually lives. Your jam players arrived having read your entry page, knowing the jam theme, often knowing you. A player arriving from a store page has none of that. Text that was sufficient because of shared context now has to carry the context itself.
Three passes are worth making over the extracted file. A clarity pass: does each message make sense to someone who has never seen the game, particularly tutorial text and error messages? A terminology pass: does every concept have exactly one name, used everywhere including the UI, the tutorial and the item descriptions? And a tone pass: read the whole file top to bottom and listen for the seams where a jam-era joke sits next to a line written last week in a different mood.
Write the terminology decisions down as you make them, in a small list of terms with the chosen wording and a one-line note on what each thing is. This costs almost nothing now and it is the document a translator, an artist, a wiki editor or a future collaborator will ask for later. It is also what stops you from re-litigating the same naming question every time you add a system.
Step 4: settle the language question before the content triples
You do not need to decide whether to translate. You need to decide whether you might, because the answer changes what you do over the next few months — and doing those things later, on ten times the content, is what makes localization look expensive.
The cheap hedge is what steps 2 and 3 already gave you, plus three small habits: keep every new string going into the table rather than into code, keep sentences whole rather than concatenated, and stop painting words into image assets that a player has to read. Together these cost roughly nothing per string and preserve every option. The expensive version is deciding in month eighteen that you would like an English build, and discovering that only the first three hundred strings ever followed the rules.
If your jam entry drew comments in languages you do not speak, that is real demand data and it is worth writing down while it is still visible — where the players came from, what they asked for, which language. Jam pages, streams and comment threads are one of the few places a solo developer gets unsolicited evidence about which markets already noticed the game, and it tends to disappear from view once the page stops being active.
Step 5: make the habit survive the next ten times as much text
The cleanup is worthless if the full game regrows the same debt, and it will unless the rules are explicit. Solo developers are not exempt from this — the version of you who is tired and shipping a build at midnight makes exactly the same shortcuts a rushed teammate would.
Adopt a short definition of done for anything that shows text: the strings are in the table with keys, nothing is concatenated, the wording matches the terminology list, character limits are noted for anything in a constrained UI slot, and no debug text is reachable from a normal play path. It is a five-item check, and it takes seconds when the feature is fresh.
Then export the whole text file and read it, start to finish, every month or two. It is the single most effective quality habit available for game text: it catches leftover placeholders, duplicated warnings, tone drift and terminology that silently changed three systems ago. It is also a decent proxy for progress — watching the file grow is a more honest measure of how much game you have added than a task board is.