Skip to content
SpeakSay

You Cannot Proofread a Variable: Pronunciation and Locale QA for Voice Agents, IVR and Phone Systems (2026)

Your voice agent demo sounded perfect because you wrote the demo text. In production the sentences are assembled at runtime from customer names, amounts, dates, medication names and addresses — strings nobody will ever read before they are spoken. Overall audio quality scores hide this completely. Here is the metric that exposes it, and the QA process that fixes it at the data boundary instead of the text boundary.

Admin5 min read
You Cannot Proofread a Variable: Pronunciation and Locale QA for Voice Agents, IVR and Phone Systems (2026)

1. Why this is not the same problem as narration

We have written before about the numbers, dates and units pass for static scripts — the one where your voice reads "$1,499" as "one four nine nine" and a pronunciation dictionary cannot help you, because the model never sees a word there at all. Everything in that post is still true. But it has an assumption baked in that does not hold here: that you can read the script before you render it.

Take that assumption away and three things change fundamentally.

Narration / static scriptVoice agent / IVR
When the text existsBefore render. You can read every word.At runtime. Nobody reads it, ever.
How you find errorsListen to the renderYou cannot listen to an infinite set of possible renders
How you fix errorsEdit the text, re-renderEdit the template, the data, or the normalisation layer — never the utterance
Cost of one errorOne file, fixed once, goneRepeats on every call that touches that data pattern, potentially forever
Who hears itAn audience, genericallyThe specific person whose name or medication it just mangled

That last row is the reason this matters more than it sounds. In narration, a mispronunciation is an irritation. In a voice agent, it is a mispronunciation of the customer's own name, to their face, which reads as institutional carelessness in a way no other audio defect does. The published best-practice guidance for production voice systems puts pronunciation among the highest-impact quality issues for exactly this reason, and gives the canonical examples: healthcare agents mangling medication names, financial-services agents mangling customer names on every single call.

"On every single call" is the phrase to sit with. A static-script error is one bad render. A runtime error on a name-handling path is a defect that fires thousands of times before anyone reports it, because the people who notice are customers, and customers do not file audio bug reports.

2. How your current quality score hides a 100% failure rate

Here is the mechanism, and it is worth understanding precisely because it explains why teams with good dashboards ship this bug.

Suppose your agent's average utterance is forty words. Thirty-eight are ordinary function and content words — "your", "balance", "is", "due", "on", "please", "confirm". Two are entities: a name and an amount. Your quality evaluation, whether it is a human rating pass or an automated score, computes something over the whole utterance. The thirty-eight ordinary words are pronounced perfectly, because that is what these models are good at. The two entities are wrong every time.

Your dashboard reports 95% word-level accuracy. That is not a lie. It is also completely useless, because the two words it got wrong are the only two words in the sentence that carry information the caller did not already know. The other thirty-eight are scaffolding. You have built a metric that measures the scaffolding and averages away the payload.

This is why the current guidance for production voice systems is explicit that pronunciation review should treat named entities, product names, medication names, amounts and acronyms separately from generic words. Not as a sub-score on the same dashboard. As a different number, with its own threshold, that can fail independently.

The reframe to take into a planning meeting: your overall quality score is hiding a 100% failure rate on the only twelve words per call that matter. If that sentence makes someone in the room uncomfortable, you have found your project.

3. The metric: entity-level pronunciation error rate

Define it narrowly enough to be actionable. Here is a workable specification.

Definition

Entity-level pronunciation error rate (E-PER) = the proportion of entity slots rendered incorrectly, where an entity slot is any template variable or any token belonging to a declared entity class, and "incorrectly" means a listener who knows the correct pronunciation would not accept the output.

Three deliberate properties of that definition:

  • The denominator is entity slots, not words. A forty-word utterance with two entities contributes two to the denominator, not forty. This is the whole point.
  • The grader is a listener who knows the right answer. For a customer name that means someone who can say it; for a medication it means a clinical reviewer; for a place name it means a local. You cannot outsource this to someone with no ground truth.
  • It is binary per slot, not graded. Resist the urge to score partial credit. "Nearly right" on a person's name is wrong, and a graded scale will let you average your way back into a comfortable number.

Slice it, or it will lie to you again

Report E-PER broken down by entity class and by locale, never as a single figure. A pooled E-PER of 6% can easily be 0% on amounts, 2% on dates and 34% on surnames — and only the third number is a launch blocker.

