Khmer text normalization: turn written text into spoken words for TTS, and spoken words back into written text for ASR.
Paper: arXiv:2609.30984
pip install thaTha needs pynini. On macOS, install it from conda-forge first:
conda install -c conda-forge pyniniimport tha
tha.normalize_text("αα·ααα
02/01/2024 ααααα $1.05")
# αα·ααα
ααααααΈβααΈαβααβααααΆβααααΆαβααΈαααΆααβαααααα½α ααααα αα½αβαα»ααααΆαβααααΆαβααα| Input | Output |
|---|---|
1,234 |
αα½αααΆααβααΈαααβααΆααα·ααα½α |
12.5% |
αααααΈαβα
α»α
βααααΆαβααΆααα |
5km |
ααααΆαβααΈα‘αΌαααααα |
10:23 |
ααααβαααβααααααΈβααΆααΈ |
012 345 678 |
ααΌαααβαααααΈαβααΆααα·ααα½αβα αΆαα·αααααΆααα½αβα
α·ααα·αααααΆαααΈ |
-5 |
ααβααααΆα |
It also handles ordinals, fractions, phone numbers, e-mails, license plates, ranges, scores and more. Words of one number are joined with β, which you can change:
normalizer = tha.Normalizer(separator=" ") # reuse it, loading takes ~2s
normalizer.normalize("$1.05") # αα½α αα»ααααΆα ααααΆα αααimport tha
tha.inverse_normalize_text("αα·ααα
ααααααΈααΈαααααααΆααααΆαααΈαααΆαααααααα½αααααααα½ααα»ααααΆαααααΆαααα")
# αα·ααα
02/01/2024ααααα$1.05| Input | Output |
|---|---|
αααααΆαα |
10,000 |
ααααααααΆαααΈααααα |
8:30 |
αααααα’ααααΆαα |
-10Β°C |
ααααααααα |
20 - 10 |
αα½αα
ααα½α |
αα½αα
ααα½α |
ααΈααα |
ααΈααα |
A single number word on its own stays a word, and number words inside other words are left alone. For Khmer digits, use tha.InverseNormalizer(khmer_digits=True).
import tha.normalize
import tha.hashtags
import tha.parenthesis
tha.normalize.processor("αα·αβα²αα") # αα·αα±αα
tha.hashtags.processor("Hello world #αα»α hello") # Hello world hello
tha.parenthesis.processor("Hello (ignored) world") # Hello worlduv sync
make testOn macOS, pynini is built from source, so install OpenFst first:
brew install openfst
CPPFLAGS="-I$(brew --prefix)/include" LDFLAGS="-L$(brew --prefix)/lib" uv sync@misc{yath2026tha,
author = {Yath, Seanghay},
title = {{THA}: Weighted Finite-State Text Normalization and Inverse Text Normalization for Khmer},
year = {2026},
eprint = {2609.30984},
archivePrefix = {arXiv},
primaryClass = {cs.CL},
doi = {10.48550/arXiv.2609.30984},
url = {https://arxiv.org/abs/2609.30984}
}