Urdu text normalization and WER/CER scoring for ASR evaluation, under named
protocols you can report. Zero runtime dependencies. pip install urdunorm
from urdunorm import wer
reference = ["کے لیے شکریہ"] # what the transcriber wrote
hypothesis = ["کیلئے شکریہ"] # what the model emitted, compound joined
wer(reference, hypothesis) # 0.0
wer(reference, hypothesis, mode="lums-compat") # 0.667Same audio, same words, one spelling difference, two published protocols. That gap is why Urdu ASR results do not compare across papers, and why this package makes you name the protocol.
| input | output | what happened |
|---|---|---|
علي كي |
علی کی |
Arabic-keyboard letters unified to their Urdu equivalents. Same word, different keyboard. |
ﻤﯿﮟ |
میں |
Presentation forms, common in PDF-extracted text, folded to normal letters. |
مُحَمَّد |
محمد |
Vowel diacritics stripped. Optional in writing, so a model is not charged for omitting them. |
۲۰۲۶ |
2026 |
All three numeral systems Urdu uses folded onto one. |
کیلئے |
کے لیے |
A compound written without its space, repaired. WER counts whitespace tokens, so this alone costs two errors. |
بھائی |
بھائی |
Unchanged. That letter marks aspiration and is phonemic. Merging it into its lookalike reports a lower WER by destroying the distinction. |
The last row matters most: phonemic characters are a contract, and the package refuses to import if a rule table would merge one.
Those are general Urdu NLP toolkits: tokenizers, POS tagging, NER. This is an evaluation protocol, normalization plus scoring, nothing else. Different jobs.
Differences are measured, not asserted. This table is generated by
compare_normalizers.py --emit-table
running each package at a pinned version:
| what it is | input | urdunorm |
urduhack |
LughaatNLP |
|---|---|---|---|---|
| Arabic-keyboard yeh and kaf | علي كي دكان |
علی کی دکان |
علی کی دکان |
علي كي دكان |
| presentation forms | ﻤﻴﮟ |
میں |
مےں |
ﻤﻴﮟ |
| vowel diacritics | مُحَمَّد |
محمد |
محمّد |
محمد |
| Urdu-Indic digits | ۲۰۲۶ |
2026 |
۲۰۲۶ |
۲۰۲۶ |
| joined compound | کیلئے |
کے لیے |
کیلئے |
کیلئے |
| aspiration, must not change | بھائی |
بھائی |
بھائی |
بھائی |
| alef madda, must not change | آج |
آج |
آج |
اج |
Measured on urdunorm 0.1.0, urduhack 1.1.1, LughaatNLP 1.3.1.
Both also carry heavy dependencies: urduhack imports TensorFlow at package
import; LughaatNLP requires torch, transformers, scikit-learn and scipy.
| Mode | Characters | Diacritics | Punctuation | Digits | Space repair | ZWNJ |
|---|---|---|---|---|---|---|
eval |
unified | stripped | dropped | unified | on | to space |
display |
unified | kept | kept | kept | off | kept |
lums-compat |
unified | stripped | 5 chars only | unified | off | to space |
Use eval unless you know why not. display is for showing transcripts to
people. lums-compat reproduces
WER We Stand (COLING 2025), the
only published Urdu ASR benchmark, so numbers can be compared against it. Its
five-character punctuation class keeps the Urdu full stop: that is theirs, not a
bug here. Report under two protocols; a single-protocol Urdu WER compares to
nothing.
from urdunorm import wer, cer, score, normalize, EVAL
normalize("کیلئے", mode="eval")
wer(references, hypotheses, mode="eval")
cer(references, hypotheses, mode="eval")
result = score(references, hypotheses, mode="eval")
str(result) # 'WER 12.40% [eval]'
EVAL.with_(repair_spaces=False) # an ablation is a field overrideBoth sides are normalized under the named mode and the result records which protocol produced it. Tokenization is a whitespace split. The corpus rate is total errors over total reference tokens, never the mean of per-utterance rates, which would weight a three-word clip like a thirty-word one.
Why this exists · Measured comparison · Where the rules come from · Review the Urdu rules (no Python needed) · Contributing
The space-repair lexicon and the number tables have not been signed off by a
native speaker, so numbers_to_words ships off in every mode.
@software{urdunorm,
title = {urdunorm: Urdu text normalization for reproducible ASR evaluation},
author = {Hassan Bhatti, Hunzalah},
year = {2026},
doi = {10.5281/zenodo.21864104},
url = {https://github.com/hunzed/urdunorm}
}Apache-2.0.