readability

package module
v0.0.0-...-41d8ce8 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 27, 2025 License: MIT Imports: 4 Imported by: 0

README

readability

A pure Go library for calculating text readability metrics, including Flesch-Kincaid Grade Level. Zero external dependencies, streaming-capable, and optimized for processing large texts like books.

Originally ported from words/syllable (MIT License).

A big thanks to the Project Gutenberg and OpenBible for providing test data for the grading.

See the example folder for API usage

Features

  • Flesch-Kincaid Grade Level — Direct US grade level output (5.0 = 5th grade)
  • Flesch Reading Ease — 0-100 score (higher = easier)
  • Automated Readability Index (ARI) — Character-based, no syllable counting needed
  • Gunning Fog Index — Estimates years of education needed
  • SMOG Index — Simple Measure of Gobbledygook
  • Coleman-Liau Index — Character-based grade level
Why This Library?
  • No regex — Uses rune iteration and string matching for better performance
  • Streaming support — Process files of any size via io.Reader
  • Zero dependencies — Only uses Go standard library
  • Accurate syllable counting — Handles silent e, -ed/-es endings, compound vowels, and 100+ problematic words

Installation

go get github.com/acmacalister/readability

Quick Start

package main

import (
    "fmt"
    "github.com/acmacalister/readability"
)

func main() {
    text := "The quick brown fox jumps over the lazy dog."
    
    // Get Flesch-Kincaid Grade Level
    grade := readability.FleschKincaidGrade(text)
    fmt.Printf("Grade Level: %.1f\n", grade)
    
    // Get Flesch Reading Ease
    ease := readability.FleschReadingEase(text)
    fmt.Printf("Reading Ease: %.1f\n", ease)
}

Usage

From a String
grade := readability.FleschKincaidGrade(text)
ease := readability.FleschReadingEase(text)
ari := readability.AutomatedReadabilityIndex(text)
fog := readability.GunningFogIndex(text)
smog := readability.SMOGIndex(text)
cli := readability.ColemanLiauIndex(text)
From an io.Reader (Files, HTTP responses, etc.)
file, _ := os.Open("book.txt")
defer file.Close()

grade, err := readability.FleschKincaidGradeReader(file)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("Book grade level: %.1f\n", grade)
Analyze Once, Calculate Multiple Metrics

If you need multiple metrics, analyze the text once and reuse the stats:

stats := readability.Analyze(text)

grade := readability.FleschKincaidGradeFromStats(stats)
ease := readability.FleschReadingEaseFromStats(stats)
ari := readability.AutomatedReadabilityIndexFromStats(stats)
fog := readability.GunningFogIndexFromStats(stats)
Streaming Large Files

For memory-efficient processing of large files:

file, _ := os.Open("large-book.txt")
defer file.Close()

stats, err := readability.AnalyzeReader(file)
if err != nil {
    log.Fatal(err)
}

grade := readability.FleschKincaidGradeFromStats(stats)
Syllable Counting Only

If you only need syllable counts:

count := readability.Count("beautiful") // returns 3

// Or from a reader
file, _ := os.Open("document.txt")
count, err := readability.CountReader(file)

Grade Level Interpretation

Flesch-Kincaid Grade Level
Score Grade Level
1.0 1st grade
5.0 5th grade
8.0 8th grade
12.0 12th grade (senior)
13+ College level
Flesch Reading Ease
Score Difficulty Grade Level
90-100 Very easy 5th grade
80-89 Easy 6th grade
70-79 Fairly easy 7th grade
60-69 Standard 8th-9th grade
50-59 Fairly difficult 10th-12th grade
30-49 Difficult College
0-29 Very difficult College graduate

API Reference

Types
// Stats holds text statistics for readability calculations
type Stats struct {
    Characters int // Letter/digit count (for ARI)
    Words      int
    Sentences  int
    Syllables  int
}

// Analyzer provides streaming text analysis
type Analyzer struct { /* ... */ }
Functions
Analysis
func Analyze(text string) Stats
func AnalyzeReader(r io.Reader) (Stats, error)
func NewAnalyzer() *Analyzer
Flesch-Kincaid Grade Level
func FleschKincaidGrade(text string) float64
func FleschKincaidGradeReader(r io.Reader) (float64, error)
func FleschKincaidGradeFromStats(stats Stats) float64
Flesch Reading Ease
func FleschReadingEase(text string) float64
func FleschReadingEaseReader(r io.Reader) (float64, error)
func FleschReadingEaseFromStats(stats Stats) float64
Automated Readability Index
func AutomatedReadabilityIndex(text string) float64
func AutomatedReadabilityIndexReader(r io.Reader) (float64, error)
func AutomatedReadabilityIndexFromStats(stats Stats) float64
Other Metrics
func GunningFogIndex(text string) float64
func GunningFogIndexFromStats(stats Stats) float64

