Skip to content

Support Ukrainian language #105

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions pysbd/abbreviation_replacer.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ def replace(self):
abbr_handled_text = ""
for line in self.text.splitlines(True):
abbr_handled_text += self.search_for_abbreviations_in_string(line)

self.text = abbr_handled_text
self.replace_multi_period_abbreviations()
self.text = Text(self.text).apply(*self.lang.AmPmRules.All)
Expand Down
2 changes: 1 addition & 1 deletion pysbd/about.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
# https://python-packaging-user-guide.readthedocs.org/en/latest/single_source_version/

__title__ = "pysbd"
__version__ = "0.3.4"
__version__ = "0.3.5"
__summary__ = "pysbd (Python Sentence Boundary Disambiguation) is a rule-based sentence boundary detection that works out-of-the-box across many languages."
__uri__ = "http://nipunsadvilkar.github.io/"
__author__ = "Nipun Sadvilkar"
Expand Down
39 changes: 39 additions & 0 deletions pysbd/lang/ukrainian.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
import re

from pysbd.abbreviation_replacer import AbbreviationReplacer
from pysbd.lang.common import Common, Standard

class Ukrainian(Common, Standard):

iso_code = 'uk'

# Handling Cyrillic characters in re module
# https://stackoverflow.com/a/10982308/5462100
MULTI_PERIOD_ABBREVIATION_REGEX = r'\b[\u0400-\u0500]+(?:\.\s?[\u0400-\u0500])+[.∯]|b[a-z](?:\.[a-z])+[.∯]'

