Die normale Textkorrektur mit aspell (oder ispell oder hunspell) prüft leider nur einzelne Wörter. Fehler bei der Groß-/Kleinschreibung und Grammatikfehler werden zum Beispiel häufig nicht erkannt: »Die Text ist am Besten.« Das Programm LanguageTool hingegen kann dies, weshalb es sich als Ergänzung oder Ersatz für die Textprüfung lohnt. Da das Programm unter der LGPL entwickelt wird, steht der Quelltext zur Verfügung und somit lässt sich eine eigene Instanz einrichten.

Installation

Für LanguageTool wird die Java-Umgebung benötigt: apt install default-jre-headless oder konkret eine aktuelle Version von OpenJDK apt install openjdk-11-jre-headless. Das Paket mit LanguageTool muss von dessen Webseite (Alternative) geladen und in ein Verzeichnis entpackt werden:

% wget https://languagetool.org/download/LanguageTool-stable.zip
% sudo unzip LanguageTool-stable.zip -d /opt
% sudo mv /opt/LanguageTool-stable /opt/LanguageTool

Um zu prüfen, ob die Installation funktioniert, kann man das Kommandozeilenprogramm von LanguageTool verwenden und einen einfachen Text überprüfen lassen:

% java -Dfile.encoding=UTF-8 -jar /opt/LanguageTool/languagetool-commandline.jar -c utf8 -l de-DE <<<'Die Text ist am Besten.'
Expected text language: German (Germany)
Working on STDIN...
1.) Line 1, column 1, Rule ID: DE_AGREEMENT
Message: Möglicherweise fehlende grammatische Übereinstimmung des Genus (männlich, weiblich, sächlich - Beispiel: 'der Fahrrad' statt 'das Fahrrad') und Numerus (Einzahl, Mehrzahl - Beispiel: 'das Fahrräder' statt 'die Fahrräder').
Suggestion: Dem Text; Den Text; Der Text; Die Texte
Die Text ist am Besten.
^^^^^^^^

2.) Line 1, column 17, Rule ID: AM_BESTEN[1]
Message: In der Wendung 'am besten' schreibt man 'besten' klein.
Suggestion: besten
Die Text ist am Besten.
                ^^^^^^
More info: https://www.korrekturen.de/beliebte_fehler/am-besten.shtml
Time: 1496ms for 1 sentences (0.7 sentences/sec)

Daten für n-Gramme installieren

Zur Verbesserung der Fehlererkennung gibt es Erweiterungsdaten für n-Gramme. Die Idee dahinter ist, die Korrektheit von Wörtern mithilfe eines, zweier oder mehrerer Nachbarwörter zu prüfen, also ob das Wort in die Wortgruppe passt.

Vorsicht: Diese Daten sind mehrere Gigabyte groß, also darauf achten, ob das Verzeichnis vom Backup erfasst wird.

Die gewünschten Pakete von languagetool.org/download/ngram-data/ laden und in ein Verzeichnis (z. B. /opt/LanguageTool-ngram) entpacken.

Den Unterschied in der Fehlererkennung sieht man an diesem Beispiel:

% java -Dfile.encoding=UTF-8 -jar /opt/LanguageTool/languagetool-commandline.jar -c utf8 -l de-DE <<<'In den christlichen Traditionen gibt es unterschiedliche Anleitungen zur Mediation und Kontemplation.'
Expected text language: German (Germany)
Working on STDIN...
Time: 1458ms for 1 sentences (0.7 sentences/sec)
java -enableassertions -Dfile.encoding=UTF-8 -jar  -c utf8 -l de-DE <<<   usr 15,35s sys 0,41s tot 4,838 698MB pf 0 182680 cs 8453 5425

% java -Dfile.encoding=UTF-8 -jar /opt/LanguageTool/languagetool-commandline.jar -c utf8 -l de-DE --languagemodel /opt/LanguageTool-ngram <<<'In den christlichen Traditionen gibt es unterschiedliche Anleitungen zur Mediation und Kontemplation.'
Expected text language: German (Germany)
Working on STDIN...
1.) Line 1, column 74, Rule ID: CONFUSION_RULE
Message: Rein statistisch ist 'Meditation' (spirituelle Übung) in diesem Kontext wahrscheinlicher als 'Mediation' (Verfahren zur Konfliktlösung). Bitte prüfen.
Suggestion: Meditation
...nen gibt es unterschiedliche Anleitungen zur Mediation und Kontemplation.
                                                ^^^^^^^^^
