ChatTTS Text Normalizer
Get the model to read numbers like a human
- STRING
Ever had a TTS model read "The meeting is at 2:30 PM, unit 42, $1,234.56" like a stranger reciting a barcode? That's what this node fixes. The Text Normalizer takes raw text and rewrites it the way a human would actually speak it - numbers become words, dates and times get expanded, units and currency get spelled out - before it ever reaches the sampler.
Here's the thing: ChatTTS runs this normalization internally anyway, every time you sample. The node exists to expose that step so you can control it, inspect what the model will actually "read," and normalize once instead of on every run.
The inputs
text- the required input, multiline. Whatever you'd otherwise paste straight into the Sampler.language-auto(default),en, orzh. Set it when auto-detection guesses wrong; for a Chinese sentence with mixed English, telling it explicitly can clean up how numbers and dates come out.do_text_normalization- on by default. This is the "spell it out" pass:123→ "one hundred twenty-three",2026→ "twenty twenty-six" (context-dependent, like a real reader).do_homophone_replacement- on by default. Uses ChatTTS's homophone map to fix common same-sound wrong-character errors, which matters most for Chinese text. Turning it off is the debugging move if a sentence is being mangled.
Output is a single STRING - the cleaned text, ready for the Sampler or a Text Splitter.
How it works
The node borrows ChatTTS's built-in normalizer directly (it constructs a Chat object just for its normalizer). It's the same code the sampler calls mid-pipeline - you're just getting it on the front end. One practical effect: you can eyeball the normalized output and see exactly what the model is about to say, which makes "why did it pronounce that wrong?" much easier to answer.
When it earns its place
- Numbers, dates, prices, units in your script. This is the difference between a demo and a finished voiceover.
- Chinese text with English mixed in, where homophone replacement and language hints do the heavy lifting.
- Making a workflow deterministic. If you normalize once and reuse the string across seeds, you're not re-running the same text processing every time - and it's a visible, debuggable step in the graph.
The honest take: for simple English paragraphs you can skip it and the sampler's internal pass handles it. Reach for it when output text is genuinely messy - product descriptions, timestamps, anything with figures - or when you want to see what the model thinks your text says. Wiring it in is one connection upstream of the sampler, and it's the cheapest quality upgrade in the pack.
Inputs (4)
| Name | Type | Default | Description |
|---|---|---|---|
| text | STRING | — | |
| languageopt | COMBO | auto | 3 options: auto, en, zh |
| do_text_normalizationopt | BOOLEAN | true | — |
| do_homophone_replacementopt | BOOLEAN | true | — |
Outputs (1)
| Name | Type | Description |
|---|---|---|
| STRING | STRING | — |