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)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| voxnorm-0.5.1.tar.gz | 73.3 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| 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 logRelease 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 logSecurity 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.
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 logA 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.