My Chrome extension has an options page with a short paragraph about the paid tier and its price. From the release that added the paid tier in August until late October, the code rendered that price as .99.

No dollar sign, no leading digit. Just the cents.

The HTML had the right text in it the whole time. No test looked at it. Someone on our side finally saw it on October 26, checking the real options page before a launch. It's the only time we have a record of it on screen.

What the page actually rendered

The options page ships with English text hard-coded in the markup, and then a small function swaps in the translated string for every element that has a data-i18n key:

function applyI18n() { document.querySelectorAll('[data-i18n]').forEach((el) => { const m = chrome.i18n.getMessage(el.dataset.i18n); if (m) el.textContent = m; }); } 

The fallback is there for a missing key. If getMessage returns an empty string, the markup stays. It returned .99, which is not empty, so the correct markup got replaced with the broken string.

The message file looked fine to the eye. The price line was a dollar sign, a digit, a dot and two more digits (I've trimmed the digits here):

"proPrice": { "message": "$N.99" } 

Why $9 isn't a dollar sign

chrome.i18n treats a dollar sign followed by a digit as a positional substitution. $1 is the first value you pass to getMessage, $2 the second, and so on. Our price started with a 9, so the API read the first two characters as the ninth substitution. We passed none. It dropped the placeholder and kept the rest.

The same file has 7 other lines that use $1 and $2 on purpose: cookie counts, a delay in seconds, the number of rules imported. To the API, the price line looked exactly like those. There was nothing malformed to warn about.

Both locale files had the same line, so English and Korean were broken the same way.

Why nothing caught it

Before the fix, no test in the extension read the price text back. Not one file in tests/ mentioned it.

The markup also made it harder to spot. Anyone reading options.html saw the right price on line 144. You had to load the page in a browser, with the extension's locale files active, to see what a user saw.

The fix

  1. Escape the dollar sign as $$ in both locale files, so the API emits a literal $.
  2. Add a UI test that opens the options page and checks the rendered price text, not the message file.
  3. Commit a script that captures screenshots of the real UI, in the same commit.
  4. Ship it as a patch release. It went live on October 27.

The fix itself is one added character per file. The test is five lines.

Takeaway

A translation layer sits between the text you wrote and the text a user reads, and it has its own syntax. Most of the time that syntax is invisible, because most strings don't contain it. A price or a shell command in a help string can.

The check that would have caught this is boring: load the page, read the text back, compare it to what you meant. Reading the source file only tells you what the API was given.

If I could only keep one of those, I'd keep the rendered-text test. A lint rule for $ plus digit would have flagged all 7 of those lines too, and someone would have learned to ignore it.

If this had happened in your project, which would have caught it first: a lint pass over the message files for stray $ plus digit, a UI test that reads rendered text, or a screenshot someone actually looks at?

This post was drafted with AI help. The numbers and facts come from my own logs and repo.