Engineeringこの記事を日本語で読む

Localizing a Godot game: an orientation

Godot's localization system is built around an idea that is easy to hold in your head: translations live in a table with one column per key and one additional column per language, imported into the engine, and at display time your code calls a translate function with a key, which returns the string for whatever locale is currently active. If your text updates automatically when the locale changes for a given control, that is this mechanism doing its job; if it does not, the string was probably set once as a literal instead of being looked up through the key.

The exact import settings, column naming conventions, and translate function signature are documented by Godot itself and are worth reading directly rather than relying on secondhand descriptions, since they are the kind of detail that is easy to get slightly wrong from memory. What follows here is the part that stays true regardless of which specific mechanism you use to wire a key to a string.

Keys are not English text

It is tempting, especially with a CSV-based system, to use the English source text itself as the key — the first column is already full of readable strings, so why add a second layer. The problem shows up the first time you need to fix a typo or reword a line in English: the key changes along with it, and every translated column for that row is now orphaned, silently disconnected from a key that no longer exists anywhere in your code.

Use short, stable identifiers instead, grouped by where the text appears, and keep the English text as just another column in the table rather than the row's identity:

key,en,ja
ui.settings.title,Settings,設定
dialogue.ch1.guard01.line03,"Halt! Who goes there?","止まれ!何者だ?"

One CSV, one source of truth

A CSV-based translation table has a real advantage: it is a single file that both engineers and translators can open, and it makes every language's coverage visible in one place — an empty cell is a missing translation, immediately visible by scanning a column. Keep it that way rather than splintering translations across scattered inline strings in scenes or scripts. Every piece of player-facing text should have exactly one home, and that home should be the table, not a `Label` node's default text or a string literal in a script that happens to display correctly in your source language.

Comments or a separate notes column, even if your import step ignores them, are worth keeping in your working copy of the file — context that helps a translator (who is a character, what screen this appears on, whether it's urgent or casual in tone) rarely fits naturally into the key alone.

Placeholders and plurals

Strings built from parts — a player name, a count, a location — need a formatting convention a translator can rely on and reorder without breaking. Whatever placeholder syntax you settle on, the rule is the same across every engine: a translator may move a placeholder to match their language's word order, but must never rename, delete, or duplicate the token, since code on the other side is matching those tokens exactly.

Plural forms need real thought at the key-design stage, not as an afterthought. English has one plural rule; many languages have several, keyed to the exact quantity rather than a simple singular/plural split. A single flat string with no plural-aware structure gives a translator no way to express that correctly, no matter how good the translation is otherwise.

Fonts and glyph coverage

Godot renders text through whatever font resource a control is assigned, and that font only draws the glyphs it actually contains. Adding a language whose script your current font does not cover — Japanese, Korean, Arabic, and many others relative to a typical default font — means missing characters render as blank boxes, not errors, so this is invisible until someone actually looks at that screen in that language.

Test this directly in a running scene, with real (or realistically long) text in the target language, for every UI element and every font used across menus, dialogue, and any custom fonts assigned to specific labels. A font preview in isolation does not tell you how it behaves inside your actual layout.

Text expansion and locale selection

Container sizes and label widths tuned to fit English text will not survive translation as-is — many languages run meaningfully longer for the same meaning, and CJK text, while often shorter in character count, renders wider per character. Build layouts that can grow, and test with placeholder strings padded to a realistic length for your target languages before real translations exist, so you find overflow problems early rather than after a translation batch lands.

Locale selection itself deserves a deliberate default: detecting a reasonable starting locale from the system while still giving the player an explicit in-game way to override it, since automatic detection is sometimes wrong (a shared computer, a language set for reasons unrelated to gameplay preference) and a player should never be stuck unable to read their own settings menu.

  • Every string has a stable key and lives in the translation table, not inline in a scene or script
  • Placeholders are verified automatically, not just read over by eye
  • Fonts are checked in a running scene for every target script, not previewed in isolation
  • Layouts are tested with realistically long strings before real translations exist
  • The player can change the active locale explicitly, not just rely on system detection

Related articles