from typing import Dict, List, Tuple from .money_amount import Money from .text_integer_to_words import integer_to_words #: 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