diff --git a/num2words/__init__.py b/num2words/__init__.py index 0942928f..53a28591 100644 --- a/num2words/__init__.py +++ b/num2words/__init__.py @@ -23,8 +23,8 @@ lang_FR_BE, lang_FR_CH, lang_FR_DZ, lang_HE, lang_HU, lang_ID, lang_IS, 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) + lang_RO, lang_RU, lang_SK, 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(), @@ -57,6 +57,7 @@ 'pl': lang_PL.Num2Word_PL(), 'ro': lang_RO.Num2Word_RO(), 'ru': lang_RU.Num2Word_RU(), + 'sk': lang_SK.Num2Word_SK(), 'sl': lang_SL.Num2Word_SL(), 'sr': lang_SR.Num2Word_SR(), 'sv': lang_SV.Num2Word_SV(), diff --git a/num2words/lang_RU.py b/num2words/lang_RU.py index b521cfed..aa052aed 100644 --- a/num2words/lang_RU.py +++ b/num2words/lang_RU.py @@ -20,80 +20,227 @@ from .base import Num2Word_Base from .utils import get_digits, splitbyx -ZERO = ('ноль',) - -ONES_FEMININE = { - 1: ('одна',), - 2: ('две',), - 3: ('три',), - 4: ('четыре',), - 5: ('пять',), - 6: ('шесть',), - 7: ('семь',), - 8: ('восемь',), - 9: ('девять',), +GENDER_PLURAL_INDEXES = { + 'm': 0, 'masculine': 0, 'м': 0, 'мужской': 0, + 'f': 1, 'feminine': 1, 'ж': 0, 'женский': 0, + 'n': 2, 'neuter': 2, 'с': 0, 'средний': 0, + 'p': 3, 'plural': 3 } +CASE_INDEXES = { + 'n': 0, 'nominative': 0, 'и': 0, 'именительный': 0, + 'g': 1, 'genitive': 1, 'р': 1, 'родительный': 1, + 'd': 2, 'dative': 2, 'д': 2, 'дательный': 2, + 'a': 3, 'accusative': 3, 'в': 3, 'винительный': 3, + 'i': 4, 'instrumental': 4, 'т': 4, 'творительный': 4, + 'p': 5, 'prepositional': 5, 'п': 5, 'предложный': 5 +} +# Default values +D_CASE = 'n' +D_PLURAL = False +D_GENDER = 'm' +D_ANIMATE = True + + +def get_num_element(cases_dict, num, **kwargs): + return case_classifier_element(cases_dict[num], **kwargs) + + +def case_classifier_element(classifier, case=D_CASE, plural=D_PLURAL, + gender=D_GENDER, animate=D_ANIMATE): + case = classifier[CASE_INDEXES[case]] + if isinstance(case, str): + return case + + if plural: + gender = case[GENDER_PLURAL_INDEXES['plural']] + else: + gender = case[GENDER_PLURAL_INDEXES[gender]] + if isinstance(gender, str): + return gender + + if animate: + return gender[0] + return gender[1] + +# format: +# {n : [case_1 .. case_5]} +# case: text or [gender_1 .. gender_3 plural_4] +# gender: text or [animate, inanimate] ONES = { - 1: ('один',), - 2: ('два',), - 3: ('три',), - 4: ('четыре',), - 5: ('пять',), - 6: ('шесть',), - 7: ('семь',), - 8: ('восемь',), - 9: ('девять',), + 0: ['ноль', 'ноля', 'нолю', 'ноль', 'нолём', 'ноле'], + 1: [['один', 'одна', 'одно', 'одни'], + ['одного', 'одной', 'одного', 'одних'], + ['одному', 'одной', 'одному', 'одним'], + [['одного', 'один'], 'одну', 'одно', ['одних', 'одни']], + ['одним', 'одной', 'одним', 'одними'], + ['одном', 'одной', 'одном', 'одних']], + 2: [['два', 'две', 'два', 'двое'], + ['двух'] * 3 + ['двоих'], + ['двум'] * 3 + ['двоим'], + [['двух', 'два'], ['двух', 'две'], 'два', 'двоих'], + ['двумя'] * 3 + ['двоими'], + ['двух'] * 3 + ['двоих']], + 3: [['три'] * 3 + ['трое'], + ['трёх'] * 3 + ['троих'], + ['трём'] * 3 + ['троим'], + [['трёх', 'три'], ['трёх', 'три'], 'три', 'троих'], + ['тремя'] * 3 + ['троими'], + ['трёх'] * 3 + ['троих']], + 4: [['четыре'] * 3 + ['четверо'], + ['четырёх'] * 3 + ['четверых'], + ['четырём'] * 3 + ['четверым'], + [['четырёх', 'четыре'], ['четырёх', 'четыре'], 'четыре', 'четверых'], + ['четырьмя'] * 3 + ['четверыми'], + ['четырёх'] * 3 + ['четверых']], + 5: ['пять', 'пяти', 'пяти', 'пять', 'пятью', 'пяти'], + 6: ['шесть', 'шести', 'шести', 'шесть', 'шестью', 'шести'], + 7: ['семь', 'семи', 'семи', 'семь', 'семью', 'семи'], + 8: ['восемь', 'восьми', 'восьми', 'восемь', 'восемью', 'восьми'], + 9: ['девять', 'девяти', 'девяти', 'девять', 'девятью', 'девяти'] } -TENS = { - 0: ('десять',), - 1: ('одиннадцать',), - 2: ('двенадцать',), - 3: ('тринадцать',), - 4: ('четырнадцать',), - 5: ('пятнадцать',), - 6: ('шестнадцать',), - 7: ('семнадцать',), - 8: ('восемнадцать',), - 9: ('девятнадцать',), +ONES_ORD_PREFIXES = {0: 'нулев', 1: 'перв', 2: 'втор', 4: 'четвёрт', 5: 'пят', + 6: 'шест', 7: 'седьм', 8: 'восьм', 9: 'девят'} +ONES_ORD_POSTFIXES_GROUPS = {0: 0, 1: 1, 2: 0, 4: 1, 5: 1, 6: 0, 7: 0, 8: 0, + 9: 1} +CASE_POSTFIXES = [[{0: 'ой', 1: 'ый'}, 'ая', 'ое', 'ые'], + ['ого', 'ой', 'ого', 'ых'], + ['ому', 'ой', 'ому', 'ым'], + [['ого', {0: 'ой', 1: 'ый'}], 'ую', 'ое', ['ых', 'ые']], + ['ым', 'ой', 'ым', 'ыми'], + ['ом', 'ой', 'ом', 'ых']] + + +def get_cases(prefix, post_group): + return [[ + prefix + postfix if isinstance(postfix, str) else + [prefix + animate if isinstance(animate, str) else + prefix + animate[post_group] + for animate in postfix] if isinstance(postfix, list) else + prefix + postfix[post_group] + for postfix in case] + for case in CASE_POSTFIXES] + + +def get_ord_classifier(prefixes, post_groups): + if isinstance(post_groups, int): + post_groups = {n: post_groups for n, i in prefixes.items()} + return { + num: get_cases(prefix, post_groups[num]) + for num, prefix in prefixes.items() + } + + +ONES_ORD = { + 3: [['третий', 'третья', 'третье', 'третьи'], + ['третьего', 'третьей', 'третьего', 'третьих'], + ['третьему', 'третьей', 'третьему', 'третьим'], + [['третьего', 'третий'], 'третью', 'третье', ['третьих', 'третьи']], + ['третьим', 'третьей', 'третьим', 'третьими'], + ['третьем', 'третьей', 'третьем', 'третьих']], } +ONES_ORD.update( + get_ord_classifier(ONES_ORD_PREFIXES, ONES_ORD_POSTFIXES_GROUPS) +) + +TENS_PREFIXES = {1: 'один', 2: 'две', 3: 'три', 4: 'четыр', 5: 'пят', + 6: 'шест', 7: 'сем', 8: 'восем', 9: 'девят'} +TENS_POSTFIXES = ['надцать', 'надцати', 'надцати', 'надцать', 'надцатью', + 'надцати'] +TENS = {0: ['десять', 'десяти', 'десяти', 'десять', 'десятью', 'десяти']} +TENS.update({ + num: [prefix + postfix for postfix in TENS_POSTFIXES] + for num, prefix in TENS_PREFIXES.items() +}) + +TENS_ORD_PREFIXES = {0: "десят"} +TENS_ORD_PREFIXES.update({ + num: prefix + 'надцат' for num, prefix in TENS_PREFIXES.items() +}) +TENS_ORD = get_ord_classifier(TENS_ORD_PREFIXES, 1) TWENTIES = { - 2: ('двадцать',), - 3: ('тридцать',), - 4: ('сорок',), - 5: ('пятьдесят',), - 6: ('шестьдесят',), - 7: ('семьдесят',), - 8: ('восемьдесят',), - 9: ('девяносто',), + 2: ['двадцать', 'двадцати', 'двадцати', 'двадцать', 'двадцатью', + 'двадцати'], + 3: ['тридцать', 'тридцати', 'тридцати', 'тридцать', 'тридцатью', + 'тридцати'], + 4: ['сорок', 'сорока', 'сорока', 'сорок', 'сорока', 'сорока'], + 5: ['пятьдесят', 'пятидесяти', 'пятидесяти', 'пятьдесят', 'пятьюдесятью', + 'пятидесяти'], + 6: ['шестьдесят', 'шестидесяти', 'шестидесяти', 'шестьдесят', + 'шестьюдесятью', 'шестидесяти'], + 7: ['семьдесят', 'семидесяти', 'семидесяти', 'семьдесят', 'семьюдесятью', + 'семидесяти'], + 8: ['восемьдесят', 'восьмидесяти', 'восьмидесяти', 'восемьдесят', + 'восемьюдесятью', 'восьмидесяти'], + 9: ['девяносто', 'девяноста', 'девяноста', 'девяносто', 'девяноста', + 'девяноста'], } +TWENTIES_ORD_PREFIXES = {2: 'двадцат', 3: 'тридцат', 4: 'сороков', + 5: 'пятидесят', 6: 'шестидесят', 7: 'семидесят', + 8: 'восьмидесят', 9: 'девяност'} +TWENTIES_ORD_POSTFIXES_GROUPS = {2: 1, 3: 1, 4: 0, 5: 1, 6: 1, 7: 1, 8: 1, + 9: 1} +TWENTIES_ORD = get_ord_classifier(TWENTIES_ORD_PREFIXES, + TWENTIES_ORD_POSTFIXES_GROUPS) + HUNDREDS = { - 1: ('сто',), - 2: ('двести',), - 3: ('триста',), - 4: ('четыреста',), - 5: ('пятьсот',), - 6: ('шестьсот',), - 7: ('семьсот',), - 8: ('восемьсот',), - 9: ('девятьсот',), + 1: ['сто', 'ста', 'ста', 'сто', 'ста', 'ста'], + 2: ['двести', 'двухсот', 'двумстам', 'двести', 'двумястами', 'двухстах'], + 3: ['триста', 'трёхсот', 'трёмстам', 'триста', 'тремястами', 'трёхстах'], + 4: ['четыреста', 'четырёхсот', 'четырёмстам', 'четыреста', 'четырьмястами', + 'четырёхстах'], + 5: ['пятьсот', 'пятисот', 'пятистам', 'пятьсот', 'пятьюстами', 'пятистах'], + 6: ['шестьсот', 'шестисот', 'шестистам', 'шестьсот', 'шестьюстами', + 'шестистах'], + 7: ['семьсот', 'семисот', 'семистам', 'семьсот', 'семьюстами', 'семистах'], + 8: ['восемьсот', 'восьмисот', 'восьмистам', 'восемьсот', 'восемьюстами', + 'восьмистах'], + 9: ['девятьсот', 'девятисот', 'девятистам', 'девятьсот', 'девятьюстами', + 'девятистах'], } +HUNDREDS_ORD_PREFIXES = { + num: case[1] if num != 1 else 'сот' for num, case in HUNDREDS.items() +} +HUNDREDS_ORD = get_ord_classifier(HUNDREDS_ORD_PREFIXES, 1) + + +THOUSANDS_PREFIXES = {2: 'миллион', 3: 'миллиард', 4: 'триллион', + 5: 'квадриллион', 6: 'квинтиллион', 7: 'секстиллион', + 8: 'септиллион', 9: 'октиллион', 10: 'нониллион'} +THOUSANDS_POSTFIXES = [('', 'а', 'ов'), + ('а', 'ов', 'ов'), + ('у', 'ам', 'ам'), + ('', 'а', 'ов'), + ('ом', 'ами', 'ами'), + ('е', 'ах', 'ах')] THOUSANDS = { - 1: ('тысяча', 'тысячи', 'тысяч'), # 10^3 - 2: ('миллион', 'миллиона', 'миллионов'), # 10^6 - 3: ('миллиард', 'миллиарда', 'миллиардов'), # 10^9 - 4: ('триллион', 'триллиона', 'триллионов'), # 10^12 - 5: ('квадриллион', 'квадриллиона', 'квадриллионов'), # 10^15 - 6: ('квинтиллион', 'квинтиллиона', 'квинтиллионов'), # 10^18 - 7: ('секстиллион', 'секстиллиона', 'секстиллионов'), # 10^21 - 8: ('септиллион', 'септиллиона', 'септиллионов'), # 10^24 - 9: ('октиллион', 'октиллиона', 'октиллионов'), # 10^27 - 10: ('нониллион', 'нониллиона', 'нониллионов'), # 10^30 + 1: [['тысяча', 'тысячи', 'тысяч'], + ['тысячи', 'тысяч', 'тысяч'], + ['тысяче', 'тысячам', 'тысячам'], + ['тысячу', 'тысячи', 'тысяч'], + ['тысячей', 'тысячами', 'тысячами'], + ['тысяче', 'тысячах', 'тысячах']] } +THOUSANDS.update({ + num: [ + [prefix + postfix for postfix in case] for case in THOUSANDS_POSTFIXES + ] for num, prefix in THOUSANDS_PREFIXES.items() +}) + + +def get_thousands_elements(num, case): + return THOUSANDS[num][CASE_INDEXES[case]] + + +THOUSANDS_ORD_PREFIXES = {1: 'тысячн'} +THOUSANDS_ORD_PREFIXES.update({ + num: prefix + 'н' for num, prefix in THOUSANDS_PREFIXES.items() +}) +THOUSANDS_ORD = get_ord_classifier(THOUSANDS_ORD_PREFIXES, 1) class Num2Word_RU(Num2Word_Base): @@ -124,110 +271,104 @@ class Num2Word_RU(Num2Word_Base): def setup(self): self.negword = "минус" - self.pointword = "запятая" - self.ords = {"ноль": "нулевой", - "один": "первый", - "два": "второй", - "три": "третий", - "четыре": "четвертый", - "пять": "пятый", - "шесть": "шестой", - "семь": "седьмой", - "восемь": "восьмой", - "девять": "девятый", - "сто": "сотый"} - self.ords_feminine = {"один": "", - "одна": "", - "две": "двух", - "три": "трёх", - "четыре": "четырёх", - "пять": "пяти", - "шесть": "шести", - "семь": "семи", - "восемь": "восьми", - "девять": "девяти"} - - def to_cardinal(self, number): + self.pointword = ('целая', 'целых', 'целых') + self.pointword_ord = get_cases("цел", 1) + + def to_cardinal(self, number, case=D_CASE, plural=D_PLURAL, + gender=D_GENDER, animate=D_ANIMATE): n = str(number).replace(',', '.') if '.' in n: left, right = n.split('.') - leading_zero_count = len(right) - len(right.lstrip('0')) - decimal_part = ((ZERO[0] + ' ') * leading_zero_count + - self._int2word(int(right))) - return u'%s %s %s' % ( - self._int2word(int(left)), - self.pointword, - decimal_part + decimal_part = self._int2word(int(right), cardinal=True, + gender='f') + return u'%s %s %s %s' % ( + self._int2word(int(left), cardinal=True, gender='f'), + self.pluralize(int(left), self.pointword), + decimal_part, + self.__decimal_bitness(right) ) else: - return self._int2word(int(n)) + return self._int2word(int(n), cardinal=True, case=case, + plural=plural, gender=gender, + animate=animate) + + def __decimal_bitness(self, n): + if n[-1] == "1" and n[-2:] != "11": + return self._int2word(10 ** len(n), cardinal=False, gender='f') + return self._int2word(10 ** len(n), cardinal=False, case='g', + plural=True) def pluralize(self, n, forms): - if n % 100 < 10 or n % 100 > 20: - if n % 10 == 1: - form = 0 - elif 5 > n % 10 > 1: - form = 1 - else: - form = 2 - else: - form = 2 - return forms[form] + if n % 100 in (11, 12, 13, 14): + return forms[2] + if n % 10 == 1: + return forms[0] + if n % 10 in (2, 3, 4): + return forms[1] + return forms[2] - def to_ordinal(self, number): + def to_ordinal(self, number, case=D_CASE, plural=D_PLURAL, gender=D_GENDER, + animate=D_ANIMATE): self.verify_ordinal(number) - outwords = self.to_cardinal(number).split(" ") - lastword = outwords[-1].lower() - try: - if len(outwords) > 1: - if outwords[-2] in self.ords_feminine: - outwords[-2] = self.ords_feminine.get( - outwords[-2], outwords[-2]) - elif outwords[-2] == 'десять': - outwords[-2] = outwords[-2][:-1] + 'и' - if len(outwords) == 3: - if outwords[-3] in ['один', 'одна']: - outwords[-3] = '' - lastword = self.ords[lastword] - except KeyError: - if lastword[:-3] in self.ords_feminine: - lastword = self.ords_feminine.get( - lastword[:-3], lastword) + "сотый" - elif lastword[-1] == "ь" or lastword[-2] == "т": - lastword = lastword[:-1] + "ый" - elif lastword[-1] == "к": - lastword = lastword + "овой" - elif lastword[-5:] == "десят": - lastword = lastword.replace('ь', 'и') + 'ый' - elif lastword[-2] == "ч" or lastword[-1] == "ч": - if lastword[-2] == "ч": - lastword = lastword[:-1] + "ный" - if lastword[-1] == "ч": - lastword = lastword + "ный" - elif lastword[-1] == "н" or lastword[-2] == "н": - lastword = lastword[:lastword.rfind('н') + 1] + "ный" - elif lastword[-1] == "д" or lastword[-2] == "д": - lastword = lastword[:lastword.rfind('д') + 1] + "ный" - outwords[-1] = self.title(lastword) - return " ".join(outwords).strip() + n = str(number).replace(',', '.') + return self._int2word(int(n), cardinal=False, case=case, plural=plural, + gender=gender, animate=animate) def _money_verbose(self, number, currency): - return self._int2word(number, currency == 'UAH') + if currency == 'UAH': + return self._int2word(number, gender='f') + return self._int2word(number, gender='m') def _cents_verbose(self, number, currency): - return self._int2word(number, currency in ('UAH', 'RUB', 'BYN')) + if currency in ('UAH', 'RUB', 'BYN'): + return self._int2word(number, gender='f') + return self._int2word(number, gender='m') + + def _int2word(self, n, feminine=False, cardinal=True, case=D_CASE, + plural=D_PLURAL, gender=D_GENDER, animate=D_ANIMATE): + """ + n: number + feminine: not used - for backward compatibility + cardinal:True - cardinal + False - ordinal + case: 'n' - nominative + 'g' - genitive + 'd' - dative + 'a' - accusative + 'i' - instrumental + 'p' - prepositional + plural: True - plural + False - singular + gender: 'f' - masculine + 'm' - feminine + 'n' - neuter + animate: True - animate + False - inanimate + """ + # For backward compatibility + if feminine: + gender = 'f' + + kwargs = {'case': case, 'plural': plural, 'gender': gender, + 'animate': animate} - def _int2word(self, n, feminine=False): if n < 0: - return ' '.join([self.negword, self._int2word(abs(n))]) + return ' '.join([self.negword, self._int2word(abs(n), + cardinal=cardinal, + **kwargs)]) if n == 0: - return ZERO[0] + return get_num_element(ONES, 0, **kwargs) if cardinal else \ + get_num_element(ONES_ORD, 0, **kwargs) words = [] chunks = list(splitbyx(str(n), 3)) + ord_join = chunks[-1] == 0 # join in one word if ending on 'тысячный' i = len(chunks) + rightest_nonzero_chunk_i = i - 1 - max( + [i for i, e in enumerate(chunks) if e != 0]) for x in chunks: + chunk_words = [] i -= 1 if x == 0: @@ -235,19 +376,122 @@ def _int2word(self, n, feminine=False): n1, n2, n3 = get_digits(x) - if n3 > 0: - words.append(HUNDREDS[n3][0]) - - if n2 > 1: - words.append(TWENTIES[n2][0]) + if cardinal: + chunk_words.extend( + self.__chunk_cardianl(n3, n2, n1, i, **kwargs) + ) + if i > 0: + chunk_words.append( + self.pluralize(x, get_thousands_elements(i, case))) + # ordinal, not joined like 'двухтысячный' + elif not (ord_join and rightest_nonzero_chunk_i == i): + chunk_words.extend( + self.__chunk_ordinal(n3, n2, n1, i, **kwargs) + ) + if i > 0: + t_case = case if rightest_nonzero_chunk_i == i else 'n' + chunk_words.append( + self.pluralize(x, get_thousands_elements(i, t_case))) + # ordinal, joined + else: + chunk_words.extend( + self.__chunk_ordinal_join(n3, n2, n1, i, **kwargs) + ) + if i > 0: + chunk_words.append( + get_num_element(THOUSANDS_ORD, i, **kwargs)) - if n2 == 1: - words.append(TENS[n1][0]) - elif n1 > 0: - ones = ONES_FEMININE if i == 1 or feminine and i == 0 else ONES - words.append(ones[n1][0]) + chunk_words = [''.join(chunk_words)] - if i > 0: - words.append(self.pluralize(x, THOUSANDS[i])) + words.extend(chunk_words) return ' '.join(words) + + def __chunk_cardianl(self, hundreds, tens, ones, chunk_num, **kwargs): + words = [] + if hundreds > 0: + words.append(get_num_element(HUNDREDS, hundreds, **kwargs)) + + if tens > 1: + words.append(get_num_element(TWENTIES, tens, **kwargs)) + + if tens == 1: + words.append(get_num_element(TENS, ones, **kwargs)) + elif ones > 0: + if chunk_num == 0: + w_ones = get_num_element(ONES, ones, **kwargs) + elif chunk_num == 1: + # Thousands are feminine + f_kwargs = kwargs.copy() + f_kwargs['gender'] = 'f' + w_ones = get_num_element(ONES, ones, **f_kwargs) + else: + w_ones = get_num_element(ONES, ones, **kwargs) + + words.append(w_ones) + return words + + def __chunk_ordinal(self, hundreds, tens, ones, chunk_num, **kwargs): + words = [] + if hundreds > 0: + if tens == 0 and ones == 0: + words.append(get_num_element(HUNDREDS_ORD, hundreds, **kwargs)) + else: + words.append(get_num_element(HUNDREDS, hundreds)) + + if tens > 1: + if ones == 0: + words.append(get_num_element(TWENTIES_ORD, tens, **kwargs)) + else: + words.append(get_num_element(TWENTIES, tens)) + + if tens == 1: + words.append(get_num_element(TENS_ORD, ones, **kwargs)) + elif ones > 0: + if chunk_num == 0: + w_ones = get_num_element(ONES_ORD, ones, **kwargs) + # тысячный, миллионнный и т.д. + elif chunk_num > 0 and ones == 1 and hundreds == 0 and tens == 0: + w_ones = None + elif chunk_num == 1: + # Thousands are feminine + w_ones = get_num_element(ONES, ones, gender='f') + else: + w_ones = get_num_element(ONES, ones) + + if w_ones: + words.append(w_ones) + + return words + + def __chunk_ordinal_join(self, hundreds, tens, ones, chunk_num, **kwargs): + words = [] + + if hundreds > 1: + words.append(get_num_element(HUNDREDS, hundreds, case='g')) + elif hundreds == 1: + words.append(get_num_element(HUNDREDS, hundreds)) # стО, not стА + + if tens == 9: + words.append(get_num_element(TWENTIES, tens)) # девяностО, not А + elif tens > 1: + words.append(get_num_element(TWENTIES, tens, case='g')) + + if tens == 1: + words.append(get_num_element(TENS, ones, case='g')) + elif ones > 0: + if chunk_num == 0: + w_ones = get_num_element(ONES_ORD, ones, **kwargs) + # тысячный, миллионнный и т.д., двадцатиодномиллионный + elif chunk_num > 0 and ones == 1 and tens != 1: + if tens == 0 and hundreds == 0: + w_ones = None + else: + w_ones = get_num_element(ONES, 1, gender='n') + else: + w_ones = get_num_element(ONES, ones, case='g') + + if w_ones: + words.append(w_ones) + + return words diff --git a/num2words/lang_SK.py b/num2words/lang_SK.py new file mode 100644 index 00000000..35ece024 --- /dev/null +++ b/num2words/lang_SK.py @@ -0,0 +1,160 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2013, Savoir-faire Linux inc. All Rights Reserved. + +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# This library is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# Lesser General Public License for more details. +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, +# MA 02110-1301 USA + +from __future__ import unicode_literals + +from .base import Num2Word_Base +from .utils import get_digits, splitbyx + +ZERO = ('nula',) + +ONES = { + 1: ('jeden', 'jeden', set()), + 2: ('dva', 'dve', {1, 3, 5, 7, 9}), + 3: ('tri', 'tri', set()), + 4: ('štyri', 'štyri', set()), + 5: ('päť', 'päť', set()), + 6: ('šesť', 'šesť', set()), + 7: ('sedem', 'sedem', set()), + 8: ('osem', 'osem', set()), + 9: ('deväť', 'deväť', set()), +} + +TENS = { + 0: ('desať',), + 1: ('jedenásť',), + 2: ('dvanásť',), + 3: ('trinásť',), + 4: ('štrnásť',), + 5: ('pätnásť',), + 6: ('šestnásť',), + 7: ('sedemnásť',), + 8: ('osemnásť',), + 9: ('devätnásť',), +} + +TWENTIES = { + 2: ('dvadsať',), + 3: ('tridsať',), + 4: ('štyridsať',), + 5: ('päťdesiat',), + 6: ('šesťdesiat',), + 7: ('sedemdesiat',), + 8: ('osemdesiat',), + 9: ('deväťdesiat',), +} + +HUNDREDS = { + 1: ('sto',), + 2: ('dvesto',), + 3: ('tristo',), + 4: ('štyristo',), + 5: ('päťsto',), + 6: ('šesťsto',), + 7: ('sedemsto',), + 8: ('osemsto',), + 9: ('deväťsto',), +} + +THOUSANDS = { + 1: ('tisíc', 'tisíc', 'tisíc'), # 10^3 + 2: ('milión', 'milióny', 'miliónov'), # 10^6 + 3: ('miliarda', 'miliardy', 'miliárd'), # 10^9 + 4: ('bilión', 'bilióny', 'biliónov'), # 10^12 + 5: ('biliarda', 'biliardy', 'biliárd'), # 10^15 + 6: ('trilión', 'trilióny', 'triliónov'), # 10^18 + 7: ('triliarda', 'triliardy', 'triliárd'), # 10^21 + 8: ('kvadrilión', 'kvadrilióny', 'kvadriliónov'), # 10^24 + 9: ('kvadriliarda', 'kvadriliardy', 'kvadriliárd'), # 10^27 + 10: ('kvintilión', 'kvintillióny', 'kvintiliónov'), # 10^30 +} + + +class Num2Word_SK(Num2Word_Base): + CURRENCY_FORMS = { + 'EUR': ( + ('euro', 'eurá', 'eur'), ('cent', 'centy', 'centov') + ), + } + + def setup(self): + self.negword = "mínus" + self.pointword = "celých" + + def to_cardinal(self, number): + n = str(number).replace(',', '.') + if '.' in n: + left, right = n.split('.') + leading_zero_count = len(right) - len(right.lstrip('0')) + decimal_part = ((ZERO[0] + ' ') * leading_zero_count + + self._int2word(int(right))) + return u'%s %s %s' % ( + self._int2word(int(left)), + self.pointword, + decimal_part + ) + else: + return self._int2word(int(n)) + + def pluralize(self, n, forms): + if n == 1: + form = 0 + elif 0 < n < 5: + form = 1 + else: + form = 2 + return forms[form] + + def to_ordinal(self, value): + raise NotImplementedError() + + def _int2word(self, n): + if n == 0: + return ZERO[0] + + words = [] + chunks = list(splitbyx(str(n), 3)) + i = len(chunks) + for x in chunks: + i -= 1 + + if x == 0: + continue + + n1, n2, n3 = get_digits(x) + + word_chunk = [] + + if n3 > 0: + word_chunk.append(HUNDREDS[n3][0]) + + if n2 > 1: + word_chunk.append(TWENTIES[n2][0]) + + if n2 == 1: + word_chunk.append(TENS[n1][0]) + elif n1 > 0 and not (i > 0 and x == 1): + if n2 == 0 and n3 == 0 and i in ONES[n1][2]: + word_chunk.append(ONES[n1][1]) + else: + word_chunk.append(ONES[n1][0]) + if i > 1 and word_chunk: + word_chunk.append(' ') + if i > 0: + word_chunk.append(self.pluralize(x, THOUSANDS[i])) + words.append(''.join(word_chunk)) + + return ' '.join(words[:-1]) + ''.join(words[-1:]) diff --git a/num2words/lang_UK.py b/num2words/lang_UK.py index a6c0a3bb..188d7ced 100644 --- a/num2words/lang_UK.py +++ b/num2words/lang_UK.py @@ -23,27 +23,27 @@ ZERO = ('нуль',) ONES_FEMININE = { - 1: ('одна',), - 2: ('дві',), - 3: ('три',), - 4: ('чотири',), - 5: ('п\'ять',), - 6: ('шість',), - 7: ('сім',), - 8: ('вісім',), - 9: ('дев\'ять',), + 1: ('одна', "однієї", "одній", "одну", "однією", "одній"), + 2: ('дві', "двох", "двом", "дві", "двома", "двох"), + 3: ('три', "трьох", "трьом", "три", "трьома", "трьох"), + 4: ('чотири', "чотирьох", "чотирьом", "чотири", "чотирма", "чотирьох"), + 5: ('п\'ять', "п'яти", "п'яти", "п'ять", "п'ятьма", "п'яти"), + 6: ('шість', "шести", "шести", "шість", "шістьма", "шести"), + 7: ('сім', "семи", "семи", "сім", "сьома", "семи"), + 8: ('вісім', "восьми", "восьми", "вісім", "вісьма", "восьми"), + 9: ("дев'ять", "дев'яти", "дев'яти", "дев'ять", "дев'ятьма", "дев'яти"), } ONES = { - 1: ('один',), - 2: ('два',), - 3: ('три',), - 4: ('чотири',), - 5: ('п\'ять',), - 6: ('шість',), - 7: ('сім',), - 8: ('вісім',), - 9: ('дев\'ять',), + 1: ('один', 'одного', "одному", "один", "одним", "одному"), + 2: ('два', 'двох', "двом", "два", "двома", "двох"), + 3: ('три', 'трьох', "трьом", "три", "трьома", "трьох"), + 4: ('чотири', 'чотирьох', "чотирьом", "чотири", "чотирма", "чотирьох"), + 5: ('п\'ять', "п'яти", "п'яти", "п'ять", "п'ятьма", "п'яти"), + 6: ('шість', 'шести', "шести", "шість", "шістьма", "шести"), + 7: ('сім', 'семи', "семи", "сім", "сьома", "семи"), + 8: ('вісім', 'восьми', "восьми", "вісім", "вісьма", "восьми"), + 9: ("дев'ять", "дев'яти", "дев'яти", "дев'ять", "дев'ятьма", "дев'яти"), } ONES_ORDINALS = { @@ -67,29 +67,118 @@ 18: ("вісімнадцятий", "вісімнадцяти"), 19: ("дев'ятнадцятий", "дев'ятнадцяти"), } - TENS = { - 0: ('десять',), - 1: ('одинадцять',), - 2: ('дванадцять',), - 3: ('тринадцять',), - 4: ('чотирнадцять',), - 5: ('п\'ятнадцять',), - 6: ('шістнадцять',), - 7: ('сімнадцять',), - 8: ('вісімнадцять',), - 9: ('дев\'ятнадцять',), + 0: ('десять', + 'десяти', + "десяти", + "десять", + "десятьма", + "десяти"), + 1: ('одинадцять', + 'одинадцяти', + "одинадцяти", + "одинадцять", + "одинадцятьма", + "одинадцяти"), + 2: ('дванадцять', + 'дванадцяти', + "дванадцяти", + "дванадцять", + "дванадцятьма", + "дванадцяти"), + 3: ('тринадцять', + 'тринадцяти', + "тринадцяти", + "тринадцять", + "тринадцятьма", + "тринадцяти"), + 4: ('чотирнадцять', + 'чотирнадцяти', + "чотирнадцяти", + "чотирнадцять", + "чотирнадцятьма", + "чотирнадцяти"), + 5: ("п'ятнадцять", + "п'ятнадцяти", + "п'ятнадцяти", + "п'ятнадцять", + "п'ятнадцятьма", + "п'ятнадцяти"), + 6: ('шістнадцять', + 'шістнадцяти', + "шістнадцяти", + "шістнадцять", + "шістнадцятьма", + "шістнадцяти"), + 7: ('сімнадцять', + 'сімнадцяти', + "сімнадцяти", + "сімнадцять", + "сімнадцятьма", + "сімнадцяти"), + 8: ('вісімнадцять', + 'вісімнадцяти', + "вісімнадцяти", + "вісімнадцять", + "вісімнадцятьма", + "вісімнадцяти"), + 9: ("дев'ятнадцять", + "дев'ятнадцяти", + "дев'ятнадцяти", + "дев'ятнадцять", + "дев'ятнадцятьма", + "дев'ятнадцяти"), } TWENTIES = { - 2: ('двадцять',), - 3: ('тридцять',), - 4: ('сорок',), - 5: ('п\'ятдесят',), - 6: ('шістдесят',), - 7: ('сімдесят',), - 8: ('вісімдесят',), - 9: ('дев\'яносто',), + 2: ('двадцять', + "двадцяти", + "двадцяти", + "двадцять", + "двадцятьма", + "двадцяти"), + 3: ('тридцять', + "тридцяти", + "тридцяти", + "тридцять", + "тридцятьма", + "тридцяти"), + 4: ('сорок', + "сорока", + "сорока", + "сорок", + "сорока", + "сорока"), + 5: ('п\'ятдесят', + "п'ятдесяти", + "п'ятдесяти", + "п'ятдесят", + "п'ятдесятьма", + "п'ятдесяти"), + 6: ('шістдесят', + "шістдесяти", + "шістдесяти", + "шістдесят", + "шістдесятьма", + "шістдесяти"), + 7: ('сімдесят', + "сімдесяти", + "сімдесяти", + "сімдесят", + "сімдесятьма", + "сімдесяти"), + 8: ('вісімдесят', + "вісімдесяти", + "вісімдесяти", + "вісімдесят", + "вісімдесятьма", + "вісімдесяти"), + 9: ('дев\'яносто', + "дев'яноста", + "дев'яноста", + "дев'яносто", + "дев'яностами", + "дев'яноста"), } TWENTIES_ORDINALS = { @@ -104,15 +193,60 @@ } HUNDREDS = { - 1: ('сто',), - 2: ('двісті',), - 3: ('триста',), - 4: ('чотириста',), - 5: ('п\'ятсот',), - 6: ('шістсот',), - 7: ('сімсот',), - 8: ('вісімсот',), - 9: ('дев\'ятсот',), + 1: ('сто', + "ста", + "ста", + "сто", + "стами", + "стах"), + 2: ('двісті', + "двохста", + "двомстам", + "двісті", + "двомастами", + "двохстах"), + 3: ('триста', + "трьохста", + "трьомстам", + "триста", + "трьомастами", + "трьохстах"), + 4: ('чотириста', + "чотирьохста", + "чотирьомстам", + "чотириста", + "чотирмастами", + "чотирьохстах"), + 5: ('п\'ятсот', + "п'ятиста", + "п'ятистам", + "п'ятсот", + "п'ятьмастами", + "п'ятистах"), + 6: ('шістсот', + "шестиста", + "шестистам", + "шістсот", + "шістьмастами", + "шестистах"), + 7: ('сімсот', + "семиста", + "семистам", + "сімсот", + "сьомастами", + "семистах"), + 8: ('вісімсот', + "восьмиста", + "восьмистам", + "вісімсот", + "восьмастами", + "восьмистах"), + 9: ("дев'ятсот", + "дев'ятиста", + "дев'ятистам", + "дев'ятсот", + "дев'ятьмастами", + "дев'ятистах"), } HUNDREDS_ORDINALS = { @@ -128,16 +262,76 @@ } THOUSANDS = { - 1: ('тисяча', 'тисячі', 'тисяч'), # 10^3 - 2: ('мільйон', 'мільйони', 'мільйонів'), # 10^6 - 3: ('мільярд', 'мільярди', 'мільярдів'), # 10^9 - 4: ('трильйон', 'трильйони', 'трильйонів'), # 10^12 - 5: ('квадрильйон', 'квадрильйони', 'квадрильйонів'), # 10^15 - 6: ('квінтильйон', 'квінтильйони', 'квінтильйонів'), # 10^18 - 7: ('секстильйон', 'секстильйони', 'секстильйонів'), # 10^21 - 8: ('септильйон', 'септильйони', 'септильйонів'), # 10^24 - 9: ('октильйон', 'октильйони', 'октильйонів'), # 10^27 - 10: ('нонільйон', 'нонільйони', 'нонільйонів'), # 10^30 + # 10^3 + 1: (('тисяча', 'тисячі', 'тисяч'), + ('тисячи', 'тисяч', 'тисяч'), + ('тисячі', 'тисячам', 'тисячам'), + ('тисячу', 'тисячі', 'тисяч'), + ('тисячею', 'тисячами', 'тисячами'), + ('тисячі', 'тисячах', 'тисячах'),), + # 10^6 + 2: (('мільйон', 'мільйони', 'мільйонів'), + ('мільйона', 'мільйонів', 'мільйонів'), + ('мільйону', 'мільйонам', 'мільйонам'), + ('мільйон', 'мільйони', 'мільйонів'), + ('мільйоном', 'мільйонами', 'мільйонів'), + ('мільйоні', 'мільйонах', 'мільйонах'),), + # 10^9 + 3: (('мільярд', 'мільярди', 'мільярдів'), + ('мільярда', 'мільярдів', 'мільярдів'), + ('мільярду', 'мільярдам', 'мільярдам'), + ('мільярд', 'мільярди', 'мільярдів'), + ('мільярдом', 'мільярдами', 'мільярдів'), + ('мільярді', 'мільярдах', 'мільярдах'),), + # 10^12 + 4: (('трильйон', 'трильйони', 'трильйонів'), + ('трильйона', 'трильйонів', 'трильйонів'), + ('трильйону', 'трильйонам', 'трильйонам'), + ('трильйон', 'трильйони', 'трильйонів'), + ('трильйоном', 'трильйонами', 'трильйонів'), + ('трильйоні', 'трильйонах', 'трильйонах'),), + # 10^15 + 5: (('квадрильйон', 'квадрильйони', 'квадрильйонів'), + ('квадрильйона', 'квадрильйонів', 'квадрильйонів'), + ('квадрильйону', 'квадрильйонам', 'квадрильйонам'), + ('квадрильйон', 'квадрильйони', 'квадрильйонів'), + ('квадрильйоном', 'квадрильйонами', 'квадрильйонів'), + ('квадрильйоні', 'квадрильйонах', 'квадрильйонах'),), + # 10^18 + 6: (('квінтильйон', 'квінтильйони', 'квінтильйонів'), + ('квінтильйона', 'квінтильйонів', 'квінтильйонів'), + ('квінтильйону', 'квінтильйонам', 'квінтильйонам'), + ('квінтильйон', 'квінтильйони', 'квінтильйонів'), + ('квінтильйоном', 'квінтильйонами', 'квінтильйонів'), + ('квінтильйоні', 'квінтильйонах', 'квінтильйонах'),), + # 10^21 + 7: (('секстильйон', 'секстильйони', 'секстильйонів'), + ('секстильйона', 'секстильйонів', 'секстильйонів'), + ('секстильйону', 'секстильйонам', 'секстильйонам'), + ('секстильйон', 'секстильйони', 'секстильйонів'), + ('секстильйоном', 'секстильйонами', 'секстильйонів'), + ('секстильйоні', 'секстильйонах', 'секстильйонах'),), + # 10^24 + 8: (('септильйон', 'септильйони', 'септильйонів'), + ('септильйона', 'септильйонів', 'септильйонів'), + ('септильйону', 'септильйонам', 'септильйонам'), + ('септильйон', 'септильйони', 'септильйонів'), + ('септильйоном', 'септильйонами', 'септильйонів'), + ('септильйоні', 'септильйонах', 'септильйонах'),), + # 10^27 + 9: (('октильйон', 'октильйони', 'октильйонів'), + ('октильйона', 'октильйонів', 'октильйонів'), + ('октильйону', 'октильйонам', 'октильйонам'), + ('октильйон', 'октильйони', 'октильйонів'), + ('октильйоном', 'октильйонами', 'октильйонів'), + ('октильйоні', 'октильйонах', 'октильйонах'),), + # 10^30 + 10: (('нонільйон', 'нонільйони', 'нонільйонів'), + ('нонільйона', 'нонільйонів', 'нонільйонів'), + ('нонільйону', 'нонільйонам', 'нонільйонам'), + ('нонільйон', 'нонільйони', 'нонільйонів'), + ('нонільйоном', 'нонільйонами', 'нонільйонів'), + ('нонільйоні', 'нонільйонах', 'нонільйонах'),), } prefixes_ordinal = { @@ -711,20 +905,38 @@ def setup(self): self.negword = "мінус" self.pointword = "кома" - def to_cardinal(self, number): + def to_cardinal(self, number, **kwargs): + if 'case' in kwargs: + case = kwargs['case'] + morphological_case = [ + "nominative", + "genitive", + "dative", + "accusative", + "instrumental", + "locative"].index(case) + else: + morphological_case = 0 + + if 'gender' in kwargs: + gender = kwargs['gender'] == 'feminine' + else: + gender = False + n = str(number).replace(',', '.') if '.' in n: left, right = n.split('.') leading_zero_count = len(right) - len(right.lstrip('0')) + right_side = self._int2word(int(right), gender, morphological_case) decimal_part = ((ZERO[0] + ' ') * leading_zero_count + - self._int2word(int(right))) + right_side) return u'%s %s %s' % ( - self._int2word(int(left)), + self._int2word(int(left), gender, morphological_case), self.pointword, decimal_part ) else: - return self._int2word(int(n)) + return self._int2word(int(n), gender, morphological_case) def pluralize(self, n, forms): if n % 100 < 10 or n % 100 > 20: @@ -739,9 +951,11 @@ def pluralize(self, n, forms): return forms[form] - def _int2word(self, n, feminine=False): + def _int2word(self, n, feminine=False, morphological_case=0): if n < 0: - return ' '.join([self.negword, self._int2word(abs(n))]) + n_value = self._int2word(abs(n), feminine, morphological_case) + return ' '.join([self.negword, + n_value]) if n == 0: return ZERO[0] @@ -758,20 +972,21 @@ def _int2word(self, n, feminine=False): n1, n2, n3 = get_digits(x) if n3 > 0: - words.append(HUNDREDS[n3][0]) + words.append(HUNDREDS[n3][morphological_case]) if n2 > 1: - words.append(TWENTIES[n2][0]) + words.append(TWENTIES[n2][morphological_case]) if n2 == 1: - words.append(TENS[n1][0]) + words.append(TENS[n1][morphological_case]) # elif n1 > 0 and not (i > 0 and x == 1): elif n1 > 0: ones = ONES_FEMININE if i == 1 or feminine and i == 0 else ONES - words.append(ones[n1][0]) + words.append(ones[n1][morphological_case]) if i > 0: - words.append(self.pluralize(x, THOUSANDS[i])) + thousands_val = THOUSANDS[i][morphological_case] + words.append(self.pluralize(x, thousands_val)) return ' '.join(words) diff --git a/tests/test_ru.py b/tests/test_ru.py index 85d6dc3e..206873c4 100644 --- a/tests/test_ru.py +++ b/tests/test_ru.py @@ -35,7 +35,9 @@ def test_cardinal(self): self.assertEqual(num2words(2012, lang='ru'), "две тысячи двенадцать") self.assertEqual( num2words(12519.85, lang='ru'), - "двенадцать тысяч пятьсот девятнадцать запятая восемьдесят пять") + "двенадцать тысяч пятьсот девятнадцать целых восемьдесят пять " + "сотых" + ) self.assertEqual( num2words(1234567890, lang='ru'), "один миллиард двести тридцать четыре миллиона пятьсот " @@ -73,22 +75,69 @@ def test_cardinal(self): self.assertEqual(num2words(-15, lang='ru'), "минус пятнадцать") self.assertEqual(num2words(-100, lang='ru'), "минус сто") - def test_floating_point(self): - self.assertEqual(num2words(5.2, lang='ru'), "пять запятая два") + def test_cardinal_feminine(self): + self.assertEqual(num2words(1, lang='ru', gender='f'), 'одна') + self.assertEqual(num2words(2, lang='ru', gender='f'), 'две') + self.assertEqual(num2words(3, lang='ru', gender='f'), 'три') + self.assertEqual(num2words(100, lang='ru', gender='f'), "сто") + self.assertEqual(num2words(101, lang='ru', gender='f'), "сто одна") + self.assertEqual(num2words(110, lang='ru', gender='f'), "сто десять") self.assertEqual( - num2words(10.02, lang='ru'), - "десять запятая ноль два" + num2words(115, lang='ru', gender='f'), "сто пятнадцать" ) self.assertEqual( - num2words(15.007, lang='ru'), - "пятнадцать запятая ноль ноль семь" + num2words(122, lang='ru', gender='f'), "сто двадцать две" ) self.assertEqual( - num2words(561.42, lang='ru'), - "пятьсот шестьдесят один запятая сорок два" + num2words(125.1, lang='ru', gender='f'), + 'сто двадцать пять целых одна десятая' ) + self.assertEqual(num2words(-1, lang='ru', gender='f'), "минус одна") + self.assertEqual(num2words(-100, lang='ru', gender='f'), "минус сто") + + def test_cardinal_neuter(self): + self.assertEqual(num2words(1, lang='ru', gender='n'), 'одно') + self.assertEqual(num2words(2, lang='ru', gender='n'), 'два') + self.assertEqual(num2words(3, lang='ru', gender='n'), 'три') + self.assertEqual(num2words(100, lang='ru', gender='n'), "сто") + self.assertEqual(num2words(101, lang='ru', gender='n'), "сто одно") + self.assertEqual(num2words(110, lang='ru', gender='n'), "сто десять") + self.assertEqual( + num2words(115, lang='ru', gender='n'), "сто пятнадцать" + ) + self.assertEqual( + num2words(122, lang='ru', gender='n'), "сто двадцать два" + ) + self.assertEqual( + num2words(125.1, lang='ru', gender='n'), + 'сто двадцать пять целых одна десятая') + self.assertEqual(num2words(-1, lang='ru', gender='n'), "минус одно") + self.assertEqual(num2words(-100, lang='ru', gender='n'), "минус сто") + + def test_floating_point(self): + self.assertEqual(num2words(5.2, lang='ru'), "пять целых две десятых") + self.assertEqual(num2words(5.0, lang='ru'), "пять целых ноль десятых") + self.assertEqual(num2words(5.10, lang='ru'), "пять целых одна десятая") + self.assertEqual(num2words("5.10", lang='ru'), + "пять целых десять сотых") + self.assertEqual(num2words(1.001, lang='ru'), + "одна целая одна тысячная") + self.assertEqual(num2words(1.011, lang='ru'), + "одна целая одиннадцать тысячных") + self.assertEqual(num2words(10.02, lang='ru'), + "десять целых две сотых") + self.assertEqual(num2words(15.007, lang='ru'), + "пятнадцать целых семь тысячных") + self.assertEqual(num2words(561.42, lang='ru'), + "пятьсот шестьдесят одна целая сорок две сотых") + self.assertEqual(num2words(561.00001, lang='ru'), + "пятьсот шестьдесят одна целая одна стотысячная") def test_to_ordinal(self): + self.assertEqual( + num2words(0, lang='ru', to='ordinal'), + 'нулевой' + ) self.assertEqual( num2words(1, lang='ru', to='ordinal'), 'первый' @@ -142,22 +191,187 @@ def test_to_ordinal(self): num2words(1001, lang='ru', to='ordinal'), 'тысяча первый' ) + self.assertEqual( + num2words(1060, lang='ru', to='ordinal'), + 'тысяча шестидесятый' + ) self.assertEqual( num2words(2000, lang='ru', to='ordinal'), - 'двух тысячный' + 'двухтысячный' ) self.assertEqual( num2words(10000, lang='ru', to='ordinal'), - 'десяти тысячный' + 'десятитысячный' + ) + self.assertEqual( + num2words(90000, lang='ru', to='ordinal'), + 'девяностотысячный' + ) + self.assertEqual( + num2words(21000, lang='ru', to='ordinal'), + 'двадцатиоднотысячный' + ) + self.assertEqual( + num2words(130000, lang='ru', to='ordinal'), + 'стотридцатитысячный' + ) + self.assertEqual( + num2words(135000, lang='ru', to='ordinal'), + 'стотридцатипятитысячный' + ) + self.assertEqual( + num2words(135100, lang='ru', to='ordinal'), + 'сто тридцать пять тысяч сотый' + ) + self.assertEqual( + num2words(135120, lang='ru', to='ordinal'), + 'сто тридцать пять тысяч сто двадцатый' + ) + self.assertEqual( + num2words(135121, lang='ru', to='ordinal'), + 'сто тридцать пять тысяч сто двадцать первый' + ) + self.assertEqual( + num2words(190000, lang='ru', to='ordinal'), + 'стодевяностотысячный' ) self.assertEqual( num2words(1000000, lang='ru', to='ordinal'), 'миллионный' ) + self.assertEqual( + num2words(2000000, lang='ru', to='ordinal'), + 'двухмиллионный' + ) + self.assertEqual( + num2words(5135000, lang='ru', to='ordinal'), + 'пять миллионов стотридцатипятитысячный' + ) + self.assertEqual( + num2words(21000000, lang='ru', to='ordinal'), + 'двадцатиодномиллионный' + ) self.assertEqual( num2words(1000000000, lang='ru', to='ordinal'), 'миллиардный' ) + self.assertEqual( + num2words(123456000000, lang='ru', to='ordinal'), + 'сто двадцать три миллиарда четырёхсотпятидесятишестимиллионный' + ) + + def test_to_ordinal_feminine(self): + self.assertEqual( + num2words(1, lang='ru', to='ordinal', gender='f'), 'первая' + ) + self.assertEqual( + num2words(3, lang='ru', to='ordinal', gender='f'), 'третья' + ) + self.assertEqual( + num2words(10, lang='ru', to='ordinal', gender='f'), 'десятая' + ) + self.assertEqual( + num2words(23, lang='ru', to='ordinal', gender='f'), + 'двадцать третья' + ) + self.assertEqual( + num2words(1000, lang='ru', to='ordinal', gender='f'), 'тысячная' + ) + self.assertEqual( + num2words(2000000, lang='ru', to='ordinal', gender='f'), + 'двухмиллионная' + ) + + def test_to_ordinal_neuter(self): + self.assertEqual( + num2words(1, lang='ru', to='ordinal', gender='n'), 'первое' + ) + self.assertEqual( + num2words(3, lang='ru', to='ordinal', gender='n'), 'третье' + ) + self.assertEqual( + num2words(10, lang='ru', to='ordinal', gender='n'), 'десятое' + ) + self.assertEqual( + num2words(23, lang='ru', to='ordinal', gender='n'), + 'двадцать третье' + ) + self.assertEqual( + num2words(1000, lang='ru', to='ordinal', gender='n'), 'тысячное' + ) + self.assertEqual( + num2words(2000000, lang='ru', to='ordinal', gender='n'), + 'двухмиллионное' + ) + + def test_cardinal_cases(self): + self.assertEqual( + num2words(1, lang='ru', case='nominative'), 'один') + self.assertEqual( + num2words(1, lang='ru', case='genitive'), 'одного') + self.assertEqual( + num2words(1, lang='ru', case='a', plural=True, animate=False), + 'одни') + self.assertEqual( + num2words(2, lang='ru', case='a', gender='f', animate=True), + 'двух') + self.assertEqual( + num2words(2, lang='ru', case='a', gender='f', animate=False), + 'две') + self.assertEqual( + num2words(100, lang='ru', case='g'), + 'ста') + self.assertEqual( + num2words(122, lang='ru', case='d'), + 'ста двадцати двум') + self.assertEqual( + num2words(1000, lang='ru', case='p'), + 'одной тысяче') + self.assertEqual( + num2words(1122, lang='ru', case='p'), + 'одной тысяче ста двадцати двух') + self.assertEqual( + num2words(1211, lang='ru', case='i', gender='f'), + 'одной тысячей двумястами одиннадцатью') + self.assertEqual( + num2words(5121000, lang='ru', case='i'), + 'пятью миллионами ста двадцатью одной тысячей') + + def test_ordinal_cases(self): + self.assertEqual( + num2words(1, lang='ru', to='ordinal', case='nominative'), 'первый') + self.assertEqual( + num2words(1, lang='ru', to='ordinal', case='genitive'), 'первого') + self.assertEqual( + num2words(1, lang='ru', to='ordinal', case='a', plural=True, + animate=False), + 'первые') + self.assertEqual( + num2words(2, lang='ru', to='ordinal', case='a', gender='f', + animate=True), + 'вторую') + self.assertEqual( + num2words(2, lang='ru', to='ordinal', case='a', gender='f', + animate=False), + 'вторую') + self.assertEqual( + num2words(100, lang='ru', to='ordinal', case='g'), + 'сотого') + self.assertEqual( + num2words(122, lang='ru', to='ordinal', case='d'), + 'сто двадцать второму') + self.assertEqual( + num2words(1000, lang='ru', to='ordinal', case='p'), + 'тысячном') + self.assertEqual( + num2words(1122, lang='ru', to='ordinal', case='p'), + 'тысяча сто двадцать втором') + self.assertEqual( + num2words(1211, lang='ru', to='ordinal', case='i', gender='f'), + 'тысяча двести одиннадцатой') + self.assertEqual( + num2words(5121000, lang='ru', to='ordinal', case='i'), + 'пять миллионов стодвадцатиоднотысячным') def test_to_currency(self): self.assertEqual( diff --git a/tests/test_sk.py b/tests/test_sk.py new file mode 100644 index 00000000..314a7c2b --- /dev/null +++ b/tests/test_sk.py @@ -0,0 +1,98 @@ + +# -*- 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 Num2WordsSKTest(TestCase): + def test_cardinal(self): + self.assertEqual(num2words(100, lang='sk'), "sto") + self.assertEqual(num2words(101, lang='sk'), "stojeden") + self.assertEqual(num2words(110, lang='sk'), "stodesať") + self.assertEqual(num2words(115, lang='sk'), "stopätnásť") + self.assertEqual(num2words(123, lang='sk'), "stodvadsaťtri") + self.assertEqual(num2words(1000, lang='sk'), "tisíc") + self.assertEqual(num2words(1001, lang='sk'), "tisícjeden") + self.assertEqual(num2words(2012, lang='sk'), "dvetisícdvanásť") + self.assertEqual( + num2words(10.02, lang='sk'), + "desať celých nula dva" + ) + self.assertEqual( + num2words(15.007, lang='sk'), + "pätnásť celých nula nula sedem" + ) + self.assertEqual( + num2words(12519.85, lang='sk'), + "dvanásťtisícpäťstodevätnásť celých osemdesiatpäť" + ) + self.assertEqual( + num2words(123.50, lang='sk'), + "stodvadsaťtri celých päť" + ) + self.assertEqual( + num2words(1234567890, lang='sk'), + "miliarda dvestotridsaťštyri miliónov päťstošesťdesiat" + "sedemtisícosemstodeväťdesiat" + ) + self.assertEqual( + num2words(215461407892039002157189883901676, lang='sk'), + "dvestopätnásť kvintiliónov štyristošesťdesiatjeden kvadriliárd " + "štyristosedem kvadriliónov osemstodeväťdesiatdva triliárd " + "tridsaťdeväť triliónov dve biliardy stopäťdesiatsedem biliónov " + "stoosemdesiatdeväť miliárd osemstoosemdesiattri miliónov " + "deväťstojedentisícšesťstosedemdesiatšesť" + ) + self.assertEqual( + num2words(719094234693663034822824384220291, lang='sk'), + "sedemstodevätnásť kvintiliónov deväťdesiatštyri kvadriliárd " + "dvestotridsaťštyri kvadriliónov šesťstodeväťdesiattri triliárd " + "šesťstošesťdesiattri triliónov tridsaťštyri biliárd " + "osemstodvadsaťdva biliónov osemstodvadsaťštyri miliárd " + "tristoosemdesiatštyri miliónov " + "dvestodvadsaťtisícdvestodeväťdesiatjeden" + ) + + def test_to_ordinal(self): + # @TODO: implement to_ordinal + with self.assertRaises(NotImplementedError): + num2words(1, lang='sk', to='ordinal') + + def test_currency(self): + self.assertEqual( + num2words(10.0, lang='sk', to='currency', currency='EUR'), + "desať eur, nula centov") + self.assertEqual( + num2words(1234.56, lang='sk', to='currency', currency='EUR'), + "tisícdvestotridsaťštyri eur, päťdesiatšesť centov") + self.assertEqual( + num2words(101.11, lang='sk', to='currency', currency='EUR', + separator=' a'), + "stojeden eur a jedenásť centov") + self.assertEqual( + num2words(-12519.85, lang='sk', to='currency', cents=False), + "mínus dvanásťtisícpäťstodevätnásť eur, 85 centov" + ) + self.assertEqual( + num2words(19.50, lang='sk', to='currency', cents=False), + "devätnásť eur, 50 centov" + ) diff --git a/tests/test_uk.py b/tests/test_uk.py index 84fb770a..4dcd7cb1 100644 --- a/tests/test_uk.py +++ b/tests/test_uk.py @@ -117,6 +117,588 @@ "двісті двадцять тисяч двісті дев'яносто один") ) +TEST_CASES_CARDINAL_FEMININE = ( + (1, "одна"), + (2, "дві"), + (3, "три"), + (4, "чотири"), + (5, "п'ять"), + (6, "шість"), + (7, "сім"), + (8, "вісім"), + (9, "дев'ять"), + (10, "десять"), + (10.02, "десять кома нуль дві"), + (11, "одинадцять"), + (12, "дванадцять"), + (12.40, "дванадцять кома чотири"), + (13, "тринадцять"), + (14, "чотирнадцять"), + (14.13, "чотирнадцять кома тринадцять"), + (15, "п'ятнадцять"), + (16, "шістнадцять"), + (17, "сімнадцять"), + (17.31, "сімнадцять кома тридцять одна"), + (18, "вісімнадцять"), + (19, "дев'ятнадцять"), + (20, "двадцять"), + (21, "двадцять одна"), + (21.20, "двадцять одна кома дві"), + (30, "тридцять"), + (32, "тридцять дві"), + (40, "сорок"), + (43, "сорок три"), + (43.007, "сорок три кома нуль нуль сім"), + (50, "п'ятдесят"), + (54, "п'ятдесят чотири"), + (60, "шістдесят"), + (60.059, "шістдесят кома нуль п'ятдесят дев'ять"), + (65, "шістдесят п'ять"), + (70, "сімдесят"), + (76, "сімдесят шість"), + (80, "вісімдесят"), + (87, "вісімдесят сім"), + (90, "дев'яносто"), + (98, "дев'яносто вісім"), + (99, "дев'яносто дев'ять"), + (100, "сто"), + (101, "сто одна"), + (199, "сто дев'яносто дев'ять"), + (200, "двісті"), + (203, "двісті три"), + (300, "триста"), + (356, "триста п'ятдесят шість"), + (400, "чотириста"), + (434, "чотириста тридцять чотири"), + (500, "п'ятсот"), + (578, "п'ятсот сімдесят вісім"), + (600, "шістсот"), + (689, "шістсот вісімдесят дев'ять"), + (700, "сімсот"), + (729, "сімсот двадцять дев'ять"), + (800, "вісімсот"), + (894, "вісімсот дев'яносто чотири"), + (900, "дев'ятсот"), + (999, "дев'ятсот дев'яносто дев'ять"), + (1000, "одна тисяча"), + (1001, "одна тисяча одна"), + (2012, "дві тисячі дванадцять"), + (12519, "дванадцять тисяч п'ятсот дев'ятнадцять"), + (12519.85, "дванадцять тисяч п'ятсот дев'ятнадцять кома вісімдесят п'ять"), + (-260000, "мінус двісті шістдесят тисяч"), + (1000000, "один мільйон"), + (1000000000, "один мільярд"), + (1234567890, "один мільярд двісті тридцять чотири мільйони " + "п'ятсот шістдесят сім тисяч вісімсот дев'яносто"), + (1000000000000, "один трильйон"), + (1000000000000000, "один квадрильйон"), + (1000000000000000000, "один квінтильйон"), + (1000000000000000000000, "один секстильйон"), + (1000000000000000000000000, "один септильйон"), + (1000000000000000000000000000, "один октильйон"), + (1000000000000000000000000000000, "один нонільйон"), + (215461407892039002157189883901676, + "двісті п'ятнадцять нонільйонів чотириста шістдесят один " + "октильйон чотириста сім септильйонів вісімсот дев'яносто " + "два секстильйони тридцять дев'ять квінтильйонів два " + "квадрильйони сто п'ятдесят сім трильйонів сто вісімдесят " + "дев'ять мільярдів вісімсот вісімдесят три мільйони " + "дев'ятсот одна тисяча шістсот сімдесят шість"), + (719094234693663034822824384220291, + "сімсот дев'ятнадцять нонільйонів дев'яносто чотири октильйони " + "двісті тридцять чотири септильйони шістсот дев'яносто три " + "секстильйони шістсот шістдесят три квінтильйони тридцять " + "чотири квадрильйони вісімсот двадцять два трильйони вісімсот " + "двадцять чотири мільярди триста вісімдесят чотири мільйони " + "двісті двадцять тисяч двісті дев'яносто одна") +) + +TEST_CASES_CARDINAL_GENITIVE = ( + (1, "одного"), + (2, "двох"), + (3, "трьох"), + (4, "чотирьох"), + (5, "п'яти"), + (6, "шести"), + (7, "семи"), + (8, "восьми"), + (9, "дев'яти"), + (10, "десяти"), + (10.02, "десяти кома нуль двох"), + (11, "одинадцяти"), + (12, "дванадцяти"), + (12.40, "дванадцяти кома чотирьох"), + (13, "тринадцяти"), + (14, "чотирнадцяти"), + (14.13, "чотирнадцяти кома тринадцяти"), + (15, "п'ятнадцяти"), + (16, "шістнадцяти"), + (17, "сімнадцяти"), + (17.31, "сімнадцяти кома тридцяти одного"), + (18, "вісімнадцяти"), + (19, "дев'ятнадцяти"), + (20, "двадцяти"), + (21, "двадцяти одного"), + (21.20, "двадцяти одного кома двох"), + (30, "тридцяти"), + (32, "тридцяти двох"), + (40, "сорока"), + (43, "сорока трьох"), + (43.007, "сорока трьох кома нуль нуль семи"), + (50, "п'ятдесяти"), + (54, "п'ятдесяти чотирьох"), + (60, "шістдесяти"), + (60.059, "шістдесяти кома нуль п'ятдесяти дев'яти"), + (65, "шістдесяти п'яти"), + (70, "сімдесяти"), + (76, "сімдесяти шести"), + (80, "вісімдесяти"), + (87, "вісімдесяти семи"), + (90, "дев'яноста"), + (98, "дев'яноста восьми"), + (99, "дев'яноста дев'яти"), + (100, "ста"), + (101, "ста одного"), + (199, "ста дев'яноста дев'яти"), + (200, "двохста"), + (203, "двохста трьох"), + (300, "трьохста"), + (356, "трьохста п'ятдесяти шести"), + (400, "чотирьохста"), + (434, "чотирьохста тридцяти чотирьох"), + (500, "п'ятиста"), + (578, "п'ятиста сімдесяти восьми"), + (600, "шестиста"), + (689, "шестиста вісімдесяти дев'яти"), + (700, "семиста"), + (729, "семиста двадцяти дев'яти"), + (800, "восьмиста"), + (894, "восьмиста дев'яноста чотирьох"), + (900, "дев'ятиста"), + (999, "дев'ятиста дев'яноста дев'яти"), + (1000, "однієї тисячи"), + (1001, "однієї тисячи одного"), + (2012, "двох тисяч дванадцяти"), + (12519, "дванадцяти тисяч п'ятиста дев'ятнадцяти"), + (12519.85, "дванадцяти тисяч п'ятиста дев'ятнадцяти " + "кома вісімдесяти п'яти"), + (-260000, "мінус двохста шістдесяти тисяч"), + (1000000, "одного мільйона"), + (1000000000, "одного мільярда"), + (1234567890, "одного мільярда двохста тридцяти чотирьох мільйонів " + "п'ятиста шістдесяти семи тисяч восьмиста дев'яноста"), + (1000000000000, "одного трильйона"), + (1000000000000000, "одного квадрильйона"), + (1000000000000000000, "одного квінтильйона"), + (1000000000000000000000, "одного секстильйона"), + (1000000000000000000000000, "одного септильйона"), + (1000000000000000000000000000, "одного октильйона"), + (1000000000000000000000000000000, "одного нонільйона"), + (215461407892039002157189883901676, + "двохста п'ятнадцяти нонільйонів чотирьохста шістдесяти одного " + "октильйона чотирьохста семи септильйонів восьмиста дев'яноста " + "двох секстильйонів тридцяти дев'яти квінтильйонів двох " + "квадрильйонів ста п'ятдесяти семи трильйонів ста вісімдесяти " + "дев'яти мільярдів восьмиста вісімдесяти трьох мільйонів " + "дев'ятиста однієї тисячи шестиста сімдесяти шести"), + (719094234693663034822824384220291, + "семиста дев'ятнадцяти нонільйонів дев'яноста чотирьох октильйонів " + "двохста тридцяти чотирьох септильйонів шестиста дев'яноста трьох " + "секстильйонів шестиста шістдесяти трьох квінтильйонів тридцяти " + "чотирьох квадрильйонів восьмиста двадцяти двох трильйонів восьмиста " + "двадцяти чотирьох мільярдів трьохста вісімдесяти чотирьох мільйонів " + "двохста двадцяти тисяч двохста дев'яноста одного") +) + +TEST_CASES_CARDINAL_DATIVE = ( + (1, "одному"), + (2, "двом"), + (3, "трьом"), + (4, "чотирьом"), + (5, "п'яти"), + (6, "шести"), + (7, "семи"), + (8, "восьми"), + (9, "дев'яти"), + (10, "десяти"), + (10.02, "десяти кома нуль двом"), + (11, "одинадцяти"), + (12, "дванадцяти"), + (12.40, "дванадцяти кома чотирьом"), + (13, "тринадцяти"), + (14, "чотирнадцяти"), + (14.13, "чотирнадцяти кома тринадцяти"), + (15, "п'ятнадцяти"), + (16, "шістнадцяти"), + (17, "сімнадцяти"), + (17.31, "сімнадцяти кома тридцяти одному"), + (18, "вісімнадцяти"), + (19, "дев'ятнадцяти"), + (20, "двадцяти"), + (21, "двадцяти одному"), + (21.20, "двадцяти одному кома двом"), + (30, "тридцяти"), + (32, "тридцяти двом"), + (40, "сорока"), + (43, "сорока трьом"), + (43.007, "сорока трьом кома нуль нуль семи"), + (50, "п'ятдесяти"), + (54, "п'ятдесяти чотирьом"), + (60, "шістдесяти"), + (60.059, "шістдесяти кома нуль п'ятдесяти дев'яти"), + (65, "шістдесяти п'яти"), + (70, "сімдесяти"), + (76, "сімдесяти шести"), + (80, "вісімдесяти"), + (87, "вісімдесяти семи"), + (90, "дев'яноста"), + (98, "дев'яноста восьми"), + (99, "дев'яноста дев'яти"), + (100, "ста"), + (101, "ста одному"), + (199, "ста дев'яноста дев'яти"), + (200, "двомстам"), + (203, "двомстам трьом"), + (300, "трьомстам"), + (356, "трьомстам п'ятдесяти шести"), + (400, "чотирьомстам"), + (434, "чотирьомстам тридцяти чотирьом"), + (500, "п'ятистам"), + (578, "п'ятистам сімдесяти восьми"), + (600, "шестистам"), + (689, "шестистам вісімдесяти дев'яти"), + (700, "семистам"), + (729, "семистам двадцяти дев'яти"), + (800, "восьмистам"), + (894, "восьмистам дев'яноста чотирьом"), + (900, "дев'ятистам"), + (999, "дев'ятистам дев'яноста дев'яти"), + (1000, "одній тисячі"), + (1001, "одній тисячі одному"), + (2012, "двом тисячам дванадцяти"), + (12519, "дванадцяти тисячам п'ятистам дев'ятнадцяти"), + (12519.85, "дванадцяти тисячам п'ятистам дев'ятнадцяти " + "кома вісімдесяти п'яти"), + (-260000, "мінус двомстам шістдесяти тисячам"), + (1000000, "одному мільйону"), + (1000000000, "одному мільярду"), + (1234567890, "одному мільярду двомстам тридцяти чотирьом мільйонам " + "п'ятистам шістдесяти семи тисячам восьмистам дев'яноста"), + (1000000000000, "одному трильйону"), + (1000000000000000, "одному квадрильйону"), + (1000000000000000000, "одному квінтильйону"), + (1000000000000000000000, "одному секстильйону"), + (1000000000000000000000000, "одному септильйону"), + (1000000000000000000000000000, "одному октильйону"), + (1000000000000000000000000000000, "одному нонільйону"), + (215461407892039002157189883901676, + "двомстам п'ятнадцяти нонільйонам чотирьомстам шістдесяти одному " + "октильйону чотирьомстам семи септильйонам восьмистам дев'яноста " + "двом секстильйонам тридцяти дев'яти квінтильйонам двом " + "квадрильйонам ста п'ятдесяти семи трильйонам ста вісімдесяти " + "дев'яти мільярдам восьмистам вісімдесяти трьом мільйонам " + "дев'ятистам одній тисячі шестистам сімдесяти шести"), + (719094234693663034822824384220291, + "семистам дев'ятнадцяти нонільйонам дев'яноста чотирьом октильйонам " + "двомстам тридцяти чотирьом септильйонам шестистам дев'яноста трьом " + "секстильйонам шестистам шістдесяти трьом квінтильйонам тридцяти " + "чотирьом квадрильйонам восьмистам двадцяти двом трильйонам восьмистам " + "двадцяти чотирьом мільярдам трьомстам вісімдесяти чотирьом мільйонам " + "двомстам двадцяти тисячам двомстам дев'яноста одному") +) + +TEST_CASES_CARDINAL_ACCUSATIVE = ( + (1, "один"), + (2, "два"), + (3, "три"), + (4, "чотири"), + (5, "п'ять"), + (6, "шість"), + (7, "сім"), + (8, "вісім"), + (9, "дев'ять"), + (10, "десять"), + (10.02, "десять кома нуль два"), + (11, "одинадцять"), + (12, "дванадцять"), + (12.40, "дванадцять кома чотири"), + (13, "тринадцять"), + (14, "чотирнадцять"), + (14.13, "чотирнадцять кома тринадцять"), + (15, "п'ятнадцять"), + (16, "шістнадцять"), + (17, "сімнадцять"), + (17.31, "сімнадцять кома тридцять один"), + (18, "вісімнадцять"), + (19, "дев'ятнадцять"), + (20, "двадцять"), + (21, "двадцять один"), + (21.20, "двадцять один кома два"), + (30, "тридцять"), + (32, "тридцять два"), + (40, "сорок"), + (43, "сорок три"), + (43.007, "сорок три кома нуль нуль сім"), + (50, "п'ятдесят"), + (54, "п'ятдесят чотири"), + (60, "шістдесят"), + (60.059, "шістдесят кома нуль п'ятдесят дев'ять"), + (65, "шістдесят п'ять"), + (70, "сімдесят"), + (76, "сімдесят шість"), + (80, "вісімдесят"), + (87, "вісімдесят сім"), + (90, "дев'яносто"), + (98, "дев'яносто вісім"), + (99, "дев'яносто дев'ять"), + (100, "сто"), + (101, "сто один"), + (199, "сто дев'яносто дев'ять"), + (200, "двісті"), + (203, "двісті три"), + (300, "триста"), + (356, "триста п'ятдесят шість"), + (400, "чотириста"), + (434, "чотириста тридцять чотири"), + (500, "п'ятсот"), + (578, "п'ятсот сімдесят вісім"), + (600, "шістсот"), + (689, "шістсот вісімдесят дев'ять"), + (700, "сімсот"), + (729, "сімсот двадцять дев'ять"), + (800, "вісімсот"), + (894, "вісімсот дев'яносто чотири"), + (900, "дев'ятсот"), + (999, "дев'ятсот дев'яносто дев'ять"), + (1000, "одну тисячу"), + (1001, "одну тисячу один"), + (2012, "дві тисячі дванадцять"), + (12519, "дванадцять тисяч п'ятсот дев'ятнадцять"), + (12519.85, "дванадцять тисяч п'ятсот дев'ятнадцять кома вісімдесят п'ять"), + (-260000, "мінус двісті шістдесят тисяч"), + (1000000, "один мільйон"), + (1000000000, "один мільярд"), + (1234567890, "один мільярд двісті тридцять чотири мільйони " + "п'ятсот шістдесят сім тисяч вісімсот дев'яносто"), + (1000000000000, "один трильйон"), + (1000000000000000, "один квадрильйон"), + (1000000000000000000, "один квінтильйон"), + (1000000000000000000000, "один секстильйон"), + (1000000000000000000000000, "один септильйон"), + (1000000000000000000000000000, "один октильйон"), + (1000000000000000000000000000000, "один нонільйон"), + (215461407892039002157189883901676, + "двісті п'ятнадцять нонільйонів чотириста шістдесят один " + "октильйон чотириста сім септильйонів вісімсот дев'яносто " + "два секстильйони тридцять дев'ять квінтильйонів два " + "квадрильйони сто п'ятдесят сім трильйонів сто вісімдесят " + "дев'ять мільярдів вісімсот вісімдесят три мільйони " + "дев'ятсот одну тисячу шістсот сімдесят шість"), + (719094234693663034822824384220291, + "сімсот дев'ятнадцять нонільйонів дев'яносто чотири октильйони " + "двісті тридцять чотири септильйони шістсот дев'яносто три " + "секстильйони шістсот шістдесят три квінтильйони тридцять " + "чотири квадрильйони вісімсот двадцять два трильйони вісімсот " + "двадцять чотири мільярди триста вісімдесят чотири мільйони " + "двісті двадцять тисяч двісті дев'яносто один") +) + +TEST_CASES_CARDINAL_INSTRUMENTAL = ( + (1, "одним"), + (2, "двома"), + (3, "трьома"), + (4, "чотирма"), + (5, "п'ятьма"), + (6, "шістьма"), + (7, "сьома"), + (8, "вісьма"), + (9, "дев'ятьма"), + (10, "десятьма"), + (10.02, "десятьма кома нуль двома"), + (11, "одинадцятьма"), + (12, "дванадцятьма"), + (12.40, "дванадцятьма кома чотирма"), + (13, "тринадцятьма"), + (14, "чотирнадцятьма"), + (14.13, "чотирнадцятьма кома тринадцятьма"), + (15, "п'ятнадцятьма"), + (16, "шістнадцятьма"), + (17, "сімнадцятьма"), + (17.31, "сімнадцятьма кома тридцятьма одним"), + (18, "вісімнадцятьма"), + (19, "дев'ятнадцятьма"), + (20, "двадцятьма"), + (21, "двадцятьма одним"), + (21.20, "двадцятьма одним кома двома"), + (30, "тридцятьма"), + (32, "тридцятьма двома"), + (40, "сорока"), + (43, "сорока трьома"), + (43.007, "сорока трьома кома нуль нуль сьома"), + (50, "п'ятдесятьма"), + (54, "п'ятдесятьма чотирма"), + (60, "шістдесятьма"), + (60.059, "шістдесятьма кома нуль п'ятдесятьма дев'ятьма"), + (65, "шістдесятьма п'ятьма"), + (70, "сімдесятьма"), + (76, "сімдесятьма шістьма"), + (80, "вісімдесятьма"), + (87, "вісімдесятьма сьома"), + (90, "дев'яностами"), + (98, "дев'яностами вісьма"), + (99, "дев'яностами дев'ятьма"), + (100, "стами"), + (101, "стами одним"), + (199, "стами дев'яностами дев'ятьма"), + (200, "двомастами"), + (203, "двомастами трьома"), + (300, "трьомастами"), + (356, "трьомастами п'ятдесятьма шістьма"), + (400, "чотирмастами"), + (434, "чотирмастами тридцятьма чотирма"), + (500, "п'ятьмастами"), + (578, "п'ятьмастами сімдесятьма вісьма"), + (600, "шістьмастами"), + (689, "шістьмастами вісімдесятьма дев'ятьма"), + (700, "сьомастами"), + (729, "сьомастами двадцятьма дев'ятьма"), + (800, "восьмастами"), + (894, "восьмастами дев'яностами чотирма"), + (900, "дев'ятьмастами"), + (999, "дев'ятьмастами дев'яностами дев'ятьма"), + (1000, "однією тисячею"), + (1001, "однією тисячею одним"), + (2012, "двома тисячами дванадцятьма"), + (12519, "дванадцятьма тисячами п'ятьмастами дев'ятнадцятьма"), + (12519.85, "дванадцятьма тисячами п'ятьмастами дев'ятнадцятьма " + "кома вісімдесятьма п'ятьма"), + (-260000, "мінус двомастами шістдесятьма тисячами"), + (1000000, "одним мільйоном"), + (1000000000, "одним мільярдом"), + (1234567890, "одним мільярдом двомастами тридцятьма чотирма мільйонами " + "п'ятьмастами шістдесятьма сьома тисячами восьмастами " + "дев'яностами"), + (1000000000000, "одним трильйоном"), + (1000000000000000, "одним квадрильйоном"), + (1000000000000000000, "одним квінтильйоном"), + (1000000000000000000000, "одним секстильйоном"), + (1000000000000000000000000, "одним септильйоном"), + (1000000000000000000000000000, "одним октильйоном"), + (1000000000000000000000000000000, "одним нонільйоном"), + (215461407892039002157189883901676, + "двомастами п'ятнадцятьма нонільйонів чотирмастами шістдесятьма одним " + "октильйоном чотирмастами сьома септильйонів восьмастами дев'яностами " + "двома секстильйонами тридцятьма дев'ятьма квінтильйонів двома " + "квадрильйонами стами п'ятдесятьма сьома трильйонів стами вісімдесятьма " + "дев'ятьма мільярдів восьмастами вісімдесятьма трьома мільйонами " + "дев'ятьмастами однією тисячею шістьмастами сімдесятьма шістьма"), + (719094234693663034822824384220291, + "сьомастами дев'ятнадцятьма нонільйонів дев'яностами чотирма " + "октильйонами двомастами тридцятьма чотирма септильйонами шістьмастами " + "дев'яностами трьома секстильйонами шістьмастами шістдесятьма трьома " + "квінтильйонами тридцятьма чотирма квадрильйонами восьмастами двадцятьма " + "двома трильйонами восьмастами двадцятьма чотирма мільярдами трьомастами " + "вісімдесятьма чотирма мільйонами двомастами двадцятьма тисячами " + "двомастами дев'яностами одним") +) + +TEST_CASES_CARDINAL_LOCATIVE = ( + (1, "одному"), + (2, "двох"), + (3, "трьох"), + (4, "чотирьох"), + (5, "п'яти"), + (6, "шести"), + (7, "семи"), + (8, "восьми"), + (9, "дев'яти"), + (10, "десяти"), + (10.02, "десяти кома нуль двох"), + (11, "одинадцяти"), + (12, "дванадцяти"), + (12.40, "дванадцяти кома чотирьох"), + (13, "тринадцяти"), + (14, "чотирнадцяти"), + (14.13, "чотирнадцяти кома тринадцяти"), + (15, "п'ятнадцяти"), + (16, "шістнадцяти"), + (17, "сімнадцяти"), + (17.31, "сімнадцяти кома тридцяти одному"), + (18, "вісімнадцяти"), + (19, "дев'ятнадцяти"), + (20, "двадцяти"), + (21, "двадцяти одному"), + (21.20, "двадцяти одному кома двох"), + (30, "тридцяти"), + (32, "тридцяти двох"), + (40, "сорока"), + (43, "сорока трьох"), + (43.007, "сорока трьох кома нуль нуль семи"), + (50, "п'ятдесяти"), + (54, "п'ятдесяти чотирьох"), + (60, "шістдесяти"), + (60.059, "шістдесяти кома нуль п'ятдесяти дев'яти"), + (65, "шістдесяти п'яти"), + (70, "сімдесяти"), + (76, "сімдесяти шести"), + (80, "вісімдесяти"), + (87, "вісімдесяти семи"), + (90, "дев'яноста"), + (98, "дев'яноста восьми"), + (99, "дев'яноста дев'яти"), + (100, "стах"), + (101, "стах одному"), + (199, "стах дев'яноста дев'яти"), + (200, "двохстах"), + (203, "двохстах трьох"), + (300, "трьохстах"), + (356, "трьохстах п'ятдесяти шести"), + (400, "чотирьохстах"), + (434, "чотирьохстах тридцяти чотирьох"), + (500, "п'ятистах"), + (578, "п'ятистах сімдесяти восьми"), + (600, "шестистах"), + (689, "шестистах вісімдесяти дев'яти"), + (700, "семистах"), + (729, "семистах двадцяти дев'яти"), + (800, "восьмистах"), + (894, "восьмистах дев'яноста чотирьох"), + (900, "дев'ятистах"), + (999, "дев'ятистах дев'яноста дев'яти"), + (1000, "одній тисячі"), + (1001, "одній тисячі одному"), + (2012, "двох тисячах дванадцяти"), + (12519, "дванадцяти тисячах п'ятистах дев'ятнадцяти"), + (12519.85, "дванадцяти тисячах п'ятистах дев'ятнадцяти " + "кома вісімдесяти п'яти"), + (-260000, "мінус двохстах шістдесяти тисячах"), + (1000000, "одному мільйоні"), + (1000000000, "одному мільярді"), + (1234567890, "одному мільярді двохстах тридцяти чотирьох мільйонах " + "п'ятистах шістдесяти семи тисячах восьмистах дев'яноста"), + (1000000000000, "одному трильйоні"), + (1000000000000000, "одному квадрильйоні"), + (1000000000000000000, "одному квінтильйоні"), + (1000000000000000000000, "одному секстильйоні"), + (1000000000000000000000000, "одному септильйоні"), + (1000000000000000000000000000, "одному октильйоні"), + (1000000000000000000000000000000, "одному нонільйоні"), + (215461407892039002157189883901676, + "двохстах п'ятнадцяти нонільйонах чотирьохстах шістдесяти одному " + "октильйоні чотирьохстах семи септильйонах восьмистах дев'яноста " + "двох секстильйонах тридцяти дев'яти квінтильйонах двох " + "квадрильйонах стах п'ятдесяти семи трильйонах стах вісімдесяти " + "дев'яти мільярдах восьмистах вісімдесяти трьох мільйонах " + "дев'ятистах одній тисячі шестистах сімдесяти шести"), + (719094234693663034822824384220291, + "семистах дев'ятнадцяти нонільйонах дев'яноста чотирьох октильйонах " + "двохстах тридцяти чотирьох септильйонах шестистах дев'яноста трьох " + "секстильйонах шестистах шістдесяти трьох квінтильйонах тридцяти " + "чотирьох квадрильйонах восьмистах двадцяти двох трильйонах восьмистах " + "двадцяти чотирьох мільярдах трьохстах вісімдесяти чотирьох мільйонах " + "двохстах двадцяти тисячах двохстах дев'яноста одному") +) + TEST_CASES_ORDINAL = ( (1, "перший"), (2, "другий"), @@ -2449,10 +3031,50 @@ class Num2WordsUKTest(TestCase): + def test_to_cardinal(self): for test in TEST_CASES_CARDINAL: self.assertEqual(num2words(test[0], lang='uk'), test[1]) + def test_to_cardinal_feminine(self): + for test in TEST_CASES_CARDINAL_FEMININE: + word = num2words(test[0], lang='uk', gender='feminine') + self.assertEqual(word, test[1]) + + def test_to_cardinal_nominative(self): + for test in TEST_CASES_CARDINAL: + word = num2words(test[0], lang='uk', case='nominative') + self.assertEqual(word, test[1]) + + def test_to_cardinal_genitive(self): + for test in TEST_CASES_CARDINAL_GENITIVE: + word = num2words(test[0], lang='uk', case='genitive') + self.assertEqual(word, test[1]) + + def test_to_cardinal_dative(self): + self.maxDiff = None + for test in TEST_CASES_CARDINAL_DATIVE: + word = num2words(test[0], lang='uk', case='dative') + self.assertEqual(word, test[1]) + + def test_to_cardinal_accusative(self): + self.maxDiff = None + for test in TEST_CASES_CARDINAL_ACCUSATIVE: + word = num2words(test[0], lang='uk', case='accusative') + self.assertEqual(word, test[1]) + + def test_to_cardinal_instrumental(self): + self.maxDiff = None + for test in TEST_CASES_CARDINAL_INSTRUMENTAL: + word = num2words(test[0], lang='uk', case='instrumental') + self.assertEqual(word, test[1]) + + def test_to_cardinal_locative(self): + self.maxDiff = None + for test in TEST_CASES_CARDINAL_LOCATIVE: + word = num2words(test[0], lang='uk', case='locative') + self.assertEqual(word, test[1]) + def test_to_ordinal(self): for test in TEST_CASES_ORDINAL: self.assertEqual(