tokmor 1.2.9__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.
Files changed (70) hide show
  1. tokmor/__init__.py +77 -0
  2. tokmor/api.py +194 -0
  3. tokmor/assets.py +365 -0
  4. tokmor/base.py +238 -0
  5. tokmor/brahmic.py +516 -0
  6. tokmor/cjk.py +497 -0
  7. tokmor/domain/__init__.py +11 -0
  8. tokmor/domain/sentiment.py +198 -0
  9. tokmor/factory.py +394 -0
  10. tokmor/indic.py +289 -0
  11. tokmor/inventory.py +51 -0
  12. tokmor/legacy_api.py +143 -0
  13. tokmor/lemma_store.py +102 -0
  14. tokmor/lookup_keys.py +145 -0
  15. tokmor/models/domain/sentiment/en.json +54 -0
  16. tokmor/models/domain/sentiment/ko.json +52 -0
  17. tokmor/models/seg_lexicon/km_wordfreq.pkl +0 -0
  18. tokmor/models/seg_lexicon/km_wordlist.pkl +0 -0
  19. tokmor/models/seg_lexicon/lo_wordfreq.pkl +0 -0
  20. tokmor/models/seg_lexicon/lo_wordlist.pkl +0 -0
  21. tokmor/models/seg_lexicon/my_wordfreq.pkl +0 -0
  22. tokmor/models/seg_lexicon/my_wordlist.pkl +0 -0
  23. tokmor/models/seg_lexicon/th_wordfreq.pkl +0 -0
  24. tokmor/models/seg_lexicon/th_wordlist.pkl +0 -0
  25. tokmor/models/seg_lexicon/zh_extra_dict.json +35 -0
  26. tokmor/models/seg_lexicon/zh_wordfreq.pkl +0 -0
  27. tokmor/morphology/__init__.py +395 -0
  28. tokmor/morphology/advanced_base.py +472 -0
  29. tokmor/morphology/arabic_advanced.py +247 -0
  30. tokmor/morphology/chinese.py +736 -0
  31. tokmor/morphology/chinese_advanced.py +425 -0
  32. tokmor/morphology/english.py +315 -0
  33. tokmor/morphology/english_advanced.py +560 -0
  34. tokmor/morphology/french_advanced.py +237 -0
  35. tokmor/morphology/german_advanced.py +343 -0
  36. tokmor/morphology/hindi_advanced.py +258 -0
  37. tokmor/morphology/japanese.py +417 -0
  38. tokmor/morphology/japanese_advanced.py +589 -0
  39. tokmor/morphology/korean.py +534 -0
  40. tokmor/morphology/korean_advanced.py +603 -0
  41. tokmor/morphology/russian_advanced.py +217 -0
  42. tokmor/morphology/spanish_advanced.py +226 -0
  43. tokmor/morphology/templates/__init__.py +32 -0
  44. tokmor/morphology/templates/arabic_script_template.py +162 -0
  45. tokmor/morphology/templates/brahmic_template.py +181 -0
  46. tokmor/morphology/templates/cyrillic_template.py +168 -0
  47. tokmor/morphology/templates/latin_template.py +235 -0
  48. tokmor/morphology/templates/other_scripts_template.py +475 -0
  49. tokmor/morphology/thai_native.py +274 -0
  50. tokmor/morphology/tier2.py +477 -0
  51. tokmor/morphology/tier3.py +449 -0
  52. tokmor/morphology/tier4.py +410 -0
  53. tokmor/morphology/unified.py +855 -0
  54. tokmor/morphology/universal_fallback.py +398 -0
  55. tokmor/ner_prep.py +747 -0
  56. tokmor/offline.py +89 -0
  57. tokmor/preprocess.py +80 -0
  58. tokmor/resources.py +288 -0
  59. tokmor/routing.py +147 -0
  60. tokmor/rtl.py +309 -0
  61. tokmor/schema.py +17 -0
  62. tokmor/sns_tags.py +281 -0
  63. tokmor/space_based.py +272 -0
  64. tokmor/token_quality.py +1185 -0
  65. tokmor/unified_tokens.py +228 -0
  66. tokmor-1.2.9.dist-info/METADATA +103 -0
  67. tokmor-1.2.9.dist-info/RECORD +70 -0
  68. tokmor-1.2.9.dist-info/WHEEL +5 -0
  69. tokmor-1.2.9.dist-info/licenses/LICENSE +22 -0
  70. tokmor-1.2.9.dist-info/top_level.txt +1 -0
