From c2148d15efda2196a97470de62c8f92567b660ee Mon Sep 17 00:00:00 2001 From: Diep Huu Hoang Date: Sun, 19 Mar 2017 22:41:41 +0700 Subject: [PATCH 1/2] [ADD] support VietNam --- num2words/__init__.py | 4 +- num2words/lang_VN.py | 92 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 num2words/lang_VN.py diff --git a/num2words/__init__.py b/num2words/__init__.py index 57c4d57..dfb91d0 100644 --- a/num2words/__init__.py +++ b/num2words/__init__.py @@ -33,6 +33,7 @@ from . import lang_DK from . import lang_PT_BR from . import lang_HE from . import lang_IT +from . import lang_VN CONVERTER_CLASSES = { 'en': lang_EN.Num2Word_EN(), @@ -51,7 +52,8 @@ CONVERTER_CLASSES = { 'dk': lang_DK.Num2Word_DK(), 'pt_BR': lang_PT_BR.Num2Word_PT_BR(), 'he': lang_HE.Num2Word_HE(), - 'it': lang_IT.Num2Word_IT() + 'it': lang_IT.Num2Word_IT(), + 'vi_VN': lang_VN.Num2Word_VN() } def num2words(number, ordinal=False, lang='en'): diff --git a/num2words/lang_VN.py b/num2words/lang_VN.py new file mode 100644 index 0000000..c8bdb4f --- /dev/null +++ b/num2words/lang_VN.py @@ -0,0 +1,92 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2003, Taro Ogawa. All Rights Reserved. +# Copyright (c) 2013, Savoir-faire Linux inc. All Rights Reserved. + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA + +from __future__ import unicode_literals + + +to_19 = (u'không', u'một', u'hai', u'ba', u'bốn', u'năm', u'sáu', + u'bảy', u'tám', u'chín', u'mười', u'mười một', u'mười hai', + u'mười ba', u'mười bốn', u'mười lăm', u'mười sáu', u'mười bảy', + u'mười tám', u'mười chín') +tens = (u'hai mươi', u'ba mươi', u'bốn mươi', u'năm mươi', + u'sáu mươi', u'bảy mươi', u'tám mươi', u'chín mươi') +denom = ('', + u'nghìn', u'triệu', u'tỷ', u'nghìn tỷ', u'trăm nghìn tỷ', + 'Quintillion', 'Sextillion', 'Septillion', 'Octillion', 'Nonillion', + 'Decillion', 'Undecillion', 'Duodecillion', 'Tredecillion', + 'Quattuordecillion', 'Sexdecillion', 'Septendecillion', + 'Octodecillion', 'Novemdecillion', 'Vigintillion') + + +class Num2Word_VN(object): + + # convert a value < 100 to English. + def _convert_nn(self, val): + if val < 20: + return to_19[val] + for (dcap, dval) in ((k, 20 + (10 * v)) for (v, k) in enumerate(tens)): + if dval + 10 > val: + if val % 10: + a = u'lăm' + if to_19[val % 10] == u'một': + a = u'mốt' + else: + a = to_19[val % 10] + return dcap + ' ' + a + return dcap + + def _convert_nnn(self, val): + word = '' + (mod, rem) = (val % 100, val // 100) + if rem > 0: + word = to_19[rem] + u' trăm' + if mod > 0: + word = word + ' ' + if mod > 0: + word = word + self._convert_nn(mod) + return word + + def vietnam_number(self, val): + if val < 100: + return self._convert_nn(val) + if val < 1000: + return self._convert_nnn(val) + for (didx, dval) in ((v - 1, 1000 ** v) for v in range(len(denom))): + if dval > val: + mod = 1000 ** didx + l = val // mod + r = val - (l * mod) + ret = self._convert_nnn(l) + ' ' + denom[didx] + if r > 0: + ret = ret + ' ' + self.vietnam_number(r) + return ret + + def number_to_text(self, number): + number = '%.2f' % number + the_list = str(number).split('.') + start_word = self.vietnam_number(int(the_list[0])) + final_result = start_word + if len(the_list) > 1 and int(the_list[1]) > 0: + end_word = self.vietnam_number(int(the_list[1])) + final_result = final_result + ' phẩy ' + end_word + return final_result + + def to_cardinal(self, number): + return self.number_to_text(number) + + def to_ordinal(self, number): + return self.to_cardinal(number) From c200d97bd72a74becf46c36eaa667d6c251beeb0 Mon Sep 17 00:00:00 2001 From: Diep Huu Hoang Date: Sat, 1 Apr 2017 12:30:54 +0700 Subject: [PATCH 2/2] [IMP] Improve convert number method and add unittest for Vietnam lang --- num2words/lang_VN.py | 16 +++++-- tests/test_vn.py | 106 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 119 insertions(+), 3 deletions(-) create mode 100644 tests/test_vn.py diff --git a/num2words/lang_VN.py b/num2words/lang_VN.py index c8bdb4f..51b6d88 100644 --- a/num2words/lang_VN.py +++ b/num2words/lang_VN.py @@ -34,7 +34,6 @@ denom = ('', class Num2Word_VN(object): - # convert a value < 100 to English. def _convert_nn(self, val): if val < 20: return to_19[val] @@ -46,6 +45,8 @@ class Num2Word_VN(object): a = u'mốt' else: a = to_19[val % 10] + if to_19[val % 10] == u'năm': + a = u'lăm' return dcap + ' ' + a return dcap @@ -56,7 +57,13 @@ class Num2Word_VN(object): word = to_19[rem] + u' trăm' if mod > 0: word = word + ' ' - if mod > 0: + if mod > 0 and mod < 10: + if mod == 5: + word = word != '' and word + u'lẻ năm' or word + u'năm' + else: + word = word != '' and word + u'lẻ ' \ + + self._convert_nn(mod) or word + self._convert_nn(mod) + if mod >= 10: word = word + self._convert_nn(mod) return word @@ -70,7 +77,10 @@ class Num2Word_VN(object): mod = 1000 ** didx l = val // mod r = val - (l * mod) - ret = self._convert_nnn(l) + ' ' + denom[didx] + + ret = self._convert_nnn(l) + u' ' + denom[didx] + if r > 0 and r <= 99: + ret = self._convert_nnn(l) + u' ' + denom[didx] + u' lẻ' if r > 0: ret = ret + ' ' + self.vietnam_number(r) return ret diff --git a/tests/test_vn.py b/tests/test_vn.py new file mode 100644 index 0000000..bf357b3 --- /dev/null +++ b/tests/test_vn.py @@ -0,0 +1,106 @@ +# -*- encoding: utf-8 -*- +# Copyright (c) 2015, Savoir-faire Linux inc. All Rights Reserved. + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA + +from __future__ import unicode_literals +from unittest import TestCase +from num2words import num2words + +class Num2WordsVNTest(TestCase): + + + def test_0(self): + self.assertEqual(num2words(0, lang="vi_VN"), "không") + + def test_1_to_10(self): + self.assertEqual(num2words(1, lang="vi_VN"), "một") + self.assertEqual(num2words(2, lang="vi_VN"), "hai") + self.assertEqual(num2words(7, lang="vi_VN"), "bảy") + self.assertEqual(num2words(10, lang="vi_VN"), "mười") + + def test_11_to_19(self): + self.assertEqual(num2words(11, lang="vi_VN"), "mười một") + self.assertEqual(num2words(13, lang="vi_VN"), "mười ba") + self.assertEqual(num2words(14, lang="vi_VN"), "mười bốn") + self.assertEqual(num2words(15, lang="vi_VN"), "mười lăm") + self.assertEqual(num2words(16, lang="vi_VN"), "mười sáu") + self.assertEqual(num2words(19, lang="vi_VN"), "mười chín") + + def test_20_to_99(self): + self.assertEqual(num2words(20, lang="vi_VN"), "hai mươi") + self.assertEqual(num2words(23, lang="vi_VN"), "hai mươi ba") + self.assertEqual(num2words(28, lang="vi_VN"), "hai mươi tám") + self.assertEqual(num2words(31, lang="vi_VN"), "ba mươi mốt") + self.assertEqual(num2words(40, lang="vi_VN"), "bốn mươi") + self.assertEqual(num2words(66, lang="vi_VN"), "sáu mươi sáu") + self.assertEqual(num2words(92, lang="vi_VN"), "chín mươi hai") + + def test_100_to_999(self): + self.assertEqual(num2words(100, lang="vi_VN"), "một trăm") + self.assertEqual(num2words(150, lang="vi_VN"), "một trăm năm mươi") + self.assertEqual(num2words(196, lang="vi_VN"), "một trăm chín mươi sáu") + self.assertEqual(num2words(200, lang="vi_VN"), "hai trăm") + self.assertEqual(num2words(210, lang="vi_VN"), "hai trăm mười") + + def test_1000_to_9999(self): + self.assertEqual(num2words(1000, lang="vi_VN"), "một nghìn") + self.assertEqual(num2words(1500, lang="vi_VN"), "một nghìn năm trăm") + self.assertEqual(num2words(7378, lang="vi_VN"), "bảy nghìn ba trăm bảy mươi tám") + self.assertEqual(num2words(2000, lang="vi_VN"), "hai nghìn") + self.assertEqual(num2words(2100, lang="vi_VN"), "hai nghìn một trăm") + self.assertEqual(num2words(6870, lang="vi_VN"), "sáu nghìn tám trăm bảy mươi") + self.assertEqual(num2words(10000, lang="vi_VN"), "mười nghìn") + self.assertEqual(num2words(100000, lang="vi_VN"), "một trăm nghìn") + self.assertEqual(num2words(523456, lang="vi_VN"), "năm trăm hai mươi ba nghìn bốn trăm năm mươi sáu") + + def test_big(self): + self.assertEqual(num2words(1000000, lang="vi_VN"), "một triệu") + self.assertEqual(num2words(1200000, lang="vi_VN"), "một triệu hai trăm nghìn") + self.assertEqual(num2words(3000000, lang="vi_VN"), "ba triệu") + self.assertEqual(num2words(3800000, lang="vi_VN"), "ba triệu tám trăm nghìn") + self.assertEqual(num2words(1000000000, lang="vi_VN"), "một tỷ") + self.assertEqual(num2words(2000000000, lang="vi_VN"), "hai tỷ") + self.assertEqual(num2words(2000001000, lang="vi_VN"), "hai tỷ một nghìn") + self.assertEqual(num2words(1234567890, lang="vi_VN"), "một tỷ hai trăm ba mươi bốn triệu năm trăm sáu mươi bảy nghìn tám trăm chín mươi") + + + def test_decimal_number(self): + self.assertEqual(num2words(1000.11, lang="vi_VN"), "một nghìn phẩy mười một") + self.assertEqual(num2words(1000.21, lang="vi_VN"), "một nghìn phẩy hai mươi mốt") + + def test_special_number(self): + """ + Some number will have some specail rule + """ + self.assertEqual(num2words(21, lang="vi_VN"), "hai mươi mốt") + self.assertEqual(num2words(25, lang="vi_VN"), "hai mươi lăm") + # >100 + self.assertEqual(num2words(101, lang="vi_VN"), "một trăm lẻ một") + self.assertEqual(num2words(105, lang="vi_VN"), "một trăm lẻ năm") + self.assertEqual(num2words(701, lang="vi_VN"), "bảy trăm lẻ một") + self.assertEqual(num2words(705, lang="vi_VN"), "bảy trăm lẻ năm") + + # >1000 + self.assertEqual(num2words(1001, lang="vi_VN"), "một nghìn lẻ một") + self.assertEqual(num2words(1005, lang="vi_VN"), "một nghìn lẻ năm") + self.assertEqual(num2words(98765, lang="vi_VN"), "chín mươi tám nghìn bảy trăm sáu mươi lăm") + + # > 1000000 + self.assertEqual(num2words(3000005, lang="vi_VN"), "ba triệu lẻ năm") + self.assertEqual(num2words(1000007, lang="vi_VN"), "một triệu lẻ bảy") + + # > 1000000000 + self.assertEqual(num2words(1000000017, lang="vi_VN"), "một tỷ lẻ mười bảy") + self.assertEqual(num2words(1000101017, lang="vi_VN"), "một tỷ một trăm lẻ một nghìn lẻ mười bảy")