func SMOGIndex(text string) float64
func SMOGIndexFromStats(stats Stats) float64

func ColemanLiauIndex(text string) float64
func ColemanLiauIndexFromStats(stats Stats) float64
Syllable Counting
func Count(text string) int
func CountReader(r io.Reader) (int, error)
func NewCounter() *Counter

Performance

Benchmarks on Apple M1:

BenchmarkAnalyze-8              500000    2341 ns/op    0 B/op    0 allocs/op
BenchmarkAnalyzeLongText-8        5000  234521 ns/op    0 B/op    0 allocs/op
BenchmarkFleschKincaidGrade-8     5000  234892 ns/op    0 B/op    0 allocs/op

Accuracy Notes

The syllable counter handles most English words accurately, including:

  • Silent 'e' endings (make, love, time)
  • -ed endings (jumped vs wanted)
  • -es endings (boxes vs makes)
  • Compound vowels (beautiful, idea, video)
  • Common prefixes/suffixes (un-, -ness, -tion)
  • 100+ problematic words (people, queue, recipe, Wednesday, etc.)

For maximum accuracy with book-length texts, the errors tend to average out. If you need per-word precision, consider supplementing with a dictionary lookup.

Example: Book Pipeline Integration

package main

import (
    "fmt"
    "math"
    "os"
    
    "github.com/acmacalister/readability"
)

func main() {
    file, err := os.Open("book.txt")
    if err != nil {
        panic(err)
    }
    defer file.Close()
    
    stats, err := readability.AnalyzeReader(file)
    if err != nil {
        panic(err)
    }
    
    grade := readability.FleschKincaidGradeFromStats(stats)
    
    // Round to nearest 0.5 for storage
    roundedGrade := math.Round(grade*2) / 2
    
    fmt.Printf("Words: %d\n", stats.Words)
    fmt.Printf("Sentences: %d\n", stats.Sentences)
    fmt.Printf("Syllables: %d\n", stats.Syllables)
    fmt.Printf("Grade Level: %.1f\n", roundedGrade)
}

Running the Example

cd example
go run main.go

This demonstrates all the readability metrics, syllable counting, streaming analysis, and incremental processing.

License

MIT License - see LICENSE file for details.

Syllable counting algorithm ported from words/syllable (MIT License).

Documentation

Overview

Package readability provides text readability metrics including Flesch-Kincaid Grade Level and Flesch Reading Ease scores.

Package syllable provides accurate syllable counting for English text. Ported from https://github.com/words/syllable (MIT License)

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AutomatedReadabilityIndex

func AutomatedReadabilityIndex(text string) float64

AutomatedReadabilityIndex returns the ARI score for a string. Uses character count instead of syllables, making it faster to compute. Output is a US grade level approximation.

func AutomatedReadabilityIndexFromStats

func AutomatedReadabilityIndexFromStats(stats Stats) float64

AutomatedReadabilityIndexFromStats calculates ARI from pre-computed stats. Formula: 4.71 × (characters/words) + 0.5 × (words/sentences) − 21.43

func AutomatedReadabilityIndexReader

func AutomatedReadabilityIndexReader(r io.Reader) (float64, error)

AutomatedReadabilityIndexReader calculates ARI from an io.Reader.

func ColemanLiauIndex

func ColemanLiauIndex(text string) float64

ColemanLiauIndex returns the Coleman-Liau Index. Uses character count instead of syllables. Output is a US grade level approximation.

func ColemanLiauIndexFromStats

func ColemanLiauIndexFromStats(stats Stats) float64

ColemanLiauIndexFromStats calculates Coleman-Liau from pre-computed stats. Formula: 0.0588 × L − 0.296 × S − 15.8 where L = average letters per 100 words, S = average sentences per 100 words

func Count

func Count(text string) int

Count returns the number of syllables in a string.

func CountReader

func CountReader(r io.Reader) (int, error)

CountReader counts syllables from an io.Reader.

func FleschKincaidGrade

func FleschKincaidGrade(text string) float64

FleschKincaidGrade returns the Flesch-Kincaid Grade Level for a string. The result maps directly to US grade levels:

  • 1.0 = 1st grade
  • 5.0 = 5th grade
  • 12.0 = 12th grade (senior)
  • 13+ = college level

func FleschKincaidGradeFromStats

func FleschKincaidGradeFromStats(stats Stats) float64

