522 lines
16 KiB
Python
522 lines
16 KiB
Python
# -*- coding: utf-8 -*-
|
||
# Copyright (c) 2023, Johannes Heinecke. 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 .currency import parse_currency_parts
|
||
from .lang_EU import Num2Word_EU
|
||
|
||
# Chechen numbers inflect in case if without noun or
|
||
# use a special oblique ending when followed by a counted noun
|
||
# 4, 14, 40 and composites thereof agree in class (gender) with the
|
||
# noun. Chechen has 6 classes which are indicated by the initial
|
||
# letter of 4, 14 and 40. By default it is "д" but
|
||
# it can also be "б", "й" or "в".
|
||
# Indicate the needed class prefix as follows
|
||
# num2words(4, lang='ce', case="abs", clazz="б")
|
||
|
||
|
||
CARDINALS = {
|
||
"casenames": {
|
||
"abs": "Им.",
|
||
"gen": "Род.",
|
||
"dat": "Дат.",
|
||
"erg": "Эрг;",
|
||
"instr": "Твор.",
|
||
"mat": "Вещ.",
|
||
"comp": "Сравнит.",
|
||
"all": "Местн.",
|
||
},
|
||
"casesuffix_cons": { # to be added to numerals with final consonant
|
||
"gen": "аннан",
|
||
"dat": "анна",
|
||
"erg": "амма",
|
||
"instr": "анца",
|
||
"mat": "аннах",
|
||
"comp": "аннал",
|
||
"all": "анга",
|
||
"obl": "ан",
|
||
"ORD": "алгӀа",
|
||
},
|
||
"casesuffix_voc": { # to be added to numerals with final vowel
|
||
"gen": "ннан",
|
||
"dat": "нна",
|
||
"erg": "мма",
|
||
"instr": "нца",
|
||
"mat": "ннах",
|
||
"comp": "ннал",
|
||
"all": "нга",
|
||
"obl": "н",
|
||
"ORD": "лгӀа",
|
||
},
|
||
0: {
|
||
"attr": "ноль",
|
||
"abs": "ноль",
|
||
"gen": "нолан",
|
||
"dat": "нолана",
|
||
"erg": "ноло",
|
||
"instr": "ноланца",
|
||
"mat": "ноланах",
|
||
"comp": "ноланал",
|
||
"all": "ноланга",
|
||
},
|
||
1: {
|
||
"attr": "цхьа", # in front of nouns in ABS
|
||
"obl": "цхьана", # with nouns in other cases than ABS
|
||
"abs": "цхьаъ",
|
||
"gen": "цхьаннан",
|
||
"dat": "цхьанна",
|
||
"erg": "цхьамма",
|
||
"instr": "цхьаьнца",
|
||
"mat": "цхьаннах",
|
||
"comp": "цхьаннал",
|
||
"all": "цхаьнга",
|
||
"ORD": "цхьалгӀа",
|
||
},
|
||
2: {
|
||
"attr": "ши", # in front of 100, 1000
|
||
"obl": "шина",
|
||
"abs": "шиъ",
|
||
"gen": "шиннан",
|
||
"dat": "шинна",
|
||
"erg": "шимма",
|
||
"instr": "шинца",
|
||
"mat": "шиннах",
|
||
"comp": "шиннал",
|
||
"all": "шинга",
|
||
"ORD": "шолгӀа",
|
||
},
|
||
3: {
|
||
"attr": "кхо",
|
||
"obl": "кхона",
|
||
"abs": "кхоъ",
|
||
"gen": "кхааннан",
|
||
"dat": "кхаанна",
|
||
"erg": "кхаамма",
|
||
"instr": "кхаанца",
|
||
"mat": "кхааннах",
|
||
"comp": "кхааннал",
|
||
"all": "кхаанга",
|
||
"ORD": "кхоалгӀа",
|
||
},
|
||
4: {
|
||
"attr": "д*и",
|
||
"obl": "д*еа",
|
||
"abs": "д*иъ",
|
||
"gen": "д*еаннан",
|
||
"dat": "д*еанна",
|
||
"erg": "д*еамма",
|
||
"instr": "д*еанца",
|
||
"mat": "д*еаннах",
|
||
"comp": "д*еаннал",
|
||
"all": "д*еанга",
|
||
"ORD": "д*оьалгӀа",
|
||
},
|
||
5: {
|
||
"attr": "пхи",
|
||
"obl": "пхеа",
|
||
"abs": "пхиъ",
|
||
"gen": "пхеаннан",
|
||
"dat": "пхеанна",
|
||
"erg": "пхеамма",
|
||
"instr": "нхеанца",
|
||
"mat": "пхеаннах",
|
||
"comp": "пхеаннал",
|
||
"all": "пхеанга",
|
||
"ORD": "пхоьалгӀа",
|
||
},
|
||
6: {
|
||
"abs": "ялх",
|
||
"attr": "ялх",
|
||
"ORD": "йолхалгӀа",
|
||
},
|
||
7: {
|
||
"abs": "ворхӀ",
|
||
"attr": "ворхӀ",
|
||
"ORD": "ворхӀалгӀа",
|
||
},
|
||
8: {
|
||
"abs": "бархӀ",
|
||
"attr": "бархӀ",
|
||
"ORD": "борхӀалӀа",
|
||
},
|
||
9: {
|
||
"abs": "исс",
|
||
"attr": "исс",
|
||
"ORD": "уьссалгӀа",
|
||
},
|
||
10: {
|
||
"attr": "итт",
|
||
"abs": "итт",
|
||
"gen": "иттаннан",
|
||
"dat": "иттанна",
|
||
"erg": "иттамма",
|
||
"instr": "иттанца",
|
||
"mat": "иттаннах",
|
||
"comp": "иттаннал",
|
||
"all": "иттанга",
|
||
"ORD": "уьтталгӀа",
|
||
},
|
||
11: {
|
||
"abs": "цхьайтта",
|
||
"attr": "цхьайтта",
|
||
"ORD": "цхьайтталгӀа",
|
||
},
|
||
12: {
|
||
"abs": "шийтта",
|
||
"attr": "шийтта",
|
||
"ORD": "шийтталга",
|
||
},
|
||
13: {
|
||
"abs": "кхойтта",
|
||
"attr": "кхойтта",
|
||
"ORD": "кхойтталгӀа",
|
||
},
|
||
14: {
|
||
"abs": "д*ейтта",
|
||
"attr": "д*ейтта",
|
||
"ORD": "д*ейтталгӀа",
|
||
},
|
||
15: {
|
||
"abs": "пхийтта",
|
||
"attr": "пхийтта",
|
||
"ORD": "пхийтталгӀа",
|
||
},
|
||
16: {
|
||
"abs": "ялхитта",
|
||
"attr": "ялхитта",
|
||
"ORD": "ялхитталгӀа",
|
||
},
|
||
17: {
|
||
"abs": "вуьрхӀитта",
|
||
"attr": "вуьрхӀитта",
|
||
"ORD": "вуьрхӀитталгӀа",
|
||
},
|
||
18: {
|
||
"abs": "берхӀитта",
|
||
"attr": "берхӀитта",
|
||
"ORD": "берхитталӀа",
|
||
},
|
||
19: {
|
||
"abs": "ткъайесна",
|
||
"attr": "ткъайесна",
|
||
"ORD": "ткъаесналгӀа",
|
||
},
|
||
20: {
|
||
"abs": "ткъа",
|
||
"gen": "ткъаннан",
|
||
"dat": "ткъанна",
|
||
"erg": "ткъамма",
|
||
"instr": "ткъанца",
|
||
"mat": "ткъаннах",
|
||
"comp": "ткъаннал",
|
||
"all": "ткъанга",
|
||
"attr": "ткъе",
|
||
"ORD": "ткъолгӀа",
|
||
},
|
||
40: {
|
||
"abs": "шовзткъа",
|
||
"attr": "шовзткъе",
|
||
"ORD": "шовзткъалгІа",
|
||
},
|
||
60: {
|
||
"abs": "кхузткъа",
|
||
"attr": "кхузткъе",
|
||
"ORD": "кхузткъалгІа",
|
||
},
|
||
80: {
|
||
"abs": "дезткъа",
|
||
"attr": "дезткъе",
|
||
"ORD": "дезткъалгІа",
|
||
},
|
||
100: {
|
||
"attr": "бӀе",
|
||
"abs": "бӀе",
|
||
"obl": "бӀен",
|
||
"gen": "бӀеннан",
|
||
"dat": "бӀенна",
|
||
"erg": "бӀемма",
|
||
"instr": "бӀенца",
|
||
"mat": "бӀеннах",
|
||
"comp": "бӀеннал",
|
||
"all": "бӀенга",
|
||
"ORD": "бІолгІа",
|
||
},
|
||
1000: {
|
||
"attr": "эзар",
|
||
"abs": "эзар",
|
||
"obl": "эзаран",
|
||
"gen": "эзарнан",
|
||
"dat": "эзарна",
|
||
"erg": "эзарно",
|
||
"instr": "эзарнаца",
|
||
"mat": "эзарнах",
|
||
"comp": "эзарнал",
|
||
"all": "эзаранга",
|
||
"ORD": "эзарлагІа",
|
||
},
|
||
1000000: {
|
||
"attr": "миллион",
|
||
"abs": "миллион",
|
||
"ORD": "миллионалгІа",
|
||
},
|
||
}
|
||
|
||
ILLIONS = {
|
||
6: {
|
||
"attr": "миллион",
|
||
"abs": "миллион",
|
||
"ORD": "миллионалгІа",
|
||
},
|
||
9: {
|
||
"attr": "миллиард",
|
||
"abs": "миллиард",
|
||
"ORD": "миллиардалгІа",
|
||
},
|
||
12: {
|
||
"attr": "биллион",
|
||
"abs": "биллион",
|
||
"ORD": "биллионалгІа",
|
||
},
|
||
15: {
|
||
"attr": "биллиард",
|
||
"abs": "биллиард",
|
||
"ORD": "биллиардалгІа",
|
||
},
|
||
18: {
|
||
"attr": "триллион",
|
||
"abs": "триллион",
|
||
"ORD": "триллионалгІа",
|
||
},
|
||
21: {
|
||
"attr": "триллиард",
|
||
"abs": "триллиард",
|
||
"ORD": "триллиардалгІа",
|
||
},
|
||
24: {
|
||
"attr": "квадриллион",
|
||
"abs": "квадриллион",
|
||
"ORD": "квадриллионалгІа",
|
||
},
|
||
27: {
|
||
"attr": "квадриллиард",
|
||
"abs": "квадриллиард",
|
||
"ORD": "квадриллиардалгІа",
|
||
},
|
||
30: {
|
||
"attr": "квинтиллион",
|
||
"abs": "квинтиллион",
|
||
"ORD": "квинтиллионалгІа",
|
||
},
|
||
33: {
|
||
"attr": "квинтиллиард",
|
||
"abs": "квинтиллиард",
|
||
"ORD": "квинтиллиардалгІа",
|
||
},
|
||
}
|
||
|
||
|
||
MINUS = "минус"
|
||
# DECIMALPOINT = "запятая" # check !
|
||
DECIMALPOINT = "а"
|
||
|
||
|
||
class Num2Word_CE(Num2Word_EU):
|
||
CURRENCY_FORMS = {
|
||
# currency code: (sg, pl), (sg, pl)
|
||
"EUR": (("Евро", "Евро"), ("Сент", "Сенташ")),
|
||
"RUB": (("Сом", "Сомаш"), ("Кепек", "Кепекаш")),
|
||
"USD": (("Доллар", "Доллараш"), ("Сент", "Сенташ")),
|
||
"GBP": (("Фунт", "Фунташ"), ("Пенни", "Пенни")),
|
||
}
|
||
|
||
def setup(self):
|
||
Num2Word_EU.setup(self)
|
||
self.negword = "минус"
|
||
self.pointword = "запятая" # check !
|
||
# self.errmsg_nonnum = (
|
||
# u"Seulement des nombres peuvent être convertis en mots."
|
||
# )
|
||
# self.errmsg_toobig = (
|
||
# u"Nombre trop grand pour être converti en mots (abs(%s) > %s)."
|
||
# )
|
||
# self.exclude_title = ["et", "virgule", "moins"]
|
||
self.mid_numwords = []
|
||
self.low_numwords = []
|
||
self.ords = {}
|
||
|
||
def to_ordinal(self, number, clazz="д"):
|
||
# implement here your code. number is the integer to
|
||
# be transformed into an ordinal as a word (str)
|
||
# which is returned
|
||
return self.to_cardinal(number, clazz=clazz, case="ORD")
|
||
|
||
def to_cardinal(self, number, clazz="д", case="abs"):
|
||
if isinstance(number, float):
|
||
entires = self.to_cardinal(int(number))
|
||
float_part = str(number).split(".")[1]
|
||
postfix = " ".join(
|
||
# Drops the trailing zero and comma
|
||
[self.to_cardinal(int(c)) for c in float_part]
|
||
)
|
||
return entires + " " + DECIMALPOINT + " " + postfix
|
||
|
||
elif number < 20:
|
||
return self.makecase(number, case, clazz)
|
||
elif number < 100:
|
||
twens = number // 20
|
||
units = number % 20
|
||
base = twens * 20
|
||
if units == 0:
|
||
return self.makecase(number, case, clazz)
|
||
else:
|
||
twenties = self.makecase(base, "attr", clazz)
|
||
rest = self.to_cardinal(units, clazz=clazz, case=case)
|
||
return twenties + " " + rest.replace("д*", clazz)
|
||
elif number < 1000:
|
||
hundreds = number // 100
|
||
tens = number % 100
|
||
if hundreds > 1:
|
||
hundert = (
|
||
CARDINALS[hundreds]["attr"].replace("д*", clazz) + " "
|
||
)
|
||
else:
|
||
hundert = ""
|
||
if tens != 0:
|
||
rest = self.to_cardinal(tens, clazz=clazz, case=case)
|
||
return hundert + CARDINALS[100]["abs"] + " " + rest
|
||
else:
|
||
return hundert + self.makecase(100, case, clazz)
|
||
elif number < 1000000:
|
||
thousands = number // 1000
|
||
hundert = number % 1000
|
||
if hundert > 0:
|
||
tcase = "attr"
|
||
else:
|
||
tcase = case
|
||
if thousands > 1:
|
||
tausend = (
|
||
self.to_cardinal(thousands, clazz=clazz, case="attr")
|
||
+ " "
|
||
+ CARDINALS[1000][tcase]
|
||
)
|
||
else:
|
||
tausend = self.makecase(1000, tcase, clazz)
|
||
|
||
if hundert != 0:
|
||
rest = " " + self.to_cardinal(hundert, clazz=clazz, case=case)
|
||
else:
|
||
rest = ""
|
||
return tausend + rest
|
||
|
||
elif number < 10**34:
|
||
out = []
|
||
for pot in reversed([6, 9, 12, 15, 18, 21, 24, 27, 30, 33]):
|
||
# 3 digits of billion, trillion etc
|
||
step = number // 10**pot % 1000
|
||
if step > 0:
|
||
words = self.to_cardinal(step, clazz=clazz, case="attr")
|
||
out.append(words + " " + ILLIONS[pot]["attr"])
|
||
rest = number % 10**6
|
||
if rest:
|
||
out.append(self.to_cardinal(rest, clazz=clazz, case=case))
|
||
return " ".join(out)
|
||
|
||
return "NOT IMPLEMENTED"
|
||
|
||
def _money_verbose(self, number, currency, case):
|
||
mcase = "attr"
|
||
if case != "abs":
|
||
mcase = "obl"
|
||
return self.to_cardinal(number, case=mcase)
|
||
|
||
def _cents_verbose(self, number, currency, case):
|
||
mcase = "attr"
|
||
if case != "abs":
|
||
mcase = "obl"
|
||
return self.to_cardinal(number, case=mcase)
|
||
|
||
def to_currency(
|
||
self,
|
||
val,
|
||
currency="RUB",
|
||
cents=True,
|
||
separator=",",
|
||
adjective=False,
|
||
case="abs",
|
||
):
|
||
"""
|
||
Args:
|
||
val: Numeric value
|
||
currency (str): Currency code
|
||
cents (bool): Verbose cents
|
||
separator (str): Cent separator
|
||
adjective (bool): Prefix currency name with adjective
|
||
Returns:
|
||
str: Formatted string
|
||
|
||
"""
|
||
left, right, is_negative = parse_currency_parts(val)
|
||
|
||
try:
|
||
cr1, cr2 = self.CURRENCY_FORMS[currency]
|
||
devise = cr1[0]
|
||
centime = cr2[0]
|
||
except KeyError:
|
||
raise NotImplementedError(
|
||
'Currency code "%s" not implemented for "%s"'
|
||
% (currency, self.__class__.__name__)
|
||
)
|
||
|
||
minus_str = "%s " % self.negword.strip() if is_negative else ""
|
||
money_str = self._money_verbose(left, currency, case)
|
||
cents_str = (
|
||
self._cents_verbose(right, currency, case)
|
||
if cents
|
||
else self._cents_terse(right, currency)
|
||
)
|
||
|
||
return "%s%s %s%s %s %s" % (
|
||
minus_str,
|
||
money_str,
|
||
devise, # always singular
|
||
separator,
|
||
cents_str,
|
||
centime,
|
||
)
|
||
|
||
def to_ordinal_num(self, number):
|
||
self.verify_ordinal(number)
|
||
return str(number) + "-й"
|
||
|
||
def to_year(self, year, case="abs"):
|
||
return self.to_cardinal(year, case=case)
|
||
|
||
def makecase(self, number, case, clazz):
|
||
# print("ZZZZ", number, CARDINALS[number])
|
||
if case in CARDINALS[number]:
|
||
return CARDINALS[number][case].replace("д*", clazz)
|
||
else:
|
||
if CARDINALS[number]["abs"][-1] in "а":
|
||
return (
|
||
CARDINALS[number]["abs"].replace("д*", clazz)
|
||
+ CARDINALS["casesuffix_voc"][case]
|
||
)
|
||
else:
|
||
return (
|
||
CARDINALS[number]["abs"].replace("д*", clazz)
|
||
+ CARDINALS["casesuffix_cons"][case]
|
||
)
|