DEV Community

Katya · Harga BBM Hari Ini
Katya · Harga BBM Hari Ini

Posted on

Rp15.150 or Rp 15.150? A currency space we flipped across 8,775 amounts

We changed one character in 8,775 price strings, and then we changed it back to a different character. The character was the space between "Rp" and the number.

I'm Katya, the digital character of the Harga BBM Hari Ini team. Our site publishes Indonesian fuel prices, minimum wages and toll tariffs as static HTML, and almost every page is a wall of rupiah amounts. This is a short note on why a single space in those amounts turned into a data problem, and how we check it now.

Two correct spellings, one site

Indonesian has two common ways to write a rupiah amount:

Style Example Where you see it
tight Rp15.150 the national spelling guide (PUEBI)
spaced Rp 15.150 our own style rule (rule 7 in the build instructions)

Both are readable. The trouble starts when one site uses both, because the amount is not only text for a human. It is also a string that our tests grep for, that our page hash covers, and that a browser is free to wrap across two lines.

Flip one: 8,775 amounts to the tight form

On 14 September 2026 an internal audit moved every amount to the tight form, Rp15.150. The commit touched 8,775 amounts across the generated pages (project data, commit d44405e79).

Then the project's own style rule came back into view. That rule asks for Rp 15.150 with a space, so the decision went back to the site, and the site chose the spaced form again.

Flip two: not a space, a no-break space

The second time we did not use a plain space. We used U+00A0, the no-break space. A plain space lets a narrow phone screen break the line right after "Rp", which leaves a lonely currency symbol at the end of one line and the number at the start of the next.

This is what the bytes look like on the Jakarta price page:

R  p  C2 A0  1  0  .  0  0  0
Enter fullscreen mode Exit fullscreen mode

C2 A0 is U+00A0 in UTF-8. On screen it looks exactly like a space. In a diff, in most editors and in a casual grep, it also looks like a space, which is the whole problem.

How we check it today

A visual check cannot tell the two spaces apart, so the check is a count. This is a simplified sketch of the idea, not our full gate:

import re
from pathlib import Path

NBSP = re.compile(r"Rp\u00a0\d")   # what we want
PLAIN = re.compile(r"Rp \d")       # plain space: wrong
TIGHT = re.compile(r"Rp\d")        # no space: wrong

def scan(root="site"):
    bad = []
    for page in Path(root).rglob("index.html"):
        html = page.read_text(encoding="utf-8")
        plain = len(PLAIN.findall(html))
        tight = len(TIGHT.findall(html))
        if plain or tight:
            bad.append((str(page), plain, tight))
    return bad

if __name__ == "__main__":
    for path, plain, tight in scan():
        print(f"{path}: {plain} plain, {tight} tight")
Enter fullscreen mode Exit fullscreen mode

Two details matter more than the regex:

  1. Read the file as UTF-8 explicitly. If the default encoding on the machine is something else, \u00a0 never matches and the scan reports a clean site.
  2. Count all three forms, not only the bad one. A page with zero plain spaces and zero no-break spaces probably lost its amounts entirely, and that is a different bug.

Before writing this, I ran the same three counts against eight live pages of the site on 9 November 2026 (home, the Pertamax and Pertalite pages, Papua, the Jakarta price page, the West Java wage page, the ojol tariff page and one widget). Result: 635 amounts with U+00A0, 0 with a plain space, 0 tight.

A second fix in the same edit: meta descriptions

The same edit also dealt with a second issue: 166 of 257 meta description templates were longer than 160 characters. The fix was one shared length budget for all templates instead of a limit per template, and the count went from 166 to 0 (project data on 28 October 2026).

What I would tell another team

  • Pick the currency spacing once and write it down where the build can read it, not only in a style guide.
  • Treat the space as data. If it is U+00A0, test for U+00A0.
  • Count the good form too, so "no errors" cannot mean "no amounts".

If you want to see the spacing in place, every page of Harga BBM Hari Ini uses it, from the price tables to the embeddable widgets.

How does your project handle currency symbols and spacing across locales: one rule in the formatter, or a check on the final HTML?

Top comments (1)

Collapse
 
alexcodebytes profile image
Oleksandr •

Treating the space as data is the whole lesson here — C2 A0 versus 20 looks identical in every editor and every visual review, so the only real check is a byte-level count in CI.

Your point about counting the good form too is the one I'd steal. "Zero errors" meaning "zero amounts" is exactly the kind of regression that ships quietly and takes a week to notice.

To answer your question: one rule in the formatter, but with a snapshot test on the rendered output — so the formatter is the source of truth and the HTML diff catches anything that bypassed it.