numberparrot

Convert Arabic and Persian Digits to a Number in JavaScript

Arabic-Indic and Persian digits are real digits, and Intl.NumberFormat prints them by default for locales like ar-EG and fa-IR. JavaScript cannot read them back.

The problem

example.ts
const text = new Intl.NumberFormat("ar-EG").format(1234.56); // "١٬٢٣٤٫٥٦"
Number(text);     // NaN
parseFloat(text); // NaN
Number("١٢٣");    // NaN

The usual trick and its gaps

A common snippet maps each digit with its char code:

example.ts
const toLatin = (s) =>
  s.replace(/[٠-٩]/g, (d) => d.charCodeAt(0) - 0x0660)
   .replace(/[۰-۹]/g, (d) => d.charCodeAt(0) - 0x06f0);

Number(toLatin("١٢٣"));        // 123, fine
Number(toLatin("١٬٢٣٤٫٥٦"));   // NaN: ٬ and ٫ are still there

It also misses the Arabic decimal separator ٫ (U+066B), the Arabic thousands separator ٬ (U+066C), the invisible bidi marks that Intl places around the minus sign, the Arabic percent sign ٪, and every other script: Devanagari, Bengali, Thai, full width and more.

The fix

example.ts
import { parse } from "numberparrot";

parse("١٬٢٣٤٫٥٦", "ar-EG");      // 1234.56
parse("۱۲۳٫۴۵", "fa-IR");         // 123.45
parse("١٥٪", "ar-EG");            // 0.15
parse("१२,३४,५६७.८९", "hi-IN");   // 1234567.89 (Devanagari)
parse("๑,๒๓๔.๕", "th-TH");       // 1234.5 (Thai)
parse("১,২৩৪.৫", "bn-BD");        // 1234.5 (Bengali)
parse("1234.5", "ja-JP");    // 1234.5 (full width)

Every Unicode decimal digit is supported, whatever locale you pass. Text with mixed scripts works too: parse("١٢٣", "en-US") is 123.

Normalize while the user types

example.ts
import { normalizeInput } from "numberparrot";

normalizeInput("١٢٫٥", "ar-EG");
// { text: "12٫5", value: 12.5, valid: true, complete: true }

Install

Terminal
npm install numberparrot

Zero dependencies, ESM and CommonJS, TypeScript types included. Works in Node.js 18+, Deno, Bun and browsers. Try it in the playground.

More guides