"Text is written for the eye. Speech is made for the ear. Normalization is the translation between them, and it's mostly judgment."
The Gap Between Written and Spoken
Hand a raw sentence to a synthesizer and the seams show immediately. $5 should become "five dollars," not "dollar sign five." 3pm is "three p.m." A trailing Dr. might be "Doctor" or "Drive" depending on context. Written language is full of shorthand the eye resolves instantly and the ear cannot. Normalization is the step that turns what's written into what should be said — the unglamorous work that decides whether the voice sounds fluent or robotic.
It's Preservation, Not Just Expansion
The naive view is "expand everything." But normalization is judgment in both directions: some symbols should be spoken out, and some should be preserved because they carry meaning the voice needs. A technical symbol in code or math shouldn't be mangled into prose. Getting this right is why normalization is a real subsystem with versions and tests, not a pile of find-and-replace. Expand what helps; keep what matters.
Deterministic, Versioned, and First
Two properties make normalization safe to build a cache on. It's deterministic: the same text under the same rules produces the same output, every time. And it's versioned: the dictionary and rule set have a version that rides in the cache identity, so changing how something is spoken changes the key and correctly misses the old cache. That's why normalization runs first, before the identity is computed — the cache is keyed on the sound the engine will actually make.