Functional Weave
Code in Rust

text.number-to-words@1.0.0

impl/python.py

1,550 bytes · the Python implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

from typing import Dict, List, Tuple

from .money_amount import Money  ← from money.amount ^1.0.0 · built alongside by fune
from .text_integer_to_words import integer_to_words  ← from text.integer-to-words ^1.0.0 · built alongside by fune

#: English unit names: (major one, major many, minor one, minor many). All
#: three currencies have two decimal places, so the split below is by 100. A
#: currency is not added here without its real unit names: they cannot be
#: derived from a code.
_UNITS: Dict[str, Tuple[str, str, str, str]] = {
    "GBP": ("pound", "pounds", "penny", "pence"),
    "EUR": ("euro", "euros", "cent", "cents"),
    "USD": ("dollar", "dollars", "cent", "cents"),
}


def amount_in_words(amount: Money) -> str:
    """Write a money amount in words, cheque style: 123456 GBP minor units is
    "one thousand two hundred and thirty-four pounds and fifty-six pence".
    """
    units = _UNITS.get(amount.currency)
    if units is None:
        raise ValueError('no words for currency "%s"' % (amount.currency,))
    major_one, major_many, minor_one, minor_many = units

    # Validate the whole amount first, so an out-of-range value fails with the
    # same message it would as a plain number.
    integer_to_words(amount.minor)

    major, minor = divmod(abs(amount.minor), 100)

    parts: List[str] = []
    if major > 0 or minor == 0:
        parts.append("%s %s" % (integer_to_words(major), major_one if major == 1 else major_many))
    if minor > 0:
        parts.append("%s %s" % (integer_to_words(minor), minor_one if minor == 1 else minor_many))

    body = " and ".join(parts)
    return "minus " + body if amount.minor < 0 else body