Skip to main content

One character in a placeholder breaks the substitution

A translated placeholder differs from the source by a letter or a space, so the interpolation does not bind and the raw token reaches the user.

JSON (i18next)JSON (ICU)

What you see

  • The literal `{{max}}` or `%@` appears on screen in one language.
  • The translation is otherwise perfect, which is why review misses it — the eye reads the sentence, not the token.
  • Often a single character: a transposed letter, a space inside the braces, a doubled symbol.

Why it happens

Interpolation is an exact string match. `{{max}}` and `{{mix}}` are different variables as far as the runtime is concerned, and one of them does not exist, so the token is left in the output. The same applies to `% @` versus `%@`, or `%1$$lld` versus `%1$lld`. These are typing accidents that no spell-checker flags and no key comparison can see.

1A transposed or mistyped variable name

The classic one-character case: the sentence is right, the variable is not, and the message renders with a token in the middle of it.

excalidraw/excalidraw132k★

Spanish had `{{mix}}` where the source defines `{{max}}` in the prompt-too-long error. We filed it as an issue; a contributor fixed the typo and added a placeholder-parity test suite covering every locale, so the next one fails CI.

Our report — the issue produced a permanent test, not just a string fix →

2Whitespace or punctuation inside the token

A space added inside a specifier while editing, or a symbol doubled, is enough. It is invisible in a review diff rendered with proportional fonts.

ACINQ/phoenix1.1k★

Czech rendered `**% @ **` instead of `%@`, and nineteen Arabic strings carry doubled or tripled dollar signs such as `%1$$lld`.

Our report — filed 2026-09-24 →

Find every instance

A typo shows up as one placeholder missing and another unexpected on the same key, which is the signature to look for:

npx @shipi18n/cli@latest check ./locales -s en --severity 'missing-key=off,untranslated=off,orphan-key=off'

When you see `placeholder-missing` and `placeholder-added` reported together on one string, it is almost always a misspelling rather than two separate mistakes.

✗ es  coverage 100.0%  1 error(s), 1 warning(s)
    error  chat.errors.promptTooLong  placeholder-missing — dropped {{max}}
    warning  chat.errors.promptTooLong  placeholder-added — unexpected {{mix}}

How to fix it

  1. 1Copy the token from the source string rather than retyping it, including its braces, dollars and spacing.
  2. 2Fix the whole family at once: a typo introduced during one editing pass is usually repeated in neighbouring strings.
  3. 3Add a parity test or the CI check, which is what the Excalidraw contributor did — it turns a one-off fix into a permanent guarantee.

The rules that catch this

How often this actually happens

Every finding on our scoreboard was read against its source string by hand before being filed: https://shipi18n.com/oss