Skip to main content

voxnorm

Text normalization for TTS: written forms into spoken forms, in pure Python. Speaks English, Traditional Chinese (zh-TW), Simplified Chinese (zh-CN), Japanese and Korean. One dependency (num2words).

uv add voxnorm      # or: pip install voxnorm
>>> from voxnorm import normalize
>>> normalize("The meeting starts at 12:00.")
"The meeting starts at twelve o'clock."
>>> normalize("Call 0912-345-678 before 9:30pm.")
'Call zero nine one two, three four five, six seven eight before nine thirty p m.'
>>> normalize("會議在12:00開始")
'會議在十二點開始'
>>> normalize("会議は12:00からです")
'会議は十二時からです'
>>> normalize("회의는 12:00에 시작합니다")
'회의는 열두 시에 시작합니다'

Everything the scanner does not recognise is returned character for character.

What converts

Clock times and time ranges, dates with a four-digit year, phone numbers, currency, percentages, temperatures, metric units, room and extension numbers, discounts (85折), letter-glued identifiers (A123456789), masked tails (****1234), emails and URLs, decimals and integers. Markup tags (<break time="500ms"/>) pass through untouched. The full table is in docs/charter.md.

Never guess

A form converts only when its shape alone settles the reading. 1/2 (a fraction) and 8/17 (a date) are the same string, so neither converts; 7-11 (a shop) and 3-5 (a range) likewise stay as written. Text left alone is spoken as written, which is where it started.

Language

Pass lang (en, zh-TW, zh-CN, ja, ko, …) when you know it. Without it, script decides: kana means Japanese, hangul Korean, Han characters Chinese (Simplified when distinctive Simplified glyphs appear), anything else English.

Codes

Whether 4820 is a quantity or an identifier is vocabulary, not shape, so the caller says so. code_words names the words next to a code; codes names the exact values:

>>> normalize("Your code is 4820.", code_words=["code is"])
'Your code is four eight two zero.'
>>> normalize("Ticket 4820 is ready.", codes=["4820"])
'Ticket four eight two zero is ready.'
>>> normalize("編號BK-000456", codes=["BK-000456"])
'編號BK零零零四五六'

A word matches directly before or after the digits; a value matches only as a whole token (4820 leaves 48200 alone).

Docs

charter (what converts and why) · design · languages · adding a language · testing · release · roadmap · experiments

Development

uv sync
uv run pytest
uv run ruff check . && uv run ruff format --check .

Apache-2.0.

Metadata

Release files for voxnorm 0.5.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for voxnorm 0.5.1
File Size Uploaded Provenance
voxnorm-0.5.1.tar.gz 73.3 kB With provenance Details

Built distribution (wheel)

Table of built distributions (wheels) for voxnorm 0.5.1
File Interpreter ABI Platform
voxnorm-0.5.1-py3-none-any.whl Python 3 none any Details

Total release size: 111.1 kB

Release files / voxnorm-0.5.1.tar.gz

Download URL voxnorm-0.5.1.tar.gz
Size 73.3 kB
Tags Source
SHA-256 checksum
How to use checksums
f3dd48721b50f9d571a933d35c40154bcd872ae7d59874762beca7570936e2a9
BLAKE2b-256 checksum
How to use checksums
3cbd5a85c25e40b60c7ea7454a858d094f8c225de69ee2b5f6e8226fbcdba2bb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.

Transparency log

Release files / voxnorm-0.5.1-py3-none-any.whl

Download URL voxnorm-0.5.1-py3-none-any.whl
Size 37.9 kB
Tags Python 3
SHA-256 checksum
How to use checksums
28ab35e735687a133e56c0fe20c67b919eef381d6db422c5e1aaad1dab918a3d
BLAKE2b-256 checksum
How to use checksums
7093e6c80b8a859a5b442ddf7d80ec3a7fb4ac36cc60b6a1c1511d06bdb08de7
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.

Transparency log

Security for voxnorm 0.5.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for voxnorm-0.5.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page