Your AI Voice Reads "$1,499" as "One Four Nine Nine": The Numbers, Dates and Units Pass Nobody Runs (2026)
There are two kinds of text-to-speech pronunciation failure and almost everyone applies the wrong fix to one of them. A pronunciation dictionary will never fix a price, because there is no dictionary entry for "$1,499" — the model never sees a word there at all. This is the normalisation pass: what breaks, what to write instead, and the sixty-second test that grades any voice before you commit.

1. Two failures wearing the same name
Everything called "TTS mispronunciation" is really two unrelated problems happening in two different places, and the fix for one does nothing for the other. This distinction is the whole article.
| Lexicon problems | Normalisation problems | |
|---|---|---|
| What breaks | Brand names, surnames, place names, acronyms | Numbers, currency, dates, times, ranges, units, ordinals, codes |
| Where | Inside the model — it sees a word, predicts the wrong sounds | Before the model — the step converting written form to spoken form is wrong, or never runs |
| The fix | A pronunciation dictionary entry or phonetic respelling | Rewriting the text so it is already spoken form |
| How often | Once, then it applies everywhere that word appears | Every instance — unless you change how you write |
| Why the other fix fails | Respelling a surname in the visible script is ugly and inconsistent | There is no dictionary entry for "$1,499". There is no word there to look up. |
Read that last cell again, because it is where the hours go. People open the pronunciation dictionary, type in a price, nothing changes, so they try a different voice, then a different platform. It was never going to work: a lexicon maps words to sounds, and "$1,499" is not a word. Something has to decide, before any acoustic work happens, that this string means "one thousand four hundred and ninety-nine dollars" — and that something is a text normalisation step, upstream of everything a voice setting can touch.
When it is missing, incomplete, disabled or wrong for your locale, the model receives characters with no word-level interpretation and does the only thing it can: reads the characters. The voice is fine. The model is fine. The audio is clean, confident and completely wrong.
SpeakSay's companion piece — "AI Voiceover Mispronouncing Names, Acronyms & Numbers? 7 Fixes for Perfect Pronunciation" — is the lexicon post, for brand names and dictionaries. This is the normalisation post. From here we only discuss things made of digits and symbols.
2. Why nothing in your workflow catches it
An April 2026 benchmark of commercial text-to-speech systems tested how widely used engines handle non-standard text — dates, currencies, phone numbers, identifiers and around twenty-seven other categories — with no preprocessing applied. More than sixty per cent of test sentences came back handled incorrectly by some of the most popular systems. Mispronunciation of numbers, brand names and proper nouns is separately the most frequently reported concrete TTS complaint across issue trackers, review sites and forums.
The sixty per cent is alarming. What makes it dangerous is the finding attached to it: nothing in a standard monitoring stack flags the error.
- Did the render complete? Yes — normalisation failures never throw errors.
- Is the file the expected length? Roughly. "One four nine nine" takes about as long to say as "fourteen ninety-nine".
- Is the audio clean? Perfectly. No clipping, no artefacts, confident delivery.
- Does an auto-transcript match? Often yes — the transcriber normalises in the other direction and hands "1499" back.
- Did someone proofread the script? Yes, and that is the trap: the script is correct. The error does not exist in the text. It exists only in the audio.
So it ships — and it ships specifically in your highest-stakes sentences, because those are the ones made of non-standard text.
| Sentence | Contains | Cost of getting it wrong |
|---|---|---|
| The price | Currency, separators, decimals | Listener mishears the offer, or distrusts it |
| The deadline | Numeric date, ordinal, time | Missed conversions, support tickets |
| The discount code | Alphanumeric identifier | Code unusable; the campaign leaks |
| The phone number | Digit groups, punctuation | Nobody can call you |
| The dosage or spec | Units, decimals, ranges | Real-world harm in regulated verticals |
| The model or version | Version strings | You sound like you do not know the product |
| "Here's why it matters" | Ordinary words | Renders perfectly, every time |
Your filler renders flawlessly. Your call to action is the coin flip.
Two structural facts before you assume your platform handles it. Some platforms implement normalisation for only a minority of the languages they support — one documented case covers twelve of ninety-four — so outside a short list of major languages you are doing the preprocessing yourself whether you know it or not. And on some platforms, number normalisation ships switched off by default on the fastest model tiers, because it costs latency. Moving to a faster model can silently disable the thing that was reading your prices correctly.
Test this on your own price, right now, for free
Do not take a benchmark's word for it. Paste the actual sentence containing your actual price into SpeakSay's free tools and listen to what comes back. Sixty seconds, and you will know whether you have this problem.
3. The reference table: what to write instead
The middle column is the failure pattern to expect when normalisation is absent, partial or locale-mismatched. The right column is plain English with no markup, deliberately — it works on every engine, voice, tier and model version, because it depends on no normalisation step running at all.
| Category | You wrote | What you often get | Write this instead |
|---|---|---|---|
| Currency, thousands separator | $1,499 | "one four nine nine"; "one comma four nine nine"; the figure with no "dollars" | one thousand four hundred and ninety-nine dollars |
| Currency with decimals | £24.99 | "pound twenty four point nine nine" | twenty-four pounds ninety-nine |
| Abbreviated large currency | $2.5M | "two point five em" | two point five million dollars |
| Percentages | 17.5% | "seventeen point five percent sign" | seventeen point five per cent |
| Ranges (en-dash, hyphen) | 20–30 minutes / 2-5 days | "twenty dash thirty"; "two hyphen five"; "twenty minus thirty" | twenty to thirty minutes / two to five days |
| Numeric date, either order | 03/04/2026 | "zero three slash zero four slash…" — or it resolves to the wrong one of 3 April / March 4 | the third of April twenty twenty-six |
| Times, 12-hour | 4:30pm | "four colon thirty pm" | half past four in the afternoon |
| Times, 24-hour | 14:05 | "fourteen colon zero five"; "fourteen five" | two oh five in the afternoon |
| Phone numbers | +44 20 7123 4567 | "plus forty-four twenty seven thousand one hundred twenty three…" | plus four four, two zero, seven one two three, four five six seven |
| Large round numbers | 1,000,000 | "one comma zero zero zero comma zero zero zero" | one million |
| Version strings | v2.5 / iOS 26.1 | "vee twenty-five"; "iOS twenty-six one" | version two point five / iOS twenty-six point one |
| Measurements and units | 12km / 4.5kg / 22°C | "twelve kay em"; "four point five kay gee"; "twenty two degree see" | twelve kilometres / four and a half kilograms / twenty-two degrees Celsius |
| Compound units | 1,200 ft² / 5000mAh | "twelve hundred ef tee two"; "five thousand em ay aitch" | twelve hundred square feet / a five thousand milliamp-hour battery |
| Ordinals | 1st, 3rd, 21st | "one st"; "three rd"; "twenty-one st" | first, third, twenty-first |
| Fractions, mixed numbers | 1/2, 3/4, 2½ | "one slash two"; "three over four"; "two", the glyph dropped | a half, three quarters, two and a half |
| Negative temperatures | -5°C | "dash five degrees" — or the sign dropped, inverting your meaning | minus five degrees Celsius |
| URLs | speaksay.com/tools | Often right; sometimes the domain as one unparsable token | speaksay dot com slash tools |
| Email addresses | hello@speaksay.com | Often right; sometimes "hello a speaksay", symbol dropped | hello at speaksay dot com |
| Alphanumeric codes | SAVE20 / A1B2C3 | "save twenty" — or was it two zero? Digits run together too fast to write down | S-A-V-E, two zero / A one, B two, C three |
| Serial identifiers | Order #10024 | "order hash ten thousand twenty four" — plausible, and not what you meant | order number one zero zero two four |
Three patterns generalise beyond the table. Symbols are the weak link, not digits — most engines handle a bare integer; what breaks them is the currency sign, the separator, the dash, the degree mark, the colon, each an instruction the normaliser must interpret, and each uninterpreted symbol is either spoken as a word or dropped. Dropped symbols are worse than mispronounced ones: "dash five degrees" is merely odd, while "five degrees" for -5°C has reversed your meaning and sounds perfect doing it. And digit strings need comma pacing — when you do want digits read individually, commas create the pauses a listener needs to capture the sequence.
4. Language is not locale: the date-order trap
Normalisation on many platforms is implemented per language, not per locale. English is English, so en-US and en-GB normalise identically — fine for almost all text, quietly catastrophic for the two categories where they flatly disagree: date order and currency phrasing.
You select a British voice. You write "03/04/2026". The voice is unmistakably British and reads it as March fourth. You meant the third of April. Nobody catches it, because the British voice sounded British and the date sounded like a date.
| Convention | US | UK | Germany | France | India |
|---|---|---|---|---|---|
| Numeric date order | MM/DD/YYYY | DD/MM/YYYY | DD.MM.YYYY | DD/MM/YYYY | DD/MM/YYYY |
| Decimal mark | point (1.5) | point (1.5) | comma (1,5) | comma (1,5) | point (1.5) |
| Thousands separator | comma (1,000) | comma (1,000) | point (1.000) | space (1 000) | lakh/crore (1,00,000) |
| Currency word order | "twenty-four ninety-nine" | "twenty-four pounds ninety-nine" | amount then unit | amount then unit | "…rupees fifty paise" |
| Symbol position | before ($10) | before (£10) | after (10 €) | after (10 €) | before (₹10) |
| 109 is called | billion | billion | Milliarde (billion = 1012) | milliard (billion = 1012) | billion; 100 crore |
| 24-hour default | no | mixed | yes | yes | mixed |
- Never write a bare numeric date in a script. Not for any audience, in any language. "The third of April" costs four syllables and cannot be misread by any engine in any locale. This single rule eliminates the highest-consequence normalisation error there is.
- Watch the billion/milliard row if you localise financial content. A German or French "billion" is a thousand times larger than the English one — a factual error in your copy, not a pronunciation problem.
- Do not trust a locale tag to carry the decimal comma. Write "1.5 kg" in a German script and a normaliser reading the point as a thousands separator has you saying fifteen hundred kilograms.
Picking a voice picks an accent. It does not pick a set of numeric conventions. Those two things feel like they should travel together and they do not.
5. The real fix: write it spoken from the start
Everything above is a patch list, and patching is what you will do to your existing catalogue. But it is not the real fix, because patching is per-instance and forever.
The real fix is a change in how you write, and it is free. Your script is not a document. It is an interface. Nobody reads it; its only job is to be converted into speech, so the correct form for every character in it is the spoken form. Written form exists for readers who benefit from the compression, and there is no reader — only a machine that has to expand it again, and the expansion is the unreliable step.
Before (written for a reader):
The Pro model starts at $1,499 — that's 17.5% off the RRP until 03/04/2026. It ships with a 5000mAh battery, weighs 1.2kg, and runs for 20–30 hours. Firmware v2.5 arrives in Q1. Pre-order before 4:30pm ET on the 1st and use code SAVE20, or call +44 20 7123 4567.
After (written for a voice):
The Pro model starts at one thousand four hundred and ninety-nine dollars — that's seventeen and a half per cent off the recommended price, until the third of April twenty twenty-six. It ships with a five thousand milliamp-hour battery, weighs one point two kilograms, and runs for twenty to thirty hours. Firmware version two point five arrives in the first quarter. Pre-order before half past four in the afternoon, Eastern time, on the first — and use code S-A-V-E, two zero. Or call us on plus four four, two zero, seven one two three, four five six seven.
The second version is longer on the page and identical in duration when spoken, because "one thousand four hundred and ninety-nine dollars" takes exactly as long to say whichever way you typed it. You added no runtime and removed nine opportunities for silent failure, at the writing stage where it costs nothing rather than the QA stage where it costs a re-render. It also reads better: "seventeen and a half per cent" is how a person talks, "17.5%" is how a spreadsheet talks.
Some voices handle figures far better than others
Normalisation quality varies enormously between voices and model tiers, and the only way to know is to hear your own numbers read back. Run the benchmark below across a few voices and cast the one that gets your prices right — that is a real casting criterion.
6. The sixty-second benchmark paragraph
Before committing a series or a client project to a voice or tier, grade it. Paste these twelve lines exactly as written — numerals, not spoken forms — into the text-to-speech tool and listen.
1. The Pro plan costs $1,499, or £24.99 a month. 2. That is 17.5% off, and margins rose 2.5M dollars. 3. Delivery takes 20-30 days, or 2–5 days express. 4. The deadline is 03/04/2026, before 4:30pm. 5. Doors open at 14:05 and close at 09:00. 6. Call +44 20 7123 4567 or 1-800-555-0199. 7. We passed 1,000,000 users and 1,250 partners. 8. Update to v2.5 on iOS 26.1 or Android 17. 9. It weighs 4.5kg, covers 12km, and holds 5000mAh. 10. The room is 1,200 ft² and sits at 22°C, or -5°C outside. 11. Our 1st, 3rd and 21st attempts used 1/2 and 2½ units. 12. Email hello@speaksay.com or use code SAVE20 at speaksay.com/tools.
The scoring rule. Listen once and mark each line pass or fail. A line fails if any value in it would be misunderstood by a listener who cannot see the text. That is the whole standard, and it is deliberately strict: a price is either the price or it is not.
| Score | What it means | What to do |
|---|---|---|
| 11–12 pass | Strong normalisation for this language and tier | Write naturally, spot-check the digits pass, keep the control render |
| 8–10 pass | Typical — common cases fine, symbols and locale break | Write dates, currency and codes in spoken form as standing policy |
| 4–7 pass | Partial normalisation | Write everything numeric as words; check whether you are on a fast tier with normalisation off |
| 0–3 pass | No meaningful normalisation for this language | Spoken form is mandatory — the expected result in many non-major languages |
Run it per voice and per tier, not per platform: a flagship model and a fast model can score very differently. Run it per language; your English score tells you nothing about your Spanish score. Save the render with the date and settings in the filename, because it becomes your control for the next section. The paragraph is deliberately ugly — it is not content, it is an instrument.
7. The digits-only QA pass
A full listen-through of every render is correct advice that almost nobody follows. Here is a targeted pass that catches most normalisation errors in a fraction of the time. Three rules.
- Listen. Never read. Proofreading is the trap, because the script is correct. Every minute reading it confirms the one thing that was never wrong.
- Listen only to lines containing digits. Put all your attention on the two per cent of the script carrying ninety per cent of the risk.
- Check the rendered audio, and check the exported file. Where a browser preview and an export can differ, verify the artefact you are publishing.
Find the lines by searching, not by eye. In any editor, enable regex in find-and-replace and search [0-9] with "highlight all" — you get a map of exactly where your risk is, usually six to twelve lines in a two-thousand-word script. A broader [0-9$£€%°#/@] also catches a stray dash in "20–30" where the digits were already spelled out. On the command line, grep -n '[0-9]' script.txt lists the lines to listen to. Note the timecodes before you press play and scrub to each one; twelve lines take about ninety seconds.
Work from the top by consequence: price, deadline, phone number, code, dosage, then the rest. And adopt one rule permanently: no script ships with a figure in it that has never been heard out loud by a human. Not reviewed — heard.
8. Why your fixes regress without warning
You fix the price. It reads correctly. Six weeks later it reads wrong again and you did not touch the script. This happens because normalisation behaviour belongs to the model version, not to the voice. The voice name is a front-end label; the normalisation step is part of the inference pipeline behind it. When a platform updates a model — routine, usually desirable, often unannounced — the voice can sound essentially the same while the preprocessing changes. A rule that expanded "£24.99" correctly may now behave differently; a locale default may shift; a fast tier may gain or lose a setting.
That is worse for a catalogue than a single clip. Re-render an old episode after a model change — to fix a typo, swap a sponsor read, bump the loudness — and its numbers are now normalised by a different pipeline than the rest of the series.
- Keep a dated control paragraph. The twelve lines above are the right content. Render them the day you commit to a voice and save with voice, tier, language and date in the filename:
control_en-GB_flagship_2026-09-27.mp3. - Re-render on a trigger, not a schedule. A changelog entry, a new voice in the library, a tier change, or a render that sounds subtly different.
- Compare line by line. Ninety seconds. Any line that changed tells you which category moved.
- Convert any regressed category to spoken form permanently. This is the ratchet: your writing rules absorb every category the platform has ever got wrong, so scripts become progressively immune to model changes rather than more exposed.
Text you wrote as words has no normalisation behaviour to regress. "One thousand four hundred and ninety-nine dollars" reads the same on every model version that has existed and every one that will.
9. Numbers in a second language
If you publish in more than one language, assume the problem is worse outside your main one — as a default, not an exception. Normalisation is hand-built, language-specific engineering, and those rules do not transfer between languages. It is unglamorous work, done first for the languages with the most commercial volume — hence platforms supporting ninety-plus languages for synthesis while implementing normalisation for a fraction of them.
- Your English score does not generalise. Run the benchmark separately in every language you publish in, and expect materially lower scores in each secondary one.
- Default to manual spoken form in secondary languages. Write every figure as words from the start and treat a correct automatic reading as a bonus.
- Translating a script does not translate its number conventions. This is the one that catches people. A translator, human or machine, faithfully renders your sentence into German and leaves "$1,499" and "03/04/2026" untouched, because they are not words and there is nothing to translate. Your German script now has an American date order, a dollar-first symbol position and a decimal point in a decimal-comma locale — and the translation looks complete.
- Localise the figures as an explicit separate step. After translation, run the digits pass with the locale table open: convert dates to words in the target convention, fix currency word order and decimal marks, check the billion/milliard trap in anything financial.
- Have a native speaker hear the numbers, not read them. If you get five minutes an episode, spend them on the digit lines.
10. Automating it at volume
If you render programmatically from a template, CMS, spreadsheet or LLM, manual fixing does not scale. Normalise at generation, not at synthesis. Insert a conversion step wherever your script text is produced, before it is ever sent to the voice. Instead of depending on a vendor's undocumented, version-dependent normaliser, you depend on your own code, which you can test, version and fix. The engine receives words, and words are the one input every engine handles.
- Handle structured fields explicitly. Prices, dates and quantities from a database arrive as typed values. Format them to spoken strings at templating time —
{{price_spoken}}, not{{price}}— using a locale-aware number-to-words library rather than string concatenation. - Then pattern-match the free text. Order matters: expand currency symbols before stripping punctuation, and handle ranges before individual numbers, or "20–30" becomes two separate values.
- Fail loudly on anything left over. The most valuable piece and the one most often skipped. After conversion, scan the outgoing text for any remaining digit or risky symbol and raise a warning with the line number. You have just built the monitoring that section two said does not exist, in roughly one regular expression.
- Keep a per-channel spoken-form style sheet, versioned next to your scripts, so output stays consistent across writers, languages and time.
| Style sheet entry | Example decision | Why decide it once |
|---|---|---|
| Currency phrasing | "twenty-four pounds ninety-nine" | Two writers will otherwise differ inside one series |
| Date format | "the third of April twenty twenty-six" | Removes locale ambiguity permanently |
| Year reading | "twenty twenty-six", not "two thousand and twenty-six" | Both valid; only one matches your register |
| Version numbers | "version two point five" | "vee two five" and "version twenty-five" are both wrong and both common |
| Code pacing | Letters hyphenated, digits comma-separated | Decides whether a listener can write the code down |
| Clock convention | 12-hour with am/pm for consumer, 24-hour for operational | Prevents "fourteen colon zero five" ever appearing |
| Units | Spelled out always, abbreviated never | "kay gee" is the most common unit failure |
| Approximation policy | Round in ad reads; exact figures on screen | Sometimes the right fix is not to say the number |
One operational rule with real money attached: never move a pipeline containing figures onto a faster or cheaper tier without re-running the benchmark. Latency-optimised tiers are exactly where normalisation is most likely to be off by default, and a tier change that halves your render cost while silently reading every price as a digit string is not a saving.
Pick the tier that gets your figures right, then stop thinking about it
Once you know which voice and model tier passes your benchmark, the only remaining question is volume — and that maths is simpler than the normalisation maths. Compare what you actually need, or remove the monthly calculation entirely with the lifetime plan.
11. Honest limits: what you cannot fix from the script
Spoken-form writing solves most of this category, not all of it. What remains:
- Prosody within a long figure. You control the words, not the rhythm across them. A human puts a small lift on "fourteen" and a settle on "ninety-nine"; many engines deliver a flat, evenly stressed chain. No respelling gets you there.
- Pause placement inside digit sequences. Commas help and are not precise. You are negotiating with the engine's punctuation handling rather than specifying timing.
- Stress that carries meaning. "It's only nine ninety-nine" and "it's only nine ninety-nine" mean different things, and emphasis inside numbers is largely not addressable from plain text.
- Genuinely ambiguous values. No engine can know whether "SAVE20" is "save twenty" or "save two zero" — which means this was always your problem.
When you hit one of these, the answer is not another respelling. It is to change the sentence.
- Rewrite to avoid the construction. "Just under fifteen hundred dollars" is warmer, shorter, more persuasive and completely un-manglable compared with an exact price read. "Early April" beats a date. "About twenty minutes" beats "20–30 minutes". For much marketing and narrative content the approximate version is better copy anyway — the precision was a habit, not a requirement.
- Put the exact figure on screen and say it approximately. This is what broadcast has always done. The voice says "just under fifteen hundred dollars"; the lower third says "$1,499". The listener gets the feel, the viewer gets the number, and the engine never touches a string it can break. For phone numbers, codes and long identifiers this is close to mandatory: a listener cannot reliably capture an eleven-digit number by ear however perfectly it is read, so reading it aloud was never the right design.
Know when precision is genuinely load-bearing, though. In regulated, medical, legal and technical-specification content the approximation route is closed, and the requirement escalates instead: write the exact spoken form, have a human hear it, keep the control render, and do not use a fast tier. The cost of the pass rises because the cost of the error does.
12. FAQ
Why does my AI voice read numbers wrong?Because text normalisation — the step converting written numerals into spoken words — either did not run, ran incompletely, or applied the wrong locale's rules. It happens before the voice model does any work, which is why changing voices rarely helps. Without it, the model receives characters with no word-level interpretation and reads them individually, so "$1,499" becomes "one four nine nine". The fix is to write the figure as words in the script.
Will a pronunciation dictionary fix a price or a date?No. A pronunciation dictionary maps words to sounds, and "$1,499" is not a word — there is no entry to create. Dictionaries are the correct fix for brand names, surnames and acronyms, which fail inside the model. Numbers, dates, currency and units fail before the model, in normalisation, and the only reliable fix is rewriting the text in spoken form.
How do I make text to speech read numbers correctly?Write them as words: "one thousand four hundred and ninety-nine dollars" instead of "$1,499", "the third of April twenty twenty-six" instead of "03/04/2026", "twenty to thirty minutes" instead of "20–30 minutes", "twelve kilometres" instead of "12km". This adds no runtime, because the spoken duration is identical, and it cannot regress when the platform updates its model.
Why did my British voice read the date in American order?Because normalisation on many platforms is implemented per language rather than per locale, so en-US and en-GB normalise identically despite using opposite date orders. Selecting a British voice selects an accent, not a set of numeric conventions. The permanent fix is never to write a bare numeric date — write "the third of April" and no engine in any locale can misread it.
Why does the same script suddenly read a price differently than last month?Because normalisation behaviour is a property of the model version, not of the voice. A platform update can change how figures are expanded while the voice still sounds the same, and it is rarely announced in terms you would connect to your prices. Keep a dated control render of a fixed test paragraph, re-render it whenever you notice a model change, and move any regressed category into spoken form permanently.
Does text to speech handle currency and dates in every language?No. Some platforms implement normalisation for only a minority of the languages they support — one documented case covers twelve of ninety-four — so outside a short list of major languages, assume numbers, currency and dates need manual spoken-form writing. Translating a script does not translate its number conventions either: a translator leaves "$1,499" and "03/04/2026" untouched, because they are not words.
How do I test whether a voice reads numbers properly before committing?Paste a twelve-line test paragraph containing one instance of each problem category — currency, percentages, ranges, numeric dates, 12- and 24-hour times, phone numbers, large round numbers, version strings, units, ordinals, fractions, negative temperatures and alphanumeric codes — and listen once, marking each line pass or fail. A line fails if any value would be misunderstood by a listener who cannot see the text. Run it per voice, per tier and per language.
Should I read a phone number or discount code aloud at all?Usually not. Even perfectly normalised, a listener cannot reliably capture a long digit sequence by ear, so reading it aloud was never good design. Put the exact value on screen or in the description and have the voice say something approximate and memorable. If you must read it, separate the digits with commas so the pauses land where a listener needs them.


