Skip to main content

words2num2 — words to numbers

PyPI version Python versions Downloads / month Package status AUR version CI Lint CodeQL E2E Tests Coverage Latest release Last commit Issues License

The inverse of num2words2.

words2num2 parses spoken-form numbers — "forty-two", "trois cent quatre", "二十三" — and returns numeric values. It mirrors num2words2’s locale list (100+ languages, 120 dispatch entries) and adds a free-text auto-parse mode that handles currencies, units, configurable thousands/decimal separators, and ASR/LLM-style mixed text.

It is Rust-powered with a thin Python binder. The whole parsing engine — the English grammar, the generic reverse-lookup backend, the sentence walker, number-format parsing and auto-parse — runs in a compiled Rust core (PyO3/abi3); Python only shapes arguments and results. The core embeds the num2words2 conversion engine natively, so words2num2 is self-contained and has no runtime dependencies, while running several times faster than the former pure-Python implementation. Output is unchanged — validated against a frozen corpus of ~11,000 round-trip cases.

The project is hosted on GitHub, and the full documentation is available in the Wiki. Contributions are welcome.

Why this library

Existing inverse libraries are usually English-only, lack a sentence mode, and don’t compose with the locale defaults you already use for the forward direction. words2num2:

  • Accepts the same locale codes as num2words2 so the two libraries are drop-in inverses of each other.

  • Has a hand-written grammar parser for English and a generic reverse-lookup backend that auto-derives {words → number} tables from num2words2 for every other locale out of the box.

  • Walks free text via words2num_sentence / auto_parse_sentence — useful when post-processing ASR transcripts, LLM output, or user-typed forms that mix words and digits.

  • Handles currency symbols ($ € £ ¥ ₹ ₽ ₩ ₺), ISO codes (USD/EUR/...), scale shortcuts ($5m → 5,000,000), units (length / mass / temperature / time / volume / percent), and CLDR-style number formats per locale.

  • Pluralizes long-form units in expand mode (5 dollars / 1 dollar, 5 feet / 1 foot, 5 yen / 1 yen).

Performance

The parsing engine is compiled Rust (PyO3/abi3), so there is no Python-level tokenising or table walking on the hot path. Typical native throughput (Apple M-series, nanoseconds per call, after warmup):

Operation

ns/call

English cardinal ("eight thousand seven hundred sixty-five")

~5,900

English ordinal ("forty-second")

~4,100

French cardinal ("trois cent quatre")

~1,900

Russian cardinal ("сорок два")

~1,700

parse_number_string("1.234.567,89", lang="de")

~240

auto_parse("$12,345.00")

~990

For the 100+ generic locales, the first call in a given language builds the {words → number} reverse table once (~35 ms) and caches it in the core; every call after that is a native lookup. Output is byte-for-byte identical to the former pure-Python implementation, validated against a frozen corpus of ~11,000 round-trip cases and the full test suite.

Installation

pip (Python 3.10+):

pip install words2num2

cargo (Rust) — the compiled core is published on crates.io as words2num2-core:

cargo add words2num2-core

That’s it — the wheel bundles the Python binder and the compiled Rust extension, and words2num2 has no runtime dependencies (the num2words2 conversion engine is compiled in, so you do not need the num2words2 package).

Prebuilt wheels are published for:

  • Linux — x86_64 and aarch64 (manylinux2014)

  • macOS — Apple Silicon (arm64); Intel (x86_64) from a later release

  • Windows — x86_64

On any other platform (or an older release without a matching wheel), pip falls back to the source distribution and builds the extension locally — this needs a stable Rust toolchain (rustup); nothing else.

Arch Linux / Manjaro (AUR):

# With an AUR helper
yay -S python-words2num2
paru -S python-words2num2

# Or manually
git clone https://aur.archlinux.org/python-words2num2.git
cd python-words2num2
makepkg -si

From source (needs a stable Rust toolchain and maturin):

