npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@v4nn4/ablaut

v0.10.0

Published

Fast, correct verb conjugation for 21 languages, each verified against two independent gold lexicons

Readme

The name is the German term for the vowel gradation in singen, sang, gesungen: Jacob Grimm formalized its seven classes in 1819, and they still describe every strong verb in the language.

Highlights

  • Measured correctness. Every language is validated against two independent machine-readable lexicons. Where the two sources agree, ablaut has zero known errors in nineteen of twenty-one languages; the remaining disagreements are ruled on in published adjudication logs. CI re-checks 3.5 million forms on every change.
  • Generalizes to unseen verbs. Rule engines with curated exception tables, not lookup dumps: novel verbs (googeln, tweeter) conjugate correctly.
  • Fast and small. No I/O, no runtime data files, no dependencies in the core crate. A full table takes microseconds; all twenty-one languages fit in one small WebAssembly binary.
  • Permissively licensed. MIT OR Apache-2.0. Reference lexicons are used at test time only and never shipped.

Languages

German, French, Spanish, Catalan, Portuguese, Italian, Romanian, Swedish, English, Danish, Czech, Slovenian, Estonian, Finnish, Irish, Ukrainian, Icelandic, Japanese, Korean, Dutch, and Russian.

Each language is scored against the slots where its two reference lexicons agree; the second column is that score.

| | Language | Verified forms | Accuracy | |---|---|---:|---:| | 🇩🇪 | German | 194,254 | 99.2% | | 🇫🇷 | French | 284,034 | 100.00% | | 🇪🇸 | Spanish | 339,200 | 100.00% | | 🇦🇩 | Catalan | 187,186 | 100.00% | | 🇵🇹 | Portuguese | 373,163 | 100.00% | | 🇮🇹 | Italian | 280,143 | 100.00% | | 🇷🇴 | Romanian | 225,494 | 100.00% | | 🇸🇪 | Swedish | 39,453 | 100.00% | | 🇬🇧 | English | 74,600 | 100.00% | | 🇩🇰 | Danish | 20,205 | 100.00% | | 🇨🇿 | Czech | 107,812 | 100.00% | | 🇸🇮 | Slovenian | 10,757 | 100.00% | | 🇪🇪 | Estonian | 24,184 | 100.00% | | 🇫🇮 | Finnish | 406,209 | 100.00% | | 🇮🇪 | Irish | 27,404 | 100.00% | | 🇺🇦 | Ukrainian | 71,990 | 100.00% | | 🇮🇸 | Icelandic | 5,749 | 100.00% | | 🇯🇵 | Japanese | 9,421 | 100.00% | | 🇰🇷 | Korean | 3,576 | 100.00% | | 🇳🇱 | Dutch | 27,836 | 100.00% | | 🇷🇺 | Russian | 151,942 | 99.98% |

Details per language, including which lexicons are used and every adjudicated disagreement, live in docs/{lang}/.

Usage

Rust

cargo add ablaut
use ablaut::{conjugate, Conjugation, Lang};

match conjugate("vorbi", Lang::Ron)? {
    Conjugation::Ron(t) => assert_eq!(t.present[0], "vorbesc"),
    _ => unreachable!(),
}

Per-language modules expose richer APIs; the shared contract is Verb::from_infinitive and Table::build:

use ablaut::{Mood, Number, Person, Tense, Verb};

let v = Verb::from_infinitive("aufstehen")?;
v.conjugate(Tense::Present, Mood::Indicative, Person::First, Number::Singular);
// "stehe auf"

use ablaut::fra;
let v = fra::Verb::from_infinitive("appeler")?;
v.conjugate(fra::SimpleTense::Future, fra::Person::First, fra::Number::Singular);
// "appellerai"

Reverse lookup maps a conjugated form back to its infinitive(s) and the slots it fills — fully productive for German, English, French and Spanish, irregular-index-backed for all 21 languages:

use ablaut::{reverse, Lang};

let m = reverse("suis", Lang::Fra);
// être (present 1sg) and suivre (present 1sg, present 2sg)
assert_eq!(m.len(), 2);
assert_eq!(reverse("war", Lang::Deu)[0].infinitive, "sein");
assert_eq!(reverse("hablé", Lang::Spa)[0].infinitive, "hablar");

Python

pip install ablaut
import ablaut

c = ablaut.conjugate("aufstehen")   # German is the default
c.present[0]                        # "stehe auf"
c.auxiliary                         # "sein"

ablaut.conjugate("appeler", lang="fr").present[0]  # "appelle"
ablaut.conjugate("delati", lang="sl").present[3]   # "delava" (the dual)

Wheels are abi3 (Python 3.11+); no Rust toolchain needed.

WebAssembly

wasm-pack build --target web -- --features wasm
import init, { conjugate } from "./pkg/ablaut.js";
await init();
conjugate("aufstehen").present[0];   // "stehe auf"
conjugate("andare", "it").auxiliary; // "essere"

Language codes are ISO 639-1/-3 or English names, case-insensitive.

Coverage

Each language covers its full synthetic paradigm plus the analytic tenses of its written standard: German's separable prefixes and both Konjunktiv rows, French spelling doublets and reflexive clitics, Spanish enclitic imperatives with written stress, Portuguese's personal infinitive, Italian's perfect auxiliary, Romanian's synthetic pluperfect, the Scandinavian s-passives, Czech gendered participles, Slovenian's dual, Estonian and Finnish impersonals and potentials, and Irish's initial mutations. docs/design.md documents the German engine in depth; each other language documents its scope in docs/{lang}/oracles.md.

How correctness is verified

For each language, two independently derived lexicons (a Wiktionary extraction and a national or academic resource) are converted to a common format. The engine is scored on every slot where the two agree, and CI fails if accuracy regresses. Disagreements between the sources are ruled on case by case in docs/{lang}/adjudications.tsv, with references to standard grammars. The process regularly finds errors in the reference data itself; several fixes have been filed upstream.

Design

Each engine is a productive default rule plus a finite table of stored exceptions, the dual-mechanism model of inflection (Marcus, Pinker et al., 1995). Features follow the UniMorph schema.

License

MIT OR Apache-2.0.

Data provenance: the exception tables are factual data about each language (principal parts, class membership, auxiliaries), established by the agreement of independent sources and verified against reference grammars. The reference lexicons themselves are fetched at development time and never shipped, whatever their license.