class Abbreviation(Standard.Abbreviation):
ABBREVIATIONS = ["а. с", "авт. арк", "автореф", "агр", "аграр", "адапт", "адм", "адмін", "акад", "акомп", "алф", "альб", "альм", "амер", "аналіт", "англ", "анот", "антол", "арх", "археол", "архіт", "асоц", "атл", "б. в", "б. д", "б. м", "б. р", "б. т", "б. тит. арк", "б. ц", "безкошт", "безпл", "берез", "бібл", "біблійн", "бібліогр", "бібліографозн", "бібліол", "біобібліогр", "біогр", "болг", "брош", "буд", "будів", "бух", "бюдж", "бюл", "в т. ч", "введ", "ввод", "вдоскон", "верес", "вет", "вивч", "вид", "вид. від", "викон", "використ", "виникн", "вип", "вип. дан", "випр", "вироб", "вист", "вих. дан", "вищ", "від", "відб", "відеофоногр", "відкр", "відом", "відп", "відред", "відродж", "вінниц", "віршов", "вісн", "вітчизн", "вкл. арк", "включ", "внутр", "волин", "впровадж",
"вугіл", "вчен", "газ", "ген", "генет", "геодез", "геологорозв", "гір", "гірн", "гірничорят", "глобал", "голланд", "голов", "гологр", "госп", "господарч", "грав", "громад", "ГРУД", "гуманіт", "Д", "декор", "демокр", "депоз", "депон", "держ", "держадмін", "деф", "див", "дип", "дис", "дит", "діагр", "діапоз", "діяльн", "дніпропетр", "довід", "дод", "додрук", "док", "докум", "донец", "доопрац", "доп", "допов", "допом", "дор", "доруч", "дослід", "дослідж", "драм", "друк", "духовн", "екол", "екон", "експерим", "електрон", "енерг", "енцикл", "євр", "європ", "житомир", "жовт", "журн", "забезп", "заг", "загот", "закарпат", "закінч", "закл", "зам", "зап", "запоріз", "зареєстр", "засід", "засл", "засн", "застосув", "затв", "заув", "зах", "зб", "здоб", "земел", "землероб",
"зібр", "змін", "зобр", "зрошув", "зрошувал", "ілюстр", "ім", "ін", "інв", "інд", "індивід", "інж", "інозем", "інспек", "інстр", "інструкт", "інструм", "інтегр", "інтенс", "інтерн", "інфл", "інформ", "ісп", "іст", "історіогр", "італ", "К", "каб", "канд", "карпат", "карт", "картогр", "картограф", "кас", "кат", "кат", "каф", "квіт", "кер", "керівн", "кіберн", "кінематогр", "кіровогр", "кл", "класиф", "класич", "клін.-експерим", "клініч", "кн", "книгозн", "коеф", "кол. авт", "колоніал", "ком", "комент", "комерц", "коміс", "коміч", "комп'ют", "комп'ют. наб", "компенс", "композ", "конгр", "конф", "концеп", "кооп", "коп", "кор", "корпор", "косм", "кресл", "крим", "кримін", "кримськотатар", "критич", "ксерокоп", "культ", "Л", "лаб", "латин", "лекс", "лип", "листоп",
"лібр", "лікар", "лікув", "лінгв", "лірич", "літ", "літогр", "луган", "луц", "львів", "лют", "М", "магістрал", "макс", "мал", "мат", "матер", "машинобуд", "мед", "меліор", "мемор", "менедж", "металоріз", "метод", "мех", "механіз", "миколаїв", "мистец", "мистецтвознав", "міжвід", "міжнар", "мін", "мінер", "міс", "міськ", "міськдержадмін", "місяч", "мкоп", "мовознав", "мол", "моногр", "морал", "муз", "мфотокоп", "н.-д", "наб. діапоз", "навч", "надзаг", "надзв", "напр", "нар", "народж", "народознав", "наст", "наук", "нац", "нім", "нормат", "О", "обкл", "обл", "обл.-вид. арк", "обладн", "облдержадмін", "образотв", "оброб", "обслуг", "обчисл", "оголош", "одес", "окр", "окр. арк", "оповід", "опр", "опрац", "опрацюв", "оптич", "опубл", "опублік", "орг", "ориг", "орк",
"оркестр", "офіц", "оформ", "паг", "парлам", "партит", "пед", "пер", "перевид", "перегл", "передм", "перероб", "переробл", "персон", "персп", "письм", "півд", "півн", "підвищ", "підгот", "підготов", "підзаг", "підруч", "планув", "пластич", "плем", "повідомл", "поетич", "покажч", "показн", "полем", "полігр", "політол", "полтав", "пом", "попул", "портр", "посіб", "почат", "поясн", "пр", "правл", "правознав", "практ", "предм", "препр", "приватиз", "пригод", "прикл", "приклад", "прим", "природн", "присвяч", "пробл", "прогр", "програмув", "прод", "продовж", "проектув", "пром", "пропоз", "проф", "проф.-викл", "псевд", "публ", "публіц", "публіч", "радіоелектрон", "райдержадмін", "район", "рац", "раціон", "ред", "редкол", "рез", "рек", "рекл", "реком", "реліг", "репр",
"репрод", "реставр", "ретросп", "реф", "рец", "рис", "рівнен", "розв", "розд", "розд. паг", "розповсюдж", "розроб", "розробл", "розшир", "рос", "ротапр", "рубр", "рубрик", "рукоп", "рум", "с.-г", "С.Пб", "сx", "сан", "сатир", "св", "селян", "семінар", "сенсац", "сер", "серп", "сес", "симп", "симф", "сист", "Сімф", "січ", "сканд", "склад", "скор", "сл", "слов", "словац", "словен", "служб", "соц", "соціол", "спец", "специф", "співавт", "співроб", "спорудж", "ст", "старш", "стат", "стер", "стереофон", "студ", "студент", "сум", "суспіл", "сцен", "Т", "т. д", "т. ін", "табл", "татар", "творч", "тез", "телегр", "теорет", "терит", "терміч", "терноп", "техн", "технол", "тиж", "тижд", "тис", "тит. арк", "торг", "трав", "тракт", "транскр", "трансліт", "трансп", "турец",
"у т. ч", "увед", "увод", "угор", "удоскон", "уклад", "укр", "українознав", "ум. друк. арк", "унів", "упоряд", "упорядкув", "упр", "урив", "устатк", "учасн", "факс", "факульт", "фантаст", "фармац", "федер", "фест", "фіз", "філ", "філол", "філос", "фільмогр", "фін", "фкоп", "фоногр", "формув", "фотогр", "фотокоп", "фототип", "фр", "фрагм", "франків", "футл", "харк", "херсон", "хім", "хмельниц", "хор", "хорв", "хореогр", "християн", "церк", "цит", "черв", "черкас", "чернів", "черніг", "чес", "шк", "щодекад", "щоден", "щокв", "щомісяч", "щоріч", "щотиж", "ювіл", "юнац", "яз", "ян", "яп", "X",
"р", "акад", "арт", "асист", "асп", "в. о", "вид", "викл", "викон", "вул", "д. чл", "деп", "дир", "дириг", "доц", "зав", "заст", "іл", "інсп", "комп", "м", "наб", "оз", "п", "пер", "пл", "пров", "просп", "проф", "ред", "реж", "с", "секр", "ст", "уклад", "упоряд", "худож", "чл.-кор",
"№ пор", "р", "рр", "арк", "б", "в", "гл", "к", "к", "кв", "од", "пр", "с", "ст", "т", "ц", "ч", "часоп", "чис", "н.е"]
PREPOSITIVE_ABBREVIATIONS = []
NUMBER_ABBREVIATIONS = []

