diff --git a/num2words/__init__.py b/num2words/__init__.py index 24830b7..d0c8eb1 100644 --- a/num2words/__init__.py +++ b/num2words/__init__.py @@ -27,6 +27,7 @@ from . import lang_LT from . import lang_LV from . import lang_PL from . import lang_RU +from . import lang_ID CONVERTER_CLASSES = { 'en': lang_EN.Num2Word_EN(), @@ -36,6 +37,7 @@ CONVERTER_CLASSES = { 'fr_CH': lang_FR_CH.Num2Word_FR_CH(), 'de': lang_DE.Num2Word_DE(), 'es': lang_ES.Num2Word_ES(), + 'id': lang_ID.Num2Word_ID(), 'lt': lang_LT.Num2Word_LT(), 'lv': lang_LV.Num2Word_LV(), 'pl': lang_PL.Num2Word_PL(), diff --git a/num2words/base.py b/num2words/base.py index b2b7cab..bc0c5b9 100644 --- a/num2words/base.py +++ b/num2words/base.py @@ -182,7 +182,7 @@ class Num2Word_Base(object): pass - def to_ordinal(value): + def to_ordinal(self, value): return self.to_cardinal(value) diff --git a/num2words/lang_ID.py b/num2words/lang_ID.py new file mode 100644 index 0000000..ee07065 --- /dev/null +++ b/num2words/lang_ID.py @@ -0,0 +1,194 @@ +# Copyright (c) 2003, Taro Ogawa. All Rights Reserved. +# Copyright (c) 2013, Savoir-faire Linux inc. All Rights Reserved. + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA + +class Num2Word_ID(): + + BASE = {0: [], + 1: ["satu"], + 2: ["dua"], + 3: ["tiga"], + 4: ["empat"], + 5: ["lima"], + 6: ["enam"], + 7: ["tujuh"], + 8: ["delapan"], + 9: ["sembilan"]} + + TENS_TO = {3: "ribu", + 6: "juta", + 9: "miliar", + 12: "triliun", + 15: "kuadriliun", + 18: "kuantiliun", + 21: "sekstiliun", + 24: "septiliun", + 27: "oktiliun", + 30: "noniliun", + 33: "desiliun"} + + errmsg_floatord = "Cannot treat float number as ordinal" + errmsg_negord = "Cannot treat negative number as ordinal" + errmsg_toobig = "Too large" + max_num = 10**36 + + def split_by_koma(self, number): + return str(number).split('.') + + def split_by_3(self, number): + """ + starting here, it groups the number by three from the tail + '1234567' -> (('1',),('234',),('567',)) + :param number:str + :rtype:tuple + """ + blocks = () + length = len(number) + + if length < 3: + blocks += ((number,),) + else: + len_of_first_block = length % 3 + + if len_of_first_block > 0: + first_block = number[0:len_of_first_block], + blocks += first_block, + + for i in range(len_of_first_block, length, 3): + next_block = (number[i:i+3],), + blocks += next_block + + return blocks + + def spell(self, blocks): + """ + it adds the list of spelling to the blocks + (('1',),('034',)) -> (('1',['satu']),('234',['tiga', 'puluh', 'empat'])) + :param blocks: tuple + :rtype: tuple + """ + word_blocks = () + first_block = blocks[0] + if len(first_block[0]) == 1: + if first_block[0] == '0': + spelling = ['nol'] + else: + spelling = self.BASE[int(first_block[0])] + elif len(first_block[0]) == 2: + spelling = self.puluh(first_block[0]) + else: + spelling = self.ratus(first_block[0][0]) + self.puluh(first_block[0][1:3]) + + word_blocks += (first_block[0], spelling), + + for block in blocks[1:]: + spelling = self.ratus(block[0][0]) + self.puluh(block[0][1:3]) + block += spelling, + word_blocks += block, + + return word_blocks + + def ratus(self, number): + # it is used to spell + if number == '1': + return ['seratus'] + elif number == '0': + return [] + else: + return self.BASE[int(number)]+['ratus'] + + def puluh(self, number): + # it is used to spell + if number[0] == '1': + if number[1]== '0': + return ['sepuluh'] + elif number[1] == '1': + return ['sebelas'] + else: + return self.BASE[int(number[1])]+['belas'] + elif number[0] == '0': + return self.BASE[int(number[1])] + else: + return self.BASE[int(number[0])]+['puluh']+ self.BASE[int(number[1])] + + def spell_float(self, float_part): + # spell the float number + word_list = [] + for n in float_part: + if n == '0': + word_list += ['nol'] + continue + word_list += self.BASE[int(n)] + return ' '.join(['','koma']+word_list) + + def join(self, word_blocks, float_part): + """ + join the words by first join lists in the tuple + :param word_blocks: tuple + :rtype: str + """ + word_list = [] + length = len(word_blocks)-1 + first_block = word_blocks[0], + start = 0 + + if length == 1 and first_block[0][0] == '1': + word_list += ['seribu'] + start = 1 + + for i in range(start, length+1, 1): + word_list += word_blocks[i][1] + if not word_blocks[i][1]: + continue + if i == length: + break + word_list += [self.TENS_TO[(length-i)*3]] + + return ' '.join(word_list)+float_part + + def to_cardinal(self, number): + if number >= self.max_num: + raise OverflowError(self.errmsg_toobig % (number, self.maxnum)) + minus = '' + if number < 0: + minus = 'min ' + float_word = '' + n = self.split_by_koma(abs(number)) + if len(n)==2: + float_word = self.spell_float(n[1]) + return minus + self.join(self.spell(self.split_by_3(n[0])), float_word) + + def to_ordinal(self, number): + self.verify_ordinal(number) + out_word = self.to_cardinal(number) + if out_word == "satu": + return "pertama" + return "ke" + out_word + + def to_ordinal_num(self, number): + self.verify_ordinal(number) + return "ke-" + str(number) + + def to_currency(self, value): + return self.to_cardinal(value)+" rupiah" + + def to_year(self, value): + return self.to_cardinal(value) + + def verify_ordinal(self, value): + if not value == long(value): + raise TypeError, self.errmsg_floatord %(value) + if not abs(value) == value: + raise TypeError, self.errmsg_negord %(value) diff --git a/tests/test_id.py b/tests/test_id.py new file mode 100644 index 0000000..a1716a0 --- /dev/null +++ b/tests/test_id.py @@ -0,0 +1,49 @@ +# Copyright (c) 2013, Savoir-faire Linux inc. All Rights Reserved. + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA + +from unittest import TestCase + +from num2words import num2words + +class Num2WordsIDTest(TestCase): + def test_cardinal_for_natural_number(self): + self.assertEqual(num2words(10, lang='id'), "sepuluh") + self.assertEqual(num2words(11, lang='id'), "sebelas") + self.assertEqual(num2words(108, lang='id'), "seratus delapan") + self.assertEqual(num2words(1075, lang='id'), "seribu tujuh puluh lima") + self.assertEqual(num2words(1087231, lang='id'), "satu juta delapan puluh tujuh ribu dua ratus tiga puluh satu") + self.assertEqual(num2words(1000000408, lang='id'), "satu miliar empat ratus delapan") + + def test_cardinal_for_decimal_number(self): + self.assertEqual(num2words(12.234, lang='id'), "dua belas koma dua tiga empat") + self.assertEqual(num2words(9.076, lang='id'), "sembilan koma nol tujuh enam") + + def test_cardinal_for_negative_number(self): + self.assertEqual(num2words(-923, lang='id'), "min sembilan ratus dua puluh tiga") + self.assertEqual(num2words(-0.234, lang='id'), "min nol koma dua tiga empat") + + def test_ordinal_for_natural_number(self): + self.assertEqual(num2words(1, ordinal=True, lang='id'), "pertama") + self.assertEqual(num2words(10, ordinal=True, lang='id'), "kesepuluh") + + #def test_ordinal_numeric_for_natural_number(self): + # self.assertEqual(num2words(1, ordinal=True, lang='id'), "ke-1") + # self.assertEqual(num2words(10, ordinal=True, lang='id'), "ke-10") + + def test_ordinal_for_negative_number(self): + self.assertRaises(TypeError, num2words, -12, ordinal=True, lang='id') + + def test_ordinal_for_floating_number(self): + self.assertRaises(TypeError, num2words, 3.243, ordinal=True, lang='id')