From 797e74d1fc342704d68be15bc69b44f408fed89e Mon Sep 17 00:00:00 2001 From: isnani Date: Sat, 12 Dec 2015 16:54:30 +0100 Subject: [PATCH 01/31] correction on lang_DE: ordnum, plural and ord form of >= 10**6 --- num2words/base.py | 1 - num2words/lang_DE.py | 45 ++++++++++++++++++++++---------------------- tests/test_de.py | 38 +++++++++++++++++++++++++++++++++++++ 3 files changed, 61 insertions(+), 23 deletions(-) create mode 100644 tests/test_de.py diff --git a/num2words/base.py b/num2words/base.py index f892d42..b094c67 100644 --- a/num2words/base.py +++ b/num2words/base.py @@ -101,7 +101,6 @@ class Num2Word_Base(object): if value >= self.MAXVAL: raise OverflowError(self.errmsg_toobig % (value, self.MAXVAL)) - val = self.splitnum(value) words, num = self.clean(val) diff --git a/num2words/lang_DE.py b/num2words/lang_DE.py index e31dc5b..62b67fb 100644 --- a/num2words/lang_DE.py +++ b/num2words/lang_DE.py @@ -26,7 +26,6 @@ class Num2Word_DE(Num2Word_EU): self.cards[10**n] = word + "illiarde" self.cards[10**(n-3)] = word + "illion" - def setup(self): self.negword = "minus " self.pointword = "Komma" @@ -49,14 +48,19 @@ class Num2Word_DE(Num2Word_EU): "zw\xF6lf", "elf", "zehn", "neun", "acht", "sieben", "sechs", "f\xFCnf", "vier", "drei", "zwei", "eins", "null"] - self.ords = { "eins" : "ers", - "drei" : "drit", - "acht" : "ach", - "sieben" : "sieb", - "ig" : "igs" } + self.ords = {"eins": "ers", + "drei": "drit", + "acht": "ach", + "sieben": "sieb", + "ig": "igs", + "ert": "erts", + "end": "ends", + "ion": "ions", + "nen": "nens", + "rde": "rdes", + "rden": "rdens"} self.ordflag = False - def merge(self, curr, next): ctext, cnum, ntext, nnum = curr + next @@ -69,41 +73,35 @@ class Num2Word_DE(Num2Word_EU): if nnum >= 10**6: if cnum > 1: if ntext.endswith("e") or self.ordflag: - ntext += "s" + ntext += "n" else: - ntext += "es" + ntext += "en" ctext += " " val = cnum * nnum else: if nnum < 10 < cnum < 100: if nnum == 1: ntext = "ein" - ntext, ctext = ctext, ntext + "und" + ntext, ctext = ctext, ntext + "und" elif cnum >= 10**6: ctext += " " val = cnum + nnum word = ctext + ntext return (word, val) - def to_ordinal(self, value): self.verify_ordinal(value) - self.ordflag = True outword = self.to_cardinal(value) - self.ordflag = False for key in self.ords: if outword.endswith(key): outword = outword[:len(outword) - len(key)] + self.ords[key] break return outword + "te" - - # Is this correct?? def to_ordinal_num(self, value): self.verify_ordinal(value) - return str(value) + "te" - + return str(value) + "." def to_currency(self, val, longval=True, old=False): if old: @@ -117,8 +115,6 @@ class Num2Word_DE(Num2Word_EU): return self.to_cardinal(val) return self.to_splitnum(val, hightxt="hundert", longval=longval) - - n2w = Num2Word_DE() to_card = n2w.to_cardinal to_ord = n2w.to_ordinal @@ -126,15 +122,20 @@ to_ordnum = n2w.to_ordinal_num def main(): - for val in [ 1, 11, 12, 21, 31, 33, 71, 80, 81, 91, 99, 100, 101, 102, 155, + for val in [1, 7, 8, 12, 17, 81, 91, 99, 100, 101, 102, 155, 180, 300, 308, 832, 1000, 1001, 1061, 1100, 1500, 1701, 3000, - 8280, 8291, 150000, 500000, 1000000, 2000000, 2000001, + 8280, 8291, 150000, 500000, 3000000, 1000000, 2000001, 1000000000, 2000000000, -21212121211221211111, -2.121212, -1.0000100]: n2w.test(val) - n2w.test(1325325436067876801768700107601001012212132143210473207540327057320957032975032975093275093275093270957329057320975093272950730) + # n2w.test(1325325436067876801768700107601001012212132143210473207540327057320957032975032975093275093275093270957329057320975093272950730) + n2w.test(3000000) + n2w.test(3000000000001) + n2w.test(3000000324566) print n2w.to_currency(112121) print n2w.to_year(2000) + print n2w.to_year(1820) + print n2w.to_year(2001) if __name__ == "__main__": main() diff --git a/tests/test_de.py b/tests/test_de.py new file mode 100644 index 0000000..c5b4402 --- /dev/null +++ b/tests/test_de.py @@ -0,0 +1,38 @@ +# -*- encoding: utf-8 -*- +# Copyright (c) 2015, 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 + +class Num2WordsDETest(TestCase): + def test_ordinal_less_than_twenty(self): + self.assertEqual(num2words(7, ordinal=True, lang='de'), "siebte") + self.assertEqual(num2words(8, ordinal=True, lang='de'), "achte") + self.assertEqual(num2words(12, ordinal=True, lang='de'), "zwölfte") + self.assertEqual(num2words(17, ordinal=True, lang='de'), "siebzehnte") + + def test_ordinal_more_than_twenty(self): + self.assertEqual(num2words(81, ordinal=True, lang='de'), "einundachtzigste") + + def test_ordinal_at_crucial_number(self): + self.assertEqual(num2words(100, ordinal=True, lang='de'), "hundertste") + self.assertEqual(num2words(1000, ordinal=True, lang='de'), "tausendste") + self.assertEqual(num2words(4000, ordinal=True, lang='de'), "viertausendste") + self.assertEqual(num2words(2000000, ordinal=True, lang='de'), "zwei millionenste") + self.assertEqual(num2words(5000000000, ordinal=True, lang='de'), "fünf milliardenste") \ No newline at end of file From 41a97d693194904fc7f556949572c8da1bda594b Mon Sep 17 00:00:00 2001 From: isnani Date: Mon, 14 Dec 2015 08:26:08 +0100 Subject: [PATCH 02/31] add tests for lang_DE --- tests/test_de.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/tests/test_de.py b/tests/test_de.py index c5b4402..98e34f2 100644 --- a/tests/test_de.py +++ b/tests/test_de.py @@ -35,4 +35,17 @@ class Num2WordsDETest(TestCase): self.assertEqual(num2words(1000, ordinal=True, lang='de'), "tausendste") self.assertEqual(num2words(4000, ordinal=True, lang='de'), "viertausendste") self.assertEqual(num2words(2000000, ordinal=True, lang='de'), "zwei millionenste") - self.assertEqual(num2words(5000000000, ordinal=True, lang='de'), "fünf milliardenste") \ No newline at end of file + self.assertEqual(num2words(5000000000, ordinal=True, lang='de'), "fünf milliardenste") + + def test_cardinal_at_some_numbers(self): + self.assertEqual(num2words(2000000, lang='de'), "zwei millionen") + self.assertEqual(num2words(4000000000, lang='de'), "vier milliarden") + + def test_cardinal_for_decimal_number(self): + self.assertEqual(num2words(3.486, lang='de'), "drei Komma vier acht") + + def test_ordinal_for_negative_numbers(self): + self.assertRaises(TypeError, num2words, -12, ordinal=True, lang='de') + + def test_ordinal_for_floating_numbers(self): + self.assertRaises(TypeError, num2words, 2.453, ordinal=True, lang='de') \ No newline at end of file From 9d99647e17ef1f2e3e521393a9bbd4b0bc7cdbe6 Mon Sep 17 00:00:00 2001 From: isnani Date: Mon, 14 Dec 2015 08:40:32 +0100 Subject: [PATCH 03/31] remove unnecessary self.ordflag --- num2words/lang_DE.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/num2words/lang_DE.py b/num2words/lang_DE.py index 62b67fb..c9f73eb 100644 --- a/num2words/lang_DE.py +++ b/num2words/lang_DE.py @@ -59,20 +59,19 @@ class Num2Word_DE(Num2Word_EU): "nen": "nens", "rde": "rdes", "rden": "rdens"} - self.ordflag = False def merge(self, curr, next): ctext, cnum, ntext, nnum = curr + next if cnum == 1: - if nnum < 10**6 or self.ordflag: + if nnum < 10**6: return next ctext = "eine" if nnum > cnum: if nnum >= 10**6: if cnum > 1: - if ntext.endswith("e") or self.ordflag: + if ntext.endswith("e"): ntext += "n" else: ntext += "en" From 3cafa38ddb9d7472117923025e7f2f5f1f533702 Mon Sep 17 00:00:00 2001 From: AntonKorobkov Date: Thu, 4 Aug 2016 18:47:13 +0300 Subject: [PATCH 04/31] added Russian language test with some basic coverage --- tests/test_ru.py | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 tests/test_ru.py diff --git a/tests/test_ru.py b/tests/test_ru.py new file mode 100644 index 0000000..fc6edce --- /dev/null +++ b/tests/test_ru.py @@ -0,0 +1,27 @@ +# -*- encoding: utf-8 -*- +# Copyright (c) 2013, 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 unittest import TestCase + +from num2words import num2words + +class Num2WordsRUTest(TestCase): + + def test_cardinal(self): + self.assertEqual(num2words(5, lang='ru'), u"пять") + self.assertEqual(num2words(15, lang='ru'), u"пятнадцать") + self.assertEqual(num2words(154, lang='ru'), u"сто пятьдесят четыре") + self.assertEqual(num2words(418531, lang='ru'), u"четыреста восемнадцать тысяч пятьсот тридцать один") \ No newline at end of file From a63cee01dac047bd3718c4f064f4362dfce065f0 Mon Sep 17 00:00:00 2001 From: AntonKorobkov Date: Sun, 7 Aug 2016 20:28:14 +0300 Subject: [PATCH 05/31] floating point numbers test added --- tests/test_ru.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/tests/test_ru.py b/tests/test_ru.py index fc6edce..9ba224b 100644 --- a/tests/test_ru.py +++ b/tests/test_ru.py @@ -24,4 +24,8 @@ class Num2WordsRUTest(TestCase): self.assertEqual(num2words(5, lang='ru'), u"пять") self.assertEqual(num2words(15, lang='ru'), u"пятнадцать") self.assertEqual(num2words(154, lang='ru'), u"сто пятьдесят четыре") - self.assertEqual(num2words(418531, lang='ru'), u"четыреста восемнадцать тысяч пятьсот тридцать один") \ No newline at end of file + self.assertEqual(num2words(418531, lang='ru'), u"четыреста восемнадцать тысяч пятьсот тридцать один") + + def test_floating_point(self): + self.assertEqual(num2words(5.2, lang='ru'), u"пять запятая два") + self.assertEqual(num2words(561.42, lang='ru'), u"пятьсот шестьдесят один запятая сорок два") From 4e5c07522c3d9afc52b07389c933c5ff17c1d255 Mon Sep 17 00:00:00 2001 From: Magno Costa - Akretion Date: Tue, 9 Aug 2016 18:30:46 -0300 Subject: [PATCH 06/31] In Brazilian Portuguese the decimal sign is comma ',' instead of dot '.'. Ex.: we use 1,01 instead of 1.01 --- num2words/lang_PT_BR.py | 4 ++-- tests/test_pt_BR.py | 20 ++++++++++---------- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/num2words/lang_PT_BR.py b/num2words/lang_PT_BR.py index bf4a8f3..88a8416 100644 --- a/num2words/lang_PT_BR.py +++ b/num2words/lang_PT_BR.py @@ -30,9 +30,9 @@ class Num2Word_PT_BR(lang_EU.Num2Word_EU): def setup(self): self.negword = "menos " - self.pointword = "ponto" + self.pointword = "vírgula" self.errmsg_nornum = "Somente números podem ser convertidos para palavras" - self.exclude_title = ["e", "ponto", "menos"] + self.exclude_title = ["e", "vírgula", "menos"] self.mid_numwords = [ (1000, "mil"), (100, "cem"), (90, "noventa"), diff --git a/tests/test_pt_BR.py b/tests/test_pt_BR.py index d2f4b22..9a82481 100644 --- a/tests/test_pt_BR.py +++ b/tests/test_pt_BR.py @@ -81,18 +81,18 @@ class Num2WordsPTBRTest(TestCase): def test_cardinal_float(self): self.assertEquals(num2words(Decimal('1.00'), lang='pt_BR'), 'um') - self.assertEquals(num2words(Decimal('1.01'), lang='pt_BR'), 'um ponto zero um') - self.assertEquals(num2words(Decimal('1.035'), lang='pt_BR'), 'um ponto zero três') - self.assertEquals(num2words(Decimal('1.35'), lang='pt_BR'), 'um ponto três cinco') - self.assertEquals(num2words(Decimal('3.14159'), lang='pt_BR'), 'três ponto um quatro') - self.assertEquals(num2words(Decimal('101.22'), lang='pt_BR'), 'cento e um ponto dois dois') - self.assertEquals(num2words(Decimal('2345.75'), lang='pt_BR'), 'dois mil, trezentos e quarenta e cinco ponto sete cinco') + self.assertEquals(num2words(Decimal('1.01'), lang='pt_BR'), 'um vírgula zero um') + self.assertEquals(num2words(Decimal('1.035'), lang='pt_BR'), 'um vírgula zero três') + self.assertEquals(num2words(Decimal('1.35'), lang='pt_BR'), 'um vírgula três cinco') + self.assertEquals(num2words(Decimal('3.14159'), lang='pt_BR'), 'três vírgula um quatro') + self.assertEquals(num2words(Decimal('101.22'), lang='pt_BR'), 'cento e um vírgula dois dois') + self.assertEquals(num2words(Decimal('2345.75'), lang='pt_BR'), 'dois mil, trezentos e quarenta e cinco vírgula sete cinco') def test_cardinal_float_negative(self): - self.assertEquals(num2words(Decimal('-2.34'), lang='pt_BR'), 'menos dois ponto três quatro') - self.assertEquals(num2words(Decimal('-9.99'), lang='pt_BR'), 'menos nove ponto nove nove') - self.assertEquals(num2words(Decimal('-7.01'), lang='pt_BR'), 'menos sete ponto zero um') - self.assertEquals(num2words(Decimal('-222.22'), lang='pt_BR'), 'menos duzentos e vinte e dois ponto dois dois') + self.assertEquals(num2words(Decimal('-2.34'), lang='pt_BR'), 'menos dois vírgula três quatro') + self.assertEquals(num2words(Decimal('-9.99'), lang='pt_BR'), 'menos nove vírgula nove nove') + self.assertEquals(num2words(Decimal('-7.01'), lang='pt_BR'), 'menos sete vírgula zero um') + self.assertEquals(num2words(Decimal('-222.22'), lang='pt_BR'), 'menos duzentos e vinte e dois vírgula dois dois') def test_ordinal(self): self.assertEquals(num2words(1, lang='pt_BR', ordinal=True), 'primeiro') From 273349a2347f487d5d8fb4de329ef101a2cf379c Mon Sep 17 00:00:00 2001 From: mr Date: Mon, 15 Aug 2016 13:09:18 +0200 Subject: [PATCH 07/31] Translation of error messages See https://github.com/savoirfairelinux/num2words/pull/45 --- num2words/lang_DE.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/num2words/lang_DE.py b/num2words/lang_DE.py index c9f73eb..252ccb7 100644 --- a/num2words/lang_DE.py +++ b/num2words/lang_DE.py @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- # Copyright (c) 2003, Taro Ogawa. All Rights Reserved. # Copyright (c) 2013, Savoir-faire Linux inc. All Rights Reserved. @@ -17,7 +18,6 @@ from __future__ import unicode_literals from .lang_EU import Num2Word_EU -#//TODO: Use German error messages class Num2Word_DE(Num2Word_EU): def set_high_numwords(self, high): max = 3 + 6*len(high) @@ -29,8 +29,10 @@ class Num2Word_DE(Num2Word_EU): def setup(self): self.negword = "minus " self.pointword = "Komma" - self.errmsg_nonnum = "Only numbers may be converted to words." - self.errmsg_toobig = "Number is too large to convert to words." + self.errmsg_floatord = "Die Gleitkommazahl %s kann nicht in eine Ordnungszahl konvertiert werden." # "Cannot treat float %s as ordinal." + self.errmsg_nonnum = "Nur Zahlen (type(%s)) können in Wörter konvertiert werden." # "type(((type(%s)) ) not in [long, int, float]" + self.errmsg_negord = "Die negative Zahl %s kann nicht in eine Ordnungszahl konvertiert werden." # "Cannot treat negative num %s as ordinal." + self.errmsg_toobig = "Die Zahl %s muss kleiner als %s sein." # "abs(%s) must be less than %s." self.exclude_title = [] lows = ["non", "okt", "sept", "sext", "quint", "quadr", "tr", "b", "m"] From 22072268736c9652c2f478454fcf1def80beaa4d Mon Sep 17 00:00:00 2001 From: Alexey Porotnikov Date: Tue, 13 Sep 2016 10:05:43 +0300 Subject: [PATCH 08/31] add actual Lithuanian currency (euro since 1 January 2015) --- num2words/lang_LT.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/num2words/lang_LT.py b/num2words/lang_LT.py index d70c39b..a274e4a 100644 --- a/num2words/lang_LT.py +++ b/num2words/lang_LT.py @@ -85,7 +85,13 @@ vienas litas, nulis centų vienas tūkstantis du šimtai trisdešimt keturi litai, penkiasdešimt šeši centai >>> print(to_currency(-1251985, cents = False)) -minus dvylika tūkstančių penki šimtai devyniolika litų, 85 centai +minus dvylika tūkstančių penki šimtai devyniolika eurų, 85 centai + +>>> print(to_currency(1.0, 'EUR')) +vienas euras, nulis centų + +>>> print(to_currency(1234.56, 'EUR')) +vienas tūkstantis du šimtai trisdešimt keturi eurai, penkiasdešimt šeši centai """ from __future__ import unicode_literals @@ -144,6 +150,7 @@ THOUSANDS = { CURRENCIES = { 'LTL': ((u'litas', u'litai', u'litų'), (u'centas', u'centai', u'centų')), + 'EUR': ((u'euras', u'eurai', u'eurų'), (u'centas', u'centai', u'centų')), } def splitby3(n): @@ -210,7 +217,7 @@ def n2w(n): else: return int2word(int(n)) -def to_currency(n, currency='LTL', cents = True): +def to_currency(n, currency='EUR', cents = True): if type(n) == int: if n < 0: minus = True From 0e06eb81dde3ccb4a5f35d6eaf08a4c10d1130a8 Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Tue, 18 Oct 2016 09:46:11 -0400 Subject: [PATCH 09/31] v0.5.4 --- CHANGES.rst | 15 +++++++++++++++ setup.py | 2 +- 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/CHANGES.rst b/CHANGES.rst index f7f9d28..d9dd429 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -1,6 +1,21 @@ Changelog ========= +Version 0.5.4 -- 2016/10/18 +--------------------------- + +* Tons of new languages! +* Add Polish localization. (#23) +* Add Swiss-French localization. (#38) +* Add Russian localization. (#28, #46, #48) +* Add Indonesian localization. (#29) +* Add Norwegian localization. (#33) +* Add Danish localization. (#40) +* Add Brazilian localization. (#37, #47) +* Improve German localization. (#25, #27, #49) +* Improve Lithuanian localization. (#52) +* Improve floating point spelling. (#24) + Version 0.5.3 -- 2015/06/09 --------------------------- diff --git a/setup.py b/setup.py index 693ea27..b842523 100644 --- a/setup.py +++ b/setup.py @@ -17,7 +17,7 @@ LONG_DESC = open('README.rst', 'rt').read() + '\n\n' + open('CHANGES.rst', 'rt') setup( name='num2words', - version='0.5.3', + version='0.5.4', description='Modules to convert numbers to words. Easily extensible.', long_description=LONG_DESC, license='LGPL', From 20f634028dd97b29eee4768a70f5230c3e17b7a5 Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Tue, 22 Nov 2016 09:13:18 -0500 Subject: [PATCH 10/31] Convert to single codebase for py2 and py3 No more 2to3. Also, added tox config for easy cross-python testing. --- .gitignore | 3 +- num2words/base.py | 29 ++-- num2words/compat.py | 26 ++++ num2words/lang_DE.py | 10 +- num2words/lang_DK.py | 6 +- num2words/lang_EN.py | 16 ++- num2words/lang_EN_GB.py | 10 +- num2words/lang_ES.py | 8 +- num2words/lang_FR.py | 6 +- num2words/lang_FR_CH.py | 6 +- num2words/lang_ID.py | 8 +- num2words/lang_NO.py | 16 ++- setup.py | 1 - tests/test_pt_BR.py | 312 ++++++++++++++++++++-------------------- tox.ini | 5 + 15 files changed, 256 insertions(+), 206 deletions(-) create mode 100644 num2words/compat.py create mode 100644 tox.ini diff --git a/.gitignore b/.gitignore index f3a9967..d578da0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *.pyc build dist -*.egg-info \ No newline at end of file +*.egg-info +/.tox diff --git a/num2words/base.py b/num2words/base.py index 72577b3..272d5bb 100644 --- a/num2words/base.py +++ b/num2words/base.py @@ -15,7 +15,11 @@ # MA 02110-1301 USA from __future__ import unicode_literals + +import math + from .orderedmapping import OrderedMapping +from .compat import to_s class Num2Word_Base(object): @@ -88,7 +92,7 @@ class Num2Word_Base(object): def to_cardinal(self, value): try: - assert long(value) == value + assert int(value) == value except (ValueError, TypeError, AssertionError): return self.to_cardinal_float(value) @@ -113,9 +117,18 @@ class Num2Word_Base(object): except (ValueError, TypeError, AssertionError): raise TypeError(self.errmsg_nonnum % value) + value = float(value) pre = int(value) - post = str(abs(value - pre) * 10**self.precision) - post = '0' * (self.precision - len(post.split('.')[0])) + post + post = abs(value - pre) * 10**self.precision + if abs(round(post) - post) < 0.01: + # We generally floor all values beyond our precision (rather than rounding), but in + # cases where we have something like 1.239999999, which is probably due to python's + # handling of floats, we actually want to consider it as 1.24 instead of 1.23 + post = int(round(post)) + else: + post = int(math.floor(post)) + post = str(post) + post = '0' * (self.precision - len(post)) + post out = [self.to_cardinal(pre)] if self.precision: @@ -123,7 +136,7 @@ class Num2Word_Base(object): for i in range(self.precision): curr = int(post[i]) - out.append(unicode(self.to_cardinal(curr))) + out.append(to_s(self.to_cardinal(curr))) return " ".join(out) @@ -168,10 +181,10 @@ class Num2Word_Base(object): def verify_ordinal(self, value): - if not value == long(value): - raise TypeError, self.errmsg_floatord %(value) + if not value == int(value): + raise TypeError(self.errmsg_floatord % value) if not abs(value) == value: - raise TypeError, self.errmsg_negord %(value) + raise TypeError(self.errmsg_negord % value) def verify_num(self, value): @@ -181,7 +194,7 @@ class Num2Word_Base(object): def set_wordnums(self): pass - + def to_ordinal(self, value): return self.to_cardinal(value) diff --git a/num2words/compat.py b/num2words/compat.py new file mode 100644 index 0000000..7395f4c --- /dev/null +++ b/num2words/compat.py @@ -0,0 +1,26 @@ +# Copyright (c) 2003, Taro Ogawa. All Rights Reserved. +# Copyright (c) 2016, 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 + +import sys + +PY3 = sys.version_info[0] == 3 + +def to_s(val): + if PY3: + return str(val) + else: + return unicode(val) + diff --git a/num2words/lang_DE.py b/num2words/lang_DE.py index 252ccb7..1c2fa0f 100644 --- a/num2words/lang_DE.py +++ b/num2words/lang_DE.py @@ -15,7 +15,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301 USA -from __future__ import unicode_literals +from __future__ import unicode_literals, print_function from .lang_EU import Num2Word_EU class Num2Word_DE(Num2Word_EU): @@ -133,10 +133,10 @@ def main(): n2w.test(3000000) n2w.test(3000000000001) n2w.test(3000000324566) - print n2w.to_currency(112121) - print n2w.to_year(2000) - print n2w.to_year(1820) - print n2w.to_year(2001) + print(n2w.to_currency(112121)) + print(n2w.to_year(2000)) + print(n2w.to_year(1820)) + print(n2w.to_year(2001)) if __name__ == "__main__": main() diff --git a/num2words/lang_DK.py b/num2words/lang_DK.py index bc88c06..64cf793 100644 --- a/num2words/lang_DK.py +++ b/num2words/lang_DK.py @@ -14,7 +14,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301 USA -from __future__ import division, unicode_literals +from __future__ import division, unicode_literals, print_function from num2words import lang_EU class Num2Word_DK(lang_EU.Num2Word_EU): @@ -146,8 +146,8 @@ def main(): n2w.test(val) n2w.test(1325325436067876801768700107601001012212132143210473207540327057320957032975032975093275093275093270957329057320975093272950730) for val in [1,120, 160, 1000,1120,1800, 1976,2000,2010,2099,2171]: - print val, "er", n2w.to_currency(val) - print val, "er", n2w.to_year(val) + print(val, "er", n2w.to_currency(val)) + print(val, "er", n2w.to_year(val)) n2w.test(65132) if __name__ == "__main__": diff --git a/num2words/lang_EN.py b/num2words/lang_EN.py index 54646f5..656abfc 100644 --- a/num2words/lang_EN.py +++ b/num2words/lang_EN.py @@ -14,7 +14,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301 USA -from __future__ import division, unicode_literals +from __future__ import division, unicode_literals, print_function from . import lang_EU class Num2Word_EN(lang_EU.Num2Word_EU): @@ -47,7 +47,9 @@ class Num2Word_EN(lang_EU.Num2Word_EU): "twelve" : "twelfth" } - def merge(self, (ltext, lnum), (rtext, rnum)): + def merge(self, lpair, rpair): + ltext, lnum = lpair + rtext, rnum = rpair if lnum == 1 and rnum < 100: return (rtext, rnum) elif 100 > lnum > rnum : @@ -68,9 +70,9 @@ class Num2Word_EN(lang_EU.Num2Word_EU): lastword = self.ords[lastword] except KeyError: if lastword[-1] == "y": - lastword = lastword[:-1] + "ie" + lastword = lastword[:-1] + "ie" lastword += "th" - lastwords[-1] = self.title(lastword) + lastwords[-1] = self.title(lastword) outwords[-1] = "-".join(lastwords) return " ".join(outwords) @@ -105,9 +107,9 @@ def main(): n2w.test(val) n2w.test(1325325436067876801768700107601001012212132143210473207540327057320957032975032975093275093275093270957329057320975093272950730) for val in [1,120,1000,1120,1800, 1976,2000,2010,2099,2171]: - print val, "is", n2w.to_currency(val) - print val, "is", n2w.to_year(val) - + print(val, "is", n2w.to_currency(val)) + print(val, "is", n2w.to_year(val)) + if __name__ == "__main__": main() diff --git a/num2words/lang_EN_GB.py b/num2words/lang_EN_GB.py index 568594d..a01c41a 100644 --- a/num2words/lang_EN_GB.py +++ b/num2words/lang_EN_GB.py @@ -14,10 +14,10 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301 USA -from __future__ import unicode_literals +from __future__ import unicode_literals, print_function from .lang_EN import Num2Word_EN - + class Num2Word_EN_GB(Num2Word_EN): def to_currency(self, val, longval=True): return self.to_splitnum(val, hightxt="pound/s", lowtxt="pence", @@ -38,9 +38,9 @@ def main(): n2w.test(val) n2w.test(1325325436067876801768700107601001012212132143210473207540327057320957032975032975093275093275093270957329057320975093272950730) for val in [1,120,1000,1120,1800, 1976,2000,2010,2099,2171]: - print val, "is", n2w.to_currency(val) - print val, "is", n2w.to_year(val) - + print(val, "is", n2w.to_currency(val)) + print(val, "is", n2w.to_year(val)) + if __name__ == "__main__": main() diff --git a/num2words/lang_ES.py b/num2words/lang_ES.py index 71103a8..c010d52 100644 --- a/num2words/lang_ES.py +++ b/num2words/lang_ES.py @@ -16,7 +16,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301 USA -from __future__ import unicode_literals +from __future__ import unicode_literals, print_function from .lang_EU import Num2Word_EU class Num2Word_ES(Num2Word_EU): @@ -173,9 +173,9 @@ def main(): n2w.test(val) n2w.test(1325325436067876801768700107601001012212132143210473207540327057320957032975032975093275093275093270957329057320975093272950730) - print n2w.to_currency(1222) - print n2w.to_currency(1222, old=True) - print n2w.to_year(1222) + print(n2w.to_currency(1222)) + print(n2w.to_currency(1222, old=True)) + print(n2w.to_year(1222)) if __name__ == "__main__": main() diff --git a/num2words/lang_FR.py b/num2words/lang_FR.py index a4e0f6a..1209eab 100644 --- a/num2words/lang_FR.py +++ b/num2words/lang_FR.py @@ -15,7 +15,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301 USA -from __future__ import unicode_literals +from __future__ import unicode_literals, print_function from .lang_EU import Num2Word_EU @@ -106,8 +106,8 @@ def main(): n2w.test(val) n2w.test(1325325436067876801768700107601001012212132143210473207540327057320957032975032975093275093275093270957329057320975093272950730) - print n2w.to_currency(112121) - print n2w.to_year(1996) + print(n2w.to_currency(112121)) + print(n2w.to_year(1996)) if __name__ == "__main__": diff --git a/num2words/lang_FR_CH.py b/num2words/lang_FR_CH.py index b2130eb..b6d40a0 100644 --- a/num2words/lang_FR_CH.py +++ b/num2words/lang_FR_CH.py @@ -15,7 +15,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301 USA -from __future__ import unicode_literals +from __future__ import unicode_literals, print_function from .lang_EU import Num2Word_EU class Num2Word_FR_CH(Num2Word_EU): @@ -101,8 +101,8 @@ def main(): n2w.test(val) n2w.test(1325325436067876801768700107601001012212132143210473207540327057320957032975032975093275093275093270957329057320975093272950730) - print n2w.to_currency(112121) - print n2w.to_year(1996) + print(n2w.to_currency(112121)) + print(n2w.to_year(1996)) if __name__ == "__main__": diff --git a/num2words/lang_ID.py b/num2words/lang_ID.py index ee07065..9437db7 100644 --- a/num2words/lang_ID.py +++ b/num2words/lang_ID.py @@ -14,6 +14,8 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301 USA +from __future__ import unicode_literals, print_function + class Num2Word_ID(): BASE = {0: [], @@ -188,7 +190,7 @@ class Num2Word_ID(): return self.to_cardinal(value) def verify_ordinal(self, value): - if not value == long(value): - raise TypeError, self.errmsg_floatord %(value) + if not value == int(value): + raise TypeError(self.errmsg_floatord % value) if not abs(value) == value: - raise TypeError, self.errmsg_negord %(value) + raise TypeError(self.errmsg_negord % value) diff --git a/num2words/lang_NO.py b/num2words/lang_NO.py index 273622b..2c744e1 100644 --- a/num2words/lang_NO.py +++ b/num2words/lang_NO.py @@ -14,7 +14,7 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301 USA -from __future__ import division, unicode_literals +from __future__ import division, unicode_literals, print_function from . import lang_EU class Num2Word_NO(lang_EU.Num2Word_EU): @@ -54,7 +54,9 @@ class Num2Word_NO(lang_EU.Num2Word_EU): "tjue" : "tjuende" } - def merge(self, (ltext, lnum), (rtext, rnum)): + def merge(self, lpair, rpair): + ltext, lnum = lpair + rtext, rnum = rpair if lnum == 1 and rnum < 100: return (rtext, rnum) elif 100 > lnum > rnum : @@ -75,10 +77,10 @@ class Num2Word_NO(lang_EU.Num2Word_EU): lastword = self.ords[lastword] except KeyError: if lastword[-2:] == "ti": - lastword = lastword + "ende" + lastword = lastword + "ende" else: lastword += "de" - lastwords[-1] = self.title(lastword) + lastwords[-1] = self.title(lastword) outwords[-1] = "".join(lastwords) return " ".join(outwords) @@ -113,9 +115,9 @@ def main(): n2w.test(val) n2w.test(1325325436067876801768700107601001012212132143210473207540327057320957032975032975093275093275093270957329057320975093272950730) for val in [1,120,1000,1120,1800, 1976,2000,2010,2099,2171]: - print val, "er", n2w.to_currency(val) - print val, "er", n2w.to_year(val) - + print(val, "er", n2w.to_currency(val)) + print(val, "er", n2w.to_year(val)) + if __name__ == "__main__": main() diff --git a/setup.py b/setup.py index b842523..ab6aaf2 100644 --- a/setup.py +++ b/setup.py @@ -29,5 +29,4 @@ setup( url='https://github.com/savoirfairelinux/num2words', packages=find_packages(exclude=['tests']), test_suite='tests', - use_2to3=True, ) diff --git a/tests/test_pt_BR.py b/tests/test_pt_BR.py index 9a82481..a681d91 100644 --- a/tests/test_pt_BR.py +++ b/tests/test_pt_BR.py @@ -29,191 +29,191 @@ class Num2WordsPTBRTest(TestCase): self.n2w = Num2Word_PT_BR() def test_cardinal_integer(self): - self.assertEquals(num2words(1, lang='pt_BR'), 'um') - self.assertEquals(num2words(2, lang='pt_BR'), 'dois') - self.assertEquals(num2words(3, lang='pt_BR'), 'três') - self.assertEquals(num2words(4, lang='pt_BR'), 'quatro') - self.assertEquals(num2words(5, lang='pt_BR'), 'cinco') - self.assertEquals(num2words(6, lang='pt_BR'), 'seis') - self.assertEquals(num2words(7, lang='pt_BR'), 'sete') - self.assertEquals(num2words(8, lang='pt_BR'), 'oito') - self.assertEquals(num2words(9, lang='pt_BR'), 'nove') - self.assertEquals(num2words(10, lang='pt_BR'), 'dez') - self.assertEquals(num2words(11, lang='pt_BR'), 'onze') - self.assertEquals(num2words(12, lang='pt_BR'), 'doze') - self.assertEquals(num2words(13, lang='pt_BR'), 'treze') - self.assertEquals(num2words(14, lang='pt_BR'), 'catorze') - self.assertEquals(num2words(15, lang='pt_BR'), 'quinze') - self.assertEquals(num2words(16, lang='pt_BR'), 'dezesseis') - self.assertEquals(num2words(17, lang='pt_BR'), 'dezessete') - self.assertEquals(num2words(18, lang='pt_BR'), 'dezoito') - self.assertEquals(num2words(19, lang='pt_BR'), 'dezenove') - self.assertEquals(num2words(20, lang='pt_BR'), 'vinte') + self.assertEqual(num2words(1, lang='pt_BR'), 'um') + self.assertEqual(num2words(2, lang='pt_BR'), 'dois') + self.assertEqual(num2words(3, lang='pt_BR'), 'três') + self.assertEqual(num2words(4, lang='pt_BR'), 'quatro') + self.assertEqual(num2words(5, lang='pt_BR'), 'cinco') + self.assertEqual(num2words(6, lang='pt_BR'), 'seis') + self.assertEqual(num2words(7, lang='pt_BR'), 'sete') + self.assertEqual(num2words(8, lang='pt_BR'), 'oito') + self.assertEqual(num2words(9, lang='pt_BR'), 'nove') + self.assertEqual(num2words(10, lang='pt_BR'), 'dez') + self.assertEqual(num2words(11, lang='pt_BR'), 'onze') + self.assertEqual(num2words(12, lang='pt_BR'), 'doze') + self.assertEqual(num2words(13, lang='pt_BR'), 'treze') + self.assertEqual(num2words(14, lang='pt_BR'), 'catorze') + self.assertEqual(num2words(15, lang='pt_BR'), 'quinze') + self.assertEqual(num2words(16, lang='pt_BR'), 'dezesseis') + self.assertEqual(num2words(17, lang='pt_BR'), 'dezessete') + self.assertEqual(num2words(18, lang='pt_BR'), 'dezoito') + self.assertEqual(num2words(19, lang='pt_BR'), 'dezenove') + self.assertEqual(num2words(20, lang='pt_BR'), 'vinte') - self.assertEquals(num2words(21, lang='pt_BR'), 'vinte e um') - self.assertEquals(num2words(22, lang='pt_BR'), 'vinte e dois') - self.assertEquals(num2words(35, lang='pt_BR'), 'trinta e cinco') - self.assertEquals(num2words(99, lang='pt_BR'), 'noventa e nove') + self.assertEqual(num2words(21, lang='pt_BR'), 'vinte e um') + self.assertEqual(num2words(22, lang='pt_BR'), 'vinte e dois') + self.assertEqual(num2words(35, lang='pt_BR'), 'trinta e cinco') + self.assertEqual(num2words(99, lang='pt_BR'), 'noventa e nove') - self.assertEquals(num2words(100, lang='pt_BR'), 'cem') - self.assertEquals(num2words(101, lang='pt_BR'), 'cento e um') - self.assertEquals(num2words(128, lang='pt_BR'), 'cento e vinte e oito') - self.assertEquals(num2words(713, lang='pt_BR'), 'setecentos e treze') + self.assertEqual(num2words(100, lang='pt_BR'), 'cem') + self.assertEqual(num2words(101, lang='pt_BR'), 'cento e um') + self.assertEqual(num2words(128, lang='pt_BR'), 'cento e vinte e oito') + self.assertEqual(num2words(713, lang='pt_BR'), 'setecentos e treze') - self.assertEquals(num2words(1000, lang='pt_BR'), 'mil') - self.assertEquals(num2words(1001, lang='pt_BR'), 'mil e um') - self.assertEquals(num2words(1111, lang='pt_BR'), 'mil, cento e onze') - self.assertEquals(num2words(2114, lang='pt_BR'), 'dois mil, cento e catorze') - self.assertEquals(num2words(73421, lang='pt_BR'), 'setenta e três mil, quatrocentos e vinte e um') + self.assertEqual(num2words(1000, lang='pt_BR'), 'mil') + self.assertEqual(num2words(1001, lang='pt_BR'), 'mil e um') + self.assertEqual(num2words(1111, lang='pt_BR'), 'mil, cento e onze') + self.assertEqual(num2words(2114, lang='pt_BR'), 'dois mil, cento e catorze') + self.assertEqual(num2words(73421, lang='pt_BR'), 'setenta e três mil, quatrocentos e vinte e um') - self.assertEquals(num2words(100000, lang='pt_BR'), 'cem mil') - self.assertEquals(num2words(250050, lang='pt_BR'), 'duzentos e cinquenta mil e cinquenta') - self.assertEquals(num2words(6000000, lang='pt_BR'), 'seis milhões') - self.assertEquals(num2words(19000000000, lang='pt_BR'), 'dezenove bilhões') - self.assertEquals(num2words(145000000002, lang='pt_BR'), 'cento e quarenta e cinco bilhões e dois') + self.assertEqual(num2words(100000, lang='pt_BR'), 'cem mil') + self.assertEqual(num2words(250050, lang='pt_BR'), 'duzentos e cinquenta mil e cinquenta') + self.assertEqual(num2words(6000000, lang='pt_BR'), 'seis milhões') + self.assertEqual(num2words(19000000000, lang='pt_BR'), 'dezenove bilhões') + self.assertEqual(num2words(145000000002, lang='pt_BR'), 'cento e quarenta e cinco bilhões e dois') def test_cardinal_integer_negative(self): - self.assertEquals(num2words(-1, lang='pt_BR'), 'menos um') - self.assertEquals(num2words(-256, lang='pt_BR'), 'menos duzentos e cinquenta e seis') - self.assertEquals(num2words(-1000, lang='pt_BR'), 'menos mil') - self.assertEquals(num2words(-1000000, lang='pt_BR'), 'menos um milhão') - self.assertEquals(num2words(-1234567, lang='pt_BR'), 'menos um milhão, duzentos e trinta e quatro mil, quinhentos e sessenta e sete') + self.assertEqual(num2words(-1, lang='pt_BR'), 'menos um') + self.assertEqual(num2words(-256, lang='pt_BR'), 'menos duzentos e cinquenta e seis') + self.assertEqual(num2words(-1000, lang='pt_BR'), 'menos mil') + self.assertEqual(num2words(-1000000, lang='pt_BR'), 'menos um milhão') + self.assertEqual(num2words(-1234567, lang='pt_BR'), 'menos um milhão, duzentos e trinta e quatro mil, quinhentos e sessenta e sete') def test_cardinal_float(self): - self.assertEquals(num2words(Decimal('1.00'), lang='pt_BR'), 'um') - self.assertEquals(num2words(Decimal('1.01'), lang='pt_BR'), 'um vírgula zero um') - self.assertEquals(num2words(Decimal('1.035'), lang='pt_BR'), 'um vírgula zero três') - self.assertEquals(num2words(Decimal('1.35'), lang='pt_BR'), 'um vírgula três cinco') - self.assertEquals(num2words(Decimal('3.14159'), lang='pt_BR'), 'três vírgula um quatro') - self.assertEquals(num2words(Decimal('101.22'), lang='pt_BR'), 'cento e um vírgula dois dois') - self.assertEquals(num2words(Decimal('2345.75'), lang='pt_BR'), 'dois mil, trezentos e quarenta e cinco vírgula sete cinco') + self.assertEqual(num2words(Decimal('1.00'), lang='pt_BR'), 'um') + self.assertEqual(num2words(Decimal('1.01'), lang='pt_BR'), 'um vírgula zero um') + self.assertEqual(num2words(Decimal('1.035'), lang='pt_BR'), 'um vírgula zero três') + self.assertEqual(num2words(Decimal('1.35'), lang='pt_BR'), 'um vírgula três cinco') + self.assertEqual(num2words(Decimal('3.14159'), lang='pt_BR'), 'três vírgula um quatro') + self.assertEqual(num2words(Decimal('101.22'), lang='pt_BR'), 'cento e um vírgula dois dois') + self.assertEqual(num2words(Decimal('2345.75'), lang='pt_BR'), 'dois mil, trezentos e quarenta e cinco vírgula sete cinco') def test_cardinal_float_negative(self): - self.assertEquals(num2words(Decimal('-2.34'), lang='pt_BR'), 'menos dois vírgula três quatro') - self.assertEquals(num2words(Decimal('-9.99'), lang='pt_BR'), 'menos nove vírgula nove nove') - self.assertEquals(num2words(Decimal('-7.01'), lang='pt_BR'), 'menos sete vírgula zero um') - self.assertEquals(num2words(Decimal('-222.22'), lang='pt_BR'), 'menos duzentos e vinte e dois vírgula dois dois') + self.assertEqual(num2words(Decimal('-2.34'), lang='pt_BR'), 'menos dois vírgula três quatro') + self.assertEqual(num2words(Decimal('-9.99'), lang='pt_BR'), 'menos nove vírgula nove nove') + self.assertEqual(num2words(Decimal('-7.01'), lang='pt_BR'), 'menos sete vírgula zero um') + self.assertEqual(num2words(Decimal('-222.22'), lang='pt_BR'), 'menos duzentos e vinte e dois vírgula dois dois') def test_ordinal(self): - self.assertEquals(num2words(1, lang='pt_BR', ordinal=True), 'primeiro') - self.assertEquals(num2words(2, lang='pt_BR', ordinal=True), 'segundo') - self.assertEquals(num2words(3, lang='pt_BR', ordinal=True), 'terceiro') - self.assertEquals(num2words(4, lang='pt_BR', ordinal=True), 'quarto') - self.assertEquals(num2words(5, lang='pt_BR', ordinal=True), 'quinto') - self.assertEquals(num2words(6, lang='pt_BR', ordinal=True), 'sexto') - self.assertEquals(num2words(7, lang='pt_BR', ordinal=True), 'sétimo') - self.assertEquals(num2words(8, lang='pt_BR', ordinal=True), 'oitavo') - self.assertEquals(num2words(9, lang='pt_BR', ordinal=True), 'nono') - self.assertEquals(num2words(10, lang='pt_BR', ordinal=True), 'décimo') - self.assertEquals(num2words(11, lang='pt_BR', ordinal=True), 'décimo primeiro') - self.assertEquals(num2words(12, lang='pt_BR', ordinal=True), 'décimo segundo') - self.assertEquals(num2words(13, lang='pt_BR', ordinal=True), 'décimo terceiro') - self.assertEquals(num2words(14, lang='pt_BR', ordinal=True), 'décimo quarto') - self.assertEquals(num2words(15, lang='pt_BR', ordinal=True), 'décimo quinto') - self.assertEquals(num2words(16, lang='pt_BR', ordinal=True), 'décimo sexto') - self.assertEquals(num2words(17, lang='pt_BR', ordinal=True), 'décimo sétimo') - self.assertEquals(num2words(18, lang='pt_BR', ordinal=True), 'décimo oitavo') - self.assertEquals(num2words(19, lang='pt_BR', ordinal=True), 'décimo nono') - self.assertEquals(num2words(20, lang='pt_BR', ordinal=True), 'vigésimo') + self.assertEqual(num2words(1, lang='pt_BR', ordinal=True), 'primeiro') + self.assertEqual(num2words(2, lang='pt_BR', ordinal=True), 'segundo') + self.assertEqual(num2words(3, lang='pt_BR', ordinal=True), 'terceiro') + self.assertEqual(num2words(4, lang='pt_BR', ordinal=True), 'quarto') + self.assertEqual(num2words(5, lang='pt_BR', ordinal=True), 'quinto') + self.assertEqual(num2words(6, lang='pt_BR', ordinal=True), 'sexto') + self.assertEqual(num2words(7, lang='pt_BR', ordinal=True), 'sétimo') + self.assertEqual(num2words(8, lang='pt_BR', ordinal=True), 'oitavo') + self.assertEqual(num2words(9, lang='pt_BR', ordinal=True), 'nono') + self.assertEqual(num2words(10, lang='pt_BR', ordinal=True), 'décimo') + self.assertEqual(num2words(11, lang='pt_BR', ordinal=True), 'décimo primeiro') + self.assertEqual(num2words(12, lang='pt_BR', ordinal=True), 'décimo segundo') + self.assertEqual(num2words(13, lang='pt_BR', ordinal=True), 'décimo terceiro') + self.assertEqual(num2words(14, lang='pt_BR', ordinal=True), 'décimo quarto') + self.assertEqual(num2words(15, lang='pt_BR', ordinal=True), 'décimo quinto') + self.assertEqual(num2words(16, lang='pt_BR', ordinal=True), 'décimo sexto') + self.assertEqual(num2words(17, lang='pt_BR', ordinal=True), 'décimo sétimo') + self.assertEqual(num2words(18, lang='pt_BR', ordinal=True), 'décimo oitavo') + self.assertEqual(num2words(19, lang='pt_BR', ordinal=True), 'décimo nono') + self.assertEqual(num2words(20, lang='pt_BR', ordinal=True), 'vigésimo') - self.assertEquals(num2words(21, lang='pt_BR', ordinal=True), 'vigésimo primeiro') - self.assertEquals(num2words(22, lang='pt_BR', ordinal=True), 'vigésimo segundo') - self.assertEquals(num2words(35, lang='pt_BR', ordinal=True), 'trigésimo quinto') - self.assertEquals(num2words(99, lang='pt_BR', ordinal=True), 'nonagésimo nono') + self.assertEqual(num2words(21, lang='pt_BR', ordinal=True), 'vigésimo primeiro') + self.assertEqual(num2words(22, lang='pt_BR', ordinal=True), 'vigésimo segundo') + self.assertEqual(num2words(35, lang='pt_BR', ordinal=True), 'trigésimo quinto') + self.assertEqual(num2words(99, lang='pt_BR', ordinal=True), 'nonagésimo nono') - self.assertEquals(num2words(100, lang='pt_BR', ordinal=True), 'centésimo') - self.assertEquals(num2words(101, lang='pt_BR', ordinal=True), 'centésimo primeiro') - self.assertEquals(num2words(128, lang='pt_BR', ordinal=True), 'centésimo vigésimo oitavo') - self.assertEquals(num2words(713, lang='pt_BR', ordinal=True), 'septigentésimo décimo terceiro') + self.assertEqual(num2words(100, lang='pt_BR', ordinal=True), 'centésimo') + self.assertEqual(num2words(101, lang='pt_BR', ordinal=True), 'centésimo primeiro') + self.assertEqual(num2words(128, lang='pt_BR', ordinal=True), 'centésimo vigésimo oitavo') + self.assertEqual(num2words(713, lang='pt_BR', ordinal=True), 'septigentésimo décimo terceiro') - self.assertEquals(num2words(1000, lang='pt_BR', ordinal=True), 'milésimo') - self.assertEquals(num2words(1001, lang='pt_BR', ordinal=True), 'milésimo primeiro') - self.assertEquals(num2words(1111, lang='pt_BR', ordinal=True), 'milésimo centésimo décimo primeiro') - self.assertEquals(num2words(2114, lang='pt_BR', ordinal=True), 'segundo milésimo centésimo décimo quarto') - self.assertEquals(num2words(73421, lang='pt_BR', ordinal=True), 'septuagésimo terceiro milésimo quadrigentésimo vigésimo primeiro') + self.assertEqual(num2words(1000, lang='pt_BR', ordinal=True), 'milésimo') + self.assertEqual(num2words(1001, lang='pt_BR', ordinal=True), 'milésimo primeiro') + self.assertEqual(num2words(1111, lang='pt_BR', ordinal=True), 'milésimo centésimo décimo primeiro') + self.assertEqual(num2words(2114, lang='pt_BR', ordinal=True), 'segundo milésimo centésimo décimo quarto') + self.assertEqual(num2words(73421, lang='pt_BR', ordinal=True), 'septuagésimo terceiro milésimo quadrigentésimo vigésimo primeiro') - self.assertEquals(num2words(100000, lang='pt_BR', ordinal=True), 'centésimo milésimo') - self.assertEquals(num2words(250050, lang='pt_BR', ordinal=True), 'ducentésimo quinquagésimo milésimo quinquagésimo') - self.assertEquals(num2words(6000000, lang='pt_BR', ordinal=True), 'sexto milionésimo') - self.assertEquals(num2words(19000000000, lang='pt_BR', ordinal=True), 'décimo nono bilionésimo') - self.assertEquals(num2words(145000000002, lang='pt_BR', ordinal=True), 'centésimo quadragésimo quinto bilionésimo segundo') + self.assertEqual(num2words(100000, lang='pt_BR', ordinal=True), 'centésimo milésimo') + self.assertEqual(num2words(250050, lang='pt_BR', ordinal=True), 'ducentésimo quinquagésimo milésimo quinquagésimo') + self.assertEqual(num2words(6000000, lang='pt_BR', ordinal=True), 'sexto milionésimo') + self.assertEqual(num2words(19000000000, lang='pt_BR', ordinal=True), 'décimo nono bilionésimo') + self.assertEqual(num2words(145000000002, lang='pt_BR', ordinal=True), 'centésimo quadragésimo quinto bilionésimo segundo') def test_currency_integer(self): - self.assertEquals(self.n2w.to_currency(1), 'um real') - self.assertEquals(self.n2w.to_currency(2), 'dois reais') - self.assertEquals(self.n2w.to_currency(3), 'três reais') - self.assertEquals(self.n2w.to_currency(4), 'quatro reais') - self.assertEquals(self.n2w.to_currency(5), 'cinco reais') - self.assertEquals(self.n2w.to_currency(6), 'seis reais') - self.assertEquals(self.n2w.to_currency(7), 'sete reais') - self.assertEquals(self.n2w.to_currency(8), 'oito reais') - self.assertEquals(self.n2w.to_currency(9), 'nove reais') - self.assertEquals(self.n2w.to_currency(10), 'dez reais') - self.assertEquals(self.n2w.to_currency(11), 'onze reais') - self.assertEquals(self.n2w.to_currency(12), 'doze reais') - self.assertEquals(self.n2w.to_currency(13), 'treze reais') - self.assertEquals(self.n2w.to_currency(14), 'catorze reais') - self.assertEquals(self.n2w.to_currency(15), 'quinze reais') - self.assertEquals(self.n2w.to_currency(16), 'dezesseis reais') - self.assertEquals(self.n2w.to_currency(17), 'dezessete reais') - self.assertEquals(self.n2w.to_currency(18), 'dezoito reais') - self.assertEquals(self.n2w.to_currency(19), 'dezenove reais') - self.assertEquals(self.n2w.to_currency(20), 'vinte reais') + self.assertEqual(self.n2w.to_currency(1), 'um real') + self.assertEqual(self.n2w.to_currency(2), 'dois reais') + self.assertEqual(self.n2w.to_currency(3), 'três reais') + self.assertEqual(self.n2w.to_currency(4), 'quatro reais') + self.assertEqual(self.n2w.to_currency(5), 'cinco reais') + self.assertEqual(self.n2w.to_currency(6), 'seis reais') + self.assertEqual(self.n2w.to_currency(7), 'sete reais') + self.assertEqual(self.n2w.to_currency(8), 'oito reais') + self.assertEqual(self.n2w.to_currency(9), 'nove reais') + self.assertEqual(self.n2w.to_currency(10), 'dez reais') + self.assertEqual(self.n2w.to_currency(11), 'onze reais') + self.assertEqual(self.n2w.to_currency(12), 'doze reais') + self.assertEqual(self.n2w.to_currency(13), 'treze reais') + self.assertEqual(self.n2w.to_currency(14), 'catorze reais') + self.assertEqual(self.n2w.to_currency(15), 'quinze reais') + self.assertEqual(self.n2w.to_currency(16), 'dezesseis reais') + self.assertEqual(self.n2w.to_currency(17), 'dezessete reais') + self.assertEqual(self.n2w.to_currency(18), 'dezoito reais') + self.assertEqual(self.n2w.to_currency(19), 'dezenove reais') + self.assertEqual(self.n2w.to_currency(20), 'vinte reais') - self.assertEquals(self.n2w.to_currency(21), 'vinte e um reais') - self.assertEquals(self.n2w.to_currency(22), 'vinte e dois reais') - self.assertEquals(self.n2w.to_currency(35), 'trinta e cinco reais') - self.assertEquals(self.n2w.to_currency(99), 'noventa e nove reais') + self.assertEqual(self.n2w.to_currency(21), 'vinte e um reais') + self.assertEqual(self.n2w.to_currency(22), 'vinte e dois reais') + self.assertEqual(self.n2w.to_currency(35), 'trinta e cinco reais') + self.assertEqual(self.n2w.to_currency(99), 'noventa e nove reais') - self.assertEquals(self.n2w.to_currency(100), 'cem reais') - self.assertEquals(self.n2w.to_currency(101), 'cento e um reais') - self.assertEquals(self.n2w.to_currency(128), 'cento e vinte e oito reais') - self.assertEquals(self.n2w.to_currency(713), 'setecentos e treze reais') + self.assertEqual(self.n2w.to_currency(100), 'cem reais') + self.assertEqual(self.n2w.to_currency(101), 'cento e um reais') + self.assertEqual(self.n2w.to_currency(128), 'cento e vinte e oito reais') + self.assertEqual(self.n2w.to_currency(713), 'setecentos e treze reais') - self.assertEquals(self.n2w.to_currency(1000), 'mil reais') - self.assertEquals(self.n2w.to_currency(1001), 'mil e um reais') - self.assertEquals(self.n2w.to_currency(1111), 'mil, cento e onze reais') - self.assertEquals(self.n2w.to_currency(2114), 'dois mil, cento e catorze reais') - self.assertEquals(self.n2w.to_currency(73421), 'setenta e três mil, quatrocentos e vinte e um reais') + self.assertEqual(self.n2w.to_currency(1000), 'mil reais') + self.assertEqual(self.n2w.to_currency(1001), 'mil e um reais') + self.assertEqual(self.n2w.to_currency(1111), 'mil, cento e onze reais') + self.assertEqual(self.n2w.to_currency(2114), 'dois mil, cento e catorze reais') + self.assertEqual(self.n2w.to_currency(73421), 'setenta e três mil, quatrocentos e vinte e um reais') - self.assertEquals(self.n2w.to_currency(100000), 'cem mil reais') - self.assertEquals(self.n2w.to_currency(250050), 'duzentos e cinquenta mil e cinquenta reais') - self.assertEquals(self.n2w.to_currency(6000000), 'seis milhões de reais') - self.assertEquals(self.n2w.to_currency(19000000000), 'dezenove bilhões de reais') - self.assertEquals(self.n2w.to_currency(145000000002), 'cento e quarenta e cinco bilhões e dois reais') + self.assertEqual(self.n2w.to_currency(100000), 'cem mil reais') + self.assertEqual(self.n2w.to_currency(250050), 'duzentos e cinquenta mil e cinquenta reais') + self.assertEqual(self.n2w.to_currency(6000000), 'seis milhões de reais') + self.assertEqual(self.n2w.to_currency(19000000000), 'dezenove bilhões de reais') + self.assertEqual(self.n2w.to_currency(145000000002), 'cento e quarenta e cinco bilhões e dois reais') def test_currency_integer_negative(self): - self.assertEquals(self.n2w.to_currency(-1), 'menos um real') - self.assertEquals(self.n2w.to_currency(-256), 'menos duzentos e cinquenta e seis reais') - self.assertEquals(self.n2w.to_currency(-1000), 'menos mil reais') - self.assertEquals(self.n2w.to_currency(-1000000), 'menos um milhão de reais') - self.assertEquals(self.n2w.to_currency(-1234567), 'menos um milhão, duzentos e trinta e quatro mil, quinhentos e sessenta e sete reais') + self.assertEqual(self.n2w.to_currency(-1), 'menos um real') + self.assertEqual(self.n2w.to_currency(-256), 'menos duzentos e cinquenta e seis reais') + self.assertEqual(self.n2w.to_currency(-1000), 'menos mil reais') + self.assertEqual(self.n2w.to_currency(-1000000), 'menos um milhão de reais') + self.assertEqual(self.n2w.to_currency(-1234567), 'menos um milhão, duzentos e trinta e quatro mil, quinhentos e sessenta e sete reais') def test_currency_float(self): - self.assertEquals(self.n2w.to_currency(Decimal('1.00')), 'um real') - self.assertEquals(self.n2w.to_currency(Decimal('1.01')), 'um real e um centavo') - self.assertEquals(self.n2w.to_currency(Decimal('1.035')), 'um real e três centavos') - self.assertEquals(self.n2w.to_currency(Decimal('1.35')), 'um real e trinta e cinco centavos') - self.assertEquals(self.n2w.to_currency(Decimal('3.14159')), 'três reais e catorze centavos') - self.assertEquals(self.n2w.to_currency(Decimal('101.22')), 'cento e um reais e vinte e dois centavos') - self.assertEquals(self.n2w.to_currency(Decimal('2345.75')), 'dois mil, trezentos e quarenta e cinco reais e setenta e cinco centavos') + self.assertEqual(self.n2w.to_currency(Decimal('1.00')), 'um real') + self.assertEqual(self.n2w.to_currency(Decimal('1.01')), 'um real e um centavo') + self.assertEqual(self.n2w.to_currency(Decimal('1.035')), 'um real e três centavos') + self.assertEqual(self.n2w.to_currency(Decimal('1.35')), 'um real e trinta e cinco centavos') + self.assertEqual(self.n2w.to_currency(Decimal('3.14159')), 'três reais e catorze centavos') + self.assertEqual(self.n2w.to_currency(Decimal('101.22')), 'cento e um reais e vinte e dois centavos') + self.assertEqual(self.n2w.to_currency(Decimal('2345.75')), 'dois mil, trezentos e quarenta e cinco reais e setenta e cinco centavos') def test_currency_float_negative(self): - self.assertEquals(self.n2w.to_currency(Decimal('-2.34')), 'menos dois reais e trinta e quatro centavos') - self.assertEquals(self.n2w.to_currency(Decimal('-9.99')), 'menos nove reais e noventa e nove centavos') - self.assertEquals(self.n2w.to_currency(Decimal('-7.01')), 'menos sete reais e um centavo') - self.assertEquals(self.n2w.to_currency(Decimal('-222.22')), 'menos duzentos e vinte e dois reais e vinte e dois centavos') + self.assertEqual(self.n2w.to_currency(Decimal('-2.34')), 'menos dois reais e trinta e quatro centavos') + self.assertEqual(self.n2w.to_currency(Decimal('-9.99')), 'menos nove reais e noventa e nove centavos') + self.assertEqual(self.n2w.to_currency(Decimal('-7.01')), 'menos sete reais e um centavo') + self.assertEqual(self.n2w.to_currency(Decimal('-222.22')), 'menos duzentos e vinte e dois reais e vinte e dois centavos') def test_year(self): - self.assertEquals(self.n2w.to_year(1001), 'mil e um') - self.assertEquals(self.n2w.to_year(1789), 'mil, setecentos e oitenta e nove') - self.assertEquals(self.n2w.to_year(1942), 'mil, novecentos e quarenta e dois') - self.assertEquals(self.n2w.to_year(1984), 'mil, novecentos e oitenta e quatro') - self.assertEquals(self.n2w.to_year(2000), 'dois mil') - self.assertEquals(self.n2w.to_year(2001), 'dois mil e um') - self.assertEquals(self.n2w.to_year(2016), 'dois mil e dezesseis') + self.assertEqual(self.n2w.to_year(1001), 'mil e um') + self.assertEqual(self.n2w.to_year(1789), 'mil, setecentos e oitenta e nove') + self.assertEqual(self.n2w.to_year(1942), 'mil, novecentos e quarenta e dois') + self.assertEqual(self.n2w.to_year(1984), 'mil, novecentos e oitenta e quatro') + self.assertEqual(self.n2w.to_year(2000), 'dois mil') + self.assertEqual(self.n2w.to_year(2001), 'dois mil e um') + self.assertEqual(self.n2w.to_year(2016), 'dois mil e dezesseis') def test_year_negative(self): - self.assertEquals(self.n2w.to_year(-30), 'trinta antes de Cristo') - self.assertEquals(self.n2w.to_year(-744), 'setecentos e quarenta e quatro antes de Cristo') - self.assertEquals(self.n2w.to_year(-10000), 'dez mil antes de Cristo') + self.assertEqual(self.n2w.to_year(-30), 'trinta antes de Cristo') + self.assertEqual(self.n2w.to_year(-744), 'setecentos e quarenta e quatro antes de Cristo') + self.assertEqual(self.n2w.to_year(-10000), 'dez mil antes de Cristo') diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..13bca3c --- /dev/null +++ b/tox.ini @@ -0,0 +1,5 @@ +[tox] +envlist = py27,py34 + +[testenv] +commands = python -m unittest discover From 182b82cf1ff47d588f50b1feb650e721626cbbd1 Mon Sep 17 00:00:00 2001 From: Noah Santacruz Date: Tue, 22 Nov 2016 10:23:15 -0500 Subject: [PATCH 11/31] Added Hebrew ordinal numbers #54 Squashed commit of the following: commit 88b946ef72928e859d078f3febaf9c76ce0849b9 Merge: bca0277 79ab811 Author: Noah Santacruz Date: Tue Nov 22 09:41:05 2016 +0200 merge commit bca0277424c074af217df5e86abfd2def3a30bc7 Author: Noah Santacruz Date: Tue Nov 22 09:40:03 2016 +0200 removed out.txt commit 79ab811e97fd14bc5899174b198e86f5c6ba2c5f Author: Noah Santacruz Date: Mon Nov 21 18:09:42 2016 +0200 Update README.rst commit 507e4d4cec5b5458b2546ebebe5e49d376b88646 Author: Noah Santacruz Date: Mon Nov 21 18:08:39 2016 +0200 updated init commit 7d3aa5ab33d92b0b374ed1bfbf17807836e465bf Author: Noah Santacruz Date: Mon Nov 21 16:05:06 2016 +0200 changed init commit 29b4c54047ff9ab84b4c95e9ff05ebcb12c15f49 Author: Noah Santacruz Date: Mon Nov 21 16:01:17 2016 +0200 added Hebrew --- README.rst | 1 + num2words/__init__.py | 2 + num2words/lang_HE.py | 162 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 165 insertions(+) create mode 100644 num2words/lang_HE.py diff --git a/README.rst b/README.rst index 1aed5bc..8e8894d 100644 --- a/README.rst +++ b/README.rst @@ -54,6 +54,7 @@ Besides the numerical argument, there's two optional arguments. * ``ru`` (Russian) * ``dk`` (Danish) * ``pt_BR`` (Brazilian Portuguese) +* ``he`` (Hebrew) You can supply values like ``fr_FR``, the code will be correctly interpreted. If you supply an unsupported language, ``NotImplementedError`` is raised. diff --git a/num2words/__init__.py b/num2words/__init__.py index 85d3328..6de57a2 100644 --- a/num2words/__init__.py +++ b/num2words/__init__.py @@ -31,6 +31,7 @@ from . import lang_ID from . import lang_NO from . import lang_DK from . import lang_PT_BR +from . import lang_HE CONVERTER_CLASSES = { 'en': lang_EN.Num2Word_EN(), @@ -48,6 +49,7 @@ CONVERTER_CLASSES = { 'no': lang_NO.Num2Word_NO(), 'dk': lang_DK.Num2Word_DK(), 'pt_BR': lang_PT_BR.Num2Word_PT_BR(), + 'he': lang_HE.Num2Word_HE() } def num2words(number, ordinal=False, lang='en'): diff --git a/num2words/lang_HE.py b/num2words/lang_HE.py new file mode 100644 index 0000000..73857dc --- /dev/null +++ b/num2words/lang_HE.py @@ -0,0 +1,162 @@ +# -*- encoding: utf-8 -*- +# Copyright (c) 2003, Taro Ogawa. All Rights Reserved. +# Copyright (c) 2013, 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 + +ZERO = (u'אפס',) + +ONES = { + 1: (u'אחד',), + 2: (u'שנים',), + 3: (u'שלש',), + 4: (u'ארבע',), + 5: (u'חמש',), + 6: (u'שש',), + 7: (u'שבע',), + 8: (u'שמנה',), + 9: (u'תשע',), +} + +TENS = { + 0: (u'עשר',), + 1: (u'אחד עשרה',), + 2: (u'שנים עשרה',), + 3: (u'שלש עשרה',), + 4: (u'ארבע עשרה',), + 5: (u'חמש עשרה',), + 6: (u'שש עשרה',), + 7: (u'שבע עשרה',), + 8: (u'שמנה עשרה',), + 9: (u'תשע עשרה',), +} + +TWENTIES = { + 2: (u'עשרים',), + 3: (u'שלשים',), + 4: (u'ארבעים',), + 5: (u'חמישים',), + 6: (u'ששים',), + 7: (u'שבעים',), + 8: (u'שמנים',), + 9: (u'תשעים',), +} + +HUNDRED = { + 1: (u'מאה',), + 2: (u'מאתיים',), + 3: (u'מאות',) +} + +THOUSANDS = { + 1: (u'אלף',), + 2: (u'אלפיים',), +} + +AND = u'ו' + +def splitby3(n): + length = len(n) + if length > 3: + start = length % 3 + if start > 0: + yield int(n[:start]) + for i in range(start, length, 3): + yield int(n[i:i+3]) + else: + yield int(n) + + +def get_digits(n): + return [int(x) for x in reversed(list(('%03d' % n)[-3:]))] + + +def pluralize(n, forms): + # gettext implementation: + # (n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2) + + form = 0 if (n % 10 == 1 and n % 100 != 11) else 1 if n != 0 else 2 + + return forms[form] + + +def int2word(n): + if n > 9999: #doesn't yet work for numbers this big + raise NotImplementedError() + + if n == 0: + return ZERO[0] + + words = [] + + chunks = list(splitby3(str(n))) + i = len(chunks) + for x in chunks: + i -= 1 + n1, n2, n3 = get_digits(x) + + # print str(n3) + str(n2) + str(n1) + + if n3 > 0: + if n3 <= 2: + words.append(HUNDRED[n3][0]) + else: + words.append(ONES[n3][0]) + words.append(HUNDRED[3][0]) + + if n2 > 1: + words.append(TWENTIES[n2][0]) + + if n2 == 1: + words.append(TENS[n1][0]) + elif n1 > 0 and not (i > 0 and x == 1): + words.append(ONES[n1][0]) + + if i > 0: + if i <= 2: + words.append(THOUSANDS[i][0]) + else: + words.append(ONES[i][0]) + words.append(THOUSANDS[1][0]) + + if len(words) > 1: + words[-1] = AND + words[-1] + return ' '.join(words) + + +def n2w(n): + return int2word(int(n)) + + +def to_currency(n, currency='EUR', cents=True, seperator=','): + raise NotImplementedError() + + +class Num2Word_HE(object): + def to_cardinal(self, number): + return n2w(number) + + def to_ordinal(self, number): + raise NotImplementedError() + + +if __name__ == '__main__': + yo = Num2Word_HE() + nums = [1, 11, 21, 24, 99, 100, 101, 200, 211, 345, 1000, 1011] + for num in nums: + print num, yo.to_cardinal(num) + From f7f1ba45834c007c2559b1f45ec8b6143c134b81 Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Tue, 22 Nov 2016 10:29:51 -0500 Subject: [PATCH 12/31] Fix python3 syntax in lang_HE --- num2words/lang_HE.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/num2words/lang_HE.py b/num2words/lang_HE.py index 73857dc..15e67c8 100644 --- a/num2words/lang_HE.py +++ b/num2words/lang_HE.py @@ -16,7 +16,7 @@ # MA 02110-1301 USA -from __future__ import unicode_literals +from __future__ import unicode_literals, print_function ZERO = (u'אפס',) @@ -158,5 +158,5 @@ if __name__ == '__main__': yo = Num2Word_HE() nums = [1, 11, 21, 24, 99, 100, 101, 200, 211, 345, 1000, 1011] for num in nums: - print num, yo.to_cardinal(num) + print(num, yo.to_cardinal(num)) From 08779b28b27c40f072b6b13116b83df227c40519 Mon Sep 17 00:00:00 2001 From: Brett Anthoine Date: Mon, 18 Jul 2016 08:30:45 +0200 Subject: [PATCH 13/31] Add support for italian. --- num2words/__init__.py | 2 + num2words/lang_IT.py | 204 ++++++++++++++++++++++++++++++++++++++++++ tests/test_it.py | 82 +++++++++++++++++ 3 files changed, 288 insertions(+) create mode 100644 num2words/lang_IT.py create mode 100644 tests/test_it.py diff --git a/num2words/__init__.py b/num2words/__init__.py index 6de57a2..7f878d4 100644 --- a/num2words/__init__.py +++ b/num2words/__init__.py @@ -32,6 +32,7 @@ from . import lang_NO from . import lang_DK from . import lang_PT_BR from . import lang_HE +from . import lang_IT CONVERTER_CLASSES = { 'en': lang_EN.Num2Word_EN(), @@ -50,6 +51,7 @@ CONVERTER_CLASSES = { 'dk': lang_DK.Num2Word_DK(), 'pt_BR': lang_PT_BR.Num2Word_PT_BR(), 'he': lang_HE.Num2Word_HE() + 'it': lang_IT.Num2Word_IT(), } def num2words(number, ordinal=False, lang='en'): diff --git a/num2words/lang_IT.py b/num2words/lang_IT.py new file mode 100644 index 0000000..a46a29c --- /dev/null +++ b/num2words/lang_IT.py @@ -0,0 +1,204 @@ +# -*- encoding: utf-8 -*- +# The PHP License, version 3.01 +# Copyright (c) 1999 - 2010 The PHP Group. All rights reserved. +# +# This source file is subject to version 3.01 of the PHP license, +# that is available at http://www.php.net/license/3_01.txt +# If you did not receive a copy of the PHP license and are unable to +# obtain it through the world-wide-web, please send a note to +# license@php.net so we can mail you a copy immediately. +# +# This code is a direct port to Python of the PHP code of the +# Number_Words package that can be found on pear at the URL : +# http://pear.php.net/package/Numbers_Words +# + +from __future__ import unicode_literals +from .lang_EU import Num2Word_EU + +import re +import math + +class Num2Word_IT(object): + def __init__(self): + self._minus = "meno " + + self._exponent = { + 0 : ('',''), + 3 : ('mille','mila'), + 6 : ('milione','miloni'), + 12 : ('miliardo','miliardi'), + 18 : ('trillone','trilloni'), + 24 : ('quadrilione','quadrilioni')} + + self._digits = ['zero', 'uno', 'due', 'tre', 'quattro', 'cinque', 'sei', 'sette', 'otto', 'nove'] + + self._sep = '' + + def _toWords(self, num, power=0): + str_num = str(num) + # The return string; + ret = '' + + # add a the word for the minus sign if necessary + if num < 0: + ret = self._sep + self._minus + + if len(str_num) > 6: + current_power = 6 + # check for highest power + if self._exponent.has_key(power): + # convert the number above the first 6 digits + # with it's corresponding $power. + snum = str_num[0:-6] + if snum != '': + ret = ret + self._toWords(int(snum), power + 6) + + num = int(str_num[-6:]) + if num == 0: + return ret + + elif num == 0 or str_num == '': + return ' ' + self._digits[0] + ' ' + else: + current_power = len(str_num) + + # See if we need "thousands" + thousands = math.floor(num / 1000) + if thousands == 1: + ret = ret + self._sep + 'mille' + self._sep + elif thousands > 1: + ret = ret + self._toWords(int(thousands), 3) + self._sep + + # values for digits, tens and hundreds + h = int(math.floor((num / 100) % 10)) + t = int(math.floor((num / 10) % 10)) + d = int(math.floor(num % 10)) + + # centinaia: duecento, trecento, etc... + if h == 1: + if ((d==0) and (t == 0)):# is it's '100' use 'cien' + ret = ret + self._sep + 'cento' + else: + ret = ret + self._sep + 'cento' + elif h == 2 or h == 3 or h == 4 or h == 6 or h == 8: + ret = ret + self._sep + self._digits[h] + 'cento' + elif h == 5: + ret = ret + self._sep + 'cinquecento' + elif h == 7: + ret = ret + self._sep + 'settecento' + elif h == 9: + ret = ret + self._sep + 'novecento' + + # decine: venti trenta, etc... + if t == 9: + if d == 1 or d == 8: + ret = ret + self._sep + 'novant' + else: + ret = ret + self._sep + 'novanta' + if t == 8: + if d == 1 or d == 8: + ret = ret + self._sep + 'ottant' + else: + ret = ret + self._sep + 'ottanta' + if t == 7: + if d == 1 or d == 8: + ret = ret + self._sep + 'settant' + else: + ret = ret + self._sep + 'settanta' + if t == 6: + if d == 1 or d == 8: + ret = ret + self._sep + 'sessant' + else: + ret = ret + self._sep + 'sessanta' + if t == 5: + if d == 1 or d == 8: + ret = ret + self._sep + 'cinquant' + else: + ret = ret + self._sep + 'cinquanta' + if t == 4: + if d == 1 or d == 8: + ret = ret + self._sep + 'quarant' + else: + ret = ret + self._sep + 'quaranta' + if t == 3: + if d == 1 or d == 8: + ret = ret + self._sep + 'trent' + else: + ret = ret + self._sep + 'trenta' + if t == 2: + if d == 0: + ret = ret + self._sep + 'venti' + elif (d == 1 or d == 8): + ret = ret + self._sep + 'vent' + self._digits[d] + else: + ret = ret + self._sep + 'venti' + self._digits[d] + if t == 1: + if d == 0: + ret = ret + self._sep + 'dieci' + elif d == 1: + ret = ret + self._sep + 'undici' + elif d == 2: + ret = ret + self._sep + 'dodici' + elif d == 3: + ret = ret + self._sep + 'tredici' + elif d == 4: + ret = ret + self._sep + 'quattordici' + elif d == 5: + ret = ret + self._sep + 'quindici' + elif d == 6: + ret = ret + self._sep + 'sedici' + elif d == 7: + ret = ret + self._sep + 'diciassette' + elif d == 8: + ret = ret + self._sep + 'diciotto' + elif d == 9: + ret = ret + self._sep + 'diciannove' + + # add digits only if it is a multiple of 10 and not 1x or 2x + if t != 1 and t != 2 and d > 0: + # don't add 'e' for numbers below 10 + if t != 0: + # use 'un' instead of 'uno' when there is a suffix ('mila', 'milloni', etc...) + if (power > 0) and ( d == 1): + ret = ret + self._sep + 'e un' + else: + ret = ret + self._sep + '' + self._digits[d] + else: + if power > 0 and d == 1: + ret = ret + self._sep + 'un ' + else: + ret = ret + self._sep + self._digits[d] + + if power > 0: + if self._exponent.has_key(power): + lev = self._exponent[power] + + if lev is None: + return None + + # if it's only one use the singular suffix + if d == 1 and t == 0 and h == 0: + suffix = lev[0] + else: + suffix = lev[1] + + if num != 0: + ret = ret + self._sep + suffix + + return ret + + + def to_cardinal(self, number): + return self._toWords(number) + + def to_ordinal_num(self, number): + pass + + def to_ordinal(self,value): + raise NotImplementedError() + +n2w = Num2Word_IT() +to_card = n2w.to_cardinal +to_ord = n2w.to_ordinal +to_ordnum = n2w.to_ordinal_num diff --git a/tests/test_it.py b/tests/test_it.py new file mode 100644 index 0000000..07cd3a2 --- /dev/null +++ b/tests/test_it.py @@ -0,0 +1,82 @@ +# -*- encoding: utf-8 -*- +# Copyright (c) 2015, 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 + +class Num2WordsITTest(TestCase): + + def test_number(self): + + test_cases = ( + (1,'uno'), + (2,'due'), + (3,'tre'), + (11,'undici'), + (12,'dodici'), + (16,'sedici'), + (19,'diciannove'), + (20,'venti'), + (21,'ventuno'), + (26,'ventisei'), + (30,'trenta'), + (31,'trentuno'), + (40,'quaranta'), + (43,'quarantatre'), + (50,'cinquanta'), + (55,'cinquantacinque'), + (60,'sessanta'), + (67,'sessantasette'), + (70,'settanta'), + (79,'settantanove'), + (100,'cento'), + (101,'centouno'), + (199,'centonovantanove'), + (203,'duecentotre'), + (287,'duecentoottantasette'), + (300,'trecento'), + (356,'trecentocinquantasei'), + (410,'quattrocentodieci'), + (434,'quattrocentotrentaquattro'), + (578,'cinquecentosettantotto'), + (689,'seicentoottantanove'), + (729,'settecentoventinove'), + (894,'ottocentonovantaquattro'), + (999,'novecentonovantanove'), + (1000,'mille'), + (1001,'milleuno'), + (1097,'millenovantasette'), + (1104,'millecentoquattro'), + (1243,'milleduecentoquarantatre'), + (2385,'duemilatrecentoottantacinque'), + (3766,'tremilasettecentosessantasei'), + (4196,'quattromilacentonovantasei'), + (5846,'cinquemilaottocentoquarantasei'), + (6459,'seimilaquattrocentocinquantanove'), + (7232,'settemiladuecentotrentadue'), + (8569,'ottomilacinquecentosessantanove'), + (9539,'novemilacinquecentotrentanove'), + (1000000,'un milione'), + (1000001,'un milioneuno'), + # (1000000100,'un miliardocento'), # DOES NOT WORK TODO: FIX + ) + + for test in test_cases: + self.assertEqual(num2words(test[0], lang='it'), test[1]) + From c9ecd07cbfffaa740e5c471fd5dacc9efde9dd3e Mon Sep 17 00:00:00 2001 From: Brett Anthoine Date: Mon, 18 Jul 2016 11:27:51 +0200 Subject: [PATCH 14/31] Add italian ordinals. --- num2words/lang_IT.py | 15 ++++++++++++++- tests/test_it.py | 14 ++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/num2words/lang_IT.py b/num2words/lang_IT.py index a46a29c..00dfebf 100644 --- a/num2words/lang_IT.py +++ b/num2words/lang_IT.py @@ -196,9 +196,22 @@ class Num2Word_IT(object): pass def to_ordinal(self,value): - raise NotImplementedError() + if 0 <= value <= 10: + return ["primo", "secondo", "terzo", "quarto", "quinto", "sesto", "settimo", "ottavo", "nono", "decimo"][value - 1] + else: + as_word = self._toWords(value) + if as_word.endswith("dici"): + return re.sub("dici$", "dicesimo", as_word) + elif as_word.endswith("to"): + return re.sub("to$", "tesimo", as_word) + elif as_word.endswith("ta"): + return re.sub("ta$", "tesimo", as_word) + else: + return as_word + "simo" + n2w = Num2Word_IT() to_card = n2w.to_cardinal to_ord = n2w.to_ordinal to_ordnum = n2w.to_ordinal_num + diff --git a/tests/test_it.py b/tests/test_it.py index 07cd3a2..57bf646 100644 --- a/tests/test_it.py +++ b/tests/test_it.py @@ -35,6 +35,7 @@ class Num2WordsITTest(TestCase): (20,'venti'), (21,'ventuno'), (26,'ventisei'), + (28,'ventotto'), (30,'trenta'), (31,'trentuno'), (40,'quaranta'), @@ -80,3 +81,16 @@ class Num2WordsITTest(TestCase): for test in test_cases: self.assertEqual(num2words(test[0], lang='it'), test[1]) + def test_ordinal(self): + + test_cases = ( + (1,'primo'), + (8,'ottavo'), + (12,'dodicesimo'), + (14,'quattordicesimo'), + (28,'ventottesimo'), + (100,'centesimo'), + ) + + for test in test_cases: + self.assertEqual(num2words(test[0], lang='it', ordinal=True), test[1]) From bd1f4faf1d1a1e80bc8843874754c2da8c3d7688 Mon Sep 17 00:00:00 2001 From: Brett Anthoine Date: Tue, 10 Jan 2017 14:39:09 +0100 Subject: [PATCH 15/31] Update license header --- num2words/lang_IT.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/num2words/lang_IT.py b/num2words/lang_IT.py index 00dfebf..000068c 100644 --- a/num2words/lang_IT.py +++ b/num2words/lang_IT.py @@ -1,17 +1,17 @@ # -*- encoding: utf-8 -*- -# The PHP License, version 3.01 -# Copyright (c) 1999 - 2010 The PHP Group. All rights reserved. -# -# This source file is subject to version 3.01 of the PHP license, -# that is available at http://www.php.net/license/3_01.txt -# If you did not receive a copy of the PHP license and are unable to -# obtain it through the world-wide-web, please send a note to -# license@php.net so we can mail you a copy immediately. -# -# This code is a direct port to Python of the PHP code of the -# Number_Words package that can be found on pear at the URL : -# http://pear.php.net/package/Numbers_Words # +# 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 .lang_EU import Num2Word_EU From 08e915436632fbe8a9193bbcfdef5c481c6007b1 Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Wed, 18 Jan 2017 11:15:03 -0500 Subject: [PATCH 16/31] Add travis config --- .travis.yml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .travis.yml diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..6df6b36 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,9 @@ +sudo: false +language: python +python: + - "2.7" + - "3.4" + - "3.5" + - "3.6" +install: pip install tox-travis +script: tox From 58a4e8e18b704cc06902aae8631c9e60d37580f9 Mon Sep 17 00:00:00 2001 From: Brett Anthoine Date: Wed, 1 Feb 2017 08:49:34 +0100 Subject: [PATCH 17/31] Fix syntax error --- num2words/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/num2words/__init__.py b/num2words/__init__.py index 7f878d4..57c4d57 100644 --- a/num2words/__init__.py +++ b/num2words/__init__.py @@ -50,8 +50,8 @@ CONVERTER_CLASSES = { 'no': lang_NO.Num2Word_NO(), 'dk': lang_DK.Num2Word_DK(), 'pt_BR': lang_PT_BR.Num2Word_PT_BR(), - 'he': lang_HE.Num2Word_HE() - 'it': lang_IT.Num2Word_IT(), + 'he': lang_HE.Num2Word_HE(), + 'it': lang_IT.Num2Word_IT() } def num2words(number, ordinal=False, lang='en'): From 0b80fd42855bf0cb278d8dd858b61cffbba8df97 Mon Sep 17 00:00:00 2001 From: Brett Anthoine Date: Wed, 1 Feb 2017 16:13:24 +0100 Subject: [PATCH 18/31] Use in operator instead of has_key for python3 support --- num2words/lang_IT.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/num2words/lang_IT.py b/num2words/lang_IT.py index 000068c..227883b 100644 --- a/num2words/lang_IT.py +++ b/num2words/lang_IT.py @@ -47,7 +47,7 @@ class Num2Word_IT(object): if len(str_num) > 6: current_power = 6 # check for highest power - if self._exponent.has_key(power): + if power in self._exponent: # convert the number above the first 6 digits # with it's corresponding $power. snum = str_num[0:-6] @@ -171,7 +171,7 @@ class Num2Word_IT(object): ret = ret + self._sep + self._digits[d] if power > 0: - if self._exponent.has_key(power): + if power in self._exponent: lev = self._exponent[power] if lev is None: From 0f21d8f82531c6375888038c4a10807e6687f83d Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Wed, 1 Feb 2017 10:31:41 -0500 Subject: [PATCH 19/31] README: Add Italian to the list of supported languages --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index 8e8894d..930aae0 100644 --- a/README.rst +++ b/README.rst @@ -55,6 +55,7 @@ Besides the numerical argument, there's two optional arguments. * ``dk`` (Danish) * ``pt_BR`` (Brazilian Portuguese) * ``he`` (Hebrew) +* ``it`` (Italian) You can supply values like ``fr_FR``, the code will be correctly interpreted. If you supply an unsupported language, ``NotImplementedError`` is raised. From 8468654d86089f7c4403de51f8b10f59df02ba39 Mon Sep 17 00:00:00 2001 From: Virgil Dupras Date: Wed, 1 Feb 2017 10:32:10 -0500 Subject: [PATCH 20/31] tox.ini: add py35 and py36 envs --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index 13bca3c..0e59285 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py27,py34 +envlist = py27,py34,py35,py36 [testenv] commands = python -m unittest discover From fb281568b2f0a0e2dfef95448cade2012b4c6972 Mon Sep 17 00:00:00 2001 From: Ernesto Rodriguez Ortiz Date: Wed, 1 Feb 2017 14:59:43 -0500 Subject: [PATCH 21/31] Show the TravisCI build status in the project page. --- README.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.rst b/README.rst index 930aae0..beb2da6 100644 --- a/README.rst +++ b/README.rst @@ -1,6 +1,8 @@ num2words - Convert numbers to words in multiple languages ========================================================== +.. image:: https://travis-ci.org/savoirfairelinux/num2words.svg?branch=master :target: https://travis-ci.org/savoirfairelinux/num2words + ``num2words`` is a library that converts numbers like ``42`` to words like ``forty-two``. It supports multiple languages (English, French, Spanish, German and Lithuanian) and can even generate ordinal numbers like ``forty-second`` (altough this last feature is a bit buggy at the moment). From f67d64196889492801a9a37140ec9036fa484d12 Mon Sep 17 00:00:00 2001 From: Krzysztof Socha Date: Sat, 25 Feb 2017 22:44:27 +0100 Subject: [PATCH 22/31] Fixed issues with wrong text for currency conversion with whole 10s (e.g., 123.50) --- num2words/lang_PL.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/num2words/lang_PL.py b/num2words/lang_PL.py index 24078d8..369e41f 100644 --- a/num2words/lang_PL.py +++ b/num2words/lang_PL.py @@ -55,6 +55,9 @@ dwa tysiące dwanaście >>> print(n2w(12519.85)) dwanaście tysięcy pięćset dziewiętnaście przecinek osiemdziesiąt pięć +>>> print(n2w(123.50)) +sto dwadzieścia trzy przecinek pięć + >>> print(fill(n2w(1234567890))) miliard dwieście trzydzieści cztery miliony pięćset sześćdziesiąt siedem tysięcy osiemset dziewięćdzisiąt @@ -95,6 +98,9 @@ sto jeden złotych i dwadzieścia jeden groszy >>> print(to_currency(-1251985, cents = False)) minus dwanaście tysięcy pięćset dziewiętnaście euro, 85 centów + +>>> print(to_currency(123.50, 'PLN', seperator=' i')) +sto dwadzieścia trzy złote i pięćdziesiąt groszy """ from __future__ import unicode_literals @@ -207,7 +213,7 @@ def int2word(n): if n3 > 0: words.append(HUNDREDS[n3][0]) - + if n2 > 1: words.append(TWENTIES[n2][0]) @@ -245,6 +251,8 @@ def to_currency(n, currency='EUR', cents=True, seperator=','): n = str(n).replace(',', '.') if '.' in n: left, right = n.split('.') + if len(right)==1: + right = right+'0' else: left, right = n, 0 left, right = int(left), int(right) From d586f620c59152d9ab1c1d26ee992362f32d4c09 Mon Sep 17 00:00:00 2001 From: Krzysztof Socha Date: Mon, 27 Feb 2017 17:18:06 +0100 Subject: [PATCH 23/31] Added unit tests for Polish language --- @test_95559_tmp | 2 ++ @test_95599_tmp | 2 ++ tests/test_pl.py | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 53 insertions(+) create mode 100644 @test_95559_tmp create mode 100644 @test_95599_tmp create mode 100644 tests/test_pl.py diff --git a/@test_95559_tmp b/@test_95559_tmp new file mode 100644 index 0000000..88d54c1 --- /dev/null +++ b/@test_95559_tmp @@ -0,0 +1,2 @@ +z = z+1 +z = z*2 diff --git a/@test_95599_tmp b/@test_95599_tmp new file mode 100644 index 0000000..88d54c1 --- /dev/null +++ b/@test_95599_tmp @@ -0,0 +1,2 @@ +z = z+1 +z = z*2 diff --git a/tests/test_pl.py b/tests/test_pl.py new file mode 100644 index 0000000..b70b9a6 --- /dev/null +++ b/tests/test_pl.py @@ -0,0 +1,49 @@ +# -*- encoding: utf-8 -*- +# Copyright (c) 2015, 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 +from num2words.lang_PL import to_currency + +class Num2WordsPLTest(TestCase): + def test_cardinal(self): + self.assertEqual(num2words(100, lang='pl'), "sto") + self.assertEqual(num2words(101, lang='pl'), "sto jeden") + self.assertEqual(num2words(110, lang='pl'), "sto dziesięć") + self.assertEqual(num2words(115, lang='pl'), "sto piętnaście") + self.assertEqual(num2words(123, lang='pl'), "sto dwadzieścia trzy") + self.assertEqual(num2words(1000, lang='pl'), "tysiąc") + self.assertEqual(num2words(1001, lang='pl'), "tysiąc jeden") + self.assertEqual(num2words(2012, lang='pl'), "dwa tysiące dwanaście") + self.assertEqual(num2words(12519.85, lang='pl'), "dwanaście tysięcy pięćset dziewiętnaście przecinek osiemdziesiąt pięć") + self.assertEqual(num2words(123.50, lang='pl'), "sto dwadzieścia trzy przecinek pięć") + self.assertEqual(num2words(1234567890, lang='pl'), "miliard dwieście trzydzieści cztery miliony pięćset sześćdziesiąt siedem tysięcy osiemset dziewięćdzisiąt") + self.assertEqual(num2words(215461407892039002157189883901676, lang='pl'), "dwieście piętnaście kwintylionów czterysta sześćdziesiąt jeden kwadryliardów czterysta siedem kwadrylionów osiemset dziewięćdzisiąt dwa tryliardy trzydzieści dziewięć trylionów dwa biliardy sto pięćdziesiąt siedem bilionów sto osiemdziesiąt dziewięć miliardów osiemset osiemdziesiąt trzy miliony dziewęćset jeden tysięcy sześćset siedemdziesiąt sześć") + self.assertEqual(num2words(719094234693663034822824384220291, lang='pl'), "siedemset dziewiętnaście kwintylionów dziewięćdzisiąt cztery kwadryliardy dwieście trzydzieści cztery kwadryliony sześćset dziewięćdzisiąt trzy tryliardy sześćset sześćdziesiąt trzy tryliony trzydzieści cztery biliardy osiemset dwadzieścia dwa biliony osiemset dwadzieścia cztery miliardy trzysta osiemdziesiąt cztery miliony dwieście dwadzieścia tysięcy dwieście dziewięćdzisiąt jeden") + + def test_currency(self): + self.assertEqual(to_currency(1.0, 'EUR'), "jeden euro, zero centów") + self.assertEqual(to_currency(1.0, 'PLN'), "jeden złoty, zero groszy") + self.assertEqual(to_currency(1234.56, 'EUR'), "tysiąc dwieście trzydzieści cztery euro, pięćdziesiąt sześć centów") + self.assertEqual(to_currency(1234.56, 'PLN'), "tysiąc dwieście trzydzieści cztery złote, pięćdziesiąt sześć groszy") + self.assertEqual(to_currency(10111, 'EUR', seperator=' i'), "sto jeden euro i jedenaście centów") + self.assertEqual(to_currency(10121, 'PLN', seperator=' i'), "sto jeden złotych i dwadzieścia jeden groszy") + self.assertEqual(to_currency(-1251985, cents = False), "minus dwanaście tysięcy pięćset dziewiętnaście euro, 85 centów") + self.assertEqual(to_currency(123.50, 'PLN', seperator=' i'), "sto dwadzieścia trzy złote i pięćdziesiąt groszy") + self.assertEqual(to_currency(1950, cents = False), "dziewiętnaście euro, 50 centów") From 94d4e8599246660fe5fde56e501a825db179120a Mon Sep 17 00:00:00 2001 From: Krzysztof Socha Date: Mon, 27 Feb 2017 17:22:31 +0100 Subject: [PATCH 24/31] Cleaned-up some test files --- .DS_Store | Bin 0 -> 6148 bytes @test_95559_tmp | 2 -- @test_95599_tmp | 2 -- 3 files changed, 4 deletions(-) create mode 100644 .DS_Store delete mode 100644 @test_95559_tmp delete mode 100644 @test_95599_tmp diff --git a/.DS_Store b/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 Date: Mon, 27 Feb 2017 17:23:52 +0100 Subject: [PATCH 25/31] Cleaned-up some test files --- .DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 .DS_Store diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 Date: Mon, 27 Feb 2017 21:44:43 +0100 Subject: [PATCH 26/31] Fixed integer division operator (/->//) to work also in python3 --- num2words/lang_PL.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/num2words/lang_PL.py b/num2words/lang_PL.py index 369e41f..4ce7380 100644 --- a/num2words/lang_PL.py +++ b/num2words/lang_PL.py @@ -245,7 +245,7 @@ def to_currency(n, currency='EUR', cents=True, seperator=','): minus = False n = abs(n) - left = n / 100 + left = n // 100 right = n % 100 else: n = str(n).replace(',', '.') From 7fdb61351b740111bfe3596b3f4a1aee4f5da925 Mon Sep 17 00:00:00 2001 From: Krzysztof Socha Date: Mon, 27 Feb 2017 21:57:11 +0100 Subject: [PATCH 27/31] Cleaned-up whitespaces around operators --- num2words/lang_PL.py | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/num2words/lang_PL.py b/num2words/lang_PL.py index 4ce7380..c232f8a 100644 --- a/num2words/lang_PL.py +++ b/num2words/lang_PL.py @@ -184,7 +184,7 @@ def splitby3(n): if start > 0: yield int(n[:start]) for i in range(start, length, 3): - yield int(n[i:i+3]) + yield int(n[i:i + 3]) else: yield int(n) @@ -194,7 +194,7 @@ def get_digits(n): def pluralize(n, forms): - form = 0 if n==1 else 1 if (n % 10 > 1 and n % 10 < 5 and (n % 100 < 10 or n % 100 > 20)) else 2 + form = 0 if n == 1 else 1 if (n % 10 > 1 and n % 10 < 5 and (n % 100 < 10 or n % 100 > 20)) else 2 return forms[form] @@ -209,8 +209,6 @@ def int2word(n): i -= 1 n1, n2, n3 = get_digits(x) - # print str(n3) + str(n2) + str(n1) - if n3 > 0: words.append(HUNDREDS[n3][0]) @@ -237,7 +235,7 @@ def n2w(n): return int2word(int(n)) -def to_currency(n, currency='EUR', cents=True, seperator=','): +def to_currency(n, currency = 'EUR', cents = True, seperator = ','): if type(n) == int: if n < 0: minus = True @@ -251,8 +249,8 @@ def to_currency(n, currency='EUR', cents=True, seperator=','): n = str(n).replace(',', '.') if '.' in n: left, right = n.split('.') - if len(right)==1: - right = right+'0' + if len(right) == 1: + right = right + '0' else: left, right = n, 0 left, right = int(left), int(right) From 907ebbc812f71dd1c5751651b7d08d176d505274 Mon Sep 17 00:00:00 2001 From: Ernesto Rodriguez Ortiz Date: Mon, 27 Feb 2017 16:05:21 -0500 Subject: [PATCH 28/31] Update README.rst to show TravisCI build status. --- README.rst | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.rst b/README.rst index beb2da6..a3808e7 100644 --- a/README.rst +++ b/README.rst @@ -1,7 +1,8 @@ num2words - Convert numbers to words in multiple languages ========================================================== -.. image:: https://travis-ci.org/savoirfairelinux/num2words.svg?branch=master :target: https://travis-ci.org/savoirfairelinux/num2words +.. image:: https://travis-ci.org/savoirfairelinux/num2words.svg?branch=master + :target: https://travis-ci.org/savoirfairelinux/num2words ``num2words`` is a library that converts numbers like ``42`` to words like ``forty-two``. It supports multiple languages (English, French, Spanish, German and Lithuanian) and can even generate From 90e7c9dc7d03a5c14c009d79b5b2caa4568d0620 Mon Sep 17 00:00:00 2001 From: Filippo Costa Date: Fri, 17 Mar 2017 14:12:44 +0100 Subject: [PATCH 29/31] Improvements, corrections, and bug fixes for the `lang_IT` module. (#59) * General refactoring and bug fixes for lang_IT * Added Python3 support for lang_IT * Bug fixes for ordinal numerals in lang_IT * Fixed lang_IT for negative values and added tests * Fixes and minor adjustments for floats in lang_IT * Decimal => float in tests for lang_IT * Moved a comment * 'tre's accentuated, big numbers support, ordinal bug fix, comments, tests * 'meno' and 'virgola' and now class values * Destroyed EU inheritance --- num2words/lang_IT.py | 367 +++++++++++++++++++++---------------------- tests/test_it.py | 177 +++++++++++++-------- 2 files changed, 284 insertions(+), 260 deletions(-) diff --git a/num2words/lang_IT.py b/num2words/lang_IT.py index 227883b..8e3cae4 100644 --- a/num2words/lang_IT.py +++ b/num2words/lang_IT.py @@ -16,202 +16,187 @@ from __future__ import unicode_literals from .lang_EU import Num2Word_EU -import re -import math +# Globals +# ------- + +ZERO = "zero" + +CARDINAL_WORDS = [ + ZERO, "uno", "due", "tre", "quattro", "cinque", "sei", "sette", "otto", + "nove", "dieci", "undici", "dodici", "tredici", "quattordici", "quindici", + "sedici", "diciassette", "diciotto", "diciannove" +] + +ORDINAL_WORDS = [ + ZERO, "primo", "secondo", "terzo", "quarto", "quinto", "sesto", "settimo", + "ottavo", "nono", "decimo", "undicesimo", "dodicesimo", "tredicesimo", + "quattordicesimo", "quindicesimo", "sedicesimo", "diciassettesimo", + "diciottesimo", "diciannovesimo" +] + +# The script can extrapolate the missing numbers from the base forms. +STR_TENS = {2: "venti", 3: "trenta", 4: "quaranta", 6: "sessanta"} + +# These prefixes are used for extremely big numbers. +EXPONENT_PREFIXES = [ + ZERO, "m", "b", "tr", "quadr", "quint", "sest", "sett", "ott", "nov", "dec" +] + +# Utils +# ===== + +def phonetic_contraction(string): + return (string + .replace("oo", "o") # ex. "centootto" + .replace("ao", "o") # ex. "settantaotto" + .replace("io", "o") # ex. "ventiotto" + .replace("au", "u") # ex. "trentauno" + ) + +def exponent_length_to_string(exponent_length): + # We always assume `exponent` to be a multiple of 3. If it's not true, then + # Num2Word_IT.big_number_to_cardinal did something wrong. + prefix = EXPONENT_PREFIXES[exponent_length // 6] + if exponent_length % 6 == 0: + return prefix + "ilione" + else: + return prefix + "iliardo" + +def accentuate(string): + # This is inefficient: it may do several rewritings when deleting + # half-sentence accents. However, it is the easiest method and speed is + # not crucial (duh), so... + return " ".join( + # Deletes half-sentence accents and accentuates the last "tre" + [w.replace("tré", "tre")[:-3] + "tré" + # We shouldn't accentuate a single "tre": is has to be a composite + # word. ~~~~~~~~~~ + if w[-3:] == "tre" and len(w) > 3 + # Deletes half-sentence accents anyway + # ~~~~~~~~~~~~~~~~~~~~~~ + else w.replace("tré", "tre") + for w in string.split() + ]) + +def omitt_if_zero(number_to_string): + return "" if number_to_string == ZERO else number_to_string + +# Main class +# ========== + +class Num2Word_IT: + + MINUS_PREFIX_WORD = "meno " + FLOAT_INFIX_WORD = " virgola " -class Num2Word_IT(object): def __init__(self): - self._minus = "meno " - - self._exponent = { - 0 : ('',''), - 3 : ('mille','mila'), - 6 : ('milione','miloni'), - 12 : ('miliardo','miliardi'), - 18 : ('trillone','trilloni'), - 24 : ('quadrilione','quadrilioni')} - - self._digits = ['zero', 'uno', 'due', 'tre', 'quattro', 'cinque', 'sei', 'sette', 'otto', 'nove'] - - self._sep = '' - - def _toWords(self, num, power=0): - str_num = str(num) - # The return string; - ret = '' - - # add a the word for the minus sign if necessary - if num < 0: - ret = self._sep + self._minus - - if len(str_num) > 6: - current_power = 6 - # check for highest power - if power in self._exponent: - # convert the number above the first 6 digits - # with it's corresponding $power. - snum = str_num[0:-6] - if snum != '': - ret = ret + self._toWords(int(snum), power + 6) - - num = int(str_num[-6:]) - if num == 0: - return ret - - elif num == 0 or str_num == '': - return ' ' + self._digits[0] + ' ' - else: - current_power = len(str_num) - - # See if we need "thousands" - thousands = math.floor(num / 1000) - if thousands == 1: - ret = ret + self._sep + 'mille' + self._sep - elif thousands > 1: - ret = ret + self._toWords(int(thousands), 3) + self._sep - - # values for digits, tens and hundreds - h = int(math.floor((num / 100) % 10)) - t = int(math.floor((num / 10) % 10)) - d = int(math.floor(num % 10)) - - # centinaia: duecento, trecento, etc... - if h == 1: - if ((d==0) and (t == 0)):# is it's '100' use 'cien' - ret = ret + self._sep + 'cento' - else: - ret = ret + self._sep + 'cento' - elif h == 2 or h == 3 or h == 4 or h == 6 or h == 8: - ret = ret + self._sep + self._digits[h] + 'cento' - elif h == 5: - ret = ret + self._sep + 'cinquecento' - elif h == 7: - ret = ret + self._sep + 'settecento' - elif h == 9: - ret = ret + self._sep + 'novecento' - - # decine: venti trenta, etc... - if t == 9: - if d == 1 or d == 8: - ret = ret + self._sep + 'novant' - else: - ret = ret + self._sep + 'novanta' - if t == 8: - if d == 1 or d == 8: - ret = ret + self._sep + 'ottant' - else: - ret = ret + self._sep + 'ottanta' - if t == 7: - if d == 1 or d == 8: - ret = ret + self._sep + 'settant' - else: - ret = ret + self._sep + 'settanta' - if t == 6: - if d == 1 or d == 8: - ret = ret + self._sep + 'sessant' - else: - ret = ret + self._sep + 'sessanta' - if t == 5: - if d == 1 or d == 8: - ret = ret + self._sep + 'cinquant' - else: - ret = ret + self._sep + 'cinquanta' - if t == 4: - if d == 1 or d == 8: - ret = ret + self._sep + 'quarant' - else: - ret = ret + self._sep + 'quaranta' - if t == 3: - if d == 1 or d == 8: - ret = ret + self._sep + 'trent' - else: - ret = ret + self._sep + 'trenta' - if t == 2: - if d == 0: - ret = ret + self._sep + 'venti' - elif (d == 1 or d == 8): - ret = ret + self._sep + 'vent' + self._digits[d] - else: - ret = ret + self._sep + 'venti' + self._digits[d] - if t == 1: - if d == 0: - ret = ret + self._sep + 'dieci' - elif d == 1: - ret = ret + self._sep + 'undici' - elif d == 2: - ret = ret + self._sep + 'dodici' - elif d == 3: - ret = ret + self._sep + 'tredici' - elif d == 4: - ret = ret + self._sep + 'quattordici' - elif d == 5: - ret = ret + self._sep + 'quindici' - elif d == 6: - ret = ret + self._sep + 'sedici' - elif d == 7: - ret = ret + self._sep + 'diciassette' - elif d == 8: - ret = ret + self._sep + 'diciotto' - elif d == 9: - ret = ret + self._sep + 'diciannove' - - # add digits only if it is a multiple of 10 and not 1x or 2x - if t != 1 and t != 2 and d > 0: - # don't add 'e' for numbers below 10 - if t != 0: - # use 'un' instead of 'uno' when there is a suffix ('mila', 'milloni', etc...) - if (power > 0) and ( d == 1): - ret = ret + self._sep + 'e un' - else: - ret = ret + self._sep + '' + self._digits[d] - else: - if power > 0 and d == 1: - ret = ret + self._sep + 'un ' - else: - ret = ret + self._sep + self._digits[d] - - if power > 0: - if power in self._exponent: - lev = self._exponent[power] - - if lev is None: - return None - - # if it's only one use the singular suffix - if d == 1 and t == 0 and h == 0: - suffix = lev[0] - else: - suffix = lev[1] - - if num != 0: - ret = ret + self._sep + suffix - - return ret - - - def to_cardinal(self, number): - return self._toWords(number) - - def to_ordinal_num(self, number): pass - def to_ordinal(self,value): - if 0 <= value <= 10: - return ["primo", "secondo", "terzo", "quarto", "quinto", "sesto", "settimo", "ottavo", "nono", "decimo"][value - 1] + def float_to_words(self, float_number, ordinal=False): + if ordinal: + prefix = self.to_ordinal(int(float_number)) else: - as_word = self._toWords(value) - if as_word.endswith("dici"): - return re.sub("dici$", "dicesimo", as_word) - elif as_word.endswith("to"): - return re.sub("to$", "tesimo", as_word) - elif as_word.endswith("ta"): - return re.sub("ta$", "tesimo", as_word) + prefix = self.to_cardinal(int(float_number)) + postfix = " ".join( + # Drops the trailing zero and comma ~~~~ + [self.to_cardinal(int(c)) for c in str(float_number % 1)[2:]] + ) + return prefix + Num2Word_IT.FLOAT_INFIX_WORD + postfix + + def tens_to_cardinal(self, number): + tens = number // 10 + units = number % 10 + if tens in STR_TENS: + prefix = STR_TENS[tens] + else: + prefix = CARDINAL_WORDS[tens][:-1] + "anta" + postfix = omitt_if_zero(CARDINAL_WORDS[units]) + return phonetic_contraction(prefix + postfix) + + def hundreds_to_cardinal(self, number): + hundreds = number // 100 + prefix = "cento" + if hundreds != 1: + prefix = CARDINAL_WORDS[hundreds] + prefix + postfix = omitt_if_zero(self.to_cardinal(number % 100)) + return phonetic_contraction(prefix + postfix) + + def thousands_to_cardinal(self, number): + thousands = number // 1000 + if thousands == 1: + prefix = "mille" + else: + prefix = self.to_cardinal(thousands) + "mila" + postfix = omitt_if_zero(self.to_cardinal(number % 1000)) + # "mille" and "mila" don't need any phonetic contractions + return prefix + postfix + + def big_number_to_cardinal(self, number): + digits = [c for c in str(number)] + length = len(digits) + if length >= 66: + raise NotImplementedError("The given number is too large.") + # This is how many digits come before the "illion" term. + # cento miliardi => 3 + # dieci milioni => 2 + # un miliardo => 1 + predigits = length % 3 or 3 + multiplier = digits[:predigits] + exponent = digits[predigits:] + # Default infix string: "milione", "biliardo", "sestilione", ecc. + infix = exponent_length_to_string(len(exponent)) + if multiplier == ["1"]: + prefix = "un " + else: + prefix = self.to_cardinal(int("".join(multiplier))) + # Plural form ~~~~~~~~~~~ + infix = " " + infix[:-1] + "i" + # Read as: Does the value of exponent equal 0? + if set(exponent) != set("0"): + postfix = self.to_cardinal(int("".join(exponent))) + if " e " in postfix: + infix += ", " else: - return as_word + "simo" + infix += " e " + else: + postfix = "" + return prefix + infix + postfix + def to_cardinal(self, number): + if number < 0: + string = Num2Word_IT.MINUS_PREFIX_WORD + self.to_cardinal(-number) + elif number % 1 != 0: + string = self.float_to_words(number) + elif number < 20: + string = CARDINAL_WORDS[number] + elif number < 100: + string = self.tens_to_cardinal(number) + elif number < 1000: + string = self.hundreds_to_cardinal(number) + elif number < 1000000: + string = self.thousands_to_cardinal(number) + else: + string = self.big_number_to_cardinal(number) + return accentuate(string) -n2w = Num2Word_IT() -to_card = n2w.to_cardinal -to_ord = n2w.to_ordinal -to_ordnum = n2w.to_ordinal_num - + def to_ordinal(self, number): + tens = number % 100 + # Italian grammar is poorly defined here ¯\_(ツ)_/¯: + # centodecimo VS centodieciesimo VS centesimo decimo? + is_outside_teens = not 10 < tens < 20 + if number < 0: + return Num2Word_IT.MINUS_PREFIX_WORD + self.to_ordinal(-number) + elif number % 1 != 0: + return self.float_to_words(number, ordinal=True) + elif number < 20: + return ORDINAL_WORDS[number] + elif is_outside_teens and tens % 10 == 3: + # Gets ride of the accent ~~~~~~~~~~ + return self.to_cardinal(number)[:-1] + "eesimo" + elif is_outside_teens and tens % 10 == 6: + return self.to_cardinal(number) + "esimo" + else: + string = self.to_cardinal(number)[:-1] + if string[-3:] == "mil": + string += "l" + return string + "esimo" diff --git a/tests/test_it.py b/tests/test_it.py index 57bf646..be03634 100644 --- a/tests/test_it.py +++ b/tests/test_it.py @@ -15,82 +15,121 @@ # MA 02110-1301 USA from __future__ import unicode_literals - from unittest import TestCase - from num2words import num2words class Num2WordsITTest(TestCase): - def test_number(self): + maxDiff = None - test_cases = ( - (1,'uno'), - (2,'due'), - (3,'tre'), - (11,'undici'), - (12,'dodici'), - (16,'sedici'), - (19,'diciannove'), - (20,'venti'), - (21,'ventuno'), - (26,'ventisei'), - (28,'ventotto'), - (30,'trenta'), - (31,'trentuno'), - (40,'quaranta'), - (43,'quarantatre'), - (50,'cinquanta'), - (55,'cinquantacinque'), - (60,'sessanta'), - (67,'sessantasette'), - (70,'settanta'), - (79,'settantanove'), - (100,'cento'), - (101,'centouno'), - (199,'centonovantanove'), - (203,'duecentotre'), - (287,'duecentoottantasette'), - (300,'trecento'), - (356,'trecentocinquantasei'), - (410,'quattrocentodieci'), - (434,'quattrocentotrentaquattro'), - (578,'cinquecentosettantotto'), - (689,'seicentoottantanove'), - (729,'settecentoventinove'), - (894,'ottocentonovantaquattro'), - (999,'novecentonovantanove'), - (1000,'mille'), - (1001,'milleuno'), - (1097,'millenovantasette'), - (1104,'millecentoquattro'), - (1243,'milleduecentoquarantatre'), - (2385,'duemilatrecentoottantacinque'), - (3766,'tremilasettecentosessantasei'), - (4196,'quattromilacentonovantasei'), - (5846,'cinquemilaottocentoquarantasei'), - (6459,'seimilaquattrocentocinquantanove'), - (7232,'settemiladuecentotrentadue'), - (8569,'ottomilacinquecentosessantanove'), - (9539,'novemilacinquecentotrentanove'), - (1000000,'un milione'), - (1000001,'un milioneuno'), - # (1000000100,'un miliardocento'), # DOES NOT WORK TODO: FIX - ) + def test_negative(self): + number = 648972145 + pos_crd = num2words(+number, lang="it") + neg_crd = num2words(-number, lang="it") + pos_ord = num2words(+number, lang="it", ordinal=True) + neg_ord = num2words(-number, lang="it", ordinal=True) + self.assertEqual("meno " + pos_crd, neg_crd) + self.assertEqual("meno " + pos_ord, neg_ord) - for test in test_cases: - self.assertEqual(num2words(test[0], lang='it'), test[1]) + def test_float_to_cardinal(self): + self.assertTrue("tre virgola uno quattro uno" in num2words(3.1415, lang="it")) + self.assertTrue("meno cinque virgola uno" in num2words(-5.15, lang="it")) + self.assertTrue("meno zero virgola uno" in num2words(-0.15, lang="it")) - def test_ordinal(self): + def test_float_to_ordinal(self): + self.assertTrue("terzo virgola uno quattro uno" in num2words(3.1415, lang="it", ordinal=True)) + self.assertTrue("meno quinto virgola uno" in num2words(-5.15, lang="it", ordinal=True)) + self.assertTrue("meno zero virgola uno" in num2words(-0.15, lang="it", ordinal=True)) - test_cases = ( - (1,'primo'), - (8,'ottavo'), - (12,'dodicesimo'), - (14,'quattordicesimo'), - (28,'ventottesimo'), - (100,'centesimo'), - ) + def test_0(self): + self.assertEqual(num2words(0, lang="it"), "zero") + self.assertEqual(num2words(0, lang="it", ordinal=True), "zero") - for test in test_cases: - self.assertEqual(num2words(test[0], lang='it', ordinal=True), test[1]) + def test_1_to_10(self): + self.assertEqual(num2words(1, lang="it"), "uno") + self.assertEqual(num2words(2, lang="it"), "due") + self.assertEqual(num2words(7, lang="it"), "sette") + self.assertEqual(num2words(10, lang="it"), "dieci") + + def test_11_to_19(self): + self.assertEqual(num2words(11, lang="it"), "undici") + self.assertEqual(num2words(13, lang="it"), "tredici") + self.assertEqual(num2words(15, lang="it"), "quindici") + self.assertEqual(num2words(16, lang="it"), "sedici") + self.assertEqual(num2words(19, lang="it"), "diciannove") + + def test_20_to_99(self): + self.assertEqual(num2words(20, lang="it"), "venti") + self.assertEqual(num2words(23, lang="it"), "ventitré") + self.assertEqual(num2words(28, lang="it"), "ventotto") + self.assertEqual(num2words(31, lang="it"), "trentuno") + self.assertEqual(num2words(40, lang="it"), "quaranta") + self.assertEqual(num2words(66, lang="it"), "sessantasei") + self.assertEqual(num2words(92, lang="it"), "novantadue") + + def test_100_to_999(self): + self.assertEqual(num2words(100, lang="it"), "cento") + self.assertEqual(num2words(111, lang="it"), "centoundici") + self.assertEqual(num2words(150, lang="it"), "centocinquanta") + self.assertEqual(num2words(196, lang="it"), "centonovantasei") + self.assertEqual(num2words(200, lang="it"), "duecento") + self.assertEqual(num2words(210, lang="it"), "duecentodieci") + self.assertEqual(num2words(701, lang="it"), "settecentouno") + + def test_1000_to_9999(self): + self.assertEqual(num2words(1000, lang="it"), "mille") + self.assertEqual(num2words(1001, lang="it"), "milleuno") + self.assertEqual(num2words(1500, lang="it"), "millecinquecento") + self.assertEqual(num2words(7378, lang="it"), "settemilatrecentosettantotto") + self.assertEqual(num2words(2000, lang="it"), "duemila") + self.assertEqual(num2words(2100, lang="it"), "duemilacento") + self.assertEqual(num2words(6870, lang="it"), "seimilaottocentosettanta") + self.assertEqual(num2words(10000, lang="it"), "diecimila") + self.assertEqual(num2words(98765, lang="it"), "novantottomilasettecentosessantacinque") + self.assertEqual(num2words(100000, lang="it"), "centomila") + self.assertEqual(num2words(523456, lang="it"), "cinquecentoventitremilaquattrocentocinquantasei") + + def test_big(self): + self.assertEqual(num2words(1000000, lang="it"), "un milione") + self.assertEqual(num2words(1000007, lang="it"), "un milione e sette") + self.assertEqual(num2words(1200000, lang="it"), "un milione e duecentomila") + self.assertEqual(num2words(3000000, lang="it"), "tre milioni") + self.assertEqual(num2words(3000005, lang="it"), "tre milioni e cinque") + self.assertEqual(num2words(3800000, lang="it"), "tre milioni e ottocentomila") + self.assertEqual(num2words(1000000000, lang="it"), "un miliardo") + self.assertEqual(num2words(1000000017, lang="it"), "un miliardo e diciassette") + self.assertEqual(num2words(2000000000, lang="it"), "due miliardi") + self.assertEqual(num2words(2000001000, lang="it"), "due miliardi e mille") + self.assertEqual(num2words(1234567890, lang="it"), "un miliardo, duecentotrentaquattro milioni e cinquecentosessantasettemilaottocentonovanta") + self.assertEqual(num2words(1000000000000, lang="it"), "un bilione") + self.assertEqual(num2words(123456789012345678901234567890, lang="it"), "centoventitré quadriliardi, quattrocentocinquantasei quadrilioni, settecentottantanove triliardi, dodici trilioni, trecentoquarantacinque biliardi, seicentosettantotto bilioni, novecentouno miliardi, duecentotrentaquattro milioni e cinquecentosessantasettemilaottocentonovanta") + + def test_nth_1_to_99(self): + self.assertEqual(num2words(1, lang="it", ordinal=True), "primo") + self.assertEqual(num2words(8, lang="it", ordinal=True), "ottavo") + self.assertEqual(num2words(23, lang="it", ordinal=True), "ventitreesimo") + self.assertEqual(num2words(47, lang="it", ordinal=True), "quarantasettesimo") + self.assertEqual(num2words(99, lang="it", ordinal=True), "novantanovesimo") + + def test_nth_100_to_999(self): + self.assertEqual(num2words(100, lang="it", ordinal=True), "centesimo") + self.assertEqual(num2words(112, lang="it", ordinal=True), "centododicesimo") + self.assertEqual(num2words(120, lang="it", ordinal=True), "centoventesimo") + self.assertEqual(num2words(316, lang="it", ordinal=True), "trecentosedicesimo") + self.assertEqual(num2words(700, lang="it", ordinal=True), "settecentesimo") + self.assertEqual(num2words(803, lang="it", ordinal=True), "ottocentotreesimo") + self.assertEqual(num2words(923, lang="it", ordinal=True), "novecentoventitreesimo") + + def test_nth_1000_to_999999(self): + self.assertEqual(num2words(1000, lang="it", ordinal=True), "millesimo") + self.assertEqual(num2words(1001, lang="it", ordinal=True), "milleunesimo") + self.assertEqual(num2words(1003, lang="it", ordinal=True), "milletreesimo") + self.assertEqual(num2words(1200, lang="it", ordinal=True), "milleduecentesimo") + self.assertEqual(num2words(8640, lang="it", ordinal=True), "ottomilaseicentoquarantesimo") + self.assertEqual(num2words(14000, lang="it", ordinal=True), "quattordicimillesimo") + self.assertEqual(num2words(123456, lang="it", ordinal=True), "centoventitremilaquattrocentocinquantaseiesimo") + self.assertEqual(num2words(987654, lang="it", ordinal=True), "novecentottantasettemilaseicentocinquantaquattresimo") + + def test_nth_big(self): + self.assertEqual(num2words(1000000001, lang="it", ordinal=True), "un miliardo e unesimo") + self.assertEqual(num2words(123456789012345678901234567890, lang="it", ordinal=True), "centoventitré quadriliardi, quattrocentocinquantasei quadrilioni, settecentottantanove triliardi, dodici trilioni, trecentoquarantacinque biliardi, seicentosettantotto bilioni, novecentouno miliardi, duecentotrentaquattro milioni e cinquecentosessantasettemilaottocentonovantesimo") From c2148d15efda2196a97470de62c8f92567b660ee Mon Sep 17 00:00:00 2001 From: Diep Huu Hoang Date: Sun, 19 Mar 2017 22:41:41 +0700 Subject: [PATCH 30/31] [ADD] support VietNam --- num2words/__init__.py | 4 +- num2words/lang_VN.py | 92 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 num2words/lang_VN.py diff --git a/num2words/__init__.py b/num2words/__init__.py index 57c4d57..dfb91d0 100644 --- a/num2words/__init__.py +++ b/num2words/__init__.py @@ -33,6 +33,7 @@ from . import lang_DK from . import lang_PT_BR from . import lang_HE from . import lang_IT +from . import lang_VN CONVERTER_CLASSES = { 'en': lang_EN.Num2Word_EN(), @@ -51,7 +52,8 @@ CONVERTER_CLASSES = { 'dk': lang_DK.Num2Word_DK(), 'pt_BR': lang_PT_BR.Num2Word_PT_BR(), 'he': lang_HE.Num2Word_HE(), - 'it': lang_IT.Num2Word_IT() + 'it': lang_IT.Num2Word_IT(), + 'vi_VN': lang_VN.Num2Word_VN() } def num2words(number, ordinal=False, lang='en'): diff --git a/num2words/lang_VN.py b/num2words/lang_VN.py new file mode 100644 index 0000000..c8bdb4f --- /dev/null +++ b/num2words/lang_VN.py @@ -0,0 +1,92 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2003, Taro Ogawa. All Rights Reserved. +# Copyright (c) 2013, 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 + + +to_19 = (u'không', u'một', u'hai', u'ba', u'bốn', u'năm', u'sáu', + u'bảy', u'tám', u'chín', u'mười', u'mười một', u'mười hai', + u'mười ba', u'mười bốn', u'mười lăm', u'mười sáu', u'mười bảy', + u'mười tám', u'mười chín') +tens = (u'hai mươi', u'ba mươi', u'bốn mươi', u'năm mươi', + u'sáu mươi', u'bảy mươi', u'tám mươi', u'chín mươi') +denom = ('', + u'nghìn', u'triệu', u'tỷ', u'nghìn tỷ', u'trăm nghìn tỷ', + 'Quintillion', 'Sextillion', 'Septillion', 'Octillion', 'Nonillion', + 'Decillion', 'Undecillion', 'Duodecillion', 'Tredecillion', + 'Quattuordecillion', 'Sexdecillion', 'Septendecillion', + 'Octodecillion', 'Novemdecillion', 'Vigintillion') + + +class Num2Word_VN(object): + + # convert a value < 100 to English. + def _convert_nn(self, val): + if val < 20: + return to_19[val] + for (dcap, dval) in ((k, 20 + (10 * v)) for (v, k) in enumerate(tens)): + if dval + 10 > val: + if val % 10: + a = u'lăm' + if to_19[val % 10] == u'một': + a = u'mốt' + else: + a = to_19[val % 10] + return dcap + ' ' + a + return dcap + + def _convert_nnn(self, val): + word = '' + (mod, rem) = (val % 100, val // 100) + if rem > 0: + word = to_19[rem] + u' trăm' + if mod > 0: + word = word + ' ' + if mod > 0: + word = word + self._convert_nn(mod) + return word + + def vietnam_number(self, val): + if val < 100: + return self._convert_nn(val) + if val < 1000: + return self._convert_nnn(val) + for (didx, dval) in ((v - 1, 1000 ** v) for v in range(len(denom))): + if dval > val: + mod = 1000 ** didx + l = val // mod + r = val - (l * mod) + ret = self._convert_nnn(l) + ' ' + denom[didx] + if r > 0: + ret = ret + ' ' + self.vietnam_number(r) + return ret + + def number_to_text(self, number): + number = '%.2f' % number + the_list = str(number).split('.') + start_word = self.vietnam_number(int(the_list[0])) + final_result = start_word + if len(the_list) > 1 and int(the_list[1]) > 0: + end_word = self.vietnam_number(int(the_list[1])) + final_result = final_result + ' phẩy ' + end_word + return final_result + + def to_cardinal(self, number): + return self.number_to_text(number) + + def to_ordinal(self, number): + return self.to_cardinal(number) From c200d97bd72a74becf46c36eaa667d6c251beeb0 Mon Sep 17 00:00:00 2001 From: Diep Huu Hoang Date: Sat, 1 Apr 2017 12:30:54 +0700 Subject: [PATCH 31/31] [IMP] Improve convert number method and add unittest for Vietnam lang --- num2words/lang_VN.py | 16 +++++-- tests/test_vn.py | 106 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 tests/test_vn.py diff --git a/num2words/lang_VN.py b/num2words/lang_VN.py index c8bdb4f..51b6d88 100644 --- a/num2words/lang_VN.py +++ b/num2words/lang_VN.py @@ -34,7 +34,6 @@ denom = ('', class Num2Word_VN(object): - # convert a value < 100 to English. def _convert_nn(self, val): if val < 20: return to_19[val] @@ -46,6 +45,8 @@ class Num2Word_VN(object): a = u'mốt' else: a = to_19[val % 10] + if to_19[val % 10] == u'năm': + a = u'lăm' return dcap + ' ' + a return dcap @@ -56,7 +57,13 @@ class Num2Word_VN(object): word = to_19[rem] + u' trăm' if mod > 0: word = word + ' ' - if mod > 0: + if mod > 0 and mod < 10: + if mod == 5: + word = word != '' and word + u'lẻ năm' or word + u'năm' + else: + word = word != '' and word + u'lẻ ' \ + + self._convert_nn(mod) or word + self._convert_nn(mod) + if mod >= 10: word = word + self._convert_nn(mod) return word @@ -70,7 +77,10 @@ class Num2Word_VN(object): mod = 1000 ** didx l = val // mod r = val - (l * mod) - ret = self._convert_nnn(l) + ' ' + denom[didx] + + ret = self._convert_nnn(l) + u' ' + denom[didx] + if r > 0 and r <= 99: + ret = self._convert_nnn(l) + u' ' + denom[didx] + u' lẻ' if r > 0: ret = ret + ' ' + self.vietnam_number(r) return ret diff --git a/tests/test_vn.py b/tests/test_vn.py new file mode 100644 index 0000000..bf357b3 --- /dev/null +++ b/tests/test_vn.py @@ -0,0 +1,106 @@ +# -*- encoding: utf-8 -*- +# Copyright (c) 2015, 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 + +class Num2WordsVNTest(TestCase): + + + def test_0(self): + self.assertEqual(num2words(0, lang="vi_VN"), "không") + + def test_1_to_10(self): + self.assertEqual(num2words(1, lang="vi_VN"), "một") + self.assertEqual(num2words(2, lang="vi_VN"), "hai") + self.assertEqual(num2words(7, lang="vi_VN"), "bảy") + self.assertEqual(num2words(10, lang="vi_VN"), "mười") + + def test_11_to_19(self): + self.assertEqual(num2words(11, lang="vi_VN"), "mười một") + self.assertEqual(num2words(13, lang="vi_VN"), "mười ba") + self.assertEqual(num2words(14, lang="vi_VN"), "mười bốn") + self.assertEqual(num2words(15, lang="vi_VN"), "mười lăm") + self.assertEqual(num2words(16, lang="vi_VN"), "mười sáu") + self.assertEqual(num2words(19, lang="vi_VN"), "mười chín") + + def test_20_to_99(self): + self.assertEqual(num2words(20, lang="vi_VN"), "hai mươi") + self.assertEqual(num2words(23, lang="vi_VN"), "hai mươi ba") + self.assertEqual(num2words(28, lang="vi_VN"), "hai mươi tám") + self.assertEqual(num2words(31, lang="vi_VN"), "ba mươi mốt") + self.assertEqual(num2words(40, lang="vi_VN"), "bốn mươi") + self.assertEqual(num2words(66, lang="vi_VN"), "sáu mươi sáu") + self.assertEqual(num2words(92, lang="vi_VN"), "chín mươi hai") + + def test_100_to_999(self): + self.assertEqual(num2words(100, lang="vi_VN"), "một trăm") + self.assertEqual(num2words(150, lang="vi_VN"), "một trăm năm mươi") + self.assertEqual(num2words(196, lang="vi_VN"), "một trăm chín mươi sáu") + self.assertEqual(num2words(200, lang="vi_VN"), "hai trăm") + self.assertEqual(num2words(210, lang="vi_VN"), "hai trăm mười") + + def test_1000_to_9999(self): + self.assertEqual(num2words(1000, lang="vi_VN"), "một nghìn") + self.assertEqual(num2words(1500, lang="vi_VN"), "một nghìn năm trăm") + self.assertEqual(num2words(7378, lang="vi_VN"), "bảy nghìn ba trăm bảy mươi tám") + self.assertEqual(num2words(2000, lang="vi_VN"), "hai nghìn") + self.assertEqual(num2words(2100, lang="vi_VN"), "hai nghìn một trăm") + self.assertEqual(num2words(6870, lang="vi_VN"), "sáu nghìn tám trăm bảy mươi") + self.assertEqual(num2words(10000, lang="vi_VN"), "mười nghìn") + self.assertEqual(num2words(100000, lang="vi_VN"), "một trăm nghìn") + self.assertEqual(num2words(523456, lang="vi_VN"), "năm trăm hai mươi ba nghìn bốn trăm năm mươi sáu") + + def test_big(self): + self.assertEqual(num2words(1000000, lang="vi_VN"), "một triệu") + self.assertEqual(num2words(1200000, lang="vi_VN"), "một triệu hai trăm nghìn") + self.assertEqual(num2words(3000000, lang="vi_VN"), "ba triệu") + self.assertEqual(num2words(3800000, lang="vi_VN"), "ba triệu tám trăm nghìn") + self.assertEqual(num2words(1000000000, lang="vi_VN"), "một tỷ") + self.assertEqual(num2words(2000000000, lang="vi_VN"), "hai tỷ") + self.assertEqual(num2words(2000001000, lang="vi_VN"), "hai tỷ một nghìn") + self.assertEqual(num2words(1234567890, lang="vi_VN"), "một tỷ hai trăm ba mươi bốn triệu năm trăm sáu mươi bảy nghìn tám trăm chín mươi") + + + def test_decimal_number(self): + self.assertEqual(num2words(1000.11, lang="vi_VN"), "một nghìn phẩy mười một") + self.assertEqual(num2words(1000.21, lang="vi_VN"), "một nghìn phẩy hai mươi mốt") + + def test_special_number(self): + """ + Some number will have some specail rule + """ + self.assertEqual(num2words(21, lang="vi_VN"), "hai mươi mốt") + self.assertEqual(num2words(25, lang="vi_VN"), "hai mươi lăm") + # >100 + self.assertEqual(num2words(101, lang="vi_VN"), "một trăm lẻ một") + self.assertEqual(num2words(105, lang="vi_VN"), "một trăm lẻ năm") + self.assertEqual(num2words(701, lang="vi_VN"), "bảy trăm lẻ một") + self.assertEqual(num2words(705, lang="vi_VN"), "bảy trăm lẻ năm") + + # >1000 + self.assertEqual(num2words(1001, lang="vi_VN"), "một nghìn lẻ một") + self.assertEqual(num2words(1005, lang="vi_VN"), "một nghìn lẻ năm") + self.assertEqual(num2words(98765, lang="vi_VN"), "chín mươi tám nghìn bảy trăm sáu mươi lăm") + + # > 1000000 + self.assertEqual(num2words(3000005, lang="vi_VN"), "ba triệu lẻ năm") + self.assertEqual(num2words(1000007, lang="vi_VN"), "một triệu lẻ bảy") + + # > 1000000000 + self.assertEqual(num2words(1000000017, lang="vi_VN"), "một tỷ lẻ mười bảy") + self.assertEqual(num2words(1000101017, lang="vi_VN"), "một tỷ một trăm lẻ một nghìn lẻ mười bảy")