FleschKincaidGradeFromStats calculates grade level from pre-computed stats. Formula: 0.39 × (words/sentences) + 11.8 × (syllables/words) − 15.59

func FleschKincaidGradeReader

func FleschKincaidGradeReader(r io.Reader) (float64, error)

FleschKincaidGradeReader calculates grade level from an io.Reader.

func FleschReadingEase

func FleschReadingEase(text string) float64

FleschReadingEase returns the Flesch Reading Ease score for a string. Scores range from 0-100 (higher = easier to read):

  • 90-100: Very easy (5th grade)
  • 80-89: Easy (6th grade)
  • 70-79: Fairly easy (7th grade)
  • 60-69: Standard (8th-9th grade)
  • 50-59: Fairly difficult (10th-12th grade)
  • 30-49: Difficult (college)
  • 0-29: Very difficult (college graduate)

func FleschReadingEaseFromStats

func FleschReadingEaseFromStats(stats Stats) float64

FleschReadingEaseFromStats calculates reading ease from pre-computed stats. Formula: 206.835 − 1.015 × (words/sentences) − 84.6 × (syllables/words)

func FleschReadingEaseReader

func FleschReadingEaseReader(r io.Reader) (float64, error)

FleschReadingEaseReader calculates reading ease from an io.Reader.

func GunningFogIndex

func GunningFogIndex(text string) float64

GunningFogIndex returns the Gunning Fog Index for a string. Output is a US grade level approximation. Note: This is an approximation as it estimates complex words from syllable count.

func GunningFogIndexFromStats

func GunningFogIndexFromStats(stats Stats) float64

GunningFogIndexFromStats calculates Gunning Fog from pre-computed stats. Formula: 0.4 × [(words/sentences) + 100 × (complex words/words)] Complex words are estimated as words with 3+ syllables.

func SMOGIndex

func SMOGIndex(text string) float64

SMOGIndex returns the SMOG (Simple Measure of Gobbledygook) index. Most accurate for texts with 30+ sentences. Output is a US grade level approximation.

func SMOGIndexFromStats

func SMOGIndexFromStats(stats Stats) float64

SMOGIndexFromStats calculates SMOG index from pre-computed stats.

Types

type Analyzer

type Analyzer struct {
	// contains filtered or unexported fields
}

Analyzer provides streaming text analysis for readability metrics.

func NewAnalyzer

func NewAnalyzer() *Analyzer

NewAnalyzer creates a new text analyzer.

func (*Analyzer) ReadFrom

func (a *Analyzer) ReadFrom(r io.Reader) (n int64, err error)

ReadFrom reads and analyzes text from an io.Reader. Implements io.ReaderFrom interface.

func (*Analyzer) Reset

func (a *Analyzer) Reset()

Reset clears all analyzer state.

func (*Analyzer) Stats

func (a *Analyzer) Stats() Stats

Stats returns the computed statistics. Safe to call multiple times.

func (*Analyzer) Write

func (a *Analyzer) Write(p []byte) (n int, err error)

Write implements io.Writer for streaming analysis.

func (*Analyzer) WriteString

func (a *Analyzer) WriteString(s string)

WriteString processes a string for analysis.

type Counter

type Counter struct {
	// contains filtered or unexported fields
}

Counter provides streaming syllable counting.

func NewCounter

func NewCounter() *Counter

NewCounter creates a new syllable counter.

func (*Counter) ReadFrom

func (c *Counter) ReadFrom(r io.Reader) (n int64, err error)

ReadFrom reads from an io.Reader and counts syllables. Implements io.ReaderFrom interface.

func (*Counter) Reset

func (c *Counter) Reset()

Reset clears the counter state.

func (*Counter) Total

func (c *Counter) Total() int

Total returns the current syllable count, flushing any pending word.

func (*Counter) Write

func (c *Counter) Write(p []byte) (n int, err error)

Write implements io.Writer for streaming.

func (*Counter) WriteString

func (c *Counter) WriteString(s string)

WriteString processes a string for syllable counting.

type Stats

type Stats struct {
	Characters int // Letter/digit count (for ARI)
	Words      int
	Sentences  int
	Syllables  int
}

Stats holds the basic text statistics needed for readability calculations.

func Analyze

func Analyze(text string) Stats

Analyze extracts readability statistics from a string.

func AnalyzeReader

func AnalyzeReader(r io.Reader) (Stats, error)

AnalyzeReader extracts readability statistics from an io.Reader.

Directories

Path Synopsis
Example demonstrates the readability package's text analysis capabilities.
Example demonstrates the readability package's text analysis capabilities.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL