marathi-transliterate 1.1.1 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,6 +14,8 @@ Most existing Sanskrit/IAST transliteration libraries convert `sakhar` to **स
14
14
 
15
15
  In everyday reality, people in Maharashtra type phonetically:
16
16
  - `sakhar` → **साखर**
17
+ - `jaad` → **जाड**
18
+ - `daba` → **डबा**
17
19
  - `kanda` → **कांदा**
18
20
  - `batata` → **बटाटा**
19
21
  - `mith` → **मीठ**
@@ -53,6 +55,9 @@ import { transliterate, transliterateWord } from 'marathi-transliterate';
53
55
 
54
56
  // Single word transliteration
55
57
  transliterateWord('sakhar'); // → 'साखर'
58
+ transliterateWord('jaad'); // → 'जाड'
59
+ transliterateWord('Daba'); // → 'डबा'
60
+ transliterateWord('D'); // → 'ड' (vs 'd' → 'द')
56
61
  transliterateWord('kanda'); // → 'कांदा'
57
62
  transliterateWord('batata'); // → 'बटाटा'
58
63
  transliterateWord('namaskar'); // → 'नमस्कार'
@@ -146,6 +151,29 @@ Converts full text, sentences, or paragraphs into Marathi while preserving numbe
146
151
  ### `getSuggestions(word: string): string[]`
147
152
  Returns candidate phonetic variations for a given word.
148
153
 
154
+ ### `extendDictionary(customWords: Record<string, string>): void`
155
+ Extends or overrides the global transliteration dictionary with your own custom vocabulary at runtime.
156
+
157
+ ```javascript
158
+ import { extendDictionary, transliterateWord } from "marathi-transliterate";
159
+
160
+ extendDictionary({
161
+ "sangamner": "संगमनेर",
162
+ "myitem": "माझामाल"
163
+ });
164
+
165
+ transliterateWord("sangamner"); // → "संगमनेर"
166
+ ```
167
+
168
+ ### `normalizeMarathi(text: string): string`
169
+ Normalizes Marathi characters (interchangeable ळ/ल, श/ष, ि/ी, ु/ू, and anusvara) for tolerant comparisons and search filtering.
170
+
171
+ ```javascript
172
+ import { normalizeMarathi } from "marathi-transliterate";
173
+
174
+ normalizeMarathi("भुसाळ") === normalizeMarathi("भुसाल"); // → true
175
+ ```
176
+
149
177
  ### `attachDevanagari(element, options): Handler`
150
178
  Attaches keyboard listeners to an `<input>` or `<textarea>` element.
151
179
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "marathi-transliterate",
3
- "version": "1.1.1",
3
+ "version": "1.3.0",
4
4
  "description": "Zero-dependency, offline-first Marathi / Devanagari phonetic transliteration library. Converts English typing to Marathi (e.g. sakhar -> साखर) seamlessly in plain JS, Node.js, and frontend frameworks.",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
