From f5fc17917c7b0c09739e5179ca09db4df0c76085 Mon Sep 17 00:00:00 2001 From: David Cormier Date: Tue, 7 Jul 2015 13:40:05 -0400 Subject: [PATCH 01/38] Add polish to the list of supported languages --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index 580504b..986ff86 100644 --- a/README.rst +++ b/README.rst @@ -49,6 +49,7 @@ Besides the numerical argument, there's two optional arguments. * ``lv`` (Latvian) * ``en_GB`` (British English) * ``en_IN`` (Indian English) +* ``pl`` (Polish) You can supply values like ``fr_FR``, the code will be correctly interpreted. If you supply an unsupported language, ``NotImplementedError`` is raised. From fc5c0dbddef3b7677ce4c24839037ca7f5d2a5e6 Mon Sep 17 00:00:00 2001 From: Brett Anthoine Date: Thu, 29 Oct 2015 16:11:58 +0100 Subject: [PATCH 02/38] Fix typo for 18 in german. --- num2words/lang_DE.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/num2words/lang_DE.py b/num2words/lang_DE.py index fc2653e..e31dc5b 100644 --- a/num2words/lang_DE.py +++ b/num2words/lang_DE.py @@ -44,7 +44,7 @@ class Num2Word_DE(Num2Word_EU): (90, "neunzig"), (80, "achtzig"), (70, "siebzig"), (60, "sechzig"), (50, "f\xFCnfzig"), (40, "vierzig"), (30, "drei\xDFig")] - self.low_numwords = ["zwanzig", "neunzehn", "achtzen", "siebzehn", + self.low_numwords = ["zwanzig", "neunzehn", "achtzehn", "siebzehn", "sechzehn", "f\xFCnfzehn", "vierzehn", "dreizehn", "zw\xF6lf", "elf", "zehn", "neun", "acht", "sieben", "sechs", "f\xFCnf", "vier", "drei", "zwei", "eins", From 797e74d1fc342704d68be15bc69b44f408fed89e Mon Sep 17 00:00:00 2001 From: isnani Date: Sat, 12 Dec 2015 16:54:30 +0100 Subject: [PATCH 03/38] 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 04/38] 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 05/38] 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 01b87602c8b30bc8365ee0c0dbaba6f0bde9bcdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98rjan=20V=C3=B8llestad?= Date: Mon, 11 Jan 2016 19:33:22 +0100 Subject: [PATCH 06/38] added norwegian support --- num2words/__init__.py | 2 + num2words/lang_NO.py | 121 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 num2words/lang_NO.py diff --git a/num2words/__init__.py b/num2words/__init__.py index 1a753ce..10fd631 100644 --- a/num2words/__init__.py +++ b/num2words/__init__.py @@ -25,6 +25,7 @@ from . import lang_ES from . import lang_LT from . import lang_LV from . import lang_PL +from . import lang_NO CONVERTER_CLASSES = { 'en': lang_EN.Num2Word_EN(), @@ -36,6 +37,7 @@ CONVERTER_CLASSES = { 'lt': lang_LT.Num2Word_LT(), 'lv': lang_LV.Num2Word_LV(), 'pl': lang_PL.Num2Word_PL(), + 'no': lang_NO.Num2Word_NO(), } def num2words(number, ordinal=False, lang='en'): diff --git a/num2words/lang_NO.py b/num2words/lang_NO.py new file mode 100644 index 0000000..273622b --- /dev/null +++ b/num2words/lang_NO.py @@ -0,0 +1,121 @@ +# 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 division, unicode_literals +from . import lang_EU + +class Num2Word_NO(lang_EU.Num2Word_EU): + def set_high_numwords(self, high): + max = 3 + 6*len(high) + for word, n in zip(high, range(max, 3, -6)): + self.cards[10**n] = word + "illard" + self.cards[10**(n-3)] = word + "illion" + + def setup(self): + self.negword = "minus " + self.pointword = "komma" + self.errmsg_nornum = "Bare tall kan bli konvertert til ord." + self.exclude_title = ["og", "komma", "minus"] + + self.mid_numwords = [(1000, "tusen"), (100, "hundre"), + (90, "nitti"), (80, "\xe5tti"), (70, "sytti"), + (60, "seksti"), (50, "femti"), (40, "f\xf8rti"), + (30, "tretti")] + self.low_numwords = ["tjue", "nitten", "atten", "sytten", + "seksten", "femten", "fjorten", "tretten", + "tolv", "elleve", "ti", "ni", "\xe5tte", + "syv", "seks", "fem", "fire", "tre", "to", + "en", "null"] + self.ords = { "en" : "f\xf8rste", + "to" : "andre", + "tre" : "tredje", + "fire" : "fjerde", + "fem" : "femte", + "seks" : "sjette", + "syv" : "syvende", + "\xe5tte" : "\xe5ttende", + "ni" : "niende", + "ti" : "tiende", + "elleve" : "ellevte", + "tolv" : "tolvte", + "tjue" : "tjuende" } + + + def merge(self, (ltext, lnum), (rtext, rnum)): + if lnum == 1 and rnum < 100: + return (rtext, rnum) + elif 100 > lnum > rnum : + return ("%s-%s"%(ltext, rtext), lnum + rnum) + elif lnum >= 100 > rnum: + return ("%s og %s"%(ltext, rtext), lnum + rnum) + elif rnum > lnum: + return ("%s %s"%(ltext, rtext), lnum * rnum) + return ("%s, %s"%(ltext, rtext), lnum + rnum) + + + def to_ordinal(self, value): + self.verify_ordinal(value) + outwords = self.to_cardinal(value).split(" ") + lastwords = outwords[-1].split("-") + lastword = lastwords[-1].lower() + try: + lastword = self.ords[lastword] + except KeyError: + if lastword[-2:] == "ti": + lastword = lastword + "ende" + else: + lastword += "de" + lastwords[-1] = self.title(lastword) + outwords[-1] = "".join(lastwords) + return " ".join(outwords) + + + def to_ordinal_num(self, value): + self.verify_ordinal(value) + return "%s%s"%(value, self.to_ordinal(value)[-2:]) + + + def to_year(self, val, longval=True): + if not (val//100)%10: + return self.to_cardinal(val) + return self.to_splitnum(val, hightxt="hundre", jointxt="og", + longval=longval) + + def to_currency(self, val, longval=True): + return self.to_splitnum(val, hightxt="krone/r", lowtxt="\xf8re/r", + jointxt="og", longval=longval, cents = True) + + +n2w = Num2Word_NO() +to_card = n2w.to_cardinal +to_ord = n2w.to_ordinal +to_ordnum = n2w.to_ordinal_num +to_year = n2w.to_year + +def main(): + for val in [ 1, 11, 12, 21, 31, 33, 71, 80, 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, + -21212121211221211111, -2.121212, -1.0000100]: + 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) + + +if __name__ == "__main__": + main() From 1589d9bb79fa8aa0c3a784f557db92112bf615ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=C3=98rjan=20V=C3=B8llestad?= Date: Mon, 11 Jan 2016 19:35:11 +0100 Subject: [PATCH 07/38] added norwegian support --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index 986ff86..f062e9f 100644 --- a/README.rst +++ b/README.rst @@ -49,6 +49,7 @@ Besides the numerical argument, there's two optional arguments. * ``lv`` (Latvian) * ``en_GB`` (British English) * ``en_IN`` (Indian English) +* ``no`` (Norwegian) * ``pl`` (Polish) You can supply values like ``fr_FR``, the code will be From 073925b1d4128da1352a4f908aa2c19402ceb640 Mon Sep 17 00:00:00 2001 From: isnani Date: Fri, 18 Dec 2015 05:36:01 +0100 Subject: [PATCH 08/38] address issue 24: floating point spelling --- num2words/base.py | 7 +++---- tests/test_en.py | 7 +++++++ 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/num2words/base.py b/num2words/base.py index f892d42..960817a 100644 --- a/num2words/base.py +++ b/num2words/base.py @@ -116,16 +116,15 @@ class Num2Word_Base(object): pre = int(value) post = abs(value - pre) + post *= 10**self.precision out = [self.to_cardinal(pre)] if self.precision: out.append(self.title(self.pointword)) for i in range(self.precision): - post *= 10 - curr = int(post) - out.append(str(self.to_cardinal(curr))) - post -= curr + curr = str(post)[i] + out.append(str(self.to_cardinal(int(curr)))) return " ".join(out) diff --git a/tests/test_en.py b/tests/test_en.py index 20eb294..7897e82 100644 --- a/tests/test_en.py +++ b/tests/test_en.py @@ -21,3 +21,10 @@ class Num2WordsENTest(TestCase): def test_and_join_199(self): # ref https://github.com/savoirfairelinux/num2words/issues/8 self.assertEqual(num2words(199), "one hundred and ninety-nine") + + def test_cardinal_for_float_number(self): + # issue 24 + self.assertEqual(num2words(12.50), "twelve point five zero") + self.assertEqual(num2words(12.51), "twelve point five one") + self.assertEqual(num2words(12.53), "twelve point five three") + self.assertEqual(num2words(12.59), "twelve point five nine") \ No newline at end of file From 3d1b8ae42ce61da046df96d096cf3e8e0e630a22 Mon Sep 17 00:00:00 2001 From: Michael Reschke Date: Wed, 24 Feb 2016 19:14:22 +0100 Subject: [PATCH 09/38] Update README.rst --- README.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.rst b/README.rst index 986ff86..96d41c3 100644 --- a/README.rst +++ b/README.rst @@ -63,7 +63,7 @@ Therefore, if you want to call ``num2words`` with a fallback, you can do:: History ------- -``num2words`` is based on an old library , ``pynum2word`` created by Taro Ogawa in 2003. +``num2words`` is based on an old library, ``pynum2word`` created by Taro Ogawa in 2003. Unfortunately, the library stopped being maintained and the author can't be reached. There was another developer, Marius Grigaitis, who in 2011 added Lithuanian support, but didn't take over maintenance of the project. From eeadcc7d5b1d04113d46a30c39db3628e5697667 Mon Sep 17 00:00:00 2001 From: Filipe Waitman Date: Fri, 26 Feb 2016 23:13:40 -0300 Subject: [PATCH 10/38] Add support to pt_BR language --- num2words/__init__.py | 2 + num2words/base.py | 14 +-- num2words/lang_PT_BR.py | 209 ++++++++++++++++++++++++++++++++++++++ tests/test_pt_BR.py | 219 ++++++++++++++++++++++++++++++++++++++++ 4 files changed, 437 insertions(+), 7 deletions(-) create mode 100644 num2words/lang_PT_BR.py create mode 100644 tests/test_pt_BR.py diff --git a/num2words/__init__.py b/num2words/__init__.py index 1a753ce..520b34c 100644 --- a/num2words/__init__.py +++ b/num2words/__init__.py @@ -25,6 +25,7 @@ from . import lang_ES from . import lang_LT from . import lang_LV from . import lang_PL +from . import lang_PT_BR CONVERTER_CLASSES = { 'en': lang_EN.Num2Word_EN(), @@ -36,6 +37,7 @@ CONVERTER_CLASSES = { 'lt': lang_LT.Num2Word_LT(), 'lv': lang_LV.Num2Word_LV(), 'pl': lang_PL.Num2Word_PL(), + 'pt_BR': lang_PT_BR.Num2Word_PT_BR(), } def num2words(number, ordinal=False, lang='en'): diff --git a/num2words/base.py b/num2words/base.py index f892d42..e1327ba 100644 --- a/num2words/base.py +++ b/num2words/base.py @@ -36,7 +36,7 @@ class Num2Word_Base(object): self.set_numwords() self.MAXVAL = 1000 * self.cards.order[0] - + def set_numwords(self): self.set_high_numwords(self.high_numwords) @@ -64,7 +64,7 @@ class Num2Word_Base(object): for elem in self.cards: if elem > value: continue - + out = [] if value == 0: div, mod = 1, 0 @@ -75,7 +75,7 @@ class Num2Word_Base(object): out.append((self.cards[1], 1)) else: if div == value: # The system tallies, eg Roman Numerals - return [(div * self.cards[elem], div*elem)] + return [(div * self.cards[elem], div*elem)] out.append(self.splitnum(div)) out.append((self.cards[elem], elem)) @@ -101,7 +101,7 @@ 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) @@ -124,7 +124,7 @@ class Num2Word_Base(object): for i in range(self.precision): post *= 10 curr = int(post) - out.append(str(self.to_cardinal(curr))) + out.append(self.to_cardinal(curr)) post -= curr return " ".join(out) @@ -183,7 +183,7 @@ class Num2Word_Base(object): def set_wordnums(self): pass - + def to_ordinal(value): return self.to_cardinal(value) @@ -260,6 +260,6 @@ class Num2Word_Base(object): _ordnum = self.to_ordinal_num(value) except: _ordnum = "invalid" - + print ("For %s, card is %s;\n\tord is %s; and\n\tordnum is %s." % (value, _card, _ord, _ordnum)) diff --git a/num2words/lang_PT_BR.py b/num2words/lang_PT_BR.py new file mode 100644 index 0000000..bf4a8f3 --- /dev/null +++ b/num2words/lang_PT_BR.py @@ -0,0 +1,209 @@ +# -*- 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 division, unicode_literals +import re + +from . import lang_EU + + +class Num2Word_PT_BR(lang_EU.Num2Word_EU): + def set_high_numwords(self, high): + max = 3 + 3*len(high) + for word, n in zip(high, range(max, 3, -3)): + self.cards[10**n] = word + "ilhão" + + def setup(self): + self.negword = "menos " + self.pointword = "ponto" + self.errmsg_nornum = "Somente números podem ser convertidos para palavras" + self.exclude_title = ["e", "ponto", "menos"] + + self.mid_numwords = [ + (1000, "mil"), (100, "cem"), (90, "noventa"), + (80, "oitenta"), (70, "setenta"), (60, "sessenta"), (50, "cinquenta"), + (40, "quarenta"), (30, "trinta") + ] + self.low_numwords = [ + "vinte", "dezenove", "dezoito", "dezessete", "dezesseis", + "quinze", "catorze", "treze", "doze", "onze", "dez", + "nove", "oito", "sete", "seis", "cinco", "quatro", "três", "dois", + "um", "zero" + ] + self.ords = [ + { + 0: "", + 1: "primeiro", + 2: "segundo", + 3: "terceiro", + 4: "quarto", + 5: "quinto", + 6: "sexto", + 7: "sétimo", + 8: "oitavo", + 9: "nono", + }, + { + 0: "", + 1: "décimo", + 2: "vigésimo", + 3: "trigésimo", + 4: "quadragésimo", + 5: "quinquagésimo", + 6: "sexagésimo", + 7: "septuagésimo", + 8: "octogésimo", + 9: "nonagésimo", + }, + { + 0: "", + 1: "centésimo", + 2: "ducentésimo", + 3: "tricentésimo", + 4: "quadrigentésimo", + 5: "quingentésimo", + 6: "seiscentésimo", + 7: "septigentésimo", + 8: "octigentésimo", + 9: "nongentésimo", + }, + ] + self.thousand_separators = { + 3: "milésimo", + 6: "milionésimo", + 9: "bilionésimo", + 12: "trilionésimo", + 15: "quadrilionésimo" + } + self.hundreds = { + 1: "cento", + 2: "duzentos", + 3: "trezentos", + 4: "quatrocentos", + 5: "quinhentos", + 6: "seiscentos", + 7: "setecentos", + 8: "oitocentos", + 9: "novecentos", + } + + def merge(self, curr, next): + ctext, cnum, ntext, nnum = curr + next + + if cnum == 1: + if nnum < 1000000: + return next + ctext = "um" + elif cnum == 100 and not nnum == 1000: + ctext = "cento" + + if nnum < cnum: + if cnum < 100: + return ("%s e %s" % (ctext, ntext), cnum + nnum) + return ("%s e %s" % (ctext, ntext), cnum + nnum) + + elif (not nnum % 1000000) and cnum > 1: + ntext = ntext[:-4] + "lhões" + + if nnum == 100: + ctext = self.hundreds[cnum] + ntext = "" + + else: + ntext = " " + ntext + + return (ctext + ntext, cnum * nnum) + + def to_cardinal(self, value): + result = super(Num2Word_PT_BR, self).to_cardinal(value) + + # Transforms "mil E cento e catorze reais" into "mil, cento e catorze reais" + for ext in ( + 'mil', 'milhão', 'milhões', 'bilhão', 'bilhões', + 'trilhão', 'trilhões', 'quatrilhão', 'quatrilhões'): + if re.match('.*{} e \w*ento'.format(ext), result): + result = result.replace('{} e'.format(ext), '{},'.format(ext), 1) + + return result + + def to_ordinal(self, value): + self.verify_ordinal(value) + + result = [] + value = str(value) + thousand_separator = '' + + for idx, char in enumerate(value[::-1]): + if idx and idx % 3 == 0: + thousand_separator = self.thousand_separators[idx] + + if char != '0' and thousand_separator: + # avoiding "segundo milionésimo milésimo" for 6000000, for instance + result.append(thousand_separator) + thousand_separator = '' + + result.append(self.ords[idx % 3][int(char)]) + + result = ' '.join(result[::-1]) + result = result.strip() + result = re.sub('\s+', ' ', result) + + if result.startswith('primeiro') and value != '1': + # avoiding "primeiro milésimo", "primeiro milionésimo" and so on + result = result[9:] + + return result + + def to_ordinal_num(self, value): + self.verify_ordinal(value) + return "%sº" % (value) + + def to_year(self, val, longval=True): + if val < 0: + return self.to_cardinal(abs(val)) + ' antes de Cristo' + return self.to_cardinal(val) + + def to_currency(self, val, longval=True): + integer_part, decimal_part = ('%.2f' % val).split('.') + + result = self.to_cardinal(int(integer_part)) + + appended_currency = False + for ext in ( + 'milhão', 'milhões', 'bilhão', 'bilhões', + 'trilhão', 'trilhões', 'quatrilhão', 'quatrilhões'): + if result.endswith(ext): + result += ' de reais' + appended_currency = True + + if result in ['um', 'menos um']: + result += ' real' + appended_currency = True + if not appended_currency: + result += ' reais' + + if int(decimal_part): + cents = self.to_cardinal(int(decimal_part)) + result += ' e ' + cents + + if cents == 'um': + result += ' centavo' + else: + result += ' centavos' + + return result diff --git a/tests/test_pt_BR.py b/tests/test_pt_BR.py new file mode 100644 index 0000000..d2f4b22 --- /dev/null +++ b/tests/test_pt_BR.py @@ -0,0 +1,219 @@ +# -*- 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 decimal import Decimal +from unittest import TestCase + +from num2words import num2words +from num2words.lang_PT_BR import Num2Word_PT_BR + + +class Num2WordsPTBRTest(TestCase): + def setUp(self): + super(Num2WordsPTBRTest, self).setUp() + 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.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.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.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.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') + + 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') + + 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') + + 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') + + 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.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.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.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.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') + + 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.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.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.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.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') + + 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') + + 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') + + 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') + + 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') + + 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') From 17cf62729e9c98cbe207158bb4fdff66909fbc13 Mon Sep 17 00:00:00 2001 From: Filipe Waitman Date: Sat, 27 Feb 2016 12:36:33 -0300 Subject: [PATCH 11/38] Update README.rst --- README.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/README.rst b/README.rst index 986ff86..7401325 100644 --- a/README.rst +++ b/README.rst @@ -50,6 +50,7 @@ Besides the numerical argument, there's two optional arguments. * ``en_GB`` (British English) * ``en_IN`` (Indian English) * ``pl`` (Polish) +* ``pt_BR`` (Brazilian Portuguese) You can supply values like ``fr_FR``, the code will be correctly interpreted. If you supply an unsupported language, ``NotImplementedError`` is raised. From e8bcc0ef2d78fec6a9f72ecb84ba8d6accd9e400 Mon Sep 17 00:00:00 2001 From: Brett Anthoine Date: Fri, 18 Mar 2016 15:52:30 +0100 Subject: [PATCH 12/38] Add Swiss-French variants of numbers between 60 and 100. --- num2words/__init__.py | 2 + num2words/lang_FR_CH.py | 113 ++++++++++++++++++++++++++++++++++++++++ tests/test_fr_ch.py | 36 +++++++++++++ 3 files changed, 151 insertions(+) create mode 100644 num2words/lang_FR_CH.py create mode 100644 tests/test_fr_ch.py diff --git a/num2words/__init__.py b/num2words/__init__.py index 1a753ce..220f0c3 100644 --- a/num2words/__init__.py +++ b/num2words/__init__.py @@ -20,6 +20,7 @@ from . import lang_EN from . import lang_EN_GB from . import lang_EN_IN from . import lang_FR +from . import lang_FR_CH from . import lang_DE from . import lang_ES from . import lang_LT @@ -31,6 +32,7 @@ CONVERTER_CLASSES = { 'en_GB': lang_EN_GB.Num2Word_EN_GB(), 'en_IN': lang_EN_IN.Num2Word_EN_IN(), 'fr': lang_FR.Num2Word_FR(), + 'fr_CH': lang_FR_CH.Num2Word_FR_CH(), 'de': lang_DE.Num2Word_DE(), 'es': lang_ES.Num2Word_ES(), 'lt': lang_LT.Num2Word_LT(), diff --git a/num2words/lang_FR_CH.py b/num2words/lang_FR_CH.py new file mode 100644 index 0000000..127037d --- /dev/null +++ b/num2words/lang_FR_CH.py @@ -0,0 +1,113 @@ +# -*- 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 +from .lang_EU import Num2Word_EU + +class Num2Word_FR_CH(Num2Word_EU): + def setup(self): + self.negword = "moins " + self.pointword = "virgule" + self.errmsg_nonnum = u"Seulement des nombres peuvent être convertis en mots." + self.errmsg_toobig = u"Nombre trop grand pour être converti en mots." + self.exclude_title = ["et", "virgule", "moins"] + self.mid_numwords = [(1000, "mille"), (100, "cent"), (90, "nonante"), + (80, "huitante"), (70, "septante"), (60, "soixante"), + (50, "cinquante"), (40, "quarante"), + (30, "trente")] + self.low_numwords = ["vingt", "dix-neuf", "dix-huit", "dix-sept", + "seize", "quinze", "quatorze", "treize", "douze", + "onze", "dix", "neuf", "huit", "sept", "six", + "cinq", "quatre", "trois", "deux", "un", "zéro"] + self.ords = { + "cinq": "cinquième", + "neuf": "neuvième", + } + + + def merge(self, curr, next): + ctext, cnum, ntext, nnum = curr + next + + if cnum == 1: + if nnum < 1000000: + return next + else: + # if (not (cnum - 80)%100 or not cnum%100) and ctext[-1] == "s": + # ctext = ctext[:-1] + if (cnum<1000 and nnum != 1000 and ntext[-1] != "s" + and not nnum%100): + ntext += "s" + + if nnum < cnum < 100: + if nnum % 10 == 1:# and cnum != 80: + return ("%s et %s"%(ctext, ntext), cnum + nnum) + return ("%s-%s"%(ctext, ntext), cnum + nnum) + elif nnum > cnum: + return ("%s %s"%(ctext, ntext), cnum * nnum) + return ("%s %s"%(ctext, ntext), cnum + nnum) + + + # Is this right for such things as 1001 - "mille unième" instead of + # "mille premier"?? "millième"?? + + def to_ordinal(self,value): + self.verify_ordinal(value) + if value == 1: + return "premier" + word = self.to_cardinal(value) + for src, repl in self.ords.items(): + if word.endswith(src): + word = word[:-len(src)] + repl + break + else: + if word[-1] == "e": + word = word[:-1] + word = word + "ième" + return word + + def to_ordinal_num(self, value): + self.verify_ordinal(value) + out = str(value) + out += {"1" : "er" }.get(out[-1], "me") + return out + + def to_currency(self, val, longval=True, old=False): + hightxt = "Euro/s" + if old: + hightxt="franc/s" + return self.to_splitnum(val, hightxt=hightxt, lowtxt="centime/s", + jointxt="et",longval=longval) + +n2w = Num2Word_FR_CH() +to_card = n2w.to_cardinal +to_ord = n2w.to_ordinal +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, + 180, 300, 308, 832, 1000, 1001, 1061, 1100, 1500, 1701, 3000, + 8280, 8291, 150000, 500000, 1000000, 2000000, 2000001, + -21212121211221211111, -2.121212, -1.0000100]: + n2w.test(val) + + n2w.test(1325325436067876801768700107601001012212132143210473207540327057320957032975032975093275093275093270957329057320975093272950730) + print n2w.to_currency(112121) + print n2w.to_year(1996) + + +if __name__ == "__main__": + main() diff --git a/tests/test_fr_ch.py b/tests/test_fr_ch.py new file mode 100644 index 0000000..97ba4e5 --- /dev/null +++ b/tests/test_fr_ch.py @@ -0,0 +1,36 @@ +# -*- 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 Num2WordsENTest(TestCase): + def test_ordinal_special_joins(self): + self.assertEqual(num2words(5, ordinal=True, lang='fr_CH'), "cinquième") + self.assertEqual(num2words(6, ordinal=True, lang='fr_CH'), "sixième") + self.assertEqual(num2words(35, ordinal=True, lang='fr_CH'), "trente-cinquième") + self.assertEqual(num2words(9, ordinal=True, lang='fr_CH'), "neuvième") + self.assertEqual(num2words(49, ordinal=True, lang='fr_CH'), "quarante-neuvième") + self.assertEqual(num2words(71, lang='fr_CH'), "septante et un") + self.assertEqual(num2words(81, lang='fr_CH'), "huitante et un") + self.assertEqual(num2words(80, lang='fr_CH'), "huitante") + self.assertEqual(num2words(880, lang='fr_CH'), "huit cents huitante") + self.assertEqual(num2words(91, ordinal=True, lang='fr_CH'), "nonante et unième") + self.assertEqual(num2words(53, lang='fr_CH'), "cinquante-trois") + From 51d34ce78eda8831b905fc29d0411bb173331090 Mon Sep 17 00:00:00 2001 From: mdc <201205975@post.au.dk> Date: Sat, 19 Mar 2016 22:21:14 +0100 Subject: [PATCH 13/38] Added functions that need to be overriden to lang_DK, created TODO's, added lang_DK to __init__ and added documentation to README --- README.rst | 1 + num2words/__init__.py | 2 + num2words/lang_DK.py | 172 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 175 insertions(+) create mode 100644 num2words/lang_DK.py diff --git a/README.rst b/README.rst index 96d41c3..d62183e 100644 --- a/README.rst +++ b/README.rst @@ -50,6 +50,7 @@ Besides the numerical argument, there's two optional arguments. * ``en_GB`` (British English) * ``en_IN`` (Indian English) * ``pl`` (Polish) +* ``dk`` (Danish) 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 220f0c3..acfa839 100644 --- a/num2words/__init__.py +++ b/num2words/__init__.py @@ -26,6 +26,7 @@ from . import lang_ES from . import lang_LT from . import lang_LV from . import lang_PL +from . import lang_DK CONVERTER_CLASSES = { 'en': lang_EN.Num2Word_EN(), @@ -38,6 +39,7 @@ CONVERTER_CLASSES = { 'lt': lang_LT.Num2Word_LT(), 'lv': lang_LV.Num2Word_LV(), 'pl': lang_PL.Num2Word_PL(), + 'dk': lang_DK.Num2Word_DK(), } def num2words(number, ordinal=False, lang='en'): diff --git a/num2words/lang_DK.py b/num2words/lang_DK.py new file mode 100644 index 0000000..7d30732 --- /dev/null +++ b/num2words/lang_DK.py @@ -0,0 +1,172 @@ +# 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 division, unicode_literals +from num2words import lang_EU + +class Num2Word_DK(lang_EU.Num2Word_EU): + def set_high_numwords(self, high): + max = 3 + 6*len(high) + for word, n in zip(high, range(max, 3, -6)): + self.cards[10**n] = word + "illard" + self.cards[10**(n-3)] = word + "illion" + + def setup(self): + self.negword = "minus " + self.pointword = "komma" + self.errmsg_nornum = "Kun tal kan blive konverteret til ord." + self.exclude_title = ["og", "komma", "minus"] + + self.mid_numwords = [(1000, "tusind"), (100, "hundrede"), + (90, "nitten"), (80, "firs"), (70, "halvfjerds"), + (60, "treds"), (50, "halvtreds"), (40, "fyrre"), + (30, "tredive")] + self.low_numwords = ["tyve", "nitten", "atten", "sytten", + "seksten", "femten", "fjorten", "tretten", + "tolv", "elleve", "ti", "ni", "\xe5tte", + "syv", "seks", "fem", "fire", "tre", "to", + "et", "nul"] + self.ords = { "en" : "f\xf8rste", + "to" : "anden", + "tre" : "tredje", + "fire" : "fjerde", + "fem" : "femte", + "seks" : "sjette", + "syv" : "syvende", + "otte" : "ottende", + "ni" : "niende", + "ti" : "tiende", + "elleve" : "ellevte", + "tolv" : "tolvte", + "tyve" : "tyvende" } + + + def merge(self, (ltext, lnum), (rtext, rnum)): + if lnum == 1 and rnum < 100: + return (rtext, rnum) + elif 100 > lnum > rnum : + return ("%s-%s"%(ltext, rtext), lnum + rnum) + elif lnum >= 100 > rnum: + return ("%s og %s"%(ltext, rtext), lnum + rnum) + elif rnum > lnum: + return ("%s %s"%(ltext, rtext), lnum * rnum) + return ("%s, %s"%(ltext, rtext), lnum + rnum) + + #TODO: skal returnere ordnum is 21ende, og ord is tyvende + def to_ordinal(self, value): + self.verify_ordinal(value) + outwords = self.to_cardinal(value).split(" ") + lastwords = outwords[-1].split("-") + lastword = lastwords[-1].lower() + try: + lastword = self.ords[lastword] + except KeyError: + if lastword[-2:] == "ti": + lastword = lastword + "ende" + else: + lastword += "de" + lastwords[-1] = self.title(lastword) + outwords[-1] = "".join(lastwords) + return " ".join(outwords) + + #TODO:Skal returnere "en" istedet for "et" i 21. + def to_splitnum(self, val, hightxt="", lowtxt="", jointxt="", + divisor=100, longval=True, cents = True): + out = [] + try: + high, low = val + except TypeError: + high, low = divmod(val, divisor) + if high: + hightxt = self.title(self.inflect(high, hightxt)) + out.append(self.to_cardinal(high)) + if low: + if longval: + if hightxt: + out.append(hightxt) + if jointxt: + out.append(self.title(jointxt)) + elif hightxt: + out.append(hightxt) + if low: + if cents: + out.append(self.to_cardinal(low)) + else: + out.append("%02d" % low) + if lowtxt and longval: + out.append(self.title(self.inflect(low, lowtxt))) + return " ".join(out) + + #TODO: 21 skal returnere enogtyve i card. + def to_cardinal(self, value): + try: + assert long(value) == value + except (ValueError, TypeError, AssertionError): + return self.to_cardinal_float(value) + + self.verify_num(value) + + out = "" + if value < 0: + value = abs(value) + out = self.negword + + if value >= self.MAXVAL: + raise OverflowError(self.errmsg_toobig % (value, self.MAXVAL)) + + + val = self.splitnum(value) + words, num = self.clean(val) + return self.title(out + words) + + + def to_ordinal_num(self, value): + self.verify_ordinal(value) + return "%s%s"%(value, self.to_ordinal(value)[-2:]) + + + def to_year(self, val, longval=True): + if not (val//100)%10: + return self.to_cardinal(val) + return self.to_splitnum(val, hightxt="hundrede", jointxt="og", + longval=longval) + + def to_currency(self, val, longval=True): + return self.to_splitnum(val, hightxt="krone/r", lowtxt="\xf8re/r", + jointxt="og", longval=longval, cents = True) + + +n2w = Num2Word_DK() +to_card = n2w.to_cardinal +to_ord = n2w.to_ordinal +to_ordnum = n2w.to_ordinal_num +to_year = n2w.to_year + +def main(): + n2w.test(21) + # for val in [ 1, 11, 12, 21, 31, 33, 71, 80, 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, + # -21212121211221211111, -2.121212, -1.0000100]: + # 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) + + +if __name__ == "__main__": + main() From cce1455846ccb23e666e39d64d51a9292443b1d7 Mon Sep 17 00:00:00 2001 From: mdc <201205975@post.au.dk> Date: Mon, 21 Mar 2016 11:09:30 +0100 Subject: [PATCH 14/38] Added the German algorithms, and begun adjusting them. --- num2words/lang_DK.py | 143 +++++++++++++++---------------------------- 1 file changed, 49 insertions(+), 94 deletions(-) diff --git a/num2words/lang_DK.py b/num2words/lang_DK.py index 7d30732..551611d 100644 --- a/num2words/lang_DK.py +++ b/num2words/lang_DK.py @@ -21,8 +21,8 @@ class Num2Word_DK(lang_EU.Num2Word_EU): def set_high_numwords(self, high): max = 3 + 6*len(high) for word, n in zip(high, range(max, 3, -6)): - self.cards[10**n] = word + "illard" - self.cards[10**(n-3)] = word + "illion" + self.cards[10**n] = word + "illarder" + self.cards[10**(n-3)] = word + "illioner" def setup(self): self.negword = "minus " @@ -53,101 +53,57 @@ class Num2Word_DK(lang_EU.Num2Word_EU): "tolv" : "tolvte", "tyve" : "tyvende" } + def merge(self, curr, next): + ctext, cnum, ntext, nnum = curr + next + + if cnum == 1: + if nnum < 10**6 or self.ordflag: + return next + ctext = "en" + + if nnum > cnum: + if nnum >= 10**6: + ctext += " " + val = cnum * nnum + else: + if nnum < 10 < cnum < 100: + if nnum == 1: + ntext = "en" + ntext, ctext = ctext, ntext + "og" + elif cnum >= 10**6: + ctext += " " + val = cnum + nnum + + word = ctext + ntext + return (word, val) - def merge(self, (ltext, lnum), (rtext, rnum)): - if lnum == 1 and rnum < 100: - return (rtext, rnum) - elif 100 > lnum > rnum : - return ("%s-%s"%(ltext, rtext), lnum + rnum) - elif lnum >= 100 > rnum: - return ("%s og %s"%(ltext, rtext), lnum + rnum) - elif rnum > lnum: - return ("%s %s"%(ltext, rtext), lnum * rnum) - return ("%s, %s"%(ltext, rtext), lnum + rnum) - #TODO: skal returnere ordnum is 21ende, og ord is tyvende def to_ordinal(self, value): self.verify_ordinal(value) - outwords = self.to_cardinal(value).split(" ") - lastwords = outwords[-1].split("-") - lastword = lastwords[-1].lower() - try: - lastword = self.ords[lastword] - except KeyError: - if lastword[-2:] == "ti": - lastword = lastword + "ende" - else: - lastword += "de" - lastwords[-1] = self.title(lastword) - outwords[-1] = "".join(lastwords) - return " ".join(outwords) - - #TODO:Skal returnere "en" istedet for "et" i 21. - def to_splitnum(self, val, hightxt="", lowtxt="", jointxt="", - divisor=100, longval=True, cents = True): - out = [] - try: - high, low = val - except TypeError: - high, low = divmod(val, divisor) - if high: - hightxt = self.title(self.inflect(high, hightxt)) - out.append(self.to_cardinal(high)) - if low: - if longval: - if hightxt: - out.append(hightxt) - if jointxt: - out.append(self.title(jointxt)) - elif hightxt: - out.append(hightxt) - if low: - if cents: - out.append(self.to_cardinal(low)) - else: - out.append("%02d" % low) - if lowtxt and longval: - out.append(self.title(self.inflect(low, lowtxt))) - return " ".join(out) - - #TODO: 21 skal returnere enogtyve i card. - def to_cardinal(self, value): - try: - assert long(value) == value - except (ValueError, TypeError, AssertionError): - return self.to_cardinal_float(value) - - self.verify_num(value) - - out = "" - if value < 0: - value = abs(value) - out = self.negword - - if value >= self.MAXVAL: - raise OverflowError(self.errmsg_toobig % (value, self.MAXVAL)) - - - val = self.splitnum(value) - words, num = self.clean(val) - return self.title(out + words) + 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 "%s%s"%(value, self.to_ordinal(value)[-2:]) + return str(value) + "te" + def to_currency(self, val, longval=True): + return self.to_splitnum(val, hightxt="kr", lowtxt="\xf8re", + jointxt="og",longval=longval) + def to_year(self, val, longval=True): if not (val//100)%10: return self.to_cardinal(val) - return self.to_splitnum(val, hightxt="hundrede", jointxt="og", - longval=longval) - - def to_currency(self, val, longval=True): - return self.to_splitnum(val, hightxt="krone/r", lowtxt="\xf8re/r", - jointxt="og", longval=longval, cents = True) - + return self.to_splitnum(val, hightxt="hundrede og", longval=longval) n2w = Num2Word_DK() to_card = n2w.to_cardinal @@ -156,16 +112,15 @@ to_ordnum = n2w.to_ordinal_num to_year = n2w.to_year def main(): - n2w.test(21) - # for val in [ 1, 11, 12, 21, 31, 33, 71, 80, 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, - # -21212121211221211111, -2.121212, -1.0000100]: - # 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) + for val in [ 1, 11, 12, 21, 31, 33, 71, 80, 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, + -21212121211221211111, -2.121212, -1.0000100]: + 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) if __name__ == "__main__": From 8fff34e3187460fecab4023c57521ffbae682bac Mon Sep 17 00:00:00 2001 From: mdc <201205975@post.au.dk> Date: Mon, 21 Mar 2016 14:16:06 +0100 Subject: [PATCH 15/38] Added og to hundreds, and a lot of other adjustments in the merge algorithm --- num2words/lang_DK.py | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/num2words/lang_DK.py b/num2words/lang_DK.py index 551611d..0534e93 100644 --- a/num2words/lang_DK.py +++ b/num2words/lang_DK.py @@ -31,12 +31,12 @@ class Num2Word_DK(lang_EU.Num2Word_EU): self.exclude_title = ["og", "komma", "minus"] self.mid_numwords = [(1000, "tusind"), (100, "hundrede"), - (90, "nitten"), (80, "firs"), (70, "halvfjerds"), + (90, "halvfems"), (80, "firs"), (70, "halvfjerds"), (60, "treds"), (50, "halvtreds"), (40, "fyrre"), (30, "tredive")] self.low_numwords = ["tyve", "nitten", "atten", "sytten", "seksten", "femten", "fjorten", "tretten", - "tolv", "elleve", "ti", "ni", "\xe5tte", + "tolv", "elleve", "ti", "ni", "otte", "syv", "seks", "fem", "fire", "tre", "to", "et", "nul"] self.ords = { "en" : "f\xf8rste", @@ -55,17 +55,22 @@ class Num2Word_DK(lang_EU.Num2Word_EU): def merge(self, curr, next): ctext, cnum, ntext, nnum = curr + next + if next[1] == 100 or next[1] == 1000: + lst = list(next) + lst[0] = 'et' + lst[0] + next = tuple(lst) if cnum == 1: if nnum < 10**6 or self.ordflag: return next ctext = "en" - if nnum > cnum: if nnum >= 10**6: ctext += " " val = cnum * nnum else: + if cnum >= 100 and cnum < 10000: + ctext += "og" if nnum < 10 < cnum < 100: if nnum == 1: ntext = "en" @@ -73,7 +78,6 @@ class Num2Word_DK(lang_EU.Num2Word_EU): elif cnum >= 10**6: ctext += " " val = cnum + nnum - word = ctext + ntext return (word, val) @@ -87,13 +91,11 @@ class Num2Word_DK(lang_EU.Num2Word_EU): if outword.endswith(key): outword = outword[:len(outword) - len(key)] + self.ords[key] break - return outword + "te" + return outword + "ende" - - # Is this correct?? def to_ordinal_num(self, value): self.verify_ordinal(value) - return str(value) + "te" + return str(value) + "ende" def to_currency(self, val, longval=True): @@ -103,7 +105,7 @@ class Num2Word_DK(lang_EU.Num2Word_EU): def to_year(self, val, longval=True): if not (val//100)%10: return self.to_cardinal(val) - return self.to_splitnum(val, hightxt="hundrede og", longval=longval) + return self.to_splitnum(val, hightxt="hundrede", longval=longval) n2w = Num2Word_DK() to_card = n2w.to_cardinal @@ -112,6 +114,7 @@ to_ordnum = n2w.to_ordinal_num to_year = n2w.to_year def main(): + #print to_card(308) for val in [ 1, 11, 12, 21, 31, 33, 71, 80, 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, From b1bbe109fc5cfac5c6c0649a6153433292bb3e6c Mon Sep 17 00:00:00 2001 From: mdc <201205975@post.au.dk> Date: Mon, 21 Mar 2016 16:11:54 +0100 Subject: [PATCH 16/38] Corrected for special cases of currency and year --- num2words/lang_DK.py | 40 ++++++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 8 deletions(-) diff --git a/num2words/lang_DK.py b/num2words/lang_DK.py index 0534e93..bc88c06 100644 --- a/num2words/lang_DK.py +++ b/num2words/lang_DK.py @@ -39,7 +39,8 @@ class Num2Word_DK(lang_EU.Num2Word_EU): "tolv", "elleve", "ti", "ni", "otte", "syv", "seks", "fem", "fire", "tre", "to", "et", "nul"] - self.ords = { "en" : "f\xf8rste", + self.ords = { "nul" : "nul", + "et" : "f\xf8rste", "to" : "anden", "tre" : "tredje", "fire" : "fjerde", @@ -51,7 +52,14 @@ class Num2Word_DK(lang_EU.Num2Word_EU): "ti" : "tiende", "elleve" : "ellevte", "tolv" : "tolvte", - "tyve" : "tyvende" } + "tretten" : "trett", + "fjorten" : "fjort", + "femten" : "femt", + "seksten" : "sekst", + "sytten" : "sytt", + "atten" : "att", + "nitten" : "nitt", + "tyve" : "tyv"} def merge(self, curr, next): ctext, cnum, ntext, nnum = curr + next @@ -69,8 +77,10 @@ class Num2Word_DK(lang_EU.Num2Word_EU): ctext += " " val = cnum * nnum else: - if cnum >= 100 and cnum < 10000: - ctext += "og" + if cnum >= 100 and cnum < 1000: + ctext += " og " + elif cnum >= 1000 and cnum <= 100000: + ctext += "e og " if nnum < 10 < cnum < 100: if nnum == 1: ntext = "en" @@ -91,18 +101,33 @@ class Num2Word_DK(lang_EU.Num2Word_EU): if outword.endswith(key): outword = outword[:len(outword) - len(key)] + self.ords[key] break - return outword + "ende" + if value %100 >= 30 and value %100 <= 39 or value %100 == 0: + outword += "te" + elif value % 100 > 12 or value %100 == 0: + outword += "ende" + return outword def to_ordinal_num(self, value): self.verify_ordinal(value) + vaerdte = (0,1,5,6,11,12) + if value %100 >= 30 and value %100 <= 39 or value % 100 in vaerdte: + return str(value) + "te" + elif value % 100 == 2: + return str(value) + "en" return str(value) + "ende" def to_currency(self, val, longval=True): + if val//100 == 1 or val == 1: + ret = self.to_splitnum(val, hightxt="kr", lowtxt="\xf8re", + jointxt="og",longval=longval) + return "en " + ret[3:] return self.to_splitnum(val, hightxt="kr", lowtxt="\xf8re", jointxt="og",longval=longval) def to_year(self, val, longval=True): + if val == 1: + return 'en' if not (val//100)%10: return self.to_cardinal(val) return self.to_splitnum(val, hightxt="hundrede", longval=longval) @@ -114,17 +139,16 @@ to_ordnum = n2w.to_ordinal_num to_year = n2w.to_year def main(): - #print to_card(308) for val in [ 1, 11, 12, 21, 31, 33, 71, 80, 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, -21212121211221211111, -2.121212, -1.0000100]: n2w.test(val) n2w.test(1325325436067876801768700107601001012212132143210473207540327057320957032975032975093275093275093270957329057320975093272950730) - for val in [1,120,1000,1120,1800, 1976,2000,2010,2099,2171]: + 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) - + n2w.test(65132) if __name__ == "__main__": main() From b9b6a8de428a7252a978fb3fd6735a4ec6312d02 Mon Sep 17 00:00:00 2001 From: alexpantyukhin Date: Tue, 15 Dec 2015 04:24:57 +0000 Subject: [PATCH 17/38] add ru lang into supported languages --- README.rst | 1 + num2words/__init__.py | 2 + num2words/lang_RU.py | 309 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 312 insertions(+) create mode 100644 num2words/lang_RU.py diff --git a/README.rst b/README.rst index 96d41c3..9902146 100644 --- a/README.rst +++ b/README.rst @@ -50,6 +50,7 @@ Besides the numerical argument, there's two optional arguments. * ``en_GB`` (British English) * ``en_IN`` (Indian English) * ``pl`` (Polish) +* ``ru`` (Russian) 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 220f0c3..24830b7 100644 --- a/num2words/__init__.py +++ b/num2words/__init__.py @@ -26,6 +26,7 @@ from . import lang_ES from . import lang_LT from . import lang_LV from . import lang_PL +from . import lang_RU CONVERTER_CLASSES = { 'en': lang_EN.Num2Word_EN(), @@ -38,6 +39,7 @@ CONVERTER_CLASSES = { 'lt': lang_LT.Num2Word_LT(), 'lv': lang_LV.Num2Word_LV(), 'pl': lang_PL.Num2Word_PL(), + 'ru': lang_RU.Num2Word_RU(), } def num2words(number, ordinal=False, lang='en'): diff --git a/num2words/lang_RU.py b/num2words/lang_RU.py new file mode 100644 index 0000000..328b336 --- /dev/null +++ b/num2words/lang_RU.py @@ -0,0 +1,309 @@ +# -*- 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 +u""" +>>> from textwrap import fill + +>>> ' '.join([str(i) for i in splitby3('1')]) +u'1' +>>> ' '.join([str(i) for i in splitby3('1123')]) +u'1 123' +>>> ' '.join([str(i) for i in splitby3('1234567890')]) +u'1 234 567 890' + +>>> print(' '.join([n2w(i) for i in range(10)])) +ноль один два три четыре пять шесть семь восемь девять + +>>> print(fill(' '.join([n2w(i+10) for i in range(10)]))) +десять одиннадцать двенадцать тринадцать четырнадцать пятнадцать +шестнадцать семнадцать восемнадцать девятнадцать + +>>> print(fill(' '.join([n2w(i*10) for i in range(10)]))) +ноль десять двадцать тридцать сорок пятьдесят шестьдесят семьдесят +восемьдесят девяносто + +>>> print(n2w(100)) +сто +>>> print(n2w(101)) +сто один +>>> print(n2w(110)) +сто десять +>>> print(n2w(115)) +сто пятнадцать +>>> print(n2w(123)) +сто двадцать три +>>> print(n2w(1000)) +тысяча +>>> print(n2w(1001)) +тысяча один +>>> print(n2w(2012)) +две тысячи двенадцать + +>>> print(n2w(12519.85)) +двенадцать тысяч пятьсот девятнадцать запятая восемьдесят пять + +>>> print(fill(n2w(1234567890))) +миллиард двести тридцать четыре миллиона пятьсот шестьдесят семь тысяч +восемьсот девяносто + +>>> print(fill(n2w(215461407892039002157189883901676))) +двести пятнадцать нониллионов четыреста шестьдесят один октиллион +четыреста семь септиллионов восемьсот девяносто два секстиллиона +тридцать девять квинтиллионов два квадриллиона сто пятьдесят семь +триллионов сто восемьдесят девять миллиардов восемьсот восемьдесят три +миллиона девятьсот одна тысяча шестьсот семьдесят шесть + +>>> print(fill(n2w(719094234693663034822824384220291))) +семьсот девятнадцать нониллионов девяносто четыре октиллиона двести +тридцать четыре септиллиона шестьсот девяносто три секстиллиона +шестьсот шестьдесят три квинтиллиона тридцать четыре квадриллиона +восемьсот двадцать два триллиона восемьсот двадцать четыре миллиарда +триста восемьдесят четыре миллиона двести двадцать тысяч двести +девяносто один + +>>> print(to_currency(1.0, 'EUR')) +один евро, ноль центов + +>>> print(to_currency(1.0, 'RUB')) +один рубль, ноль копеек + +>>> print(to_currency(1234.56, 'EUR')) +тысяча двести тридцать четыре евро, пятьдесят шесть центов + +>>> print(to_currency(1234.56, 'RUB')) +тысяча двести тридцать четыре рубля, пятьдесят шесть копеек + +>>> print(to_currency(10111, 'EUR', seperator=u' и')) +сто один евро и одиннадцать центов + +>>> print(to_currency(10121, 'RUB', seperator=u' и')) +сто один рубль и двадцать одна копейка + +>>> print(to_currency(10122, 'RUB', seperator=u' и')) +сто один рубль и двадцать две копейки + +>>> print(to_currency(10121, 'EUR', seperator=u' и')) +сто один евро и двадцать один цент + +>>> print(to_currency(-1251985, cents = False)) +минус двенадцать тысяч пятьсот девятнадцать евро, 85 центов +""" +from __future__ import unicode_literals + +ZERO = (u'ноль',) + +ONES_FEMININE = { + 1: (u'одна',), + 2: (u'две',), + 3: (u'три',), + 4: (u'четыре',), + 5: (u'пять',), + 6: (u'шесть',), + 7: (u'семь',), + 8: (u'восемь',), + 9: (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'девяносто',), +} + +HUNDREDS = { + 1: (u'сто',), + 2: (u'двести',), + 3: (u'триста',), + 4: (u'четыреста',), + 5: (u'пятьсот',), + 6: (u'шестьсот',), + 7: (u'семьсот',), + 8: (u'восемьсот',), + 9: (u'девятьсот',), +} + +THOUSANDS = { + 1: (u'тысяча', u'тысячи', u'тысяч'), # 10^3 + 2: (u'миллион', u'миллиона', u'миллионов'), # 10^6 + 3: (u'миллиард', u'миллиарда', u'миллиардов'), # 10^9 + 4: (u'триллион', u'триллиона', u'триллионов'), # 10^12 + 5: (u'квадриллион', u'квадриллиона', u'квадриллионов'), # 10^15 + 6: (u'квинтиллион', u'квинтиллиона', u'квинтиллионов'), # 10^18 + 7: (u'секстиллион', u'секстиллиона', u'секстиллионов'), # 10^21 + 8: (u'септиллион', u'септиллиона', u'септиллионов'), # 10^24 + 9: (u'октиллион', u'октиллиона', u'октиллионов'), #10^27 + 10: (u'нониллион', u'нониллиона', u'нониллионов'), # 10^30 +} + +CURRENCIES = { + 'RUB': ( + (u'рубль', u'рубля', u'рублей'), (u'копейка', u'копейки', u'копеек') + ), + 'EUR': ( + (u'евро', u'евро', u'евро'), (u'цент', u'цента', 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): + if (n % 100 < 10 or n % 100 > 20): + if n % 10 == 1: + form = 0 + elif (n % 10 > 1 and n % 10 < 5): + form = 1 + else: + form = 2 + else: + form = 2 + return forms[form] + + +def int2word(n, feminine=False): + 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) + + if n3 > 0: + words.append(HUNDREDS[n3][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): + ones = ONES_FEMININE if i == 1 or feminine and i == 0 else ONES + words.append(ones[n1][0]) + + if i > 0: + words.append(pluralize(x, THOUSANDS[i])) + + return ' '.join(words) + + +def n2w(n): + n = str(n).replace(',', '.') + if '.' in n: + left, right = n.split('.') + return u'%s запятая %s' % (int2word(int(left)), int2word(int(right))) + else: + return int2word(int(n)) + + +def to_currency(n, currency='EUR', cents=True, seperator=','): + if type(n) == int: + if n < 0: + minus = True + else: + minus = False + + n = abs(n) + left = n / 100 + right = n % 100 + else: + n = str(n).replace(',', '.') + if '.' in n: + left, right = n.split('.') + else: + left, right = n, 0 + left, right = int(left), int(right) + minus = False + cr1, cr2 = CURRENCIES[currency] + + if minus: + minus_str = "минус " + else: + minus_str = "" + + if cents: + cents_feminine = currency == 'RUB' + cents_str = int2word(right, cents_feminine) + else: + cents_str = "%02d" % right + + return u'%s%s %s%s %s %s' % ( + minus_str, + int2word(left), + pluralize(left, cr1), + seperator, + cents_str, + pluralize(right, cr2) + ) + + +class Num2Word_RU(object): + def to_cardinal(self, number): + return n2w(number) + + def to_ordinal(self, number): + raise NotImplementedError() + + +if __name__ == '__main__': + import doctest + doctest.testmod() From d3d0af304065f63b21db062ad2ac862ff30771be Mon Sep 17 00:00:00 2001 From: isnani Date: Fri, 18 Dec 2015 03:17:17 +0100 Subject: [PATCH 18/38] add indonesian ('id') --- num2words/__init__.py | 2 + num2words/base.py | 2 +- num2words/lang_ID.py | 194 ++++++++++++++++++++++++++++++++++++++++++ tests/test_id.py | 49 +++++++++++ 4 files changed, 246 insertions(+), 1 deletion(-) create mode 100644 num2words/lang_ID.py create mode 100644 tests/test_id.py diff --git a/num2words/__init__.py b/num2words/__init__.py index 24830b7..d0c8eb1 100644 --- a/num2words/__init__.py +++ b/num2words/__init__.py @@ -27,6 +27,7 @@ from . import lang_LT from . import lang_LV from . import lang_PL from . import lang_RU +from . import lang_ID CONVERTER_CLASSES = { 'en': lang_EN.Num2Word_EN(), @@ -36,6 +37,7 @@ CONVERTER_CLASSES = { 'fr_CH': lang_FR_CH.Num2Word_FR_CH(), 'de': lang_DE.Num2Word_DE(), 'es': lang_ES.Num2Word_ES(), + 'id': lang_ID.Num2Word_ID(), 'lt': lang_LT.Num2Word_LT(), 'lv': lang_LV.Num2Word_LV(), 'pl': lang_PL.Num2Word_PL(), diff --git a/num2words/base.py b/num2words/base.py index b2b7cab..bc0c5b9 100644 --- a/num2words/base.py +++ b/num2words/base.py @@ -182,7 +182,7 @@ class Num2Word_Base(object): pass - def to_ordinal(value): + def to_ordinal(self, value): return self.to_cardinal(value) diff --git a/num2words/lang_ID.py b/num2words/lang_ID.py new file mode 100644 index 0000000..ee07065 --- /dev/null +++ b/num2words/lang_ID.py @@ -0,0 +1,194 @@ +# 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 + +class Num2Word_ID(): + + BASE = {0: [], + 1: ["satu"], + 2: ["dua"], + 3: ["tiga"], + 4: ["empat"], + 5: ["lima"], + 6: ["enam"], + 7: ["tujuh"], + 8: ["delapan"], + 9: ["sembilan"]} + + TENS_TO = {3: "ribu", + 6: "juta", + 9: "miliar", + 12: "triliun", + 15: "kuadriliun", + 18: "kuantiliun", + 21: "sekstiliun", + 24: "septiliun", + 27: "oktiliun", + 30: "noniliun", + 33: "desiliun"} + + errmsg_floatord = "Cannot treat float number as ordinal" + errmsg_negord = "Cannot treat negative number as ordinal" + errmsg_toobig = "Too large" + max_num = 10**36 + + def split_by_koma(self, number): + return str(number).split('.') + + def split_by_3(self, number): + """ + starting here, it groups the number by three from the tail + '1234567' -> (('1',),('234',),('567',)) + :param number:str + :rtype:tuple + """ + blocks = () + length = len(number) + + if length < 3: + blocks += ((number,),) + else: + len_of_first_block = length % 3 + + if len_of_first_block > 0: + first_block = number[0:len_of_first_block], + blocks += first_block, + + for i in range(len_of_first_block, length, 3): + next_block = (number[i:i+3],), + blocks += next_block + + return blocks + + def spell(self, blocks): + """ + it adds the list of spelling to the blocks + (('1',),('034',)) -> (('1',['satu']),('234',['tiga', 'puluh', 'empat'])) + :param blocks: tuple + :rtype: tuple + """ + word_blocks = () + first_block = blocks[0] + if len(first_block[0]) == 1: + if first_block[0] == '0': + spelling = ['nol'] + else: + spelling = self.BASE[int(first_block[0])] + elif len(first_block[0]) == 2: + spelling = self.puluh(first_block[0]) + else: + spelling = self.ratus(first_block[0][0]) + self.puluh(first_block[0][1:3]) + + word_blocks += (first_block[0], spelling), + + for block in blocks[1:]: + spelling = self.ratus(block[0][0]) + self.puluh(block[0][1:3]) + block += spelling, + word_blocks += block, + + return word_blocks + + def ratus(self, number): + # it is used to spell + if number == '1': + return ['seratus'] + elif number == '0': + return [] + else: + return self.BASE[int(number)]+['ratus'] + + def puluh(self, number): + # it is used to spell + if number[0] == '1': + if number[1]== '0': + return ['sepuluh'] + elif number[1] == '1': + return ['sebelas'] + else: + return self.BASE[int(number[1])]+['belas'] + elif number[0] == '0': + return self.BASE[int(number[1])] + else: + return self.BASE[int(number[0])]+['puluh']+ self.BASE[int(number[1])] + + def spell_float(self, float_part): + # spell the float number + word_list = [] + for n in float_part: + if n == '0': + word_list += ['nol'] + continue + word_list += self.BASE[int(n)] + return ' '.join(['','koma']+word_list) + + def join(self, word_blocks, float_part): + """ + join the words by first join lists in the tuple + :param word_blocks: tuple + :rtype: str + """ + word_list = [] + length = len(word_blocks)-1 + first_block = word_blocks[0], + start = 0 + + if length == 1 and first_block[0][0] == '1': + word_list += ['seribu'] + start = 1 + + for i in range(start, length+1, 1): + word_list += word_blocks[i][1] + if not word_blocks[i][1]: + continue + if i == length: + break + word_list += [self.TENS_TO[(length-i)*3]] + + return ' '.join(word_list)+float_part + + def to_cardinal(self, number): + if number >= self.max_num: + raise OverflowError(self.errmsg_toobig % (number, self.maxnum)) + minus = '' + if number < 0: + minus = 'min ' + float_word = '' + n = self.split_by_koma(abs(number)) + if len(n)==2: + float_word = self.spell_float(n[1]) + return minus + self.join(self.spell(self.split_by_3(n[0])), float_word) + + def to_ordinal(self, number): + self.verify_ordinal(number) + out_word = self.to_cardinal(number) + if out_word == "satu": + return "pertama" + return "ke" + out_word + + def to_ordinal_num(self, number): + self.verify_ordinal(number) + return "ke-" + str(number) + + def to_currency(self, value): + return self.to_cardinal(value)+" rupiah" + + def to_year(self, value): + return self.to_cardinal(value) + + def verify_ordinal(self, value): + if not value == long(value): + raise TypeError, self.errmsg_floatord %(value) + if not abs(value) == value: + raise TypeError, self.errmsg_negord %(value) diff --git a/tests/test_id.py b/tests/test_id.py new file mode 100644 index 0000000..a1716a0 --- /dev/null +++ b/tests/test_id.py @@ -0,0 +1,49 @@ +# 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 Num2WordsIDTest(TestCase): + def test_cardinal_for_natural_number(self): + self.assertEqual(num2words(10, lang='id'), "sepuluh") + self.assertEqual(num2words(11, lang='id'), "sebelas") + self.assertEqual(num2words(108, lang='id'), "seratus delapan") + self.assertEqual(num2words(1075, lang='id'), "seribu tujuh puluh lima") + self.assertEqual(num2words(1087231, lang='id'), "satu juta delapan puluh tujuh ribu dua ratus tiga puluh satu") + self.assertEqual(num2words(1000000408, lang='id'), "satu miliar empat ratus delapan") + + def test_cardinal_for_decimal_number(self): + self.assertEqual(num2words(12.234, lang='id'), "dua belas koma dua tiga empat") + self.assertEqual(num2words(9.076, lang='id'), "sembilan koma nol tujuh enam") + + def test_cardinal_for_negative_number(self): + self.assertEqual(num2words(-923, lang='id'), "min sembilan ratus dua puluh tiga") + self.assertEqual(num2words(-0.234, lang='id'), "min nol koma dua tiga empat") + + def test_ordinal_for_natural_number(self): + self.assertEqual(num2words(1, ordinal=True, lang='id'), "pertama") + self.assertEqual(num2words(10, ordinal=True, lang='id'), "kesepuluh") + + #def test_ordinal_numeric_for_natural_number(self): + # self.assertEqual(num2words(1, ordinal=True, lang='id'), "ke-1") + # self.assertEqual(num2words(10, ordinal=True, lang='id'), "ke-10") + + def test_ordinal_for_negative_number(self): + self.assertRaises(TypeError, num2words, -12, ordinal=True, lang='id') + + def test_ordinal_for_floating_number(self): + self.assertRaises(TypeError, num2words, 3.243, ordinal=True, lang='id') From 266fb9d2dfa2ea3e94c86c4cf7dd3f88d7bb9fcb Mon Sep 17 00:00:00 2001 From: Ernesto Rodriguez Ortiz Date: Thu, 31 Mar 2016 14:15:25 -0400 Subject: [PATCH 19/38] Fix regression added in the PR Iss24. --- num2words/base.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/num2words/base.py b/num2words/base.py index bc0c5b9..6e910e7 100644 --- a/num2words/base.py +++ b/num2words/base.py @@ -116,6 +116,7 @@ class Num2Word_Base(object): pre = int(value) post = str(abs(value - pre) * 10**self.precision) + post = '0' * (self.precision - len(post.split('.')[0])) + post out = [self.to_cardinal(pre)] if self.precision: @@ -123,7 +124,7 @@ class Num2Word_Base(object): for i in range(self.precision): curr = int(post[i]) - out.append(str(self.to_cardinal(curr))) + out.append(unicode(self.to_cardinal(curr))) return " ".join(out) From 3cafa38ddb9d7472117923025e7f2f5f1f533702 Mon Sep 17 00:00:00 2001 From: AntonKorobkov Date: Thu, 4 Aug 2016 18:47:13 +0300 Subject: [PATCH 20/38] 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 21/38] 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 22/38] 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 f98341731ed6804aa8a0e52e5363ba99846cab91 Mon Sep 17 00:00:00 2001 From: AntonKorobkov Date: Wed, 10 Aug 2016 18:06:46 +0300 Subject: [PATCH 23/38] added support for negative integers for Russian language --- num2words/lang_RU.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/num2words/lang_RU.py b/num2words/lang_RU.py index 328b336..922a852 100644 --- a/num2words/lang_RU.py +++ b/num2words/lang_RU.py @@ -218,6 +218,9 @@ def pluralize(n, forms): def int2word(n, feminine=False): + if n < 0: + return ' '.join([u'минус', int2word(abs(n))]) + if n == 0: return ZERO[0] From 273349a2347f487d5d8fb4de329ef101a2cf379c Mon Sep 17 00:00:00 2001 From: mr Date: Mon, 15 Aug 2016 13:09:18 +0200 Subject: [PATCH 24/38] 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 25/38] 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 26/38] 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 27/38] 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 28/38] 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 29/38] 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 30/38] 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 31/38] 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 32/38] 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 33/38] 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 34/38] 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 35/38] 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 36/38] 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 37/38] 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 38/38] 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).