I built an Indic transliteration library that can recover the exact original Unicode source
I've been working on lipimala, an open-source Indic transliteration library for:
- IAST / extended Indic Latin
- Devanagari
- Gujarati
- Plain English / Hunterian
with native implementations for:
- Dart
- JavaScript / Node.js
- Python
- PHP
Repository: https://github.com/jayeshmepani/indic-script-converter
The problem I wanted to solve
The interesting part is not simply converting:
Kṛṣṇa → कृष्ण
There are already excellent projects for Indic transliteration. The problem I wanted to solve was different:
What happens when I need to convert the text back and recover exactly what the user originally supplied?
Canonical reverse is not exact reverse
Consider these three different source strings:
Kṛṣṇa
kṛṣṇa
Kr̥ṣṇa
Different source strings can legitimately produce the same visible Devanagari representation:
कृष्ण
Once that happens, the visible target string alone cannot tell us:
- the original casing
- whether the source was NFC or NFD
- whether a precomposed or decomposed character was used
- which accepted alias was entered
- the original combining-mark ordering
- exact whitespace or punctuation
A traditional reverse transliteration can return a canonical Latin representation. But canonical source is not necessarily exact original source. That distinction became the central design principle behind lipimala.
Two ways to preserve the original
lipimala provides two exact-source mechanisms.
1. TransliterationResult
Instead of treating transliteration only as string → string, the higher-level API returns a structured result containing concepts such as:
originalnormalizedInputrenderedprofileinputNormalizationoutputNormalizationrenderingIsInjectiveissuesoriginalCodePoints
The original can then be recovered independently of how lossy the visible representation is.
Conceptually:
source
│
├── original provenance
│
└── rendered representation
This is useful for editors, databases, APIs, archival systems, and other applications where the rendered text and original source need to coexist.
2. Invisible exact-source metadata
Sometimes carrying a separate object isn't convenient.
lipimala can therefore optionally attach checksummed exact-source metadata using invisible Unicode Tag characters.
Conceptually:
Kṛṣṇa
↓
कृष्ण + invisible source metadata
↓
Kṛṣṇa
The displayed Brahmic text remains normal while the original source can travel with the string.
This is intended for Unicode-preserving environments. For durable interchange, the structured result envelope remains the safer option because some systems may strip unusual Unicode characters.
Canonical, smart and exact reverse APIs
Another thing I wanted to make explicit was what "reverse" actually means.
lipimala distinguishes three modes:
Canonical
visible Devanagari
↓
canonical IAST
Smart
exact metadata exists?
│
├─ yes → restore exact source
└─ no → canonical reverse
Exact
exact metadata required
↓
restore original or fail
This avoids pretending that every visible transliteration mapping is mathematically one-to-one.
Direct Devanagari ↔ Gujarati
The library also contains a direct Devanagari/Gujarati converter.
It doesn't need to bounce through IAST:
Devanagari ↔ Gujarati
The converter handles:
- nukta combinations
- Unicode normalization
- digits
- unknown-character policy
- whitespace policy
- Vedic marks
- optional source provenance
Where the two Unicode repertoires collapse distinctions, canonical conversion remains canonical and exact provenance is handled separately.
Vedic text
Vedic accents were another area where I didn't want them to be an afterthought.
The converter preserves supported encoded Vedic marks and handles their placement/storage ordering with the surrounding vowel signs, bindu/visarga, and svara marks.
There are dedicated Vedic fixtures in the shared verification suite.
Four native implementations
lipimala isn't a JavaScript library with thin wrappers around it.
The repository contains native implementations for:
- Dart
- JavaScript
- Python
- PHP
The goal is that all four implement the same transliteration contract.
The current shared verification material contains:
- 497 transliteration cases
- 22 Vedic fixtures
and the runtimes are tested against shared expected behavior/golden outputs.
That matters for applications such as:
Flutter client
↓
Node service
↓
Python processing
↓
PHP/Laravel backend
where different parts of the same system should not transliterate the same source differently.
Installation
JavaScript
npm install lipimala
Python
pip install lipimala
Dart
dart pub add lipimala
PHP
composer require jayeshmepani/lipimala
A deliberately narrow scope
lipimala is not trying to compete with projects such as Aksharamukha or Sanscript on the number of scripts supported. Those projects are much broader.
The goal here is different: deterministic, Unicode-aware, source-preserving transliteration for a focused script set.
Today that focus is mainly:
- IAST / extended Indic
- Devanagari
- Gujarati
- Plain English / Hunterian
If your application needs 100+ scripts, another converter is probably a better choice.
If it needs to know exactly what source produced the rendered representation, that's the problem lipimala is designed around.
Links
- GitHub: https://github.com/jayeshmepani/indic-script-converter
- npm: https://www.npmjs.com/package/lipimala
- PyPI: https://pypi.org/project/lipimala/
- pub.dev: https://pub.dev/packages/lipimala/
- Packagist: https://packagist.org/packages/jayeshmepani/lipimala
I'd especially appreciate feedback from people working with Unicode, Sanskrit/Vedic texts, archival data, transliteration systems, or multi-runtime applications.
Top comments (0)