package/src/dictionary.js CHANGED
@@ -380,5 +380,384 @@ export const MARATHI_DICTIONARY = {
380
380
  'te': 'ते',
381
381
  'ha': 'हा',
382
382
  'hi': 'ही'
383
+ ,
384
+
385
+ // --- Extended Surnames, Names & Kirana Vocabulary ---
386
+ 'badama': 'बदाम',
387
+ 'pistachio': 'पिस्ता',
388
+ 'raisins': 'मनुका',
389
+ 'akrod': 'अक्रोड',
390
+ 'dal': 'डाळ',
391
+ 'daal': 'डाळ',
392
+ 'bhel': 'भेळ',
393
+ 'keli': 'केळी',
394
+ 'kele': 'केळी',
395
+ 'haladi': 'हळद',
396
+ 'shevaya': 'शेवया',
397
+ 'naral': 'नारळ',
398
+ 'nariyal': 'नारळ',
399
+ 'khul': 'खूळ',
400
+ 'khula': 'खुळा',
401
+ 'khuli': 'खुळी',
402
+ 'goli': 'गोळी',
403
+ 'golya': 'गोळ्या',
404
+ 'dole': 'डोळे',
405
+ 'dola': 'डोळा',
406
+ 'khel': 'खेळ',
407
+ 'vel': 'वेळ',
408
+ 'chul': 'चूल',
409
+ 'pola': 'पोळा',
410
+ 'shengdana': 'शेंगदाणा',
411
+ 'shengdaane': 'शेंगदाणे',
412
+ 'dana': 'दाणा',
413
+ 'peanut': 'शेंगदाणे',
414
+ 'groundnut': 'शेंगदाणे',
415
+ 'cumin': 'जिरे',
416
+ 'chilli': 'मिरची',
417
+ 'matar': 'मटार',
418
+ 'khule': 'खुळे',
419
+ 'khulee': 'खुळे',
420
+ 'khulay': 'खुळे',
421
+ 'mule': 'मुळे',
422
+ 'muley': 'मुळे',
423
+ 'moole': 'मुळे',
424
+ 'kamble': 'कांबळे',
425
+ 'kambale': 'कांबळे',
426
+ 'kamle': 'कांबळे',
427
+ 'dhole': 'ढोळे',
428
+ 'dholay': 'ढोळे',
429
+ 'gole': 'गोळे',
430
+ 'golay': 'गोळे',
431
+ 'shelke': 'शेळके',
432
+ 'shelake': 'शेळके',
433
+ 'walunj': 'वाळुंज',
434
+ 'walunje': 'वाळुंजे',
435
+ 'valunj': 'वाळुंज',
436
+ 'valunje': 'वाळुंजे',
437
+ 'ingale': 'इंगळे',
438
+ 'ingle': 'इंगळे',
439
+ 'ingole': 'इंगोले',
440
+ 'salve': 'साळवे',
441
+ 'saalve': 'साळवे',
442
+ 'salunke': 'साळुंखे',
443
+ 'jawale': 'जावळे',
444
+ 'zavale': 'जावळे',
445
+ 'bawale': 'बावळे',
446
+ 'bavale': 'बावळे',
447
+ 'phalke': 'फाळके',
448
+ 'falke': 'फाळके',
449
+ 'chalke': 'चाळके',
450
+ 'pimple': 'पिंपळे',
451
+ 'pimpal': 'पिंपळे',
452
+ 'tambole': 'तांबोळे',
453
+ 'mahale': 'महाळे',
454
+ 'bhale': 'भाळे',
455
+ 'bhalerao': 'भालेराव',
456
+ 'borule': 'बोरुळे',
457
+ 'gholap': 'घोळप',
458
+ 'pol': 'पोळ',
459
+ 'bal': 'बाळ',
460
+ 'baal': 'बाळ',
461
+ 'dalvi': 'दळवी',
462
+ 'dalavi': 'दळवी',
463
+ 'palve': 'पाळवे',
464
+ 'palande': 'पाळंदे',
465
+ 'zole': 'झोळे',
466
+ 'jole': 'जोळे',
467
+ 'wable': 'वाबळे',
468
+ 'wabale': 'वाबळे',
469
+ 'bhole': 'भोळे',
470
+ 'ghule': 'घुळे',
471
+ 'dhule': 'धुळे',
472
+ 'khole': 'खोळे',
473
+ 'thole': 'ठोळे',
474
+ 'chitale': 'चितळे',
475
+ 'chitapale': 'चितपळे',
476
+ 'daphal': 'डफळ',
477
+ 'raul': 'राऊळ',
478
+ 'rawool': 'राऊळ',
479
+ 'ravul': 'राऊळ',
480
+ 'kolhe': 'कोल्हे',
481
+ 'kolhewadi': 'कोल्हेवाडी',
482
+ 'sawale': 'सावळे',
483
+ 'kawale': 'कावळे',
484
+ 'gavale': 'गवळे',
485
+ 'gawale': 'गवळे',
486
+ 'navale': 'नवळे',
487
+ 'nawale': 'नवळे',
488
+ 'dewale': 'देवळे',
489
+ 'yewale': 'येवळे',
490
+ 'yeole': 'येवले',
491
+ 'dahale': 'डहाळे',
492
+ 'bhabale': 'भाबळे',
493
+ 'tapale': 'तापळे',
494
+ 'kulat': 'कुळट',
495
+ 'landge': 'लांडगे',
496
+ 'kaale': 'काळे',
497
+ 'maali': 'माळी',
498
+ 'bhosle': 'भोसले',
499
+ 'shirke': 'शिर्के',
500
+ 'shewale': 'शेवाळे',
501
+ 'shiwale': 'शिवाळे',
502
+ 'bhilare': 'भिळारे',
503
+ 'padwal': 'पडवळ',
504
+ 'bhadwalkar': 'भाडवळकर',
505
+ 'bendale': 'बेंदळे',
506
+ 'ghodke': 'घोडके',
507
+ 'khadke': 'खडके',
508
+ 'phadke': 'फडके',
509
+ 'kakade': 'काकडे',
510
+ 'pawale': 'पावळे',
511
+ 'sonawale': 'सोनावळे',
512
+ 'sonawane': 'सोनवणे',
513
+ 'borhade': 'बोऱ्हाडे',
514
+ 'katore': 'कटोरे',
515
+ 'shete': 'शेटे',
516
+ 'bhusal': 'भुसाळ',
517
+ 'bhusaal': 'भुसाळ',
518
+ 'bhusale': 'भुसाळे',
519
+ 'bhuswal': 'भुसावळ',
520
+ 'bhusawal': 'भुसावळ',
521
+ 'ghusal': 'घुसाळ',
522
+ 'kusal': 'कुसळ',
523
+ 'sable': 'साबळे',
524
+ 'sabale': 'साबळे',
525
+ 'taral': 'तराळ',
526
+ 'taraal': 'ताराळ',
527
+ 'pusal': 'पुसळ',
528
+ 'chitole': 'चितोळे',
529
+ 'shitole': 'शितोळे',
530
+ 'bapule': 'बापुळे',
531
+ 'kharpude': 'खरपुडे',
532
+ 'wadkar': 'वाडकर',
533
+ 'kote': 'कोते',
534
+ 'balu': 'बाळू',
535
+ 'baalu': 'बाळू',
536
+ 'bala': 'बाळा',
537
+ 'balasaheb': 'बाळासाहेब',
538
+ 'balaji': 'बाळाजी',
539
+ 'baliram': 'बळीराम',
540
+ 'kalyani': 'कल्याणी',
541
+ 'sheela': 'शीला',
542
+ 'sheetal': 'शीतल',
543
+ 'snehal': 'स्नेहल',
544
+ 'pranjal': 'प्रांजल',
545
+ 'atul': 'अतुल',
546
+ 'komal': 'कोमल',
547
+ 'mangal': 'मंगल',
548
+ 'ujwala': 'उज्ज्वला',
549
+ 'ujjwala': 'उज्ज्वला',
550
+ 'payal': 'पायल',
551
+ 'kajal': 'काजल',
552
+ 'sejal': 'सेजल',
553
+ 'nilesh': 'निलेश',
554
+ 'shailesh': 'शैलेश',
555
+ 'kamlesh': 'कमलेश',
556
+
557
+ // --- Grocery & Household with ड / ढ / ट / ठ / द / ध ---
558
+ "jaad": "जाड",
559
+ "jad": "जाड",
560
+ "jada": "जाडा",
561
+ "jaada": "जाडा",
562
+ "jadi": "जाडी",
563
+ "jaadi": "जाडी",
564
+ "barik": "बारीक",
565
+ "bareek": "बारीक",
566
+ "baareek": "बारीक",
567
+ "jadpoha": "जाड पोहे",
568
+ "jaadpoha": "जाड पोहे",
569
+ "jaadpohe": "जाड पोहे",
570
+ "jadrava": "जाड रवा",
571
+ "jaadrava": "जाड रवा",
572
+ "barikpoha": "बारीक पोहे",
573
+ "barikpohe": "बारीक पोहे",
574
+ "barikrava": "बारीक रवा",
575
+ "daba": "डबा",
576
+ "dabba": "डबा",
577
+ "dabi": "डबी",
578
+ "dabbi": "डबी",
579
+ "papad": "पापड",
580
+ "paapad": "पापड",
581
+ "kadak": "कडक",
582
+ "laddu": "लाडू",
583
+ "ladoo": "लाडू",
584
+ "laadu": "लाडू",
585
+ "peda": "पेढा",
586
+ "pedha": "पेढा",
587
+ "pendha": "पेंढा",
588
+ "bhendi": "भेंडी",
589
+ "bhindi": "भेंडी",
590
+ "bhende": "भेंडे",
591
+ "thik": "ठीक",
592
+ "theek": "ठीक",
593
+ "dhag": "ढग",
594
+ "dhol": "ढोल",
595
+ "dholak": "ढोलक",
596
+ "dholki": "ढोलकी",
597
+ "khadda": "खड्डा",
598
+ "khada": "खड्डा",
599
+ "tambda": "तांबडा",
600
+ "tambadi": "तांबडी",
601
+ "pandhra": "पांढरा",
602
+ "pandhari": "पांढरी",
603
+ "korda": "कोरडा",
604
+ "kordi": "कोरडी",
605
+ "thanda": "थंड",
606
+ "thand": "थंड",
607
+ "god": "गोड",
608
+ "godd": "गोड",
609
+ "dodka": "दोडका",
610
+ "dodke": "दोडके",
611
+ "karela": "कारले",
612
+ "karle": "कारले",
613
+ "bhopla": "भोपळा",
614
+ "ghevda": "घेवडा",
615
+ "ghevada": "घेवडा",
616
+ "danda": "दांडा",
617
+ "dandi": "दांडी",
618
+ "dada": "दादा",
619
+ "dadi": "दादी",
620
+ "undi": "उंडी",
621
+ "and": "अंडे",
622
+ "ande": "अंडे",
623
+ "anda": "अंडे",
624
+ "eggs": "अंडी",
625
+ "egg": "अंडे",
626
+ "powder": "पावडर",
627
+ "pavdar": "पावडर",
628
+ "detergent": "डिटर्जंट",
629
+ "doctor": "डॉक्टर",
630
+ "order": "ऑर्डर",
631
+ "board": "बोर्ड",
632
+ "card": "कार्ड",
633
+ "credit": "क्रेडिट",
634
+ "debit": "डेबिट",
635
+ "disco": "डिस्को",
636
+ "diesel": "डिझेल",
637
+ "petrol": "पेट्रोल",
638
+ "speed": "स्पीड",
639
+ "driver": "ड्रायव्हर",
640
+ "drive": "ड्राइव्ह",
641
+
642
+ // --- Enhanced Marathi Daily Essentials & Corner Cases ---
643
+ "ratri": "रात्री",
644
+ "raatri": "रात्री",
645
+ "vikri": "विक्री",
646
+ "kharedi": "खरेदी",
647
+ "supari": "सुपारी",
648
+ "chutney": "चटणी",
649
+ "chatani": "चटणी",
650
+ "limbu": "लिंबू",
651
+ "nimbu": "लिंबू",
652
+ "bhakri": "भाकरी",
653
+ "poli": "पोळी",
654
+ "chikki": "चिक्की",
655
+ "farsan": "फरसाण",
656
+ "chivda": "चिवडा",
657
+ "bhadang": "भडंग",
658
+ "shev": "शेव",
659
+ "bakarwadi": "बाकरवडी",
660
+ "shrikhand": "श्रीखंड",
661
+ "amrakhand": "आम्रखंड",
662
+ "basundi": "बासुंदी",
663
+ "kurdaya": "कुरडया",
664
+ "papadya": "पापड्या",
665
+ "khari": "खारी",
666
+ "toast": "टोस्ट",
667
+ "pav": "पाव",
668
+ "bread": "ब्रेड",
669
+ "butter": "बटर",
670
+ "loni": "लोणी",
671
+ "sauce": "सॉस",
672
+ "jam": "जॅम",
673
+ "noodle": "नुडल्स",
674
+ "noodles": "नुडल्स",
675
+ "pasta": "पास्ता",
676
+ "macaroni": "मॅकरोनी",
677
+ "cornflakes": "कॉर्नफ्लेक्स",
678
+ "oats": "ओट्स",
679
+ "sendha": "सेंधा",
680
+ "sendhav": "सेंधव",
681
+ "rocksalt": "सेंधव मीठ",
682
+ "sabudana": "साबुदाणा",
683
+ "bhagar": "भगर",
684
+ "rajgira": "राजगिरा",
685
+ "singhada": "शिंगाडा",
686
+ "makhana": "मखाना",
687
+ "dryfruit": "ड्रायफ्रूट",
688
+ "dryfruits": "ड्रायफ्रूट्स",
689
+ "custard": "कस्टर्ड",
690
+ "bakingpowder": "बेकिंग पावडर",
691
+ "bakingsoda": "बेकिंग सोडा",
692
+ "soda": "सोडा",
693
+ "eno": "इनो",
694
+ "matchbox": "काडीपेटी",
695
+ "diya": "दिवा",
696
+ "vaat": "वात",
697
+ "kapus": "कापूस",
698
+ "gangajal": "गंगाजल",
699
+ "gulabjal": "गुलाबजल",
700
+ "kumkum": "कुंकू",
701
+ "haladikunku": "हळदीकुंकू",
702
+ "rangoli": "रांगोळी",
703
+ "shampoo": "शॅम्पू",
704
+ "conditioner": "कंडिशनर",
705
+ "harpic": "हार्पिक",
706
+ "lizol": "लायझोल",
707
+ "phenyl": "फिनाईल",
708
+ "closeup": "क्लोजअप",
709
+ "pepsodent": "पेप्सोडेंट",
710
+ "sensodyne": "सेन्सॉडीन",
711
+ "brush": "ब्रश",
712
+ "dettol": "डेटॉल",
713
+ "savlon": "सॅव्हलॉन",
714
+ "rin": "रिन",
715
+ "tide": "टाईड",
716
+ "aerial": "एरियल",
717
+ "wheel": "व्हील",
718
+ "ghadi": "घडी",
719
+ "nirma": "निरमा",
720
+ "vim": "विम",
721
+ "exo": "एक्सो",
722
+ "prill": "प्रिल",
723
+ "scrubber": "स्क्रबर",
724
+ "sponge": "स्पंज",
725
+ "jhadu": "झाडू",
726
+ "pocha": "पोछा",
727
+ "bucket": "बादली",
728
+ "badli": "बादली",
729
+ "mug": "मग",
730
+ "duster": "डस्टर",
731
+ "tissue": "टिश्यू",
732
+ "foil": "फॉइल",
733
+ "plastic": "प्लास्टिक",
734
+ "pishvi": "पिशवी",
735
+ "kilo": "किलो",
736
+ "gram": "ग्रॅम",
737
+ "liter": "लिटर",
738
+ "militre": "मिली",
739
+ "darzon": "डझन",
740
+ "dozen": "डझन",
741
+ "dazan": "डझन",
742
+ "nag": "नग",
743
+ "box": "बॉक्स",
744
+ "carton": "खोके",
745
+ "goni": "गोणी",
746
+ "pote": "पोते",
747
+ "truck": "ट्रक",
748
+ "tractor": "ट्रॅक्टर",
749
+ "tempo": "टॅम्पो",
750
+
751
+ "pani": "पाणी",
752
+ "paani": "पाणी",
753
+ "water": "पाणी",
754
+ "paan": "पान",
755
+ "chaan": "छान",
756
+ "chan": "छान",
757
+ "man": "मन",
758
+ "aan": "आण",
759
+ "bana": "बाण",
760
+ "dane": "दाणे",
761
+ "dana": "दाणा",
383
762
  };