SliceWhy it needs its own numberTypical threshold to set
Person names (given, family, separately)Highest reputational cost, widest distribution of inputs, worst model performanceStrictest. Set it where you would be comfortable if it were your own name.
Amounts and currencyNormalisation-driven, so failures are systematic and cheaply fixedZero tolerance. These should be exactly right after one fix.
Dates and timesLocale-dependent ordering, systematic once wrongZero tolerance, per locale.
Product / plan / SKU namesFinite set you control — no excuse for failureZero tolerance. A closed list means a lexicon fixes it permanently.
Medication, clinical, legal or regulated termsSafety and compliance exposure, not just qualityZero tolerance, with named human sign-off per term.
Addresses and place namesLong tail, locale-heavy, partially fixableRealistic target with a graceful-degradation path (see §8).
Acronyms and identifiersAmbiguous between spelled-out and read-as-wordZero tolerance on your own acronyms; policy-driven on external ones.

Notice how many rows say zero tolerance. That is not perfectionism — it reflects the fact that most of these classes are closed or rule-governed, so a failure is a missing rule rather than a hard problem. Only names and addresses are genuinely open sets, and those are the two rows where you need a strategy rather than a fix.

4. Fix it at the data boundary, not the text boundary

This is the central architectural point, and it is where most teams get it wrong by trying to solve a runtime problem with an authoring-time tool.

The instinct is to improve the template — write better prompts, add SSML by hand, tune the voice. But the template is not where the problem lives. The problem lives in the gap between a value in your database and a string in an utterance, and the fix belongs in that gap.

The layer you are missing

Insert an explicit normalisation layer between your data and your synthesiser. Not inside the prompt. Not inside the TTS request. A named component with tests.

  1. Type your variables. Every slot in every template declares an entity class: person_name, currency_amount, date, product_name, phone, address, identifier. An untyped slot is a bug, and should fail your build rather than your call.
  2. Normalise per class, per locale. A currency_amount of 1499.00 with locale en-GB becomes the spoken form "one thousand four hundred and ninety-nine pounds". The synthesiser receives the words. It never receives the digits.
  3. Resolve names against a lexicon before synthesis. For closed sets — your products, your plans, your office locations, your staff names — a maintained pronunciation lexicon resolves them deterministically. For open sets, see below.
  4. Emit only spoken forms downstream. The contract of the layer is: nothing numeric, nothing abbreviated and nothing ambiguous crosses the boundary into the synthesiser. If your synthesiser is doing normalisation for you, you have no control over it and no test surface.
  5. Log the pre- and post-normalisation pair. This is what makes the whole thing debuggable, and it is what lets you compute E-PER from production traffic instead of from a lab set.

The reason this architecture is worth the effort: it moves the failure from "unobservable, happens at runtime, discovered by customers" to "unit-testable, happens in a component, discovered by CI." A normalisation layer with a test suite is a thing you can be confident about. A prompt is not.

Test a locale voice against your own strings

Before you build the layer, find out what your chosen voice actually does with your real data patterns — your product names, your amount formats, the surnames in your customer base. Ten minutes of this changes what you build.

Run your own strings through SpeakSay TTS →

5. The seven variable types and what each one needs

Practical handling, class by class.

Person names

The hardest class, and the one where you must choose a strategy rather than expect a fix. Options, roughly in order of preference:

  • Capture the pronunciation at the source. If you have any point where the customer speaks or types — an onboarding flow, a profile page, an earlier call — you can store a pronunciation hint. This is by far the strongest answer and almost nobody does it. A single optional "how do we say your name?" field solves the problem permanently for the customers who care most about it.
  • Maintain a lexicon for the frequent tail. Name distributions are heavily skewed. A few thousand surname entries covers a surprisingly large share of a regional customer base, and the entries are reusable forever.
  • Avoid the name where it adds nothing. A great deal of agent copy uses the customer's name purely as a politeness marker. "Hi, thanks for calling — I can help with your balance" is not worse than "Hi Przemysław," mispronounced. Audit your templates for names that are decorative rather than informative and cut them. This is the cheapest available win and it is usually resisted for aesthetic reasons that do not survive listening to the alternative.
  • Degrade gracefully when confidence is low. See §8.

Currency amounts

Fully solvable. Normalise to spoken words in the target locale, and be careful about three things: the currency name and its position, the "and" convention, and how you handle zero minor units. "Forty-two pounds zero zero" is a normalisation bug, not a voice problem.

Dates and times

Fully solvable, and locale-critical. The same ISO date must become different spoken orderings in different locales, and relative forms ("tomorrow", "next Tuesday") are often clearer than absolute ones on a phone call. Decide the policy once, implement it in the layer, test it per locale.

Phone numbers and identifiers