git clone https://github.com/gladiaio/words2num2
cd words2num2
pip install -e .          # builds the Rust extension via maturin
# or, to produce a wheel:  maturin build --release

Wheels for every version are also attached to the GitHub Releases page.

Quickstart

>>> from words2num2 import words2num, words2num_sentence
>>> words2num("forty-two")
42
>>> words2num("one thousand two hundred thirty-four")
1234
>>> words2num("minus seven")
-7
>>> words2num("three point one four")
Decimal('3.14')
>>> words2num("nineteen ninety nine", to="year")
1999
>>> words2num("twenty-first", to="ordinal")
21
>>> words2num("quarante-deux", lang="fr")
42
>>> words2num("zweiundvierzig", lang="de")
42
>>> words2num("сорок два", lang="ru")
42

>>> words2num_sentence("I bought twenty-three apples and fourteen pears.")
'I bought 23 apples and 14 pears.'

Auto-parse mode

auto_parse extracts a numeric value plus its unit from any free-text expression. auto_parse_sentence walks running text and replaces every quantity in place. It supports configurable thousands/decimal separators per locale, currency symbols and ISO codes, scale shortcuts, SI/imperial units, percent, and disambiguation hints.

>>> from words2num2 import auto_parse, auto_parse_sentence

# Currencies
>>> auto_parse("$12,345.00")
Quantity(value=12345.0, unit='USD', kind='currency', confidence=1.0)
>>> auto_parse("$5m").value
5000000
>>> auto_parse("12,50 €", lang="de").value
12.5

# Units
>>> auto_parse("5cm")
Quantity(value=5, unit='cm', kind='length', confidence=1.0)
>>> auto_parse("20°C").kind
'temperature'
>>> auto_parse("forty-two kg").value
42

# Configurable separators
>>> auto_parse("1.234,56", lang="de").value
1234.56
>>> auto_parse("1 234,56", lang="fr").value
1234.56

# Disambiguation for ambiguous unit tokens
>>> auto_parse("5m", prefer={"m": "mile"}).unit_long
'mile'

# Sentence mode
>>> auto_parse_sentence("Pay $12.50 for 5kg of apples at -5°C.")
'Pay 12.5 USD for 5 kg of apples at -5 °C.'

# Expand mode renders the long unit form, with English plural rules
>>> auto_parse_sentence("Pay $12.50 for 5kg.", expand=True)
'Pay 12.5 dollars for 5 kilograms.'
>>> auto_parse_sentence("Pay $1.00 for 1kg.", expand=True)
'Pay 1 dollar for 1 kilogram.'
>>> auto_parse_sentence("5 ft and 1 ft.", expand=True)
'5 feet and 1 foot.'

Configurable number formats

parse_number_string is the primitive used by auto_parse for digit-form numbers. You can call it directly with explicit separators or rely on per-locale CLDR-style defaults:

>>> from words2num2 import parse_number_string

>>> parse_number_string("12,345.67")                              # auto-detect
12345.67
>>> parse_number_string("12.345,67", lang="de")                   # German defaults
12345.67
>>> parse_number_string("1 234,56", lang="fr")                    # French defaults (NBSP)
1234.56
>>> parse_number_string("12'345.67", thousands_sep="'", decimal_sep=".")  # Swiss
12345.67
>>> parse_number_string("1_234.56", thousands_sep="_")            # programmer
1234.56

The locale defaults table covers 50+ locales: English/CJK use comma thousands and period decimal; French uses non-breaking-space + comma; Swiss French uses apostrophe + period; German/Spanish/Italian/Portuguese/ Dutch/Romanian use period + comma; Russian/Scandinavian/Slavic use space + comma. See words2num2/formats.py for the full table.

Auto-detection heuristic (when no override and no locale match):

  1. If both . and , appear, the rightmost one is the decimal.

  2. If one separator appears multiple times, it is thousands.

  3. If one separator appears once with exactly 3 trailing digits, it is thousands; otherwise it is decimal.

  4. Spaces, NBSP, apostrophe, and underscore are always thousands.

