Skip to content

Repository files navigation

Readability.German

NuGet CI

German-language readability indices — Wiener Sachtextformel (WSTF 1–4), Flesch-Reading-Ease (Amstad) and LIX (Björnsson) — computed from a single shared tokenization pass, with zero runtime dependencies.

Install

dotnet add package Readability.German

Quickstart

using Readability.German;

ReadabilityReport report = GermanReadability.Analyze(
    "Die Sonne schien den ganzen Tag über dem kleinen Dorf.");

Console.WriteLine(report.Wstf.Average);       // Wiener Sachtextformel — school-grade level, 4–15+
Console.WriteLine(report.FleschReadingEase);  // Flesch-Reading-Ease (Amstad) — 0–100, higher = easier
Console.WriteLine(report.Lix);                // LIX (Björnsson) — unbounded, higher = harder

Console.WriteLine(WstfInterpretation.DescribeGerman(report.Wstf.Average));
Console.WriteLine(FleschInterpretation.DescribeGerman(report.FleschReadingEase));
Console.WriteLine(LixInterpretation.DescribeGerman(report.Lix));

report.IsEmpty is true when the input contained no words; every metric and score is 0 rather than NaN/Infinity in that case. text == null throws ArgumentNullException.

The three indices

Direction is the most important column below — Flesch runs opposite to the other two.

Index Formula Scale Direction Bands
WSTF (report.Wstf.Average) 0.1935·MS + 0.1672·SL + 0.1297·IW − 0.0327·ES − 0.875 (and 3 more variants, see below) ~4–15 (Austrian/German school grade) higher = harder 4–6 very easy · 6–8 easy · 8–10 good · 10–12 moderately difficult · 12–14 difficult · 14+ very difficult
Flesch-Reading-Ease (Amstad) (report.FleschReadingEase) 180 − ASL − 58.5·ASW nominally 0–100 higher = easier (inverted!) 90–100 sehr leicht · 80–90 leicht · 70–80 mittelleicht · 60–70 mittel · 50–60 mittelschwer · 30–50 schwer · 0–30 sehr schwer
LIX (Björnsson) (report.Lix) ASL + IW (IW = % words with more than 6 letters) unbounded, typically 20–70 higher = harder <30 very easy · 30–40 easy · 40–50 medium · 50–60 difficult · 60+ very difficult

Where SL/ASL = mean sentence length in words, ASW = mean syllables per word, MS = % words with ≥3 syllables, ES = % words with exactly 1 syllable, IW = % long words (definition varies by index — see below).

GermanReadability.Analyze computes all three from one tokenization pass. WstfResult.Average is the mean of the four WSTF variants only — a valid average, since all four share one scale and direction. There is no cross-index average — see Advanced usage below.

Advanced usage

Reuse statistics across calls

If you want to inspect the shared statistics, or compute only some of the indices, tokenize once and pass the result to each calculator directly instead of calling Analyze:

using Readability.German;
using Readability.German.Indices;

TextStatistics stats = TextAnalyzer.GetStatistics(text);

WstfResult wstf = WienerSachtextformel.Compute(stats);
double flesch = FleschReadingEase.Compute(stats);
double lix = Lix.Compute(stats);

This is exactly what GermanReadability.Analyze does internally — useful when you need stats.SentenceCount, stats.SyllableCount, etc. alongside the scores, or when you only need one index and want to skip computing the others.

Swap the syllable counter

The built-in GermanSyllableCounter is a vowel-group heuristic (see Accuracy & limitations). Supply your own ISyllableCounter — e.g. a dictionary/hyphenation-based one — via TokenizerOptions/ReadabilityOptions:

var options = new ReadabilityOptions { SyllableCounter = new MyDictionaryCounter() };
ReadabilityReport report = GermanReadability.Analyze(text, options);

ISyllableCounter implementations must be thread-safe and pure — Readability.German never mutates shared state across calls.

You can also swap the abbreviation list used for sentence splitting (Abbreviations), and toggle clamping to each index's nominal display range (ClampScores, off by default — see Clamping).

The WSTF IW ambiguity

Sources disagree on the Wiener Sachtextformel's IW (long-word) threshold: Bamberger/Vanecek and Wikipedia define it as more than six letters (≥7), but several online calculators use six or more (≥6). LIX's definition, by contrast, is fixed at more than six letters — that's Björnsson's published definition, and changing it means the output is no longer LIX.

Because these two definitions are not interchangeable across indices, TextStatistics exposes both counts (WordsWithMoreThanSixLetters, WordsWithSixOrMoreLetters), computed in the same pass. LIX always uses WordsWithMoreThanSixLetters and is not configurable. WSTF defaults to the same (LongWordRule.MoreThanSix) but can be switched to LongWordRule.SixOrMore:

var options = new ReadabilityOptions { WstfLongWordRule = LongWordRule.SixOrMore };

This option deliberately lives on ReadabilityOptions, not the shared TokenizerOptions — a global "long word" setting would silently corrupt LIX output the moment someone tuned it for WSTF parity.

Clamping

By default, no index is clamped to its nominal display range — a short or unusual text can legitimately produce a WSTF below 4, a Flesch score above 100 or below 0, or a very low LIX. Clamping by default would silently hide those signals. Pass ClampScores = true on ReadabilityOptions to clamp WSTF to [4, 15] and Flesch to [0, 100]; LIX is never clamped — it has no hard upper bound by design.

Accuracy & limitations

  • Syllable counting is a heuristic, not a dictionary. GermanSyllableCounter counts vowel groups with German-specific adjustments (diphthongs/digraphs, no final-e stripping, qu excluded, hyphenated compounds split and summed) and targets ≥95% exact-match accuracy on a curated German word list. It does not embed a hyphenation dictionary — the standard German pattern files are LGPL/MPL-licensed, which would compromise this package's MIT license, and they are large. If you need dictionary-grade accuracy, implement ISyllableCounter against one.
  • Known failure cases: words where ie spans a morpheme boundary rather than forming a single vowel sound undercount by one syllable — e.g. Familie, Ferien, Linie. Loanwords and acronyms with non-German vowel patterns are not specially handled.
  • Flesch is the most sensitive of the three to syllable-count error. Its ASW term carries a 58.5 coefficient, so a syllable miscount moves the score substantially. WSTF is more forgiving, and LIX doesn't use syllables at all — it's immune to this class of error entirely.
  • German only. English Flesch, Flesch-Kincaid, SMOG, Gunning-Fog and RIX are out of scope for this package by design; using the English Flesch formula on German text is a common and serious error, which is why this library doesn't offer it.
  • Input is plain string — HTML/PDF/DOCX extraction is the caller's responsibility.

Provenance & license

MIT licensed — see LICENSE. This is a clean-room implementation from the published formulas and definitions (Bamberger/Vanecek 1965 for WSTF, Amstad 1978 for the German Flesch adaptation, Björnsson 1968 for LIX); no GPL or other copyleft-licensed code was used or consulted beyond formula-level facts, which are not copyrightable.

Credit and thanks to pablotheissen/wstf, a small Python WSTF implementation, for the initial inspiration to build this — its source (GPL-3.0) was not copied, translated, or transliterated in any form, and none of its test fixtures were reused.

Contributing

Issues and pull requests welcome at github.com/taskinkemal/Readability.German.

To run the tests locally:

dotnet test -c Release

The solution targets net8.0 and net10.0; global.json pins the SDK feature band so local builds match CI.

About

wiener-sachtextformel, flesch, lix, readability, german, lesbarkeit, nlp

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages