338 lines
11 KiB
Python
338 lines
11 KiB
Python
# Copyright (c) 2003, Taro Ogawa. All Rights Reserved.
|
||
# Copyright (c) 2013, Savoir-faire Linux inc. All Rights Reserved.
|
||
# Copyright (c) 2022, Sergei Ruzki. 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 = "нуль"
|
||
|
||
ONES_FEMININE = {
|
||
1: "адна",
|
||
2: "дзве",
|
||
3: "тры",
|
||
4: "чатыры",
|
||
5: "пяць",
|
||
6: "шэсць",
|
||
7: "сем",
|
||
8: "восем",
|
||
9: "дзевяць",
|
||
}
|
||
|
||
ONES = {
|
||
"f": {
|
||
1: "адна",
|
||
2: "дзве",
|
||
3: "тры",
|
||
4: "чатыры",
|
||
5: "пяць",
|
||
6: "шэсць",
|
||
7: "сем",
|
||
8: "восем",
|
||
9: "дзевяць",
|
||
},
|
||
"m": {
|
||
1: "адзін",
|
||
2: "два",
|
||
3: "тры",
|
||
4: "чатыры",
|
||
5: "пяць",
|
||
6: "шэсць",
|
||
7: "сем",
|
||
8: "восем",
|
||
9: "дзевяць",
|
||
},
|
||
"n": {
|
||
1: "адно",
|
||
2: "два",
|
||
3: "тры",
|
||
4: "чатыры",
|
||
5: "пяць",
|
||
6: "шэсць",
|
||
7: "сем",
|
||
8: "восем",
|
||
9: "дзевяць",
|
||
},
|
||
}
|
||
|
||
TENS = {
|
||
0: "дзесяць",
|
||
1: "адзінаццаць",
|
||
2: "дванаццаць",
|
||
3: "трынаццаць",
|
||
4: "чатырнаццаць",
|
||
5: "пятнаццаць",
|
||
6: "шаснаццаць",
|
||
7: "сямнаццаць",
|
||
8: "васямнаццаць",
|
||
9: "дзевятнаццаць",
|
||
}
|
||
|
||
TWENTIES = {
|
||
2: "дваццаць",
|
||
3: "трыццаць",
|
||
4: "сорак",
|
||
5: "пяцьдзясят",
|
||
6: "шэсцьдзясят",
|
||
7: "семдзесят",
|
||
8: "восемдзесят",
|
||
9: "дзевяноста",
|
||
}
|
||
|
||
HUNDREDS = {
|
||
1: "сто",
|
||
2: "дзвесце",
|
||
3: "трыста",
|
||
4: "чатырыста",
|
||
5: "пяцьсот",
|
||
6: "шэсцьсот",
|
||
7: "семсот",
|
||
8: "восемсот",
|
||
9: "дзевяцьсот",
|
||
}
|
||
|
||
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
|
||
}
|
||
|
||
|
||
class Num2Word_BY(Num2Word_Base):
|
||
CURRENCY_FORMS = {
|
||
"RUB": (
|
||
("расійскі рубель", "расійскія рублі", "расійскіх рублёў"),
|
||
("капейка", "капейкі", "капеек"),
|
||
),
|
||
"EUR": (("еўра", "еўра", "еўра"), ("цэнт", "цэнты", "цэнтаў")),
|
||
"USD": (("долар", "долары", "долараў"), ("цэнт", "цэнты", "цэнтаў")),
|
||
"UAH": (
|
||
("грыўна", "грыўны", "грыўнаў"),
|
||
("капейка", "капейкі", "капеек"),
|
||
),
|
||
"KZT": (("тэнге", "тэнге", "тэнге"), ("тыйін", "тыйіны", "тыйінаў")),
|
||
"BYN": (
|
||
("беларускі рубель", "беларускія рублі", "беларускіх рублёў"),
|
||
("капейка", "капейкі", "капеек"),
|
||
),
|
||
"UZS": (("сум", "сумы", "сумаў"), ("тыйін", "тыйіны", "тыйінаў")),
|
||
}
|
||
|
||
def setup(self):
|
||
self.negword = "мінус"
|
||
self.pointword = "коска"
|
||
self.ords = {
|
||
"нуль": "нулявы",
|
||
"адзін": "першы",
|
||
"два": "другі",
|
||
"тры": "трэці",
|
||
"чатыры": "чацвёрты",
|
||
"пяць": "пяты",
|
||
"шэсць": "шосты",
|
||
"сем": "сёмы",
|
||
"восем": "восьмы",
|
||
"дзевяць": "дзявяты",
|
||
"сто": "соты",
|
||
"тысяча": "тысячны",
|
||
}
|
||
|
||
self.ords_adjective = {
|
||
"адзін": "адна",
|
||
"адна": "адна",
|
||
"дзве": "двух",
|
||
"тры": "трох",
|
||
"чатыры": "чатырох",
|
||
"пяць": "пяці",
|
||
"шэсць": "шасці",
|
||
"сем": "сямі",
|
||
"восем": "васьмі",
|
||
"дзевяць": "дзевяцi",
|
||
"сто": "ста",
|
||
}
|
||
|
||
def to_cardinal(self, number, gender="m"):
|
||
n = str(number).replace(",", ".")
|
||
if "." in n:
|
||
left, right = n.split(".")
|
||
if set(right) == {"0"}:
|
||
leading_zero_count = 0
|
||
else:
|
||
leading_zero_count = len(right) - len(right.lstrip("0"))
|
||
|
||
decimal_part = (ZERO + " ") * leading_zero_count + self._int2word(
|
||
int(right), gender
|
||
)
|
||
return "{} {} {}".format(
|
||
self._int2word(int(left), gender), self.pointword, decimal_part
|
||
)
|
||
else:
|
||
return self._int2word(int(n), gender)
|
||
|
||
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]
|
||
|
||
def to_ordinal(self, number, gender="m"):
|
||
self.verify_ordinal(number)
|
||
outwords = self.to_cardinal(number, gender).split(" ")
|
||
lastword = outwords[-1].lower()
|
||
try:
|
||
if len(outwords) > 1:
|
||
if outwords[-2] in self.ords_adjective:
|
||
outwords[-2] = self.ords_adjective.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_adjective:
|
||
lastword = (
|
||
self.ords_adjective.get(lastword[:-3], lastword) + "соты"
|
||
)
|
||
elif lastword[-5:] == "шэсць":
|
||
lastword = "шосты"
|
||
elif lastword[-7:] == "дзесяць":
|
||
lastword = "дзясяты"
|
||
elif lastword[-9:] == "семдзесят":
|
||
lastword = "сямідзясяты"
|
||
elif lastword[-1] == "ь" or lastword[-2] == "ц":
|
||
lastword = lastword[:-2] + "ты"
|
||
elif lastword[-1] == "к":
|
||
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] + "ны"
|
||
|
||
if gender == "f":
|
||
if lastword[-2:] in [
|
||
"ці",
|
||
]:
|
||
lastword = lastword[:-2] + "цяя"
|
||
else:
|
||
lastword = lastword[:-1] + "ая"
|
||
|
||
if gender == "n":
|
||
if lastword[-2:] in [
|
||
"ці", "ца"
|
||
]:
|
||
lastword = lastword[:-2] + "цяе"
|
||
else:
|
||
lastword = lastword[:-1] + "ае"
|
||
|
||
outwords[-1] = self.title(lastword)
|
||
if len(outwords) == 2 and "адна" in outwords[-2]:
|
||
outwords[-2] = outwords[-1]
|
||
del outwords[-1]
|
||
|
||
if len(outwords) > 2 and "тысяч" in outwords[-1]:
|
||
if 'сорак' in outwords[-3]:
|
||
outwords[-3] = outwords[-3].replace('сорак', 'сарака')
|
||
outwords[-3] = outwords[-3] + outwords[-2] + outwords[-1]
|
||
del outwords[-1]
|
||
del outwords[-1]
|
||
|
||
elif len(outwords) > 1 and "тысяч" in outwords[-1]:
|
||
outwords[-2] = outwords[-2] + outwords[-1]
|
||
del outwords[-1]
|
||
return " ".join(outwords).strip()
|
||
|
||
def _money_verbose(self, number, currency):
|
||
gender = "m"
|
||
if currency == "UAH":
|
||
gender = "f"
|
||
|
||
return self._int2word(number, gender)
|
||
|
||
def _cents_verbose(self, number, currency):
|
||
if currency in ("UAH", "RUB", "BYN"):
|
||
gender = "f"
|
||
else:
|
||
gender = "m"
|
||
|
||
return self._int2word(number, gender)
|
||
|
||
def _int2word(self, n, gender="m"):
|
||
if isinstance(gender, bool) and gender:
|
||
gender = "f"
|
||
if n < 0:
|
||
return " ".join([self.negword, self._int2word(abs(n), gender)])
|
||
|
||
if n == 0:
|
||
return ZERO
|
||
|
||
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)
|
||
|
||
if n3 > 0:
|
||
words.append(HUNDREDS[n3])
|
||
|
||
if n2 > 1:
|
||
words.append(TWENTIES[n2])
|
||
|
||
if n2 == 1:
|
||
words.append(TENS[n1])
|
||
elif n1 > 0:
|
||
if i == 0:
|
||
ones = ONES[gender]
|
||
elif i == 1:
|
||
ones = ONES["f"] # Thousands are feminine
|
||
else:
|
||
ones = ONES["m"]
|
||
|
||
words.append(ones[n1])
|
||
|
||
if i > 0:
|
||
words.append(self.pluralize(x, THOUSANDS[i]))
|
||
|
||
return " ".join(words)
|