Time: 1583ms for 1 sentences (0.6 sentences/sec)
java -enableassertions -Dfile.encoding=UTF-8 -jar  -c utf8 -l de-DE   <<<   usr 17,46s sys 0,41s tot 5,355 701MB pf 0 181455 cs 9875 6135

Daten für word2vec einrichten

Es gibt noch eine weitere Erweiterung für die Erkennung von Fehlern, die mit einem Word2vec-Modell für ein neuronales Netzwerk arbeitet. Aktuell (Juli 2020) funktioniert sie, jedoch kümmert sich nicht niemand mehr um den Code.

Ein Beispiel für die verbesserte Erkennung zeigt sich an diesem Text:

Ich glaube, das der Spieleabend gut besucht sein wir, da wir fiel Werbung gemacht haben. Was machst du den da? Ab wann seit ihr in der Uni? Ich bin gerade ihm Copyshop.

Ob die Erkennung funktioniert, lässt sich mit folgendem kleinen Beispiel prüfen:

% java -Dfile.encoding=UTF-8 -jar /opt/LanguageTool/languagetool-commandline.jar \
  -c utf8 -l de-DE --languagemodel /opt/LanguageTool-ngram \
  --word2vecmodel /opt/LanguageTool-word2vec <<<'Was machst du den da?'
Expected text language: German (Germany)
Working on STDIN...
1.) Line 1, column 15, Rule ID: DE_den_VS_denn_NEURALNETWORK
Message: Unser neuronales Netz sagt, dass 'denn' hier korrekt sein könnte statt 'den'. Bitte prüfen.
Suggestion: denn
Was machst du den da?
              ^^^
Time: 1341ms for 1 sentences (0.7 sentences/sec)

Bessere Spracherkennung mit Fasttext

Es gibt noch ein Modul für bessere Spracherkennung, aber deren Sinn hat sich mir nicht so richtig erschlossen. Bei gemischtsprachigen Texten war die Erkennung auch nicht besser und sonst gebe ich immer die Sprache an. Vielleicht ist es für Texte, bei denen keine Sprache angegeben wurde.

Für Fasttext sind zwei Dinge notwendig: eine Datenbank und ein Programm. Die Datenbank lid.176.bin muss man von der Seite fastText laden und beim Aufruf des Kommandozeilenprogramms mittels --fasttextbinary … mit dem Dateinamen angeben.

Das Programm fasttext kann man entweder aus den Quellen bauen oder man nutzt das Debian-Paket. Beim Aufruf des Kommandozeilenprogramms muss man den Pfad zum Programm mit --fasttextbinary … angeben.

Der Dienst, wie er im Abschnitt über Systemd genutzt wird, benötigt für Fasttext eine Konfigurationsdatei, in der die Pfade angegeben werden:

fasttextModel=/opt/LanguageTool-fasttext-lid.176.bin
fasttextBinary=/usr/bin/fasttext

Beschleunigung der Abfragen

In einer Forumsdiskussion habe ich Einstellungen gefunden, die die Antwortgeschwindigkeit wesentlich beschleunigen. Hierfür muss LanguageTool mit einer Konfigurationsdatei mit folgendem Inhalt gestartet werden:

pipelineCaching=true
maxPipelinePoolSize=500
pipelineExpireTimeInSeconds=3600

Die Datei habe ich der Einfachheit halber als /etc/opt/languagetool.cfg abgelegt und dem Start die Option --config /etc/opt/languagetool.cfg ergänzt.

Der Vergleich der beiden Varianten lässt sich sehr schön mit dem Rust-Programm hyperfine vornehmen (Installation mit cargo install hyperfine). Dem Programm werden einfach mehrere Programmaufrufe übergeben, die dann mehrfach aufgerufen und deren Laufzeit gemessen wird. Als Beispiele habe ich eine sehr einfache Abfrage und eine Abfrage des Firefox-Add-ons verwendet.