@@ -0,0 +1,477 @@
1
+ """
2
+ Tier 2 Languages - Major Regional Languages
3
+ ============================================
4
+
5
+ 15 languages: pt, it, nl, pl, tr, vi, th, id, he, fa, uk, el, cs, ro, sv
6
+ """
7
+
8
+ from .templates.latin_template import LatinScriptAnalyzer
9
+ from .templates.cyrillic_template import CyrillicScriptAnalyzer
10
+ from .templates.arabic_script_template import ArabicScriptAnalyzer
11
+ from .templates.other_scripts_template import HebrewScriptAnalyzer, GreekScriptAnalyzer, ThaiScriptAnalyzer
12
+
13
+
14
+ # =============================================================================
15
+ # Portuguese (pt)
16
+ # =============================================================================
17
+ class PortugueseAnalyzer(LatinScriptAnalyzer):
18
+ LANG_CODE = "pt"
19
+ LANG_NAME = "Portuguese"
20
+ VERB_INFINITIVE_SUFFIXES = ['ar', 'er', 'ir']
21
+ VERB_PARTICIPLE_SUFFIXES = ['ado', 'ido', 'ando', 'endo', 'indo']
22
+ NOUN_PLURAL_SUFFIXES = ['s', 'es', 'ões', 'ães', 'ais', 'éis', 'óis']
23
+ ADJECTIVE_SUFFIXES = ['oso', 'osa', 'ivo', 'iva', 'al', 'el', 'il']
24
+ ADVERB_SUFFIXES = ['mente']
25
+
26
+ def _build_base_dictionary(self):
27
+ self.function_words = {
28
+ 'o': 'DET', 'a': 'DET', 'os': 'DET', 'as': 'DET', 'um': 'DET', 'uma': 'DET',
29
+ 'eu': 'PRON', 'tu': 'PRON', 'ele': 'PRON', 'ela': 'PRON', 'nós': 'PRON', 'eles': 'PRON',
30
+ 'de': 'PREP', 'em': 'PREP', 'para': 'PREP', 'com': 'PREP', 'por': 'PREP',
31
+ 'e': 'CONJ', 'ou': 'CONJ', 'mas': 'CONJ', 'que': 'CONJ',
32
+ 'não': 'NEG', 'muito': 'ADV', 'bem': 'ADV', 'mais': 'ADV',
33
+ }
34
+ self.irregular_verbs = {
35
+ 'sou': 'ser', 'é': 'ser', 'são': 'ser', 'foi': 'ser', 'eram': 'ser',
36
+ 'tenho': 'ter', 'tem': 'ter', 'tinha': 'ter', 'teve': 'ter',
37
+ 'vou': 'ir', 'vai': 'ir', 'vamos': 'ir', 'foi': 'ir',
38
+ 'faço': 'fazer', 'faz': 'fazer', 'fez': 'fazer',
39
+ 'estou': 'estar', 'está': 'estar', 'estava': 'estar',
40
+ }
41
+
42
+
43
+ # =============================================================================
44
+ # Italian (it)
45
+ # =============================================================================
46
+ class ItalianAnalyzer(LatinScriptAnalyzer):
47
+ LANG_CODE = "it"
48
+ LANG_NAME = "Italian"
49
+ VERB_INFINITIVE_SUFFIXES = ['are', 'ere', 'ire']
50
+ VERB_PARTICIPLE_SUFFIXES = ['ato', 'ito', 'uto', 'ando', 'endo']
51
+ NOUN_PLURAL_SUFFIXES = ['i', 'e', 'a']
52
+ ADJECTIVE_SUFFIXES = ['oso', 'osa', 'ale', 'ile', 'ivo', 'iva']
53
+ ADVERB_SUFFIXES = ['mente']
54
+
55
+ def _build_base_dictionary(self):
56
+ self.function_words = {
57
+ 'il': 'DET', 'la': 'DET', 'lo': 'DET', 'i': 'DET', 'le': 'DET', 'gli': 'DET',
58
+ 'un': 'DET', 'una': 'DET', 'uno': 'DET',
59
+ 'io': 'PRON', 'tu': 'PRON', 'lui': 'PRON', 'lei': 'PRON', 'noi': 'PRON', 'loro': 'PRON',
60
+ 'di': 'PREP', 'a': 'PREP', 'da': 'PREP', 'in': 'PREP', 'con': 'PREP', 'su': 'PREP', 'per': 'PREP',
61
+ 'e': 'CONJ', 'o': 'CONJ', 'ma': 'CONJ', 'che': 'CONJ',
62
+ 'non': 'NEG', 'molto': 'ADV', 'bene': 'ADV', 'più': 'ADV',
63
+ }
64
+ self.irregular_verbs = {
65
+ 'sono': 'essere', 'è': 'essere', 'siamo': 'essere', 'era': 'essere', 'stato': 'essere',
66
+ 'ho': 'avere', 'ha': 'avere', 'abbiamo': 'avere', 'aveva': 'avere', 'avuto': 'avere',
67
+ 'vado': 'andare', 'va': 'andare', 'andiamo': 'andare', 'andato': 'andare',
68
+ 'faccio': 'fare', 'fa': 'fare', 'fatto': 'fare',
69
+ }
70
+
71
+
72
+ # =============================================================================
73
+ # Dutch (nl)
74
+ # =============================================================================
75
+ class DutchAnalyzer(LatinScriptAnalyzer):
76
+ LANG_CODE = "nl"
77
+ LANG_NAME = "Dutch"
78
+ VERB_INFINITIVE_SUFFIXES = ['en']
79
+ VERB_PARTICIPLE_SUFFIXES = ['t', 'd', 'de', 'te', 'end']
80
+ NOUN_PLURAL_SUFFIXES = ['en', 's', 'eren']
81
+ ADJECTIVE_SUFFIXES = ['ig', 'lijk', 'isch', 'baar']
82
+ ADVERB_SUFFIXES = []
83
+
84
+ def _build_base_dictionary(self):
85
+ self.function_words = {
86
+ 'de': 'DET', 'het': 'DET', 'een': 'DET',
87
+ 'ik': 'PRON', 'jij': 'PRON', 'hij': 'PRON', 'zij': 'PRON', 'wij': 'PRON', 'jullie': 'PRON',
88
+ 'van': 'PREP', 'in': 'PREP', 'op': 'PREP', 'met': 'PREP', 'voor': 'PREP', 'naar': 'PREP',
89
+ 'en': 'CONJ', 'of': 'CONJ', 'maar': 'CONJ', 'dat': 'CONJ',
90
+ 'niet': 'NEG', 'geen': 'NEG', 'heel': 'ADV', 'zeer': 'ADV', 'goed': 'ADV',
91
+ }
92
+ self.irregular_verbs = {
93
+ 'ben': 'zijn', 'is': 'zijn', 'zijn': 'zijn', 'was': 'zijn', 'waren': 'zijn', 'geweest': 'zijn',
94
+ 'heb': 'hebben', 'hebt': 'hebben', 'heeft': 'hebben', 'had': 'hebben', 'gehad': 'hebben',
95
+ 'ga': 'gaan', 'gaat': 'gaan', 'ging': 'gaan', 'gegaan': 'gaan',
96
+ 'kom': 'komen', 'komt': 'komen', 'kwam': 'komen', 'gekomen': 'komen',
97
+ }
98
+
99
+
100
+ # =============================================================================
101
+ # Polish (pl)
102
+ # =============================================================================
103
+ class PolishAnalyzer(LatinScriptAnalyzer):
104
+ LANG_CODE = "pl"
105
+ LANG_NAME = "Polish"
106
+ VERB_INFINITIVE_SUFFIXES = ['ć', 'c']
107
+ VERB_PARTICIPLE_SUFFIXES = ['ący', 'ąca', 'ące', 'any', 'ana', 'ane', 'ony', 'ona', 'one']
108
+ NOUN_PLURAL_SUFFIXES = ['y', 'i', 'e', 'a', 'owie']
109
+ ADJECTIVE_SUFFIXES = ['ny', 'na', 'ne', 'wy', 'wa', 'we', 'ki', 'ka', 'ke']
110
+ ADVERB_SUFFIXES = ['o', 'ie', 'e']
111
+
112
+ def _build_base_dictionary(self):
113
+ self.function_words = {
114
+ 'ja': 'PRON', 'ty': 'PRON', 'on': 'PRON', 'ona': 'PRON', 'ono': 'PRON', 'my': 'PRON', 'oni': 'PRON',
115
+ 'w': 'PREP', 'na': 'PREP', 'z': 'PREP', 'do': 'PREP', 'od': 'PREP', 'po': 'PREP', 'przy': 'PREP',
116
+ 'i': 'CONJ', 'lub': 'CONJ', 'ale': 'CONJ', 'że': 'CONJ', 'bo': 'CONJ',
117
+ 'nie': 'NEG', 'tak': 'ADV', 'bardzo': 'ADV', 'dobrze': 'ADV',
118
+ }
119
+ self.irregular_verbs = {
120
+ 'jestem': 'być', 'jest': 'być', 'są': 'być', 'był': 'być', 'była': 'być',
121
+ 'mam': 'mieć', 'ma': 'mieć', 'mają': 'mieć', 'miał': 'mieć',
122
+ 'idę': 'iść', 'idzie': 'iść', 'szedł': 'iść', 'szła': 'iść',
123
+ }
124
+
125
+
126
+ # =============================================================================
127
+ # Turkish (tr)
128
+ # =============================================================================
129
+ class TurkishAnalyzer(LatinScriptAnalyzer):
130
+ LANG_CODE = "tr"
131
+ LANG_NAME = "Turkish"
132
+ VERB_INFINITIVE_SUFFIXES = ['mak', 'mek']
133
+ # 현재진행형 어미
134
+ VERB_PRESENT_SUFFIXES = [
135
+ 'iyorum', 'ıyorum', 'uyorum', 'üyorum', # 1인칭 단수
136
+ 'iyorsun', 'ıyorsun', 'uyorsun', 'üyorsun', # 2인칭 단수
137
+ 'iyor', 'ıyor', 'uyor', 'üyor', # 3인칭 단수
138
+ 'iyoruz', 'ıyoruz', 'uyoruz', 'üyoruz', # 1인칭 복수
139
+ 'iyorsunuz', 'ıyorsunuz', 'uyorsunuz', 'üyorsunuz', # 2인칭 복수
140
+ 'iyorlar', 'ıyorlar', 'uyorlar', 'üyorlar', # 3인칭 복수
141
+ ]
142
+ # 과거형 어미
143
+ VERB_PAST_SUFFIXES = [
144
+ 'dim', 'dım', 'dum', 'düm', 'tim', 'tım', 'tum', 'tüm', # 1인칭
145
+ 'din', 'dın', 'dun', 'dün', 'tin', 'tın', 'tun', 'tün', # 2인칭
146
+ 'di', 'dı', 'du', 'dü', 'ti', 'tı', 'tu', 'tü', # 3인칭
147
+ 'dik', 'dık', 'duk', 'dük', 'tik', 'tık', 'tuk', 'tük', # 1인칭 복수
148
+ 'diniz', 'dınız', 'dunuz', 'dünüz', # 2인칭 복수
149
+ 'diler', 'dılar', 'dular', 'düler', # 3인칭 복수
150
+ ]
151
+ # 미래형 어미
152
+ VERB_FUTURE_SUFFIXES = [
153
+ 'eceğim', 'acağım', 'yacağım', 'yeceğim', # 1인칭
154
+ 'eceksin', 'acaksın', # 2인칭
155
+ 'ecek', 'acak', # 3인칭
156
+ 'eceğiz', 'acağız', # 1인칭 복수
157
+ ]
158
+ VERB_PARTICIPLE_SUFFIXES = ['yor', 'iyor', 'uyor', 'üyor', 'dı', 'di', 'du', 'dü', 'mış', 'miş']
159
+ NOUN_PLURAL_SUFFIXES = ['lar', 'ler']
160
+ # 명사 격조사
161
+ NOUN_CASE_SUFFIXES = [
162
+ 'ı', 'i', 'u', 'ü', # 목적격
163
+ 'a', 'e', 'ya', 'ye', # 여격 (방향)
164
+ 'da', 'de', 'ta', 'te', # 처소격
165
+ 'dan', 'den', 'tan', 'ten', # 탈격
166
+ 'ın', 'in', 'un', 'ün', 'nın', 'nin', 'nun', 'nün', # 소유격
167
+ ]
168
+ ADJECTIVE_SUFFIXES = ['lı', 'li', 'lu', 'lü', 'sız', 'siz', 'suz', 'süz']
169
+ ADVERB_SUFFIXES = ['ca', 'ce', 'ça', 'çe']
170
+
171
+ def _build_base_dictionary(self):
172
+ self.function_words = {
173
+ 'bir': 'DET', 'bu': 'DET', 'şu': 'DET',
174
+ 'ben': 'PRON', 'sen': 'PRON', 'o': 'PRON', 'biz': 'PRON', 'siz': 'PRON', 'onlar': 'PRON',
175
+ 'beni': 'PRON', 'seni': 'PRON', 'onu': 'PRON', 'bizi': 'PRON', 'sizi': 'PRON', 'onları': 'PRON',
176
+ 'de': 'PSP', 'da': 'PSP', 'den': 'PSP', 'dan': 'PSP', 'e': 'PSP', 'a': 'PSP',
177
+ 've': 'CONJ', 'veya': 'CONJ', 'ama': 'CONJ', 'fakat': 'CONJ',
178
+ 'değil': 'NEG', 'hayır': 'NEG', 'çok': 'ADV', 'iyi': 'ADV', 'daha': 'ADV',
179
+ 'için': 'POSTP', 'ile': 'POSTP', 'gibi': 'POSTP', 'kadar': 'POSTP',
180
+ }
181
+ # 일반 동사
182
+ self.common_verbs = {
183
+ 'gidiyorum': 'V', 'gidiyorsun': 'V', 'gidiyor': 'V',
184
+ 'geliyorum': 'V', 'geliyorsun': 'V', 'geliyor': 'V',
185
+ 'yapıyorum': 'V', 'yapıyorsun': 'V', 'yapıyor': 'V',
186
+ 'istiyorum': 'V', 'istiyorsun': 'V', 'istiyor': 'V',
187
+ 'biliyorum': 'V', 'biliyorsun': 'V', 'biliyor': 'V',
188
+ 'seviyorum': 'V', 'seviyorsun': 'V', 'seviyor': 'V',
189
+ 'bakıyorum': 'V', 'bakıyorsun': 'V', 'bakıyor': 'V',
190
+ 'okuyorum': 'V', 'okuyorsun': 'V', 'okuyor': 'V',
191
+ 'yazıyorum': 'V', 'yazıyorsun': 'V', 'yazıyor': 'V',
192
+ 'alıyorum': 'V', 'alıyorsun': 'V', 'alıyor': 'V',
193
+ 'veriyorum': 'V', 'veriyorsun': 'V', 'veriyor': 'V',
194
+ 'çalışıyorum': 'V', 'çalışıyorsun': 'V', 'çalışıyor': 'V',
195
+ 'konuşuyorum': 'V', 'konuşuyorsun': 'V', 'konuşuyor': 'V',
196
+ 'koşuyorum': 'V', 'koşuyorsun': 'V', 'koşuyor': 'V',
197
+ 'yiyorum': 'V', 'yiyorsun': 'V', 'yiyor': 'V',
198
+ 'içiyorum': 'V', 'içiyorsun': 'V', 'içiyor': 'V',
199
+ 'oturuyorum': 'V', 'oturuyorsun': 'V', 'oturuyor': 'V',
200
+ 'yürüyorum': 'V', 'yürüyorsun': 'V', 'yürüyor': 'V',
201
+ 'bekliyorum': 'V', 'bekliyorsun': 'V', 'bekliyor': 'V',
202
+ 'anlıyorum': 'V', 'anlıyorsun': 'V', 'anlıyor': 'V',
203
+ 'düşünüyorum': 'V', 'düşünüyorsun': 'V', 'düşünüyor': 'V',
204
+ 'görüyorum': 'V', 'görüyorsun': 'V', 'görüyor': 'V',
205
+ 'duyuyorum': 'V', 'duyuyorsun': 'V', 'duyuyor': 'V',
206
+ # 과거형
207
+ 'gittim': 'V', 'gittin': 'V', 'gitti': 'V',
208
+ 'geldim': 'V', 'geldin': 'V', 'geldi': 'V',
209
+ 'yaptım': 'V', 'yaptın': 'V', 'yaptı': 'V',
210
+ 'istedim': 'V', 'istedin': 'V', 'istedi': 'V',
211
+ 'oldum': 'V', 'oldun': 'V', 'oldu': 'V',
212
+ # var/yok
213
+ 'var': 'V', 'yok': 'V',
214
+ }
215
+ # 일반 명사 (+ 격조사 붙은 형태)
216
+ self.common_nouns = {
217
+ # 목적격(-ı,-i,-u,-ü) 포함
218
+ 'okul': 'N', 'okula': 'N', 'okulda': 'N', 'okuldan': 'N', 'okulun': 'N', 'okulu': 'N',
219
+ 'ev': 'N', 'eve': 'N', 'evde': 'N', 'evden': 'N', 'evin': 'N', 'evi': 'N',
220
+ 'iş': 'N', 'işe': 'N', 'işte': 'N', 'işten': 'N', 'işin': 'N', 'işi': 'N',
221
+ 'yol': 'N', 'yola': 'N', 'yolda': 'N', 'yoldan': 'N', 'yolun': 'N', 'yolu': 'N',
222
+ 'su': 'N', 'suyu': 'N', 'suda': 'N', 'sudan': 'N', 'suyun': 'N',
223
+ 'gün': 'N', 'güne': 'N', 'günde': 'N', 'günden': 'N', 'günün': 'N', 'günü': 'N',
224
+ 'yıl': 'N', 'yıla': 'N', 'yılda': 'N', 'yıldan': 'N', 'yılın': 'N', 'yılı': 'N',
225
+ 'insan': 'N', 'insana': 'N', 'insanda': 'N', 'insanın': 'N', 'insanı': 'N',
226
+ 'şehir': 'N', 'şehre': 'N', 'şehirde': 'N', 'şehrin': 'N', 'şehri': 'N',
227
+ 'ülke': 'N', 'ülkeye': 'N', 'ülkede': 'N', 'ülkenin': 'N', 'ülkeyi': 'N',
228
+ 'kitap': 'N', 'kitaba': 'N', 'kitapta': 'N', 'kitabın': 'N', 'kitabı': 'N',
229
+ 'elma': 'N', 'elmayı': 'N', 'elmada': 'N', 'elmanın': 'N',
230
+ 'araba': 'N', 'arabayı': 'N', 'arabada': 'N', 'arabanın': 'N',
231
+ 'kapı': 'N', 'kapıyı': 'N', 'kapıda': 'N', 'kapının': 'N',
232
+ 'para': 'N', 'parayı': 'N', 'parada': 'N', 'paranın': 'N',
233
+ }
234
+
235
+ def _analyze_word(self, word: str, offset: int, domain) -> 'Morpheme':
236
+ """터키어 특화 단어 분석 - common_verbs, common_nouns 체크"""
237
+ from .advanced_base import Morpheme
238
+ word_lower = word.lower()
239
+
240
+ # 1. User dictionary
241
+ if word_lower in self._user_dictionary:
242
+ lemma, pos_tag, _ = self._user_dictionary[word_lower]
243
+ return Morpheme(surface=word, lemma=lemma, pos=pos_tag, start=offset, end=offset + len(word))
244
+
245
+ # 2. Domain dictionary
246
+ domain_sense = self._get_domain_sense(word_lower, domain)
247
+ if domain_sense:
248
+ return Morpheme(surface=word, lemma=domain_sense[0], pos=domain_sense[1], start=offset, end=offset + len(word))
249
+
250
+ # 3. Function words
251
+ if hasattr(self, 'function_words') and word_lower in self.function_words:
252
+ return Morpheme(surface=word, lemma=word_lower, pos=self.function_words[word_lower], start=offset, end=offset + len(word))
253
+
254
+ # 4. Common verbs (터키어 특화)
255
+ if hasattr(self, 'common_verbs') and word_lower in self.common_verbs:
256
+ return Morpheme(surface=word, lemma=word_lower, pos=self.common_verbs[word_lower], start=offset, end=offset + len(word))
257
+
258
+ # 5. Common nouns (터키어 특화)
259
+ if hasattr(self, 'common_nouns') and word_lower in self.common_nouns:
260
+ return Morpheme(surface=word, lemma=word_lower, pos=self.common_nouns[word_lower], start=offset, end=offset + len(word))
261
+
262
+ # 6. Irregular verbs
263
+ if hasattr(self, 'irregular_verbs') and word_lower in self.irregular_verbs:
264
+ return Morpheme(surface=word, lemma=self.irregular_verbs[word_lower], pos='V', start=offset, end=offset + len(word))
265
+
266
+ # 7. 동사 어미 분석 (현재진행/과거/미래)
267
+ for suffix in self.VERB_PRESENT_SUFFIXES + self.VERB_PAST_SUFFIXES + self.VERB_FUTURE_SUFFIXES:
268
+ if word_lower.endswith(suffix) and len(word_lower) > len(suffix) + 1:
269
+ return Morpheme(surface=word, lemma=word_lower, pos='V', start=offset, end=offset + len(word))
270
+
271
+ # 8. Extended dictionary (optional external)
272
+ if hasattr(self, 'extended_verbs') and word_lower in self.extended_verbs:
273
+ return Morpheme(surface=word, lemma=word_lower, pos='V', start=offset, end=offset + len(word))
274
+ if hasattr(self, 'extended_adjs') and word_lower in self.extended_adjs:
275
+ return Morpheme(surface=word, lemma=word_lower, pos='ADJ', start=offset, end=offset + len(word))
276
+ if hasattr(self, 'extended_advs') and word_lower in self.extended_advs:
277
+ return Morpheme(surface=word, lemma=word_lower, pos='ADV', start=offset, end=offset + len(word))
278
+ if hasattr(self, 'extended_nouns') and word_lower in self.extended_nouns:
279
+ return Morpheme(surface=word, lemma=word_lower, pos=self.extended_nouns[word_lower], start=offset, end=offset + len(word))
280
+
281
+ # 9. Morphological analysis (기본)
282
+ lemma, pos_tag = self._analyze_morphology(word)
283
+ return Morpheme(surface=word, lemma=lemma, pos=pos_tag, start=offset, end=offset + len(word))
284
+
285
+
286
+ # =============================================================================
287
+ # Vietnamese (vi)
288
+ # =============================================================================
289
+ class VietnameseAnalyzer(LatinScriptAnalyzer):
290
+ LANG_CODE = "vi"
291
+ LANG_NAME = "Vietnamese"
292
+ # Vietnamese is isolating - no inflection
293
+ VERB_INFINITIVE_SUFFIXES = []
294
+ VERB_PARTICIPLE_SUFFIXES = []
295
+ NOUN_PLURAL_SUFFIXES = []
296
+
297
+ def _build_base_dictionary(self):
298
+ self.function_words = {
299
+ 'tôi': 'PRON', 'bạn': 'PRON', 'anh': 'PRON', 'chị': 'PRON', 'em': 'PRON', 'nó': 'PRON',
300
+ 'chúng': 'PRON', 'họ': 'PRON',
301
+ 'của': 'PREP', 'trong': 'PREP', 'trên': 'PREP', 'dưới': 'PREP', 'với': 'PREP',
302
+ 'và': 'CONJ', 'hoặc': 'CONJ', 'nhưng': 'CONJ', 'vì': 'CONJ',
303
+ 'không': 'NEG', 'rất': 'ADV', 'lắm': 'ADV', 'quá': 'ADV',
304
+ 'là': 'V', 'có': 'V', 'được': 'V', 'đi': 'V', 'đến': 'V',
305
+ }
306
+
307
+
308
+ # =============================================================================
309
+ # Thai (th) - Uses template
310
+ # =============================================================================
311
+ class ThaiAnalyzer(ThaiScriptAnalyzer):
312
+ pass
313
+
314
+
315
+ # =============================================================================
316
+ # Indonesian (id)
317
+ # =============================================================================
318
+ class IndonesianAnalyzer(LatinScriptAnalyzer):
319
+ LANG_CODE = "id"
320
+ LANG_NAME = "Indonesian"
321
+ VERB_INFINITIVE_SUFFIXES = ['kan', 'i']
322
+ NOUN_PLURAL_SUFFIXES = [] # Reduplication, not suffix
323
+
324
+ def _build_base_dictionary(self):
325
+ self.function_words = {
326
+ 'yang': 'REL', 'ini': 'DET', 'itu': 'DET',
327
+ 'saya': 'PRON', 'aku': 'PRON', 'kamu': 'PRON', 'dia': 'PRON', 'kami': 'PRON', 'mereka': 'PRON',
328
+ 'di': 'PREP', 'ke': 'PREP', 'dari': 'PREP', 'dengan': 'PREP', 'untuk': 'PREP',
329
+ 'dan': 'CONJ', 'atau': 'CONJ', 'tetapi': 'CONJ', 'karena': 'CONJ',
330
+ 'tidak': 'NEG', 'bukan': 'NEG', 'sangat': 'ADV', 'sudah': 'ADV', 'akan': 'ADV',
331
+ 'adalah': 'V', 'ada': 'V', 'menjadi': 'V',
332
+ }
333
+
334
+
335
+ # =============================================================================
336
+ # Hebrew (he) - Uses template
337
+ # =============================================================================
338
+ class HebrewAnalyzer(HebrewScriptAnalyzer):
339
+ pass
340
+
341
+
342
+ # =============================================================================
343
+ # Persian/Farsi (fa)
344
+ # =============================================================================
345
+ class PersianAnalyzer(ArabicScriptAnalyzer):
346
+ LANG_CODE = "fa"
347
+ LANG_NAME = "Persian"
348
+
349
+ def _build_base_dictionary(self):
350
+ self.prefixes = {'می': 'PRES', 'ن': 'NEG'}
351
+ self.suffixes = {
352
+ 'ها': 'PL', 'ان': 'PL',
353
+ 'م': 'PRON', 'ت': 'PRON', 'ش': 'PRON', 'مان': 'PRON', 'تان': 'PRON', 'شان': 'PRON',
354
+ }
355
+ self.function_words = {
356
+ 'من': 'PRON', 'تو': 'PRON', 'او': 'PRON', 'ما': 'PRON', 'شما': 'PRON', 'آنها': 'PRON',
357
+ 'در': 'PREP', 'به': 'PREP', 'از': 'PREP', 'با': 'PREP', 'برای': 'PREP',
358
+ 'و': 'CONJ', 'یا': 'CONJ', 'اما': 'CONJ', 'که': 'CONJ',
359
+ 'نه': 'NEG', 'خیلی': 'ADV', 'خوب': 'ADV',
360
+ 'است': 'V', 'هست': 'V', 'بود': 'V', 'شد': 'V',
361
+ }
362
+
363
+
364
+ # =============================================================================
365
+ # Ukrainian (uk)
366
+ # =============================================================================
367
+ class UkrainianAnalyzer(CyrillicScriptAnalyzer):
368
+ LANG_CODE = "uk"
369
+ LANG_NAME = "Ukrainian"
370
+ VERB_INFINITIVE_SUFFIX = 'ти'
371
+ REFLEXIVE_SUFFIX = 'ся'
372
+
373
+ def _build_base_dictionary(self):
374
+ self.function_words = {
375
+ 'я': 'PRON', 'ти': 'PRON', 'він': 'PRON', 'вона': 'PRON', 'воно': 'PRON',
376
+ 'ми': 'PRON', 'ви': 'PRON', 'вони': 'PRON',
377
+ 'в': 'PREP', 'на': 'PREP', 'з': 'PREP', 'до': 'PREP', 'від': 'PREP', 'за': 'PREP',
378
+ 'і': 'CONJ', 'та': 'CONJ', 'але': 'CONJ', 'що': 'CONJ', 'як': 'CONJ',
379
+ 'не': 'NEG', 'так': 'ADV', 'дуже': 'ADV', 'добре': 'ADV',
380
+ }
381
+ self.irregular_verbs = {
382
+ 'є': 'бути', 'був': 'бути', 'була': 'бути', 'було': 'бути', 'були': 'бути',
383
+ 'маю': 'мати', 'має': 'мати', 'мав': 'мати',
384
+ 'іду': 'йти', 'йде': 'йти', 'йшов': 'йти', 'йшла': 'йти',
385
+ }
386
+
387
+
388
+ # =============================================================================
389
+ # Greek (el) - Uses template
390
+ # =============================================================================
391
+ class GreekAnalyzer(GreekScriptAnalyzer):
392
+ pass
393
+
394
+
395
+ # =============================================================================
396
+ # Czech (cs)
397
+ # =============================================================================
398
+ class CzechAnalyzer(LatinScriptAnalyzer):
399
+ LANG_CODE = "cs"
400
+ LANG_NAME = "Czech"
401
+ VERB_INFINITIVE_SUFFIXES = ['t', 'ti', 'ci']
402
+ NOUN_PLURAL_SUFFIXES = ['y', 'i', 'e', 'a', 'ové']
403
+ ADJECTIVE_SUFFIXES = ['ý', 'á', 'é', 'í']
404
+
405
+ def _build_base_dictionary(self):
406
+ self.function_words = {
407
+ 'já': 'PRON', 'ty': 'PRON', 'on': 'PRON', 'ona': 'PRON', 'ono': 'PRON',
408
+ 'my': 'PRON', 'vy': 'PRON', 'oni': 'PRON',
409
+ 'v': 'PREP', 'na': 'PREP', 'z': 'PREP', 'do': 'PREP', 's': 'PREP', 'k': 'PREP',
410
+ 'a': 'CONJ', 'nebo': 'CONJ', 'ale': 'CONJ', 'že': 'CONJ',
411
+ 'ne': 'NEG', 'ano': 'ADV', 'velmi': 'ADV', 'dobře': 'ADV',
412
+ }
413
+ self.irregular_verbs = {
414
+ 'jsem': 'být', 'jsi': 'být', 'je': 'být', 'jsme': 'být', 'jste': 'být', 'jsou': 'být',
415
+ 'byl': 'být', 'byla': 'být', 'bylo': 'být', 'byli': 'být',
416
+ 'mám': 'mít', 'má': 'mít', 'máme': 'mít', 'měl': 'mít',
417
+ }
418
+
419
+
420
+ # =============================================================================
421
+ # Romanian (ro)
422
+ # =============================================================================
423
+ class RomanianAnalyzer(LatinScriptAnalyzer):
424
+ LANG_CODE = "ro"
425
+ LANG_NAME = "Romanian"
426
+ VERB_INFINITIVE_SUFFIXES = ['a', 'ea', 'e', 'i', 'î']
427
+ NOUN_PLURAL_SUFFIXES = ['i', 'e', 'uri', 'le']
428
+ ADJECTIVE_SUFFIXES = ['os', 'oasă', 'ic', 'ică']
429
+
430
+ def _build_base_dictionary(self):
431
+ self.function_words = {
432
+ 'eu': 'PRON', 'tu': 'PRON', 'el': 'PRON', 'ea': 'PRON', 'noi': 'PRON', 'ei': 'PRON', 'ele': 'PRON',
433
+ 'un': 'DET', 'o': 'DET', 'niște': 'DET',
434
+ 'în': 'PREP', 'pe': 'PREP', 'la': 'PREP', 'de': 'PREP', 'cu': 'PREP', 'din': 'PREP',
435
+ 'și': 'CONJ', 'sau': 'CONJ', 'dar': 'CONJ', 'că': 'CONJ',
436
+ 'nu': 'NEG', 'da': 'ADV', 'foarte': 'ADV', 'bine': 'ADV',
437
+ }
438
+ self.irregular_verbs = {
439
+ 'sunt': 'fi', 'ești': 'fi', 'este': 'fi', 'suntem': 'fi', 'sunteți': 'fi',
440
+ 'am': 'avea', 'ai': 'avea', 'are': 'avea', 'avem': 'avea',
441
+ }
442
+
443
+
444
+ # =============================================================================
445
+ # Swedish (sv)
446
+ # =============================================================================
447
+ class SwedishAnalyzer(LatinScriptAnalyzer):
448
+ LANG_CODE = "sv"
449
+ LANG_NAME = "Swedish"
450
+ VERB_INFINITIVE_SUFFIXES = ['a']
451
+ VERB_PARTICIPLE_SUFFIXES = ['r', 'de', 't', 'ande', 'ende']
452
+ NOUN_PLURAL_SUFFIXES = ['or', 'ar', 'er', 'n', 'en']
453
+ ADJECTIVE_SUFFIXES = ['ig', 'lig', 'isk', 'sk']
454
+
455
+ def _build_base_dictionary(self):
456
+ self.function_words = {
457
+ 'en': 'DET', 'ett': 'DET', 'den': 'DET', 'det': 'DET', 'de': 'DET',
458
+ 'jag': 'PRON', 'du': 'PRON', 'han': 'PRON', 'hon': 'PRON', 'vi': 'PRON', 'de': 'PRON',
459
+ 'i': 'PREP', 'på': 'PREP', 'till': 'PREP', 'från': 'PREP', 'med': 'PREP', 'av': 'PREP',
460
+ 'och': 'CONJ', 'eller': 'CONJ', 'men': 'CONJ', 'att': 'CONJ',
461
+ 'inte': 'NEG', 'mycket': 'ADV', 'bra': 'ADV', 'nu': 'ADV',
462
+ }
463
+ self.irregular_verbs = {
464
+ 'är': 'vara', 'var': 'vara', 'varit': 'vara',
465
+ 'har': 'ha', 'hade': 'ha', 'haft': 'ha',
466
+ 'går': 'gå', 'gick': 'gå', 'gått': 'gå',
467
+ 'kommer': 'komma', 'kom': 'komma', 'kommit': 'komma',
468
+ }
469
+
470
+
471
+ # Export all
472
+ __all__ = [
473
+ 'PortugueseAnalyzer', 'ItalianAnalyzer', 'DutchAnalyzer', 'PolishAnalyzer',
474
+ 'TurkishAnalyzer', 'VietnameseAnalyzer', 'ThaiAnalyzer', 'IndonesianAnalyzer',
475
+ 'HebrewAnalyzer', 'PersianAnalyzer', 'UkrainianAnalyzer', 'GreekAnalyzer',
476
+ 'CzechAnalyzer', 'RomanianAnalyzer', 'SwedishAnalyzer',
477
+ ]