Convert Unicode Number Decimal Digit to ASCII, revisited

I first published a variant of this function in August 29, 2012. Since then a couple of new ranges have been added to the Unicode standard.

The function below has support for Unicode class Nd (Number, Decimal Digit): Adlam, Ahom, Arabic-indic, Balinese, Bengali, Bhaiksuki, Brahmi, Chakma, Cham, Devanagari, Extended Arabic-indic, Fullwidth, Gujarati, Gurmukhi, Javanese, Kannada, Kayah Li, Khmer, Khudawadi, Lao, Lepcha, Limbu, Malayalam, Mathematical Bold, Mathematical Double-struck, Mathematical Monospace, Mathematical Sans-serif Bold, Mathematical Sans-serif, Meetei Mayek, Modi, Mongolian, Mro, Myanmar Shan, Myanmar Tai Laing, Myanmar, New Tai Lue, Newa, Nko, Ol Chiki, Oriya, Osmanya, Pahawh Hmong, Saurashtra, Sharada, Sinhala Lith, Sora Sompeng, Sundanese, Tai Tham Hora, Tai Tham Tham, Takri, Tamil, Telugu, Thai, Tibetan, Tirhuta, Vai, Warang Citi.

There is no support for roman numbers and other non-decimal numbers, because they are not decimal.

This function was fist published as an answer on Stackoverflow.
It is also available for node.js as a module called unicodedigits and on github.

// This function takes an UTF16 encoded string as input,
// and returns with all suported digits from Unicode
// class 'Nd' (Number, Decimal Digit) replaced with their
// equivalent ASCII digit.
// Source :
// License: MIT
// Author :
// Note   : If you are going to use this code I would appreciate to
//          get an email to You don't have to but
//          it would make me happier!
var digitsToASCII=
  (function () {
    // Regexp that matches all supported digits.
    // Most Unicode digit classes have the zero digit at a codepoint
    // where the four least significant bits are ether zero or six.
    // The notable exception is the Math-class where several classes
    // have sequential codepoints. The information about the offset
    // is needed when decoding, and by using groups in the RexExp
    // no lookup is needed.
    var reDigit = new RegExp(
      '('+ // Offset 0
      ')|('+ // Offset 6
      ')|('+ // Math

    function replace(match, offset0, offset6, offsetMath) {
      // 'match' contains the whole match and can therefore have
      // a length longer than one character if surrogate pairs is used.
      // By getting the last character from 'match' the operation is simplified.
      var raw = match.charCodeAt( match.length - 1);
      var digit =
        offset0 ? raw & 0xF : // use 4 bits
        offset6 ? (raw -6) & 0xF : // subtract 6, use 4 bits
        offsetMath ? ((raw - 0xCE) & 0x3F) % 10 : // subtract CE, use 6 bits

      return String.fromCharCode(48 + digit); // Digit to ASCII

    return function replaceDigits(input) {
      return input.replace(reDigit, replace);

Posted in javascript | Leave a comment