% hyperfine --warmup 3 \
  'curl -s --data "language=de-DE&text=Eine erster Versuch" http://localhost:8081/v2/check' \
  "curl -s http://localhost:8081/v2/check?c=1 \
  --data-raw 'data=%7B%22text%22%3A%22Einen+guten+Morgen%22%7D&textSessionId=56639%3A1637566034156&enableHiddenRules=true&motherTongue=de&level=picky&language=auto&noopLanguages=de%2Cen&preferredLanguages=de%2Cen&preferredVariants=en-GB%2Cde-DE%2Cpt-BR%2Cca-ES&disabledRules=WHITESPACE_RULE%2CCONSECUTIVE_SPACES&mode=textLevelOnly'"

Werte ohne die Konfigurationsdatei:

Benchmark 1: curl -s --data "language=de-DE&text=Eine erster Versuch" http://localhost:8081/v2/check
  Time (mean ± σ):     440.5 ms ±  48.1 ms    [User: 5.2 ms, System: 1.6 ms]
  Range (min … max):   387.3 ms … 547.8 ms    10 runs

Benchmark 2: curl -s http://localhost:8081/v2/check?c=1 --data-raw 'data=%7B%22text%22%3A%22Einen+guten+Morgen%22%7D&textSessionId=56639%3A1637566034156&enableHiddenRules=true&motherTongue=de&level=picky&language=auto&noopLanguages=de%2Cen&preferredLanguages=de%2Cen&preferredVariants=en-GB%2Cde-DE%2Cpt-BR%2Cca-ES&disabledRules=WHITESPACE_RULE%2CCONSECUTIVE_SPACES&mode=textLevelOnly'
  Time (mean ± σ):      82.1 ms ±  10.6 ms    [User: 5.2 ms, System: 1.7 ms]
  Range (min … max):    72.4 ms … 119.5 ms    31 runs

Werte mit der Konfigurationsdatei:

Benchmark 1: curl -s --data "language=de-DE&text=Eine erster Versuch" http://localhost:8081/v2/check
  Time (mean ± σ):      34.0 ms ±   3.5 ms    [User: 5.3 ms, System: 1.6 ms]
  Range (min … max):    30.7 ms …  50.0 ms    87 runs

Benchmark 2: curl -s http://localhost:8081/v2/check?c=1 --data-raw 'data=%7B%22text%22%3A%22Einen+guten+Morgen%22%7D&textSessionId=56639%3A1637566034156&enableHiddenRules=true&motherTongue=de&level=picky&language=auto&noopLanguages=de%2Cen&preferredLanguages=de%2Cen&preferredVariants=en-GB%2Cde-DE%2Cpt-BR%2Cca-ES&disabledRules=WHITESPACE_RULE%2CCONSECUTIVE_SPACES&mode=textLevelOnly'
  Time (mean ± σ):      10.3 ms ±   0.5 ms    [User: 4.4 ms, System: 2.2 ms]
  Range (min … max):     9.5 ms …  13.4 ms    278 runs

Systemd einrichten

Die Einrichtung des Servers ist recht einfach, jedoch sollte bedacht werden, dass der Prozess im Betrieb auf eine Größe von 2,5 Gigabyte RAM anwächst. Der Dienst ist ein HTTP-Server (ohne SSL), der auf dem Port 8081 lauscht. Wenn man mit --port … einen anderen Port wählt, muss man im Browser-Addon als Serveradresse http://localhost:…/v2 angeben.

Wie immer lässt sich ein solcher Dienst leicht mit Systemd einrichten: sudo systemctl edit --force --full languagetool.service:

[Unit]
Description=Language checking backend LanguageTool
Documentation=https://languagetool.org/
Documentation=http://wiki.languagetool.org/http-server

[Service]
SyslogIdentifier=langtool
ExecStart=/usr/bin/java -cp /opt/LanguageTool/languagetool-server.jar \
  org.languagetool.server.HTTPServer --allow-origin * \
  --config /etc/opt/languagetool.cfg \
  --languageModel /opt/LanguageTool-ngram \
  --word2vecModel /opt/LanguageTool-word2vec

