nepali-numbers-pro-max
v1.1.0
Published
नेपाली number / NPR currency library for the Nepali developer community. Devanagari ↔ ASCII digits, Indian-style lakh/crore/arab grouping, NPR formatting, number → words (एक करोड पच्चीस लाख…), words → number, ordinals, fractions, compact, paisa-safe BigIn
Maintainers
Readme
nepali-numbers-pro-max
नेपाली अंक र मुद्रा — pro-max edition
The complete Nepali number toolkit. Devanagari ↔ ASCII digits, Indian-style lakh/crore grouping, NPR currency formatting, number → words (एक करोड पच्चीस लाख), words → number, paisa-safe BigInt math, ordinals, fractions, percentages, compact notation.
📑 Table of Contents
- Highlights
- Install
- Quick Start
- Comparison
- Mental Model
- Currency Formatting
- Number → Words
- Words → Number
- Indian-style Grouping
NepaliMoneyClass- Compact / Percent / Fractions / Ordinals
- Full API Reference
- Recipes
- Range & Accuracy
- Contributing
✨ Highlights
- 🇳🇵 Nepal-first — NPR currency, paisa precision, Indian numbering, Devanagari I/O
- 📜 Full word coverage —
एकtoमहाशंख(10²¹),onetomahashankh, in both languages - 🔁 Round-trip parsing —
toNepaliWords⟷parseNepaliWords(no other npm package does this for Nepali) - 💰 Paisa-safe arithmetic — BigInt-backed, no
0.1 + 0.2 = 0.30000000000000004 - 📐 Indian 3-2-2 grouping —
1,23,45,67,890not1,234,567,890 - 🧮 6 rounding modes — half-up, half-even (banker's), half-down, ceil, floor, trunc
- 📝 5 NPR symbols —
Rs.,रु.,रू,₨,NPR - 🪙 Cheque-style words —
"One thousand two hundred thirty-four rupees fifty-six paisa Only" - 🔢 Smart parser — accepts ASCII, Devanagari, mixed scripts, accounting parens, custom symbols
- 📦 Tree-shakeable — pure ESM exports,
sideEffects: false - 🎯 TypeScript-first — strict mode, full type exports, JSDoc on every function
- 🪶 Zero dependencies — works in Node, Deno, Bun, the browser, and Workers
📦 Install
# npm
npm install nepali-numbers-pro-max
# pnpm
pnpm add nepali-numbers-pro-max
# yarn
yarn add nepali-numbers-pro-max
# bun
bun add nepali-numbers-pro-maxWorks with Node ≥ 14, modern browsers, Deno, Bun, Cloudflare Workers, Vercel Edge.
⚡ Quick Start
import {
toDevanagariDigits,
groupNepali,
formatNPR,
formatNPRDevanagari,
toNepaliWords,
toNepaliCurrencyWords,
parseNepaliWords,
parseAmount,
NepaliMoney,
} from "nepali-numbers-pro-max";
// Digits
toDevanagariDigits("Rs. 1,234.56"); // "Rs. १,२३४.५६"
// Indian grouping
groupNepali(12345678); // "1,23,45,678"
// NPR
formatNPR(1234567.89); // "Rs. 12,34,567.89"
formatNPRDevanagari(1234567.89); // "रु. १२,३४,५६७.८९"
// Words
toNepaliWords(125_000_00); // "एक करोड पच्चीस लाख"
toNepaliCurrencyWords(1234.56);
// "एक हजार दुई सय चौँतीस रुपैयाँ छपन्न पैसा मात्र"
// Words → number (round-trip)
parseNepaliWords("एक करोड पच्चीस लाख"); // 12500000n
// Smart amount parser (Devanagari, NPR symbol, Indian grouping)
parseAmount("रु. १२,३४,५६७.८९");
// { paisa: 123456789n, value: 1234567.89, raw: "रु. १२,३४,५६७.८९", negative: false }
// Money class with paisa-safe arithmetic
NepaliMoney.fromRupees(0.1).add(0.2).rupees; // 0.3 (not 0.30000000000000004)
NepaliMoney.fromRupees(1000).multiply(1.13).formatNPR(); // "Rs. 1,130.00"📊 Comparison with popular packages
| Capability | nepali-number | nepali-number-words | get-nepali-number | ngx-nepali-number-to-words | nepali-numbers-pro-max | |---|---|---|---|---|---| | Last published | 2019 | 2026 | 2020 | 2020 | 2026 | | TypeScript-first | partial | partial | no | yes (Angular) | ✅ strict | | Zero deps | ✅ | ✅ | ✅ | tslib | ✅ | | Devanagari ↔ ASCII | ✅ | ✅ | ✅ | partial | ✅ | | Indian 3-2-2 grouping | ✅ | partial | ✅ | no | ✅ | | NPR currency formatter | partial | partial | no | no | ✅ 5 symbols, accounting, compact | | Number → Nepali words | ✅ | ✅ | no | ✅ (buggy paisa) | ✅ up to महाशंख | | Number → English (Indian) words | ✅ | no | no | no | ✅ | | Words → number (parser) | no | experimental | no | no | ✅ first-class, both languages | | BigInt support | no | no | no | no | ✅ | | Banker's rounding / paisa-int math | no | no | no | no | ✅ 6 modes | | Compact (१.२ लाख / 1.5Cr) | partial | no | no | no | ✅ | | Fractions (आधा, चौथाइ, डेढ) | no | no | no | no | ✅ | | Ordinals (पहिलो, १०१st) | ✅ | no | no | no | ✅ irregular + suffix | | Smart amount parser | partial | no | no | no | ✅ Devanagari + parens + symbols | | Money class with chaining | no | no | no | no | ✅ NepaliMoney | | Tree-shakeable per-function | no | no | no | no | ✅ | | Tests | no | no | no | no | ✅ 105 passing |
🧠 Mental Model
| Rule | Why |
|---|---|
| Indian-style 3-2-2 grouping is the default (1,23,45,67,890). | Nepal/India use it; Western 3-3-3 is opt-in via groupWestern. |
| Functions split per language — *Nepali for Devanagari, *English…NepaliSystem for Roman. | Avoids { locale } option-bag noise. Each function has one job. |
| Money uses BigInt paisa internally, exposes number rupees. | Sidesteps IEEE-754 drift. 0.1 + 0.2 always equals 0.3. |
| English words use the Indian scale ladder (lakh/crore/arab), not Western (million/billion). | Mirrors how Nepali speakers express English numbers in everyday Nepal contexts. |
| Parsers accept Devanagari, ASCII, mixed, parens, NPR symbols. | Real input is messy — handle it. |
| NPR symbol presets: Rs., रु., रू, ₨, NPR. | All five used in production banking software. |
💰 Currency Formatting
formatNPR(1234567.89); // "Rs. 12,34,567.89"
formatNPR(1234567.89, { symbol: "NPR" }); // "NPR 12,34,567.89"
formatNPR(1234567.89, { precision: 0 }); // "Rs. 12,34,568"
formatNPR(-500); // "-Rs. 500.00"
formatNPRDevanagari(1234567.89); // "रु. १२,३४,५६७.८९"
formatNPRAccounting(-500); // "(Rs. 500.00)"
formatNPRCompact(2_50_00_000); // "Rs. 2.5Cr"
formatNPRCompact(150_000); // "Rs. 1.5L"| Function | Output |
|---|---|
| formatNPR(n) | Rs. 12,34,567.89 |
| formatNPRDevanagari(n) | रु. १२,३४,५६७.८९ |
| formatNPRAccounting(-n) | (Rs. 500.00) |
| formatNPRCompact(n) | Rs. 2.5Cr, Rs. 1.5L, Rs. 1.2K |
| stripNprSymbol(s) | strips Rs./रु./रू/₨/NPR |
📝 Number → Words
Up to 10²¹ (महाशंख) via BigInt internally.
toNepaliWords(0); // "शून्य"
toNepaliWords(99); // "उनान्सय"
toNepaliWords(125); // "एक सय पच्चीस"
toNepaliWords(99999); // "उनान्सय हजार नौ सय उनान्सय"
toNepaliWords(125_000_00); // "एक करोड पच्चीस लाख"
toNepaliWords(1_00_00_00_000); // "एक अर्ब"
toNepaliWords(-25); // "ऋणात्मक पच्चीस"
toNepaliWords(100000000000000000000n); // "दश महाशंख"
toEnglishWordsNepaliSystem(125_000_00); // "one crore twenty-five lakh"
// Currency words
toNepaliCurrencyWords(1234.56);
// "एक हजार दुई सय चौँतीस रुपैयाँ छपन्न पैसा मात्र"
toNepaliCurrencyWordsWithoutMatra(1234); // "एक हजार दुई सय चौँतीस रुपैयाँ"
toNepaliCurrencyWordsBare(1234.5); // "एक हजार दुई सय चौँतीस पचास"
toEnglishCurrencyWordsNepaliSystem(1234.56);
// "one thousand two hundred thirty-four rupees fifty-six paisa only"
toChequeStyleEnglish(1234.56);
// "One thousand two hundred thirty-four rupees fifty-six paisa Only"The Indian scale ladder used: हजार (10³) → लाख (10⁵) → करोड (10⁷) → अर्ब (10⁹) → खर्ब (10¹¹) → नील (10¹³) → पद्म (10¹⁵) → शंख (10¹⁷) → महाशंख (10¹⁹).
🔁 Words → Number (parsing)
The headline reverse-direction feature. Round-trips with the words generators.
parseNepaliWords("एक करोड पच्चीस लाख"); // 12500000n
parseNepaliWords("दुई हजार र पच्चीस"); // 2025n (र = "and" connector)
parseNepaliWords("ऋणात्मक पच्चीस"); // -25n
parseEnglishWordsNepaliSystem("one crore twenty-five lakh"); // 12500000n
parseEnglishWordsNepaliSystem("one hundred and twenty-five"); // 125n
parseNepaliCurrencyWords("एक हजार रुपैयाँ पच्चीस पैसा मात्र");
// { rupee: 1000n, paisa: 25, negative: false }
parseEnglishCurrencyWordsNepaliSystem(
"one thousand rupees twenty-five paisa only",
);
// { rupee: 1000n, paisa: 25, negative: false }
// Smart string-amount parser
parseAmount("रु. १२,३४,५६७.८९");
// { paisa: 123456789n, value: 1234567.89, raw: "...", negative: false }
parseAmount("(Rs. 100.00)"); // { paisa: -10000n, negative: true, ... }
safeParseAmount("not a number"); // null
canonicalizeAmount("रु. १,२३४.५६"); // "1234.56"📐 Indian-style Grouping
groupNepali(0); // "0"
groupNepali(1234); // "1,234"
groupNepali(12345); // "12,345"
groupNepali(123456); // "1,23,456"
groupNepali(12345678); // "1,23,45,678"
groupNepali(1234567890); // "1,23,45,67,890"
groupNepali(123456.789); // "1,23,456.789"
groupNepali(-12345); // "-12,345"
groupNepali(123456789012345n); // "12,34,56,78,90,12,345" (BigInt)
groupNepaliDevanagari(12345678); // "१,२३,४५,६७८"
groupWestern(12345678); // "12,345,678"
compareGrouping(12345678);
// { nepali: "1,23,45,678", western: "12,345,678" }🪙 NepaliMoney Class
Immutable money object with paisa-precise arithmetic.
import { NepaliMoney } from "nepali-numbers-pro-max";
const total = NepaliMoney.fromRupees(1234.56)
.add(NepaliMoney.fromRupees(100))
.multiply(1.13) // add 13% VAT
.subtract(50);
total.formatNPR(); // "Rs. 1,458.05"
total.formatNPRDevanagari(); // "रु. १,४५८.०५"
total.toNepaliWords(); // "एक हजार चार सय अन्ठाउन्न रुपैयाँ पाँच पैसा मात्र"
// Allocate without losing rupees to rounding
NepaliMoney.fromRupees(10).allocate(3).map(p => p.rupees);
// [3.34, 3.33, 3.33] — sums to exactly 10
// Comparison
const a = NepaliMoney.fromRupees(100);
const b = NepaliMoney.fromRupees(200);
a.lessThan(b); // true
a.compare(b); // -1
// Parse straight from a formatted string
NepaliMoney.parse("रु. १,२३४.५६").rupees; // 1234.56Internal representation is signed BigInt paisa — no FP drift, supports values past Number.MAX_SAFE_INTEGER.
🎨 Compact / Percent / Fractions / Ordinals
// Compact
formatCompactEnglish(1234); // "1.2K"
formatCompactEnglish(125000); // "1.3L"
formatCompactEnglish(2_50_00_000); // "2.5Cr"
formatCompactEnglish(2_50_00_000, { long: true }); // "2.5 crore"
formatCompactNepali(1234); // "१.२ हजार"
formatCompactNepali(125000); // "१.३ लाख"
formatCompactNepali(2_50_00_000); // "२.५ करोड"
// Percent
formatPercent(25); // "25%"
formatPercent(25.5, { precision: 1 }); // "25.5%"
formatPercentDevanagari(25); // "२५%"
percentToNepaliWords(25); // "पच्चीस प्रतिशत"
percentToEnglishWords(25.5); // "twenty-five point five percent"
// Fractions
fractionToNepaliWords(1, 2); // "आधा"
fractionToNepaliWords(3, 4); // "तीन चौथाइ"
fractionToNepaliWords(2, 3); // "दुई तिहाइ"
fractionToEnglishWords(3, 4); // "three quarters"
decimalToNepaliFraction(0.5); // "आधा"
decimalToNepaliFraction(0.42); // null
// Ordinals
toNepaliOrdinal(1); // "पहिलो"
toNepaliOrdinal(10); // "दशौँ"
toNepaliOrdinal(11); // "एघारऔँ"
toEnglishOrdinal(21); // "21st"
toEnglishOrdinal(113); // "113th"🧰 Full API Reference
| Function | Description |
|---|---|
| toDevanagariDigits(input) | Convert digits in string to Devanagari (०-९). |
| toAsciiDigits(input) | Convert digits in string to ASCII (0-9). |
| toDevanagariChar(ch) | Single-char converter (throws if invalid). |
| toAsciiChar(ch) | Single-char converter (throws if invalid). |
| detectScript(s) | "ascii" \| "devanagari" \| "mixed" \| "none" |
| normalizeDigits(s, "ascii" \| "devanagari") | Coerce mixed strings to one script. |
| isAllAsciiDigits(s) | True if every char is 0-9. |
| isAllDevanagariDigits(s) | True if every char is ०-९. |
| DEVANAGARI_DIGITS, ASCII_DIGITS | Constant lookup tables. |
| Function | Description |
|---|---|
| groupNepali(n, opts?) | Indian-style 3-2-2 grouping (1,23,45,678). |
| groupNepaliDevanagari(n, opts?) | Same, in Devanagari digits. |
| groupWestern(n, opts?) | Western 3-3-3 grouping (12,345,678). |
| compareGrouping(n) | { nepali, western }. |
Options: { separator?: string, decimal?: string }.
| Function | Output style |
|---|---|
| formatNPR(n, opts?) | Rs. 12,34,567.89 |
| formatNPRDevanagari(n, opts?) | रु. १२,३४,५६७.८९ |
| formatNPRAccounting(n, opts?) | (Rs. 500.00) for negatives |
| formatNPRCompact(n, opts?) | Rs. 2.5Cr, Rs. 1.5L, Rs. 1.2K |
| stripNprSymbol(s) | Remove Rs., रु., रू, ₨, NPR |
FormatNprOptions: { symbol, precision, rounding, symbolPosition, symbolSpacing, devanagari, separator, decimal }.
| Function | Example output |
|---|---|
| toNepaliWords(n) | "एक करोड पच्चीस लाख" |
| toEnglishWordsNepaliSystem(n) | "one crore twenty-five lakh" |
| toNepaliCurrencyWords(amount) | "… रुपैयाँ … पैसा मात्र" |
| toNepaliCurrencyWordsWithoutMatra(amount) | drops मात्र suffix |
| toNepaliCurrencyWordsBare(amount) | no रुपैयाँ/पैसा labels |
| toEnglishCurrencyWordsNepaliSystem(amount) | "… rupees … paisa only" |
| toEnglishCurrencyWordsWithoutOnly(amount) | drops only suffix |
| toEnglishCurrencyWordsBare(amount) | no rupees/paisa labels |
| toChequeStyleEnglish(amount) | capitalized cheque-style |
| Function | Returns |
|---|---|
| parseNepaliWords(s) | bigint |
| parseEnglishWordsNepaliSystem(s) | bigint |
| parseNepaliCurrencyWords(s) | { rupee, paisa, negative } |
| parseEnglishCurrencyWordsNepaliSystem(s) | { rupee, paisa, negative } |
| parseAmount(s) | { paisa, value, raw, negative } |
| safeParseAmount(s) | ParsedAmount \| null |
| canonicalizeAmount(s) | normalized "1234.56" form |
| isValidAmount(s) | boolean |
| isValidNepaliDigitString(s) | boolean |
| isValidAsciiNumberString(s) | boolean |
| Function | Description |
|---|---|
| roundPaisa(n, mode?, precision?) | 6 rounding modes |
| roundToScale(n, scale, mode?) | round to nearest 1000, 1L, 1Cr, etc. |
| rupeeToPaisa(rupee) | number → bigint |
| paisaToRupee(paisa) | bigint → number |
| splitRupeePaisa(amount) | { rupee: bigint, paisa: 0-99, negative } |
| addAmount(a, b) | FP-drift-free addition |
| subtractAmount(a, b) | FP-drift-free subtraction |
| multiplyAmount(a, factor, mode?) | rounded to paisa |
| divideAmount(a, divisor, mode?) | rounded to paisa |
| splitAmount(amount, n) | n-way split with remainder distribution |
| toFixedAmount(n, decimals?) | drift-free toFixed |
Rounding modes: "half-up" \| "half-even" \| "half-down" \| "ceil" \| "floor" \| "trunc".
| Function | Example |
|---|---|
| formatCompactEnglish(n, opts?) | 1.2K, 1.5L, 2.5Cr |
| formatCompactNepali(n, opts?) | १.२ हजार, १.५ लाख, २.५ करोड |
| formatPercent(n, opts?) | 25% |
| formatPercentDevanagari(n, opts?) | २५% |
| percentToNepaliWords(n) | पच्चीस प्रतिशत |
| percentToEnglishWords(n) | twenty-five percent |
| fractionToNepaliWords(num, den) | आधा, तीन चौथाइ |
| fractionToEnglishWords(num, den) | half, three quarters |
| decimalToNepaliFraction(v, tol?) | आधा for 0.5, null if no match |
| toNepaliOrdinal(n) | पहिलो, दोस्रो, एघारऔँ |
| toEnglishOrdinal(n) | 1st, 2nd, 113th |
| Method | Description |
|---|---|
| NepaliMoney.fromRupees(n) | from rupees number |
| NepaliMoney.fromPaisa(p) | from BigInt paisa |
| NepaliMoney.parse(s) | parse formatted string |
| NepaliMoney.zero() | zero instance |
| .rupees, .paisa, .negative, .isZero | accessors |
| .split() | { rupee, paisa, negative } |
| .add(x) / .subtract(x) | x can be NepaliMoney or number |
| .multiply(factor) / .divide(divisor) | scalar math, paisa-rounded |
| .negate() / .abs() | sign helpers |
| .allocate(n) | n-way split, no rupee lost |
| .equals(o) / .greaterThan(o) / .lessThan(o) / .compare(o) | comparison |
| .formatNPR(opts?), .formatNPRDevanagari(), .formatNPRAccounting(), .formatCompact() | formatting |
| .toNepaliWords() / .toEnglishWords() | currency words |
| .toString() | default formatNPR() |
| .toJSON() | canonical "1234.56" |
| Constant | Description |
|---|---|
| DEVANAGARI_DIGITS, ASCII_DIGITS | digit tables |
| SCALES | scale ladder up to महाशंख |
| NEPALI_ONES | 0-99 lookup table |
| ENGLISH_ONES, ENGLISH_TENS | English word tables |
| NEPALI_HUNDRED | "सय" |
| RUPEE_WORD_NE, RUPEE_WORD_EN | "रुपैयाँ" / "rupees" |
| PAISA_WORD_NE, PAISA_WORD_EN | "पैसा" / "paisa" |
| MATRA_NE, ONLY_EN | "मात्र" / "only" |
| NEGATIVE_NE, NEGATIVE_EN | "ऋणात्मक" / "negative" |
| AND_NE, AND_EN | "र" / "and" |
| NEPALI_ORDINALS_LOW | irregular 1-10 |
| NEPALI_FRACTIONS_TABLE | curated fraction words |
🎯 Recipes
Invoice line item with VAT
import { NepaliMoney } from "nepali-numbers-pro-max";
const subtotal = NepaliMoney.fromRupees(50000);
const vat = subtotal.multiply(0.13);
const total = subtotal.add(vat);
console.log(total.formatNPRDevanagari()); // "रु. ५६,५००.००"
console.log(total.toNepaliWords());
// "छपन्न हजार पाँच सय रुपैयाँ मात्र"Cheque writing
import { toChequeStyleEnglish } from "nepali-numbers-pro-max";
const chequeText = toChequeStyleEnglish(1_25_000.50);
// "One lakh twenty-five thousand rupees fifty paisa Only"Reading user-typed Nepali amounts
import { parseAmount, formatNPRDevanagari } from "nepali-numbers-pro-max";
const userInput = "रु. १,२३,४५६.७८";
const parsed = parseAmount(userInput);
console.log(parsed.value); // 123456.78
console.log(formatNPRDevanagari(parsed.value)); // "रु. १,२३,४५६.७८"Splitting a bill
import { NepaliMoney } from "nepali-numbers-pro-max";
const dinner = NepaliMoney.fromRupees(2575.65);
const split = dinner.allocate(4);
// First person pays 1 paisa more (no rupee disappears)
console.log(split.map(p => p.formatNPR()));
// [ "Rs. 643.92", "Rs. 643.91", "Rs. 643.91", "Rs. 643.91" ]Tally with paisa precision
import { NepaliMoney } from "nepali-numbers-pro-max";
const items = [49.99, 19.95, 4.50, 2.75];
const total = items.reduce(
(sum, item) => sum.add(item),
NepaliMoney.zero(),
);
total.formatNPR(); // "Rs. 77.19" — exact, no FP error📐 Range & Accuracy
- Words generation supports up to 10²¹ (दश महाशंख) via BigInt internally.
- Money arithmetic uses signed BigInt paisa — no precision loss.
numberrupee accessor is lossy pastNumber.MAX_SAFE_INTEGER / 100≈ 90 trillion rupees. For larger values, use.paisadirectly.- Rounding defaults to half-up (Nepali invoicing convention). Use
"half-even"for accounting-grade tax math. - Devanagari digit range: U+0966 (
०) to U+0966+9 (९). Other Indic digit ranges (Tamil, Bengali, etc.) are not transliterated — Devanagari only.
🤝 Contributing
PRs welcome. Open an issue for:
- New scale-name variants (regional Nepali / Maithili / Bhojpuri)
- Additional fraction lookups
- Operator/region-specific currency symbol presets
npm install
npm test
npm run typecheck
npm run build📜 License
MIT © 2026 l3lackcurtains
Made with ❤️ for the Nepali developer community.
बनाइएको नेपाली डेभलपर समुदायको लागि।
