Readability.German
1.0.0
dotnet add package Readability.German --version 1.0.0
NuGet\Install-Package Readability.German -Version 1.0.0
<PackageReference Include="Readability.German" Version="1.0.0" />
<PackageVersion Include="Readability.German" Version="1.0.0" />
<PackageReference Include="Readability.German" />
paket add Readability.German --version 1.0.0
#r "nuget: Readability.German, 1.0.0"
#:package Readability.German@1.0.0
#addin nuget:?package=Readability.German&version=1.0.0
#tool nuget:?package=Readability.German&version=1.0.0
Readability.German
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 | ❤️0 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.
GermanSyllableCountercounts vowel groups with German-specific adjustments (diphthongs/digraphs, no final-estripping,quexcluded, 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, implementISyllableCounteragainst one. - Known failure cases: words where
iespans 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
ASWterm 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.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- No dependencies.
-
net8.0
- No dependencies.
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 77 | 8/9/2026 |