Put the tone marks back into text that lost them

    Restore diacritics and tone marks on text that was typed, scraped, or keyed in without them — across 10 languages, from Yorùbá and Igbo to Vietnamese and Polish.

    Media & publishingArchives & librariesEdtechGovernment records
    ~0.02
    MEDIAN CER ACROSS LANGUAGES
    10
    LANGUAGES, ONE MODEL
    9 / 10
    BEAT THE DO-NOTHING FLOOR
    / The problem

    Why this breaks today

    Almost nobody types diacritics. Phone keyboards make them slow, legacy databases strip them, and OCR of older print drops them entirely. The result is an archive where "ọkọ" (husband), "oko" (farm), and "òkò" (stone) all collapse into the same string.

    For a publisher that means unreadable copy. For an archive it means text that no longer says what the author wrote. For any search index built on top, it means queries that silently fail to match.

    Restoring the marks by hand is a proofreading job measured in person-months, and it needs speakers of every language involved.

    / How it works

    The pipeline, step by step

    01

    Tag the target language

    Tell the model which of its ten languages the text is in — yo, vi, ig, ha, pl, tr, pt, es, fr, it. One joint model covers all of them, so there is no per-language model to load or route between. Pass model="auto" and the SDK detects the language and routes for you.

    diacnet-1.0
    02

    Restore at the byte level

    diacnet-1.0 is fine-tuned from google/byt5-small, so it works on bytes rather than words. Yorùbá tone marks, Vietnamese combining diacritics, and Polish special characters all go through the same mechanism, and unseen words get handled instead of skipped.

    diacnet-1.0
    03

    Run it sentence by sentence

    The model is trained on sentence-level examples with a 256-byte window. Split long documents on sentence boundaries and batch them — a ~300M-parameter seq2seq model is cheap enough to sweep an entire back catalogue on one GPU.

    / The code

    Running in about ten lines

    The Olaverse SDK wraps the models with sane defaults. If you would rather not add a dependency, the second tab is the same pipeline in plain transformers.

    $pip install olaverse[deeplearning]
    restore.py
    from olaverse.nlp import Diacritizer
    
    # One joint model, ten languages — name the one your text is in
    d = Diacritizer(model="diacnet-1.0", lang="yo")
    d.restore("se eranko naa si gbo o?")
    # → 'ṣé ẹranko náà sì gbọ́ ọ?'
    
    # Or let the SDK detect the language and route for you
    Diacritizer(model="auto").restore("Ojo lo si oja lana")
    # → 'Òjó lọ sí ọjà lana'
    
    # lang= accepts: yo vi ig ha pl tr pt es fr it

    Language coverage

    / 10 LANGUAGES

    Every language the models in this pipeline were trained on, with the codes you pass and the codes you get back.

    Yorùbá
    yo · yor
    Vietnamese
    vi · vie
    Igbo
    ig · ibo
    Hausa
    ha · hau
    Polish
    pl · pol
    Turkish
    tr · tur
    Portuguese
    pt · por
    Spanish
    es · spa
    French
    fr · fra
    Italian
    it · ita

    Scoped deliberately to languages where diacritics are lexically meaningful. The other 15 languages in the source corpus (Swahili, Zulu, Amharic, Japanese and the rest) were left out because restoration either does not apply or is not the right frame for the script.

    / This is you if
    • You hold a corpus of undiacritized text — CMS content, scanned print, user submissions, or an old database export.
    • Your users type without tone marks but expect to find correctly written text.
    • You publish in Yorùbá, Igbo, Hausa, Vietnamese, Polish, Turkish, Portuguese, Spanish, French, or Italian.
    / What it can't do
    • Yorùbá is the hardest language in the set: 29.1% WER against an 87.3% do-nothing floor. A huge improvement, but not proofreader-grade — plan on a human pass for published copy.
    • Hausa is the one language where the model does not beat leaving the text alone; Hausa needs so few marks that the floor is already 7.8% WER.
    • Trained on sentence-level input with a 256-byte window. Long passages must be split before they go in.

    Every figure on this page comes from the published model cards, and every limitation is one the cards state themselves. Download the weights and check us.

    / Models used

    All open weights, all Stable tier

    Want this on your data?

    The weights are open, so you can build it yourself this afternoon. If you would rather we tuned it to your domain, evaluated it properly, and handed it over, tell us what you are working on.