Rails renders %{name} instead of the value
A Rails locale file shows the literal %{name}, or a sentence with the value missing, in one language. Why I18n.t cannot substitute it, and how to find every instance across your locales.
What you see
- •A flash message or mailer prints `%{name}` as text, or reads as a complete sentence with the specific value gone.
- •English is correct. The broken locale often looks fluent, which is why review passes it.
- •`i18n-tasks` reports nothing, because every key is present on both sides.
Why it happens
Rails interpolates by name: `t("...", names: items)` substitutes `%{names}` and nothing else. If the translation spells the variable differently, translates it, or simply leaves it out, Rails either raises `I18n::MissingInterpolationArgument` or renders a sentence that has lost the thing it was supposed to name. Key-set comparison cannot see any of it, because the key is right there.
1The variable was dropped and the sentence rewritten around it
The translator produced a generic sentence rather than a template. It reads well, it passes review, and it can no longer tell the user which item, order or amount the message is about.
solidusio/solidus5.3k★
Dutch and Polish rendered the out-of-stock flash without `%{names}`, so a shopper was told something became unavailable but never which item. The call site passes `names: out_of_stock_items`.
2The source renamed the variable and the translations kept the old one
English moves from `%{commentable_id}` to `%{typename}` and forty locale files still reference the retired name. Rails raises on a missing interpolation argument, so this is the version that shows up as an exception rather than bad copy.
ifmeorg/ifme1.6k★
Catalan, Spanish, Argentinian Spanish and Brazilian Portuguese still used `%{commentable_id}`; Swedish used `%{commented_on}`. Hindi, Indonesian, Vietnamese and Chinese had gone further and translated the variable name itself.
3A link or fragment interpolation disappears entirely
Sibling keys that hold a link label are a common casualty: the sentence survives, the anchor does not, and the page quietly loses a route to its own documentation.
24pullrequests/24pullrequests1.7k★
Thirteen locales rewrote the homepage line as plain prose and dropped `%{contributing_path}`, so the link to the contributing primer never rendered in those languages.
Find every instance
Point the checker at your locale directory. It unwraps the Rails root key, so trees compare key for key:
npx @shipi18n/cli@latest check ./config/locales -s en --severity 'missing-key=off,untranslated=off,orphan-key=off'Pipes are not plural separators in Rails, and strftime patterns under `date.formats` are not placeholders; the Rails grammar knows both, so neither shows up as noise.
✗ nl coverage 100.0% 1 error(s), 0 warning(s)
error spree.inventory_error_flash_for_insufficient_quantity placeholder-missing — dropped %{names}How to fix it
- 1Restore the variable using the source spelling exactly. `%{names}` is an identifier; it is never translated, even when the sentence around it is.
- 2When the source renames a variable, update every locale in the same change. In all three cases above the reported locale was not the only one affected.
- 3Add the check to CI so the next rename fails the pull request. `--baseline` freezes what exists today and fails only on new drift, which keeps a large tree green on the first run.
The rules that catch this
How often this actually happens
Rails projects were a blind spot in our first scan and turned out to be among the most affected once the checker learned the format: https://shipi18n.com/oss