From 4dab316025fe1b36e621b8de4daba2d2ced6809b Mon Sep 17 00:00:00 2001 From: Daniel Marai Date: Tue, 28 Jan 2020 17:17:16 +0100 Subject: [PATCH] Added support for Hungarian language --- README.rst | 1 + num2words/__init__.py | 12 ++- num2words/lang_EU.py | 2 + num2words/lang_HU.py | 165 ++++++++++++++++++++++++++++++++ tests/test_hu.py | 213 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 388 insertions(+), 5 deletions(-) create mode 100644 num2words/lang_HU.py create mode 100644 tests/test_hu.py diff --git a/README.rst b/README.rst index 9914f1c..db64202 100644 --- a/README.rst +++ b/README.rst @@ -92,6 +92,7 @@ Besides the numerical argument, there are two main optional arguments. * ``fr_BE`` (French - Belgium) * ``fr_DZ`` (French - Algeria) * ``he`` (Hebrew) +* ``hu`` (Hungarian) * ``id`` (Indonesian) * ``it`` (Italian) * ``ja`` (Japanese) diff --git a/num2words/__init__.py b/num2words/__init__.py index b513642..bb18ea4 100644 --- a/num2words/__init__.py +++ b/num2words/__init__.py @@ -19,10 +19,11 @@ 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_FR_BE, lang_FR_CH, lang_FR_DZ, lang_HE, 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_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(), @@ -62,7 +63,8 @@ CONVERTER_CLASSES = { 'tr': lang_TR.Num2Word_TR(), 'nl': lang_NL.Num2Word_NL(), 'uk': lang_UK.Num2Word_UK(), - 'te': lang_TE.Num2Word_TE() + 'te': lang_TE.Num2Word_TE(), + 'hu': lang_HU.Num2Word_HU() } diff --git a/num2words/lang_EU.py b/num2words/lang_EU.py index 748f935..3bc7c80 100644 --- a/num2words/lang_EU.py +++ b/num2words/lang_EU.py @@ -43,6 +43,7 @@ class Num2Word_EU(Num2Word_Base): 'MXN': (('peso', 'pesos'), GENERIC_CENTS), 'RON': (('leu', 'lei', 'de lei'), ('ban', 'bani', 'de bani')), 'INR': (('rupee', 'rupees'), ('paisa', 'paise')) + 'HUF': (('forint', 'forint'), ('fillér', 'fillér')) } CURRENCY_ADJECTIVES = { @@ -55,6 +56,7 @@ class Num2Word_EU(Num2Word_Base): 'MXN': 'Mexican', 'RON': 'Romanian', 'INR': 'Indian', + 'HUF': 'Hungarian' } GIGA_SUFFIX = "illiard" diff --git a/num2words/lang_HU.py b/num2words/lang_HU.py new file mode 100644 index 0000000..1bf54cf --- /dev/null +++ b/num2words/lang_HU.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. + +# 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 + +ZERO = 'nulla' + + +class Num2Word_HU(lang_EU.Num2Word_EU): + GIGA_SUFFIX = "illiárd" + MEGA_SUFFIX = "illió" + + def setup(self): + super(Num2Word_HU, self).setup() + + self.negword = "mínusz " + self.pointword = "egész" + + self.mid_numwords = [(1000, "ezer"), (100, "száz"), (90, "kilencven"), + (80, "nyolcvan"), (70, "hetven"), (60, "hatvan"), + (50, "ötven"), (40, "negyven"), (30, "harminc")] + + low_numwords = ["kilenc", "nyolc", "hét", "hat", "öt", "négy", "három", + "kettő", "egy"] + self.low_numwords = (['tizen' + w for w in low_numwords] + + ['tíz'] + + low_numwords) + self.low_numwords = (['huszon' + w for w in low_numwords] + + ['húsz'] + + self.low_numwords + + [ZERO]) + + self.partial_ords = { + 'nulla': 'nullad', + 'egy': 'egyed', + 'kettő': 'ketted', + 'három': 'harmad', + 'négy': 'negyed', + 'öt': 'ötöd', + 'hat': 'hatod', + 'hét': 'heted', + 'nyolc': 'nyolcad', + 'kilenc': 'kilenced', + 'tíz': 'tized', + 'húsz': 'huszad', + 'harminc': 'harmincad', + 'negyven': 'negyvened', + 'ötven': 'ötvened', + 'hatvan': 'hatvanad', + 'hetven': 'hetvened', + 'nyolcvan': 'nyolcvanad', + 'kilencven': 'kilencvened', + 'száz': 'század', + 'ezer': 'ezred', + 'illió': 'milliomod', + 'illiárd': 'milliárdod' + } + + def to_cardinal(self, value, zero=ZERO): + if int(value) != value: + return self.to_cardinal_float(value) + elif value < 0: + out = self.negword + self.to_cardinal(-value) + elif value == 0: + out = zero + elif zero == '' and value == 2: + out = 'két' + elif value < 30: + out = self.cards[value] + elif value < 100: + out = self.tens_to_cardinal(value) + elif value < 1000: + out = self.hundreds_to_cardinal(value) + elif value < 10**6: + out = self.thousands_to_cardinal(value) + else: + out = self.big_number_to_cardinal(value) + return out + + def tens_to_cardinal(self, value): + try: + return self.cards[value] + except KeyError: + return self.cards[value // 10 * 10] + self.to_cardinal(value % 10) + + def hundreds_to_cardinal(self, value): + hundreds = value // 100 + prefix = "száz" + if hundreds != 1: + prefix = self.to_cardinal(hundreds, zero="") + prefix + postfix = self.to_cardinal(value % 100, zero="") + return prefix + postfix + + def thousands_to_cardinal(self, value): + thousands = value // 1000 + prefix = "ezer" + if thousands != 1: + prefix = self.to_cardinal(thousands, zero="") + prefix + postfix = self.to_cardinal(value % 1000, zero="") + return prefix + ('' if value <= 2000 or not postfix else '-') + postfix + + def big_number_to_cardinal(self, value): + digits = len(str(value)) + digits = digits if digits % 3 != 0 else digits - 2 + exp = 10 ** (digits // 3 * 3) + rest = self.to_cardinal(value % exp, '') + return (self.to_cardinal(value // exp, '') + self.cards[exp] + + ('-' + rest if rest else '')) + + def to_ordinal(self, value): + if value < 0: + return self.negword + self.to_ordinal(-value) + if value == 1: + return 'első' + elif value == 2: + return 'második' + else: + out = self.to_cardinal(value) + for card_word, ord_word in self.partial_ords.items(): + if out[-len(card_word):] == card_word: + out = out[:-len(card_word)] + ord_word + break + return out + 'ik' + + def to_ordinal_num(self, value): + self.verify_ordinal(value) + return str(value) + '.' + + def to_year(self, val, suffix=None, longval=True): + # suffix is prefix here + prefix = '' + if val < 0 or suffix is not None: + val = abs(val) + prefix = (suffix + ' ' if suffix is not None else 'i. e. ') + return prefix + self.to_cardinal(val) + + def to_currency(self, val, currency='HUF', cents=True, separator=',', + adjective=False): + return super(Num2Word_HU, self).to_currency( + val, currency, cents, separator, adjective) + + def to_cardinal_float(self, value): + if abs(value) != value: + return self.negword + self.to_cardinal_float(-value) + left, right = str(value).split('.') + return (self.to_cardinal(int(left)) + + ' egész ' + + self.to_cardinal(int(right)) + + ' ' + self.partial_ords[self.cards[10 ** len(right)]]) diff --git a/tests/test_hu.py b/tests/test_hu.py new file mode 100644 index 0000000..d0c159d --- /dev/null +++ b/tests/test_hu.py @@ -0,0 +1,213 @@ +# -*- 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 Num2WordsHUTest(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(self): + self.assertEqual( + num2words(-1, lang='hu'), + 'mínusz egy' + ) + self.assertEqual( + num2words(0, lang='hu'), + 'nulla' + ) + self.assertEqual( + num2words(1, lang='hu'), + 'egy' + ) + self.assertEqual( + num2words(13, lang='hu'), + 'tizenhárom' + ) + self.assertEqual( + num2words(22, lang='hu'), + 'huszonkettő' + ) + self.assertEqual( + num2words(75, lang='hu'), + 'hetvenöt' + ) + self.assertEqual( + num2words(124, lang='hu'), + 'százhuszonnégy' + ) + self.assertEqual( + num2words(651, lang='hu'), + 'hatszázötvenegy' + ) + self.assertEqual( + num2words(2232, lang='hu'), + 'kétezer-kétszázharminckettő' + ) + self.assertEqual( + num2words(16501, lang='hu'), + 'tizenhatezer-ötszázegy' + ) + self.assertEqual( + num2words(1900000000000, lang='hu'), + 'egybillió-kilencszázmilliárd' + ) + self.assertEqual( + num2words(24656451324564987566, lang='hu'), + 'huszonnégytrillió-hatszázötvenhatbilliárd-négyszázötvenegybillió' + '-háromszázhuszonnégymilliárd-ötszázhatvannégymillió-' + 'kilencszáznyolcvanhétezer-ötszázhatvanhat' + ) + + def test_ordinal(self): + self.assertEqual( + num2words(0, lang='hu', to='ordinal'), + 'nulladik' + ) + self.assertEqual( + num2words(1, lang='hu', to='ordinal'), + 'első' + ) + self.assertEqual( + num2words(2, lang='hu', to='ordinal'), + 'második' + ) + self.assertEqual( + num2words(-3, lang='hu', to='ordinal'), + 'mínusz harmadik' + ) + self.assertEqual( + num2words(13, lang='hu', to='ordinal'), + 'tizenharmadik' + ) + self.assertEqual( + num2words(22, lang='hu', to='ordinal'), + 'huszonkettedik' + ) + self.assertEqual( + num2words(75, lang='hu', to='ordinal'), + 'hetvenötödik' + ) + self.assertEqual( + num2words(124, lang='hu', to='ordinal'), + 'százhuszonnegyedik' + ) + self.assertEqual( + num2words(1532, lang='hu', to='ordinal'), + 'ezerötszázharminckettedik' + ) + self.assertEqual( + num2words(16501, lang='hu', to='ordinal'), + 'tizenhatezer-ötszázegyedik' + ) + self.assertEqual( + num2words(458755640120000, lang='hu', to='ordinal'), + 'négyszázötvennyolcbillió-hétszázötvenötmilliárd-' + 'hatszáznegyvenmillió-százhúszezredik' + ) + + def test_ordinal_num(self): + self.assertEqual(num2words(10, lang='hu', to='ordinal_num'), '10.') + self.assertEqual(num2words(21, lang='hu', to='ordinal_num'), '21.') + self.assertEqual(num2words(102, lang='hu', to='ordinal_num'), '102.') + self.assertEqual(num2words(73, lang='hu', to='ordinal_num'), '73.') + + def test_cardinal_for_float_number(self): + # issue 24 + self.assertEqual(num2words(12, lang='hu'), + "tizenkettő") + self.assertEqual(num2words(12.0, lang='hu'), + "tizenkettő") + self.assertEqual(num2words(12.5, lang='hu'), + "tizenkettő egész öt tized") + self.assertEqual(num2words(-12.5, lang='hu'), + "mínusz tizenkettő egész öt tized") + self.assertEqual(num2words(12.51, lang='hu'), + "tizenkettő egész ötvenegy század") + self.assertEqual(num2words(12.53, lang='hu'), + "tizenkettő egész ötvenhárom század") + self.assertEqual(num2words(12.590, lang='hu'), + "tizenkettő egész ötvenkilenc század") + self.assertEqual(num2words(12.005, lang='hu'), + "tizenkettő egész öt ezred") + + def test_overflow(self): + with self.assertRaises(OverflowError): + num2words("1000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000") + + def test_to_currency(self): + self.assertEqual( + num2words('38.4', lang='hu', to='currency', separator=' és', + cents=False, currency='HUF'), + "harmincnyolc forint és 40 fillér" + ) + self.assertEqual( + num2words('0', lang='hu', to='currency', separator=' és', + cents=False, currency='HUF'), + "nulla forint és 00 fillér" + ) + + self.assertEqual( + num2words('1.01', lang='hu', to='currency', separator=' és', + cents=True, currency='HUF'), + "egy forint és egy fillér" + ) + + self.assertEqual( + num2words('4778.00', lang='hu', to='currency', separator=' és', + cents=True, currency='HUF', adjective=True), + 'négyezer-hétszázhetvennyolc Hungarian forint' + ' és nulla fillér') + + self.assertEqual( + num2words('4778.00', lang='hu', to='currency', separator=' és', + cents=True, currency='HUF'), + 'négyezer-hétszázhetvennyolc forint és nulla fillér') + + def test_to_year(self): + # issue 141 + # "e2 e2" + self.assertEqual(num2words(1990, lang='hu', to='year'), + 'ezerkilencszázkilencven') + self.assertEqual(num2words(5555, lang='hu', to='year'), + 'ötezer-ötszázötvenöt') + self.assertEqual(num2words(2020, lang='hu', to='year'), + 'kétezer-húsz') + self.assertEqual(num2words(905, lang='hu', to='year'), + 'kilencszázöt') + self.assertEqual(num2words(0, lang='hu', to='year'), + 'nulla') + # suffixes + self.assertEqual(num2words(-44, lang='hu', to='year'), + 'i. e. negyvennégy') + self.assertEqual(num2words(-44, lang='hu', to='year', suffix='Kr. e.'), + 'Kr. e. negyvennégy') + self.assertEqual(num2words(1, lang='hu', to='year', suffix='Kr. u.'), + 'Kr. u. egy') + self.assertEqual(num2words(-66000000, lang='hu', to='year'), + 'i. e. hatvanhatmillió')