chyslivnyk 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
chyslivnyk/__init__.py
ADDED
@@ -0,0 +1,978 @@
|
|
1
|
+
"""
|
2
|
+
Chyslivnyk - Бібліотека для генерації українських числівників у відповідній граматичній формі.
|
3
|
+
|
4
|
+
Підтримує:
|
5
|
+
- Кількісні числівники (цілі, збірні, дробові)
|
6
|
+
- Порядкові числівники
|
7
|
+
- Відмінки, роди, числа (де застосовно)
|
8
|
+
"""
|
9
|
+
|
10
|
+
# --- Константы для граматичних параметрів ---
|
11
|
+
|
12
|
+
# Відмінки
|
13
|
+
CASE_NOMINATIVE = "називний" # Називний відмінок
|
14
|
+
CASE_GENITIVE = "родовий" # Родовий відмінок
|
15
|
+
CASE_DATIVE = "давальний" # Давальний відмінок
|
16
|
+
CASE_ACCUSATIVE = "знахідний" # Знахідний відмінок
|
17
|
+
CASE_INSTRUMENTAL = "орудний" # Орудний відмінок
|
18
|
+
CASE_LOCATIVE = "місцевий" # Місцевий відмінок
|
19
|
+
|
20
|
+
# Роди
|
21
|
+
GENDER_MASCULINE = "чоловічий" # Чоловічий рід
|
22
|
+
GENDER_FEMININE = "жіночий" # Жіночий рід
|
23
|
+
GENDER_NEUTER = "середній" # Середній рід
|
24
|
+
|
25
|
+
# Числа
|
26
|
+
NUMBER_SINGULAR = "однина" # Однина
|
27
|
+
NUMBER_PLURAL = "множина" # Множина
|
28
|
+
|
29
|
+
|
30
|
+
class Chyslivnyk:
|
31
|
+
# --- Внутрішні словники для відмінювання ---
|
32
|
+
# Зверніть увагу, що ці словники є базовими і можуть потребувати розширення
|
33
|
+
# для абсолютної точності для всіх числівників та їх форм.
|
34
|
+
|
35
|
+
_ONES = {
|
36
|
+
0: "", # Використовується як порожній рядок для формування складених
|
37
|
+
1: {
|
38
|
+
GENDER_MASCULINE: {CASE_NOMINATIVE: "один", CASE_GENITIVE: "одного", CASE_DATIVE: "одному",
|
39
|
+
CASE_ACCUSATIVE: "один", CASE_INSTRUMENTAL: "одним", CASE_LOCATIVE: "одному"},
|
40
|
+
GENDER_FEMININE: {CASE_NOMINATIVE: "одна", CASE_GENITIVE: "однієї", CASE_DATIVE: "одній",
|
41
|
+
CASE_ACCUSATIVE: "одну", CASE_INSTRUMENTAL: "однією", CASE_LOCATIVE: "одній"},
|
42
|
+
GENDER_NEUTER: {CASE_NOMINATIVE: "одне", CASE_GENITIVE: "одного", CASE_DATIVE: "одному",
|
43
|
+
CASE_ACCUSATIVE: "одне", CASE_INSTRUMENTAL: "одним", CASE_LOCATIVE: "одному"},
|
44
|
+
NUMBER_PLURAL: {CASE_NOMINATIVE: "одні", CASE_GENITIVE: "одних", CASE_DATIVE: "одним",
|
45
|
+
CASE_ACCUSATIVE: "одні",
|
46
|
+
CASE_INSTRUMENTAL: "одними", CASE_LOCATIVE: "одних"}
|
47
|
+
},
|
48
|
+
2: {
|
49
|
+
GENDER_MASCULINE: {CASE_NOMINATIVE: "два", CASE_GENITIVE: "двох", CASE_DATIVE: "двом",
|
50
|
+
CASE_ACCUSATIVE: "два",
|
51
|
+
CASE_INSTRUMENTAL: "двома", CASE_LOCATIVE: "двох"},
|
52
|
+
GENDER_FEMININE: {CASE_NOMINATIVE: "дві", CASE_GENITIVE: "двох", CASE_DATIVE: "двом",
|
53
|
+
CASE_ACCUSATIVE: "дві",
|
54
|
+
CASE_INSTRUMENTAL: "двома", CASE_LOCATIVE: "двох"},
|
55
|
+
GENDER_NEUTER: {CASE_NOMINATIVE: "два", CASE_GENITIVE: "двох", CASE_DATIVE: "двом", CASE_ACCUSATIVE: "два",
|
56
|
+
CASE_INSTRUMENTAL: "двома", CASE_LOCATIVE: "двох"},
|
57
|
+
},
|
58
|
+
3: {CASE_NOMINATIVE: "три", CASE_GENITIVE: "трьох", CASE_DATIVE: "трьом", CASE_ACCUSATIVE: "три",
|
59
|
+
CASE_INSTRUMENTAL: "трьома", CASE_LOCATIVE: "трьох"},
|
60
|
+
4: {CASE_NOMINATIVE: "чотири", CASE_GENITIVE: "чотирьох", CASE_DATIVE: "чотирьом", CASE_ACCUSATIVE: "чотири",
|
61
|
+
CASE_INSTRUMENTAL: "чотирма", CASE_LOCATIVE: "чотирьох"},
|
62
|
+
5: {CASE_NOMINATIVE: "п'ять", CASE_GENITIVE: "п'яти", CASE_DATIVE: "п'яти", CASE_ACCUSATIVE: "п'ять",
|
63
|
+
CASE_INSTRUMENTAL: "п'ятьма", CASE_LOCATIVE: "п'яти"},
|
64
|
+
6: {CASE_NOMINATIVE: "шість", CASE_GENITIVE: "шести", CASE_DATIVE: "шести", CASE_ACCUSATIVE: "шість",
|
65
|
+
CASE_INSTRUMENTAL: "шістьма", CASE_LOCATIVE: "шести"},
|
66
|
+
7: {CASE_NOMINATIVE: "сім", CASE_GENITIVE: "семи", CASE_DATIVE: "семи", CASE_ACCUSATIVE: "сім",
|
67
|
+
CASE_INSTRUMENTAL: "сімома", CASE_LOCATIVE: "семи"},
|
68
|
+
8: {CASE_NOMINATIVE: "вісім", CASE_GENITIVE: "восьми", CASE_DATIVE: "восьми", CASE_ACCUSATIVE: "вісім",
|
69
|
+
CASE_INSTRUMENTAL: "вісьмома", CASE_LOCATIVE: "восьми"},
|
70
|
+
9: {CASE_NOMINATIVE: "дев'ять", CASE_GENITIVE: "дев'яти", CASE_DATIVE: "дев'яти", CASE_ACCUSATIVE: "дев'ять",
|
71
|
+
CASE_INSTRUMENTAL: "дев'ятьма", CASE_LOCATIVE: "дев'яти"}
|
72
|
+
}
|
73
|
+
|
74
|
+
_TEENS = {
|
75
|
+
10: {CASE_NOMINATIVE: "десять", CASE_GENITIVE: "десяти", CASE_DATIVE: "десяти", CASE_ACCUSATIVE: "десять",
|
76
|
+
CASE_INSTRUMENTAL: "десятьма", CASE_LOCATIVE: "десяти"},
|
77
|
+
11: {CASE_NOMINATIVE: "одинадцять", CASE_GENITIVE: "одинадцяти", CASE_DATIVE: "одинадцяти",
|
78
|
+
CASE_ACCUSATIVE: "одинадцять", CASE_INSTRUMENTAL: "одинадцятьма", CASE_LOCATIVE: "одинадцяти"},
|
79
|
+
12: {CASE_NOMINATIVE: "дванадцять", CASE_GENITIVE: "дванадцяти", CASE_DATIVE: "дванадцяти",
|
80
|
+
CASE_ACCUSATIVE: "дванадцять", CASE_INSTRUMENTAL: "дванадцятьма", CASE_LOCATIVE: "дванадцяти"},
|
81
|
+
13: {CASE_NOMINATIVE: "тринадцять", CASE_GENITIVE: "тринадцяти", CASE_DATIVE: "тринадцяти",
|
82
|
+
CASE_ACCUSATIVE: "тринадцять", CASE_INSTRUMENTAL: "тринадцятьма", CASE_LOCATIVE: "тринадцяти"},
|
83
|
+
14: {CASE_NOMINATIVE: "чотирнадцять", CASE_GENITIVE: "чотирнадцяти", CASE_DATIVE: "чотирнадцяти",
|
84
|
+
CASE_ACCUSATIVE: "чотирнадцять", CASE_INSTRUMENTAL: "чотирнадцятьма", CASE_LOCATIVE: "чотирнадцяти"},
|
85
|
+
15: {CASE_NOMINATIVE: "п'ятнадцять", CASE_GENITIVE: "п'ятнадцяти", CASE_DATIVE: "п'ятнадцяти",
|
86
|
+
CASE_ACCUSATIVE: "п'ятнадцять", CASE_INSTRUMENTAL: "п'ятнадцятьма", CASE_LOCATIVE: "п'ятнадцяти"},
|
87
|
+
16: {CASE_NOMINATIVE: "шістнадцять", CASE_GENITIVE: "шістнадцяти", CASE_DATIVE: "шістнадцяти",
|
88
|
+
CASE_ACCUSATIVE: "шістнадцять", CASE_INSTRUMENTAL: "шістнадцятьма", CASE_LOCATIVE: "шістнадцяти"},
|
89
|
+
17: {CASE_NOMINATIVE: "сімнадцять", CASE_GENITIVE: "сімнадцяти", CASE_DATIVE: "сімнадцяти",
|
90
|
+
CASE_ACCUSATIVE: "сімнадцять", CASE_INSTRUMENTAL: "сімнадцятьма", CASE_LOCATIVE: "сімнадцяти"},
|
91
|
+
18: {CASE_NOMINATIVE: "вісімнадцять", CASE_GENITIVE: "вісімнадцяти", CASE_DATIVE: "вісімнадцяти",
|
92
|
+
CASE_ACCUSATIVE: "вісімнадцять", CASE_INSTRUMENTAL: "вісімнадцятьма", CASE_LOCATIVE: "вісімнадцяти"},
|
93
|
+
19: {CASE_NOMINATIVE: "дев'ятнадцять", CASE_GENITIVE: "дев'ятнадцяти", CASE_DATIVE: "дев'ятнадцяти",
|
94
|
+
CASE_ACCUSATIVE: "дев'ятнадцять", CASE_INSTRUMENTAL: "дев'ятнадцятьма", CASE_LOCATIVE: "дев'ятнадцяти"}
|
95
|
+
}
|
96
|
+
|
97
|
+
_TENS = {
|
98
|
+
20: {CASE_NOMINATIVE: "двадцять", CASE_GENITIVE: "двадцяти", CASE_DATIVE: "двадцяти",
|
99
|
+
CASE_ACCUSATIVE: "двадцять",
|
100
|
+
CASE_INSTRUMENTAL: "двадцятьма", CASE_LOCATIVE: "двадцяти"},
|
101
|
+
30: {CASE_NOMINATIVE: "тридцять", CASE_GENITIVE: "тридцяти", CASE_DATIVE: "тридцяти",
|
102
|
+
CASE_ACCUSATIVE: "тридцять",
|
103
|
+
CASE_INSTRUMENTAL: "тридцятьма", CASE_LOCATIVE: "тридцяти"},
|
104
|
+
40: {CASE_NOMINATIVE: "сорок", CASE_GENITIVE: "сорока", CASE_DATIVE: "сорока", CASE_ACCUSATIVE: "сорок",
|
105
|
+
CASE_INSTRUMENTAL: "сорока", CASE_LOCATIVE: "сорока"},
|
106
|
+
50: {CASE_NOMINATIVE: "п'ятдесят", CASE_GENITIVE: "п'ятдесяти", CASE_DATIVE: "п'ятдесяти",
|
107
|
+
CASE_ACCUSATIVE: "п'ятдесят", CASE_INSTRUMENTAL: "п'ятдесятьма", CASE_LOCATIVE: "п'ятдесяти"},
|
108
|
+
60: {CASE_NOMINATIVE: "шістдесят", CASE_GENITIVE: "шістдесяти", CASE_DATIVE: "шістдесяти",
|
109
|
+
CASE_ACCUSATIVE: "шістдесят", CASE_INSTRUMENTAL: "шістдесятьма", CASE_LOCATIVE: "шістдесяти"},
|
110
|
+
70: {CASE_NOMINATIVE: "сімдесят", CASE_GENITIVE: "сімдесяти", CASE_DATIVE: "сімдесяти",
|
111
|
+
CASE_ACCUSATIVE: "сімдесят",
|
112
|
+
CASE_INSTRUMENTAL: "сімдесятьма", CASE_LOCATIVE: "сімдесяти"},
|
113
|
+
80: {CASE_NOMINATIVE: "вісімдесят", CASE_GENITIVE: "вісімдесяти", CASE_DATIVE: "вісімдесяти",
|
114
|
+
CASE_ACCUSATIVE: "вісімдесят", CASE_INSTRUMENTAL: "вісімдесятьма", CASE_LOCATIVE: "вісімдесяти"},
|
115
|
+
90: {CASE_NOMINATIVE: "дев'яносто", CASE_GENITIVE: "дев'яноста", CASE_DATIVE: "дев'яноста",
|
116
|
+
CASE_ACCUSATIVE: "дев'яносто", CASE_INSTRUMENTAL: "дев'яноста", CASE_LOCATIVE: "дев'яноста"}
|
117
|
+
}
|
118
|
+
|
119
|
+
_HUNDREDS = {
|
120
|
+
100: {CASE_NOMINATIVE: "сто", CASE_GENITIVE: "ста", CASE_DATIVE: "ста", CASE_ACCUSATIVE: "сто",
|
121
|
+
CASE_INSTRUMENTAL: "ста", CASE_LOCATIVE: "ста"},
|
122
|
+
200: {CASE_NOMINATIVE: "двісті", CASE_GENITIVE: "двохсот", CASE_DATIVE: "двомстам", CASE_ACCUSATIVE: "двісті",
|
123
|
+
CASE_INSTRUMENTAL: "двомастами", CASE_LOCATIVE: "двохстах"},
|
124
|
+
300: {CASE_NOMINATIVE: "триста", CASE_GENITIVE: "трьохсот", CASE_DATIVE: "трьомстам", CASE_ACCUSATIVE: "триста",
|
125
|
+
CASE_INSTRUMENTAL: "трьомастами", CASE_LOCATIVE: "трьохстах"},
|
126
|
+
400: {CASE_NOMINATIVE: "чотириста", CASE_GENITIVE: "чотирьохсот", CASE_DATIVE: "чотирьомстам",
|
127
|
+
CASE_ACCUSATIVE: "чотириста", CASE_INSTRUMENTAL: "чотирмастами", CASE_LOCATIVE: "чотирьохстах"},
|
128
|
+
500: {CASE_NOMINATIVE: "п'ятсот", CASE_GENITIVE: "п'ятисот", CASE_DATIVE: "п'ятистам",
|
129
|
+
CASE_ACCUSATIVE: "п'ятсот",
|
130
|
+
CASE_INSTRUMENTAL: "п'ятьмастами", CASE_LOCATIVE: "п'ятистах"},
|
131
|
+
600: {CASE_NOMINATIVE: "шістсот", CASE_GENITIVE: "шестисот", CASE_DATIVE: "шестистам",
|
132
|
+
CASE_ACCUSATIVE: "шістсот",
|
133
|
+
CASE_INSTRUMENTAL: "шістьмастами", CASE_LOCATIVE: "шестистах"},
|
134
|
+
700: {CASE_NOMINATIVE: "сімсот", CASE_GENITIVE: "семисот", CASE_DATIVE: "семистам", CASE_ACCUSATIVE: "сімсот",
|
135
|
+
CASE_INSTRUMENTAL: "сімомастами", CASE_LOCATIVE: "семистах"},
|
136
|
+
800: {CASE_NOMINATIVE: "вісімсот", CASE_GENITIVE: "восьмисот", CASE_DATIVE: "восьмистам",
|
137
|
+
CASE_ACCUSATIVE: "вісімсот", CASE_INSTRUMENTAL: "вісьмомастами", CASE_LOCATIVE: "восьмистах"},
|
138
|
+
900: {CASE_NOMINATIVE: "дев'ятсот", CASE_GENITIVE: "дев'ятисот", CASE_DATIVE: "дев'ятистам",
|
139
|
+
CASE_ACCUSATIVE: "дев'ятсот", CASE_INSTRUMENTAL: "дев'ятьмастами", CASE_LOCATIVE: "дев'ятистах"}
|
140
|
+
}
|
141
|
+
|
142
|
+
_THOUSANDS = {
|
143
|
+
CASE_NOMINATIVE: "тисяча", CASE_GENITIVE: "тисячі", CASE_DATIVE: "тисячі",
|
144
|
+
CASE_ACCUSATIVE: "тисячу", CASE_INSTRUMENTAL: "тисячею", CASE_LOCATIVE: "тисячі"
|
145
|
+
}
|
146
|
+
|
147
|
+
_MILLIONS = {
|
148
|
+
CASE_NOMINATIVE: "мільйон", CASE_GENITIVE: "мільйона", CASE_DATIVE: "мільйону",
|
149
|
+
CASE_ACCUSATIVE: "мільйон", CASE_INSTRUMENTAL: "мільйоном", CASE_LOCATIVE: "мільйоні"
|
150
|
+
}
|
151
|
+
|
152
|
+
_BILLIONS = {
|
153
|
+
CASE_NOMINATIVE: "мільярд", CASE_GENITIVE: "мільярда", CASE_DATIVE: "мільярду",
|
154
|
+
CASE_ACCUSATIVE: "мільярд", CASE_INSTRUMENTAL: "мільярдом", CASE_LOCATIVE: "мільярді"
|
155
|
+
}
|
156
|
+
|
157
|
+
_TRILLIONS = {
|
158
|
+
CASE_NOMINATIVE: "трильйон", CASE_GENITIVE: "трильйона", CASE_DATIVE: "трильйону",
|
159
|
+
CASE_ACCUSATIVE: "трильйон", CASE_INSTRUMENTAL: "трильйоном", CASE_LOCATIVE: "трильйоні"
|
160
|
+
}
|
161
|
+
|
162
|
+
_THOUSAND_ENDING = {'root': "тисяч", 'ending_1': "а", 'ending_2_4': "і", 'ending_5': ""}
|
163
|
+
_MILLION_ENDING = {'root': "мільйон", 'ending_1': "", 'ending_2_4': "и", 'ending_5': "ів"}
|
164
|
+
_BILLION_ENDING = {'root': "мільярд", 'ending_1': "", 'ending_2_4': "и", 'ending_5': "ів"}
|
165
|
+
_TRILLION_ENDING = {'root': "трильйон", 'ending_1': "", 'ending_2_4': "и", 'ending_5': "ів"}
|
166
|
+
|
167
|
+
_COLLECTIVE = {
|
168
|
+
2: {CASE_NOMINATIVE: "двоє", CASE_GENITIVE: "двох", CASE_DATIVE: "двом", CASE_ACCUSATIVE: "двоє",
|
169
|
+
CASE_INSTRUMENTAL: "двома", CASE_LOCATIVE: "двох"},
|
170
|
+
3: {CASE_NOMINATIVE: "троє", CASE_GENITIVE: "трьох", CASE_DATIVE: "трьом", CASE_ACCUSATIVE: "троє",
|
171
|
+
CASE_INSTRUMENTAL: "трьома", CASE_LOCATIVE: "трьох"},
|
172
|
+
4: {CASE_NOMINATIVE: "четверо", CASE_GENITIVE: "чотирьох", CASE_DATIVE: "чотирьом", CASE_ACCUSATIVE: "четверо",
|
173
|
+
CASE_INSTRUMENTAL: "чотирма", CASE_LOCATIVE: "чотирьох"},
|
174
|
+
5: {CASE_NOMINATIVE: "п'ятеро", CASE_GENITIVE: "п'ятьох", CASE_DATIVE: "п'ятьом", CASE_ACCUSATIVE: "п'ятеро",
|
175
|
+
CASE_INSTRUMENTAL: "п'ятьма", CASE_LOCATIVE: "п'ятьох"},
|
176
|
+
6: {CASE_NOMINATIVE: "шестеро", CASE_GENITIVE: "шістьох", CASE_DATIVE: "шістьом", CASE_ACCUSATIVE: "шестеро",
|
177
|
+
CASE_INSTRUMENTAL: "шістьма", CASE_LOCATIVE: "шістьох"},
|
178
|
+
7: {CASE_NOMINATIVE: "семеро", CASE_GENITIVE: "сімох", CASE_DATIVE: "сімом", CASE_ACCUSATIVE: "семеро",
|
179
|
+
CASE_INSTRUMENTAL: "сімома", CASE_LOCATIVE: "сімох"},
|
180
|
+
8: {CASE_NOMINATIVE: "восьмеро", CASE_GENITIVE: "вісьмох", CASE_DATIVE: "вісьмом", CASE_ACCUSATIVE: "восьмеро",
|
181
|
+
CASE_INSTRUMENTAL: "вісьмома", CASE_LOCATIVE: "вісьмох"},
|
182
|
+
9: {CASE_NOMINATIVE: "дев'ятеро", CASE_GENITIVE: "дев'ятьох", CASE_DATIVE: "дев'ятьом",
|
183
|
+
CASE_ACCUSATIVE: "дев'ятеро", CASE_INSTRUMENTAL: "дев'ятьма", CASE_LOCATIVE: "дев'ятьох"},
|
184
|
+
10: {CASE_NOMINATIVE: "десятеро", CASE_GENITIVE: "десятьох", CASE_DATIVE: "десятьом",
|
185
|
+
CASE_ACCUSATIVE: "десятеро",
|
186
|
+
CASE_INSTRUMENTAL: "десятьма", CASE_LOCATIVE: "десятьох"},
|
187
|
+
}
|
188
|
+
|
189
|
+
# Словник для порядкових числівників (базові форми та відмінки)
|
190
|
+
# Важливо: Для складених порядкових числівників відмінюється лише останнє слово.
|
191
|
+
# Тому тут зберігаємо лише форми для "простих" порядкових або тих, що змінюють основу.
|
192
|
+
_ORDINALS_BASES = {
|
193
|
+
1: "перш",
|
194
|
+
2: "друг",
|
195
|
+
3: "трет",
|
196
|
+
4: "четверт",
|
197
|
+
5: "п'ят",
|
198
|
+
6: "шост",
|
199
|
+
7: "сьом",
|
200
|
+
8: "восьм",
|
201
|
+
9: "дев'ят",
|
202
|
+
10: "десят",
|
203
|
+
11: "одинадцят",
|
204
|
+
12: "дванадцят",
|
205
|
+
13: "тринадцят",
|
206
|
+
14: "чотирнадцят",
|
207
|
+
15: "п'ятнадцят",
|
208
|
+
16: "шістнадцят",
|
209
|
+
17: "сімнадцят",
|
210
|
+
18: "вісімнадцят",
|
211
|
+
19: "дев'ятнадцят",
|
212
|
+
20: "двадцят",
|
213
|
+
30: "тридцят",
|
214
|
+
40: "сорок", # Особливий випадок
|
215
|
+
50: "п'ятдесят", # Особливий випадок, відмінюється як складний
|
216
|
+
60: "шістдесят", # те саме
|
217
|
+
70: "сімдесят", # те саме
|
218
|
+
80: "вісімдесят", # те саме
|
219
|
+
90: "дев'яност", # Особливий випадок
|
220
|
+
100: "сот",
|
221
|
+
200: "двохсот",
|
222
|
+
300: "трьохсот",
|
223
|
+
400: "чотирьохсот",
|
224
|
+
500: "п'ятисот",
|
225
|
+
600: "шестисот",
|
226
|
+
700: "семисот",
|
227
|
+
800: "восьмисот",
|
228
|
+
900: "дев'ятисот",
|
229
|
+
1000: "тисячн",
|
230
|
+
1000000: "мільйонн",
|
231
|
+
1000000000: "мільярдн",
|
232
|
+
1000000000000: "трильйонн",
|
233
|
+
}
|
234
|
+
|
235
|
+
# Закінчення порядкових числівників за родами, числами та відмінками
|
236
|
+
_ORDINAL_ENDINGS = {
|
237
|
+
GENDER_MASCULINE: {
|
238
|
+
CASE_NOMINATIVE: "ий",
|
239
|
+
CASE_GENITIVE: "ого",
|
240
|
+
CASE_DATIVE: "ому",
|
241
|
+
CASE_ACCUSATIVE: "ий", # для неістот, для істот як Р.в.
|
242
|
+
CASE_INSTRUMENTAL: "им",
|
243
|
+
CASE_LOCATIVE: "ому"
|
244
|
+
},
|
245
|
+
GENDER_FEMININE: {
|
246
|
+
CASE_NOMINATIVE: "а",
|
247
|
+
CASE_GENITIVE: "ої",
|
248
|
+
CASE_DATIVE: "ій",
|
249
|
+
CASE_ACCUSATIVE: "у",
|
250
|
+
CASE_INSTRUMENTAL: "ою",
|
251
|
+
CASE_LOCATIVE: "ій"
|
252
|
+
},
|
253
|
+
GENDER_NEUTER: {
|
254
|
+
CASE_NOMINATIVE: "е",
|
255
|
+
CASE_GENITIVE: "ого",
|
256
|
+
CASE_DATIVE: "ому",
|
257
|
+
CASE_ACCUSATIVE: "е",
|
258
|
+
CASE_INSTRUMENTAL: "им",
|
259
|
+
CASE_LOCATIVE: "ому"
|
260
|
+
},
|
261
|
+
NUMBER_PLURAL: {
|
262
|
+
CASE_NOMINATIVE: "і",
|
263
|
+
CASE_GENITIVE: "их",
|
264
|
+
CASE_DATIVE: "им",
|
265
|
+
CASE_ACCUSATIVE: "і", # для неістот, для істот як Р.в.
|
266
|
+
CASE_INSTRUMENTAL: "ими",
|
267
|
+
CASE_LOCATIVE: "их"
|
268
|
+
}
|
269
|
+
}
|
270
|
+
|
271
|
+
# Винятки для ORDINAL_ENDINGS для деяких чисел (наприклад, "третій", "третя", "третє")
|
272
|
+
_ORDINAL_SPECIAL_ENDINGS = {
|
273
|
+
3: {
|
274
|
+
GENDER_MASCULINE: {CASE_NOMINATIVE: "третій", CASE_GENITIVE: "третього", CASE_DATIVE: "третьому",
|
275
|
+
CASE_ACCUSATIVE: "третій", CASE_INSTRUMENTAL: "третім", CASE_LOCATIVE: "третьому"},
|
276
|
+
GENDER_FEMININE: {CASE_NOMINATIVE: "третя", CASE_GENITIVE: "третьої", CASE_DATIVE: "третій",
|
277
|
+
CASE_ACCUSATIVE: "третю", CASE_INSTRUMENTAL: "третьою", CASE_LOCATIVE: "третій"},
|
278
|
+
GENDER_NEUTER: {CASE_NOMINATIVE: "третє", CASE_GENITIVE: "третього", CASE_DATIVE: "третьому",
|
279
|
+
CASE_ACCUSATIVE: "третє", CASE_INSTRUMENTAL: "третім", CASE_LOCATIVE: "третьому"},
|
280
|
+
NUMBER_PLURAL: {CASE_NOMINATIVE: "треті", CASE_GENITIVE: "третіх", CASE_DATIVE: "третім",
|
281
|
+
CASE_ACCUSATIVE: "треті", CASE_INSTRUMENTAL: "третіми", CASE_LOCATIVE: "третіх"}
|
282
|
+
},
|
283
|
+
# Для 40 (сороковий), 90 (дев'яностий), 100 (сотий)
|
284
|
+
40: { # Сороковий
|
285
|
+
GENDER_MASCULINE: {CASE_NOMINATIVE: "сороковий", CASE_GENITIVE: "сорокового", CASE_DATIVE: "сороковому",
|
286
|
+
CASE_ACCUSATIVE: "сороковий", CASE_INSTRUMENTAL: "сороковим",
|
287
|
+
CASE_LOCATIVE: "сороковому"},
|
288
|
+
GENDER_FEMININE: {CASE_NOMINATIVE: "сорокова", CASE_GENITIVE: "сорокової", CASE_DATIVE: "сороковій",
|
289
|
+
CASE_ACCUSATIVE: "сорокову", CASE_INSTRUMENTAL: "сороковою", CASE_LOCATIVE: "сороковій"},
|
290
|
+
GENDER_NEUTER: {CASE_NOMINATIVE: "сорокове", CASE_GENITIVE: "сорокового", CASE_DATIVE: "сороковому",
|
291
|
+
CASE_ACCUSATIVE: "сорокове", CASE_INSTRUMENTAL: "сороковим", CASE_LOCATIVE: "сороковому"},
|
292
|
+
NUMBER_PLURAL: {CASE_NOMINATIVE: "сорокові", CASE_GENITIVE: "сорокових", CASE_DATIVE: "сороковим",
|
293
|
+
CASE_ACCUSATIVE: "сорокові", CASE_INSTRUMENTAL: "сороковими", CASE_LOCATIVE: "сорокових"}
|
294
|
+
},
|
295
|
+
90: { # Дев'яностий
|
296
|
+
GENDER_MASCULINE: {CASE_NOMINATIVE: "дев'яностий", CASE_GENITIVE: "дев'яностого",
|
297
|
+
CASE_DATIVE: "дев'яностому",
|
298
|
+
CASE_ACCUSATIVE: "дев'яностий", CASE_INSTRUMENTAL: "дев'яностим",
|
299
|
+
CASE_LOCATIVE: "дев'яностому"},
|
300
|
+
GENDER_FEMININE: {CASE_NOMINATIVE: "дев'яноста", CASE_GENITIVE: "дев'яностої", CASE_DATIVE: "дев'яностій",
|
301
|
+
CASE_ACCUSATIVE: "дев'яносту", CASE_INSTRUMENTAL: "дев'яностою",
|
302
|
+
CASE_LOCATIVE: "дев'яностій"},
|
303
|
+
GENDER_NEUTER: {CASE_NOMINATIVE: "дев'яносте", CASE_GENITIVE: "дев'яностого", CASE_DATIVE: "дев'яностому",
|
304
|
+
CASE_ACCUSATIVE: "дев'яносте", CASE_INSTRUMENTAL: "дев'яностим",
|
305
|
+
CASE_LOCATIVE: "дев'яностому"},
|
306
|
+
NUMBER_PLURAL: {CASE_NOMINATIVE: "дев'яності", CASE_GENITIVE: "дев'яностих", CASE_DATIVE: "дев'яностим",
|
307
|
+
CASE_ACCUSATIVE: "дев'яності", CASE_INSTRUMENTAL: "дев'яностими",
|
308
|
+
CASE_LOCATIVE: "дев'яностих"}
|
309
|
+
},
|
310
|
+
100: { # Сотий
|
311
|
+
GENDER_MASCULINE: {CASE_NOMINATIVE: "сотий", CASE_GENITIVE: "сотого", CASE_DATIVE: "сотому",
|
312
|
+
CASE_ACCUSATIVE: "сотий", CASE_INSTRUMENTAL: "сотим", CASE_LOCATIVE: "сотому"},
|
313
|
+
GENDER_FEMININE: {CASE_NOMINATIVE: "сота", CASE_GENITIVE: "сотої", CASE_DATIVE: "сотій",
|
314
|
+
CASE_ACCUSATIVE: "соту", CASE_INSTRUMENTAL: "сотою", CASE_LOCATIVE: "сотій"},
|
315
|
+
GENDER_NEUTER: {CASE_NOMINATIVE: "соте", CASE_GENITIVE: "сотого", CASE_DATIVE: "сотому",
|
316
|
+
CASE_ACCUSATIVE: "соте", CASE_INSTRUMENTAL: "сотим", CASE_LOCATIVE: "сотому"},
|
317
|
+
NUMBER_PLURAL: {CASE_NOMINATIVE: "соті", CASE_GENITIVE: "сотих", CASE_DATIVE: "сотим",
|
318
|
+
CASE_ACCUSATIVE: "соті",
|
319
|
+
CASE_INSTRUMENTAL: "сотими", CASE_LOCATIVE: "сотих"}
|
320
|
+
}
|
321
|
+
}
|
322
|
+
|
323
|
+
# --- Допоміжні функції для перевірки аргументів ---
|
324
|
+
|
325
|
+
@staticmethod
|
326
|
+
def _validate_case(case):
|
327
|
+
"""Перевіряє, чи є відмінок дійсним."""
|
328
|
+
valid_cases = [CASE_NOMINATIVE, CASE_GENITIVE, CASE_DATIVE,
|
329
|
+
CASE_ACCUSATIVE, CASE_INSTRUMENTAL, CASE_LOCATIVE]
|
330
|
+
if case not in valid_cases:
|
331
|
+
raise ValueError(f"Недійсний відмінок: '{case}'. Допустимі: {', '.join(valid_cases)}")
|
332
|
+
|
333
|
+
@staticmethod
|
334
|
+
def _validate_gender(gender):
|
335
|
+
"""Перевіряє, чи є рід дійсним."""
|
336
|
+
valid_genders = [GENDER_MASCULINE, GENDER_FEMININE, GENDER_NEUTER]
|
337
|
+
if gender not in valid_genders:
|
338
|
+
raise ValueError(f"Недійсний рід: '{gender}'. Допустимі: {', '.join(valid_genders)}")
|
339
|
+
|
340
|
+
@staticmethod
|
341
|
+
def _validate_number_type(number_type):
|
342
|
+
"""Перевіряє, чи є тип числа дійсним."""
|
343
|
+
valid_number_types = [NUMBER_SINGULAR, NUMBER_PLURAL]
|
344
|
+
if number_type not in valid_number_types:
|
345
|
+
raise ValueError(f"Недійсний тип числа: '{number_type}'. Допустимі: {', '.join(valid_number_types)}")
|
346
|
+
|
347
|
+
# --- Внутрішні функції для відмінювання цілих чисел ---
|
348
|
+
|
349
|
+
@staticmethod
|
350
|
+
def _incline_hundreds(num, case):
|
351
|
+
"""Відмінює сотні (100-900)."""
|
352
|
+
if num not in Chyslivnyk._HUNDREDS:
|
353
|
+
# Це має бути відловлено раніше або це виняток для цієї функції
|
354
|
+
raise ValueError(f"Невідоме число для сотень: {num}")
|
355
|
+
return Chyslivnyk._HUNDREDS[num].get(case)
|
356
|
+
|
357
|
+
@staticmethod
|
358
|
+
def _incline_tens(num, case):
|
359
|
+
"""Відмінює десятки (20-90)."""
|
360
|
+
if num not in Chyslivnyk._TENS:
|
361
|
+
raise ValueError(f"Невідоме число для десятків: {num}")
|
362
|
+
return Chyslivnyk._TENS[num].get(case)
|
363
|
+
|
364
|
+
@staticmethod
|
365
|
+
def _incline_teens(num, case):
|
366
|
+
"""Відмінює числа від 10 до 19."""
|
367
|
+
if num not in Chyslivnyk._TEENS:
|
368
|
+
raise ValueError(f"Невідоме число для чисел 10-19: {num}")
|
369
|
+
return Chyslivnyk._TEENS[num].get(case)
|
370
|
+
|
371
|
+
@staticmethod
|
372
|
+
def _incline_ones(num, case, gender=None, number_type=NUMBER_SINGULAR):
|
373
|
+
"""Відмінює одиниці (1-9)."""
|
374
|
+
if num not in Chyslivnyk._ONES:
|
375
|
+
raise ValueError(f"Невідоме число для одиниць: {num}")
|
376
|
+
|
377
|
+
if num == 1:
|
378
|
+
if number_type == NUMBER_PLURAL:
|
379
|
+
return Chyslivnyk._ONES[1][NUMBER_PLURAL].get(case)
|
380
|
+
if gender not in Chyslivnyk._ONES[1]:
|
381
|
+
# Цей виняток має бути оброблений на рівні `get_cardinal`
|
382
|
+
raise ValueError(
|
383
|
+
f"Для числа 1 обов'язково вкажіть рід: {GENDER_MASCULINE}, {GENDER_FEMININE} або {GENDER_NEUTER}")
|
384
|
+
return Chyslivnyk._ONES[1][gender].get(case)
|
385
|
+
elif num == 2:
|
386
|
+
if gender == GENDER_FEMININE:
|
387
|
+
return Chyslivnyk._ONES[2][GENDER_FEMININE].get(case)
|
388
|
+
return Chyslivnyk._ONES[2][GENDER_MASCULINE].get(case) # Чоловічий та середній рід
|
389
|
+
return Chyslivnyk._ONES[num].get(case)
|
390
|
+
|
391
|
+
@staticmethod
|
392
|
+
def _get_cardinal_part(num, case, gender=GENDER_MASCULINE):
|
393
|
+
"""
|
394
|
+
Допоміжна функція для генерації частини кількісного числівника.
|
395
|
+
Обробляє числа до 999.
|
396
|
+
"""
|
397
|
+
if not (0 <= num < 1000):
|
398
|
+
raise ValueError("Число має бути в діапазоні від 0 до 999 для цієї функції.")
|
399
|
+
|
400
|
+
parts = []
|
401
|
+
|
402
|
+
if num >= 100:
|
403
|
+
hundreds = (num // 100) * 100
|
404
|
+
parts.append(Chyslivnyk._incline_hundreds(hundreds, case))
|
405
|
+
num %= 100
|
406
|
+
|
407
|
+
if num >= 10 and num < 20:
|
408
|
+
parts.append(Chyslivnyk._incline_teens(num, case))
|
409
|
+
elif num >= 20:
|
410
|
+
tens = (num // 10) * 10
|
411
|
+
parts.append(Chyslivnyk._incline_tens(tens, case))
|
412
|
+
num %= 10
|
413
|
+
|
414
|
+
if num > 0 and num < 10:
|
415
|
+
if num == 1 or num == 2: # 1 та 2 мають особливості за родом
|
416
|
+
parts.append(Chyslivnyk._incline_ones(num, case, gender=gender))
|
417
|
+
else:
|
418
|
+
parts.append(Chyslivnyk._incline_ones(num, case))
|
419
|
+
|
420
|
+
return " ".join(filter(None, parts)) # Видаляємо порожні рядки
|
421
|
+
|
422
|
+
@staticmethod
|
423
|
+
def _get_significant_magnitude(number):
|
424
|
+
"""
|
425
|
+
Returns the significance of the last non-zero digit of a number.
|
426
|
+
|
427
|
+
Args:
|
428
|
+
number (int or float): The input number.
|
429
|
+
|
430
|
+
Returns:
|
431
|
+
str: A string indicating the magnitude (0, 1, 2,
|
432
|
+
3, 4).
|
433
|
+
"""
|
434
|
+
number = abs(number) # Work with the absolute value for magnitude
|
435
|
+
|
436
|
+
if 0 <= number <= 999:
|
437
|
+
return 0
|
438
|
+
elif 1000 <= number <= 999_999:
|
439
|
+
return 1
|
440
|
+
elif 1_000_000 <= number <= 999_999_999:
|
441
|
+
return 2
|
442
|
+
elif 1_000_000_000 <= number <= 999_999_999_999:
|
443
|
+
return 3
|
444
|
+
elif 1_000_000_000_000 <= number <= 999_999_999_999_999: # Up to just under a quadrillion for trillions
|
445
|
+
return 4
|
446
|
+
else:
|
447
|
+
return 5 # You might want to handle larger numbers or define a limit
|
448
|
+
|
449
|
+
@staticmethod
|
450
|
+
def _get_correct_ending(n: int, ending_rules: dict) -> str:
|
451
|
+
"""
|
452
|
+
Повертає коректне закінчення для числівника на основі числа та правил відмінювання.
|
453
|
+
|
454
|
+
Args:
|
455
|
+
n (int): Число, для якого потрібно визначити закінчення.
|
456
|
+
ending_rules (dict): Словник з правилами відмінювання (наприклад, THOUSAND_ENDING).
|
457
|
+
|
458
|
+
Returns:
|
459
|
+
str: Коректне закінчення.
|
460
|
+
"""
|
461
|
+
if not isinstance(n, int) or n < 0:
|
462
|
+
raise ValueError("Число має бути невід'ємним цілим числом.")
|
463
|
+
|
464
|
+
# Обробка останніх двох цифр для чисел від 1 до 20
|
465
|
+
last_two_digits = n % 100
|
466
|
+
if 10 <= last_two_digits <= 20:
|
467
|
+
return ending_rules['root'] + ending_rules['ending_5']
|
468
|
+
|
469
|
+
# Обробка останньої цифри
|
470
|
+
last_digit = n % 10
|
471
|
+
|
472
|
+
if last_digit == 1:
|
473
|
+
return ending_rules['root'] + ending_rules['ending_1']
|
474
|
+
elif 2 <= last_digit <= 4:
|
475
|
+
return ending_rules['root'] + ending_rules['ending_2_4']
|
476
|
+
else: # last_digit == 0 or last_digit > 4
|
477
|
+
return ending_rules['root'] + ending_rules['ending_5']
|
478
|
+
|
479
|
+
# --- Основні функції бібліотеки ---
|
480
|
+
|
481
|
+
@staticmethod
|
482
|
+
def get_cardinal(number: int, case: str = CASE_NOMINATIVE, gender: str = GENDER_MASCULINE) -> str:
|
483
|
+
"""
|
484
|
+
Генерує цілий кількісний числівник у вказаній граматичній формі.
|
485
|
+
|
486
|
+
:param number: Ціле число (наприклад, 1, 25, 100).
|
487
|
+
:param case: Відмінок (CASE_NOMINATIVE, CASE_GENITIVE, etc.). За замовчуванням: називний.
|
488
|
+
:param gender: Рід (GENDER_MASCULINE, GENDER_FEMININE, GENDER_NEUTER). Застосовно для 1, 2.
|
489
|
+
За замовчуванням: чоловічий.
|
490
|
+
:return: Рядок з числівником.
|
491
|
+
:raises TypeError: Якщо `number` не є цілим числом.
|
492
|
+
:raises ValueError: Якщо `number` від'ємне або `case` недійсний.
|
493
|
+
"""
|
494
|
+
if not isinstance(number, int):
|
495
|
+
raise TypeError("Число повинно бути цілим (int).")
|
496
|
+
if number < 0:
|
497
|
+
raise ValueError("Число не може бути від'ємним для кількісних числівників.")
|
498
|
+
|
499
|
+
Chyslivnyk._validate_case(case)
|
500
|
+
Chyslivnyk._validate_gender(gender) # Перевіряємо, хоча використовується не завжди
|
501
|
+
|
502
|
+
if number == 0:
|
503
|
+
return "нуль" # Нуль не відмінюється за родами чи числами
|
504
|
+
|
505
|
+
magnitude = Chyslivnyk._get_significant_magnitude(number)
|
506
|
+
|
507
|
+
original_number = number
|
508
|
+
|
509
|
+
parts = []
|
510
|
+
# Обробка числа по розрядах (тисячі, мільйони тощо)
|
511
|
+
# Ця частина є спрощеною і не повністю реалізує відмінювання "тисяча", "мільйон" тощо.
|
512
|
+
# Для повної реалізації потрібна значно складніша логіка відмінювання груп.
|
513
|
+
# Тут я реалізую базову логіку для невеликих чисел та просту конкатенацію для великих.
|
514
|
+
|
515
|
+
# Обробка трильйонів
|
516
|
+
if number >= 1_000_000_000_000:
|
517
|
+
if original_number == 1_000_000_000_000:
|
518
|
+
return Chyslivnyk._TRILLIONS[case]
|
519
|
+
tri = number // 1_000_000_000_000
|
520
|
+
|
521
|
+
parts.append(Chyslivnyk._get_cardinal_part(tri, CASE_NOMINATIVE, gender=GENDER_MASCULINE))
|
522
|
+
parts.append(Chyslivnyk._get_correct_ending(tri, Chyslivnyk._TRILLION_ENDING))
|
523
|
+
|
524
|
+
number %= 1_000_000_000_000
|
525
|
+
|
526
|
+
# Обробка мільярдів
|
527
|
+
if number >= 1_000_000_000:
|
528
|
+
if original_number == 1_000_000_000:
|
529
|
+
return Chyslivnyk._BILLIONS[case]
|
530
|
+
bil = number // 1_000_000_000
|
531
|
+
|
532
|
+
parts.append(Chyslivnyk._get_cardinal_part(bil, CASE_NOMINATIVE, gender=GENDER_MASCULINE))
|
533
|
+
parts.append(Chyslivnyk._get_correct_ending(bil, Chyslivnyk._BILLION_ENDING))
|
534
|
+
|
535
|
+
number %= 1_000_000_000
|
536
|
+
|
537
|
+
# Обробка мільйонів
|
538
|
+
if number >= 1_000_000:
|
539
|
+
if original_number == 1_000_000:
|
540
|
+
return Chyslivnyk._MILLIONS[case]
|
541
|
+
|
542
|
+
mil = number // 1_000_000
|
543
|
+
parts.append(Chyslivnyk._get_cardinal_part(mil, CASE_NOMINATIVE, gender=GENDER_MASCULINE))
|
544
|
+
parts.append(Chyslivnyk._get_correct_ending(mil, Chyslivnyk._MILLION_ENDING))
|
545
|
+
|
546
|
+
number %= 1_000_000
|
547
|
+
|
548
|
+
# Обробка тисяч
|
549
|
+
if number >= 1_000:
|
550
|
+
if original_number == 1_000:
|
551
|
+
return Chyslivnyk._THOUSANDS[case]
|
552
|
+
thou = number // 1_000
|
553
|
+
|
554
|
+
parts.append(Chyslivnyk._get_cardinal_part(thou, CASE_NOMINATIVE, gender=GENDER_FEMININE))
|
555
|
+
parts.append(Chyslivnyk._get_correct_ending(thou, Chyslivnyk._THOUSAND_ENDING))
|
556
|
+
|
557
|
+
number %= 1_000
|
558
|
+
|
559
|
+
if number > 0:
|
560
|
+
parts.append(Chyslivnyk._get_cardinal_part(number, case, gender))
|
561
|
+
|
562
|
+
return " ".join(filter(None, parts)).strip()
|
563
|
+
|
564
|
+
@staticmethod
|
565
|
+
def get_collective(number: int, case: str = CASE_NOMINATIVE) -> str:
|
566
|
+
"""
|
567
|
+
Генерує збірний числівник у вказаному відмінку.
|
568
|
+
Підтримує числа від 2 до 10 (для повноти словника).
|
569
|
+
|
570
|
+
:param number: Ціле число (2-10).
|
571
|
+
:param case: Відмінок (CASE_NOMINATIVE, CASE_GENITIVE, etc.). За замовчуванням: називний.
|
572
|
+
:return: Рядок зі збірним числівником.
|
573
|
+
:raises TypeError: Якщо `number` не є цілим числом.
|
574
|
+
:raises ValueError: Якщо `number` не в діапазоні 2-10 або `case` недійсний.
|
575
|
+
"""
|
576
|
+
if not isinstance(number, int):
|
577
|
+
raise TypeError("Число повинно бути цілим (int).")
|
578
|
+
# Додано підтримку до 20 для збірних, але словник поки тільки до 10.
|
579
|
+
if not (2 <= number <= 20):
|
580
|
+
# Якщо треба підтримувати більше, треба розширювати _COLLECTIVE
|
581
|
+
raise ValueError("Збірні числівники підтримуються лише для чисел від 2 до 20.")
|
582
|
+
|
583
|
+
Chyslivnyk._validate_case(case)
|
584
|
+
|
585
|
+
if number not in Chyslivnyk._COLLECTIVE:
|
586
|
+
# Для чисел, що не в словнику (наприклад, 11-19)
|
587
|
+
# можна було б додати логіку для "одинадцятеро", "дванадцятеро" тощо,
|
588
|
+
# але це вимагає розширення _COLLECTIVE або загальної логіки.
|
589
|
+
raise ValueError(f"Збірний числівник для числа {number} не знайдено в словнику.")
|
590
|
+
|
591
|
+
return Chyslivnyk._COLLECTIVE[number].get(case, Chyslivnyk._COLLECTIVE[number][CASE_NOMINATIVE])
|
592
|
+
|
593
|
+
@staticmethod
|
594
|
+
def get_fractional(numerator: int, denominator: int, case: str = CASE_NOMINATIVE,
|
595
|
+
gender: str = GENDER_FEMININE) -> str:
|
596
|
+
"""
|
597
|
+
Генерує дробовий числівник у вказаній граматичній формі.
|
598
|
+
Підтримує лише прості дроби.
|
599
|
+
|
600
|
+
:param numerator: Чисельник (ціле число).
|
601
|
+
:param denominator: Знаменник (ціле число, не 0).
|
602
|
+
:param case: Відмінок (CASE_NOMINATIVE, CASE_GENITIVE, etc.). За замовчуванням: називний.
|
603
|
+
:param gender: Рід знаменника для правильного узгодження (GENDER_FEMININE, GENDER_MASCULINE, GENDER_NEUTER).
|
604
|
+
За замовчуванням: жіночий (як у "одна друга").
|
605
|
+
:return: Рядок з дробовим числівником.
|
606
|
+
:raises TypeError: Якщо чисельник або знаменник не є цілим числом.
|
607
|
+
:raises ValueError: Якщо знаменник 0 або `case` недійсний.
|
608
|
+
"""
|
609
|
+
if not isinstance(numerator, int) or not isinstance(denominator, int):
|
610
|
+
raise TypeError("Чисельник та знаменник повинні бути цілими числами (int).")
|
611
|
+
if denominator == 0:
|
612
|
+
raise ValueError("Знаменник не може бути нулем.")
|
613
|
+
if numerator < 0:
|
614
|
+
raise ValueError("Чисельник не може бути від'ємним для дробових числівників.")
|
615
|
+
if denominator < 0:
|
616
|
+
raise ValueError("Знаменник не може бути від'ємним.")
|
617
|
+
|
618
|
+
Chyslivnyk._validate_case(case)
|
619
|
+
Chyslivnyk._validate_gender(gender)
|
620
|
+
|
621
|
+
if numerator == 0:
|
622
|
+
return "нуль"
|
623
|
+
|
624
|
+
# Ціла частина
|
625
|
+
if numerator >= denominator and numerator % denominator == 0:
|
626
|
+
return Chyslivnyk.get_cardinal(numerator // denominator, case=case, gender=gender)
|
627
|
+
|
628
|
+
# Дробова частина
|
629
|
+
num_str = Chyslivnyk.get_cardinal(numerator, case=case, gender=gender) # Чисельник як кількісний
|
630
|
+
|
631
|
+
# Знаменник як порядковий числівник
|
632
|
+
# Після 1 чисельника - знаменник у однині (одна друга)
|
633
|
+
# Після 2, 3, 4 чисельників - знаменник у множині (дві третіх)
|
634
|
+
# Після 5+ чисельників - знаменник у множині родового відмінка (п'ять восьмих)
|
635
|
+
if numerator == 1:
|
636
|
+
denom_word = Chyslivnyk.get_ordinal(denominator, case=case, gender=gender, number_type=NUMBER_SINGULAR)
|
637
|
+
elif 2 <= numerator <= 4:
|
638
|
+
if case == CASE_NOMINATIVE:
|
639
|
+
case = CASE_GENITIVE
|
640
|
+
# Для 2,3,4 чисельників знаменник залишається в називному множини
|
641
|
+
# Але його форма має бути як у Р.в. множини
|
642
|
+
# Наприклад: "дві третіх", "чотири п'ятих"
|
643
|
+
# Це вимагає обробки як порядкового числівника у родовому відмінку множини
|
644
|
+
denom_word = Chyslivnyk.get_ordinal(denominator, case=case, number_type=NUMBER_PLURAL)
|
645
|
+
else: # > 4
|
646
|
+
if case == CASE_NOMINATIVE:
|
647
|
+
case = CASE_GENITIVE
|
648
|
+
denom_word = Chyslivnyk.get_ordinal(denominator, case=case, number_type=NUMBER_PLURAL)
|
649
|
+
|
650
|
+
return f"{num_str} {denom_word}"
|
651
|
+
|
652
|
+
@staticmethod
|
653
|
+
def get_decimal_fractional(number: float, case: str = CASE_NOMINATIVE, gender: str = None) -> str:
|
654
|
+
"""
|
655
|
+
Генерує дробовий числівник з десяткового дробу.
|
656
|
+
|
657
|
+
:param number: Десятковий дріб (наприклад, 0.25, 3.14).
|
658
|
+
:param case: Відмінок (CASE_NOMINATIVE, CASE_GENITIVE, etc.). За замовчуванням: називний.
|
659
|
+
:param gender: Рід (GENDER_FEMININE, GENDER_MASCULINE, GENDER_NEUTER). Застосовно для цілої частини та останнього слова дробу.
|
660
|
+
За замовчуванням: жіночий.
|
661
|
+
:return: Рядок з дробовим числівником.
|
662
|
+
:raises TypeError: Якщо `number` не є числом з плаваючою комою.
|
663
|
+
:raises ValueError: Якщо `case` недійсний.
|
664
|
+
"""
|
665
|
+
if not isinstance(number, (float, int)):
|
666
|
+
raise TypeError("Число повинно бути числом (float або int).")
|
667
|
+
|
668
|
+
Chyslivnyk._validate_case(case)
|
669
|
+
|
670
|
+
if number == 0:
|
671
|
+
return "нуль"
|
672
|
+
|
673
|
+
int_part = int(number)
|
674
|
+
frac_part_abs = abs(number - int_part)
|
675
|
+
|
676
|
+
if gender is None:
|
677
|
+
if frac_part_abs > 0:
|
678
|
+
gender = GENDER_FEMININE
|
679
|
+
else:
|
680
|
+
gender = GENDER_MASCULINE
|
681
|
+
|
682
|
+
Chyslivnyk._validate_gender(gender)
|
683
|
+
|
684
|
+
result_parts = []
|
685
|
+
|
686
|
+
# Обробка цілої частини
|
687
|
+
if int_part != 0:
|
688
|
+
card_int_part = Chyslivnyk.get_cardinal(int_part, case=case, gender=gender)
|
689
|
+
result_parts.append(card_int_part)
|
690
|
+
else:
|
691
|
+
result_parts.append('нуль цілих')
|
692
|
+
|
693
|
+
flag_1_2 = False
|
694
|
+
|
695
|
+
is_1_or_2 = (int_part % 10 == 1) or (int_part % 10 == 2)
|
696
|
+
not_11_12 = (int_part != 11) and (int_part != 12)
|
697
|
+
|
698
|
+
if is_1_or_2 and not_11_12:
|
699
|
+
flag_1_2 = True
|
700
|
+
|
701
|
+
# Обробка дробової частини
|
702
|
+
if frac_part_abs > 0:
|
703
|
+
s = str(number)
|
704
|
+
# Знаходимо кількість цифр після коми
|
705
|
+
if '.' in s:
|
706
|
+
decimal_places = len(s) - s.find('.') - 1
|
707
|
+
# Обмежуємо кількість знаків після коми для уникнення проблем з float
|
708
|
+
# Наприклад, 0.3 може бути 0.29999999999999999.
|
709
|
+
# Округлюємо до 5 знаків після коми, щоб уникнути помилок перетворення
|
710
|
+
# float на numerator/denominator для простих випадків.
|
711
|
+
if decimal_places > 5:
|
712
|
+
decimal_places = 5
|
713
|
+
numerator_frac = int(round(frac_part_abs * (10 ** decimal_places)))
|
714
|
+
denominator_frac = 10 ** decimal_places
|
715
|
+
else: # Якщо число було цілим, але передано як float (напр. 5.0)
|
716
|
+
numerator_frac = 0
|
717
|
+
denominator_frac = 1
|
718
|
+
|
719
|
+
if numerator_frac > 0:
|
720
|
+
if result_parts: # Якщо є ціла частина, додаємо "цілих"
|
721
|
+
|
722
|
+
if flag_1_2:
|
723
|
+
if gender == GENDER_MASCULINE:
|
724
|
+
if case == CASE_NOMINATIVE or case == CASE_ACCUSATIVE:
|
725
|
+
result_parts.append("ціле")
|
726
|
+
elif case == CASE_GENITIVE:
|
727
|
+
result_parts.append("цілого")
|
728
|
+
elif case == CASE_DATIVE:
|
729
|
+
result_parts.append("цілому")
|
730
|
+
elif case == CASE_INSTRUMENTAL:
|
731
|
+
result_parts.append("цілим")
|
732
|
+
elif case == CASE_LOCATIVE:
|
733
|
+
result_parts.append("цілому")
|
734
|
+
else: # За замовчуванням
|
735
|
+
result_parts.append("ціле")
|
736
|
+
|
737
|
+
if gender == GENDER_FEMININE:
|
738
|
+
if case == CASE_NOMINATIVE:
|
739
|
+
result_parts.append("ціла")
|
740
|
+
elif case == CASE_GENITIVE:
|
741
|
+
result_parts.append("цілої")
|
742
|
+
elif case == CASE_DATIVE:
|
743
|
+
result_parts.append("цілій")
|
744
|
+
elif case == CASE_ACCUSATIVE:
|
745
|
+
result_parts.append("цілу")
|
746
|
+
elif case == CASE_INSTRUMENTAL:
|
747
|
+
result_parts.append("цілою")
|
748
|
+
elif case == CASE_LOCATIVE:
|
749
|
+
result_parts.append("цілій")
|
750
|
+
else: # За замовчуванням
|
751
|
+
result_parts.append("ціла")
|
752
|
+
|
753
|
+
else:
|
754
|
+
if int_part != 0:
|
755
|
+
# Якщо десятковий дріб починається на нуль, то частина цілих не відмінюється. В інших випадках вона відмінюється:
|
756
|
+
if case == CASE_NOMINATIVE or case == CASE_ACCUSATIVE:
|
757
|
+
result_parts.append("цілих")
|
758
|
+
elif case == CASE_GENITIVE:
|
759
|
+
result_parts.append("цілих")
|
760
|
+
elif case == CASE_DATIVE:
|
761
|
+
result_parts.append("цілим")
|
762
|
+
elif case == CASE_INSTRUMENTAL:
|
763
|
+
result_parts.append("цілими")
|
764
|
+
elif case == CASE_LOCATIVE:
|
765
|
+
result_parts.append("цілих")
|
766
|
+
else: # За замовчуванням
|
767
|
+
result_parts.append("цілих")
|
768
|
+
|
769
|
+
frac_str = Chyslivnyk.get_fractional(numerator_frac, denominator_frac, case=case, gender=gender)
|
770
|
+
result_parts.append(frac_str)
|
771
|
+
|
772
|
+
return " ".join(result_parts).strip()
|
773
|
+
|
774
|
+
@staticmethod
|
775
|
+
def _get_ordinal_in_form(num, case, gender, number_type, clear=True):
|
776
|
+
"""
|
777
|
+
Допоміжна функція для генерації порядкового числівника.
|
778
|
+
Обробляє числа рекурсивно, відмінюючи лише останнє слово для складених.
|
779
|
+
"""
|
780
|
+
if num == 0:
|
781
|
+
# Для нуля
|
782
|
+
if number_type == NUMBER_SINGULAR:
|
783
|
+
if gender == GENDER_MASCULINE: return "нульовий"
|
784
|
+
if gender == GENDER_FEMININE: return "нульова"
|
785
|
+
if gender == GENDER_NEUTER: return "нульове"
|
786
|
+
return "нульові" # Множина
|
787
|
+
|
788
|
+
# Спеціальні випадки (третій, сороковий, дев'яностий, сотий)
|
789
|
+
if num in Chyslivnyk._ORDINAL_SPECIAL_ENDINGS:
|
790
|
+
target_dict = Chyslivnyk._ORDINAL_SPECIAL_ENDINGS[num]
|
791
|
+
if number_type == NUMBER_PLURAL:
|
792
|
+
return target_dict[NUMBER_PLURAL].get(case)
|
793
|
+
else:
|
794
|
+
return target_dict[gender].get(case)
|
795
|
+
|
796
|
+
# Обробка складених порядкових числівників
|
797
|
+
# Відмінюється лише останнє слово
|
798
|
+
if num >= 1000000000000: # Трильйони
|
799
|
+
prefix_num = num // 1000000000000
|
800
|
+
remainder = num % 1000000000000
|
801
|
+
prefix_word = Chyslivnyk.get_cardinal(prefix_num, case=CASE_NOMINATIVE) # Попередні слова в називному
|
802
|
+
# Останнє слово в цьому випадку "трильйонний"
|
803
|
+
last_word_base = Chyslivnyk._ORDINALS_BASES[1000000000000]
|
804
|
+
last_word = last_word_base + (
|
805
|
+
Chyslivnyk._ORDINAL_ENDINGS[number_type].get(case) if number_type == NUMBER_PLURAL else
|
806
|
+
Chyslivnyk._ORDINAL_ENDINGS[
|
807
|
+
gender].get(
|
808
|
+
case))
|
809
|
+
if remainder == 0:
|
810
|
+
if prefix_num == 1 and clear == True:
|
811
|
+
return f"{last_word}"
|
812
|
+
else:
|
813
|
+
return f"{prefix_word} {last_word}"
|
814
|
+
else:
|
815
|
+
if prefix_num == 1 and clear == True:
|
816
|
+
return f"{Chyslivnyk._TRILLIONS[CASE_NOMINATIVE]} {Chyslivnyk._get_ordinal_in_form(remainder, case, gender, number_type, clear=False)}"
|
817
|
+
else:
|
818
|
+
return f"{prefix_word} {Chyslivnyk._TRILLIONS[CASE_NOMINATIVE]} {Chyslivnyk._get_ordinal_in_form(remainder, case, gender, number_type, clear=False)}"
|
819
|
+
|
820
|
+
if num >= 1000000000: # Мільярди
|
821
|
+
prefix_num = num // 1000000000
|
822
|
+
remainder = num % 1000000000
|
823
|
+
prefix_word = Chyslivnyk.get_cardinal(prefix_num, case=CASE_NOMINATIVE) # Попередні слова в називному
|
824
|
+
last_word_base = Chyslivnyk._ORDINALS_BASES[1000000000]
|
825
|
+
last_word = last_word_base + (
|
826
|
+
Chyslivnyk._ORDINAL_ENDINGS[number_type].get(case) if number_type == NUMBER_PLURAL else
|
827
|
+
Chyslivnyk._ORDINAL_ENDINGS[
|
828
|
+
gender].get(
|
829
|
+
case))
|
830
|
+
if remainder == 0:
|
831
|
+
if prefix_num == 1 and clear == True:
|
832
|
+
return f"{last_word}"
|
833
|
+
else:
|
834
|
+
return f"{prefix_word} {last_word}"
|
835
|
+
else:
|
836
|
+
if prefix_num == 1 and clear == True:
|
837
|
+
return f"{Chyslivnyk._BILLIONS[CASE_NOMINATIVE]} {Chyslivnyk._get_ordinal_in_form(remainder, case, gender, number_type, clear=False)}"
|
838
|
+
else:
|
839
|
+
return f"{prefix_word} {Chyslivnyk._BILLIONS[CASE_NOMINATIVE]} {Chyslivnyk._get_ordinal_in_form(remainder, case, gender, number_type, clear=False)}"
|
840
|
+
|
841
|
+
if num >= 1000000: # Мільйони
|
842
|
+
prefix_num = num // 1000000
|
843
|
+
remainder = num % 1000000
|
844
|
+
prefix_word = Chyslivnyk.get_cardinal(prefix_num, case=CASE_NOMINATIVE) # Попередні слова в називному
|
845
|
+
last_word_base = Chyslivnyk._ORDINALS_BASES[1000000]
|
846
|
+
last_word = last_word_base + (
|
847
|
+
Chyslivnyk._ORDINAL_ENDINGS[number_type].get(case) if number_type == NUMBER_PLURAL else
|
848
|
+
Chyslivnyk._ORDINAL_ENDINGS[
|
849
|
+
gender].get(
|
850
|
+
case))
|
851
|
+
if remainder == 0:
|
852
|
+
if prefix_num == 1 and clear == True:
|
853
|
+
return f"{last_word}"
|
854
|
+
else:
|
855
|
+
return f"{prefix_word} {last_word}"
|
856
|
+
else:
|
857
|
+
if prefix_num == 1 and clear == True:
|
858
|
+
return f"{Chyslivnyk._MILLIONS[CASE_NOMINATIVE]} {Chyslivnyk._get_ordinal_in_form(remainder, case, gender, number_type, clear=False)}"
|
859
|
+
else:
|
860
|
+
return f"{prefix_word} {Chyslivnyk._MILLIONS[CASE_NOMINATIVE]} {Chyslivnyk._get_ordinal_in_form(remainder, case, gender, number_type, clear=False)}"
|
861
|
+
|
862
|
+
if num >= 1000: # Тисячі
|
863
|
+
prefix_num = num // 1000
|
864
|
+
remainder = num % 1000
|
865
|
+
prefix_word = Chyslivnyk.get_cardinal(prefix_num, case=CASE_NOMINATIVE,
|
866
|
+
gender=GENDER_FEMININE) # Попередні слова в називному
|
867
|
+
last_word_base = Chyslivnyk._ORDINALS_BASES[1000]
|
868
|
+
last_word = last_word_base + (
|
869
|
+
Chyslivnyk._ORDINAL_ENDINGS[number_type].get(case) if number_type == NUMBER_PLURAL else
|
870
|
+
Chyslivnyk._ORDINAL_ENDINGS[
|
871
|
+
gender].get(
|
872
|
+
case))
|
873
|
+
if remainder == 0:
|
874
|
+
if prefix_num == 1 and clear == True:
|
875
|
+
return f"{last_word}"
|
876
|
+
else:
|
877
|
+
return f"{prefix_word} {last_word}"
|
878
|
+
else:
|
879
|
+
return f"{prefix_word} {Chyslivnyk._THOUSANDS[CASE_NOMINATIVE]} {Chyslivnyk._get_ordinal_in_form(remainder, case, gender, number_type, clear=False)}"
|
880
|
+
|
881
|
+
if num >= 100: # Сотні
|
882
|
+
prefix_num = (num // 100) * 100 # Отримуємо 100, 200, 300...
|
883
|
+
remainder = num % 100
|
884
|
+
if prefix_num in Chyslivnyk._ORDINAL_SPECIAL_ENDINGS: # Для 100
|
885
|
+
prefix_word = Chyslivnyk._ORDINAL_SPECIAL_ENDINGS[prefix_num][gender].get(
|
886
|
+
case) if number_type == NUMBER_SINGULAR else \
|
887
|
+
Chyslivnyk._ORDINAL_SPECIAL_ENDINGS[prefix_num][NUMBER_PLURAL].get(case)
|
888
|
+
else:
|
889
|
+
# Для 200-900, які не в _ORDINAL_SPECIAL_ENDINGS, треба генерувати їхній порядковий
|
890
|
+
# "двохсотий", "трьохсотий"
|
891
|
+
base = Chyslivnyk._ORDINALS_BASES[prefix_num] # напр., "двохсот"
|
892
|
+
prefix_word = base + (
|
893
|
+
Chyslivnyk._ORDINAL_ENDINGS[number_type].get(case) if number_type == NUMBER_PLURAL else
|
894
|
+
Chyslivnyk._ORDINAL_ENDINGS[
|
895
|
+
gender].get(case))
|
896
|
+
|
897
|
+
if remainder == 0: # Якщо число рівно 100, 200, 300
|
898
|
+
return prefix_word
|
899
|
+
else:
|
900
|
+
# Для 101, 256, 321...
|
901
|
+
# Наприклад, "сто двадцять перший" - "сто" залишається в називному
|
902
|
+
hundreds_cardinal_part = Chyslivnyk._get_cardinal_part(prefix_num, CASE_NOMINATIVE) # "сто", "двісті"
|
903
|
+
last_part = Chyslivnyk._get_ordinal_in_form(remainder, case, gender, number_type, clear=False)
|
904
|
+
return f"{hundreds_cardinal_part} {last_part}"
|
905
|
+
|
906
|
+
if num >= 20: # Десятки та одиниці (20-99)
|
907
|
+
tens_val = (num // 10) * 10
|
908
|
+
remainder = num % 10
|
909
|
+
if remainder == 0: # Якщо число рівно 20, 30, ...
|
910
|
+
if tens_val in Chyslivnyk._ORDINAL_SPECIAL_ENDINGS: # 40, 90
|
911
|
+
target_dict = Chyslivnyk._ORDINAL_SPECIAL_ENDINGS[tens_val]
|
912
|
+
return target_dict[number_type].get(case) if number_type == NUMBER_PLURAL else target_dict[
|
913
|
+
gender].get(
|
914
|
+
case)
|
915
|
+
# Для інших: двадцятий, тридцятий
|
916
|
+
base = Chyslivnyk._ORDINALS_BASES[tens_val]
|
917
|
+
return base + (
|
918
|
+
Chyslivnyk._ORDINAL_ENDINGS[number_type].get(case) if number_type == NUMBER_PLURAL else
|
919
|
+
Chyslivnyk._ORDINAL_ENDINGS[
|
920
|
+
gender].get(case))
|
921
|
+
else: # Складені: двадцять перший
|
922
|
+
tens_cardinal_part = Chyslivnyk._get_cardinal_part(tens_val, CASE_NOMINATIVE) # "двадцять", "тридцять"
|
923
|
+
last_part = Chyslivnyk._get_ordinal_in_form(remainder, case, gender, number_type)
|
924
|
+
return f"{tens_cardinal_part} {last_part}"
|
925
|
+
|
926
|
+
if num >= 10: # Числа 10-19
|
927
|
+
if num in Chyslivnyk._ORDINAL_SPECIAL_ENDINGS: # Сюди поки нічого не потрапляє з 10-19
|
928
|
+
target_dict = Chyslivnyk._ORDINAL_SPECIAL_ENDINGS[num]
|
929
|
+
return target_dict[number_type].get(case) if number_type == NUMBER_PLURAL else target_dict[gender].get(
|
930
|
+
case)
|
931
|
+
|
932
|
+
base = Chyslivnyk._ORDINALS_BASES[num]
|
933
|
+
return base + (
|
934
|
+
Chyslivnyk._ORDINAL_ENDINGS[number_type].get(case) if number_type == NUMBER_PLURAL else
|
935
|
+
Chyslivnyk._ORDINAL_ENDINGS[
|
936
|
+
gender].get(
|
937
|
+
case))
|
938
|
+
|
939
|
+
if num > 0: # Одиниці 1-9
|
940
|
+
if num in Chyslivnyk._ORDINAL_SPECIAL_ENDINGS: # 3-й
|
941
|
+
target_dict = Chyslivnyk._ORDINAL_SPECIAL_ENDINGS[num]
|
942
|
+
return target_dict[number_type].get(case) if number_type == NUMBER_PLURAL else target_dict[gender].get(
|
943
|
+
case)
|
944
|
+
|
945
|
+
base = Chyslivnyk._ORDINALS_BASES[num]
|
946
|
+
# Для 1, 2, 5, 6, 7, 8, 9
|
947
|
+
return base + (
|
948
|
+
Chyslivnyk._ORDINAL_ENDINGS[number_type].get(case) if number_type == NUMBER_PLURAL else
|
949
|
+
Chyslivnyk._ORDINAL_ENDINGS[
|
950
|
+
gender].get(
|
951
|
+
case))
|
952
|
+
|
953
|
+
return "" # Заглушка, якщо число не оброблено
|
954
|
+
|
955
|
+
@staticmethod
|
956
|
+
def get_ordinal(number: int, case: str = CASE_NOMINATIVE, gender: str = GENDER_MASCULINE,
|
957
|
+
number_type: str = NUMBER_SINGULAR) -> str:
|
958
|
+
"""
|
959
|
+
Генерує порядковий числівник у вказаній граматичній формі.
|
960
|
+
|
961
|
+
:param number: Ціле число (наприклад, 1, 7, 25).
|
962
|
+
:param case: Відмінок (CASE_NOMINATIVE, CASE_GENITIVE, etc.). За замовчуванням: називний.
|
963
|
+
:param gender: Рід (GENDER_MASCULINE, GENDER_FEMININE, GENDER_NEUTER). За замовчуванням: чоловічий.
|
964
|
+
:param number_type: Число (NUMBER_SINGULAR, NUMBER_PLURAL). За замовчуванням: однина.
|
965
|
+
:return: Рядок з числівником.
|
966
|
+
:raises TypeError: Якщо `number` не є цілим числом.
|
967
|
+
:raises ValueError: Якщо `number` від'ємне або `case`/`gender`/`number_type` недійсні.
|
968
|
+
"""
|
969
|
+
if not isinstance(number, int):
|
970
|
+
raise TypeError("Число повинно бути цілим (int).")
|
971
|
+
if number < 0:
|
972
|
+
raise ValueError("Число не може бути від'ємним для порядкових числівників.")
|
973
|
+
|
974
|
+
Chyslivnyk._validate_case(case)
|
975
|
+
Chyslivnyk._validate_gender(gender)
|
976
|
+
Chyslivnyk._validate_number_type(number_type)
|
977
|
+
|
978
|
+
return Chyslivnyk._get_ordinal_in_form(number, case, gender, number_type)
|
@@ -0,0 +1,113 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: chyslivnyk
|
3
|
+
Version: 0.1.0
|
4
|
+
Summary: A Python library for handling Ukrainian numerals (chyslivnyky) designed to convert numeric values into their textual representations in Ukrainian, taking into account grammatical features such as case, gender, and number.
|
5
|
+
Author-email: Anton Sikidin <anton.sikidin@gmail.com>
|
6
|
+
Project-URL: Homepage, https://github.com/AntonSikidin/Chyslivnyk
|
7
|
+
Project-URL: Issues, https://github.com/AntonSikidin/Chyslivnyk/issues
|
8
|
+
Keywords: ukrainian,numerals,chyslivnyk
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
10
|
+
Classifier: Operating System :: OS Independent
|
11
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
12
|
+
Classifier: Intended Audience :: Developers
|
13
|
+
Classifier: Natural Language :: Ukrainian
|
14
|
+
Requires-Python: >=3.7
|
15
|
+
Description-Content-Type: text/markdown
|
16
|
+
License-File: LICENSE
|
17
|
+
Dynamic: license-file
|
18
|
+
|
19
|
+
# Chyslivnyk - Бібліотека для українських числівників
|
20
|
+
|
21
|
+
`Chyslivnyk` - це Python-бібліотека, призначена для конвертації числових значень у їх текстові представлення українською мовою з урахуванням граматичних особливостей (відмінків, родів, чисел).
|
22
|
+
|
23
|
+
Ця бібліотека реалізована у вигляді класу `Chyslivnyk`, що дозволяє інкапсулювати логіку та дані, роблячи код більш структурованим.
|
24
|
+
|
25
|
+
**Важливо:** Ця бібліотека є базовою реалізацією. Відмінювання складних числівників в українській мові має багато нюансів, які можуть бути не повністю охоплені цією версією. Зокрема, повне відмінювання складених кількісних числівників (де всі компоненти змінюються) та складних порядкових числівників (де змінюється лише останній компонент) реалізовано спрощено або вимагає подальшого розширення словників та логіки.
|
26
|
+
|
27
|
+
## Установлення
|
28
|
+
|
29
|
+
|
30
|
+
```bash
|
31
|
+
pip install chyslivnyk
|
32
|
+
```
|
33
|
+
|
34
|
+
# Основне використання
|
35
|
+
|
36
|
+
Спочатку створіть екземпляр класу Chyslivnyk, а потім викликайте його методи. Імпортуйте необхідні константи (вони залишаються на рівні модуля для зручності):
|
37
|
+
```Python
|
38
|
+
from chyslivnyk import (
|
39
|
+
Chyslivnyk, # Імпортуємо клас
|
40
|
+
CASE_NOMINATIVE, CASE_GENITIVE, CASE_DATIVE, CASE_ACCUSATIVE, CASE_INSTRUMENTAL, CASE_LOCATIVE,
|
41
|
+
GENDER_MASCULINE, GENDER_FEMININE, GENDER_NEUTER, NUMBER_PLURAL
|
42
|
+
)
|
43
|
+
|
44
|
+
# Створіть екземпляр класу
|
45
|
+
numerals_converter = Chyslivnyk()
|
46
|
+
```
|
47
|
+
|
48
|
+
# 1. Кількісні числівники (цілі)
|
49
|
+
Використовуйте метод Chyslivnyk.get_cardinal().
|
50
|
+
|
51
|
+
```Python
|
52
|
+
# Називний відмінок
|
53
|
+
print(numerals_converter.get_cardinal(1)) # --> "один"
|
54
|
+
print(numerals_converter.get_cardinal(1, gender=GENDER_FEMININE)) # --> "одна"
|
55
|
+
print(numerals_converter.get_cardinal(5)) # --> "п'ять"
|
56
|
+
print(numerals_converter.get_cardinal(25)) # --> "двадцять п'ять"
|
57
|
+
print(numerals_converter.get_cardinal(101)) # --> "сто один"
|
58
|
+
print(numerals_converter.get_cardinal(200)) # --> "двісті"
|
59
|
+
print(numerals_converter.get_cardinal(1000)) # --> "тисяча"
|
60
|
+
print(numerals_converter.get_cardinal(2000)) # --> "дві тисячі"
|
61
|
+
print(numerals_converter.get_cardinal(12345678)) # --> "дванадцять мільйонів триста сорок п'ять тисяч шістсот сімдесят вісім"
|
62
|
+
|
63
|
+
# З родовим відмінком
|
64
|
+
print(numerals_converter.get_cardinal(25, case=CASE_GENITIVE)) # --> "двадцяти п'яти"
|
65
|
+
print(numerals_converter.get_cardinal(1, case=CASE_GENITIVE, gender=GENDER_FEMININE)) # --> "однієї"
|
66
|
+
print(numerals_converter.get_cardinal(40, case=CASE_INSTRUMENTAL)) # --> "сорока"
|
67
|
+
print(numerals_converter.get_cardinal(700, case=CASE_DATIVE)) # --> "семистам"
|
68
|
+
```
|
69
|
+
|
70
|
+
# 2. Збірні числівники
|
71
|
+
Використовуйте метод numerals_converter.get_collective(). Підтримує числа від 2 до 10.
|
72
|
+
```Python
|
73
|
+
print(numerals_converter.get_collective(2)) # --> "двоє"
|
74
|
+
print(numerals_converter.get_collective(5, case=CASE_GENITIVE)) # --> "п'ятьох"
|
75
|
+
print(numerals_converter.get_collective(7, case=CASE_INSTRUMENTAL)) # --> "сімома"
|
76
|
+
```
|
77
|
+
|
78
|
+
# 3. Дробові числівники
|
79
|
+
Використовуйте метод numerals_converter.get_fractional() для чисельника та знаменника, або numerals_converter.get_decimal_fractional() для десяткових дробів.
|
80
|
+
```Python
|
81
|
+
# З чисельником і знаменником
|
82
|
+
print(numerals_converter.get_fractional(1, 2)) # --> "одна друга"
|
83
|
+
print(numerals_converter.get_fractional(3, 4, case=CASE_DATIVE)) # --> "трьом четвертим"
|
84
|
+
print(numerals_converter.get_fractional(2, 5, case=CASE_GENITIVE)) # --> "двох п'ятих"
|
85
|
+
|
86
|
+
# З десятковим дробом
|
87
|
+
print(numerals_converter.get_decimal_fractional(0.5)) # --> "нуль цілих одна друга"
|
88
|
+
print(numerals_converter.get_decimal_fractional(3.14)) # --> "три цілих чотирнадцять сотих"
|
89
|
+
print(numerals_converter.get_decimal_fractional(1.25, case=CASE_INSTRUMENTAL)) # --> "однією цілою двадцятьма п'ятьма сотими"
|
90
|
+
```
|
91
|
+
|
92
|
+
# 4. Порядкові числівники
|
93
|
+
Використовуйте метод numerals_converter.get_ordinal().
|
94
|
+
```Python
|
95
|
+
print(numerals_converter.get_ordinal(1)) # --> "перший"
|
96
|
+
print(numerals_converter.get_ordinal(1, gender=GENDER_FEMININE)) # --> "перша"
|
97
|
+
print(numerals_converter.get_ordinal(5, case=CASE_GENITIVE)) # --> "п'ятого"
|
98
|
+
print(numerals_converter.get_ordinal(7, gender=GENDER_NEUTER, case=CASE_INSTRUMENTAL)) # --> "сьомим"
|
99
|
+
print(numerals_converter.get_ordinal(21)) # --> "двадцять перший"
|
100
|
+
print(numerals_converter.get_ordinal(21, gender=GENDER_FEMININE, case=CASE_DATIVE)) # --> "двадцять першій"
|
101
|
+
print(numerals_converter.get_ordinal(100)) # --> "сотий"
|
102
|
+
print(numerals_converter.get_ordinal(101)) # --> "сто перший"
|
103
|
+
print(numerals_converter.get_ordinal(121)) # --> "сто двадцять перший"
|
104
|
+
print(numerals_converter.get_ordinal(1000, gender=GENDER_FEMININE)) # --> "тисячна"
|
105
|
+
print(numerals_converter.get_ordinal(1000, number_type=NUMBER_PLURAL)) # --> "тисячні"а"
|
106
|
+
```
|
107
|
+
|
108
|
+
## Особливості реалізації
|
109
|
+
* Клас Chyslivnyk: Вся логіка та дані інкапсульовані в одному класі.
|
110
|
+
* Обробка винятків: Методи викликають TypeError для некоректних типів вхідних даних та ValueError для недійсних граматичних параметрів або чисел поза підтримуваним діапазоном.
|
111
|
+
* Словники відмінювання: Використовуються внутрішні словники (тепер атрибути класу) для зберігання форм числівників у різних відмінках та родах. Це робить код більш читабельним та легким для розширення.
|
112
|
+
* Обмеження: Через складність української граматики, деякі аспекти (наприклад, повне відмінювання всіх складових частин дуже великих чисел або складених порядкових числівників типу "сто двадцять перший") реалізовано спрощено. Для повної академічної точності може знадобитися значне розширення словників та логіки.
|
113
|
+
* Мова: Весь код, коментарі та документація написані українською мовою.
|
@@ -0,0 +1,6 @@
|
|
1
|
+
chyslivnyk/__init__.py,sha256=7vqr6NFHNEkyeK-I8LwileKrCEf31r0SbCBGB3I9LXI,60143
|
2
|
+
chyslivnyk-0.1.0.dist-info/licenses/LICENSE,sha256=UpD-KLPuBsKNAt8ullc00EOU9-BVLXkBbbxzXMaMrQ8,1088
|
3
|
+
chyslivnyk-0.1.0.dist-info/METADATA,sha256=QlgyH9AhUby2Pisx9tCjDRx2uW1bCdeD2haXgZRkuxE,8891
|
4
|
+
chyslivnyk-0.1.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
5
|
+
chyslivnyk-0.1.0.dist-info/top_level.txt,sha256=ZfsKPhcvWd4fFvRCYezygt7cp0ILRGlHizw8tAYGjqc,11
|
6
|
+
chyslivnyk-0.1.0.dist-info/RECORD,,
|
@@ -0,0 +1,21 @@
|
|
1
|
+
MIT License
|
2
|
+
|
3
|
+
Copyright (c) 2025 AntonSikidin
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
@@ -0,0 +1 @@
|
|
1
|
+
chyslivnyk
|