Multilingual library for accurate and deterministic hyphenation and syllable counting without relying on dictionaries.
Install from PyPI:
pip install syllabreak- 🇬🇧 English (
eng) - 🇷🇺 Russian (
rus) - 🇷🇸 Serbian Cyrillic (
srp-cyrl) - 🇷🇸 Serbian Latin (
srp-latn) - 🇧🇦 Bosnian (
bos) - 🇭🇷 Croatian (
hrv) - 🇲🇪 Montenegrin Latin (
cnr-latn) - 🇲🇪 Montenegrin Cyrillic (
cnr-cyrl) - 🇹🇷 Turkish (
tur) - 🇰🇿 Kazakh (
kaz) - 🇰🇬 Kyrgyz (
kir) - 🇬🇷 Modern Greek (
ell) - 🏛️ Ancient Greek (
grc) - 🇬🇪 Georgian (
kat) - 🇭🇺 Hungarian (
hun) - 🇩🇪 German (
deu) - 🇫🇷 French (
fra) - 🇷🇴 Romanian (
ron) - 🇪🇸 Spanish (
spa) - 🇵🇹 Portuguese (
por) - 🇵🇱 Polish (
pol) - 🇱🇻 Latvian (
lav) - 🇦🇲 Armenian (
hye) - 🇫🇮 Finnish (
fin) - 🇳🇱 Dutch (
nld) - 🇸🇪 Swedish (
swe) - 🇰🇪 Swahili (
swh) - 🇻🇳 Vietnamese (
vie) - 🏛️ Latin (
lat)
A few language-specific quirks the algorithm has to encode. Each one would otherwise produce visibly wrong splits.
- BCMS (bos, hrv, cnr) — long-jat reflex
ijeis one syllable:mli-je-kois wrong,mlije-kois correct. Two graphic-but-not-jat exceptions aredvijeandprije(Matešić 2015, rule P11).srp-latndoes not encodeijebecause Serbian dictionaries cover both ekavian and ijekavian; passlang="hrv"(orbos/cnr-latn) for ijekavian text. - Montenegrin adds
ś/ź(Latin) andс́/з́(Cyrillic, decomposedс+ U+0301 only — no precomposed Unicode points exist). - French —
eauis a trigraph vowel:châ-teau. - Romanian — final
-iafter a consonant is palatalization, not a separate syllable:stu-denți, notstu-den-ți. Adjacent vowels split into hiatus:pri-e-teni. - German —
stbetween vowels splits after a short nucleus but stays together after a long one (stra-ßevskin-der-class cases). - Latin — hiatus is mandatory:
po-e-ta,phi-lo-so-phi-a. - Polish — digraphs
sz,cz,rz,dz,chstay together inside a syllable. - Hungarian — only one consonant moves to the next syllable, so even valid onset clusters split (
ab-lak, nota-blak). Geminate digraphs are written compactly (ssz,ggy,nny,lly,tty,ccs,zzs,ddz,ddzs) and restored in full at the break per AkH 12 §226:asszony→asz-szony,mennyi→meny-nyi,poggyász→pogy-gyász. - Turkic Cyrillic (kaz, kir) — strict V-CV/VC-CV: only one consonant moves to the next syllable, three-consonant clusters split 2|1. Kyrgyz long vowels (
аа,ээ,оо,ии,уу,өө,үү) form a single nucleus:буу-дай, notбу-удай. Loanwords with foreign onset clusters fall through to the native algorithm (Қазақстан→Қа-зақс-тан,галстук→галс-тук) — fixing them would require lexical knowledge. Auto-detect caveat: a sample using only Cyrillic letters shared with Russian will detect asrus(it sits first in the rules); a sample with ң/ө/ү will detect askaz(Kyrgyz's extras are a subset of Kazakh's). Passlang="kir"explicitly for Kyrgyz. - Modern Greek — V-CV; consonant clusters keep together with the following nucleus only if they form a valid Greek word-initial onset, up to length 3 (
βι-βλί-οwith βλ,ά-στροwith στρ,άν-θρω-ποςwith θρ). Identical doubled consonants always split (ελ-λη-νι-κά,θά-λασ-σα). Vowel digraphs αι/ει/οι/υι/αυ/ευ/ηυ/ου (with all accent positions, e.g.άιinτσάι) are a single nucleus; consonant digraphs μπ/ντ/γκ/γγ/τζ/τσ are a single consonant (school orthographic convention). Adjacent vowels not forming a digraph split as hiatus (λα-ός,α-έ-ρας,βι-βλί-ο). Synizesis (modern phonetic merging of η/ι/υ with a following vowel after a consonant, e.g.ά-γνοια2 syllables) is not applied — the algorithm follows the orthographic 3-syllable splitά-γνοι-α. - Ancient Greek — same V-CV / onset principles as Modern Greek. Polytonic accents (smooth/rough breathing, acute/grave/circumflex, iota subscript) ride along their base letter automatically: the engine NFD-normalises input on entry, attaches every Unicode combining mark (Mn category) to the preceding token, and renormalises back to NFC on the way out. Differs from
ellin two ways: μπ/ντ/γκ/γγ/τζ/τσ are NOT consonant digraphs (they were two phonemes in Classical Greek), and the vowel/consonant list enumerates the full Greek Extended block so that polytonic-marked words detect asgrcrather thanell. Plain Greek without polytonic markings is ambiguous; the detector returnsellfirst as the more common modern surface. - Latvian — V-CV/VC-CV with muta-cum-liquida kept (
la-brīt). Diphthongsai,au,ei,ie,iu,oi,ui,eu,ouform a single nucleus (lie-li,pal-dies,draugs). Note: macron vowels (ā,ē,ī,ū) are shared with Latin, so a Latvian word without a cedilla letter (ļ,ķ,ģ,ņ) cannot be reliably auto-detected — the detector keepslatfirst. - Armenian — strict V-CV/VC-CV (Oxford notes on Armenian syllabification: "Word-internally, -CC- is perceived as a natural syllable boundary"). Three-consonant clusters split CC|C by the same default long-cluster rule used for Turkic/Hungarian-style strict VC-CV languages. No
clusters_keep_next— native phonotactics disallow CCV- onsets. ու is a single vowel digraph (ու-սա-նող); the ligatureև(yev, U+0587) counts as one vowel-bearing letter (ա-րև). The pronounced schwa that surfaces between written consonants (դպրոց→ [də.pə.rɔts]) is not orthographic and the algorithm does not insert it — words with one written vowel stay as one syllable. Glideյis a consonant and never an onset privilege:սենյակ→սեն-յակ,գիտություն→գի-տութ-յուն. - Finnish — strict V-CV/VC-CV per VISK §11–14 and Karlsson (1999). Long vowels (
aa ee ii oo uu yy ää öö) and diphthongs are a single nucleus: i-diphthongs (ai ei oi ui yi äi öi) and u-diphthongs (au eu iu ou ey äy iy öy) in any position, opening diphthongs (ie uo yö) formally only in a root-initial syllable but treated as one nucleus everywhere (non-initial occurrences in derived forms would need lexical knowledge to disambiguate). Three or more consecutive vowels always contain a syllable boundary (yliopisto→y-li-o-pis-to). Auto-detect caveat: Finnish has no unique characters (alphabet is a subset of German's), so words without context will route to the first-matching rule (typicallyeng, ordeufor words withä/ö) — passlang="fin"explicitly. - Dutch — strict V-CV for one consonant, VC-CV for two consonants (
kas-teel,mees-ter,pis-tool). Muta-cum-liquida (br bl dr fr fl gr gl kr kl pr pl tr vr wr) stays together as the onset of the next syllable (pa-troon,a-tri-um). In 3+ consonant clusters, s-onsets and the diminutivetjalso keep with the next syllable (ven-ster,ham-ster,in-dus-trie,pad-den-stoel) — encoded via a newtrailing_onsetsfield that only applies in this position sopis-tool(plain 2-cons "st") still splits.chis a single phoneme; vowel digraphs (aa ee oo uu ie oe eu ei ij au ou ui) and triphthongs (aai ooi oei eeu ieu) are single nuclei. Hiatus marked by diaeresis (idee-ën,pa-ti-ënt). Known limitation: morpheme-boundary cases likeab-rupt(Latin prefix) come out asa-bruptbecause the orthographic algorithm has no morphology. Auto-detect caveat: no unique characters relative to German — passlang="nld"explicitly. - Swedish —
enkonsonantsprincipenper Svenska Akademiens Ordlista: one consonant moves to the next syllable, V-CV / VC-CV (sko-la,kvin-na,vac-ker,män-nis-ka,Hel-sing-fors). No native vowel digraphs (vowel length is signalled by the consonant count that follows, not by doubling the vowel). The morphology-basedordledsprincipenfor compounds (glas-ögon) needs lexical knowledge — the algorithm produces the orthographicgla-sö-goninstead.ckis the geminate ofk; we producevac-ker(SAOL also acceptsvack-er). The/ŋ/-ngand/ɧ/-sk/sj/skj/stj/schexceptions are phonetically conditioned and not encoded — we keep strict VC-CV throughout (fis-kafor the hard-vowel case is correct, butvingarcomes outvin-garrather than the phonologicalving-ar, andmaskincomes outmas-kinrather thanma-skin). Auto-detect: words containingåroute cleanly to Swedish; words with onlyä/öcollide with German/Dutch/Finnish — passlang="swe"explicitly. - Swahili (Kiswahili) — Bantu CV syllables, mostly open. Two systematic deviations are encoded: (a) prenasalized consonant sequences
mb nd nj ng mv nzare a single consonant onset (mbu-zi,ja-mbo,nde-ge,nyu-mba); (b) word-initialmornbefore a non-prenasalised consonant forms its own syllable (m-to-to,n-chi,m-pi-shi,m-ze-e) — handled bysyllabic_consonants: "mn"plus a small engine relaxation that treats a word boundary as the buffer being satisfied for a syllabic consonant.mw/myare kept as a single onset token so the syllabic-m check does not fire on glide clusters (mwa-na, notm-wa-na); otherCw/Cyonsets stay together viaclusters_keep_next(Ki-swa-hi-li,bwa-na,kwa-he-ri). Single-phoneme digraphsch sh th dh gh kh nyare recognised. Out of scope:ng'with apostrophe for word-initial /ŋ/ would need treating the apostrophe as an internal modifier and recognising a 3-char digraph — rare; document and skip. Auto-detect: Swahili is plain ASCII with no unique characters; passlang="swh"explicitly. - Vietnamese — Latin alphabet with a rich diacritic system (6 plain vowels + 6 with breve/circumflex/horn + 5 combining tones). Vietnamese is overwhelmingly monosyllabic and written with spaces between syllables (
Việt Nam,Hà Nội), so the engine usually sees a single syllable per word. Consonant digraphs/trigraphch gh gi kh ng ngh nh ph qu th trare recognised. Vowel di- and triphthongs are listed in their base-stripped form (uoi,ieu,yeu, …) so that the tokenizer's Mn-skip path matches against the run of base letters between any combining marks —người,nhiều,tuổi,yêuall syllabify as one nucleus. The tokenizer change that enables this: for each candidate length (3, 2, 1) the Mn-skip match is tried before the direct substring match, so a longer base composition wins over a shorter direct one (yeutriphthong overyediphthong insideyêu). The letterđis listed explicitly. Auto-detect:đand the horn-bearingư/ơare distinctive — passlang="vie"for purely ASCII loanwords (tivi) and for words that only carry shared acute/grave/circumflex marks (má,cà,cô,bê) since those overlap with Romance and Romanian and are not forced to route as Vietnamese. - BCMS — syllabic
rbetween consonants is a syllable nucleus:prstandkrvare one syllable,smrt-nosplits around it. - Georgian — no digraphs, sequences of consonants split unless they appear on a small whitelist of valid onsets.
For BCMS specifically, character-based auto-detect cannot tell bos/hrv/srp-latn/cnr-latn apart for text without script-unique letters — the detector returns srp-latn first to preserve prior behaviour. Pass lang= explicitly to get ijekavian handling.
The engine accepts text in either NFC or NFD form and round-trips back to canonical NFC:
syllabify(text, lang)normalises input to NFD internally, so combining marks (Unicode categoryMn— accents, breathings, ogonek, iota subscript, cedilla, etc.) sit as their own codepoints. The tokenizer attaches each Mn codepoint to the preceding token automatically; while matching digraphs it can also skip over marks placed between two base letters (Greekἀι= α + U+0313 + ι still matches theαιentry). A diaeresis (U+0308) on the closing base of a candidate digraph vetoes the match — that's the standard convention forαϊ,Μαΐου,naïfand similar hiatus markers. The returned string is renormalised to NFC before being handed back.detect_language(text)normalises input to NFC and scores it against each rule's character set. Precomposed letters discriminate well (Polishą, Germanä, polytonic Greekἤ) via each rule'sunique_chars.- In rule files, character sets (
vowels,consonants, …) hold the base letters as they appear inrules.yaml. Multi-character entries (digraph_vowels,dont_split_digraphs,clusters_keep_next, …) are stored as the union of their NFC form and their NFD decomposition, so entries with precomposed letters (deuüh) still match against the NFD-tokenised input. Combining marks themselves never need listing — the Mn auto-attach takes care of them.
Some writing systems do not fit syllabreak's alphabetic-rules paradigm and will not be added. They need fundamentally different algorithms:
- Chinese (
cmn) — logographic; one character is already one syllable by construction. Nothing to split. - Japanese (
jpn) — kana is mora-syllabic by design; kanji cannot be syllabified without a dictionary. Belongs in a separate library. - Korean (
kor) — Hangul syllable blocks are syllables visually. Splitting is Unicode block normalization, not a vowel/consonant rule engine. - Arabic (
ara) — abjad: short vowels are optional diacritics. Syllabification is undecidable without vocalization. - Bengali (
ben), Hindi (hin), Sanskrit (san) — Brahmic abugidas. The unit is the akṣara (consonant + inherent/explicit vowel + conjuncts), which requires Unicode grapheme-cluster logic rather than a flat character table.
When no language is specified, the library automatically detects the most likely language:
>>> from syllabreak import Syllabreak
>>> s = Syllabreak("-")
>>> s.syllabify("hello")
'hel-lo'
>>> s.syllabify("здраво") # Serbian Cyrillic
'здра-во'
>>> s.syllabify("привет") # Russian
'при-вет'You can specify the language code for more predictable results:
>>> s = Syllabreak("-")
>>> s.syllabify("problem", lang="eng") # Force English rules
'pro-blem'
>>> s.syllabify("problem", lang="srp-latn") # Force Serbian Latin rules
'prob-lem'
>>> s.syllabify("mlijeko", lang="hrv") # Croatian ije is one syllable
'mlije-ko'This is useful when:
- The text could match multiple languages
- You want consistent rules for a specific language
- Processing text in a known language
>>> from syllabreak import Syllabreak
>>> s = Syllabreak()
>>> "hrv" in s.supported_languages()
TrueThe library returns all matching languages sorted by confidence:
>>> from syllabreak import Syllabreak
>>> s = Syllabreak()
>>> s.detect_language("hello")
['eng', 'srp-latn', 'tur'] # Matches English, Serbian Latin and Turkish
>>> s.detect_language("čovek")
['srp-latn', 'eng', 'tur'] # Serbian Latin has highest confidence due to čThe API reference is generated from the public Python API and deployed by GitHub Actions.
make install
make install-tools # requires Python 3.11 or newer
make test
make lint
make docs
make buildReleases are published by the Release workflow.