---
title: Converting romanized Nepali to Devanagari with a small model
date: 2026-10-31
---

# Converting romanized Nepali to Devanagari with a small model

Nepali is often typed with English letters: *namaste*, *kasto cha*, *dherai ramro*. This is romanized Nepali.

One word can be typed several ways. Devanagari search tools do not match these spellings.

I built a model that converts one romanized word into Devanagari. I trained it from scratch.

## The paper I compare to

[NepaliXlit](https://aclanthology.org/2026.chipsal-1.12/) is a published model for this task. It has **11.5 million parameters**. It starts from IndicXlit, a multilingual model for Indian languages. The authors fine-tuned it on 3,679 romanized-Devanagari word pairs from Facebook and Reddit comments. They used 80% of the pairs for training and 20% for testing.

The paper reports about 8% higher accuracy and 11% lower character error rate (CER) than IndicXlit. Those numbers come from the paper's own 736-pair test set. I do not have that test set, so I cannot confirm the paper's numbers.

Architecture of NepaliXlit (the published model, based on IndicXlit):

![Architecture of NepaliXlit: 6 encoder and 6 decoder layers](https://huggingface.co/Aananda-giri/romanized-nepali-devanagari/resolve/main/figures/architecture_nepalixlit.svg)

Starting point: IndicXlit, a multilingual model for Indian languages.
NepaliXlit is that model, fine-tuned on 3,679 romanized-Devanagari word pairs.

## Why it is hard

- There is no standard romanized spelling.
- Vowel length is not written. *Ban* can mean two different words.
- Some letters share sounds. *Cha*, *chha*, and *x* are used interchangeably.
- Some words have more than one correct Devanagari form.

Some errors cannot be avoided. I compare our model to NepaliXlit, not to 100%.

## Our model

Our model starts from random weights and learns them from our data, not from a pretrained model.

- Type: encoder-decoder Transformer.
- Layers: 3 encoder layers and 3 decoder layers.
- Width: 256 values per character. 4 attention heads.
- Feed-forward width: 512.
- Input: characters, with 30 symbols (26 letters plus 4 special tokens).
- Output: characters, with 74 symbols (70 Devanagari characters plus 4 special tokens).
- Maximum length: 48 characters.
- Size: **4.0 million parameters**.

Architecture of our model:

![Architecture of our model: 3 encoder and 3 decoder layers](https://huggingface.co/Aananda-giri/romanized-nepali-devanagari/resolve/main/figures/architecture_ours.svg)

Training happened in two stages:

- Stage 1: 1,917,918 word pairs, from the Aksharantar Nepali corpus and other public word lists.
- Stage 2: 34,173 pairs of informal spellings, for 30 epochs.

The model returns 5 candidates and picks the best one.

Weights, vocabularies, and code: https://huggingface.co/Aananda-giri/romanized-nepali-devanagari

## Results

Exact match: share of words fully correct. Higher is better.
CER: share of characters wrong. Lower is better.

| Test set | Our model (**4.0 M** parameters) | NepaliXlit, our copy (**11.5 M** parameters) |
|---|---|---|
| NepaliFlow (4,033 words) | **66.08%** exact, **6.12%** CER | 65.34% exact, 6.30% CER |
| Aksharantar (4,101 words) | 58.47% exact, 10.73% CER | **63.98%** exact, **8.39%** CER |

- On NepaliFlow, our model is slightly better.
- On Aksharantar, NepaliXlit is better. Aksharantar uses formal spellings. Our stage 2 used informal ones.
- Our NepaliXlit numbers come from our own copy of the published model, not the authors' code.

## Try it

Install the libraries, then run this:

```bash
pip install torch huggingface_hub
```

```python
from huggingface_hub import snapshot_download
import sys

path = snapshot_download("Aananda-giri/romanized-nepali-devanagari")
sys.path.insert(0, path)
from romanized_nepali import Transliterator

t = Transliterator.from_pretrained(path)
words = ["namaste", "kasto", "cha", "dherai", "ramro"]
for w, out in zip(words, t.translate(words)):
    print(w, "->", out)
```

## Limits

- Single words only.
- Unusual spellings can fail.
- Only characters seen in training can appear in output.

## Next

Train on whole sentences, so the model can use the words around each word.