class AbbreviationReplacer(AbbreviationReplacer):

SENTENCE_STARTERS = []

def __init__(self, text, lang):
super().__init__(text, lang)

def replace_period_of_abbr(self, txt, abbr):
txt = re.sub(r'(?<=\s{abbr})\.'.format(abbr=abbr.strip()), '∯', txt)
txt = re.sub(r'(?<=\A{abbr})\.'.format(abbr=abbr.strip()), '∯', txt)
txt = re.sub(r'(?<=^{abbr})\.'.format(abbr=abbr.strip()), '∯', txt)
return txt
4 changes: 3 additions & 1 deletion pysbd/languages.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from pysbd.lang.deutsch import Deutsch
from pysbd.lang.kazakh import Kazakh
from pysbd.lang.slovak import Slovak
from pysbd.lang.ukrainian import Ukrainian

LANGUAGE_CODES = {
'en': English,
Expand All @@ -46,7 +47,8 @@
'ja': Japanese,
'de': Deutsch,
'kk': Kazakh,
'sk': Slovak
'sk': Slovak,
'uk': Ukrainian,
}


Expand Down
5 changes: 5 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -146,3 +146,8 @@ def kk_default_fixture():
def sk_default_fixture():
sk_segmenter = pysbd.Segmenter(language="sk", clean=False, char_span=False)
return sk_segmenter

@pytest.fixture()
def uk_default_fixture():
uk_segmenter = pysbd.Segmenter(language="uk", clean=False, char_span=False)
return uk_segmenter
32 changes: 32 additions & 0 deletions tests/lang/test_ukrainian.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
import re
import pytest

GOLDEN_UK_RULES_TEST_CASES = [
("Я народився у м. Харків. Нині я мешкаю на вул. Сводоби 12, кв. 0. ",
["Я народився у м. Харків.", "Нині я мешкаю на вул. Сводоби 12, кв. 0."]),

('"Га?" — відповів хлопець, цілковито здивований. "Що сьогодні, мій веселий компаньйоне?" — гукнув Скрудж.',
['"Га?" — відповів хлопець, цілковито здивований.', '"Що сьогодні, мій веселий компаньйоне?" — гукнув Скрудж.']),

("Попередньо забронювала велику альтанку, ту, що якраз під лісом на 20.09. Почнемо з'їжджатися десь на 11-ту.",
["Попередньо забронювала велику альтанку, ту, що якраз під лісом на 20.09.", "Почнемо з'їжджатися десь на 11-ту."]),

("Станом на 2500 р. до н.е.",
["Станом на 2500 р. до н.е."]),
]


@pytest.mark.parametrize('text,expected_sents', GOLDEN_UK_RULES_TEST_CASES)
def test_uk_sbd(uk_default_fixture, text, expected_sents):
"""Ukrainian language SBD tests"""
segments = uk_default_fixture.segment(text)
segments = [s.strip() for s in segments]
assert segments == expected_sents


def test_uk_multiperiod_abbreviation_regex(uk_default_fixture):
text = "Станом на 2500 р. до н.е."
match = re.search(uk_default_fixture.language_module.MULTI_PERIOD_ABBREVIATION_REGEX,
text)
assert match.group() == "н.е."