Placeholder styles in game text: a tour, and why to pick just one
Almost every line of game text has a hole in it: a player name, a number of items, a damage value. That hole is filled at runtime by a placeholder token embedded in the source string. The syntax used for that token varies a lot between engines, frameworks, and hand-rolled string systems, and it is worth knowing the common ones before you pick — or inherit — one for your project.
printf-style: %s, %d, %1$s
Inherited from C's printf family, this style embeds a type code directly in the token. %s takes a string, %d takes an integer, and so on. Plain %s/%d tokens are positional by order of appearance in the string, which means a translator has no way to change the argument order without the runtime silently mismatching values.
Some implementations support explicit positional indices, letting a translator reorder arguments to match target-language grammar without the runtime reordering them itself:
You picked up %1$s x%2$d アイテム「%1$s」を%2$d個手に入れた
Indexed: {0}, {1}
A simpler positional style, common in .NET-style formatting and many custom engines. No type code is encoded in the token — the type is inferred at the call site. Reordering ({1} before {0} in translation) is usually supported directly, since the index itself carries the position, which is exactly what plain %s/%d cannot offer.
{0} dealt {1} damage to {2}Named: {playerName}, {itemCount}
Named placeholders trade brevity for clarity. A translator working from {playerName} defeated {enemyName} does not need to guess what argument 0 versus argument 1 means — the name documents it. This matters more as string count grows and reviewers stop having source-code context for every line, since the string itself now carries enough information to review in isolation.
{playerName} defeated {enemyName} in {turnCount} turnsICU MessageFormat: plurals and gender built in
ICU MessageFormat is a standardized syntax that goes beyond simple substitution — it can select text based on plural rules or gendered forms, evaluated per locale. A basic plural pattern looks like this. Many locales need more than a singular/plural split (some languages have three or more plural categories), and ICU's plural rules handle that per locale without the calling code needing to know the details.
{itemCount, plural,
one {# item}
other {# items}
}Engine-style custom tags
Many game engines and dialogue systems use their own bracket or angle-bracket tokens, sometimes mixing substitution with inline formatting or rich-text markup in the same token space, e.g. <color=red>{damage}</color> or [player]. These are the hardest to standardize on because they are project-specific and rarely documented outside the engine's own reference, and a translator moving between two projects has to relearn the convention from scratch each time.
Why one style, documented, beats a mix
Mixing styles across files is where things go wrong. A translator who has only ever seen {0} style will not intuitively know that %1$s requires keeping the argument index attached, or that ICU plural blocks are not free text to rewrite. Every style above is a legitimate choice; the risk is not in which one you pick, it's in picking more than one without telling anyone.
Standardize on a single placeholder style per project, and write it down where translators actually look: a short reference sheet with one example per token type, not a paragraph buried in an onboarding doc. A translator who knows the rule for {itemName} will get it right in string 1 and string 10,000. A translator guessing at a style they've never seen will get it wrong exactly when you can't easily check — deep in a spreadsheet, far from the game.
- Pick one placeholder style for the whole project, not per file or per feature
- Document each token type with a real example, not just a name
- Note whether your runtime supports positional reordering — if not, say so explicitly, since translators from other projects may assume it does
- Keep the reference next to the export file translators actually open, not in an internal wiki they don't have access to