From 75eec68f3ed8d7f6ec9e316ec994a98d879ad166 Mon Sep 17 00:00:00 2001 From: Antoine Planchot Date: Fri, 14 May 2021 22:31:46 +0200 Subject: [PATCH] Added support for Esperanto numbers. --- num2words/__init__.py | 1 + num2words/lang_EO.py | 130 +++++++++++++++++++++++++++++ tests/test_eo.py | 190 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 321 insertions(+) create mode 100644 num2words/lang_EO.py create mode 100644 tests/test_eo.py diff --git a/num2words/__init__.py b/num2words/__init__.py index 3c9171a..acff85c 100644 --- a/num2words/__init__.py +++ b/num2words/__init__.py @@ -38,6 +38,7 @@ CONVERTER_CLASSES = { 'fr_DZ': lang_FR_DZ.Num2Word_FR_DZ(), 'de': lang_DE.Num2Word_DE(), 'fi': lang_FI.Num2Word_FI(), + 'eo': lang_EO.Num2Word_EO(), 'es': lang_ES.Num2Word_ES(), 'es_CO': lang_ES_CO.Num2Word_ES_CO(), 'es_NI': lang_ES_NI.Num2Word_ES_NI(), diff --git a/num2words/lang_EO.py b/num2words/lang_EO.py new file mode 100644 index 0000000..473e74d --- /dev/null +++ b/num2words/lang_EO.py @@ -0,0 +1,130 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Savoir-faire Linux inc. All Rights Reserved. + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA + +from __future__ import print_function, unicode_literals + +from .base import Num2Word_Base + + +class Num2Word_EO(Num2Word_Base): + CURRENCY_FORMS = { + "EUR": (("eŭro", "eŭroj"), ("centimo", "centimoj")), + "USD": (("dolaro", "dolaroj"), ("cendo", "cendoj")), + "FRF": (("franko", "frankoj"), ("centimo", "centimoj")), + "GBP": (("pundo", "pundoj"), ("penco", "pencoj")), + "CNY": (("juano", "juanoj"), ("feno", "fenoj")), + } + + GIGA_SUFFIX = "iliardo" + MEGA_SUFFIX = "iliono" + + def set_high_numwords(self, high): + cap = 3 + 6 * len(high) + + for word, n in zip(high, range(cap, 3, -6)): + if self.GIGA_SUFFIX: + self.cards[10 ** n] = word + self.GIGA_SUFFIX + + if self.MEGA_SUFFIX: + self.cards[10 ** (n - 3)] = word + self.MEGA_SUFFIX + + def gen_high_numwords(self, units, tens, lows): + out = [u + t for t in tens for u in units] + out.reverse() + return out + lows + + def setup(self): + lows = ["naŭ", "ok", "sep", "ses", "kvin", "kvar", "tr", "b", "m"] + units = ["", "un", "duo", "tre", "kvatuor", + "kvin", "seks", "septen", "okto", "novem"] + tens = ["dek", "vigint", "trigint", "kvadragint", "kvinkvagint", + "seksagint", "septuagint", "oktogint", "nonagint"] + + self.high_numwords = ["cent"] + self.gen_high_numwords(units, tens, + lows) + + self.negword = "minus " + self.pointword = "komo" + self.errmsg_nonnum = u"Sole nombroj povas esti konvertita en vortojn." + self.errmsg_toobig = ( + u"Tro granda nombro por esti konvertita en vortojn." + ) + self.exclude_title = ["kaj", "komo", "minus"] + self.mid_numwords = [(1000, "mil"), (100, "cent"), (90, "naŭdek"), + (80, "okdek"), (70, "sepdek"), (60, "sesdek"), + (50, "kvindek"), (40, "kvardek"), (30, "tridek")] + self.low_numwords = ["dudek", "dek naŭ", "dek ok", "dek sep", + "dek ses", "dek kvin", "dek kvar", "dek tri", + "dek du", "dek unu", "dek", "naŭ", "ok", "sep", + "ses", "kvin", "kvar", "tri", "du", "unu", "nul"] + self.ords = { + "unu": "unua", + "du": "dua", + "tri": "tria", + "kvar": "kvara", + "kvin": "kvina", + "ses": "sesa", + "sep": "sepa", + "ok": "oka", + "naŭ": "naŭa", + "dek": "deka" + } + + def merge(self, curr, next): + ctext, cnum, ntext, nnum = curr + next + if cnum == 1 and nnum < 1000000: + return next + + if nnum >= 10**6 and cnum > 1: + return ("%s %sj" % (ctext, ntext), cnum + nnum) + + if nnum == 100: + return ("%s%s" % (ctext, ntext), cnum + nnum) + + return ("%s %s" % (ctext, ntext), cnum + nnum) + + def to_ordinal(self, value): + self.verify_ordinal(value) + word = self.to_cardinal(value) + for src, repl in self.ords.items(): + if word.endswith(src): + word = word[:-len(src)] + repl + return word + + if word.endswith("o"): + word = word[:-1] + "a" + elif word.endswith("oj"): + word = word[:-2] + "a" + else: + word = word + "a" + return word + + def to_ordinal_num(self, value): + self.verify_ordinal(value) + out = str(value) + out += "a" + return out + + def to_currency(self, val, currency="EUR", cents=True, separator=" kaj", + adjective=False): + result = super(Num2Word_EO, self).to_currency( + val, currency=currency, cents=cents, separator=separator, + adjective=adjective) + return result + + def pluralize(self, n, forms): + form = 0 if n <= 1 else 1 + return forms[form] diff --git a/tests/test_eo.py b/tests/test_eo.py new file mode 100644 index 0000000..64076ce --- /dev/null +++ b/tests/test_eo.py @@ -0,0 +1,190 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2021, Savoir-faire Linux inc. All Rights Reserved. + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA + +from __future__ import unicode_literals + +from unittest import TestCase + +from num2words import num2words + +TEST_CASES_CARDINAL = ( + (1, "unu"), + (2, "du"), + (3, "tri"), + (5.5, "kvin komo kvin"), + (11, "dek unu"), + (12, "dek du"), + (16, "dek ses"), + (17.42, "dek sep komo kvar du"), + (19, "dek naŭ"), + (20, "dudek"), + (21, "dudek unu"), + (26, "dudek ses"), + (27.312, "dudek sep komo tri unu du"), + (28, "dudek ok"), + (30, "tridek"), + (31, "tridek unu"), + (40, "kvardek"), + (44, "kvardek kvar"), + (50, "kvindek"), + (53.486, "kvindek tri komo kvar ok ses"), + (55, "kvindek kvin"), + (60, "sesdek"), + (67, "sesdek sep"), + (70, "sepdek"), + (79, "sepdek naŭ"), + (89, "okdek naŭ"), + (95, "naŭdek kvin"), + (100, "cent"), + (101, "cent unu"), + (199, "cent naŭdek naŭ"), + (203, "ducent tri"), + (287, "ducent okdek sep"), + (300.42, "tricent komo kvar du"), + (356, "tricent kvindek ses"), + (400, "kvarcent"), + (434, "kvarcent tridek kvar"), + (578, "kvincent sepdek ok"), + (689, "sescent okdek naŭ"), + (729, "sepcent dudek naŭ"), + (894, "okcent naŭdek kvar"), + (999, "naŭcent naŭdek naŭ"), + (1000, "mil"), + (1001, "mil unu"), + (1097, "mil naŭdek sep"), + (1104, "mil cent kvar"), + (1243, "mil ducent kvardek tri"), + (2385, "du mil tricent okdek kvin"), + (3766, "tri mil sepcent sesdek ses"), + (4196, "kvar mil cent naŭdek ses"), + (4196.42, "kvar mil cent naŭdek ses komo kvar du"), + (5846, "kvin mil okcent kvardek ses"), + (6459, "ses mil kvarcent kvindek naŭ"), + (7232, "sep mil ducent tridek du"), + (8569, "ok mil kvincent sesdek naŭ"), + (9539, "naŭ mil kvincent tridek naŭ"), + (1000000, "unu miliono"), + (1000001, "unu miliono unu"), + (4000000, "kvar milionoj"), + (4000004, "kvar milionoj kvar"), + (4300000, "kvar milionoj tricent mil"), + (80000000, "okdek milionoj"), + (300000000, "tricent milionoj"), + (10000000000000, "dek bilionoj"), + (10000000000010, "dek bilionoj dek"), + (100000000000000, "cent bilionoj"), + (1000000000000000000, "unu triliono"), + (1000000000000000000000, "unu triliardo"), + (10000000000000000000000000, "dek kvarilionoj") +) + +TEST_CASES_ORDINAL = ( + (1, "unua"), + (8, "oka"), + (12, "dek dua"), + (14, "dek kvara"), + (28, "dudek oka"), + (100, "centa"), + (1000, "mila"), + (1000000, "unu miliona"), + (1000000000000000, "unu biliarda"), + (1000000000000000000, "unu triliona") +) + +TEST_CASES_ORDINAL_NUM = ( + (1, "1a"), + (8, "8a"), + (11, "11a"), + (12, "12a"), + (14, "14a"), + (21, "21a"), + (28, "28a"), + (100, "100a"), + (101, "101a"), + (1000, "1000a"), + (1000000, "1000000a") +) + +TEST_CASES_TO_CURRENCY_EUR = ( + (1.00, "unu eŭro kaj nul centimo"), + (2.01, "du eŭroj kaj unu centimo"), + (8.10, "ok eŭroj kaj dek centimoj"), + (12.26, "dek du eŭroj kaj dudek ses centimoj"), + (21.29, "dudek unu eŭroj kaj dudek naŭ centimoj"), + (81.25, "okdek unu eŭroj kaj dudek kvin centimoj"), + (100.00, "cent eŭroj kaj nul centimo"), +) + +TEST_CASES_TO_CURRENCY_FRF = ( + (1.00, "unu franko kaj nul centimo"), + (2.01, "du frankoj kaj unu centimo"), + (8.10, "ok frankoj kaj dek centimoj"), + (12.27, "dek du frankoj kaj dudek sep centimoj"), + (21.29, "dudek unu frankoj kaj dudek naŭ centimoj"), + (81.25, "okdek unu frankoj kaj dudek kvin centimoj"), + (100.00, "cent frankoj kaj nul centimo"), +) + +TEST_CASES_TO_CURRENCY_USD = ( + (1.00, "unu dolaro kaj nul cendo"), + (2.01, "du dolaroj kaj unu cendo"), + (8.10, "ok dolaroj kaj dek cendoj"), + (12.26, "dek du dolaroj kaj dudek ses cendoj"), + (21.29, "dudek unu dolaroj kaj dudek naŭ cendoj"), + (81.25, "okdek unu dolaroj kaj dudek kvin cendoj"), + (100.00, "cent dolaroj kaj nul cendo"), +) + + +class Num2WordsEOTest(TestCase): + def test_number(self): + for test in TEST_CASES_CARDINAL: + self.assertEqual(num2words(test[0], lang="eo"), test[1]) + + def test_ordinal(self): + for test in TEST_CASES_ORDINAL: + self.assertEqual( + num2words(test[0], lang="eo", ordinal=True), + test[1] + ) + + def test_ordinal_num(self): + for test in TEST_CASES_ORDINAL_NUM: + self.assertEqual( + num2words(test[0], lang="eo", to="ordinal_num"), + test[1] + ) + + def test_currency_eur(self): + for test in TEST_CASES_TO_CURRENCY_EUR: + self.assertEqual( + num2words(test[0], lang="eo", to="currency", currency="EUR"), + test[1] + ) + + def test_currency_frf(self): + for test in TEST_CASES_TO_CURRENCY_FRF: + self.assertEqual( + num2words(test[0], lang="eo", to="currency", currency="FRF"), + test[1] + ) + + def test_currency_usd(self): + for test in TEST_CASES_TO_CURRENCY_USD: + self.assertEqual( + num2words(test[0], lang="eo", to="currency", currency="USD"), + test[1] + )