Command line

$ words2num2 "forty-two"
42
$ words2num2 "trois cent quatre" --lang=fr
304
$ words2num2 "twenty-third" --to=ordinal
23

Supported locales

words2num2 mirrors num2words2’s locale list — 120 dispatch entries including:

af, am, ar, as, az, ba, be, bg, bn, bo, br, bs, ca, ce, cs, cy, da, de, el, en, en_IN, en_NG, eo, es, es_CO, es_CR, es_GT, es_NI, es_VE, et, eu, fa, fi, fo, fr, fr_BE, fr_CH, fr_DZ, gl, gu, ha, haw, he, hi, hr, ht, hu, hy, id, is, it, ja, jw, ka, kk, km, kn, ko, kz, la, lb, ln, lo, lt, lv, mg, mi, mk, ml, mn, mr, ms, mt, my, ne, nl, nn, no, oc, pa, pl, ps, pt, pt_BR, ro, ru, sa, sd, si, sk, sl, sn, so, sq, sr, su, sv, sw, ta, te, tet, tg, th, tk, tl, tr, tt, uk, ur, uz, vi, wo, yi, yo, zh, zh_CN, zh_HK, zh_TW

Aliases: jp → ja, cn → zh_CN.

Wiki

For the full documentation, including installation, API details, CLI usage, supported locales, sentence conversion, auto-parse behavior, and migration guidance, please check the Wiki. Feel free to propose wiki enhancements.

Conversion types

The to= parameter accepts cardinal, ordinal, ordinal_num, year, and currency — same set as num2words2.

How it works

  • English (lang_EN) ships a hand-written recursive-descent parser that handles cardinals, ordinals, decimals, negatives, scale words to centillion, year mode, “and” connectors, and hyphenation.

  • Every other locale uses Words2Num_Base, which lazily builds a {normalized_words: integer} table by calling num2words2 for each integer in a configurable range (defaults to -1..10000). This guarantees correctness for the lookup window for every locale supported upstream — at the cost of out-of-range values raising Words2NumError until a hand-written parser is added.

Hand-written grammar parsers can be added incrementally per locale by overriding to_cardinal / to_ordinal in the corresponding words2num2/lang_XX.py module — same pattern as num2words2.

Public API

Function / class

Purpose

words2num(text, lang, to)

Parse a single word-form number.

words2num_sentence(text, ...)

Replace every word-number in running text.

auto_parse(text, ...)

Parse a single quantity (number + unit).

auto_parse_sentence(text, ...)

Replace every quantity in running text.

parse_number_string(text, ...)

Digit-form parser with separators.

Quantity

Dataclass returned by auto_parse.

UNITS / CURRENCIES

Registries of recognized units and currencies.

NUMBER_FORMAT_DEFAULTS

Per-locale separator defaults.

CONVERTER_CLASSES

Per-locale converter registry.

Words2NumError

Raised when input cannot be parsed.

See REFERENCE.md for the full API reference with parameters, return types, and examples.

Development

git clone https://github.com/gladiaio/words2num2
cd words2num2
make install-dev
make test          # pytest
make lint          # black + flake8 + isort
make format        # apply black + isort

Releasing

Every push of a tag matching v* triggers GitHub Actions to:

  1. Build sdist + wheel.

  2. Run the test installation in a clean environment.

  3. Generate release notes and create a GitHub Release.

  4. Publish to PyPI via Trusted Publishing (no token in CI).

To cut a release:

git tag vX.Y.Z
git push origin vX.Y.Z

A manual fallback workflow (Publish to PyPI (manual)) is available via gh workflow run and uses PYPI_API_TOKEN / TEST_PYPI_API_TOKEN repo secrets.

Changelog

See CHANGELOG.md.

License

LGPL-2.1, mirroring num2words2. See COPYING.

Release files for words2num2 0.3.3

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

Source distribution (sdist)

