Solo & indie devsこの記事を日本語で読む

Running game translation in Google Sheets: a workflow that holds up

You have a few thousand strings, one or two translators, and no budget for translation software. Someone suggests a shared spreadsheet, someone else says that is unprofessional, and the project stalls for a week over tooling instead of getting anything translated.

The spreadsheet is fine. For a small game with a small number of languages it is often better than the alternative, because it is the one tool every collaborator already has and already knows. What fails is not the spreadsheet — it is the default way people use one: three columns, no rules about who edits what, and an export step nobody tested until the day it mattered.

This is a working setup instead: how to design the columns, how to share the file without it quietly rotting, how to get the text back out without the spreadsheet corrupting it, how to track what changed between rounds, and how to tell when you have outgrown it.

Why a spreadsheet is still the right answer for a small team

The advantages are real and worth stating, because they are the reason to resist tooling for a while. A translator can open it with no installation, no account setup, and no training. Two people can work in it at the same time and see each other. It has comments, so a question about a line lives next to the line instead of in an email thread. It has version history, so a mistake is recoverable. And it filters and sorts, which is most of what a translation tool actually does for a project this size.

It is also honest about its limits, which are worth knowing before you commit. A spreadsheet has no concept of your keys being unique, no way to enforce that a placeholder in the source also appears in the translation, and no memory of how you translated a similar line last time. It does not merge — if two people edit the same cell, one edit wins, quietly. And it lives outside your version control, so the file the game builds from and the file people are editing are two different things that have to be reconciled by hand.

The practical boundary is roughly this: a spreadsheet works well up to a handful of languages and a couple of active editors, with translation rounds measured in weeks rather than continuous. Past that, the reconciliation work grows faster than the translation work, and that is your signal to move.

Designing the columns

Most spreadsheet failures trace back to the sheet having three columns — key, English, and the target language — and nowhere to put anything else. The extra columns are not bureaucracy; each one exists because a specific question kept getting asked over email instead.

Put every language in its own column on a single sheet, rather than one tab per language. One sheet per language duplicates the key and source columns, which then drift apart, and it makes it impossible to see at a glance which languages are missing a given line. One row per string, one column per language, and everything else describing that row.

  • key — never edited by anyone but you, and never reused for a different meaning
  • context — what the line is, where it appears, who says it, and what tone. The single highest-value column in the sheet
  • max — the character limit where a string must fit fixed space, blank where it does not. A translator who knows the limit writes to fit it
  • source and target language columns — one per language, always in the same order
  • status per language — the workflow lives here, and it is what makes a second round possible
  • notes or questions — one column where a translator can flag something without editing the text itself
key            | context                    | max | en              | ja        | ja_status
shop.buy       | Button, shop grid          | 8   | Buy             | 購入        | reviewed
shop.noGold    | Toast after failed buy     | -   | Not enough gold.| ゴールドが足りません。 | translated
boss2.taunt    | Boss line, ch.3, arrogant  | -   | Try again.      |           | new
item.potion.d  | Item desc. %n% = amount    | -   | Heals %n% HP.   | HPを%n%回復。 | question

Sharing it without losing control

The single rule that keeps a shared sheet usable is that source text and keys are yours, and translations are theirs. When a translator finds a typo or an ambiguity in the source, they must not fix it in the sheet — they leave a comment or a note, and you change it, because a source change has consequences in every other language column that only you can see.

Enforce that structurally rather than socially. Protect the key, context, and source columns so they are read-only for collaborators, leaving the language columns editable. Give access only to the people who need it, and prefer comment-only access for anyone who is reviewing rather than translating. Use a dropdown, via data validation, for the status column so it holds one of a fixed set of values instead of whatever someone typed that day — free-text status columns become useless within two weeks.

Two more rules save a surprising amount of pain. Nobody adds or deletes rows except you, because inserted rows in a sorted sheet are how a translation ends up attached to the wrong key. And nobody sorts or filters destructively — teach collaborators to use filter views, which change only their own view, rather than sorting the sheet under everyone else.