Grouping is the whole job. A digit string read as one continuous sequence is unusable to a caller writing it down. Group it in the convention of the locale, insert pauses between groups, and — critically — decide a policy for confusable digits and letters. If your identifiers contain both, consider a phonetic alphabet for the letters rather than hoping.

Product, plan and SKU names

Closed set, therefore zero-tolerance. Put them in a lexicon, review them once with whoever owns the brand, and treat any new product launch as requiring a lexicon entry before it can appear in an utterance. Make that a checklist item in your launch process, because it will otherwise be discovered by a customer.

Medication, clinical, legal and regulated terms

Zero tolerance with a human sign-off, because the exposure here is not a quality complaint. Build the term list with a qualified reviewer, record who approved each pronunciation and when, and never let a new term enter the system without passing through that step. If your agent can say a drug name it has not had approved, that is a process defect regardless of how it sounds.

Addresses and place names

Open set with a long tail, so this is a realistic-target class. Prioritise the places your traffic actually concerns — a delivery agent serving one metro area has a tractable list. Accept that the tail will be imperfect and design the graceful-degradation path for it.

6. Locale is not a language setting

The most expensive misunderstanding in this whole area is treating "Spanish" or "English" as the configuration unit. Platform coverage in 2026 is routinely described in terms of locale variants rather than languages — dozens of variants across a smaller number of languages, each with its own recognition and synthesis pairing — and the practical guidance is explicit that latency, prosody and pronunciation all differ per voice and per locale. A voice that is excellent in one variant of a language can be noticeably worse in another.

Then there is the part that rarely makes it into vendor material: the telephony channel itself degrades everything. Reporting from the Indian market in 2026 describes models at around 5% error on US English reaching 25% or higher on regional Indian languages over mobile phone audio, with background noise, regional accents and Hinglish code-switching compounding — and that figure concerns recognition, which is the half of the problem this post is not about. The lesson transfers regardless: the phone is a hostile channel and your evaluation must happen over it.

What this means for your test plan

  • Evaluate per locale variant, never per language. If you serve three variants, you have three deployments to sign off, with three golden sets and three E-PER numbers.
  • Evaluate over the actual codec. Audio that is fine at studio bandwidth can lose the distinctions that separate two similar-sounding digits or two similar surnames once it has been through narrowband telephony. Test on the pipeline you ship, not on a file you downloaded.
  • Handle code-switching as a first-class case. If your callers mix languages within a sentence — and in many markets they do — then your templates do too, and a monolingual locale setting will mishandle the inserted terms. Decide explicitly which language each slot is rendered in.
  • Re-evaluate on every voice or model change. Pronunciation behaviour is a property of the specific voice and version. A model upgrade is a regression risk, which means your golden set is not a launch artifact — it is a permanent part of your CI.

Compare locale variants side by side before you commit

The difference between two variants of the same language is audible in about thirty seconds, and it decides how much lexicon work you will be doing for the next two years.

Compare voices by locale →

7. Building a per-locale golden set

A golden set is a fixed, versioned collection of utterances whose correct pronunciation you have established, run on every change. It is the only mechanism that turns this from an opinion into a test. Here is a build that takes a day and pays for itself in the first month.

  1. Mine your templates, not your imagination. Extract every template in the system and every declared slot. That is your coverage target — you want at least one golden utterance per template-slot-class combination.
  2. Mine your data for real value distributions. Pull the actual top surnames, the actual amount formats, the actual date patterns, the actual product names from production. Synthetic test data will be too easy and too clean, which is precisely how this bug survives to launch.
  3. Include a deliberate hard tail. Add the awkward cases on purpose: a hyphenated surname, a name with diacritics, an amount with zero minor units, a date at a month boundary, an identifier mixing letters and digits, a place name that is not pronounced as spelled. Twenty of these are worth two hundred easy ones.
  4. Establish ground truth with someone who knows. Per locale, per class. Record the correct pronunciation as a reference audio clip or a transcription convention you can compare against. Without ground truth you have a listening exercise, not a test.
  5. Freeze and version it. The set changes only deliberately. Any change to the set is a reviewed change, exactly like a change to the code — otherwise you will edit the test until it passes.
  6. Run it in CI on every change to the normalisation layer, the templates, the lexicon, the voice or the model version. Report E-PER per class per locale, and fail the build on the zero-tolerance classes.
  7. Feed production back into it. When a customer complaint or a support note surfaces a mispronunciation, the fix is two commits: the lexicon entry, and the golden-set case that would have caught it. This is what stops the same class of bug recurring.

Keep the set small enough to run often. A hundred well-chosen utterances per locale, run on every build, beats two thousand run quarterly by a wide margin.

