Multilingual readability library for Python — 86 languages, 17 formulas, TeX-based syllable counting via Frank M. Liang algorithm.
Project description
ReadSightPy — Multilingual Readability Engine for Python
ReadSightPy measures text readability across 86 languages using 17 readability formulas with language-specific coefficients. Syllable counting is powered by the Frank M. Liang (TeX) hyphenation algorithm — the same algorithm used by TeX for decades. All with zero heavy dependencies.
This is a Python port of ReadSight (PHP).
Table of Contents
- Installation
- Quick Start
- Demo
- Supported Languages
- Readability Formulas
- FormulaResult
- Performance
- Custom Configuration
- Architecture
- Data Sources
- Development
- License
Installation
pip install readsight
Requirements:
- Python >= 3.10
regex(for Unicode regex\p{L}support)platformdirs(for cache directory)
No other runtime dependencies.
Quick Start
from readsight import ReadSight
rs = ReadSight("en-us")
# Syllable counting
rs.syllable_count("banana") # 3
rs.split_syllables("hyphenation") # ['hyp', 'hen', 'ati', 'on'] (4 syllables, heuristic split)
rs.split_word("hyphenation") # ['hy', 'phen', 'a', 'tion'] (TeX hyphenation points)
# Text analysis
stats = rs.analyze("The quick brown fox jumps over the lazy dog.")
print(f"Words: {stats.word_count}, Syllables: {stats.syllable_count}")
# Readability formulas
fre = rs.flesch_reading_ease(text)
print(f"Flesch Reading Ease: {fre.score} - {fre.interpretation}")
fog = rs.gunning_fog(text)
print(f"Gunning Fog: {fre.score} (grade {fre.grade_level})")
lix = rs.lix(text)
print(f"LIX: {fre.score} - {fre.interpretation}")
Syllable Counting Modes
ReadSightPy has three syllable counting modes, configured per language via syllableMode in data/languages/*.json:
| Mode | How it works | count accuracy |
split accuracy |
|---|---|---|---|
heuristic |
Vowel patterns + word list + prefix/suffix rules | ✓ | ≈ approximate |
tex |
Frank M. Liang hyphenation algorithm (TeX .tex patterns) |
✓ | ✓ exact |
composite |
Heuristic first, TeX as fallback | ✓ | ≈ approximate (uses heuristic split) |
80 languages use tex, 4 use composite (en-us, en-gb, it, pl), 2 use heuristic.
Example: "hyphenation" in each mode
rs = ReadSight("en-us") # composite mode — heuristic wins
rs.syllable_count("hyphenation") # 4 ✓ (in problemWords list)
rs.split_syllables("hyphenation") # ['hyp', 'hen', 'ati', 'on'] — heuristic: equal-width split, ≈ approximate
rs.split_word("hyphenation") # ['hy', 'phen', 'a', 'tion'] — TeX hyphenator: exact points
rs = ReadSight("de-1996") # tex mode
rs.syllable_count("hyphenation") # 4 ✓ (TeX patterns)
rs.split_syllables("hyphenation") # ['hy', 'phen', 'a', 'tion'] — TeX: exact
rs.split_word("hyphenation") # ['hy', 'phen', 'a', 'tion'] — same, both use TeX
Tip:
split_word()always uses the TeX hyphenator (exact).split_syllables()may use heuristic (approximate). For syllable counts both are correct.
Note:
add_hyphenations()adds overrides to the TeX hyphenator. These affectsplit_word()but NOTsplit_syllables()incomposite/heuristicmodes (the heuristic counter doesn't see them).
Demo
Run the interactive demo to see ReadSightPy in action:
python examples/demo.py
This analyzes built-in sample text and outputs:
- Syllable breakdown with hyphenation points for common words
- Text statistics — letters, words, sentences, syllables, histogram
- All applicable readability formulas with scores and interpretations
Compare the same text across 6 languages:
# Built into demo.py — runs multilingual comparison automatically
python examples/demo.py
Supported Languages
86 languages across 19 writing systems: Latin, Cyrillic, Arabic, Hebrew, Devanagari, Bengali, Tamil, Thai, Greek, Armenian, Georgian, Gujarati, Gurmukhi, Kannada, Malayalam, Odia, Telugu, Ethiopic, Coptic.
rs = ReadSight("ru") # Russian
rs = ReadSight("de-1996") # German (1996 reform)
rs = ReadSight("es") # Spanish
rs = ReadSight("th") # Thai
# List all supported languages
langs = ReadSight.get_supported_languages()
# ['af', 'ar', 'as', 'be', 'bg', 'bn', 'ca', 'cop', 'cs', 'cu', 'cy', 'da',
# 'de-1901', 'de-1996', 'de-ch-1901', 'el-monoton', 'el-polyton', 'en-gb',
# 'en-us', 'eo', 'es', 'et', 'eu', 'fa', 'fi', 'fi-x-school', 'fr', 'fur',
# 'ga', 'gl', 'grc', 'gu', 'he', 'hi', 'hr', 'hsb', 'hu', 'hy', 'ia', 'id',
# 'is', 'it', 'ka', 'kk', 'kmr', 'kn', 'la', 'la-x-classic', 'la-x-liturgic',
# 'lt', 'lv', 'mk', 'ml', 'mn-cyrl', 'mn-cyrl-x-lmc', 'mr', 'mul-ethi', 'nb',
# 'nl', 'nn', 'oc', 'or', 'pa', 'pi', 'pl', 'pms', 'pt', 'rm', 'ro', 'ru',
# 'sa', 'sh-cyrl', 'sh-latn', 'sk', 'sl', 'sq', 'sr-cyrl', 'sv', 'ta', 'te',
# 'th', 'tk', 'tr', 'uk', 'vi', 'zh-latn-pinyin']
Readability Formulas
Universal (all 86 languages)
| Formula | Method | Type | Score Range |
|---|---|---|---|
| Gunning Fog | gunning_fog() |
Syllable-based | 0–20+ |
| SMOG Index | smog_index() |
Syllable-based | 3–18+ |
| Coleman-Liau | coleman_liau() |
Letter-based | 0–18+ |
| ARI | automated_readability_index() |
Letter-based | 0–18+ |
| LIX | lix() |
Letter-based | 20–60+ |
Language-Specific
| Language | Formulas |
|---|---|
English (en-us, en-gb) |
Flesch Reading Ease, FK Grade Level, Dale-Chall*, Spache* |
German (de-*) |
Flesch Reading Ease (Amstad), FKGL, Wiener Sachtextformel (4 variants) |
Russian (ru) |
Flesch Reading Ease (Oborneva), FKGL |
Spanish (es) |
Flesch Reading Ease, Fernandez-Huerta, Szigriszt-Pazos, Gutierrez-Polini, Crawford |
Italian (it) |
Flesch Reading Ease, Gulpease |
French (fr) |
Flesch Reading Ease (Kandel-Moles) |
Dutch (nl) |
Flesch Reading Ease (Douma) |
Portuguese (pt) |
Flesch Reading Ease (Martins) |
Turkish (tr) |
Flesch Reading Ease (Ateşman) |
Polish (pl) |
FOG-PL |
Arabic (ar) |
OSMAN |
*Note: Dale-Chall and Spache formulas use a syllable-based heuristic to estimate difficult words (1-syllable ≈ easy). This is a simplified estimation, not based on the original Dale/Spache word lists.
Generic dispatching:
result = rs.score("gunning_fog", text)
result = rs.score("wiener_sachtextformel", text)
FormulaResult
result.score # float — raw formula score
result.grade_level # float | None — normalized grade level (FKGL, GF, SMOG, CL, ARI)
result.interpretation # str — qualitative interpretation ("Easy", "Hard")
result.formula_name # str — formula key
result.language_code # str — language code used
result.inputs # dict[str, float | int] — intermediate values for debugging
API Reference
Text Analysis Methods
rs.syllable_count(word: str) -> int
rs.split_syllables(word: str) -> list[str]
rs.split_word(word: str) -> list[str]
rs.word_count(text: str) -> int
rs.sentence_count(text: str) -> int
rs.letter_count(text: str) -> int
rs.total_syllables(text: str) -> int
rs.average_syllables_per_word(text: str) -> float
rs.average_words_per_sentence(text: str) -> float
rs.polysyllable_count(text: str, count_proper_nouns: bool = True) -> int
rs.words_with_more_than_n_syllables(text: str, n: int, count_proper_nouns: bool = True) -> int
rs.histogram_syllables(text: str) -> dict[int, int]
rs.analyze(text: str) -> TextStatistics
split_syllables vs split_word:
split_syllablesmay use heuristic ≈approximate split (depends on language'ssyllableMode).split_wordalways uses the TeX hyphenator for exact hyphenation points. Syllable counts are accurate in all modes. See Syllable Counting Modes.
Formula Methods
rs.flesch_reading_ease(text: str) -> FormulaResult
rs.flesch_kincaid_grade_level(text: str) -> FormulaResult
rs.gunning_fog(text: str) -> FormulaResult
rs.smog_index(text: str) -> FormulaResult
rs.coleman_liau(text: str) -> FormulaResult
rs.automated_readability_index(text: str) -> FormulaResult
rs.lix(text: str) -> FormulaResult
rs.wiener_sachtextformel(text: str, variant: int = 1) -> FormulaResult
rs.gulpease(text: str) -> FormulaResult
rs.fernandez_huerta(text: str) -> FormulaResult
rs.szigriszt_pazos(text: str) -> FormulaResult
rs.gutierrez_polini(text: str) -> FormulaResult
rs.crawford(text: str) -> FormulaResult
rs.fog_pl(text: str) -> FormulaResult
rs.dale_chall(text: str) -> FormulaResult
rs.spache(text: str) -> FormulaResult
rs.osman(text: str) -> FormulaResult
Performance
Measured on CPython 3.12, Intel Core i7 (limited data — full benchmarks TBD):
| Operation | Time |
|---|---|
| Syllable counting (single word) | ~0.05 ms |
| Text analysis (45 words) | ~1 ms |
| Formula calculation (incl. analysis) | ~1 ms |
| Engine init (en-us, cached) | ~10 ms |
| Engine init (de-1996, first load) | ~60 ms |
Caching: compiled patterns are stored as JSON in the system cache directory (platformdirs.user_cache_dir). First load parses .tex files (native hyph-utf8 format); subsequent loads use the pre-compiled cache.
Custom Configuration
from readsight import ReadSight, Config
# Set default paths (before creating engines)
ReadSight.set_default_config(Config(
patterns_dir="/custom/patterns",
languages_dir="/custom/languages",
cache_dir="/var/cache/readsight",
))
# Or per-instance
rs = ReadSight(
language="en-us",
patterns_dir="/custom/patterns",
cache_dir="/custom/cache",
)
# Add custom hyphenation rules (affects split_word, not split_syllables)
rs.add_hyphenations({
"customword": "cus-tom-word",
})
rs.split_word("customword") # ['cus', 'tom', 'word']
Architecture
ReadSight (facade)
├── TextAnalyzer (syllable counting, text metrics)
│ ├── SyllableCounter (strategy: tex | heuristic | composite)
│ │ ├── CompositeSyllableCounter (problemWords → heuristic, rest → TeX)
│ │ ├── HeuristicSyllableCounter (vowel patterns + word list)
│ │ └── TexSyllableCounter → LiangHyphenator (TeX hyphenation)
│ ├── LiangHyphenator
│ │ ├── TexSource (parses .tex from hyph-utf8)
│ │ ├── PatternsCollection (pattern data)
│ │ ├── HyphenationExceptionsCollection (word overrides)
│ │ └── JsonPatternCache (compiled patterns)
│ └── TextSplitter (word/sentence/letter counting)
├── Language (JSON config per language, syllableMode + formulaConfigs)
└── FormulaRegistry (17 formulas)
├── FleschReadingEase (with lang-specific coefficients)
├── GunningFog, SMOG, ColemanLiau, ARI, LIX (universal)
└── WSTF, Gulpease, Fernandez-Huerta, etc. (lang-specific)
Data Sources
- TeX hyphenation patterns: hyph-utf8 version 2026-02-21 —
the canonical TeX hyphenation repository maintained by the TeX Users Group (TUG).
86
.texpattern files from hyph-utf8 covering 86 language variants. Packaged under each pattern file's original license. - FRE coefficients: Amstad (DE), Oborneva (RU), Fernandez-Huerta (ES), Vacca-Franchina (IT), Kandel-Moles (FR), Douma (NL), Martins (PT), Ateşman (TR)
- WSTF: Bamberger & Vanecek (DE)
- Gulpease: GULP, La Sapienza University (IT)
Development
pip install -e ".[dev]" # Install with dev dependencies
pytest # Run all tests (133 tests)
pytest --cov=readsight # With coverage report
mypy src/ # Static type checking (strict mode)
ruff check src/ tests/ # Lint
ruff format src/ tests/ # Format
Quality Metrics
| Metric | Value |
|---|---|
| Tests | 133 |
| Mypy | Strict mode, 0 errors |
| Ruff | 0 errors |
| Source files | 56 |
| Test files | 18 |
| Supported languages | 86 |
| Writing systems | 19 |
| Readability formulas | 17 |
| Runtime dependencies | 2 (regex, platformdirs) |
License
MIT. Author: Yevhen Leonidov.
TeX pattern files from hyph-utf8 are packaged under their original licenses (see individual file headers).
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file readsight-1.0.3.tar.gz.
File metadata
- Download URL: readsight-1.0.3.tar.gz
- Upload date:
- Size: 1.4 MB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
a0a2b67c7c61696dc30bca2fb6cf35a6e4f070f2f8696246fa8567fae2fd06c7
|
|
| MD5 |
8420dadeea239b142701b72b03ac5d4f
|
|
| BLAKE2b-256 |
dbd225b7bd100ad460e451d338bbc72c3118240b20e46cff41aed3dffd11bcb4
|
File details
Details for the file readsight-1.0.3-py3-none-any.whl.
File metadata
- Download URL: readsight-1.0.3-py3-none-any.whl
- Upload date:
- Size: 1.4 MB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.6
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
169971f65794ff8eadaea13ce962a24b21bd5430c850c6851dcb5a8b8d0254e9
|
|
| MD5 |
ce7c374de1d20468ec41917e5e3fb4ea
|
|
| BLAKE2b-256 |
d362ec076587475d229860b65bf17ba8057a7e1d09fa81565facc37267cf0e5a
|