Version history is your undo, and it is more capable than most people realize: you can see who changed a cell and when, and restore a previous state of the whole file. Before any large operation — a bulk paste, a sort, an import — make a named version so there is an obvious point to return to.

Getting the text back out safely

This is where spreadsheets bite, because the app is designed to be helpful with numbers and that helpfulness is destructive to text. Export to CSV or TSV and check for these before you trust the file.

A cell whose content begins with an equals sign, a plus, or a minus may be interpreted as a formula rather than text. Game text does this more often than you would expect — a damage modifier line, a stylized arrow, a mathematical expression in a puzzle hint. Formatting the language columns as plain text before anyone pastes into them prevents the conversion, and it also stops the other automatic conversions: strings that look like numbers losing their leading zeros, strings that look like dates becoming actual dates, and long numeric strings being rewritten in scientific notation.

Automatic substitution is the second hazard. Straight quotes silently become curly quotes, three periods become a single ellipsis character, and hyphens become dashes. Sometimes that is what you want typographically; the problem is that it happens inconsistently and without asking, so half your file has one character and half has another, and a font that lacks the substituted glyph renders a blank box. Turn substitutions off in the app's preferences before starting, and check for them in the export.

The rest is mechanical but worth a checklist. Line breaks inside a cell export as a quoted multi-line CSV field, which is valid but breaks naive line-by-line parsers — decide whether your pipeline supports them or whether breaks should be an explicit marker in the text instead. Trailing and leading whitespace is invisible in a cell and meaningful in a game; trim it on import. Non-breaking spaces arrive from pasted content and look identical to normal spaces. And commas and quotation marks inside a cell must be quoted and escaped correctly by whatever reads the file, which is the standard reason a CSV imports with every column shifted by one from a particular row onward.

Keeping track of what changed

A spreadsheet handles the first translation round easily. Everything after the first round is where an undesigned sheet collapses, because the hard question is no longer 'what does this say' but 'what has changed since last time'.

The status column carries most of this. Keep the set of values small and unambiguous — new, translated, reviewed, needs-update, question — and set the rule that a row is only exported to the build when it reaches a defined state. That turns a half-finished sheet into something shippable, because unfinished rows fall back rather than shipping as blanks.

The harder case is a source line that changes after it has been translated. The translation is now silently wrong: it looks complete, its status says reviewed, and it describes something the game no longer does. The lightweight fix is a column holding a copy of the source text as it was when translated, plus a formula comparing it to the current source; when they differ, the row is stale and its status should flip to needs-update. This one column catches the most damaging class of error a spreadsheet workflow produces.

Also resist deleting rows for cut content. Mark them removed and keep them, because content comes back, and because a deleted row takes its translation and its history with it. Deleting is also how row counts stop matching between the sheet and the export, which is a bad afternoon.

status values (data validation dropdown)
  new          — source exists, no translation yet
  translated   — translated, not yet checked
  reviewed     — checked in context, safe to ship
  needs-update — source changed after translation
  question     — blocked, see the notes column
  removed      — cut from the game, kept for history

When to graduate off the spreadsheet

There is no virtue in leaving early, and no virtue in staying too long. The signals that it is time are specific rather than a matter of taste: you have more than a handful of languages and the sheet has become too wide to read; several people are editing at once and you are resolving conflicting edits by hand; you need translation memory because the same lines keep recurring across updates; your build pipeline needs the current text automatically rather than after a manual export; or you find yourself writing scripts to validate the sheet, which is the point at which you have started building the tool you were avoiding.

One decision made early makes leaving cheap: keep the key column stable and unique from day one. Every migration path out of a spreadsheet — into a localization platform, into files in your repository, into a database — is a mapping from keys to text. If the keys are clean, that migration is an afternoon. If identity has been carried by row position or by the English text itself, it is a manual reconciliation of every string in the project, and that is the real cost of a badly designed sheet, paid all at once at the end.

Related articles