jayeshmepani / lipimala
Exact round-trip IAST and extended Indic transliteration for Devanagari, Gujarati, plain English, canonical reverse conversion, and direct Gujarati/Devanagari conversion.
Requires
- php: >=8.3
Requires (Dev)
- laravel/pint: ^1.29
- rector/rector: ^2.4
README
A PHP 8.3+ port of the supplied Dart/Python Indic-script conversion libraries. It preserves the same conversion tables, profile defaults, contextual rules, Vedic handling, canonical reverse conversion, exact round-trip result envelope, and checksummed exact-source metadata format. The direct Gujarati ↔ Devanagari converter is included as well.
Runtime
- PHP 8.3 or newer
declare(strict_types=1)throughout- no runtime package dependencies
- no
intl/mbstringrequirement - bundled Unicode 17.0.0 canonical normalization and mark data
The source project uses extendedIndic as the default forward profile, and this PHP port keeps that default.
Included conversions
- Latin/IAST + extended Indic → Devanagari
- Latin/IAST + extended Indic → Gujarati
- Latin/IAST → plain English
- explicit Hunterian view
- Devanagari → canonical IAST
- Gujarati → canonical IAST
- Devanagari → Gujarati
- Gujarati → Devanagari
- metadata-backed exact source recovery
- exact round-trip JSON envelope serialization
Installation
Via Composer (recommended)
composer require jayeshmepani/lipimala
Manual (no Composer)
require '/path/to/lipimala-php/autoload.php';
Requirements
- PHP 8.3 or newer
- No runtime dependencies — no
intl, nombstring
Basic API
<?php declare(strict_types=1); require __DIR__ . '/autoload.php'; use function Lipimala\toCanonicalIastFromDevanagari; use function Lipimala\toCanonicalIastFromGujarati; use function Lipimala\toDevanagariFromIast; use function Lipimala\toGujaratiFromIast; use function Lipimala\toPlainEnglishFromIast; echo toDevanagariFromIast('Kṛṣṇa'); // कृष्ण echo toGujaratiFromIast('Kṛṣṇa'); // કૃષ્ણ echo toPlainEnglishFromIast('Kṛṣṇa'); // Krishna echo toCanonicalIastFromDevanagari('कृष्ण'); // kṛṣṇa echo toCanonicalIastFromGujarati('કૃષ્ણ'); // kṛṣṇa
Forward profiles
The default is extendedIndic:
use Lipimala\DevanagariRomanizationProfile; use Lipimala\IastToDevanagariOptions; $options = new IastToDevanagariOptions( profile: DevanagariRomanizationProfile::EXTENDED_INDIC, );
The forward script APIs expose the same policy families as the source implementation:
strictIastiso15919CoreextendedIndic- unknown Latin handling
- digit conversion/preservation
- punctuation preservation/Indic danda
- OM letter/sign policy
- ambiguous
ḷhandling - ASCII long-vowel aliases
sh,x,wcompatibility switches- Vedic accent preservation
- whitespace collapse
- exact-source metadata embedding
Exact Latin-key recovery
Visible Brahmic output is necessarily many-to-one. Case, source normalization, aliases, and equivalent extended spellings can collapse to the same visible script. Exact recovery therefore uses the same invisible Unicode-Tag trailer as the Dart/Python/JavaScript implementations.
use Lipimala\IastToDevanagariOptions; use function Lipimala\toDevanagariFromIast; use function Lipimala\toExactIastFromDevanagari; $source = 'Kṛṣṇa / Kr̥ṣṇa / ḫāna / ṣ́akti'; $tagged = toDevanagariFromIast( $source, new IastToDevanagariOptions( embedExactSourceMetadata: true, ), ); assert(toExactIastFromDevanagari($tagged) === $source);
Gujarati uses IastToGujaratiOptions and toExactIastFromGujarati().
The metadata encodes the exact UTF-16LE source unit sequence, stores independent FNV-1a checksums for source and visible rendering, and rejects corrupted/tampered trailers. The PHP implementation is byte-for-byte compatible with the supplied cross-language metadata vector, including supplementary code points and isolated UTF-16 surrogate units.
Canonical reverse vs exact reverse
toIastFromDevanagari() exact metadata when present, canonical otherwise
toExactIastFromDevanagari() metadata required
toCanonicalIastFromDevanagari() always canonical visible reverse
toIastFromGujarati() exact metadata when present, canonical otherwise
toExactIastFromGujarati() metadata required
toCanonicalIastFromGujarati() always canonical visible reverse
Canonical reverse cannot infer which Latin alias/case/normalization originally produced a visible script string.
Direct Gujarati ↔ Devanagari
Canonical visible conversion:
use function Lipimala\toCanonicalDevanagariFromGujarati; use function Lipimala\toCanonicalGujaratiFromDevanagari; echo toCanonicalGujaratiFromDevanagari('कृष्ण'); // કૃષ્ણ echo toCanonicalDevanagariFromGujarati('કૃષ્ણ'); // कृष्ण
The two scripts have unequal Unicode repertoires, so visible conversion is non-injective. Exact script-source recovery uses a typed source marker inside the same metadata trailer:
use Lipimala\IndicScriptConversionOptions; use function Lipimala\toCanonicalGujaratiFromDevanagari; use function Lipimala\toExactDevanagariFromGujarati; $source = 'ऄ ऎ ऍ ॲ ऒ ऑ ॵ ळ ऴ ग़ ॻ ड़ ॸ ॾ'; $taggedGujarati = toCanonicalGujaratiFromDevanagari( $source, new IndicScriptConversionOptions( embedExactSourceMetadata: true, ), ); assert(toExactDevanagariFromGujarati($taggedGujarati) === $source);
The opposite direction uses toCanonicalDevanagariFromGujarati() and toExactGujaratiFromDevanagari().
Direct conversion also exposes source-digit preservation, target-digit conversion, unknown-character preservation/strict error, normalization, whitespace collapse, and exact round-trip-envelope APIs.
Vedic ordering
The forward implementation keeps the source library's Unicode-recommended Brahmic storage order: vowel/matra, then bindu/visarga, then svara. Forms such as वसोः॑ and जुष्टं॑ therefore remain unchanged from the verified Dart behavior.
Exact Round-Trip envelope
use function Lipimala\toDevanagari; $result = toDevanagari('Kṛṣṇa'); $json = $result->toJsonText(); $original = $result->restoreOriginal();
The envelope schema is exact round-trip-indic-transliteration/1 and includes original code-point integrity checking.
CLI output generators
Run from the PHP project root:
php tools/latn_iast_transliteration_verification/latn_iast_to_deva_test.php > latn_iast_to_deva_output.txt php tools/latn_iast_transliteration_verification/latn_iast_to_gujr_test.php > latn_iast_to_gujr_output.txt php tools/latn_iast_transliteration_verification/latn_iast_transcription_test.php > latn_iast_transcription_output.txt php tools/latn_iast_transliteration_verification/deva_to_latn_iast_test.php > deva_to_latn_iast_output.txt php tools/latn_iast_transliteration_verification/gujr_to_latn_iast_test.php > gujr_to_latn_iast_output.txt php tools/latn_iast_transliteration_verification/deva_to_gujr_test.php > deva_to_gujr_output.txt php tools/latn_iast_transliteration_verification/gujr_to_deva_test.php > gujr_to_deva_output.txt
All runners JSON-escape each source/result string so quotes and embedded newlines cannot corrupt record boundaries.
Run every output generator:
php tools/latn_iast_transliteration_verification/run_all.php
Verification
php tests/run.php
The bundled regression suite verifies the five original 497-case directions, both 497-case direct script directions, all 22 Vedic fixtures, profile/option behavior, exact metadata, tamper rejection, exact round-trip JSON envelopes, and exact direct-script corpus recovery.
See PORT_VERIFICATION.md for the executed result and parity counts.