8. What to do when it still goes wrong on a live call

Names and addresses are open sets, so some proportion will be wrong no matter how good your process is. The mature answer is not to pretend otherwise — it is to design the failure.

  • Route low-confidence slots to a different template. If your pipeline can flag a name as outside the lexicon and structurally unusual, use the template that does not say it. Silently choosing the name-free variant is better than an attempt, every time.
  • Let the caller correct it, and store the correction. "Have I got your name right?" is a single turn that converts a defect into data. The correction should write back to the customer record, which means the same customer never hears the error twice. Almost no deployment does this and it is the highest-value single feature in this entire post.
  • Never spell a name at a caller as a fallback. Spelling out a name the system could not pronounce draws attention to the failure and sounds worse than omitting it.
  • Instrument for silent failure. Callers rarely complain about pronunciation; they just disengage. Look for correlations between entity-heavy paths and drop-off, repeat-request or escalation rates. A path where callers ask for a human more often than the baseline is worth listening to, literally — pull the calls.
  • Have a hot-fix path for the lexicon. A mispronounced product name on a launch-day campaign should be fixable in minutes without a full deployment. If your lexicon only updates on release, it will not be updated.

9. Pre-launch checklist

#CheckDone when
1Every template slot has a declared entity classAn untyped slot fails the build
2A normalisation layer exists as a named, tested componentIt has unit tests and nothing numeric or abbreviated crosses into the synthesiser
3Closed-set terms are in a maintained lexiconProducts, plans, locations and staff names all resolve deterministically
4Regulated terms have named human sign-offYou can name who approved each one and when
5Golden set exists per locale variantIt runs in CI and includes a deliberate hard tail
6E-PER is reported per class per localeIt is a separate number from overall quality, with its own thresholds
7Evaluation runs over the production codecNot on studio-bandwidth files
8Decorative names have been audited out of templatesEvery remaining name slot is there because it carries information
9Low-confidence degradation path existsThere is a name-free template variant and it is reachable
10Caller corrections write back to the recordThe same customer cannot hear the same error twice
11Lexicon can be hot-fixed without a releaseTime-to-fix for a mispronounced product name is minutes
12Voice and model versions are pinned, with regression gatesAn upgrade cannot ship without re-running the golden set

Get the voice and locale decision right before the engineering

How much of the work above you end up doing depends heavily on which voice and locale you start from. Comparing candidates against your own worst-case strings is the cheapest hour in the project.

See volume and team pricing →

10. FAQ

Why does my voice agent mispronounce customer names when the demo was fine?

Because you wrote the demo text and you do not write the production text. In production, names arrive from a database at runtime, are inserted into a template, and are synthesised with no human reading them first. The demo tested the scaffolding of your sentences; production tests the entities inside them, which is where these models are weakest. The fix is a normalisation and lexicon layer between your data and your synthesiser, plus a pronunciation error rate measured on entity slots only.

Can I fix this with a pronunciation dictionary?

Partly. A lexicon fully solves closed sets — your products, plans, locations and staff. It cannot solve open sets like arbitrary customer names and addresses, and it does nothing at all for amounts, dates and identifiers, because those are normalisation problems where the model is not seeing a word to look up. You need both mechanisms, applied to different classes.

What is a good pronunciation error rate to target?

Set it per class rather than globally. Amounts, dates, product names and regulated terms should be at zero — they are rule-governed or closed, so any failure is a missing rule. Person names and addresses are open sets where a realistic target plus a graceful-degradation path beats an aspirational target with no fallback. A single pooled figure is the thing to avoid: it is what let the problem reach production in the first place.

Do I need to test every locale separately?

Yes, and by locale variant rather than by language. Pronunciation, prosody and latency are properties of a specific voice in a specific variant, so three variants means three sign-offs with three golden sets. Test over your real telephony codec too — narrowband audio erases distinctions that are perfectly clear in a studio-quality file.

Is it better to just leave the customer's name out?

Often, yes — and this is underused. A large share of name usage in agent copy is decorative politeness rather than information. If the sentence works without the name, cutting it removes an entire failure class at no cost. Keep names where they disambiguate or confirm identity; cut them where they are garnish.

How do I catch this in production if customers do not complain?

They will not complain, but they will behave differently: more repeat requests, more escalations to a human, more early drop-off on paths that are heavy in entities. Instrument by path, compare entity-heavy paths against the baseline, and pull the actual call recordings where the gap is largest. Then write both fixes — the lexicon entry and the golden-set case that would have caught it.

Give your words a voice

Start free with 10,000 credits. No credit card required.