28

I'm trying to use a DOM coming from an external source, and in it there are some numeric values in Hindi/Arabic transcription, like "۱۶۶۰", and when I want to convert it into numeric value I get NaN.

What's wrong here?

A small code snippet to be tried:

alert(Number("۱۶۶۰") + ' - ' + Number("1660"));
Mahozad
  • 18,032
  • 13
  • 118
  • 133
  • 3
    I tried your code in Chrome's console, and I got `TypeError: Object function Number() { [native code] } has no method 'parseLocale'`. No luck searching Mozilla's documentation either. Is that an IE only thing? – Geeky Guy Jun 10 '13 at 13:25
  • 1
    No need to use `parseLocale` method. – Saeed Neamati Jun 10 '13 at 13:27
  • 2
    JavaScript doesn't [yet](http://wiki.ecmascript.org/doku.php?id=globalization:globalization) [support](http://es5.github.io/#x7.8.3) [parsing](http://es5.github.io/#x15.1.2.2) such numbers. – Jonathan Lonowski Jun 10 '13 at 13:38

3 Answers3

55

Well, the Number function does expect the digits 0 to 9 and does not handle arabic ones.

You will need to take care of that yourself:

function parseArabic(str) {
  return Number(str
    .replace(/[٠١٢٣٤٥٦٧٨٩]/g, d => d.charCodeAt(0) - 1632) // convert Arabic digits
    .replace(/[۰۱۲۳۴۵۶۷۸۹]/g, d => d.charCodeAt(0) - 1776) // convert Persian digits
  );
}
// usage example:
console.log( parseArabic("۱۶۶۰") )
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • 1
    The first number set is Arabic, the second one is [Persian](http://sartre2.byu.edu/persian/persianword/numbers.htm). – Ahmad Ahmadi Sep 21 '16 at 07:54
  • 1
    Because I couldn't find any answer for the other way around, here it is (Arabic only) function eurToArab(num) { return num.toString().replace(/\d/g, function(d) { return String.fromCharCode(parseInt(d[0], 10) + 1632); }); } – Haroen Viaene Sep 04 '18 at 16:43
  • Are there any examples of number systems that do not use Base 10 that we may need to handle? – Richard Corfield May 10 '21 at 11:02
  • @RichardCorfield [None that would matter](https://en.wikipedia.org/wiki/List_of_numeral_systems), at least not in this question – Bergi May 10 '21 at 11:11
15

I would suggest you handle it at a lower level: replace the Arabic digits with the corresponding ASCII digits and then convert.

For example:

>a='\u0661\u0666\u0666\u0660'
"١٦٦٠"
>b='\u06f1\u06f6\u06f6\u06f0'
"۱۶۶۰"
>r=/[\u0660-\u0669\u06F0-\u06F9]/g;
/[\u0660-\u0669\u06F0-\u06F9]/g
>a.replace(r,function(c) { return '0123456789'[c.charCodeAt(0)&0xf]; } )
"1660"
>b.replace(r,function(c) { return '0123456789'[c.charCodeAt(0)&0xf]; } )
"1660"
Jason S
  • 184,598
  • 164
  • 608
  • 970
0

Here is a function called paserNumber that converts a string representing a number into an actual JS Number object. It can also accept number strings with fractions (decimal numbers) and Arabic/Persian/English thousands separators. I don't know whether this solution is the best, performance-wise.

function parseNumber(numberText: string) {
    return Number(
        // Convert Persian (and Arabic) digits to Latin digits
        normalizeDigits(numberText)
        // Convert Persian/Arabic decimal separator to English decimal separator (dot)
        .replace(/٫/g, ".")
        // Remove other characters such as thousands separators
        .replace(/[^\d.]/g, "")
    );
}

const persianDigitsRegex = [/۰/g, /۱/g, /۲/g, /۳/g, /۴/g, /۵/g, /۶/g, /۷/g, /۸/g, /۹/g];
const arabicDigitsRegex = [/٠/g, /١/g, /٢/g, /٣/g, /٤/g, /٥/g, /٦/g, /٧/g, /٨/g, /٩/g];

function normalizeDigits(text: string) {
    for (let i = 0; i < 10; i++) {
        text = text
                .replace(persianDigitsRegex[i], i.toString())
                .replace(arabicDigitsRegex[i], i.toString());
    }
    return text;
}

Note that the parse function is quite forgiving and the number string can be a combination of Persian/Arabic/Latin numerals and separators.

Side note

After getting a Number you can format it back however you want with Number.toLocaleString function:

let numberString = "۱۲۳۴.5678";
let number = parseNumber(numberString);
val formatted1 = number.toLocaleString("fa"); // OR "fa-IR" for IRAN
val formatted2 = number.toLocaleString("en"); // OR "en-US" for USA
val formatted3 = number.toLocaleString("ar-EG"); // OR "ar" which uses western numerals

For more information about formatting numbers, refer to this answer.

Mahozad
  • 18,032
  • 13
  • 118
  • 133