From a6cae07703045c251fc8f608aef55ba706896499 Mon Sep 17 00:00:00 2001 From: gshekler Date: Sat, 3 Oct 2020 15:02:49 +0300 Subject: [PATCH 01/28] fix pluralize --- num2words/lang_HE.py | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/num2words/lang_HE.py b/num2words/lang_HE.py index 34b2ec8..2b96dc1 100644 --- a/num2words/lang_HE.py +++ b/num2words/lang_HE.py @@ -81,11 +81,7 @@ AND = u'ו' 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 - + form = 1 if n == 0 else 0 if n == 1 else 1 return forms[form] @@ -151,6 +147,9 @@ class Num2Word_HE(Num2Word_Base): def to_ordinal(self, number): raise NotImplementedError() + def pluralize(self, n, forms): + return pluralize(n, forms) + if __name__ == '__main__': yo = Num2Word_HE() From 92a0915508514983a8f1388d6acf062f593ac8e4 Mon Sep 17 00:00:00 2001 From: gshekler Date: Sat, 3 Oct 2020 15:03:43 +0300 Subject: [PATCH 02/28] implement currency for HE --- num2words/lang_HE.py | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/num2words/lang_HE.py b/num2words/lang_HE.py index 2b96dc1..f987192 100644 --- a/num2words/lang_HE.py +++ b/num2words/lang_HE.py @@ -136,11 +136,13 @@ def n2w(n): return int2word(int(n)) -def to_currency(n, currency='EUR', cents=True, separator=','): - raise NotImplementedError() - - class Num2Word_HE(Num2Word_Base): + CURRENCY_FORMS = { + 'NIS': (('שקל', 'שקלים'), ('אגורה', 'אגורות')), + 'EUR': (('אירו', 'אירו'), ('סנט', 'סנט')), + 'USD': (('דולר', 'דולרים'), ('סנט', 'סנט')), + } + def to_cardinal(self, number): return n2w(number) @@ -150,6 +152,14 @@ class Num2Word_HE(Num2Word_Base): def pluralize(self, n, forms): return pluralize(n, forms) + def to_currency(self, val, currency='NIS', cents=True, separator=' ו', + adjective=False): + result = super(Num2Word_HE, self).to_currency( + val, currency=currency, cents=cents, separator=separator, + adjective=adjective) + # In Hebrew the separator is along with the following word + return result.replace(" ו ", " ו") + if __name__ == '__main__': yo = Num2Word_HE() From 6ea1a3da713906117e0660bd5dd68a5cad392053 Mon Sep 17 00:00:00 2001 From: gshekler Date: Sat, 3 Oct 2020 15:03:57 +0300 Subject: [PATCH 03/28] add unit tests --- tests/test_he.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/tests/test_he.py b/tests/test_he.py index 206d7cc..5a17694 100644 --- a/tests/test_he.py +++ b/tests/test_he.py @@ -20,6 +20,7 @@ from __future__ import unicode_literals from unittest import TestCase from num2words import num2words +from num2words.lang_HE import Num2Word_HE class Num2WordsHETest(TestCase): @@ -71,3 +72,39 @@ class Num2WordsHETest(TestCase): self.assertEqual( num2words(6870, lang="he"), u'ששת אלפים שמונה מאות ושבעים' ) + + def test_pluralize(self): + n = Num2Word_HE() + cr1, cr2 = n.CURRENCY_FORMS['NIS'] + self.assertEqual(n.pluralize(1, cr1), 'שקל') + self.assertEqual(n.pluralize(2, cr1), 'שקלים') + self.assertEqual(n.pluralize(1, cr2), 'אגורה') + self.assertEqual(n.pluralize(2, cr2), 'אגורות') + + cr1, cr2 = n.CURRENCY_FORMS['USD'] + self.assertEqual(n.pluralize(1, cr1), 'דולר') + self.assertEqual(n.pluralize(2, cr1), 'דולרים') + self.assertEqual(n.pluralize(1, cr2), 'סנט') + self.assertEqual(n.pluralize(2, cr2), 'סנט') + + def test_currency_(self): + n = Num2Word_HE() + self.assertEqual(n.to_currency(20.0 ,currency='NIS'), 'עשרים שקלים ואפס אגורות') + self.assertEqual(n.to_currency(100.0 ,currency='NIS'), 'מאה שקלים ואפס אגורות') + self.assertEqual(n.to_currency(100.50 ,currency='NIS'), 'מאה שקלים וחמישים אגורות') + + def test_to_cardinal(self): + n = Num2Word_HE() + self.assertEqual(n.to_cardinal(1500), u'אלף וחמש מאות') + + +class Num2WordsHETestNotImplementedMethofs(TestCase): + n = Num2Word_HE() + + def test_to_ordinal(self): + with self.assertRaises(NotImplementedError): + self.n.to_ordinal('1') + + def test_large_number(self): + with self.assertRaises(NotImplementedError): + num2words(2000000, lang="he") From c95fe6260b1a5fd3f40cb4ac5f10557315d1507f Mon Sep 17 00:00:00 2001 From: gshekler Date: Sat, 3 Oct 2020 15:14:21 +0300 Subject: [PATCH 04/28] typo --- tests/test_he.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_he.py b/tests/test_he.py index 5a17694..688bbf0 100644 --- a/tests/test_he.py +++ b/tests/test_he.py @@ -87,7 +87,7 @@ class Num2WordsHETest(TestCase): self.assertEqual(n.pluralize(1, cr2), 'סנט') self.assertEqual(n.pluralize(2, cr2), 'סנט') - def test_currency_(self): + def test_to_currency(self): n = Num2Word_HE() self.assertEqual(n.to_currency(20.0 ,currency='NIS'), 'עשרים שקלים ואפס אגורות') self.assertEqual(n.to_currency(100.0 ,currency='NIS'), 'מאה שקלים ואפס אגורות') From f65df3fc613b45a6987fa966fb99ffcc89efe58e Mon Sep 17 00:00:00 2001 From: hamidreza kalbasi Date: Wed, 16 Dec 2020 21:11:52 +0330 Subject: [PATCH 05/28] add basic farsi support --- README.rst | 1 + num2words/__init__.py | 3 +- num2words/lang_FA.py | 165 ++++++++++++++++++++++++++++++++++++++++++ tests/test_fa.py | 85 ++++++++++++++++++++++ 4 files changed, 253 insertions(+), 1 deletion(-) create mode 100644 num2words/lang_FA.py create mode 100644 tests/test_fa.py diff --git a/README.rst b/README.rst index db64202..c8524c5 100644 --- a/README.rst +++ b/README.rst @@ -86,6 +86,7 @@ Besides the numerical argument, there are two main optional arguments. * ``es_CO`` (Spanish - Colombia) * ``es_VE`` (Spanish - Venezuela) * ``eu`` (EURO) +* ``fa`` (Farsi) * ``fi`` (Finnish) * ``fr`` (French) * ``fr_CH`` (French - Switzerland) diff --git a/num2words/__init__.py b/num2words/__init__.py index 931d28a..0b85ec7 100644 --- a/num2words/__init__.py +++ b/num2words/__init__.py @@ -18,7 +18,7 @@ from __future__ import unicode_literals from . import (lang_AR, lang_CZ, lang_DE, lang_DK, lang_EN, lang_EN_IN, - lang_ES, lang_ES_CO, lang_ES_NI, lang_ES_VE, lang_FI, lang_FR, + lang_ES, lang_ES_CO, lang_ES_NI, lang_ES_VE, lang_FA, lang_FI, lang_FR, lang_FR_BE, lang_FR_CH, lang_FR_DZ, lang_HE, lang_HU, lang_ID, lang_IT, lang_JA, lang_KN, lang_KO, lang_KZ, lang_LT, lang_LV, lang_NL, lang_NO, lang_PL, lang_PT, lang_PT_BR, lang_RO, @@ -30,6 +30,7 @@ CONVERTER_CLASSES = { 'cz': lang_CZ.Num2Word_CZ(), 'en': lang_EN.Num2Word_EN(), 'en_IN': lang_EN_IN.Num2Word_EN_IN(), + 'fa': lang_FA.Num2Word_FA(), 'fr': lang_FR.Num2Word_FR(), 'fr_CH': lang_FR_CH.Num2Word_FR_CH(), 'fr_BE': lang_FR_BE.Num2Word_FR_BE(), diff --git a/num2words/lang_FA.py b/num2words/lang_FA.py new file mode 100644 index 0000000..9e232a7 --- /dev/null +++ b/num2words/lang_FA.py @@ -0,0 +1,165 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2003, Taro Ogawa. All Rights Reserved. +# Copyright (c) 2013, Savoir-faire Linux inc. All Rights Reserved. +# Copyright (c) 2018, Abdullah Alhazmy, Alhazmy13. All Rights Reserved. +# Copyright (c) 2020, Hamidreza Kalbasi. 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 re +from decimal import Decimal +from math import floor + +farsiOnes = [ + "", "یک", "دو", "سه", "چهار", "پنج", "شش", "هفت", "هشت", + "نه", + "ده", + "یازده", + "دوازده", + "سیزده", + "چهارده", + "پونزده", + "شونزده", + "هیفده", + "هیجده", + "نوزده", +] + +farsiTens = [ + "", + "ده", + "بیست", + "سی", + "چهل", + "پنجاه", + "شصت", + "هفتاد", + "هشتاد", + "نود", +] + +farsiHundreds = [ + "", + "صد", + "دویست", + "سیصد", + "چهارصد", + "پانصد", + "ششصد", + "هفتصد", + "هشتصد", + "نهصد", +] + +farsiBig = [ + '', + ' هزار', + ' میلیون', + " میلیارد", + ' تریلیون', + " تریلیارد", +] + +farsiFrac = ["", "دهم", "صدم"] +farsiFracBig = ["", "هزارم", "میلیونیم", "میلیاردیم"] + +farsiSeperator = ' و ' + +class Num2Word_FA(object): + errmsg_too_big = "Too large" + max_num = 10 ** 36 + + def __init__(self): + self.number = 0 + + def float2tuple(self, value): + pre = int(value) + + # Simple way of finding decimal places to update the precision + self.precision = abs(Decimal(str(value)).as_tuple().exponent) + + 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)) + + return pre, post, self.precision + + + def cardinal3(self, number): + if (number < 19): + return farsiOnes[number] + if (number < 100): + x, y = divmod(number, 10) + if y == 0: + return farsiTens[x] + return farsiTens[x] + farsiSeperator + farsiOnes[y] + x, y = divmod(number, 100) + if y == 0: + return farsiHundreds[x] + return farsiHundreds[x] + farsiSeperator + self.cardinal3(y) + + def cardinalPos(self, number): + x = number + res = '' + for b in farsiBig: + x, y = divmod(x, 1000) + if (y == 0): + continue + yx = self.cardinal3(y) + b + if (res == ''): + res = yx + else: + res = yx + farsiSeperator + return res + + def fractional(self, number, l): + if (number == 5): + return "نیم" + x = self.cardinalPos(number) + ld3, lm3 = divmod(l, 3) + ltext = (farsiFrac[lm3] + " " + farsiFracBig[ld3]).strip() + return x + " " + ltext + + def to_currency(self, value): + return self.to_cardinal(value) + " تومان" + + def to_ordinal(self, number): + r = self.to_cardinal(number) + if (r[-1] == 'ه' and r[-2] == 'س'): + return r[:-1] + 'وم' + return r + 'م' + + def to_year(self, value): + return self.to_cardinal(value) + + def to_ordinal_num(self, value): + return str(value)+"م" + + def to_cardinal(self, number): + if number < 0: + return "منفی " + self.to_cardinal(-number) + if (number == 0): + return "صفر" + x, y, l = self.float2tuple(number) + if y == 0: + return self.cardinalPos(x) + if x == 0: + return self.fractional(y, l) + return self.cardinalPos(x) + farsiSeperator + self.fractional(y, l) \ No newline at end of file diff --git a/tests/test_fa.py b/tests/test_fa.py new file mode 100644 index 0000000..dbe9574 --- /dev/null +++ b/tests/test_fa.py @@ -0,0 +1,85 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2003, Taro Ogawa. All Rights Reserved. +# Copyright (c) 2013, Savoir-faire Linux inc. All Rights Reserved. +# Copyright (c) 2020, Hamidreza Kalbasi. 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 Num2WordsFATest(TestCase): + def test_and_join_199(self): + self.assertEqual(num2words(199, lang='fa'), "صد و نود و نه") + + def test_ordinal(self): + self.assertEqual( + num2words(0, lang='fa', to='ordinal'), + 'صفرم' + ) + self.assertEqual( + num2words(1, lang='fa', to='ordinal'), + 'یکم' + ) + self.assertEqual( + num2words(13, lang='fa', to='ordinal'), + 'سیزدهم' + ) + self.assertEqual( + num2words(23, lang='fa', to='ordinal'), + 'بیست و سوم' + ) + self.assertEqual( + num2words(12, lang='fa', to='ordinal'), + 'دوازدهم' + ) + self.assertEqual( + num2words(113, lang='fa', to='ordinal'), + 'صد و سیزدهم' + ) + self.assertEqual( + num2words(103, lang='fa', to='ordinal'), + 'صد و سوم' + ) + + def test_cardinal(self): + self.assertEqual(num2words(130000, lang='fa'), "صد و سی هزار") + self.assertEqual(num2words(242, lang='fa'), "دویست و چهل و دو") + + + + def test_ordinal_num(self): + self.assertEqual(num2words(10, lang='fa', to='ordinal_num'), '10م') + self.assertEqual(num2words(21, lang='fa', to='ordinal_num'), '21م') + self.assertEqual(num2words(102, lang='fa', to='ordinal_num'), '102م') + self.assertEqual(num2words(73, lang='fa', to='ordinal_num'), '73م') + + def test_cardinal_for_float_number(self): + self.assertEqual(num2words(12.5, lang='fa'), "دوازده و نیم") + self.assertEqual(num2words(0.75, lang='fa'), "هفتاد و پنج صدم") + self.assertEqual(num2words(12.51, lang='fa'), "دوازده و پنجاه و یک صدم") + self.assertEqual(num2words(12.53, lang='fa'), "دوازده و پنجاه و سه صدم") + self.assertEqual(num2words(12.59, lang='fa'), "دوازده و پنجاه و نه صدم") + self.assertEqual(num2words(0.000001, lang='fa'), "یک میلیونیم") + + def test_overflow(self): + with self.assertRaises(OverflowError): + num2words("1000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000") From b191c1fe1eca031a1fa144b66ea859b9ca5793d2 Mon Sep 17 00:00:00 2001 From: hamidreza kalbasi Date: Sun, 24 Jan 2021 10:10:24 +0330 Subject: [PATCH 06/28] fix test coverage --- num2words/lang_FA.py | 4 +++- tests/test_fa.py | 18 +++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/num2words/lang_FA.py b/num2words/lang_FA.py index 9e232a7..ca7004c 100644 --- a/num2words/lang_FA.py +++ b/num2words/lang_FA.py @@ -123,10 +123,12 @@ class Num2Word_FA(object): if (y == 0): continue yx = self.cardinal3(y) + b + if b == ' هزار' and y == 1: + yx = 'هزار' if (res == ''): res = yx else: - res = yx + farsiSeperator + res = yx + farsiSeperator + res return res def fractional(self, number, l): diff --git a/tests/test_fa.py b/tests/test_fa.py index dbe9574..4082e7f 100644 --- a/tests/test_fa.py +++ b/tests/test_fa.py @@ -58,8 +58,24 @@ class Num2WordsFATest(TestCase): def test_cardinal(self): self.assertEqual(num2words(130000, lang='fa'), "صد و سی هزار") self.assertEqual(num2words(242, lang='fa'), "دویست و چهل و دو") + self.assertEqual(num2words(800, lang='fa'), "هشتصد") + self.assertEqual(num2words(-203, lang='fa'), "منفی دویست و سه") + self.assertEqual( + num2words(1234567890, lang='fa'), + "یک میلیارد و دویست و سی و چهار میلیون و پانصد و شصت و هفت هزار و هشتصد و نود" + ) + + def test_year(self): + self.assertEqual(num2words(1398, lang='fa', to='year'), "هزار و سیصد و نود و هشت") + self.assertEqual(num2words(1399, lang='fa', to='year'), "هزار و سیصد و نود و نه") + self.assertEqual(num2words(1400, lang='fa', to='year'), "هزار و چهارصد") - + def test_currency(self): + self.assertEqual(num2words(1000, lang='fa', to='currency'), 'هزار تومان') + self.assertEqual( + num2words(1500000, lang='fa', to='currency'), + 'یک میلیون و پانصد هزار تومان' + ) def test_ordinal_num(self): self.assertEqual(num2words(10, lang='fa', to='ordinal_num'), '10م') From ca2651ad0f71632a0681805f83fedb63cb35769e Mon Sep 17 00:00:00 2001 From: hamidreza kalbasi Date: Sun, 24 Jan 2021 10:30:06 +0330 Subject: [PATCH 07/28] remove unneccery if --- num2words/lang_FA.py | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/num2words/lang_FA.py b/num2words/lang_FA.py index ca7004c..9c5ff74 100644 --- a/num2words/lang_FA.py +++ b/num2words/lang_FA.py @@ -90,14 +90,7 @@ class Num2Word_FA(object): self.precision = abs(Decimal(str(value)).as_tuple().exponent) 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 = int(math.floor(post)) return pre, post, self.precision From a51b3e4d2be219818467cd2a06eb0d312d30da58 Mon Sep 17 00:00:00 2001 From: hamidreza kalbasi Date: Mon, 25 Jan 2021 00:14:05 +0330 Subject: [PATCH 08/28] try fix test failure --- num2words/lang_FA.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/num2words/lang_FA.py b/num2words/lang_FA.py index 9c5ff74..f49b5f3 100644 --- a/num2words/lang_FA.py +++ b/num2words/lang_FA.py @@ -90,8 +90,14 @@ class Num2Word_FA(object): self.precision = abs(Decimal(str(value)).as_tuple().exponent) post = abs(value - pre) * 10**self.precision - post = int(math.floor(post)) - + 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(floor(post)) return pre, post, self.precision From 5bb2be74a2d24c3adfc3cbba653b7a95ed6332ee Mon Sep 17 00:00:00 2001 From: hamidreza kalbasi Date: Mon, 25 Jan 2021 01:16:45 +0330 Subject: [PATCH 09/28] fix format problem --- num2words/__init__.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/num2words/__init__.py b/num2words/__init__.py index 0b85ec7..67b8631 100644 --- a/num2words/__init__.py +++ b/num2words/__init__.py @@ -18,12 +18,12 @@ from __future__ import unicode_literals from . import (lang_AR, lang_CZ, lang_DE, lang_DK, lang_EN, lang_EN_IN, - lang_ES, lang_ES_CO, lang_ES_NI, lang_ES_VE, lang_FA, lang_FI, lang_FR, - lang_FR_BE, lang_FR_CH, lang_FR_DZ, lang_HE, lang_HU, lang_ID, - lang_IT, lang_JA, lang_KN, lang_KO, lang_KZ, lang_LT, lang_LV, - lang_NL, lang_NO, lang_PL, lang_PT, lang_PT_BR, lang_RO, - lang_RU, lang_SL, lang_SR, lang_TE, lang_TH, lang_TR, lang_UK, - lang_VI) + lang_ES, lang_ES_CO, lang_ES_NI, lang_ES_VE, lang_FA, lang_FI, + lang_FR, lang_FR_BE, lang_FR_CH, lang_FR_DZ, lang_HE, lang_HU, + lang_ID, lang_IT, lang_JA, lang_KN, lang_KO, lang_KZ, lang_LT, + lang_LV, lang_NL, lang_NO, lang_PL, lang_PT, lang_PT_BR, + lang_RO, lang_RU, lang_SL, lang_SR, lang_TE, lang_TH, lang_TR, + lang_UK, lang_VI) CONVERTER_CLASSES = { 'ar': lang_AR.Num2Word_AR(), From 6cbd81cfbb291eccbed6bd79206caead8938ce06 Mon Sep 17 00:00:00 2001 From: hamidreza kalbasi Date: Mon, 25 Jan 2021 10:34:01 +0330 Subject: [PATCH 10/28] fix flake8 problems --- num2words/lang_FA.py | 17 ++++++++--------- tests/test_fa.py | 26 +++++++++++++++++--------- 2 files changed, 25 insertions(+), 18 deletions(-) diff --git a/num2words/lang_FA.py b/num2words/lang_FA.py index f49b5f3..96e9e52 100644 --- a/num2words/lang_FA.py +++ b/num2words/lang_FA.py @@ -17,7 +17,6 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, # MA 02110-1301 USA -import re from decimal import Decimal from math import floor @@ -76,6 +75,7 @@ farsiFracBig = ["", "هزارم", "میلیونیم", "میلیاردیم"] farsiSeperator = ' و ' + class Num2Word_FA(object): errmsg_too_big = "Too large" max_num = 10 ** 36 @@ -100,7 +100,6 @@ class Num2Word_FA(object): post = int(floor(post)) return pre, post, self.precision - def cardinal3(self, number): if (number < 19): return farsiOnes[number] @@ -108,12 +107,12 @@ class Num2Word_FA(object): x, y = divmod(number, 10) if y == 0: return farsiTens[x] - return farsiTens[x] + farsiSeperator + farsiOnes[y] + return farsiTens[x] + farsiSeperator + farsiOnes[y] x, y = divmod(number, 100) if y == 0: return farsiHundreds[x] return farsiHundreds[x] + farsiSeperator + self.cardinal3(y) - + def cardinalPos(self, number): x = number res = '' @@ -130,11 +129,11 @@ class Num2Word_FA(object): res = yx + farsiSeperator + res return res - def fractional(self, number, l): + def fractional(self, number, level): if (number == 5): return "نیم" x = self.cardinalPos(number) - ld3, lm3 = divmod(l, 3) + ld3, lm3 = divmod(level, 3) ltext = (farsiFrac[lm3] + " " + farsiFracBig[ld3]).strip() return x + " " + ltext @@ -158,9 +157,9 @@ class Num2Word_FA(object): return "منفی " + self.to_cardinal(-number) if (number == 0): return "صفر" - x, y, l = self.float2tuple(number) + x, y, level = self.float2tuple(number) if y == 0: return self.cardinalPos(x) if x == 0: - return self.fractional(y, l) - return self.cardinalPos(x) + farsiSeperator + self.fractional(y, l) \ No newline at end of file + return self.fractional(y, level) + return self.cardinalPos(x) + farsiSeperator + self.fractional(y, level) diff --git a/tests/test_fa.py b/tests/test_fa.py index 4082e7f..9a9c4ea 100644 --- a/tests/test_fa.py +++ b/tests/test_fa.py @@ -62,16 +62,21 @@ class Num2WordsFATest(TestCase): self.assertEqual(num2words(-203, lang='fa'), "منفی دویست و سه") self.assertEqual( num2words(1234567890, lang='fa'), - "یک میلیارد و دویست و سی و چهار میلیون و پانصد و شصت و هفت هزار و هشتصد و نود" + "یک میلیارد و دویست و سی و چهار میلیون و" + " پانصد و شصت و هفت هزار و هشتصد و نود" ) def test_year(self): - self.assertEqual(num2words(1398, lang='fa', to='year'), "هزار و سیصد و نود و هشت") - self.assertEqual(num2words(1399, lang='fa', to='year'), "هزار و سیصد و نود و نه") - self.assertEqual(num2words(1400, lang='fa', to='year'), "هزار و چهارصد") - + self.assertEqual(num2words(1398, lang='fa', to='year'), + "هزار و سیصد و نود و هشت") + self.assertEqual(num2words(1399, lang='fa', to='year'), + "هزار و سیصد و نود و نه") + self.assertEqual( + num2words(1400, lang='fa', to='year'), "هزار و چهارصد") + def test_currency(self): - self.assertEqual(num2words(1000, lang='fa', to='currency'), 'هزار تومان') + self.assertEqual( + num2words(1000, lang='fa', to='currency'), 'هزار تومان') self.assertEqual( num2words(1500000, lang='fa', to='currency'), 'یک میلیون و پانصد هزار تومان' @@ -86,9 +91,12 @@ class Num2WordsFATest(TestCase): def test_cardinal_for_float_number(self): self.assertEqual(num2words(12.5, lang='fa'), "دوازده و نیم") self.assertEqual(num2words(0.75, lang='fa'), "هفتاد و پنج صدم") - self.assertEqual(num2words(12.51, lang='fa'), "دوازده و پنجاه و یک صدم") - self.assertEqual(num2words(12.53, lang='fa'), "دوازده و پنجاه و سه صدم") - self.assertEqual(num2words(12.59, lang='fa'), "دوازده و پنجاه و نه صدم") + self.assertEqual(num2words(12.51, lang='fa'), + "دوازده و پنجاه و یک صدم") + self.assertEqual(num2words(12.53, lang='fa'), + "دوازده و پنجاه و سه صدم") + self.assertEqual(num2words(12.59, lang='fa'), + "دوازده و پنجاه و نه صدم") self.assertEqual(num2words(0.000001, lang='fa'), "یک میلیونیم") def test_overflow(self): From 5374a0ee44acb9395097164803437ff35d87c4ce Mon Sep 17 00:00:00 2001 From: Drew Echerd Date: Fri, 2 Jul 2021 17:00:13 +0500 Subject: [PATCH 11/28] Added Tajik language support --- README.rst | 1 + num2words/__init__.py | 5 +- num2words/lang_TG.py | 149 ++++++++++++++++++++++++++++++++++++++++++ tests/test_tg.py | 104 +++++++++++++++++++++++++++++ 4 files changed, 257 insertions(+), 2 deletions(-) create mode 100644 num2words/lang_TG.py create mode 100644 tests/test_tg.py diff --git a/README.rst b/README.rst index ab4cf65..e4c9fcf 100644 --- a/README.rst +++ b/README.rst @@ -111,6 +111,7 @@ Besides the numerical argument, there are two main optional arguments. * ``ro`` (Romanian) * ``ru`` (Russian) * ``te`` (Telugu) +* ``tg`` (Tajik) * ``tr`` (Turkish) * ``th`` (Thai) * ``vi`` (Vietnamese) diff --git a/num2words/__init__.py b/num2words/__init__.py index a8c4737..2ec96e6 100644 --- a/num2words/__init__.py +++ b/num2words/__init__.py @@ -22,8 +22,8 @@ from . import (lang_AR, lang_CZ, lang_DE, lang_DK, lang_EN, lang_EN_IN, lang_FR_BE, lang_FR_CH, lang_FR_DZ, lang_HE, lang_HU, lang_ID, lang_IT, lang_JA, lang_KN, lang_KO, lang_KZ, lang_LT, lang_LV, lang_NL, lang_NO, lang_PL, lang_PT, lang_PT_BR, lang_RO, - lang_RU, lang_SL, lang_SR, lang_SV, lang_TE, lang_TH, lang_TR, - lang_UK, lang_VI) + lang_RU, lang_SL, lang_SR, lang_SV, lang_TE, lang_TG, lang_TH, + lang_TR, lang_UK, lang_VI) CONVERTER_CLASSES = { 'ar': lang_AR.Num2Word_AR(), @@ -60,6 +60,7 @@ CONVERTER_CLASSES = { 'he': lang_HE.Num2Word_HE(), 'it': lang_IT.Num2Word_IT(), 'vi': lang_VI.Num2Word_VI(), + 'tg': lang_TG.Num2Word_TG(), 'th': lang_TH.Num2Word_TH(), 'tr': lang_TR.Num2Word_TR(), 'nl': lang_NL.Num2Word_NL(), diff --git a/num2words/lang_TG.py b/num2words/lang_TG.py new file mode 100644 index 0000000..cc0bf49 --- /dev/null +++ b/num2words/lang_TG.py @@ -0,0 +1,149 @@ +# -*- 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, print_function, unicode_literals + +from . import lang_EU + +GENERIC_DOLLARS = ("доллар", "доллар") +GENERIC_CENTS = ("сент", "сент") + + +class Num2Word_TG(lang_EU.Num2Word_EU): + CURRENCY_FORMS = { + # repalced by EUR + "EUR": (("евро", "евро"), GENERIC_CENTS), + # replaced by EUR + "USD": (GENERIC_DOLLARS, GENERIC_CENTS), + "RUB": (("рубл", "рубл"), ("копейк", "копейк")), + "TJS": (("сомонӣ", "сомонӣ"), ("дирам", "дирам")), + } + + GIGA_SUFFIX = "иллиард" + MEGA_SUFFIX = "иллион" + + def set_high_numwords(self, high): + cap = 3 * (len(high) + 1) + + for word, n in zip(high, range(cap, 5, -3)): + if n == 9: + self.cards[10 ** n] = word + self.GIGA_SUFFIX + + elif self.MEGA_SUFFIX: + self.cards[10 ** n] = word + self.MEGA_SUFFIX + + def setup(self): + super(Num2Word_TG, self).setup() + + lows = ["квинт", "квадр", "тр", "м", "м"] + self.high_numwords = self.gen_high_numwords([], [], lows) + self.negword = "минус " + self.pointword = "нуқта" + self.exclude_title = ["ва", "минус", "нуқта"] + + self.mid_numwords = [ + (1000, "ҳазор"), + (100, "сад"), + (90, "навад"), + (80, "ҳаштод"), + (70, "ҳафтод"), + (60, "шаст"), + (50, "панҷоҳ"), + (40, "чил"), + (30, "си"), + ] + self.low_numwords = [ + "бист", + "нуздаҳ", + "ҳаждаҳ", + "ҳабдаҳ", + "шонздаҳ", + "понздаҳ", + "чордаҳ", + "сенздаҳ", + "дувоздаҳ", + "ёздаҳ", + "даҳ", + "нӯҳ", + "ҳашт", + "ҳафт", + "шаш", + "панҷ", + "чор", + "се", + "ду", + "як", + "сифр", + ] + + def to_cardinal(self, value): + try: + assert int(value) == value + except (ValueError, TypeError, AssertionError): + return self.to_cardinal_float(value) + + out = "" + if value < 0: + value = abs(value) + out = self.negword + + if value >= self.MAXVAL: + raise OverflowError(self.errmsg_toobig % (value, self.MAXVAL)) + + if value == 100: + return self.title(out + "сад") + else: + val = self.splitnum(value) + words, num = self.clean(val) + return self.title(out + words) + + def merge(self, lpair, rpair): + ltext, lnum = lpair + rtext, rnum = rpair + if lnum == 1 and rnum < 100: + return (rtext, rnum) + elif 100 > lnum > rnum: + if ltext == "си": + return ("%sю %s" % (ltext, rtext), lnum + rnum) + elif ltext == "панҷоҳ": + return ("панҷову %s" % (rtext), lnum + rnum) + else: + return ("%sу %s" % (ltext, rtext), lnum + rnum) + elif lnum >= 100 > rnum: + return ("%sу %s" % (ltext, rtext), lnum + rnum) + elif rnum > lnum: + if ltext == "яксад" and rtext not in self.low_numwords: + return ("сад %s" % (rtext), lnum * rnum) + if rtext == "сад": + return ("%s%s" % (ltext, rtext), lnum * rnum) + else: + return ("%s %s" % (ltext, rtext), lnum * rnum) + return ("%sу %s" % (ltext, rtext), lnum + rnum) + + def to_ordinal(self, value): + self.verify_ordinal(value) + cardinal = self.to_cardinal(value) + outwords = cardinal.split(" ") + lastword = outwords[-1] + if lastword in ["ду", "се", "си"]: + return "%sюм" % (cardinal) + else: + return "%sум" % (cardinal) + + def to_ordinal_num(self, value): + self.verify_ordinal(value) + return "%s%s" % (value, self.to_ordinal(value)[-2:]) diff --git a/tests/test_tg.py b/tests/test_tg.py new file mode 100644 index 0000000..2caa9cf --- /dev/null +++ b/tests/test_tg.py @@ -0,0 +1,104 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2003, Taro Ogawa. All Rights Reserved. +# Copyright (c) 2013, Savoir-faire Linux inc. All Rights Reserved. + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA + +from __future__ import unicode_literals + +from unittest import TestCase + +from num2words import num2words + + +class Num2WordsTGTest(TestCase): + def test_cardinal(self): + self.assertEqual(num2words(100, lang="tg"), "сад") + self.assertEqual(num2words(101, lang="tg"), "яксаду як") + self.assertEqual(num2words(110, lang="tg"), "яксаду даҳ") + self.assertEqual(num2words(115, lang="tg"), "яксаду понздаҳ") + self.assertEqual(num2words(123, lang="tg"), "яксаду бисту се") + self.assertEqual(num2words(1000, lang="tg"), "як ҳазор") + self.assertEqual(num2words(1001, lang="tg"), "як ҳазору як") + self.assertEqual(num2words(2012, lang="tg"), "ду ҳазору дувоздаҳ") + self.assertEqual( + num2words(12519.85, lang="tg"), + "дувоздаҳ ҳазору панҷсаду нуздаҳ нуқта ҳашт панҷ", + ) + + self.assertEqual( + num2words(1234567890, lang="tg"), + "як миллиарду дусаду сию чор миллиону панҷсаду шасту ҳафт ҳазору " + "ҳаштсаду навад", + ) + self.assertEqual(num2words(5, lang="tg"), "панҷ") + self.assertEqual(num2words(-1, lang="tg"), "минус як") + self.assertEqual(num2words(-15, lang="tg"), "минус понздаҳ") + self.assertEqual(num2words(-100, lang="tg"), "минус сад") + + def test_to_ordinal(self): + self.assertEqual(num2words(1, lang="tg", to="ordinal"), "якум") + self.assertEqual(num2words(2, lang="tg", to="ordinal"), "дуюм") + self.assertEqual(num2words(3, lang="tg", to="ordinal"), "сеюм") + self.assertEqual(num2words(30, lang="tg", to="ordinal"), "сиюм") + + self.assertEqual(num2words(13, lang="tg", to="ordinal"), "сенздаҳум") + self.assertEqual(num2words(20, lang="tg", to="ordinal"), "бистум") + self.assertEqual(num2words(23, lang="tg", to="ordinal"), "бисту сеюм") + self.assertEqual(num2words(100, lang="tg", to="ordinal"), "садум") + self.assertEqual(num2words(136, lang="tg", to="ordinal"), + "яксаду сию шашум") + self.assertEqual(num2words(500, lang="tg", to="ordinal"), "панҷсадум") + self.assertEqual( + num2words(1000, lang="tg", to="ordinal"), "як ҳазорум" + ) + self.assertEqual( + num2words(1001, lang="tg", to="ordinal"), "як ҳазору якум" + ) + self.assertEqual( + num2words(2000, lang="tg", to="ordinal"), "ду ҳазорум" + ) + self.assertEqual( + num2words(1000000, lang="tg", to="ordinal"), "як миллионум" + ) + self.assertEqual( + num2words(1000000000, lang="tg", to="ordinal"), "як миллиардум" + ) + + def test_to_currency(self): + self.assertEqual( + num2words(1.0, lang="tg", to="currency", currency="EUR"), + "як евро, сифр сент", + ) + self.assertEqual( + num2words(1.0, lang="tg", to="currency", currency="TJS"), + "як сомонӣ, сифр дирам", + ) + self.assertEqual( + num2words(1234.56, lang="tg", to="currency", currency="TJS"), + "як ҳазору дусаду сию чор сомонӣ, панҷову шаш дирам", + ) + self.assertEqual( + num2words(1234.56, lang="tg", to="currency", currency="RUB"), + "як ҳазору дусаду сию чор рубл, панҷову шаш копейк", + ) + self.assertEqual( + num2words(12519.85, lang="tg", to="currency", currency="TJS", + cents=False), + "дувоздаҳ ҳазору панҷсаду нуздаҳ сомонӣ, 85 дирам", + ) + self.assertEqual( + num2words("1230.56", lang="tg", to="currency", currency="USD"), + "як ҳазору дусаду си доллар, панҷову шаш сент", + ) \ No newline at end of file From 1d98e0261e53f27a2537b36d8ae82ebf63d41c32 Mon Sep 17 00:00:00 2001 From: Drew Echerd Date: Fri, 2 Jul 2021 18:51:22 +0500 Subject: [PATCH 12/28] Added test for OverflowError and ordinal_num --- tests/test_tg.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_tg.py b/tests/test_tg.py index 2caa9cf..a846d70 100644 --- a/tests/test_tg.py +++ b/tests/test_tg.py @@ -24,7 +24,10 @@ from num2words import num2words class Num2WordsTGTest(TestCase): def test_cardinal(self): + with self.assertRaises(OverflowError): + num2words(1000000000000000000000000, lang='tg') self.assertEqual(num2words(100, lang="tg"), "сад") + self.assertEqual(num2words(100000, lang="tg"), "сад ҳазор") self.assertEqual(num2words(101, lang="tg"), "яксаду як") self.assertEqual(num2words(110, lang="tg"), "яксаду даҳ") self.assertEqual(num2words(115, lang="tg"), "яксаду понздаҳ") @@ -101,4 +104,9 @@ class Num2WordsTGTest(TestCase): self.assertEqual( num2words("1230.56", lang="tg", to="currency", currency="USD"), "як ҳазору дусаду си доллар, панҷову шаш сент", + ) + + def test_to_ordinal_num(self): + self.assertEqual( + num2words("100", lang="tg", to="ordinal_num"), "100ум", ) \ No newline at end of file From cdb2acb052cae15fb1e37dd3b70dfd8f79adc584 Mon Sep 17 00:00:00 2001 From: Drew Echerd Date: Fri, 2 Jul 2021 22:29:28 +0500 Subject: [PATCH 13/28] Added MEGA_SUFFIX and GIGA_SUFFIX tests --- num2words/lang_TG.py | 3 ++- tests/test_tg.py | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/num2words/lang_TG.py b/num2words/lang_TG.py index cc0bf49..8a028d5 100644 --- a/num2words/lang_TG.py +++ b/num2words/lang_TG.py @@ -43,7 +43,7 @@ class Num2Word_TG(lang_EU.Num2Word_EU): if n == 9: self.cards[10 ** n] = word + self.GIGA_SUFFIX - elif self.MEGA_SUFFIX: + else: self.cards[10 ** n] = word + self.MEGA_SUFFIX def setup(self): @@ -147,3 +147,4 @@ class Num2Word_TG(lang_EU.Num2Word_EU): def to_ordinal_num(self, value): self.verify_ordinal(value) return "%s%s" % (value, self.to_ordinal(value)[-2:]) + diff --git a/tests/test_tg.py b/tests/test_tg.py index a846d70..482913b 100644 --- a/tests/test_tg.py +++ b/tests/test_tg.py @@ -45,6 +45,9 @@ class Num2WordsTGTest(TestCase): "як миллиарду дусаду сию чор миллиону панҷсаду шасту ҳафт ҳазору " "ҳаштсаду навад", ) + self.assertEqual(num2words(1000000, lang="tg"), "як миллион") + self.assertEqual(num2words(1000000000, lang="tg"), "як миллиард") + self.assertEqual(num2words(1000000000000, lang="tg"), "як триллион") self.assertEqual(num2words(5, lang="tg"), "панҷ") self.assertEqual(num2words(-1, lang="tg"), "минус як") self.assertEqual(num2words(-15, lang="tg"), "минус понздаҳ") From b8a1d3168ef76a11b8932b4c3cb14e7ed5957d50 Mon Sep 17 00:00:00 2001 From: Katsuya Iida Date: Fri, 20 Aug 2021 17:29:01 +0900 Subject: [PATCH 14/28] =?UTF-8?q?Support=20Japanese=20Reiwa=20(=E4=BB=A4?= =?UTF-8?q?=E5=92=8C/=E3=82=8C=E3=81=84=E3=82=8F)=20era.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- num2words/lang_JA.py | 1 + tests/test_ja.py | 17 +++++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/num2words/lang_JA.py b/num2words/lang_JA.py index 58a2946..1e0e622 100644 --- a/num2words/lang_JA.py +++ b/num2words/lang_JA.py @@ -335,6 +335,7 @@ ERA_START = [ (1912, ("大正", "たいしょう")), (1926, ("昭和", "しょうわ")), (1989, ("平成", "へいせい")), + (2019, ("令和", "れいわ")), ] diff --git a/tests/test_ja.py b/tests/test_ja.py index eaac20b..42da144 100644 --- a/tests/test_ja.py +++ b/tests/test_ja.py @@ -163,6 +163,21 @@ class Num2WordsJATest(TestCase): "はちじゅうきゅうえん") def test_year(self): + self.assertEqual(n2j(2021, to="year"), "令和三年") + self.assertEqual(n2j(2021, to="year", reading=True), + "れいわさんねん") + self.assertEqual(n2j(2021, to="year", reading="arabic"), + "令和3年") + self.assertEqual(n2j(2019, to="year"), "令和元年") + self.assertEqual(n2j(2019, to="year", reading=True), + "れいわがんねん") + self.assertEqual(n2j(2019, to="year", reading="arabic"), + "令和1年") + self.assertEqual(n2j(2018, to="year"), "平成三十年") + self.assertEqual(n2j(2018, to="year", reading=True), + "へいせいさんじゅうねん") + self.assertEqual(n2j(2018, to="year", reading="arabic"), + "平成30年") self.assertEqual(n2j(2017, to="year"), "平成二十九年") self.assertEqual(n2j(2017, to="year", reading=True), "へいせいにじゅうくねん") @@ -176,8 +191,6 @@ class Num2WordsJATest(TestCase): "にせんねん") self.assertEqual(n2j(645, to="year"), "大化元年") self.assertEqual(n2j(645, to="year", reading=True), "たいかがんねん") - self.assertEqual(n2j(645, to="year"), "大化元年") - self.assertEqual(n2j(645, to="year", reading=True), "たいかがんねん") self.assertEqual(n2j(-99, to="year", era=False), "紀元前九十九年") self.assertEqual(n2j(-99, to="year", era=False, reading=True), "きげんぜんきゅうじゅうくねん") From d741a9d79015f2b009706951791a09c8b89dd478 Mon Sep 17 00:00:00 2001 From: Marlon Rodriguez Garcia <47992153+mrodriguezg1991@users.noreply.github.com> Date: Thu, 4 Aug 2022 16:17:58 -0400 Subject: [PATCH 15/28] Update __init__.py remove space --- num2words/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/num2words/__init__.py b/num2words/__init__.py index 6040017..1dfb5b0 100644 --- a/num2words/__init__.py +++ b/num2words/__init__.py @@ -21,7 +21,7 @@ from . import (lang_AR, lang_CZ, lang_DE, lang_DK, lang_EN, lang_EN_IN, lang_ES, lang_ES_CO, lang_ES_NI, lang_ES_VE, lang_FA, lang_FI, lang_FR, lang_FR_BE, lang_FR_CH, lang_FR_DZ, lang_HE, lang_HU, lang_ID, lang_IT, lang_JA, lang_KN, lang_KO, lang_KZ, lang_LT, - lang_LV, lang_NL, lang_NO, lang_PL, lang_PT, lang_PT_BR, + lang_LV, lang_NL, lang_NO, lang_PL, lang_PT, lang_PT_BR, lang_RO, lang_RU, lang_SL, lang_SR, lang_SV, lang_TE, lang_TH, lang_TR, lang_UK, lang_VI) From 4b13dfddb7dc60821d840dbdf85c0372ba96c1fd Mon Sep 17 00:00:00 2001 From: Gabriel Shekler <46564521+gs202@users.noreply.github.com> Date: Sat, 6 Aug 2022 13:51:57 +0300 Subject: [PATCH 16/28] fix flake issues --- tests/test_he.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/test_he.py b/tests/test_he.py index 688bbf0..2ce6797 100644 --- a/tests/test_he.py +++ b/tests/test_he.py @@ -89,9 +89,15 @@ class Num2WordsHETest(TestCase): def test_to_currency(self): n = Num2Word_HE() - self.assertEqual(n.to_currency(20.0 ,currency='NIS'), 'עשרים שקלים ואפס אגורות') - self.assertEqual(n.to_currency(100.0 ,currency='NIS'), 'מאה שקלים ואפס אגורות') - self.assertEqual(n.to_currency(100.50 ,currency='NIS'), 'מאה שקלים וחמישים אגורות') + self.assertEqual( + n.to_currency(20.0, currency='NIS'), 'עשרים שקלים ואפס אגורות' + ) + self.assertEqual( + (n.to_currency(100.0, currency='NIS'), 'מאה שקלים ואפס אגורות' + ) + self.assertEqual( + (n.to_currency(100.50, currency='NIS'), 'מאה שקלים וחמישים אגורות' + ) def test_to_cardinal(self): n = Num2Word_HE() From 55deaa9cbee39498baa9614abc33a7c89fcadc57 Mon Sep 17 00:00:00 2001 From: Gabriel Shekler <46564521+gs202@users.noreply.github.com> Date: Sat, 6 Aug 2022 13:53:14 +0300 Subject: [PATCH 17/28] typo --- tests/test_he.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_he.py b/tests/test_he.py index 2ce6797..e50a6a2 100644 --- a/tests/test_he.py +++ b/tests/test_he.py @@ -93,10 +93,10 @@ class Num2WordsHETest(TestCase): n.to_currency(20.0, currency='NIS'), 'עשרים שקלים ואפס אגורות' ) self.assertEqual( - (n.to_currency(100.0, currency='NIS'), 'מאה שקלים ואפס אגורות' + n.to_currency(100.0, currency='NIS'), 'מאה שקלים ואפס אגורות' ) self.assertEqual( - (n.to_currency(100.50, currency='NIS'), 'מאה שקלים וחמישים אגורות' + n.to_currency(100.50, currency='NIS'), 'מאה שקלים וחמישים אגורות' ) def test_to_cardinal(self): From ed97e11bfbc8521bffc74ead59927f83d813cfc2 Mon Sep 17 00:00:00 2001 From: Eyosiyas Bereketab Date: Sun, 7 Aug 2022 22:19:49 +0300 Subject: [PATCH 18/28] Add Amharic language --- num2words/lang_AM.py | 107 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 num2words/lang_AM.py diff --git a/num2words/lang_AM.py b/num2words/lang_AM.py new file mode 100644 index 0000000..5d1e7bb --- /dev/null +++ b/num2words/lang_AM.py @@ -0,0 +1,107 @@ +# -*- 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, print_function, unicode_literals + +from . import lang_EU + + +class Num2Word_AM(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 + "ሊዮን" + + def setup(self): + super(Num2Word_AM, self).setup() + + self.negword = "አሉታዊ " + self.pointword = "ነጥብ" + self.exclude_title = ["እና", "ነጥብ", "አሉታዊ"] + + self.mid_numwords = [(1000, "ሺህ"), (100, "መቶ"), (90, "ዘጠና"), + (80, "ሰማኒያ"), (70, "ሰባ"), (60, "ስድሳ"), + (50, "አምሳ"), (40, "አርባ"), (30, "ሠላሳ")] + self.low_numwords = ["ሃያ", "አሥራ ዘጠኝ", "አሥራ ስምንት", "አሥራ ሰባት", + "አስራ ስድስት", "አሥራ አምስት", "አሥራ አራት", "አሥራ ሦስት", + "አሥራ ሁለት", "አሥራ አንድ", "አሥር", "ዘጠኝ", "ስምንት", + "ሰባት", "ስድስት", "አምስት", "አራት", "ሦስት", "ሁለት", + "አንድ", "ዜሮ"] + self.ords = {"አንድ": "አንደኛ", + "ሁለት": "ሁለተኛ", + "ሦስት": "ሦስተኛ", + "አራት": "አራተኛ", + "አምስት": "አምስተኛ", + "ስድስት": "ስድስተኛ", + "ሰባት": "ሰባተኛ", + "ስምንት": "ስምንተኛ", + "ዘጠኝ": "ዘጠነኛ", + "አሥር": "አሥረኛ", + "አሥራ አንድ": "አሥራ አንድ", + "አሥራ ሁለት": "አሥራ ሁለተኛ", + "አሥራ ሦስት": "አሥራ ሦስተኛ", + "አሥራ አራት": "አሥራ አራተኛ", + "አሥራ አምስት": "አሥራ አምስተኛ", + "አሥራ ስድስት": "አሥራ ስድስተኛ", + "አሥራ ሰባት": "አሥራ ሰባተኛ", + "አሥራ ስምንት": "አሥራ ስምንተኛ", + "አሥራ ዘጠኝ": "አሥራ ዘጠነኛ"} + + def merge(self, lpair, rpair): + ltext, lnum = lpair + rtext, rnum = rpair + 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 እና %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[-1] == "y": + lastword = lastword[:-1] + "ie" + lastword += "th" + 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_currency(self, val, currency='ብር', cents=True, separator='.', + adjective=False): + result = super(Num2Word_AM, self).to_currency( + val, currency=currency, cents=cents, separator=separator, + adjective=adjective) + return result + + def to_year(self, val, longval=True): + if not (val // 100) % 10: + return self.to_cardinal(val) + return self.to_splitnum(val, hightxt="መቶ", longval=longval) \ + .replace(' ', '') From c06ff54f6485b67f4d0819be4cf88627eb1ca891 Mon Sep 17 00:00:00 2001 From: Eyosiyas Bereketab Date: Sun, 7 Aug 2022 22:30:30 +0300 Subject: [PATCH 19/28] Update init for Amharic language --- num2words/__init__.py | 3 ++- tests/test_am.py | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 1 deletion(-) create mode 100644 tests/test_am.py diff --git a/num2words/__init__.py b/num2words/__init__.py index 1dfb5b0..baba5bd 100644 --- a/num2words/__init__.py +++ b/num2words/__init__.py @@ -23,10 +23,11 @@ from . import (lang_AR, lang_CZ, lang_DE, lang_DK, lang_EN, lang_EN_IN, lang_ID, lang_IT, lang_JA, lang_KN, lang_KO, lang_KZ, lang_LT, lang_LV, lang_NL, lang_NO, lang_PL, lang_PT, lang_PT_BR, lang_RO, lang_RU, lang_SL, lang_SR, lang_SV, lang_TE, lang_TH, - lang_TR, lang_UK, lang_VI) + lang_TR, lang_UK, lang_VI, lang_AM) CONVERTER_CLASSES = { 'ar': lang_AR.Num2Word_AR(), + 'am': lang_AM.Num2Word_AM(), 'cz': lang_CZ.Num2Word_CZ(), 'en': lang_EN.Num2Word_EN(), 'en_IN': lang_EN_IN.Num2Word_EN_IN(), diff --git a/tests/test_am.py b/tests/test_am.py new file mode 100644 index 0000000..1a8436c --- /dev/null +++ b/tests/test_am.py @@ -0,0 +1,23 @@ +# -*- 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 unittest import TestCase + +from num2words import num2words + + +class Num2WordsAMTest(TestCase): From 47d63acabf78005bc8d71a853425416047dce2b6 Mon Sep 17 00:00:00 2001 From: Eyosiyas Bereketab Date: Sun, 7 Aug 2022 22:34:49 +0300 Subject: [PATCH 20/28] Update Amharic language --- num2words/lang_AM.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/num2words/lang_AM.py b/num2words/lang_AM.py index 5d1e7bb..6e4f2f9 100644 --- a/num2words/lang_AM.py +++ b/num2words/lang_AM.py @@ -67,9 +67,9 @@ class Num2Word_AM(lang_EU.Num2Word_EU): if lnum == 1 and rnum < 100: return (rtext, rnum) elif 100 > lnum > rnum: - return ("%s-%s" % (ltext, rtext), lnum + rnum) + return ("%s %s" % (ltext, rtext), lnum + rnum) elif lnum >= 100 > rnum: - return ("%s እና %s" % (ltext, rtext), lnum + rnum) + return ("%s %s" % (ltext, rtext), lnum + rnum) elif rnum > lnum: return ("%s %s" % (ltext, rtext), lnum * rnum) return ("%s, %s" % (ltext, rtext), lnum + rnum) @@ -86,7 +86,7 @@ class Num2Word_AM(lang_EU.Num2Word_EU): lastword = lastword[:-1] + "ie" lastword += "th" lastwords[-1] = self.title(lastword) - outwords[-1] = "-".join(lastwords) + outwords[-1] = " ".join(lastwords) return " ".join(outwords) def to_ordinal_num(self, value): From d1a85b315a5401e3313d0023dbe6f19e6b9c5aae Mon Sep 17 00:00:00 2001 From: Eyosiyas Bereketab Date: Sun, 7 Aug 2022 23:02:25 +0300 Subject: [PATCH 21/28] Add Amharic currency form --- num2words/lang_AM.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/num2words/lang_AM.py b/num2words/lang_AM.py index 6e4f2f9..024e3e1 100644 --- a/num2words/lang_AM.py +++ b/num2words/lang_AM.py @@ -21,6 +21,8 @@ from . import lang_EU class Num2Word_AM(lang_EU.Num2Word_EU): + CURRENCY_FORMS = {'ETB': (('ብር', 'ብር'), ('ሳንቲም', 'ሳንቲም'))} + def set_high_numwords(self, high): max = 3 + 3 * len(high) for word, n in zip(high, range(max, 3, -3)): @@ -93,8 +95,8 @@ class Num2Word_AM(lang_EU.Num2Word_EU): self.verify_ordinal(value) return "%s%s" % (value, self.to_ordinal(value)[-2:]) - def to_currency(self, val, currency='ብር', cents=True, separator='.', - adjective=False): + def to_currency(self, val, currency='ብር', cents=True, separator=' ከ', + adjective=True): result = super(Num2Word_AM, self).to_currency( val, currency=currency, cents=cents, separator=separator, adjective=adjective) From 308965bb993a46138fdf9585bccfa940f295f631 Mon Sep 17 00:00:00 2001 From: Eyosiyas Bereketab Date: Sun, 7 Aug 2022 23:06:48 +0300 Subject: [PATCH 22/28] Add test cases --- num2words/lang_AM.py | 3 +-- tests/test_am.py | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/num2words/lang_AM.py b/num2words/lang_AM.py index 024e3e1..4072559 100644 --- a/num2words/lang_AM.py +++ b/num2words/lang_AM.py @@ -105,5 +105,4 @@ class Num2Word_AM(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="መቶ", longval=longval) \ - .replace(' ', '') + return self.to_splitnum(val, hightxt="መቶ", longval=longval) diff --git a/tests/test_am.py b/tests/test_am.py index 1a8436c..6c47955 100644 --- a/tests/test_am.py +++ b/tests/test_am.py @@ -21,3 +21,46 @@ from num2words import num2words class Num2WordsAMTest(TestCase): + def test_and_join_199(self): + self.assertEqual(num2words(199, lang='am'), "አንድ መቶ ዘጠና ዘጠኝ") + + def test_ordinal(self): + self.assertEqual( + num2words(1, lang='am', to='ordinal'), + 'አንደኛ' + ) + self.assertEqual( + num2words(13, lang='am', to='ordinal'), + 'አሥራ ሦስተኛ' + ) + self.assertEqual( + num2words(22, lang='am', to='ordinal'), + 'ሃያ ሁለተኛ' + ) + + def test_to_currency(self): + self.assertEqual( + num2words('38.4', lang='am', to='currency', cents=False, currency='ETB'), + "ሠላሳ ስምንት ብር ከ 40 ሳንቲም" + ) + self.assertEqual( + num2words('0', lang='am', to='currency', separator=' እና', cents=True, currency='ETB'), + "ዜሮ ብር እና ዜሮ ሳንቲም" + ) + + self.assertEqual( + num2words('1.50', lang='am', to='currency', cents=True, currency='ETB'), + "አንድ ብር ከ አምሳ ሳንቲም" + ) + + def test_to_year(self): + self.assertEqual(num2words(1990, lang='am', to='year'), + 'አሥራ ዘጠኝ መቶ ዘጠና') + self.assertEqual(num2words(5555, lang='am', to='year'), + 'አምሳ አምስት መቶ አምሳ አምስት') + self.assertEqual(num2words(2017, lang='am', to='year'), + 'ሁለት ሺህ አሥራ ሰባት') + self.assertEqual(num2words(1066, lang='am', to='year'), + 'አንድ ሺህ ስድሳ ስድስት') + self.assertEqual(num2words(1865, lang='am', to='year'), + 'አሥራ ስምንት መቶ ስድሳ አምስት') From c586bef2d6cdd0e18e8941177674c9ed5ec80c14 Mon Sep 17 00:00:00 2001 From: Eyosiyas Bereketab Date: Tue, 9 Aug 2022 11:14:40 +0300 Subject: [PATCH 23/28] Fix flake 8 requirements in python3.1 and increase test coverage. --- num2words/__init__.py | 16 ++++++++-------- num2words/lang_AM.py | 2 +- tests/test_am.py | 31 +++++++++++++++++++++++++------ 3 files changed, 34 insertions(+), 15 deletions(-) diff --git a/num2words/__init__.py b/num2words/__init__.py index baba5bd..dc42220 100644 --- a/num2words/__init__.py +++ b/num2words/__init__.py @@ -17,17 +17,17 @@ from __future__ import unicode_literals -from . import (lang_AR, lang_CZ, lang_DE, lang_DK, lang_EN, lang_EN_IN, - lang_ES, lang_ES_CO, lang_ES_NI, lang_ES_VE, lang_FA, lang_FI, - lang_FR, lang_FR_BE, lang_FR_CH, lang_FR_DZ, lang_HE, lang_HU, - lang_ID, lang_IT, lang_JA, lang_KN, lang_KO, lang_KZ, lang_LT, - lang_LV, lang_NL, lang_NO, lang_PL, lang_PT, lang_PT_BR, - lang_RO, lang_RU, lang_SL, lang_SR, lang_SV, lang_TE, lang_TH, - lang_TR, lang_UK, lang_VI, lang_AM) +from . import (lang_AM, lang_AR, lang_CZ, lang_DE, lang_DK, lang_EN, + lang_EN_IN, lang_ES, lang_ES_CO, lang_ES_NI, lang_ES_VE, + lang_FA, lang_FI, lang_FR, lang_FR_BE, lang_FR_CH, lang_FR_DZ, + lang_HE, lang_HU, lang_ID, lang_IT, lang_JA, lang_KN, lang_KO, + lang_KZ, lang_LT, lang_LV, lang_NL, lang_NO, lang_PL, lang_PT, + lang_PT_BR, lang_RO, lang_RU, lang_SL, lang_SR, lang_SV, lang_TE, + lang_TH, lang_TR, lang_UK, lang_VI) CONVERTER_CLASSES = { - 'ar': lang_AR.Num2Word_AR(), 'am': lang_AM.Num2Word_AM(), + 'ar': lang_AR.Num2Word_AR(), 'cz': lang_CZ.Num2Word_CZ(), 'en': lang_EN.Num2Word_EN(), 'en_IN': lang_EN_IN.Num2Word_EN_IN(), diff --git a/num2words/lang_AM.py b/num2words/lang_AM.py index 4072559..4e40bda 100644 --- a/num2words/lang_AM.py +++ b/num2words/lang_AM.py @@ -93,7 +93,7 @@ class Num2Word_AM(lang_EU.Num2Word_EU): def to_ordinal_num(self, value): self.verify_ordinal(value) - return "%s%s" % (value, self.to_ordinal(value)[-2:]) + return "%s%s" % (value, self.to_ordinal(value)[-1:]) def to_currency(self, val, currency='ብር', cents=True, separator=' ከ', adjective=True): diff --git a/tests/test_am.py b/tests/test_am.py index 6c47955..a3dc95d 100644 --- a/tests/test_am.py +++ b/tests/test_am.py @@ -38,19 +38,38 @@ class Num2WordsAMTest(TestCase): 'ሃያ ሁለተኛ' ) + def test_ordinal_num(self): + self.assertEqual(num2words(10, lang='am', to='ordinal_num'), '10ኛ') + self.assertEqual(num2words(21, lang='am', to='ordinal_num'), '21ኛ') + self.assertEqual(num2words(102, lang='am', to='ordinal_num'), '102ኛ') + + def test_cardinal_for_float_number(self): + self.assertEqual(num2words(12.5, lang='am'), "አሥራ ሁለት ነጥብ አምስት") + self.assertEqual(num2words(12.51, lang='am'), "አሥራ ሁለት ነጥብ አምስት አንድ") + self.assertEqual(num2words(12.53, lang='am'), "አሥራ ሁለት ነጥብ አምስት ሦስት") + + def test_overflow(self): + with self.assertRaises(OverflowError): + num2words("1000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000", lang='am') + def test_to_currency(self): self.assertEqual( - num2words('38.4', lang='am', to='currency', cents=False, currency='ETB'), - "ሠላሳ ስምንት ብር ከ 40 ሳንቲም" + num2words('38.4', lang='am', to='currency', cents=False, + currency='ETB'), "ሠላሳ ስምንት ብር ከ 40 ሳንቲም" ) self.assertEqual( - num2words('0', lang='am', to='currency', separator=' እና', cents=True, currency='ETB'), - "ዜሮ ብር እና ዜሮ ሳንቲም" + num2words('0', lang='am', to='currency', separator=' እና', + cents=True, currency='ETB'), "ዜሮ ብር እና ዜሮ ሳንቲም" ) self.assertEqual( - num2words('1.50', lang='am', to='currency', cents=True, currency='ETB'), - "አንድ ብር ከ አምሳ ሳንቲም" + num2words('1.50', lang='am', to='currency', cents=True, + currency='ETB'), "አንድ ብር ከ አምሳ ሳንቲም" ) def test_to_year(self): From c966c983371b665cf294c8804d7cfa58b4b2d451 Mon Sep 17 00:00:00 2001 From: Drew Echerd Date: Tue, 9 Aug 2022 09:57:58 -0400 Subject: [PATCH 24/28] Fixed flake8 errors --- num2words/lang_TG.py | 1 - tests/test_tg.py | 19 +++++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/num2words/lang_TG.py b/num2words/lang_TG.py index 8a028d5..48291a2 100644 --- a/num2words/lang_TG.py +++ b/num2words/lang_TG.py @@ -147,4 +147,3 @@ class Num2Word_TG(lang_EU.Num2Word_EU): def to_ordinal_num(self, value): self.verify_ordinal(value) return "%s%s" % (value, self.to_ordinal(value)[-2:]) - diff --git a/tests/test_tg.py b/tests/test_tg.py index 482913b..53a49e4 100644 --- a/tests/test_tg.py +++ b/tests/test_tg.py @@ -25,7 +25,7 @@ from num2words import num2words class Num2WordsTGTest(TestCase): def test_cardinal(self): with self.assertRaises(OverflowError): - num2words(1000000000000000000000000, lang='tg') + num2words(1000000000000000000000000, lang="tg") self.assertEqual(num2words(100, lang="tg"), "сад") self.assertEqual(num2words(100000, lang="tg"), "сад ҳазор") self.assertEqual(num2words(101, lang="tg"), "яксаду як") @@ -63,8 +63,9 @@ class Num2WordsTGTest(TestCase): self.assertEqual(num2words(20, lang="tg", to="ordinal"), "бистум") self.assertEqual(num2words(23, lang="tg", to="ordinal"), "бисту сеюм") self.assertEqual(num2words(100, lang="tg", to="ordinal"), "садум") - self.assertEqual(num2words(136, lang="tg", to="ordinal"), - "яксаду сию шашум") + self.assertEqual( + num2words(136, lang="tg", to="ordinal"), "яксаду сию шашум" + ) self.assertEqual(num2words(500, lang="tg", to="ordinal"), "панҷсадум") self.assertEqual( num2words(1000, lang="tg", to="ordinal"), "як ҳазорум" @@ -100,16 +101,18 @@ class Num2WordsTGTest(TestCase): "як ҳазору дусаду сию чор рубл, панҷову шаш копейк", ) self.assertEqual( - num2words(12519.85, lang="tg", to="currency", currency="TJS", - cents=False), + num2words( + 12519.85, lang="tg", to="currency", currency="TJS", cents=False + ), "дувоздаҳ ҳазору панҷсаду нуздаҳ сомонӣ, 85 дирам", ) self.assertEqual( num2words("1230.56", lang="tg", to="currency", currency="USD"), "як ҳазору дусаду си доллар, панҷову шаш сент", ) - + def test_to_ordinal_num(self): self.assertEqual( - num2words("100", lang="tg", to="ordinal_num"), "100ум", - ) \ No newline at end of file + num2words("100", lang="tg", to="ordinal_num"), + "100ум", + ) From 6ea21b82005e0faf5d2fb0b0ae58a80237aa37e8 Mon Sep 17 00:00:00 2001 From: Eyosiyas Bereketab Date: Tue, 9 Aug 2022 17:00:56 +0300 Subject: [PATCH 25/28] Fix line too long __init__.py --- num2words/__init__.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/num2words/__init__.py b/num2words/__init__.py index dc42220..86f1b63 100644 --- a/num2words/__init__.py +++ b/num2words/__init__.py @@ -22,8 +22,8 @@ from . import (lang_AM, lang_AR, lang_CZ, lang_DE, lang_DK, lang_EN, lang_FA, lang_FI, lang_FR, lang_FR_BE, lang_FR_CH, lang_FR_DZ, lang_HE, lang_HU, lang_ID, lang_IT, lang_JA, lang_KN, lang_KO, lang_KZ, lang_LT, lang_LV, lang_NL, lang_NO, lang_PL, lang_PT, - lang_PT_BR, lang_RO, lang_RU, lang_SL, lang_SR, lang_SV, lang_TE, - lang_TH, lang_TR, lang_UK, lang_VI) + lang_PT_BR, lang_RO, lang_RU, lang_SL, lang_SR, lang_SV, + lang_TE, lang_TH, lang_TR, lang_UK, lang_VI) CONVERTER_CLASSES = { 'am': lang_AM.Num2Word_AM(), From 75fdd6289f80926d0870206e257436917433b18c Mon Sep 17 00:00:00 2001 From: Eyosiyas Bereketab Date: Tue, 9 Aug 2022 21:13:53 +0300 Subject: [PATCH 26/28] Update Amharic language support --- README.rst | 3 +- num2words/lang_AM.py | 120 ++++++++++++++++++++++++++----------------- tests/test_am.py | 39 ++++++++------ 3 files changed, 98 insertions(+), 64 deletions(-) diff --git a/README.rst b/README.rst index 0414531..7b9b487 100644 --- a/README.rst +++ b/README.rst @@ -76,6 +76,7 @@ Besides the numerical argument, there are two main optional arguments. **lang:** The language in which to convert the number. Supported values are: * ``en`` (English, default) +* ``am`` (Amharic) * ``ar`` (Arabic) * ``cz`` (Czech) * ``de`` (German) @@ -149,4 +150,4 @@ added Lithuanian support, but didn't take over maintenance of the project. I am thus basing myself on Marius Grigaitis' improvements and re-publishing ``pynum2word`` as ``num2words``. -Virgil Dupras, Savoir-faire Linux +Virgil Dupras, Savoir-faire Linux \ No newline at end of file diff --git a/num2words/lang_AM.py b/num2words/lang_AM.py index 4e40bda..3efd9c2 100644 --- a/num2words/lang_AM.py +++ b/num2words/lang_AM.py @@ -23,45 +23,73 @@ from . import lang_EU class Num2Word_AM(lang_EU.Num2Word_EU): CURRENCY_FORMS = {'ETB': (('ብር', 'ብር'), ('ሳንቲም', 'ሳንቲም'))} + GIGA_SUFFIX = 'ቢሊዮን' + MEGA_SUFFIX = 'ሚሊዮን' + 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 + "ሊዮን" + cap = 3 * (len(high) + 1) + + for word, n in zip(high, range(cap, 5, -3)): + if n == 9: + self.cards[10 ** n] = word + self.GIGA_SUFFIX + else: + self.cards[10 ** n] = word + self.MEGA_SUFFIX def setup(self): super(Num2Word_AM, self).setup() - self.negword = "አሉታዊ " - self.pointword = "ነጥብ" - self.exclude_title = ["እና", "ነጥብ", "አሉታዊ"] + self.negword = 'አሉታዊ ' + self.pointword = 'ነጥብ' + self.exclude_title = ['እና', 'ነጥብ', 'አሉታዊ'] - self.mid_numwords = [(1000, "ሺህ"), (100, "መቶ"), (90, "ዘጠና"), - (80, "ሰማኒያ"), (70, "ሰባ"), (60, "ስድሳ"), - (50, "አምሳ"), (40, "አርባ"), (30, "ሠላሳ")] - self.low_numwords = ["ሃያ", "አሥራ ዘጠኝ", "አሥራ ስምንት", "አሥራ ሰባት", - "አስራ ስድስት", "አሥራ አምስት", "አሥራ አራት", "አሥራ ሦስት", - "አሥራ ሁለት", "አሥራ አንድ", "አሥር", "ዘጠኝ", "ስምንት", - "ሰባት", "ስድስት", "አምስት", "አራት", "ሦስት", "ሁለት", - "አንድ", "ዜሮ"] - self.ords = {"አንድ": "አንደኛ", - "ሁለት": "ሁለተኛ", - "ሦስት": "ሦስተኛ", - "አራት": "አራተኛ", - "አምስት": "አምስተኛ", - "ስድስት": "ስድስተኛ", - "ሰባት": "ሰባተኛ", - "ስምንት": "ስምንተኛ", - "ዘጠኝ": "ዘጠነኛ", - "አሥር": "አሥረኛ", - "አሥራ አንድ": "አሥራ አንድ", - "አሥራ ሁለት": "አሥራ ሁለተኛ", - "አሥራ ሦስት": "አሥራ ሦስተኛ", - "አሥራ አራት": "አሥራ አራተኛ", - "አሥራ አምስት": "አሥራ አምስተኛ", - "አሥራ ስድስት": "አሥራ ስድስተኛ", - "አሥራ ሰባት": "አሥራ ሰባተኛ", - "አሥራ ስምንት": "አሥራ ስምንተኛ", - "አሥራ ዘጠኝ": "አሥራ ዘጠነኛ"} + self.mid_numwords = [(1000, 'ሺህ'), (100, 'መቶ'), (90, 'ዘጠና'), + (80, 'ሰማኒያ'), (70, 'ሰባ'), (60, 'ስድሳ'), + (50, 'አምሳ'), (40, 'አርባ'), (30, 'ሠላሳ')] + self.low_numwords = ['ሃያ', 'አሥራ ዘጠኝ', 'አሥራ ስምንት', 'አሥራ ሰባት', + 'አስራ ስድስት', 'አሥራ አምስት', 'አሥራ አራት', 'አሥራ ሦስት', + 'አሥራ ሁለት', 'አሥራ አንድ', 'አሥር', 'ዘጠኝ', 'ስምንት', + 'ሰባት', 'ስድስት', 'አምስት', 'አራት', 'ሦስት', 'ሁለት', + 'አንድ', 'ዜሮ'] + self.ords = {'አንድ': 'አንደኛ', + 'ሁለት': 'ሁለተኛ', + 'ሦስት': 'ሦስተኛ', + 'አራት': 'አራተኛ', + 'አምስት': 'አምስተኛ', + 'ስድስት': 'ስድስተኛ', + 'ሰባት': 'ሰባተኛ', + 'ስምንት': 'ስምንተኛ', + 'ዘጠኝ': 'ዘጠነኛ', + 'አሥር': 'አሥረኛ', + 'አሥራ አንድ': 'አሥራ አንድ', + 'አሥራ ሁለት': 'አሥራ ሁለተኛ', + 'አሥራ ሦስት': 'አሥራ ሦስተኛ', + 'አሥራ አራት': 'አሥራ አራተኛ', + 'አሥራ አምስት': 'አሥራ አምስተኛ', + 'አሥራ ስድስት': 'አሥራ ስድስተኛ', + 'አሥራ ሰባት': 'አሥራ ሰባተኛ', + 'አሥራ ስምንት': 'አሥራ ስምንተኛ', + 'አሥራ ዘጠኝ': 'አሥራ ዘጠነኛ'} + + def to_cardinal(self, value): + try: + assert int(value) == value + except (ValueError, TypeError, AssertionError): + return self.to_cardinal_float(value) + + out = '' + if value < 0: + value = abs(value) + out = self.negword + + if value >= self.MAXVAL: + raise OverflowError(self.errmsg_toobig % (value, self.MAXVAL)) + + if value == 100: + return self.title(out + 'መቶ') + else: + val = self.splitnum(value) + words, num = self.clean(val) + return self.title(out + words) def merge(self, lpair, rpair): ltext, lnum = lpair @@ -69,31 +97,31 @@ class Num2Word_AM(lang_EU.Num2Word_EU): if lnum == 1 and rnum < 100: return (rtext, rnum) elif 100 > lnum > rnum: - return ("%s %s" % (ltext, rtext), lnum + rnum) + return ('%s %s' % (ltext, rtext), lnum + rnum) elif lnum >= 100 > rnum: - return ("%s %s" % (ltext, rtext), lnum + rnum) + return ('%s %s' % (ltext, rtext), lnum + rnum) elif rnum > lnum: - return ("%s %s" % (ltext, rtext), lnum * rnum) - return ("%s, %s" % (ltext, rtext), lnum + rnum) + 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("-") + outwords = self.to_cardinal(value).split(' ') + lastwords = outwords[-1].split('-') lastword = lastwords[-1].lower() try: lastword = self.ords[lastword] except KeyError: - if lastword[-1] == "y": - lastword = lastword[:-1] + "ie" - lastword += "th" + if lastword[-1] == 'y': + lastword = lastword[:-1] + 'ie' + lastword += 'th' lastwords[-1] = self.title(lastword) - outwords[-1] = " ".join(lastwords) - return " ".join(outwords) + 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)[-1:]) + return '%s%s' % (value, self.to_ordinal(value)[-1:]) def to_currency(self, val, currency='ብር', cents=True, separator=' ከ', adjective=True): @@ -105,4 +133,4 @@ class Num2Word_AM(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="መቶ", longval=longval) + return self.to_splitnum(val, hightxt='መቶ', longval=longval) diff --git a/tests/test_am.py b/tests/test_am.py index a3dc95d..460ca6e 100644 --- a/tests/test_am.py +++ b/tests/test_am.py @@ -21,10 +21,15 @@ from num2words import num2words class Num2WordsAMTest(TestCase): - def test_and_join_199(self): - self.assertEqual(num2words(199, lang='am'), "አንድ መቶ ዘጠና ዘጠኝ") + def test_cardinal(self): + self.assertEqual(num2words(100, lang='am'), 'መቶ') + self.assertEqual(num2words(100000, lang='am'), 'አንድ መቶ ሺህ') + self.assertEqual(num2words(101, lang='am'), 'አንድ መቶ አንድ') - def test_ordinal(self): + def test_and_join_199(self): + self.assertEqual(num2words(199, lang='am'), 'አንድ መቶ ዘጠና ዘጠኝ') + + def test_to_ordinal(self): self.assertEqual( num2words(1, lang='am', to='ordinal'), 'አንደኛ' @@ -38,38 +43,38 @@ class Num2WordsAMTest(TestCase): 'ሃያ ሁለተኛ' ) - def test_ordinal_num(self): + def test_to_ordinal_num(self): self.assertEqual(num2words(10, lang='am', to='ordinal_num'), '10ኛ') self.assertEqual(num2words(21, lang='am', to='ordinal_num'), '21ኛ') self.assertEqual(num2words(102, lang='am', to='ordinal_num'), '102ኛ') def test_cardinal_for_float_number(self): - self.assertEqual(num2words(12.5, lang='am'), "አሥራ ሁለት ነጥብ አምስት") - self.assertEqual(num2words(12.51, lang='am'), "አሥራ ሁለት ነጥብ አምስት አንድ") - self.assertEqual(num2words(12.53, lang='am'), "አሥራ ሁለት ነጥብ አምስት ሦስት") + self.assertEqual(num2words(12.5, lang='am'), 'አሥራ ሁለት ነጥብ አምስት') + self.assertEqual(num2words(12.51, lang='am'), 'አሥራ ሁለት ነጥብ አምስት አንድ') + self.assertEqual(num2words(12.53, lang='am'), 'አሥራ ሁለት ነጥብ አምስት ሦስት') - def test_overflow(self): + def test_to_overflow(self): with self.assertRaises(OverflowError): - num2words("1000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000" - "0000000000000000000000000000000000000000000000000000000" - "00000000000000000000000000000000", lang='am') + num2words('1000000000000000000000000000000000000000000000000000000' + '0000000000000000000000000000000000000000000000000000000' + '0000000000000000000000000000000000000000000000000000000' + '0000000000000000000000000000000000000000000000000000000' + '0000000000000000000000000000000000000000000000000000000' + '00000000000000000000000000000000', lang='am') def test_to_currency(self): self.assertEqual( num2words('38.4', lang='am', to='currency', cents=False, - currency='ETB'), "ሠላሳ ስምንት ብር ከ 40 ሳንቲም" + currency='ETB'), 'ሠላሳ ስምንት ብር ከ 40 ሳንቲም' ) self.assertEqual( num2words('0', lang='am', to='currency', separator=' እና', - cents=True, currency='ETB'), "ዜሮ ብር እና ዜሮ ሳንቲም" + cents=True, currency='ETB'), 'ዜሮ ብር እና ዜሮ ሳንቲም' ) self.assertEqual( num2words('1.50', lang='am', to='currency', cents=True, - currency='ETB'), "አንድ ብር ከ አምሳ ሳንቲም" + currency='ETB'), 'አንድ ብር ከ አምሳ ሳንቲም' ) def test_to_year(self): From b7277ffb93f2421899831dfe3efc53df5467b53e Mon Sep 17 00:00:00 2001 From: Eyosiyas Bereketab Date: Tue, 9 Aug 2022 21:19:56 +0300 Subject: [PATCH 27/28] Update __init__.py --- num2words/__init__.py | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/num2words/__init__.py b/num2words/__init__.py index 236f2c7..3c9171a 100644 --- a/num2words/__init__.py +++ b/num2words/__init__.py @@ -17,15 +17,16 @@ from __future__ import unicode_literals -from . import (lang_AR, lang_CZ, lang_DE, lang_DK, lang_EN, lang_EN_IN, - lang_ES, lang_ES_CO, lang_ES_NI, lang_ES_VE, lang_FA, lang_FI, - lang_FR, lang_FR_BE, lang_FR_CH, lang_FR_DZ, lang_HE, lang_HU, - lang_ID, lang_IT, lang_JA, lang_KN, lang_KO, lang_KZ, lang_LT, - lang_LV, lang_NL, lang_NO, lang_PL, lang_PT, lang_PT_BR, - lang_RO, lang_RU, lang_SL, lang_SR, lang_SV, lang_TE, lang_TG, - lang_TH, lang_TR, lang_UK, lang_VI) +from . import (lang_AM, lang_AR, lang_CZ, lang_DE, lang_DK, lang_EN, + lang_EN_IN, lang_ES, lang_ES_CO, lang_ES_NI, lang_ES_VE, + lang_FA, lang_FI, lang_FR, lang_FR_BE, lang_FR_CH, lang_FR_DZ, + lang_HE, lang_HU, lang_ID, lang_IT, lang_JA, lang_KN, lang_KO, + lang_KZ, lang_LT, lang_LV, lang_NL, lang_NO, lang_PL, lang_PT, + lang_PT_BR, lang_RO, lang_RU, lang_SL, lang_SR, lang_SV, + lang_TE, lang_TG, lang_TH, lang_TR, lang_UK, lang_VI) CONVERTER_CLASSES = { + 'am': lang_AM.Num2Word_AM(), 'ar': lang_AR.Num2Word_AR(), 'cz': lang_CZ.Num2Word_CZ(), 'en': lang_EN.Num2Word_EN(), From 4c873a6a88e90333c53a753fcf2f520265258bf6 Mon Sep 17 00:00:00 2001 From: Eyosiyas Bereketab Date: Wed, 10 Aug 2022 09:34:03 +0300 Subject: [PATCH 28/28] Add additional test case --- num2words/lang_AM.py | 19 ++++++------------- tests/test_am.py | 4 ++++ 2 files changed, 10 insertions(+), 13 deletions(-) diff --git a/num2words/lang_AM.py b/num2words/lang_AM.py index 3efd9c2..77922db 100644 --- a/num2words/lang_AM.py +++ b/num2words/lang_AM.py @@ -60,7 +60,7 @@ class Num2Word_AM(lang_EU.Num2Word_EU): 'ስምንት': 'ስምንተኛ', 'ዘጠኝ': 'ዘጠነኛ', 'አሥር': 'አሥረኛ', - 'አሥራ አንድ': 'አሥራ አንድ', + 'አሥራ አንድ': 'አሥራ አንደኛ', 'አሥራ ሁለት': 'አሥራ ሁለተኛ', 'አሥራ ሦስት': 'አሥራ ሦስተኛ', 'አሥራ አራት': 'አሥራ አራተኛ', @@ -77,10 +77,6 @@ class Num2Word_AM(lang_EU.Num2Word_EU): return self.to_cardinal_float(value) out = '' - if value < 0: - value = abs(value) - out = self.negword - if value >= self.MAXVAL: raise OverflowError(self.errmsg_toobig % (value, self.MAXVAL)) @@ -95,14 +91,13 @@ class Num2Word_AM(lang_EU.Num2Word_EU): ltext, lnum = lpair rtext, rnum = rpair if lnum == 1 and rnum < 100: - return (rtext, rnum) + return rtext, rnum elif 100 > lnum > rnum: - return ('%s %s' % (ltext, rtext), lnum + rnum) + return '%s %s' % (ltext, rtext), lnum + rnum elif lnum >= 100 > rnum: - return ('%s %s' % (ltext, rtext), lnum + rnum) + return '%s %s' % (ltext, rtext), lnum + rnum elif rnum > lnum: - return ('%s %s' % (ltext, rtext), lnum * rnum) - return ('%s, %s' % (ltext, rtext), lnum + rnum) + return '%s %s' % (ltext, rtext), lnum * rnum def to_ordinal(self, value): self.verify_ordinal(value) @@ -112,9 +107,7 @@ class Num2Word_AM(lang_EU.Num2Word_EU): try: lastword = self.ords[lastword] except KeyError: - if lastword[-1] == 'y': - lastword = lastword[:-1] + 'ie' - lastword += 'th' + lastword += 'ኛ' lastwords[-1] = self.title(lastword) outwords[-1] = ' '.join(lastwords) return ' '.join(outwords) diff --git a/tests/test_am.py b/tests/test_am.py index 460ca6e..58709c3 100644 --- a/tests/test_am.py +++ b/tests/test_am.py @@ -42,6 +42,10 @@ class Num2WordsAMTest(TestCase): num2words(22, lang='am', to='ordinal'), 'ሃያ ሁለተኛ' ) + self.assertEqual( + num2words(10000, lang='am', to='ordinal'), + 'አሥር ሺህኛ' + ) def test_to_ordinal_num(self): self.assertEqual(num2words(10, lang='am', to='ordinal_num'), '10ኛ')