lesya 0.0.1__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.
lesya/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .language.case_helpers import CaseUA
2
+ from .language.case_helpers import Gender
3
+ from .language.name_case import Lesya
File without changes
@@ -0,0 +1,138 @@
1
+ class Gender:
2
+ """
3
+ A class containing constants for gender determination
4
+ """
5
+ MALE = 'male'
6
+ FEMALE = 'female'
7
+ MAN = 'male'
8
+ WOMAN = 'female'
9
+
10
+
11
+ class CaseUA:
12
+ """
13
+ A class containing constants for grammatical cases in Ukrainian
14
+ """
15
+ NOMINATIVE = "nominative" # "Називний відмінок (Nominative)" # Nominative case
16
+ GENITIVE = "genitive" # "Родовий відмінок (Genitive)" # Genitive case
17
+ DATIVE = "dative" # "Давальний відмінок (Dative)" # Dative case
18
+ ACCUSATIVE = "accusative" # "Знахідний відмінок (Accusative)" # Accusative case
19
+ INSTRUMENTAL = "instrumental" # "Орудний відмінок (Instrumental)" # Instrumental case
20
+ PREPOSITIONAL = "prepositional" # "Місцевий відмінок (Prepositional)" # Prepositional case
21
+ VOCATIVE = "vocative" # "Кличний відмінок (Vocative)" # Vocative case
22
+
23
+ @classmethod
24
+ def __getitem__(cls, number: int):
25
+ if number in range(7):
26
+ return {0: cls.NOMINATIVE,
27
+ 1: cls.GENITIVE,
28
+ 2: cls.DATIVE,
29
+ 3: cls.ACCUSATIVE,
30
+ 4: cls.INSTRUMENTAL,
31
+ 5: cls.PREPOSITIONAL,
32
+ 6: cls.VOCATIVE}.get(number)
33
+ raise TypeError("The number must be in the range from 0 to 6")
34
+
35
+ def all(self, lang='en'):
36
+ if lang == 'en':
37
+ return [self.NOMINATIVE, self.GENITIVE, self.DATIVE, self.ACCUSATIVE, self.INSTRUMENTAL, self.PREPOSITIONAL,
38
+ self.VOCATIVE]
39
+ elif lang == 'ua':
40
+ return ['називний', 'родовий', 'давальний', 'знахідний', 'орудний', 'місцевий', 'кличний']
41
+
42
+ @staticmethod
43
+ def count():
44
+ return 7
45
+
46
+ @classmethod
47
+ def __len__(cls):
48
+ return cls.count()
49
+
50
+
51
+ class NamePart:
52
+ FIRSTNAME = 'firstname'
53
+ LASTNAME = 'lastname'
54
+ PATRONYMIC = 'patronymic'
55
+
56
+
57
+ class NCLNameCaseWord:
58
+ """
59
+ A class representing a word with additional properties and methods for handling name cases and gender determination.
60
+ """
61
+ def __init__(self, word):
62
+ self.word = word.lower()
63
+ self.word_orig = word
64
+ self.name_part = None
65
+ self.gender_man = 0
66
+ self.gender_woman = 0
67
+ self.gender_solved = None
68
+ # mask to define origin capital and string letters: X, or x
69
+ self.letter_mask = ''
70
+ # array of cases for the word
71
+ self.name_cases = []
72
+ # self.rule = 0
73
+ self.create_mask(word)
74
+
75
+ def create_mask(self, word):
76
+ """
77
+ Generating mask for the word
78
+ """
79
+ for letter in word:
80
+ if letter.isupper():
81
+ self.letter_mask += 'X'
82
+ else:
83
+ self.letter_mask += 'x'
84
+
85
+ def return_mask(self):
86
+ """ Convert the word to the capital / small letter format according to its mask
87
+ """
88
+ mask_length = len(self.letter_mask)
89
+ for i, case in enumerate(self.name_cases):
90
+ case_length = len(case)
91
+ max_len = min(case_length, mask_length)
92
+ new_case = ''
93
+ for j in range(max_len):
94
+ letter = case[j]
95
+ if self.letter_mask[j] == 'X':
96
+ letter = letter.upper()
97
+ new_case += letter
98
+ new_case += case[max_len:case_length]
99
+ self.name_cases[i] = new_case
100
+
101
+ def set_name_cases(self, name_cases, is_return_mask=True):
102
+ self.name_cases = name_cases
103
+ if is_return_mask:
104
+ self.return_mask()
105
+
106
+ def get_name_cases(self):
107
+ return self.name_cases
108
+
109
+ def gender(self):
110
+ if not self.gender_solved:
111
+ if self.gender_man >= self.gender_woman:
112
+ self.gender_solved = Gender.MALE
113
+ else:
114
+ self.gender_solved = Gender.FEMALE
115
+ return self.gender_solved
116
+
117
+ def set_gender(self, man, woman):
118
+ self.gender_man = man
119
+ self.gender_woman = woman
120
+
121
+ def set_true_gender(self, gender):
122
+ self.gender_solved = gender
123
+
124
+ def get_gender(self):
125
+ return {Gender.MALE: self.gender_man, Gender.FEMALE: self.gender_woman}
126
+
127
+ def is_gender_solved(self):
128
+ return bool(self.gender_solved)
129
+
130
+ def __repr__(self):
131
+ return f'{self.word_orig} - {self.name_cases}'
132
+
133
+ def __eq__(self, other):
134
+ if isinstance(other, str):
135
+ return self.word_orig == other
136
+ elif isinstance(other, NCLNameCaseWord):
137
+ return self.word_orig == other.word_orig
138
+ return False
@@ -0,0 +1,409 @@
1
+ from lesya.language.nc_core import NameCaseCore, NamePart
2
+ from lesya.language.name_constants import NAMES, FEMALE_NAMES, LASTNAME_ENDINGS2, LASTNAME_ENDINGS3
3
+ from lesya.language.case_helpers import CaseUA
4
+
5
+ NAMES_LIST = {n.lower() for n in NAMES + FEMALE_NAMES}
6
+ NAMES = {n.lower() for n in NAMES}
7
+ FEMALE_NAMES = {n.lower() for n in FEMALE_NAMES}
8
+
9
+
10
+ class NameCaseUa(NameCaseCore):
11
+ def __init__(self):
12
+ super().__init__()
13
+ self.vowels = 'аеиоуіїєюя'
14
+ self.consonant = "бвгджзйклмнпрстфхцчшщ"
15
+ self.shyplyachi = "жчшщ"
16
+ self.neshyplyachi = "бвгдзклмнпрстфхц"
17
+ self.myaki = 'ьюяєї'
18
+ self.gubni = 'мвпбф'
19
+
20
+ @property
21
+ def forms(self):
22
+ if self.finished:
23
+ all_forms = dict()
24
+ for i, case in enumerate(self.cases):
25
+ all_forms[case] = ' '.join([w.name_cases[i] for w in self.words])
26
+ return all_forms
27
+ return None
28
+
29
+ def get_firstname(self):
30
+ words = [w for w in self.words if w.name_part == NamePart.FIRSTNAME]
31
+ return words[0].word_orig if words else None
32
+
33
+ def get_lastname(self):
34
+ words = [w for w in self.words if w.name_part == NamePart.LASTNAME]
35
+ return words[0].word_orig if words else None
36
+
37
+ def get_patronym(self):
38
+ words = [w for w in self.words if w.name_part == NamePart.PATRONYMIC]
39
+ return words[0].word_orig if words else None
40
+
41
+ @staticmethod
42
+ def inverse_gkh(letter):
43
+ return {'г': 'з', 'к': 'ц', 'х': 'с'}.get(letter, letter)
44
+
45
+ @staticmethod
46
+ def inverse_gk2(letter):
47
+ return {'к': 'ч', 'г': 'ж'}.get(letter, letter)
48
+
49
+ @staticmethod
50
+ def is_apostrof(char):
51
+ return char in "'`’\""
52
+
53
+ @staticmethod
54
+ def first_last_vowel(word, vowels):
55
+ for char in word[::-1]:
56
+ if char in vowels:
57
+ return char
58
+ return None
59
+
60
+ def get_base_word(self, word):
61
+ base_word = word.lower()
62
+ while base_word[-1] in self.vowels + 'ь':
63
+ base_word = base_word[:-1]
64
+ return base_word.lower()
65
+
66
+ def male_rule1(self):
67
+ last2 = self.working_word[-2]
68
+ if self.working_word[-1] == 'а':
69
+ self.word_forms(self.working_word,
70
+ [last2 + 'и', self.inverse_gkh(last2) + 'і', last2 + 'у', last2 + 'ою',
71
+ self.inverse_gkh(last2) + 'і', last2 + 'о'], 2)
72
+ return True
73
+ elif self.working_word[-1] == 'я':
74
+ if last2 == 'і':
75
+ self.word_forms(self.working_word, ['ї', 'ї', 'ю', 'єю', 'ї', 'є'], 1)
76
+ return True
77
+ else:
78
+ self.word_forms(self.working_word,
79
+ [last2 + 'і', self.inverse_gkh(last2) + 'і', last2 + 'ю',
80
+ last2 + 'ею', self.inverse_gkh(last2) + 'і',
81
+ last2 + 'е'], 2)
82
+ return True
83
+ return False
84
+
85
+ def male_rule2(self):
86
+ if self.working_word[-1] == 'р':
87
+ base_word = self.working_word
88
+ if self.working_word in ('Ігор', 'Лазар') or self.working_word.lower().endswith('якір'):
89
+ endings = ['я', 'еві', 'я', 'ем', 'еві', 'е']
90
+ if base_word[-2] == 'і':
91
+ base_word = base_word[:-2] + 'о' + base_word[-1]
92
+ else:
93
+ # це має працювати на іменах (Федір -Федора, Сидір-Сидора)
94
+ # а не на прізвищах (Кушнір -Кушніра)
95
+ if base_word[-2] == 'і':
96
+ word = [w for w in self.words if w == self.working_word]
97
+ if word[0].name_part == NamePart.FIRSTNAME:
98
+ base_word = base_word[:-2] + 'о' + base_word[-1]
99
+ endings = ['а', 'у', 'а', 'ом', 'ові', 'е']
100
+ self.word_forms(base_word, endings)
101
+ return True
102
+ return False
103
+
104
+ def male_rule3(self):
105
+ last2 = self.working_word[-2]
106
+ if self.working_word[-1] in self.consonant + 'оь':
107
+ group = self.detect_second_group(self.working_word)
108
+ base_word = self.get_base_word(self.working_word)
109
+ last = base_word[-1]
110
+ if (last not in 'йм' and base_word[-2] == 'і'
111
+ and not base_word.lower() in ('світ', 'цвіт')
112
+ and self.working_word not in ('Гліб', 'Леонід')
113
+ and not self.working_word[-2:] in ('ік', 'іч')):
114
+ if self.working_word[-4:] in ('бідь', 'мінь'):
115
+ # заміна на -е-
116
+ base_word = base_word[:-2] + 'е' + base_word[-1]
117
+ elif self.working_word[-4:] in ('сіль'):
118
+ # не змінюється
119
+ pass
120
+ else:
121
+ # заміна на -о-
122
+ base_word = base_word[:-2] + 'о' + base_word[-1]
123
+
124
+ if (base_word[0] in ('о', 'О') and self.first_last_vowel(base_word, self.vowels + 'гк') == 'е'
125
+ and not self.working_word[-2:] in ('сь', 'ць', 'нь', 'ть', 'дь', 'ль', 'рь')):
126
+ delim = base_word.rfind('е')
127
+ base_word = base_word[:delim] + base_word[delim + 1:]
128
+ if group == 1:
129
+ if self.working_word[-2:] == 'ок' and self.working_word[-3:] != 'оок':
130
+ self.word_forms(self.working_word, ['ка', 'кові', 'ка', 'ком', 'кові', 'че'], 2)
131
+ return True
132
+ elif self.working_word[-2:] in ('ов', 'ев', 'єв') and not self.working_word in ('Лев', 'Остромов'):
133
+ self.word_forms(base_word, [last + 'а', last + 'у', last + 'а', last + 'им', last + 'у',
134
+ self.inverse_gk2(last) + 'е'], 1)
135
+ return True
136
+ elif self.working_word[-2:] == 'ін':
137
+ self.word_forms(self.working_word, ['а', 'у', 'а', 'ом', 'у', 'е'])
138
+ return True
139
+ else:
140
+ if self.working_word.lower() == 'пес':
141
+ base_word = 'пс'
142
+ if self.working_word[-3:] in ('ких', 'гих', 'чих', 'дих'):
143
+ return False
144
+ self.word_forms(base_word, [last + 'а', last + 'у', last + 'а', last + 'ом', last + 'ові',
145
+ self.inverse_gk2(last) + 'е'], 1)
146
+ return True
147
+ if group == 2:
148
+ self.word_forms(base_word, ['а', 'у', 'а', 'ем', 'еві', 'е'])
149
+ return True
150
+ if group == 3:
151
+ if self.working_word[-2:] == 'ей' and self.working_word[-3] in self.gubni:
152
+ base_word = self.working_word[:-2] + '’'
153
+ self.word_forms(base_word, ['я', 'єві', 'я', 'єм', 'єві', 'ю'])
154
+ return True
155
+ elif self.working_word[-1] == 'й' or last2 == 'і':
156
+ if self.working_word[-3:] == 'ній':
157
+ self.word_forms(self.working_word, ['ього', 'ьому', 'ього', 'ім', 'ьому', 'ій'], 2)
158
+ else:
159
+ self.word_forms(self.working_word, ['я', 'єві', 'я', 'єм', 'єві', 'ю'], 1)
160
+ return True
161
+ elif self.working_word[-3:] == 'ець':
162
+ base_word = self.get_exclusions(base_word)
163
+ self.word_forms(base_word, ['ця', 'цеві', 'ця', 'цем', 'цеві', 'цю'], 2)
164
+ return True
165
+ elif self.working_word[-3:] in ('єць', 'яць'):
166
+ self.word_forms(self.working_word, ['йця', 'йцеві', 'йця', 'йцем', 'йцеві', 'йцю'], 3)
167
+ return True
168
+ else:
169
+ if self.working_word[-4:] in ('вель', 'вень', 'день'):
170
+ base_word = self.working_word[:-3] + base_word[-1]
171
+ self.word_forms(base_word, ['я', 'еві', 'я', 'ем', 'еві', 'ю'])
172
+ return True
173
+ return False
174
+
175
+ def male_rule4(self):
176
+ if self.working_word[-1] == 'і':
177
+ self.word_forms(self.working_word, ['их', 'им', 'их', 'ими', 'их', 'і'], 1)
178
+ return True
179
+ return False
180
+
181
+ def male_rule5(self):
182
+ if self.working_word[-2:] in ['ий', 'ой']:
183
+ self.word_forms(self.working_word, ['ого', 'ому', 'ого', 'им', 'ому', 'ий'], 2)
184
+ return True
185
+ return False
186
+
187
+ def noun_is_not_declined(self):
188
+ """ exclusions for words that are not declined """
189
+ if self.working_word[-1] in self.vowels and self.working_word[-2] in self.vowels:
190
+ return True
191
+ if self.working_word[-2:] in ('ьє', 'ні', 'лі', 'рі', 'ьї', 'те', 'се', 'же', 'хе',):
192
+ return True
193
+ if self.working_word.lower() in ('педро', 'дюма', 'дідро', ):
194
+ return True
195
+ return False
196
+
197
+ def get_exclusions(self, base_word):
198
+ if base_word[-3] in self.gubni + "н" and sum([1 for ch in base_word if ch in self.vowels]) > 1:
199
+ # Кравець, правдивець, але не Швець
200
+ return base_word[:-2] + 'ц*'
201
+ if base_word[-3:] == 'лец' and sum([1 for ch in base_word if ch in self.vowels]) > 1:
202
+ # Слепець, але не Лець
203
+ return base_word[:-3] + 'льц*'
204
+ return self.working_word
205
+
206
+ def female_rule1(self):
207
+ last2 = self.working_word[-2]
208
+ if self.working_word[-1] == 'а':
209
+ self.word_forms(self.working_word,
210
+ [last2 + 'и', self.inverse_gkh(last2) + 'і', last2 + 'у', last2 + 'ою',
211
+ self.inverse_gkh(last2) + 'і', last2 + 'о'], 2)
212
+ return True
213
+ elif self.working_word[-2:] == 'яя':
214
+ self.word_forms(self.working_word, ['ьої', 'ій', 'ю', 'ьою', 'ій', 'яя'], 2)
215
+ return True
216
+ elif self.working_word[-1] == 'я':
217
+ if last2 in self.vowels or self.is_apostrof(last2):
218
+ self.word_forms(self.working_word, ['ї', 'ї', 'ю', 'єю', 'ї', 'є'], 1)
219
+ return True
220
+ else:
221
+ self.word_forms(self.working_word,
222
+ [last2 + 'і', self.inverse_gkh(last2) + 'і', last2 + 'ю',
223
+ last2 + 'ею', self.inverse_gkh(last2) + 'і',
224
+ last2 + 'е'], 2)
225
+ return True
226
+ return False
227
+
228
+ def female_rule2(self):
229
+ if self.working_word[-1] in self.consonant + 'ь':
230
+ base_word = self.get_base_word(self.working_word)
231
+ apostrof = ''
232
+ duplicate = ''
233
+ last = base_word[-1]
234
+ last2 = base_word[-2]
235
+ if last in self.gubni and last2 in self.vowels:
236
+ apostrof = '’'
237
+ if last in 'дтзсцлн':
238
+ duplicate = last
239
+ if self.working_word[-1] == 'ь':
240
+ self.word_forms(base_word, ['і', 'і', 'ь', duplicate + apostrof + 'ю', 'і', 'е'])
241
+ return True
242
+ else:
243
+ self.word_forms(base_word, ['і', 'і', '', duplicate + apostrof + 'ю', 'і', 'е'])
244
+ return True
245
+ return False
246
+
247
+ def female_rule3(self):
248
+ last2 = self.working_word[-2]
249
+ if self.working_word[-2:] == 'ая':
250
+ self.word_forms(self.working_word, ['ої', 'ій', 'ую', 'ою', 'ій', 'ая'], 2)
251
+ return
252
+ if self.working_word[-1] == 'а' and (self.working_word[-2] in 'чнв' or self.working_word[-3:-1] in ['ьк']):
253
+ self.word_forms(self.working_word,
254
+ [last2 + 'ої', last2 + 'ій', last2 + 'у', last2 + 'ою',
255
+ last2 + 'ій', last2 + 'о'], 2)
256
+ return True
257
+ return False
258
+
259
+ def detect_second_group(self, word):
260
+ base_word = word
261
+ stack = []
262
+ while base_word[-1] in self.vowels + 'ь':
263
+ stack.append(base_word[-1])
264
+ base_word = base_word[0: -1]
265
+ last = 'Z'
266
+ if stack:
267
+ last = stack[- 1]
268
+ base_word_end = base_word[-1]
269
+ if base_word_end in self.neshyplyachi and last not in self.myaki:
270
+ return 1
271
+ elif base_word_end in self.shyplyachi and last not in self.myaki:
272
+ return 2
273
+ else:
274
+ return 3
275
+
276
+ def male_first_name(self):
277
+ if self.noun_is_not_declined():
278
+ return False
279
+ return self.rules_chain('male', [1, 2, 3])
280
+
281
+ def female_first_name(self):
282
+ return self.rules_chain('female', [1, 2])
283
+
284
+ def male_second_name(self):
285
+ if self.noun_is_not_declined():
286
+ return False
287
+ return self.rules_chain('male', [5, 1, 2, 3, 4])
288
+
289
+ def female_second_name(self):
290
+ return self.rules_chain('female', [3, 1])
291
+
292
+ def male_father_name(self):
293
+ if self.working_word[-2:] in ['ич', 'іч']:
294
+ self.word_forms(self.working_word, ['а', 'у', 'а', 'ем', 'у', 'у'])
295
+ return True
296
+ return False
297
+
298
+ def female_father_name(self):
299
+ if self.working_word[-3:] == 'вна':
300
+ self.word_forms(self.working_word, ['и', 'і', 'у', 'ою', 'і', 'о'], 1)
301
+ return True
302
+ return False
303
+
304
+ def gender_by_first_name(self, word):
305
+ self.working_word = word.word
306
+ man = 0
307
+ woman = 0
308
+ if self.working_word[-1] == 'й':
309
+ man += 0.9
310
+ if self.working_word in NAMES:
311
+ man += 30
312
+ if self.working_word in FEMALE_NAMES:
313
+ woman += 30
314
+ if self.working_word[-2:] in {'ро', 'яр', 'ус', 'ис', 'ст', 'ив', 'им', 'ам', 'іт', 'ід', 'ат', 'іб', 'нн',
315
+ 'ап', 'ік', 'ів', 'ас', 'ир', 'ил', 'ет', 'ип', 'ик', 'ол', 'лк', 'ім', 'єр',
316
+ 'ок', 'ур', 'їл', 'ох', 'сл', 'ад', 'др', 'ор', 'ар', 'ав', 'сь', 'ій', 'ло',
317
+ 'ко', 'ен', 'ин', 'юб', 'ін', 'ид', 'од', 'ем', 'ум', 'рт', 'ян', 'ег', 'он'}:
318
+ man += 0.5
319
+ if self.working_word[-3:] in {"'я", 'ая', 'га', 'да', 'дь', 'ер', 'ея', 'за', 'ит', 'иф', 'йя', 'на', 'па',
320
+ 'ря', 'ся', 'тя', 'фа', 'ха', 'ша', 'ія'}:
321
+ woman += 0.5
322
+ if self.working_word[-1] in self.consonant:
323
+ man += 0.01
324
+ if self.working_word[-1] == 'ь':
325
+ man += 0.02
326
+
327
+ word.set_gender(man, woman)
328
+
329
+ def gender_by_patronym(self, word):
330
+ self.working_word = word.word
331
+ if self.working_word[-2:] in ('ич', 'іч'):
332
+ word.set_gender(10, 0)
333
+ if self.working_word[-2:] == 'на':
334
+ word.set_gender(0, 12)
335
+
336
+ def detect_name_part(self, word):
337
+ name_part = word.word
338
+ self.working_word = name_part
339
+
340
+ first = 0
341
+ second = 0
342
+ father = 0
343
+
344
+ if self.working_word[-3:] in ('вна', 'чна', 'ліч') or self.working_word[-4:] in ('ьмич', 'ович', 'огли'):
345
+ father += 3
346
+ if (self.working_word[-3:] == 'тин'
347
+ or self.working_word[-4:] in ('ьмич', 'юбов', 'івна', 'явка', 'орив', 'кіян')):
348
+ first += 0.5
349
+ if name_part in NAMES_LIST:
350
+ first += 10
351
+ if self.working_word[-2:] in LASTNAME_ENDINGS2:
352
+ second += 0.4
353
+ if self.working_word[-3:] in LASTNAME_ENDINGS3:
354
+ second += 0.4
355
+
356
+ max_val = max(first, second, father)
357
+ if first == max_val:
358
+ word.name_part = NamePart.FIRSTNAME
359
+ elif second == max_val:
360
+ word.name_part = NamePart.LASTNAME
361
+ else:
362
+ word.name_part = NamePart.PATRONYMIC
363
+
364
+
365
+ class Lesya:
366
+ def __init__(self, name, gender=None):
367
+ self._core = NameCaseUa()
368
+ self._core.q(name, gender=gender)
369
+ self.forms = self._core.forms
370
+
371
+ @property
372
+ def nominative(self):
373
+ return self._core[CaseUA.NOMINATIVE]
374
+
375
+ @property
376
+ def genitive(self):
377
+ return self._core[CaseUA.GENITIVE]
378
+
379
+ @property
380
+ def dative(self):
381
+ return self._core[CaseUA.DATIVE]
382
+
383
+ @property
384
+ def accusative(self):
385
+ return self._core[CaseUA.ACCUSATIVE]
386
+
387
+ @property
388
+ def instrumental(self):
389
+ return self._core[CaseUA.INSTRUMENTAL]
390
+
391
+ @property
392
+ def prepositional(self):
393
+ return self._core[CaseUA.PREPOSITIONAL]
394
+
395
+ @property
396
+ def vocative(self):
397
+ return self._core[CaseUA.VOCATIVE]
398
+
399
+ def __getitem__(self, item):
400
+ if not self._core.finished:
401
+ return None
402
+ if isinstance(item, int) and item in range(self._core.case_count):
403
+ return self._core.forms[self._core.cases[item]]
404
+ if isinstance(item, str):
405
+ if item in self._core.cases:
406
+ return self._core.forms[item]
407
+ if item in CaseUA().all():
408
+ index = CaseUA().all().index(item)
409
+ return self[index]
@@ -0,0 +1,131 @@
1
+ NAMES = (
2
+ 'Євген', 'Євгеній', 'Євлампій', 'Євстафій', 'Єгор', 'Єремій', 'Іван', 'Ігор', 'Ізяслав', 'Іларіон', 'Ілля', 'Іоанн',
3
+ 'Адам', 'Адріан', 'Адріян', 'Азар', 'Азарій', 'Алевтин', 'Альберт', 'Амвросій', 'Анастас', 'Анастасій', 'Анатолій',
4
+ 'Андрон', 'Андрій', 'Антон', 'Антоній', 'Анісій', 'Аркадій', 'Арсен', 'Арсеній', 'Артем', 'Артур', 'Архип', 'Атанас',
5
+ 'Афанасій', 'Благовіст', 'Благодар', 'Богдан', 'Богуслав', 'Божемир', 'Божен', 'Болеслав', 'Боримир', 'Боримисл',
6
+ 'Борис', 'Борислав', 'Боронислав', 'Братислав', 'Братослав', 'Бронислав', 'Будимир', 'Будислав', 'Буйтур', 'Буревій',
7
+ 'Буревіст', "В'ячеслав", 'Вадим', 'Вакула', 'Валентин', 'Валерій', 'Варфоломій', 'Василь', 'Велемир', 'Велемудр',
8
+ 'Велет', 'Веремій', 'Вернислав', 'Вишеслав', 'Влад', 'Владислав', 'Власт', 'Вогнедар', 'Вогнян', 'Волелюб', 'Володар',
9
+ 'Володимир', 'Володислав', 'Воротислав', 'Воїслав', 'Вратислав', 'Всевлад', 'Всеволод', 'Всеслав', 'Вукол', 'Віктор',
10
+ 'Вір', 'Вірослав', 'Віталій', 'Вітан', 'Вітомир', 'Гаврило', 'Геннадій', 'Георгій', 'Герасим', 'Гліб', 'Гнат',
11
+ 'Годомир', 'Гордислав', 'Гордомисл', 'Гордослав', 'Гордій', 'Горимир', 'Горимисл', 'Горисвіт', 'Горислав', 'Градимир',
12
+ 'Градислав', 'Гранислав', 'Григорій', 'Давид', 'Далемил', 'Далемир', 'Данило', 'Данко', 'Даромир', 'Дарій', "Дем'ян",
13
+ 'Демид', 'Денис', 'Дмитро', 'Добрик', 'Добриня', 'Добромир', 'Добромисл', 'Доморад', 'Домослав', 'Домінік', 'Дорогомир',
14
+ 'Дорогомисл', 'Дорофій', 'Драган', 'Драгомир', 'Жадан', 'Ждан', 'Живослав', 'Захар', 'Захарій', 'Зборислав', 'Звенигор',
15
+ 'Звенимир', 'Зиновій', 'Злат', 'Златан', 'Златомир', 'Златоус', 'Зореслав', 'Зорян', 'Зіновій', 'Кий', 'Кирило', 'Киян',
16
+ 'Корнелій', 'Корнило', 'Корнилій', 'Корній', 'Костянтин', 'Кузьма', 'Курило', 'Кіндрат', 'Лаврентій', 'Лаврін',
17
+ 'Ладимир', 'Ладислав', 'Ладолюб', 'Ладомир', 'Лев', 'Левко', 'Леонід', "Лук'ян", 'Лука', 'Любим', 'Любовир', 'Любодар',
18
+ 'Любозар', 'Любомил', 'Любомир', 'Любомисл', 'Любослав', 'Людислав', 'Людомил', 'Лютобор', 'Лютомисл', 'Маврикій',
19
+ 'Магадар', 'Магамир', 'Магаслав', 'Май', 'Макар', 'Максим', "Мар'ян", 'Марко', 'Маркіян', 'Мартин', 'Матвій', 'Медомир',
20
+ 'Межамир', 'Мечислав', 'Микита', 'Микола', 'Милан', 'Милован', 'Милодар', 'Милослав', 'Мир', 'Миролюб', 'Мирон',
21
+ 'Мирослав', 'Михайло', 'Мстислав', 'Мудролюб', 'Мусій', 'Надій', 'Назарій', 'Наслав', 'Нестор', 'Никифор', 'Никодим',
22
+ 'Ничипор', 'Ничипір', 'Нікіта', 'Олег', 'Олекса', 'Олександр', 'Олексій', 'Олесь', 'Омелян', 'Ондрій', 'Онтін',
23
+ 'Онуфрій', 'Онісій', 'Опанас', 'Орест', 'Орхип', 'Остап', 'Остромир', 'Остромисл', 'Остромов', 'Охрім', "П'єр", 'Павло',
24
+ 'Панас', 'Пантелеймон', 'Перелюб', 'Перемил', 'Перемисл', 'Пересвіт', 'Переяслав', 'Першик', 'Петро', 'Пилип', 'Пимен',
25
+ 'Пимон', 'Пимін', 'Порфир', 'Потап', 'Рава', 'Рад', 'Радан', 'Радимир', 'Радован', 'Радомир', 'Радомисл', 'Радослав',
26
+ 'Ратибор', 'Ратимир', 'Рафаїл', 'Родослав', 'Родіон', 'Роксолан', 'Роман', 'Ростислав', 'Русан', 'Руслан', 'Сармат',
27
+ 'Свирид', 'Святовид', 'Святогор', 'Святолюб', 'Святополк', 'Святослав', 'Святояр', 'Світлан', 'Світлогор', 'Світогор',
28
+ 'Світодар', 'Світозар', 'Світолюб', 'Світослав', 'Світояр', 'Северин', 'Семен', 'Серафим', 'Сергій', 'Сидор', 'Сидір',
29
+ 'Силослав', 'Синьоок', 'Слава', 'Славомир', 'Сновид', 'Снозір', 'Сніжан', 'Спас', 'Станимир', 'Станислав', 'Стародум',
30
+ 'Степан', 'Стефаній', 'Стожар', 'Судислав', 'Тарас', 'Тарослав', 'Твердислав', 'Творислав', 'Теодозій', 'Терентій',
31
+ 'Тимофій', 'Тимур', 'Тихомир', 'Тихон', 'Толислав', 'Тригост', 'Тімох', 'Улас', 'Уличан', 'Устим', 'Фауст', 'Федор',
32
+ 'Федір', 'Флор', 'Фрол', 'Хвалимир', 'Ходота', 'Хорив', 'Хотимир', 'Христофор', 'Чара', 'Чеслав', 'Честислав', 'Щек',
33
+ 'Юліан', 'Юлій', 'Юрій', 'Юхим', 'Яволод', 'Явір', 'Яків', 'Ян', 'Яр', 'Ярема', 'Ярило', 'Яромисл', 'Ярополк',
34
+ 'Яросвіт', 'Ярослав', 'Ясновид', 'Ясногор', 'Яснозір', 'Яснолик')
35
+
36
+ FEMALE_NAMES = (
37
+ 'Єва', 'Євгенія', 'Євдокія', 'Євфросинія', 'Єкатерина', 'Єлизавета', 'Єпистима', 'Єпистимія', 'Єфросинія', 'Іванна',
38
+ 'Ігорина', 'Іларія', 'Ілона', 'Інга', 'Інеса', 'Інна', 'Ірина', 'Ірма', 'Ісидора', 'Ія', 'Ївга', 'Августа', 'Аврелія',
39
+ 'Аврора', 'Агапія', 'Агата', 'Агафоника', 'Агафія', 'Аглая', 'Аглаїда', 'Агнеса', 'Агнія', 'Агрипина', 'Ада',
40
+ 'Аделаїда', 'Аделіна', 'Адріана', 'Аеліта', 'Аза', 'Азалія', 'Акилина', 'Аксенія', 'Алевтина', 'Алла', 'Альбертина',
41
+ 'Альбертіна', 'Альбіна', 'Альвіна', 'Альфреда', 'Аліна', 'Аліса', 'Анастасія', 'Анатолія', 'Ангеліна', 'Анжела',
42
+ 'Анжеліка', 'Анна', 'Антонида', 'Антоніна', 'Антонія', 'Анфіса', 'Аполлонія', 'Аполлінарія', 'Аркадія', 'Арсена',
43
+ 'Арсенія', 'Артеміза', 'Артемізія', 'Артемісія', 'Артемія', 'Аріадна', 'Аскліпія', 'Ася', 'Атена', 'Аурика', 'Афанасія',
44
+ 'Афродіта', 'Афіна', 'Аїда', 'Бажана', 'Барбара', 'Беатриса', 'Белла', 'Берта', 'Богдана', 'Богуслава', 'Божена',
45
+ 'Болеслава', 'Борислава', "В'ячеслава", 'Валентина', 'Валерія', 'Ванда', 'Варвара', 'Василина', 'Васса', 'Векла',
46
+ 'Венера', 'Вероніка', 'Влада', 'Владислава', 'Владлена', 'Власта', 'Володимира', 'Всеслава', 'Вікторина', 'Вікторія',
47
+ 'Вілена', 'Віленіна', 'Віліна', 'Віола', 'Віолетта', 'Віра', 'Віргінія', 'Віринея', 'Віта', 'Віталіна', 'Віталія',
48
+ 'Галина', 'Ганна', 'Гафія', 'Гаїна', 'Гелена', 'Георгіна', 'Гертруда', 'Глафира', 'Глафіра', 'Гликерія', 'Горпина',
49
+ 'Густава', 'Дана', 'Дарина', 'Дарія', 'Дзвенислава', 'Дзвінка', 'Домаха', 'Домна', 'Домініка', 'Донна', 'Доротея',
50
+ 'Дорофея', 'Діана', 'Діна', 'Евеліна', 'Едіта', 'Елвіна', 'Елеонора', 'Елла', 'Ельвіра', 'Емма', 'Еммануель',
51
+ 'Еммануїла', 'Емілія', 'Еріка', 'Есмеральда', 'Естер', 'Есфір', 'Жадана', 'Жанна', 'Ждана', 'Жозефіна', 'Звенислава',
52
+ 'Звонимира', 'Земфіра', 'Злата', 'Зореслава', 'Зоря', 'Зоряна', 'Зоя', 'Зінаїда', 'Зірка', 'Йосипа', 'Йосифата',
53
+ 'Казимира', 'Калина', 'Камілла', 'Капитолина', 'Капитоліна', 'Капітоліна', 'Карина', 'Кароліна', 'Кассандра',
54
+ 'Катерина', 'Катря', 'Квітка', 'Кикилія', 'Килина', 'Клавдія', 'Клара', 'Клеопатра', 'Констанція', 'Кора', 'Корина',
55
+ 'Корнелія', 'Ксенія', 'Кіра', 'Лада', 'Лариса', 'Леля', 'Леся', 'Либідь', 'Ликера', 'Ликерія', 'Лисавета', 'Лола',
56
+ 'Лоліта', 'Лукина', 'Лукреція', 'Лукія', 'Любов', 'Любомира', 'Людмила', 'Лідія', 'Лілея', 'Лілія', 'Ліна', 'Мавка',
57
+ 'Магда', 'Магдалена', 'Магдалина', 'Майя', 'Макрина', 'Малуша', 'Мальва', 'Мальвіна', "Мар'я", "Мар'яна", 'Маргарита',
58
+ 'Марина', 'Марта', 'Мартина', 'Мартіна', 'Марфа', 'Маріамна', 'Маріанна', 'Марічка', 'Марія', 'Матрона', 'Меланія',
59
+ 'Мелітина', 'Мечислава', 'Милана', 'Мирослава', 'Михайлина', 'Млада', 'Мокрина', 'Моніка', 'Мотрона', 'Мотря',
60
+ 'Мстислава', 'Надія', 'Найда', 'Найдена', 'Настасія', 'Настя', 'Наталка', 'Наталя', 'Наталія', 'Неля', 'Нонна', 'Ніка',
61
+ 'Ніна', 'Нінель', 'Одарина', 'Одарка', 'Оксана', 'Оксенія', 'Октавія', 'Олександра', 'Олена', 'Олеся', 'Ольга',
62
+ 'Олівія', 'Олімпіада', 'Олімпія', 'Ореста', 'Орина', 'Орися', 'Осипа', 'Пава', 'Павла', 'Павлина', 'Палагна', 'Палазга',
63
+ 'Параскева', 'Параскевія', 'Парасковія', 'Пелагея', 'Пелагія', 'Пистина', 'Поліна', 'Пріська', 'Пульхера', 'Пульхерія',
64
+ 'Рада', 'Радимира', 'Радмила', 'Радогоста', 'Радомира', 'Радослава', 'Ракель', 'Рахель', 'Рахіль', 'Раїна', 'Раїса',
65
+ 'Ребекка', 'Ревека', 'Регіна', 'Рената', 'Римма', 'Рогволода', 'Рогнеда', 'Рогніда', 'Родослава', 'Рожана', 'Роза',
66
+ 'Розалія', 'Роксана', 'Роксолана', 'Романа', 'Романна', 'Романія', 'Ростислава', 'Рошель', 'Рузалія', 'Руслана',
67
+ 'Русудан', 'Русудана', 'Рут', 'Руф', 'Руфина', 'Руфіна', 'Ріана', 'Ріанна', 'Сабріна', 'Санта', 'Сара', 'Сарра',
68
+ 'Святогора', 'Святослава', 'Світлана', 'Світозара', 'Світояра', 'Севастіана', 'Северина', 'Секлета', 'Секлетина',
69
+ 'Серафима', 'Синезора', 'Слава', 'Сніжана', 'Соломія', 'Соня', 'Сосанна', 'Софія', 'Станіслава', 'Стелла', 'Стефанида',
70
+ 'Стефанія', 'Судислава', 'Сусанна', 'Сюзанна', 'Сільвія', 'Сімона', 'Тава', 'Тамара', 'Таїса', 'Таїсія', 'Текля',
71
+ 'Теодозія', 'Тереза', 'Тетяна', 'Тодора', 'Трояна', 'Тіна', 'Уляна', 'Фаїна', 'Февронія', 'Февросія', 'Федора',
72
+ 'Федосія', 'Фекла', 'Фелікса', 'Феліція', 'Феодора', 'Феодосія', 'Филікитата', 'Филіцата', 'Филіцитата', 'Фота',
73
+ 'Фотина', 'Фотинія', 'Харита', 'Харитина', 'Харитя', 'Хотина', 'Хриса', 'Христина', 'Христя', 'Хівря', 'Цветана',
74
+ 'Цвітана', 'Цецилія', 'Цецілія', 'Чеслава', 'Чухрія', 'Югина', 'Юдит', 'Юдита', 'Юдиф', 'Юзефа', 'Юлина', 'Юліана',
75
+ 'Юліанна', 'Юліанія', 'Юлія', 'Юнія', 'Юстина', 'Юхимина', 'Юхимія', 'Явдоха', 'Ядвіга', 'Яна', 'Яніна', 'Ярина',
76
+ 'Яромира', 'Ярослава')
77
+
78
+ FOREIGN_NAMES = (
79
+ 'Ігнас', 'Ігнатій', 'Іда', 'Іден', 'Ідріс', 'Ізабел', 'Ізідор', 'Ілай', 'Іммануїл', 'Імоджен', 'Інез', 'Інесса',
80
+ 'Ірвін', 'Ірена', 'Ісаак', 'Істер', 'Ісідора', 'Ієн', 'Айвес', 'Айвор', 'Айда', 'Айзек', 'Айк', 'Айлін', 'Айра',
81
+ 'Айрін', 'Айріс', 'Айседора', 'Аптон', 'Аян', 'Бейлі', 'Бентлі', 'Бет', 'Боб', 'Біл', 'Валентайн', 'Валері', 'Ванесса',
82
+ 'Вебб', 'Вейн', 'Венда', 'Венді', 'Вера', 'Верджил', 'Вериті', 'Водделл', 'Вокер', 'Воллі', 'Волт', 'Волтер', 'Вудро',
83
+ 'Вуді', "Вів'єн", 'Вілберт', 'Вілла', 'Віллі', 'Вілфред', 'Вілфрід', 'Вільгельміна', 'Вільям', 'Вінн', 'Вінні',
84
+ 'Вінстон', 'Вінфред', 'Вінфрід', 'Вінцент', 'Вініфред', 'Вірджил', 'Вірджинія', 'Віт', 'Вітні', "Г'ю", "Г'юберт",
85
+ "Г'юм", "Г'юґо", 'Гайрем', 'Ганнібал', 'Гарві', 'Гарді', 'Гарольд', 'Гаррі', 'Гаррієт', 'Гектор', 'Генк', 'Генрі',
86
+ 'Генрієтта', 'Герберт', 'Герміона', 'Гетті', 'Говард', 'Голлі', 'Голі', 'Гомер', 'Горацій', 'Гораціо', 'Гоуп', 'Гуго',
87
+ 'Гумберт', 'Гілларі', 'Гільда', 'Джайлс', 'Джаклін', 'Джасмін', 'Джаспер', 'Джастін', 'Джейд', 'Джейкоб', 'Джеймс',
88
+ 'Джейн', 'Джейсон', 'Джек', 'Джеклін', 'Дженніфер', 'Дженіс', 'Джералд', 'Джерард', 'Джервас', 'Джеремі', 'Джером',
89
+ 'Джессі', 'Джессіка', 'Джеффрі', 'Джилл', 'Джим', 'Джин', 'Джинджер', 'Джинна', 'Джинні', 'Джо', 'Джоаким', 'Джоан',
90
+ 'Джоб', 'Джоді', 'Джоел', 'Джозеф', 'Джозефіна', 'Джой', 'Джойс', 'Джолін', 'Джон', 'Джонас', 'Джонатан', 'Джордан',
91
+ 'Джордж', 'Джорджія', 'Джоселін', 'Джудіт', 'Джуелл', 'Джуліан', 'Джуліус', 'Джулія', 'Джун', 'Джія', 'Дональд',
92
+ 'Ебенезер', 'Ева', 'Еван', 'Еванджелін', 'Едвард', 'Едвін', 'Едвіна', 'Едді', 'Едмунд', 'Едіт', 'Едґар', 'Ездра',
93
+ 'Езра', 'Ейлін', 'Елвін', 'Елдред', 'Елеазар', 'Елеанор', 'Елейн', 'Еллен', 'Елліс', 'Елмер', 'Елрой', 'Елса', 'Елтон',
94
+ 'Елфріда', 'Еліас', 'Елізабет', 'Елінора', 'Еліот', 'Еліса', 'Еммануїл', 'Еммелін', 'Еммерік', 'Еммі', 'Емілі', 'Енох',
95
+ 'Ервін', 'Ернест', 'Ернестіна', 'Ерік', 'Ерін', 'Есмі', 'Етель', 'Еґберт', 'Жаклін', 'Зак', 'Йоланда', 'Кайла',
96
+ 'Каміла', 'Карен', 'Карл', 'Каріна', 'Карісса', 'Квентін', 'Кевін', 'Кейт', 'Келлі', 'Кеннет', 'Кент', 'Керрі',
97
+ 'Кетлін', 'Кортні', 'Корі', 'Кріс', 'Крістал', 'Крістофер', 'Крістіан', 'Крістіна', "Ксав'єр", 'Кімберлі', 'Лавінія',
98
+ 'Лайонел', 'Ламберт', 'Ланс', 'Ланселот', 'Ларрі', 'Лаура', 'Леа', 'Ленора', 'Лео', 'Леон', 'Леона', 'Леонард',
99
+ 'Леопольд', 'Леслі', 'Лестер', 'Летиція', 'Летіша', 'Лора', 'Лорейн', 'Лорен', 'Лоренс', 'Лорін', 'Лотта', 'Лоґан',
100
+ 'Лукас', 'Луїза', 'Луїс', 'Льюїс', 'Люк', 'Люсінда', 'Лі', 'Лізбет', 'Ліллі', 'Ліліана', 'Лін', 'Лінда', 'Ліндон',
101
+ 'Ліса', 'Лія', 'Майкл', 'Майлз', 'Майра', 'Майрон', 'Макс', 'Максиміліан', 'Малкольм', 'Мануїл', 'Манфред', "Мар'янна",
102
+ 'Марджері', 'Марджорі', 'Марк', 'Маркус', 'Марлен', 'Марсел', 'Марсі', 'Мартін', 'Маріон', 'Марґарет', 'Матіас',
103
+ 'Матільда', 'Маґдален', 'Маґнус', 'Медді', 'Меделін', 'Мей', 'Мейбел', 'Мелані', 'Мелвін', 'Мелінда', 'Мелісса',
104
+ 'Мередіт', 'Мерил', 'Мерилін', 'Мерлін', 'Меррі', 'Мерсі', 'Мері', 'Меріан', 'Мерілу', 'Меттью', 'Метью', 'Меґан',
105
+ 'Меґґі', 'Мод', 'Мозес', 'Моллі', 'Моріс', 'Морґан', 'Мюріель', 'Мідж', 'Мілдред', 'Міллі', 'Мірабелла', 'Міранда',
106
+ 'Мішел', 'Найджел', 'Наомі', 'Натаніель', 'Неллі', 'Ненсі', 'Ноа', 'Ноел', 'Ноемі', 'Ной', 'Нора', 'Норберт', 'Норма',
107
+ 'Норман', 'Ніколас', 'Ніколь', 'Нікі', 'Ніл', 'Ніро', 'Одетта', 'Олівер', 'Омар', 'Осборн', 'Освальд', 'Освін', 'Оскар',
108
+ 'Оттвел', 'Отто', 'Оттілія', 'Оуен', 'Офелія', 'Памела', 'Патриція', 'Патрік', 'Педді', 'Пенелопа', 'Пенні', 'Переґрін',
109
+ 'Перл', 'Персиваль', 'Персі', 'Періс', 'Пол', 'Пола', 'Поллі', 'Поппі', 'Порція', 'Порша', 'Проспер', 'Пруденс',
110
+ 'Прімроуз', 'Прісцилла', 'Пірс', 'Пітер', 'Райс', 'Ральф', 'Рандольф', 'Ранульф', 'Рафаель', 'Ред', 'Реджина',
111
+ 'Реджинальд', 'Реймонд', 'Рейнард', 'Рейнер', 'Рейнольд', 'Рейчел', 'Рендел', 'Ренні', 'Роб', 'Роббі', 'Роберт',
112
+ 'Роберта', 'Робін', 'Рода', 'Родерік', 'Роджер', 'Родні', 'Розалінда', 'Розмарі', 'Рой', 'Роланд', 'Рольф', 'Рональд',
113
+ 'Ронні', 'Росс', 'Роуз', 'Рубен', 'Рубі', 'Рудольф', 'Руперт', 'Руфус', 'Рік', 'Ріта', 'Річард', 'Сабіна', 'Сайлас',
114
+ 'Саймон', 'Саллі', 'Саманта', 'Сандра', 'Себастьян', 'Севідж', 'Седі', 'Селіна', 'Сем', 'Семюел', 'Серена', 'Сетон',
115
+ 'Сибілла', 'Сильвестер', 'Симон', 'Скарлетт', 'Скот', 'Сол', 'Спенсер', 'Стефані', 'Стефен', 'Стівен', 'Сьюард',
116
+ 'Сьюзан', 'Сьюзанна', 'Сідні', 'Табіта', 'Тай', 'Тед', 'Тедді', 'Темзін', 'Теобальд', 'Теодора', 'Теофілус', 'Теренс',
117
+ 'Террі', 'Тит', 'Тобі', 'Тобіас', 'Том', 'Томазіна', 'Томас', 'Томмі', 'Тоні', 'Труді', 'Трістан', 'Тімоті', 'Тіффані',
118
+ 'Ульріка', 'Улісс', 'Уна', 'Урсула', 'Урія', 'Фабіан', 'Фалкон', 'Фанні', 'Фейт', 'Фелікс', 'Фелісія', 'Фердінанд',
119
+ 'Ферфакс', 'Флора', 'Флоренс', 'Форд', 'Франс', 'Франциск', 'Фред', 'Фредерік', 'Фредеріка', 'Френк', 'Френсіс',
120
+ 'Фріда', 'Фібі', 'Філандер', 'Філліп', 'Філомена', 'Філіп', 'Філіппа', "Х'юго", 'Харві', 'Хенк', 'Холлі', 'Холі',
121
+ 'Хілларі', 'Челсі', 'Шейн', 'Шерон', 'Шон', 'Юджин', 'Юм', 'Юна', 'Юніс', 'Янґ', 'Ґабрієла', 'Ґабрієль', 'Ґай', 'Ґарет',
122
+ 'Ґаррі', 'Ґвен', 'Ґвендолін', 'Ґевін', 'Ґеддес', 'Ґедеон', 'Ґерда', 'Ґерміна', 'Ґеррі', 'Ґертруда', 'Ґерті', 'Ґледіс',
123
+ 'Ґлен', 'Ґлорія', 'Ґодвін', 'Ґор', 'Ґордон', 'Ґрей', 'Ґрейс', 'Ґреґорі', 'Ґризельда', 'Ґріффін', 'Ґустав', 'Ґідеон',
124
+ 'Ґілберт')
125
+
126
+ with open('lesya/language/language_data/family-names.csv') as f:
127
+ lines = [l.strip() for l in f.readlines()]
128
+
129
+ LASTNAME_ENDINGS2 = {l[-2:] for l in lines}
130
+ LASTNAME_ENDINGS3 = {l[-3:] for l in lines if len(l) > 4}
131
+ LASTNAME_ENDINGS4 = {l[-4:] for l in lines if len(l) > 6}
@@ -0,0 +1,371 @@
1
+ from lesya.language.case_helpers import NamePart, CaseUA, NCLNameCaseWord, Gender
2
+
3
+
4
+ class NameCaseCore:
5
+ """
6
+ Base class for name case handling.
7
+ """
8
+ def __init__(self):
9
+ self.case_count = 7
10
+ # System readiness:
11
+ # - All words are identified (know which part of FIO the word belongs to)
12
+ # - Gender is determined for all words
13
+ # If all is done, the flag is set to true, adding a new word resets the flag to false
14
+ self._ready = False
15
+ # If all current words have been declined and each word already has a declination result,
16
+ # then true. Adding a new word resets the flag to false
17
+ self._finished = False
18
+ # Array contains elements of type NCLNameCaseWord. These are all the words to be processed and declined
19
+ self.words = []
20
+ # Variable into which the current working word is placed
21
+ self.working_word = None
22
+ # Array contains the result of the declination of the word - the word in all cases
23
+ self.last_result = []
24
+ # Array contains information about which words from the array <var>$this->words</var> belong to
25
+ # the surname, which to the patronymic, and which to the first name. The array is needed because when adding words
26
+ # we do not always know which part of the FIO it is, so after identifying all words, an array of
27
+ # indexes for quick search is generated.
28
+ self.index = {NamePart.FIRSTNAME: [], NamePart.LASTNAME: [], NamePart.PATRONYMIC: []}
29
+ self.cases = CaseUA().all('ua')
30
+
31
+ def __repr__(self):
32
+ return f'{self.words}'
33
+
34
+ def forms(self):
35
+ pass
36
+
37
+ @property
38
+ def ready(self):
39
+ return self._ready
40
+
41
+ @property
42
+ def finished(self):
43
+ return self._finished
44
+
45
+ def full_reset(self):
46
+ """
47
+ Resets all information to the initial state. Clears all words added to the system.
48
+ After execution, the system is ready to work from scratch.
49
+ :return: NCLNameCaseCore
50
+ """
51
+ self.words = []
52
+ self.last_result = []
53
+ self.index = {NamePart.FIRSTNAME: [], NamePart.LASTNAME: [], NamePart.PATRONYMIC: []}
54
+ self.not_ready()
55
+ return self
56
+
57
+ def not_ready(self):
58
+ """
59
+ Sets flags that the system is not ready and the words have not yet been declined.
60
+ """
61
+ self._ready = False
62
+ self._finished = False
63
+
64
+ def rules_chain(self, gender, rules_array):
65
+ for rule_id in rules_array:
66
+ rule_method = f"{gender}_rule{rule_id}"
67
+ if getattr(self, rule_method)():
68
+ return True
69
+ return False
70
+
71
+ def word_forms(self, word, endings, replace_last=0):
72
+ result = [self.working_word]
73
+ # word = NCLStr.substr(word, 0, len(word) - replace_last)
74
+ word = word[0:len(word) - replace_last]
75
+ for case_index in range(1, self.case_count):
76
+ result.append(word + endings[case_index - 1])
77
+ self.last_result = result
78
+
79
+ def set_first_name(self, firstname=""):
80
+ if firstname:
81
+ index = len(self.words)
82
+ self.words.append(NCLNameCaseWord(firstname))
83
+ self.words[index].name_part = NamePart.FIRSTNAME
84
+ self.not_ready()
85
+ return self
86
+
87
+ def set_second_name(self, secondname=""):
88
+ if secondname:
89
+ index = len(self.words)
90
+ self.words.append(NCLNameCaseWord(secondname))
91
+ self.words[index].name_part = NamePart.LASTNAME
92
+ self.not_ready()
93
+ return self
94
+
95
+ def set_father_name(self, fathername=""):
96
+ if fathername:
97
+ index = len(self.words)
98
+ self.words.append(NCLNameCaseWord(fathername))
99
+ self.words[index].name_part = NamePart.PATRONYMIC
100
+ self.not_ready()
101
+ return self
102
+
103
+ def set_gender(self, gender):
104
+ for word in self.words:
105
+ word.set_true_gender(gender)
106
+ return self
107
+
108
+ def set_full_name(self, second_name="", first_name="", father_name=""):
109
+ self.set_first_name(first_name)
110
+ self.set_second_name(second_name)
111
+ self.set_father_name(father_name)
112
+ return self
113
+
114
+ def set_name(self, firstname=""):
115
+ return self.set_first_name(firstname)
116
+
117
+ def set_last_name(self, secondname=""):
118
+ return self.set_second_name(secondname)
119
+
120
+ def set_sir_name(self, secondname=""):
121
+ return self.set_second_name(secondname)
122
+
123
+ def prepare_name_part(self, word):
124
+ if not word.name_part:
125
+ self.detect_name_part(word)
126
+
127
+ def prepare_all_name_parts(self):
128
+ for word in self.words:
129
+ self.prepare_name_part(word)
130
+
131
+ def prepare_gender(self, word):
132
+ if not word.is_gender_solved():
133
+ name_part = word.name_part
134
+ if name_part == NamePart.FIRSTNAME:
135
+ self.gender_by_first_name(word)
136
+ elif name_part == NamePart.PATRONYMIC:
137
+ self.gender_by_patronym(word)
138
+ elif name_part == NamePart.LASTNAME:
139
+ self.gender_by_lastname(word)
140
+
141
+ def solve_gender(self):
142
+ for word in self.words:
143
+ if word.is_gender_solved():
144
+ self.set_gender(word.gender())
145
+ return True
146
+ man, woman = 0, 0
147
+ for word in self.words:
148
+ self.prepare_gender(word)
149
+ gender = word.get_gender()
150
+ man += gender[Gender.MAN]
151
+ woman += gender[Gender.WOMAN]
152
+ self.set_gender(Gender.MAN if man > woman else Gender.WOMAN)
153
+ return True
154
+
155
+ def generate_index(self):
156
+ self.index = {NamePart.FIRSTNAME: [], NamePart.LASTNAME: [], NamePart.PATRONYMIC: []}
157
+ for index, word in enumerate(self.words):
158
+ name_part = word.name_part
159
+ self.index[name_part].append(index)
160
+
161
+ def prepare_everything(self):
162
+ if not self._ready:
163
+ self.prepare_all_name_parts()
164
+ self.solve_gender()
165
+ self.generate_index()
166
+ self._ready = True
167
+
168
+ def split_full_name(self, fullname):
169
+ fullname = fullname.strip()
170
+ words_list = [w for w in fullname.split() if w]
171
+ for word in words_list:
172
+ self.words.append(NCLNameCaseWord(word))
173
+ self.prepare_everything()
174
+ return self.words
175
+
176
+ def word_case(self, word):
177
+ gender = 'male' if word.gender() == Gender.MAN else 'female'
178
+ name_part = {NamePart.FIRSTNAME: 'first', NamePart.LASTNAME: 'second', NamePart.PATRONYMIC: 'father'}.get(
179
+ word.name_part)
180
+ method = f'{gender}_{name_part}_name'
181
+ tmp = word.word_orig
182
+ cur_words = tmp.split('-')
183
+ o_cur_words = []
184
+ result = ['']*self.case_count
185
+ cnt = len(cur_words)
186
+ for k, cur_word in enumerate(cur_words):
187
+ is_norm_rules = True
188
+ o_ncw = NCLNameCaseWord(cur_word)
189
+ if word.name_part == NamePart.LASTNAME and cnt > 1 and k < cnt - 1:
190
+ if cur_word.lower() not in ('тулуз'):
191
+ self.detect_name_part(o_ncw)
192
+ is_norm_rules = o_ncw.name_part == NamePart.LASTNAME
193
+ else:
194
+ is_norm_rules = False
195
+
196
+ self.working_word = cur_word
197
+
198
+ if is_norm_rules and getattr(self, method)():
199
+ result_tmp = self.last_result
200
+ else:
201
+ result_tmp = [cur_word] * self.case_count
202
+
203
+ o_ncw.set_name_cases(result_tmp)
204
+ o_cur_words.append(o_ncw)
205
+
206
+ for o_ncw in o_cur_words:
207
+ namecases = o_ncw.get_name_cases()
208
+ for k, namecase in enumerate(namecases):
209
+ if result[k]:
210
+ result[k] = result[k] + '-' + namecase
211
+ else:
212
+ result[k] = namecase
213
+
214
+ word.set_name_cases(result, False)
215
+
216
+ def all_word_cases(self):
217
+ if not self._finished:
218
+ self.prepare_everything()
219
+ for word in self.words:
220
+ self.word_case(word)
221
+ self._finished = True
222
+
223
+ def get_word_case(self, word, number=None):
224
+ cases = word.get_name_cases()
225
+ if number is None or number < 0 or number > (self.case_count - 1):
226
+ return cases
227
+ else:
228
+ return cases[number]
229
+
230
+ def get_cases_connected(self, index_array, number=None):
231
+ ready = []
232
+ for index in index_array:
233
+ ready.append(self.get_word_case(self.words[index], number))
234
+
235
+ all_cases = len(ready)
236
+ if all_cases:
237
+ if isinstance(ready[0], list):
238
+ result = []
239
+ for case in range(self.case_count):
240
+ tmp = []
241
+ for i in range(all_cases):
242
+ tmp.append(ready[i][case])
243
+ result.append(' '.join(tmp))
244
+ return result
245
+ else:
246
+ return ' '.join(ready)
247
+ return ''
248
+
249
+ def get_firstname_case(self, number=None):
250
+ self.all_word_cases()
251
+ return self.get_cases_connected(self.index[NamePart.FIRSTNAME], number)
252
+
253
+ def get_lastname_case(self, number=None):
254
+ self.all_word_cases()
255
+ return self.get_cases_connected(self.index[NamePart.LASTNAME], number)
256
+
257
+ def get_patronymic_case(self, number=None):
258
+ self.all_word_cases()
259
+ return self.get_cases_connected(self.index[NamePart.PATRONYMIC], number)
260
+
261
+ def get_formatted_array(self, format_):
262
+ if isinstance(format_, list):
263
+ return self.get_formatted_array_forced(format_)
264
+ result = []
265
+ cases = {
266
+ NamePart.LASTNAME: self.get_cases_connected(self.index[NamePart.LASTNAME]),
267
+ NamePart.FIRSTNAME: self.get_cases_connected(self.index[NamePart.FIRSTNAME]),
268
+ NamePart.PATRONYMIC: self.get_cases_connected(self.index[NamePart.PATRONYMIC])
269
+ }
270
+ for cur_case in range(self.case_count):
271
+ line = ""
272
+ for symbol in format_.split():
273
+ if symbol == 'S':
274
+ line += cases[NamePart.LASTNAME][cur_case]
275
+ elif symbol == 'N':
276
+ line += cases[NamePart.FIRSTNAME][cur_case]
277
+ elif symbol == 'F':
278
+ line += cases[NamePart.PATRONYMIC][cur_case]
279
+ else:
280
+ line += symbol
281
+ result.append(line)
282
+ return result
283
+
284
+ def get_formatted_array_forced(self, format_):
285
+ result = []
286
+ cases = []
287
+ for word in format_:
288
+ cases.append(word.get_name_cases())
289
+ for curCase in range(self.case_count):
290
+ line = ""
291
+ for value in cases:
292
+ line += value[curCase] + ' '
293
+ result.append(line.strip())
294
+ return result
295
+
296
+ @staticmethod
297
+ def get_formatted_forced(case_num=0, format_=None):
298
+ format_ = format_ if format_ else []
299
+ result = ""
300
+ for word in format_:
301
+ cases = word.get_name_cases()
302
+ result += cases[case_num] + ' '
303
+ return result.strip()
304
+
305
+ def get_formatted(self, case_num=0, format_="S N F"):
306
+ self.all_word_cases()
307
+ if case_num is None or not case_num:
308
+ return self.get_formatted_array(format_)
309
+ elif isinstance(format_, list):
310
+ return self.get_formatted_forced(case_num, format_)
311
+ else:
312
+ result = ""
313
+ for symbol in format_.split():
314
+ if symbol == 'S':
315
+ result += self.get_lastname_case(case_num)
316
+ elif symbol == 'N':
317
+ result += self.get_firstname_case(case_num)
318
+ elif symbol == 'F':
319
+ result += self.get_patronymic_case(case_num)
320
+ else:
321
+ result += symbol
322
+ return result
323
+
324
+ def q(self, fullname, case_num=None, gender=None):
325
+ self.full_reset()
326
+ format_ = self.split_full_name(fullname)
327
+ if gender:
328
+ self.set_gender(gender)
329
+ return self.get_formatted(case_num, format_)
330
+
331
+ def male_first_name(self):
332
+ return False
333
+
334
+ def female_first_name(self):
335
+ return False
336
+
337
+ def male_second_name(self):
338
+ return False
339
+
340
+ def female_second_name(self):
341
+ return False
342
+
343
+ def male_father_name(self):
344
+ return False
345
+
346
+ def female_father_name(self):
347
+ return False
348
+
349
+ def gender_by_first_name(self, word):
350
+ pass
351
+
352
+ def gender_by_lastname(self, word):
353
+ pass
354
+
355
+ def gender_by_patronym(self, word):
356
+ pass
357
+
358
+ def detect_name_part(self, word):
359
+ pass
360
+
361
+ def __getitem__(self, item):
362
+ if not self._finished:
363
+ return None
364
+ if isinstance(item, int) and item in range(self.case_count):
365
+ return self.forms[self.cases[item]]
366
+ if isinstance(item, str):
367
+ if item in self.cases:
368
+ return self.forms[item]
369
+ if item in CaseUA().all():
370
+ index = CaseUA().all().index(item)
371
+ return self[index]
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2018 The Python Packaging Authority
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -0,0 +1,109 @@
1
+ Metadata-Version: 2.1
2
+ Name: lesya
3
+ Version: 0.0.1
4
+ Summary: Python package to form Ukrainian names cases : 'Леся Українка' -- 'Лесі Українки' , 'Лесі Українці', 'Лесею Українкою' ... etc.
5
+ Author: Dmytro Ustynov
6
+ Author-email: Dmytro Ustynov <ustynov.dev@gmail.com>
7
+ License: Copyright (c) 2018 The Python Packaging Authority
8
+
9
+ Permission is hereby granted, free of charge, to any person obtaining a copy
10
+ of this software and associated documentation files (the "Software"), to deal
11
+ in the Software without restriction, including without limitation the rights
12
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13
+ copies of the Software, and to permit persons to whom the Software is
14
+ furnished to do so, subject to the following conditions:
15
+
16
+ The above copyright notice and this permission notice shall be included in all
17
+ copies or substantial portions of the Software.
18
+
19
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25
+ SOFTWARE.
26
+ Project-URL: Homepage, https://github.com/ustynov-py-dev/lesya.git
27
+ Project-URL: Issues, https://github.com/ustynov-py-dev/lesya/issues
28
+ Classifier: Programming Language :: Python :: 3
29
+ Classifier: License :: OSI Approved :: MIT License
30
+ Classifier: Operating System :: OS Independent
31
+ Requires-Python: >=3.8
32
+ Description-Content-Type: text/markdown
33
+ License-File: LICENSE
34
+
35
+ # Lesya
36
+
37
+ Lesya is a simple Python package for declining Ukrainian personal names into Ukrainian grammatical cases (_відмінки_): NOMINATIVE, GENITIVE, DATIVE, ACCUSATIVE, INSTRUMENTAL, PREPOSITIONAL, and VOCATIVE (_називний, родовий, давальний, знахідний, орудний, місцевий, кличний_).
38
+
39
+ ## Usage example
40
+
41
+ The most common usage is to convert a name into all cases:
42
+
43
+ ```python
44
+ from lesya import Lesya
45
+
46
+ person = Lesya('Леся Українка')
47
+ print(person.nominative) # Леся Українка
48
+ print(person.genitive) # Лесі Українки
49
+ print(person.dative) # Лесі Українці
50
+ print(person.accusative) # Лесю Українку
51
+ print(person.instrumental) # Лесею Українкою
52
+ print(person.prepositional) # Лесі Українці
53
+ print(person.vocative) # Лесе Українко
54
+ ```
55
+
56
+ You can also get a name in a specific case:
57
+
58
+ ```python
59
+ from lesya import Lesya
60
+ from lesya import CaseUA
61
+
62
+ person = Lesya('Тарас Григорович Шевченко')
63
+ print(person[CaseUA.DATIVE]) # Тарасу Григоровичу Шевченку
64
+ print(person[CaseUA.PREPOSITIONAL]) # Тарасові Григоровичу Шевченкові
65
+ print(person['орудний']) # Тарасом Григоровичем Шевченком
66
+ ```
67
+
68
+ ## Double names support
69
+
70
+ Lesya works well with double last names:
71
+
72
+ ```python
73
+ from lesya import Lesya
74
+
75
+ person = Lesya('Іван Семенович Нечуй-Левицький')
76
+ print(person.forms)
77
+ # Output:
78
+ {
79
+ 'називний': 'Іван Семенович Нечуй-Левицький',
80
+ 'родовий': 'Івана Семеновича Нечуя-Левицького',
81
+ 'давальний': 'Івану Семеновичу Нечуєві-Левицькому',
82
+ 'знахідний': 'Івана Семеновича Нечуя-Левицького',
83
+ 'орудний': 'Іваном Семеновичем Нечуєм-Левицьким',
84
+ 'місцевий': 'Іванові Семеновичу Нечуєві-Левицькому',
85
+ 'кличний': 'Іване Семеновичу Нечую-Левицький'
86
+ }
87
+ ```
88
+
89
+ ## `forms` attribute
90
+
91
+ You can get all cases at once using the forms attribute. It returns a dictionary where the keys are case names (in lowercase Ukrainian), and the values are the corresponding declined names.
92
+
93
+ ## Foreign names support
94
+
95
+ Lesya supports foreign names. However, since it does not use an ML model to automatically detect a person's gender, it works better if you explicitly provide the gender:
96
+
97
+ ```python
98
+ from lesya import Lesya
99
+ from lesya import CaseUA
100
+ from lesya import Gender
101
+
102
+ person = Lesya('Джозеф Байден', gender='male')
103
+ print(person[CaseUA.DATIVE]) # Джозефу Байдену
104
+ print(person[CaseUA.PREPOSITIONAL]) # Джозефові Байденові
105
+
106
+ person = Lesya('Камала Гаріс', gender=Gender.FEMALE)
107
+ print(person[CaseUA.DATIVE]) # Камалі Гаріс
108
+ print(person[CaseUA.PREPOSITIONAL]) # Камалі Гаріс
109
+ ```
@@ -0,0 +1,11 @@
1
+ lesya/__init__.py,sha256=3y0FfeVwUx8tdy9tiRJx_YnJ2pQXQzBBym-X2lKZ7ZU,122
2
+ lesya/language/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
+ lesya/language/case_helpers.py,sha256=hmQoMqZ_BKvnWFd6nYir_fYBjOFBJSGZtolgVW9uKek,4649
4
+ lesya/language/name_case.py,sha256=86pCwS676D_sd-nS9KcMrLra0exnKSe9gVTRL0meWH8,17665
5
+ lesya/language/name_constants.py,sha256=IQpG9oNgH_Mkb4jQb8hQoULfIXZOyPp_z1tKkB3iYkc,22141
6
+ lesya/language/nc_core.py,sha256=fB101ZwrFsxUdpAGawKteha3dN2U6b8nkurm2OzrOq0,13003
7
+ lesya-0.0.1.dist-info/LICENSE,sha256=2bm9uFabQZ3Ykb_SaSU_uUbAj2-htc6WJQmS_65qD00,1073
8
+ lesya-0.0.1.dist-info/METADATA,sha256=wrsgKbWxiq9Oi1_4I5DfOIbWR1rMvNeLQP-1K7-DoHY,4990
9
+ lesya-0.0.1.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
10
+ lesya-0.0.1.dist-info/top_level.txt,sha256=G6dTztx6aA4OObUA0lP5EBa39WTk7XJzGvfMMV9F4IE,6
11
+ lesya-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (75.6.0)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1 @@
1
+ lesya