num2words converts numbers to their word representations. pip install num2words. Basic: from num2words import num2words. num2words(42) → “forty-two”. num2words(1234567) → “one million, two hundred and thirty-four thousand, five hundred and sixty-seven”. Ordinal: num2words(3, to="ordinal") → “third”. num2words(21, to="ordinal") → “twenty-first”. Ordinal num: num2words(21, to="ordinal_num") → “21st”. Year: num2words(1999, to="year") → “nineteen ninety-nine”. Currency: num2words(1234.56, to="currency", lang="en_GB") → “one thousand, two hundred and thirty-four pounds, fifty-six pence”. Lang: num2words(42, lang="de") → “zweiundvierzig”. num2words(42, lang="fr") → “quarante-deux”. num2words(42, lang="es") → “cuarenta y dos”. num2words(42, lang="ru") → “сорок два”. num2words(42, lang="ar") → Arabic text. num2words(42, lang="ja") → “四十二”. Available: en en_GB de fr es it pt ru tr ar uk ko ja zh. Negative: num2words(-5) → “minus five”. Float: num2words(3.14) → “three point one four”. num2words(0.5) → “zero point five”. Currency with denominator: num2words(10.50, to="currency", currency="USD"). Claude Code generates num2words converters, invoice amount writers, and multilingual number renderers.
CLAUDE.md for num2words
## num2words Stack
- Version: num2words >= 0.5.13 | pip install num2words
- Cardinal: num2words(1234) → "one thousand two hundred and thirty-four"
- Ordinal: num2words(3, to="ordinal") → "third" | to="ordinal_num" → "3rd"
- Year: num2words(1999, to="year") → "nineteen ninety-nine"
- Currency: num2words(99.50, to="currency", lang="en_GB") — amount in words
- Lang: lang="de"/"fr"/"es"/"ru"/"ar"/"ja" — 40+ languages supported
- Jinja2: env.filters["num2words"] = lambda n, **kw: num2words(n, **kw)
num2words Number-to-Words Pipeline
# app/number_words.py — num2words cardinal, ordinal, currency, and multilingual
from __future__ import annotations
from typing import Any
from num2words import num2words, CONVERTER_CLASSES
# ─────────────────────────────────────────────────────────────────────────────
# 1. Core conversion helpers
# ─────────────────────────────────────────────────────────────────────────────
def to_words(number: int | float, lang: str = "en") -> str:
"""
Convert a number to its cardinal word form.
num2words(1234567) → "one million, two hundred and thirty-four thousand,
five hundred and sixty-seven"
"""
return num2words(number, lang=lang)
def to_ordinal(number: int, lang: str = "en") -> str:
"""
Convert a number to its ordinal word form.
to="ordinal": 3 → "third", 21 → "twenty-first"
Supports ordinal in German (dritte), French (troisième), Spanish (tercero), etc.
"""
return num2words(number, to="ordinal", lang=lang)
def to_ordinal_num(number: int, lang: str = "en") -> str:
"""
Convert a number to ordinal numeral form (suffix style).
to="ordinal_num": 1 → "1st", 2 → "2nd", 21 → "21st"
"""
return num2words(number, to="ordinal_num", lang=lang)
def to_year(year: int, lang: str = "en") -> str:
"""
Convert a year to its spoken form.
1999 → "nineteen ninety-nine"
2024 → "twenty twenty-four"
1900 → "nineteen hundred"
"""
return num2words(year, to="year", lang=lang)
def to_currency_words(
amount: float,
currency: str = "USD",
lang: str = "en",
) -> str:
"""
Convert a monetary amount to words.
Used on checks, invoices, and legal documents.
98.50 USD → "ninety-eight dollars, fifty cents"
1234.56 EUR → "one thousand, two hundred and thirty-four euros, fifty-six cents"
"""
return num2words(amount, to="currency", currency=currency, lang=lang)
# ─────────────────────────────────────────────────────────────────────────────
# 2. Invoice / check amount formatter
# ─────────────────────────────────────────────────────────────────────────────
def invoice_amount_words(
amount: float,
currency: str = "USD",
lang: str = "en",
capitalize: bool = True,
) -> str:
"""
Format an invoice amount as words for check writing or legal documents.
Returns e.g. "One Thousand Two Hundred Forty-Five Dollars and 50/100"
"""
words = to_currency_words(amount, currency=currency, lang=lang)
if capitalize:
words = words.capitalize()
# Add "and XX/100" suffix common on US checks
if lang == "en" and currency in ("USD", "CAD"):
cents = round((amount % 1) * 100)
# Remove the "cents" part from num2words and replace with xx/100
# num2words format: "X dollars, Y cents" → strip after comma
if "," in words:
dollar_part = words.split(",")[0]
return f"{dollar_part} and {cents:02d}/100"
return words
def check_line(amount: float, currency: str = "USD") -> str:
"""Format a check amount line: '**One Thousand Two Hundred and 50/100**'"""
words = invoice_amount_words(amount, currency=currency)
return f"**{words}**"
# ─────────────────────────────────────────────────────────────────────────────
# 3. Multilingual number conversion
# ─────────────────────────────────────────────────────────────────────────────
SUPPORTED_LANGS = sorted(CONVERTER_CLASSES.keys())
def to_words_multilingual(number: int | float) -> dict[str, str]:
"""
Convert a number to words in all supported languages.
Returns {lang_code: word_string}.
"""
results = {}
for lang in SUPPORTED_LANGS:
try:
results[lang] = num2words(number, lang=lang)
except Exception:
pass
return results
def to_words_langs(number: int | float, langs: list[str]) -> dict[str, str]:
"""Convert a number to words in a specific list of languages."""
results = {}
for lang in langs:
try:
results[lang] = num2words(number, lang=lang)
except Exception as e:
results[lang] = f"(error: {e})"
return results
# ─────────────────────────────────────────────────────────────────────────────
# 4. Ordinal suffix helper (for format strings)
# ─────────────────────────────────────────────────────────────────────────────
def ordinal_suffix(n: int) -> str:
"""
Return the ordinal suffix for an integer: 1 → "st", 2 → "nd", 3 → "rd", 4+ → "th".
This is the English-only quick version; use num2words for international ordinals.
"""
if 11 <= (n % 100) <= 13:
return "th"
return {1: "st", 2: "nd", 3: "rd"}.get(n % 10, "th")
def with_ordinal_suffix(n: int) -> str:
"""Return e.g. '21st', '22nd', '3rd', '11th'."""
return f"{n}{ordinal_suffix(n)}"
# ─────────────────────────────────────────────────────────────────────────────
# 5. Sequence word generators
# ─────────────────────────────────────────────────────────────────────────────
def ordinal_list(n: int, lang: str = "en") -> list[str]:
"""Return ordinal words for 1..n: ["first", "second", ..., "nth"]."""
return [num2words(i, to="ordinal", lang=lang) for i in range(1, n + 1)]
def numbered_items(items: list[str], lang: str = "en") -> list[str]:
"""Prefix list items with their ordinal number in words."""
return [
f"{num2words(i, to='ordinal', lang=lang).capitalize()}: {item}"
for i, item in enumerate(items, 1)
]
# ─────────────────────────────────────────────────────────────────────────────
# 6. Jinja2 filter registration
# ─────────────────────────────────────────────────────────────────────────────
def register_num2words_filters(env) -> None:
"""
Register num2words as Jinja2 filters.
Template usage:
{{ invoice.total | currency_words("USD") }}
{{ rank | ordinal }}
{{ year | year_words }}
{{ 42 | n2w }}
"""
env.filters["n2w"] = lambda n, lang="en": to_words(n, lang=lang)
env.filters["ordinal"] = lambda n, lang="en": to_ordinal(n, lang=lang)
env.filters["ordinal_num"] = lambda n: to_ordinal_num(n)
env.filters["year_words"] = lambda y, lang="en": to_year(y, lang=lang)
env.filters["currency_words"] = lambda n, curr="USD", lang="en": to_currency_words(n, currency=curr, lang=lang)
# ─────────────────────────────────────────────────────────────────────────────
# Demo
# ─────────────────────────────────────────────────────────────────────────────
if __name__ == "__main__":
print("=== Cardinal numbers ===")
for n in [0, 1, 13, 42, 100, 1_000, 1_234_567, -7, 3.14]:
print(f" {str(n):12} → {num2words(n)}")
print("\n=== Ordinals (en) ===")
for n in [1, 2, 3, 4, 11, 12, 13, 21, 22, 100]:
print(f" {n:5} → ordinal={num2words(n, to='ordinal'):20} "
f"ordinal_num={num2words(n, to='ordinal_num')}")
print("\n=== Years ===")
for y in [1066, 1776, 1900, 1999, 2000, 2024]:
print(f" {y} → {num2words(y, to='year')}")
print("\n=== Currencies ===")
amounts = [(99.50, "USD", "en"), (1234.56, "EUR", "de"),
(9999.99, "GBP", "en_GB"), (500.00, "JPY", "ja")]
for amount, curr, lang in amounts:
print(f" {amount} {curr}/{lang:6} → {to_currency_words(amount, currency=curr, lang=lang)}")
print("\n=== Invoice check line ===")
for amount in [99.50, 1_234.56, 100_000.00]:
print(f" ${amount:>12,.2f} → {check_line(amount)}")
print("\n=== Multilingual (42) ===")
DEMO_LANGS = ["en", "de", "fr", "es", "it", "ru", "ar", "ja", "ko", "zh"]
ml = to_words_langs(42, DEMO_LANGS)
for lang, words in ml.items():
print(f" {lang:5} → {words}")
print("\n=== Ordinal list (first 5) ===")
for w in ordinal_list(5):
print(f" {w}")
print("\n=== Numbered items ===")
tasks = ["Parse input", "Validate schema", "Process records", "Write output"]
for item in numbered_items(tasks):
print(f" {item}")
For the inflect alternative — inflect handles pluralization, article selection (“a” vs “an”), and some number → word conversion, but its number conversion is limited to English and doesn’t support ordinals in other languages; num2words covers 40+ languages with lang="de"/"fr"/"es"/"ru"/"ja" etc., supports to="ordinal", to="year", and to="currency" formats, and is the standard choice when you need multilingual number text output. For the humanize.apnumber alternative — humanize.apnumber(3) → “three” handles 0–9 in AP style and returns the numeral for 10 and above; num2words handles arbitrarily large integers, floats, negative numbers, ordinals, years, and currencies in 40+ languages — the right choice when “forty-two” or “일천이백삼십사” is what you need. The Claude Skills 360 bundle includes num2words skill sets covering num2words() cardinal conversion, to=“ordinal” for word ordinals, to=“ordinal_num” for suffix ordinals (1st/2nd/3rd), to=“year” for year reading, to=“currency” with currency code, to_words_multilingual() for all languages, to_words_langs() for selected languages, invoice_amount_words() with check format, check_line() formatter, ordinal_suffix() English suffix helper, ordinal_list() generator, numbered_items() list formatter, and Jinja2 filter registration. Start with the free tier to try number-to-words conversion code generation.