384
763
 
package/src/index.js CHANGED
@@ -4,18 +4,48 @@
4
4
  * for Marathi (Devanagari) script with live smart suggestions.
5
5
  */
6
6
 
7
- import { MARATHI_DICTIONARY } from './dictionary.js';
8
- import { phoneticTransliterateWord } from './phonetics.js';
9
- import { getCachedWord, setCachedWord, fetchOnlineMarathi } from './cache.js';
10
- import { attachDevanagari } from './dom.js';
7
+ import { MARATHI_DICTIONARY } from "./dictionary.js";
8
+ import { phoneticTransliterateWord } from "./phonetics.js";
9
+ import { getCachedWord, setCachedWord, fetchOnlineMarathi } from "./cache.js";
10
+ import { attachDevanagari } from "./dom.js";
11
11
 
12
12
  export { MARATHI_DICTIONARY, fetchOnlineMarathi, attachDevanagari };
13
13
 
14
+ /**
15
+ * Extends or overrides the global transliteration dictionary at runtime.
16
+ * @param {Record<string, string>} customWords - Map of English -> Marathi words
17
+ */
18
+ export function extendDictionary(customWords) {
19
+ if (customWords && typeof customWords === "object") {
20
+ Object.assign(MARATHI_DICTIONARY, customWords);
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Normalizes Marathi characters (interchangeable ळ/ल, श/ष, ि/ी, ु/ू, and anusvar)
26
+ * for tolerant comparisons and search filtering.
27
+ * @param {string} str
28
+ * @returns {string}
29
+ */
30
+ export function normalizeMarathi(str) {
31
+ if (!str) return "";
32
+ return String(str)
33
+ .toLowerCase()
34
+ .replace(/ळ/g, "ल")
35
+ .replace(/ष/g, "श")
36
+ .replace(/ी/g, "ि")
37
+ .replace(/ू/g, "ु")
38
+ .replace(/[ँं]/g, "")
39
+ .trim();
40
+ }
41
+
14
42
  /**
15
43
  * Transliterates a single English word into Marathi (Devanagari).
16
- * @param {string} word - Romanized word e.g. sakhar, kanda, atata * @returns {string} - Marathi Devanagari script e.g. साखर, कांदा, बटाटा */
44
+ * @param {string} word - Romanized word e.g. sakhar, kanda, batata
45
+ * @returns {string} - Marathi Devanagari script e.g. साखर, कांदा, बटाटा
46
+ */
17
47
  export function transliterateWord(word) {
18
- if (!word) return '';
48
+ if (!word) return "";
19
49
  const clean = word.toLowerCase().trim();
20
50
 
21
51
  // 1. Check curated high-accuracy dictionary first
@@ -33,7 +63,7 @@ export function transliterateWord(word) {
33
63
  const phonetic = phoneticTransliterateWord(word);
34
64
 
35
65
  // Background fetch to enrich cache if online (non-blocking)
36
- if (typeof fetch !== 'undefined') {
66
+ if (typeof fetch !== "undefined") {
37
67
  fetchOnlineMarathi(clean).catch(() => {});
38
68
  }
39
69
 
@@ -47,7 +77,7 @@ export function transliterateWord(word) {
47
77
  * @returns {string} - Transliterated text
48
78
  */
49
79
  export function transliterate(text) {
50
- if (!text) return '';
80
+ if (!text) return "";
51
81
 
52
82
  return text.replace(/[a-zA-Z]+/g, match => {
53
83
  return transliterateWord(match);
@@ -61,6 +91,9 @@ export const toDevanagari = transliterate;
61
91
 
62
92
  /**
63
93
  * Returns ranked candidate spelling suggestions for a word.
94
+ * Guarantees that any word containing "l" automatically provides both "ळ" (LLA)
95
+ * and "ल" (LA) variants in candidate suggestions (e.g. bhusal -> भुसाळ, भुसळ, भुसल).
96
+ *
64
97
  * @param {string} word
65
98
  * @param {Object} [options]
66
99
  * @param {number} [options.maxSuggestions=5]
@@ -95,7 +128,88 @@ export function getSuggestions(word, options = {}) {
95
128
  const phonetic = phoneticTransliterateWord(word);
96
129
  if (phonetic) add(phonetic);
97
130
 
98
- // 4. Prefix matches from dictionary (if clean.length >= 3)
131
+ // 4. Smart Marathi phonetic variants expansion (ळ/ल, ड/द, ढ/ध, ट/त, ठ/थ, ण/न, ष/श, झ/ज, ई/इ, ऊ/उ)
132
+ const initialCandidates = [...suggestions];
133
+ for (const s of initialCandidates) {
134
+ // 4a. ळ (LLA) and ल (LA) - e.g. "keli" -> "केळी", "bhusal" -> "भुसाळ"
135
+ if (s.includes("ल")) {
136
+ if (clean.endsWith("al") && s.endsWith("ल") && !s.endsWith("ाल")) {
137
+ add(s.slice(0, -1) + "ाळ");
138
+ }
139
+ add(s.replace(/ल/g, "ळ"));
140
+ } else if (s.includes("ळ")) {
141
+ add(s.replace(/ळ/g, "ल"));
142
+ }
143
+
144
+ // 4b. ड (DDA) and द (DA) - e.g. "Jaad" -> "जाड", "Daba" -> "डबा", "kadak" -> "कडक"
145
+ if (s.includes("द")) {
146
+ if (clean.endsWith("ad") && s.endsWith("द") && !s.endsWith("ाद")) {
147
+ add(s.slice(0, -1) + "ाड");
148
+ }
149
+ add(s.replace(/द/g, "ड"));
150
+ } else if (s.includes("ड")) {
151
+ add(s.replace(/ड/g, "द"));
152
+ }
153
+
154
+ // 4c. ढ (DDHA) and ध (DHA) - e.g. "dhag" -> "ढग", "dhol" -> "ढोल"
155
+ if (s.includes("ध")) {
156
+ add(s.replace(/ध/g, "ढ"));
157
+ } else if (s.includes("ढ")) {
158
+ add(s.replace(/ढ/g, "ध"));
159
+ }
160
+
161
+ // 4d. ट (TTA) and त (TA) - e.g. "batata" -> "बटाटा", "tomato" -> "टोमॅटो"
162
+ if (s.includes("त")) {
163
+ add(s.replace(/त/g, "ट"));
164
+ } else if (s.includes("ट")) {
165
+ add(s.replace(/त/g, "त"));
166
+ }
167
+
168
+ // 4e. ठ (TTHA) and थ (THA) - e.g. "thik" -> "ठीक", "path" -> "पाठ"
169
+ if (s.includes("थ")) {
170
+ add(s.replace(/थ/g, "ठ"));
171
+ } else if (s.includes("ठ")) {
172
+ add(s.replace(/ठ/g, "थ"));
173
+ }
174
+
175
+ // 4f. ण (NNA) and न (NA) - e.g. "pani" -> "पाणी", "bana" -> "बाण", "dane" -> "दाणे"
176
+ if (s.includes("न")) {
177
+ add(s.replace(/न/g, "ण"));
178
+ } else if (s.includes("ण")) {
179
+ add(s.replace(/ण/g, "न"));
180
+ }
181
+
182
+ // 4g. ष (SSA) and श (SHA) - e.g. "vishesh" -> "विशेष"
183
+ if (s.includes("श")) {
184
+ add(s.replace(/श/g, "ष"));
185
+ } else if (s.includes("ष")) {
186
+ add(s.replace(/ष/g, "श"));
187
+ }
188
+
189
+ // 4h. झ (JHA) and ज (JA) - e.g. "jhadu" -> "झाडू"
190
+ if (s.includes("ज") && (clean.includes("jh") || clean.includes("zh") || clean.includes("z"))) {
191
+ add(s.replace(/ज/g, "झ"));
192
+ }
193
+
194
+ // 4i. Authentic Marathi final dirgha vowel rules (मराठी शुद्धलेखन: अंत्य इकार/उकार दीर्घ)
195
+ if (s.endsWith("ि")) {
196
+ add(s.slice(0, -1) + "ी");
197
+ } else if (s.endsWith("ी")) {
198
+ add(s.slice(0, -1) + "ि");
199
+ }
200
+ if (s.endsWith("ु")) {
201
+ add(s.slice(0, -1) + "ू");
202
+ } else if (s.endsWith("ू")) {
203
+ add(s.slice(0, -1) + "ु");
204
+ }
205
+
206
+ // 4j. Word-ending "a" kana variants (e.g. rama -> रामा & राम)
207
+ if (clean.endsWith("a") && s.endsWith("ा")) {
208
+ add(s.slice(0, -1));
209
+ }
210
+ }
211
+
212
+ // 5. Prefix matches from dictionary (if clean.length >= 3)
99
213
  if (clean.length >= 3 && suggestions.length < max) {
100
214
  for (const [key, val] of Object.entries(MARATHI_DICTIONARY)) {
101
215
  if (key.startsWith(clean) && key !== clean) {
@@ -105,7 +219,7 @@ export function getSuggestions(word, options = {}) {
105
219
  }
106
220
  }
107
221
 
108
- // 5. Add raw English word at the end so user can easily keep original
222
+ // 6. Add raw English word at the end so user can easily keep original
109
223
  if (includeOriginal && !suggestions.includes(word)) {
110
224
  suggestions.push(word);
111
225
  }
@@ -120,6 +234,8 @@ export default {
120
234
  toDevanagari,
121
235
  getSuggestions,
122
236
  attachDevanagari,
237
+ extendDictionary,
238
+ normalizeMarathi,
123
239
  MARATHI_DICTIONARY,
124
240
  fetchOnlineMarathi
125
241
  };
package/src/phonetics.js CHANGED
@@ -5,108 +5,118 @@
5
5
 
6
6
  const CONSONANTS = [
7
7
  // Multi-character special combinations
8
- { en: 'dny', mr: 'ज्ञ' },
9
- { en: 'jn', mr: 'ज्ञ' },
10
- { en: 'gy', mr: 'ज्ञ' },
11
- { en: 'ksh', mr: 'क्ष' },
12
- { en: 'shr', mr: 'श्र' },
13
- { en: 'chh', mr: 'छ' },
14
- { en: 'ch', mr: 'च' },
15
- { en: 'kh', mr: 'ख' },
16
- { en: 'gh', mr: 'घ' },
17
- { en: 'jh', mr: 'झ' },
18
- { en: 'zh', mr: 'झ' },
19
- { en: 'th', mr: 'थ' },
20
- { en: 'dh', mr: 'ध' },
21
- { en: 'ph', mr: 'फ' },
22
- { en: 'bh', mr: 'भ' },
23
- { en: 'shh', mr: 'ष' },
24
- { en: 'sh', mr: 'श' },
25
- { en: 'ng', mr: 'ङ' },
26
-
27
- // Single consonants
28
- { en: 'k', mr: 'क' },
29
- { en: 'g', mr: 'ग' },
30
- { en: 'j', mr: 'ज' },
31
- { en: 't', mr: 'त' },
32
- { en: 'd', mr: 'द' },
33
- { en: 'n', mr: 'न' },
34
- { en: 'p', mr: 'प' },
35
- { en: 'f', mr: 'फ' },
36
- { en: 'b', mr: 'ब' },
37
- { en: 'm', mr: 'म' },
38
- { en: 'y', mr: 'य' },
39
- { en: 'r', mr: 'र' },
40
- { en: 'l', mr: 'ल' },
41
- { en: 'L', mr: 'ळ' },
42
- { en: 'v', mr: 'व' },
43
- { en: 'w', mr: 'व' },
44
- { en: 's', mr: 'स' },
45
- { en: 'h', mr: 'ह' },
46
- { en: 'x', mr: 'क्ष' },
47
- { en: 'z', mr: 'झ' },
48
- { en: 'T', mr: 'ट' },
49
- { en: 'D', mr: 'ड' },
50
- { en: 'N', mr: 'ण' }
8
+ { en: "dny", mr: "ज्ञ" },
9
+ { en: "jn", mr: "ज्ञ" },
10
+ { en: "gy", mr: "ज्ञ" },
11
+ { en: "ksh", mr: "क्ष" },
12
+ { en: "shr", mr: "श्र" },
13
+ { en: "chh", mr: "छ" },
14
+ { en: "ch", mr: "च" },
15
+ { en: "kh", mr: "ख" },
16
+ { en: "gh", mr: "घ" },
17
+ { en: "jh", mr: "झ" },
18
+ { en: "zh", mr: "झ" },
19
+ { en: "Dh", mr: "ढ", caseSensitive: true },
20
+ { en: "DH", mr: "ढ", caseSensitive: true },
21
+ { en: "dh", mr: "ध" },
22
+ { en: "Th", mr: "ठ", caseSensitive: true },
23
+ { en: "TH", mr: "ठ", caseSensitive: true },
24
+ { en: "th", mr: "थ" },
25
+ { en: "ph", mr: "फ" },
26
+ { en: "bh", mr: "भ" },
27
+ { en: "shh", mr: "ष" },
28
+ { en: "Sh", mr: "ष", caseSensitive: true },
29
+ { en: "SH", mr: "ष", caseSensitive: true },
30
+ { en: "sh", mr: "श" },
31
+ { en: "ck", mr: "क" },
32
+ { en: "ng", mr: "ङ" },
33
+ { en: "c", mr: "क" },
34
+
35
+ // Single consonants - case-sensitive retroflex & special Marathi sounds FIRST
36
+ { en: "T", mr: "ट", caseSensitive: true },
37
+ { en: "D", mr: "ड", caseSensitive: true },
38
+ { en: "N", mr: "ण", caseSensitive: true },
39
+ { en: "L", mr: "ळ", caseSensitive: true },
40
+
41
+ // Single consonants - standard
42
+ { en: "k", mr: "क" },
43
+ { en: "g", mr: "ग" },
44
+ { en: "j", mr: "ज" },
45
+ { en: "t", mr: "त" },
46
+ { en: "d", mr: "द" },
47
+ { en: "n", mr: "न" },
48
+ { en: "p", mr: "प" },
49
+ { en: "f", mr: "फ" },
50
+ { en: "b", mr: "ब" },
51
+ { en: "m", mr: "म" },
52
+ { en: "y", mr: "य" },
53
+ { en: "r", mr: "र" },
54
+ { en: "l", mr: "ल" },
55
+ { en: "v", mr: "व" },
56
+ { en: "w", mr: "व" },
57
+ { en: "s", mr: "स" },
58
+ { en: "h", mr: "ह" },
59
+ { en: "x", mr: "क्ष" },
60
+ { en: "z", mr: "झ" }
51
61
  ];
52
62
 
53
63
  const INDEPENDENT_VOWELS = [
54
- { en: 'aa', mr: 'आ' },
55
- { en: 'A', mr: 'आ' },
56
- { en: 'a', mr: 'अ' },
57
- { en: 'ee', mr: 'ई' },
58
- { en: 'ii', mr: 'ई' },
59
- { en: 'i', mr: 'इ' },
60
- { en: 'oo', mr: 'ऊ' },
61
- { en: 'uu', mr: 'ऊ' },
62
- { en: 'u', mr: 'उ' },
63
- { en: 'ai', mr: 'ऐ' },
64
- { en: 'ay', mr: 'ऐ' },
65
- { en: 'au', mr: 'औ' },
66
- { en: 'ou', mr: 'औ' },
67
- { en: 'ow', mr: 'औ' },
68
- { en: 'e', mr: 'ए' },
69
- { en: 'o', mr: 'ओ' },
70
- { en: 'am', mr: 'अं' },
71
- { en: 'an', mr: 'अं' },
72
- { en: 'ri', mr: 'ऋ' },
73
- { en: 'ru', mr: 'ऋ' }
64
+ { en: "aa", mr: "आ" },
65
+ { en: "A", mr: "आ" },
66
+ { en: "a", mr: "अ" },
67
+ { en: "ee", mr: "ई" },
68
+ { en: "ii", mr: "ई" },
69
+ { en: "i", mr: "इ" },
70
+ { en: "oo", mr: "ऊ" },
71
+ { en: "uu", mr: "ऊ" },
72
+ { en: "u", mr: "उ" },
73
+ { en: "ai", mr: "ऐ" },
74
+ { en: "ay", mr: "ऐ" },
75
+ { en: "au", mr: "औ" },
76
+ { en: "ou", mr: "औ" },
77
+ { en: "ow", mr: "औ" },
78
+ { en: "e", mr: "ए" },
79
+ { en: "o", mr: "ओ" },
80
+ { en: "am", mr: "अं" },
81
+ { en: "an", mr: "अं" },
82
+ { en: "ri", mr: "ऋ" },
83
+ { en: "ru", mr: "ऋ" },
84
+ { en: "R", mr: "ऋ", caseSensitive: true }
74
85
  ];
75
86
 
76
87
  const MATRAS = [
77
- { en: 'aa', mr: 'ा' },
78
- { en: 'A', mr: 'ा' },
79
- { en: 'a', mr: '' }, // Inherent vowel: cancels halant, no visible matra
80
- { en: 'ee', mr: 'ी' },
81
- { en: 'ii', mr: 'ी' },
82
- { en: 'i', mr: 'ि' },
83
- { en: 'oo', mr: 'ू' },
84
- { en: 'uu', mr: 'ू' },
85
- { en: 'u', mr: 'ु' },
86
- { en: 'ai', mr: 'ै' },
87
- { en: 'ay', mr: 'ै' },
88
- { en: 'au', mr: 'ौ' },
89
- { en: 'ou', mr: 'ौ' },
90
- { en: 'ow', mr: 'ौ' },
91
- { en: 'e', mr: 'े' },
92
- { en: 'o', mr: 'ो' },
93
- { en: 'am', mr: 'ं' },
94
- { en: 'an', mr: 'ं' },
95
- { en: 'ri', mr: 'ृ' },
96
- { en: 'ru', mr: 'ृ' }
88
+ { en: "aa", mr: "ा" },
89
+ { en: "A", mr: "ा" },
90
+ { en: "a", mr: "" }, // Inherent vowel: cancels halant; at word end becomes kana
91
+ { en: "ee", mr: "ी" },
92
+ { en: "ii", mr: "ी" },
93
+ { en: "i", mr: "ि" },
94
+ { en: "oo", mr: "ू" },
95
+ { en: "uu", mr: "ू" },
96
+ { en: "u", mr: "ु" },
97
+ { en: "ai", mr: "ै" },
98
+ { en: "ay", mr: "ै" },
99
+ { en: "au", mr: "ौ" },
100
+ { en: "ou", mr: "ौ" },
101
+ { en: "ow", mr: "ौ" },
102
+ { en: "e", mr: "े" },
103
+ { en: "o", mr: "ो" },
104
+ { en: "am", mr: "ं" },
105
+ { en: "an", mr: "ं" },
106
+ { en: "R", mr: "ृ", caseSensitive: true }
97
107
  ];
98
108
 
99
109
  /**
100
110
  * Phonetically transliterates a single Roman word to Devanagari.
101
- * @param {string} text - Single English word (e.g. "shantanu", "ramesh")
111
+ * @param {string} text - Single English word (e.g. "shantanu", "ramesh", "Jaad", "Daba")
102
112
  * @returns {string} - Devanagari representation
103
113
  */
104
114
  export function phoneticTransliterateWord(text) {
105
- if (!text) return '';
115
+ if (!text) return "";
106
116
  const clean = text.trim();
107
- if (!clean) return '';
117
+ if (!clean) return "";
108
118
 
109
- let res = '';
119
+ let res = "";
110
120
  let i = 0;
111
121
  const n = clean.length;
112
122
 
@@ -122,7 +132,9 @@ export function phoneticTransliterateWord(text) {
122
132
  let matchedVowel = false;
123
133
  if (i === 0 || !/[a-zA-Z]/.test(clean[i - 1])) {
124
134
  for (const v of INDEPENDENT_VOWELS) {
125
- if (clean.substring(i, i + v.en.length).toLowerCase() === v.en.toLowerCase()) {
135
+ const sub = clean.substring(i, i + v.en.length);
136
+ const isMatch = v.caseSensitive ? sub === v.en : sub.toLowerCase() === v.en.toLowerCase();
137
+ if (isMatch) {
126
138
  res += v.mr;
127
139
  i += v.en.length;
128
140
  matchedVowel = true;
@@ -136,8 +148,7 @@ export function phoneticTransliterateWord(text) {
136
148
  let matchedConsonant = false;
137
149
  for (const c of CONSONANTS) {
138
150
  const sub = clean.substring(i, i + c.en.length);
139
- const isCaseSensitive = c.en === 'L' || c.en === 'T' || c.en === 'D' || c.en === 'N';
140
- const isMatch = isCaseSensitive ? sub === c.en : sub.toLowerCase() === c.en.toLowerCase();
151
+ const isMatch = c.caseSensitive ? sub === c.en : sub.toLowerCase() === c.en.toLowerCase();
141
152
 
142
153
  if (isMatch) {
143
154
  res += c.mr;
@@ -148,11 +159,17 @@ export function phoneticTransliterateWord(text) {
148
159
  let matchedMatra = false;
149
160
  for (const m of MATRAS) {
150
161
  const nextSub = clean.substring(i, i + m.en.length);
151
- const mCaseSensitive = m.en === 'A';
162
+ const mCaseSensitive = m.caseSensitive || m.en === "A";
152
163
  const mMatch = mCaseSensitive ? nextSub === m.en : nextSub.toLowerCase() === m.en.toLowerCase();
153
164
 
154
165
  if (mMatch) {
155
- res += m.mr;
166
+ // When user types trailing "a" at the end of word (e.g. daba -> डबा, patrika -> पत्रिका),
167
+ // in casual Marathi it represents the kana "ा"
168
+ if (m.en === "a" && i + 1 === n && i > 0) {
169
+ res += "ा";
170
+ } else {
171
+ res += m.mr;
172
+ }
156
173
  i += m.en.length;
157
174
  matchedMatra = true;
158
175
  break;
@@ -163,7 +180,7 @@ export function phoneticTransliterateWord(text) {
163
180
  // In Marathi/Hindi phonetics, word-final consonants do NOT get halant
164
181
  if (!matchedMatra) {
165
182
  if (i < n && /[a-zA-Z]/.test(clean[i])) {
166
- res += '्';
183
+ res += "्";
167
184
  }
168
185
  }
169
186