UMask=0077
User=daemon
Group=daemon

# OOM killer should prefer this process
OOMPolicy=kill
OOMScoreAdjust=800

# Common security settings 'https://jo-so.de/2021-05/H%C3%A4rtung-Systemd.html'
CapabilityBoundingSet=

LockPersonality=true
MemoryDenyWriteExecute=true
NoNewPrivileges=true

PrivateDevices=true
PrivateTmp=true
ProtectClock=true
ProtectControlGroups=true
ProtectHostname=true
ProtectKernelLogs=true
ProtectKernelModules=true
ProtectKernelTunables=true

# strict: mounts the whole system read-only
# full: mount only /var as read-write
# true: mount /var and /etc as read-write
ProtectSystem=strict
# Make some paths writable
#ReadWritePaths=/…
ProtectHome=true

# Make sure that the process can only see PIDs and process details of itself,
# and the second option disables seeing details of things like system load and
# I/O etc
ProtectProc=invisible
ProcSubset=pid

RemoveIPC=true
RestrictAddressFamilies=AF_UNIX
RestrictNamespaces=true
RestrictRealtime=true
RestrictSUIDSGID=true

SystemCallArchitectures=native
SystemCallFilter=@system-service
SystemCallFilter=~@privileged @resources @obsolete
# end of common security settings

MemoryDenyWriteExecute=false
RestrictAddressFamilies=AF_INET

[Install]
WantedBy=default.target

Danach den Dienst aktivieren und gleich starten sudo systemctl enable --now languagetool.service und mit einer einfach Abfrage prüfen:

% curl -s --data "language=de-DE&text=Eine erster Versuch" http://localhost:8081/v2/check |jq .
{
  "software": {
    "name": "LanguageTool",
    "version": "4.9.1",
    "buildDate": "2020-04-27 15:56",
    "apiVersion": 1,
    "premium": false,
    "premiumHint": "You might be missing errors only the Premium version can find. Contact us at support<at>languagetoolplus.com.",
    "status": ""
  },
  "warnings": {
    "incompleteResults": false
  },
  "language": {
    "name": "German (Germany)",
    "code": "de-DE",
    "detectedLanguage": {
      "name": "German (Germany)",
      "code": "de-DE",
      "confidence": 0.9999903
    }
  },
  "matches": [
    {
      "message": "Möglicherweise fehlende grammatische Übereinstimmung von Kasus, Numerus oder Genus. Beispiel: 'mein kleiner Haus' statt 'mein kleines Haus'",
      "shortMessage": "Möglicherweise keine Übereinstimmung von Kasus, Numerus oder Genus",
      "replacements": [],
      "offset": 0,
      "length": 19,
      "context": {
        "text": "Eine erster Versuch",
        "offset": 0,
        "length": 19
      },
      "sentence": "Eine erster Versuch",
      "type": {
        "typeName": "Other"
      },
      "rule": {
        "id": "DE_AGREEMENT",
        "description": "Kongruenz von Nominalphrasen (unvollständig!), z.B. 'mein kleiner(kleines) Haus'",
        "issueType": "uncategorized",
        "category": {
          "id": "GRAMMAR",
          "name": "Grammatik"
        }
      },
      "ignoreForIncompleteSentence": true,
      "contextForSureMatch": 6
    }
  ]
}

Browser-Addon

Für den Browser Firefox, aber auch für Chrome, gibt es ein Addon, damit während der Eingabe die Rechtschreibkontrolle von LanguageTool genutzt wird. An vielen Stellen ist dies praktisch, aber bei Chats nervt es zum Teil, da man dort nicht immer exaktes Deutsch schreibt. Weiterhin hat die Erweiterung den Nachteil, dass die Korrekturvorschläge mit einem Links-Klick und nicht mit der rechten Maustaste erscheinen und somit die Menütaste an der Tastatur nicht genutzt werden kann. Die Prüfung mit LanguageTool ist auch nicht so schnell wie die eingebaute Rechtschreibprüfung, weshalb man oft Tippfehler erst einige Worte später angezeigt bekommt und dann zurück zum Wort laufen muss und dann wieder an die Tippposition.

