Skip to main content
Most applications shouldn’t pre-normalize. Rime natively handles common patterns: currency with symbols, dates with years, clock times, phone numbers, standard measurements, and most numeric formats. Pre-normalizing adds latency and engineering complexity for behavior you usually get for free. Before adding a pre-processing layer, review the formats Rime handles natively and verify any specific input with the /textnorm endpoint.
If you still need to pre-normalize, start with the patterns listed under Known gaps. The prompt template at the end covers them, and you can drop it into the layer that generates your text.

When pre-normalizing makes sense

There are two cases where pre-normalization in your application is worth the cost:
  1. The input falls into a known gap. A small set of patterns aren’t reliably expanded today. Pre-expanding those specific patterns is the reliable fix.
  2. You need guaranteed consistency across regenerations. For utterances that must read identically every time, such as regulatory disclosures, legal read-backs, or confirmation flows, pre-normalizing removes a source of variability.
For everything else, trust Rime’s normalizer and use /textnorm to verify anything that sounds off. If you find a pattern Rime doesn’t handle, flag it to Rime so fixes ship on Rime’s side instead of in every customer’s pre-processing layer.

Known gaps

These patterns aren’t reliably expanded today. Either avoid them at the source, or pre-expand them in the layer that generates your text.

Dates

  • Financial quarters (Q1 2025, 1Q, 2Q). Not consistently expanded.
  • Cross-month date ranges (May-June 2024). Not a recognized pattern.

Times

  • European 15h30 times. The h separator isn’t recognized. Use 15:30.
  • Suffixed approximations. 9:00-ish reads the colon literally. Avoid or pre-normalize.

Numbers

  • Bare runs of five or more digits (10000000) often read digit by digit. Add thousands separators (10,000,000) to get the quantity reading, or use shorthand (10M).
  • e notation (1e6) reads the letter: “one e six.” Write 10⁶ (“ten to the sixth power”) or 10^6 instead.
  • A letter K or M right after a number can read as thousand or million even when it isn’t a quantity, so a 5K race can become “a five thousand race.” Put a space before the letter (5 K) to keep it as a letter.

Measurements

  • Uncommon units (btu, psi, dyne) are not reliably expanded.
  • Parenthesized compound units (5(kg/m²)) aren’t a recognized form.

Ranges

  • Inconsistent connector. Hyphen ranges are usually read as “to,” but in some contexts the hyphen is read literally. If consistency matters, use the word “to” directly.

Roman numerals

  • Roman numerals expand after a name or a context word: World War II reads “World War two” and Louis XIV reads “Louis the fourteenth.” Elsewhere, use spell() to force letter-by-letter, or write out the number.
  • Mixed-format numbering (section 2.IV.3) isn’t handled.

Phone numbers

  • Vanity numbers (1-800-FLOWERS) keep the letters as a literal string. Use spell(FLOWERS) if you need letter-by-letter.
  • Extensions (555-1234 ext. 567, 555-1234 x 567) aren’t explicitly handled.

Miscellaneous

  • Context-dependent abbreviations (Dr., Mr., St.) rely on context and may not always resolve as expected. See Abbreviations.
  • Repeating decimals (0.3̄, 0.(3)) aren’t recognized.
  • Internationalized domain names (non-ASCII in URLs) aren’t supported.

Pre-normalization prompt template

If your application uses an LLM to generate text before sending it to Rime, you can add the rules below to its system prompt or run them as a post-processing pass. The template targets only the known gaps. Rime’s normalizer handles the rest, so there’s no need to pre-expand everything.