Source distribution for words2num2 0.3.3
File Size Uploaded
words2num2-0.3.3.tar.gz 87.9 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for words2num2 0.3.3
File
words2num2-0.3.3-cp38-abi3-win_amd64.whl CPython 3.8 abi3 Windows x86-64 Details
words2num2-0.3.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl CPython 3.8 abi3 Linux glibc 2.17+ x86-64 Details
words2num2-0.3.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl CPython 3.8 abi3 Linux glibc 2.17+ ARM64 Details
words2num2-0.3.3-cp38-abi3-macosx_11_0_arm64.whl CPython 3.8 abi3 macOS 11.0+ ARM64 Details
words2num2-0.3.3-cp38-abi3-macosx_10_12_x86_64.whl CPython 3.8 abi3 macOS 10.12+ x86-64 Details

Total release size: 13.0 MB

Release files / words2num2-0.3.3.tar.gz

Download URL words2num2-0.3.3.tar.gz
Size 87.9 kB
Tags Source
SHA-256 checksum
How to use checksums
ee88697a25e25edcf027a718c950a20625313cf72e1faec7311a3b313e13e956
BLAKE2b-256 checksum
How to use checksums
87b645dae4976aded54b800e2e9dea1045e6077a637df2669ac983c4575f722f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 25, 2026.

Transparency log

Release files / words2num2-0.3.3-cp38-abi3-win_amd64.whl

Download URL words2num2-0.3.3-cp38-abi3-win_amd64.whl
Size 2.7 MB
Tags CPython 3.8 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
e32eb3f578495d8a7c27d3bfd8bdb29023e970803b99728ef0efad8b685d4c93
BLAKE2b-256 checksum
How to use checksums
d32002ed1dafdc74ce0c89b6188b0f23af40d2ca56f43baea87d1e7c3da8f364
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 25, 2026.

Transparency log

Release files / words2num2-0.3.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl

Download URL words2num2-0.3.3-cp38-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl
Size 2.7 MB
Tags CPython 3.8 Linux glibc 2.17+ x86-64 abi3
SHA-256 checksum
How to use checksums
256ae263b668d878f7230e2df1aad4a89673d6631951951131c6b0556f22e7b9
BLAKE2b-256 checksum
How to use checksums
a79f0148cdda053fc2e8e949aa606ba4565f5ef5cfd148893e145d31ed788051
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 25, 2026.

Transparency log

Release files / words2num2-0.3.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl

Download URL words2num2-0.3.3-cp38-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl
Size 2.6 MB
Tags CPython 3.8 Linux glibc 2.17+ ARM64 abi3
SHA-256 checksum
How to use checksums
e0d956e4f3c4bd55e664052d630d49598047ce715bd59d5c5479ee0f237c9bd6
BLAKE2b-256 checksum
How to use checksums
d7dbbcc70e29a18b71410478b2b4f9af3087efce6314a8243c649ea2250e1038
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 25, 2026.

Transparency log

Release files / words2num2-0.3.3-cp38-abi3-macosx_11_0_arm64.whl

Download URL words2num2-0.3.3-cp38-abi3-macosx_11_0_arm64.whl
Size 2.4 MB
Tags CPython 3.8 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
a3b5fa3e514a79bbe854e8fc78f986e9e3534b8282425fee0c6e4254861e32a7
BLAKE2b-256 checksum
How to use checksums
a64ad5c4ae8661d1d66b852e6f15e2e5b97e39a156070579ca60692ccd378ef6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 25, 2026.

Transparency log

Release files / words2num2-0.3.3-cp38-abi3-macosx_10_12_x86_64.whl

Download URL words2num2-0.3.3-cp38-abi3-macosx_10_12_x86_64.whl
Size 2.6 MB
Tags CPython 3.8 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
93b670a126848a9f9a439be13988eb98a7c513f2997bf516de06de30c8c9aedd
BLAKE2b-256 checksum
How to use checksums
7f48ec539b9110c8e006677685ffa9f5f0d99c02c510ddc8b1a62fa0c8cc051c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

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 Sep 25, 2026.

Transparency log
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