Vorsicht ist auch bei der Erweiterung geboten, da man zum Teil sensible Texte (auch Passwörter z. B. bei /join #channel key bei IRC und Matrix) eintippt, die dann an einen Server übertragen werden. Die Erweiterung sollte also nur mit einem vertrauenswürdigen Server genutzt werden.

Den Server kann man in den Einstellungen zum Addon unter Experimental settings (only for advanced users) auf Local server (localhost) oder einen anderen umschalten.

Emacs-Mode langtool für Servernutzung

Für Emacs gibt es das Melpa-Paket langtool, mit dem man einen LanguageTool-Server nutzen kann. Aber auch hier ist die Warnung wie beim Firefox-Addon: Es sollte nur ein vertrauenswürdiger Server genutzt werden, da alle Texte dorthin gesandt werden.

Die Installation ist recht leicht über M-x package-list-packages und die Konfiguration in der init.el ist übersichtlich kurz:

(setq
 langtool-http-server-host "localhost"
 langtool-http-server-port 8081
 langtool-mother-tongue "de-DE"
 langtool-default-language "de-DE"
 )

Nutzen lässt sich die Textprüfung mit M-x langtool-check, womit der aktive Bereich oder die gesamte Datei geprüft werden. Fehler werden dann hervorgehoben und können mit M-x langtool-correct-buffer schrittweise bearbeitet werden. Die Textprüfung kann mit M-x langtool-check-done abgeschlossen werden, wodurch die Hervorhebungen verschwinden.

Nützlich ist es, mit M-h den aktuellen Absatz als Region vor der Prüfung zu markieren.

Wenn man mit langtool-correct-buffer die Fehlerstellen durchgeht, kann man mit

Emacs-Mode languagetool für lokale Nutzung

Den ersten Versuch habe ich mit dem Melpa-Paket languagetoool (Installation über M-x package-list-packages) unternommen, welches den Vorteil hat, dass es LanguageTool als Prozess aufruft und über eine Standardein- und -ausgabe kommuniziert. Da ich dann aber auch das Firefox-Addon genutzt habe, brauchte ich die Servervariante und bin daher auf das Emacs-Paket langtool umgestiegen.

(setq
 languagetool-language-tool-jar (expand-file-name
   "/opt/LanguageTool/languagetool-commandline.jar")

 languagetool-java-arguments '("-Dfile.encoding=UTF-8")
 languagetool-default-language "de-DE"
 languagetool-mother-tongue "de-DE"
 languagetool-language-tool-arguments '("--languagemodel"
   (expand-file-name "/opt/LanguageTool-ngram"))
 )

Die Prüfung des Textes kann man mit M-x languagetool-check vornehmen, ggf. zuvor mit M-h den Paragraph markieren. Fehler und Statusmeldungen findet man im Puffer *LanguageTool Output*.

Beispiele für Fehlererkennung

Im Leben lernt der Mensch als erstes ["Erstes" scheint hier ein Nomen zu sein und muss dann großgeschrieben werden.] das Gehen und Sprechen. Später lernt er still zu sitzen [Wenn der erweiterte Infinitv von dem Verb 'stillsitzen' abgeleitet ist, sollte er zusammengeschrieben werden.] und den Mund zu halten.

Addin für Word

Auf der Webseite wird eine Erweiterung für Microsoft Word angeboten, jedoch habe ich dafür nicht den Quelltext finden können und beim Laden im Microsoft Store werden persönliche Daten wie die Telefonnummer abgefragt. Daher sollte man von diesem Addin abstand nehmen.

Stattdessen sollte besser diese freie Erweiterung für Word genutzt werden.

Weitere Informationen

Bei LWN gibt es den Beitrag »Tools to improve English text«, der auch LanguageTool als hilfreich erwähnt, aber noch weitere Programme aufführt. Das Ziel des Artikels sind die Dokumentation von Software, weshalb auch recht spezielle Programme genannt werden: