29

I want to convert every number in the html content to Persian numerals without other effects on page elements.

For example:

<div style='color: #c2c2c2'>
  text number 1
  <span>text number 2</span>
  <div>
    text number 3
    <b>text number 4</b>
    <a href='#page2'>text number 5</a>
  </div>
</div>

be converted to:

<div style='color: #c2c2c2'>
  text number ۱
  <span>text number ۲</span>
  <div>
    text number ۳
    <b>text number ۴</b>
    <a href='#page2'>text number ۵</a>
  </div>
</div>
let persian = array('۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹');
let english = array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9');

Thanks.

Mahozad
  • 18,032
  • 13
  • 118
  • 133
Alireza
  • 1,428
  • 4
  • 21
  • 33
  • Why would you do that client-side (as your tags imply), instead of generating the desired content when producing the HTML document in the first place? – Jukka K. Korpela Mar 06 '13 at 13:07
  • I know do it server side is simpler but i have to do it client side with javascript – Alireza Mar 06 '13 at 13:20

14 Answers14

34

You can use this method: (http://jsfiddle.net/A4NfG/1/)

persian={0:'۰',1:'۱',2:'۲',3:'۳',4:'۴',5:'۵',6:'۶',7:'۷',8:'۸',9:'۹'};
function traverse(el){
    if(el.nodeType==3){
        var list=el.data.match(/[0-9]/g);
        if(list!=null && list.length!=0){
            for(var i=0;i<list.length;i++)
                el.data=el.data.replace(list[i],persian[list[i]]);
        }
    }
    for(var i=0;i<el.childNodes.length;i++){
        traverse(el.childNodes[i]);
    }
}
haitaka
  • 1,832
  • 12
  • 21
8

There is this findAndReplaceDOMText.js that may help you. It walks through all nodes in the document (as opposed to all elements) and replaces the text when the nodeType argument equals 3, which is TEXT_NODE.

This example will replace numbers in the whole page:

function walkNode(node) { 
    if (node.nodeType == 3) {
        // Do your replacement here
        node.data = node.data.replace(/\d/g,convert);
    }
        
    // Also replace text in child nodes
    for (var i = 0; i < node.childNodes.length; i++) {
        walkNode(node.childNodes[i]); 
    }
}
    
walkNode(document.getElementsByTagName('body')[0]);

function convert(a){
    return ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'][a];
}

See JavaScript String.replace documentation here.

Mahozad
  • 18,032
  • 13
  • 118
  • 133
Sjoerd
  • 74,049
  • 16
  • 131
  • 175
5

If you want to select some elements by a selector, you can use this simple code (JsFiddle):

persian={0:'۰',1:'۱',2:'۲',3:'۳',4:'۴',5:'۵',6:'۶',7:'۷',8:'۸',9:'۹'};
$(".persian-digit").each(function(){
    for(var i=0;i<=9;i++) {
        var re = new RegExp(i,"g");
        $(this).html($(this).html().replace(re,persian[i]));
    }
});

Then for use it:

<span class="persian-digit">This span contains persian digits (0123456789),</span>
<span>and this one contains english digits (0123456789).</span>
Navid Farhadi
  • 3,397
  • 2
  • 28
  • 34
5

I wrote this short and simple code.

// ES6
const regex = /[۰-۹]/g
let str = '۰۱۲۳۵۴۸۹۰۷۸۹۰۱۲';
let result = str.replace(regex, function (w) {
    return String.fromCharCode(w.charCodeAt(0) - 1728)
  }
)

console.log(result);
Masoud
  • 1,008
  • 1
  • 9
  • 22
4

ES6 one-liner

Functions based on @mcrunix's answer:

fa2enDigits Returns string with Persian digits replaced with English digits:

const fa2enDigits = s => s.replace(/[۰-۹]/g, w => String.fromCharCode(w.charCodeAt(0) - 1728) )

en2faDigits Returns string with English digits replaced with Persian digits:

const en2faDigits = s => s.replace(/[0-9]/g, w => String.fromCharCode(w.charCodeAt(0) + 1728) )

examples:

const fa2enDigits = s => s.replace(/[۰-۹]/g, w => String.fromCharCode(w.charCodeAt(0) - 1728) )

const en2faDigits = s => s.replace(/[0-9]/g, w => String.fromCharCode(w.charCodeAt(0) + 1728) )


console.log('Tel: ۰۹۱۲', '=>' , fa2enDigits('Tel: ۰۹۱۲') );
console.log('Tel: 0912', '=>' , en2faDigits('Tel: 0912') );
Positivity
  • 5,406
  • 6
  • 41
  • 61
2
<script>

    function en2fa(num){
        let arr = [];
        persian = {0:'۰',1:'۱',2:'۲',3:'۳',4:'۴',5:'۵',6:'۶',7:'۷',8:'۸',9:'۹'};
        num.split('').map((number,index)=>{
            arr[index] = (persian[number]);
        });
        return arr.join('');
    }

    console.log(en2fa("86598231452"));

</script>

test this code in codepen

https://codepen.io/bashirpour/pen/JvgeYy

Mahdi Bashirpour
  • 17,147
  • 12
  • 117
  • 144
1

If you wanna use it on a specific element or selector, this might help :

var persian = Array('۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹');
   function replaceDigits(selector) {

      for(i=0;i<10;i++){

         var regex=eval("/"+i+"/g");

         $(selector).html($(selector).html().replace(regex,persian[i]));

      }

   }

replaceDigits(".selected");

Worked for me!

Behzad
  • 425
  • 4
  • 10
1

You can use Number.toLocaleString() to convert and format a single number.

let myNumberStr = "123456.7890";
let myNumber = Number(myNumberStr); // Convert from string to number
let persian = myNumber.toLocaleString("fa"); // OR "fa-IR" for IRAN

The more advanced and complete answer (convert all numbers in page)

localizeNumbers(document.getElementsByTagName('body')[0]);

function localizeNumbers(node) { 
    if (node.nodeType == Node.TEXT_NODE) {
        // Use this if target numbers may contain comma as thousands separators
        node.data = node.data.replace(/([,\d]*\.?\d+)/g, localize);
        // Use this if you do not want to treat comma as thousands separator
        // node.data = node.data.replace(/(\d*\.?\d+)/g, localize);
    }
    // Also, replace numbers in child nodes
    node.childNodes.forEach(localizeNumbers);
}

function localize(numberString) {
  // Convert from string to number
  // Use this if target numbers may contain comma as thousands separators
  let number = Number(numberString.replace(/[^\d.]/g, ""));
  // Use this if you do not want to treat comma as thousands separator
  // let number = Number(numberString);

  // Could also have used "fa-IR" for IRAN
  let persian = number.toLocaleString("fa", { maximumFractionDigits: 10 });
  // Could also have used "en-US" for USA
  let english = number.toLocaleString("en", { maximumFractionDigits: 10 });
  // Could also have used "ar" (which seems to use western digits). "EG" is for Egypt
  let arabic = number.toLocaleString("ar-EG", { maximumFractionDigits: 10 });

  console.log(`Persian: ${persian}`);
  console.log(`English: ${english}`);
  console.log(`Arabic: ${arabic}\n`);

  return persian;
}

localizeNumbers(document.getElementsByTagName('body')[0]);

function localizeNumbers(node) { 
  if (node.nodeType == Node.TEXT_NODE) {
    node.data = node.data.replace(/([,\d]*\.?\d+)/g, localize);
  }
  node.childNodes.forEach(localizeNumbers);
}

function localize(numberString) {
  let number = Number(numberString.replace(/[^\d.]/g, ""));
  let persian = number.toLocaleString("fa", { maximumFractionDigits: 10 });
  let english = number.toLocaleString("en", { maximumFractionDigits: 10 });
  let arabic = number.toLocaleString("ar-EG", { maximumFractionDigits: 10 });

  console.log(`Persian: ${persian}`);
  console.log(`English: ${english}`);
  console.log(`Arabic: ${arabic}\n`);

  return persian;
}
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Test</title>
<script src="script.js" defer></script>
</head>
<body>


  <div style='color: #c2c2c2'>
    text number 1
    <span>text number 2</span>
    <div>
      text number 3
      <b>text number 4</b>
      <a href='#page2'>text number 5</a>
    </div>
  </div>


  1234
  <div>
    1234
    <p>1234</p>
  </div>
  <p>1234</p>
  <p>1,234</p>
  <p>1234.5678</p>
  <p>1,234.5678</p>
  <p>0.1234</p>
  <p>.1234</p>
  <p>abc1234</p>
  <p>abc1,234</p>
  <p>abc1234def</p>
  <p>abc1,234def</p>
  <p>abc 1234def</p>
  <p>abc 1,234def</p>
  <p>abc 1234 def</p>
  <p>abc 1,234 def</p>
  <p>abc1234.567</p>
  <p>abc1,234.567</p>
  <p>abc1234.567def</p>
  <p>abc1,234.567def</p>
  <p>abc 1234.567def</p>
  <p>abc 1,234.567def</p>
  <p>abc 1234.567 def</p>
  <p>abc 1,234.567 def</p>
</body>
</html>

Notes

  1. The solution will treat numbers as Numbers not as Strings. If you do not specify the maximumFractionDigits in the options object of toLocaleString, the formatted number my be rounded. See this post.

  2. To omit thousands separators in the formatted number, add useGrouping: false in the options object of toLocaleString. Refer to MDN Web Docs: Intl.NumberFormat() and W3Schools: JavaScript Number toLocaleString() to see all the other available options.

  3. The solution will format the number with correct Persian thousands separator and decimal separator (called momayyez). With the default browser fonts and some other fonts, both of those characters may look similar to a Latin comma (,) but they are actually three different characters with three different Unicode values:

    Name Unicode value Rendered
    Latin comma U+002C ,
    Arabic/Persian thousands separator U+066C ٬
    Arabic/Persian decimal separator U+066B ٫
  4. The code assumes that all the original numbers in the HTML are in English format (which uses comma for thousands separator and dot for decimal separator).

I used a modified version of Sjoerd answer here. Thanks to him for his answer.

For a list of different common numerals see this Wikipedia article. Also, see this post and this post. You could also use persian.js library to do this all.

Mahozad
  • 18,032
  • 13
  • 118
  • 133
1

just put it in a span or p tag with specific class and change the font of that class to a persian font like 'B yekan'. i've tested.worked.

0

Another Simple way:

String.prototype.toEnglishDigits = function () {
     var charCodeZero = '۰'.charCodeAt(0);

     return this.replace(/[0-9]/g, function (w) {
         return String.fromCharCode(parseInt(w) + charCodeZero);
      });
}
Mehran Prs
  • 509
  • 4
  • 18
0

all of the ways are not confident because lots of Persian users, use Arabic keyboard and Arabic chars are different with Persian numbers.

it's better to replace Arabic chars too.

for example this is zero char in Persian: ۰ and this is zero char in Arabic: ٠

as you see, both are the same but their Unicode are not the same. if you use Persian in your project, i recommend you persian.js, it's just 5 KB.

or if you don't want to use this library you can use this code:

function convertToPersianNumber(value) {
  const arabicNumbers = ['٠', '١', '٢', '٣', '٤', '٥', '٦', '٧', '٨', '٩'];
  const persianNumbers = ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'];
  const englishNumbers = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9];

  let confidentValue = value;
  for (var i = 0; i < value.length; i++) {
    const inArabicIndex = arabicNumbers.indexOf(value[i]);
    if (inArabicIndex > -1) {
    confidentValue = confidentValue.replace(
      value[i],
      englishNumbers[inArabicIndex]
    );
  }

  const inPersianIndex = persianNumbers.indexOf(value[i]);
  if (inPersianIndex > -1) {
      confidentValue = confidentValue.replace(
        value[i],
        englishNumbers[inPersianIndex]
      );
    }
  }
  return confidentValue;
}
Hamid Tanhaei
  • 29
  • 1
  • 8
0
  try {
    if (!enDigit &&  !enDigit.toString().length)
      return "";

  let enDigitString = typeof enDigit === 'number' ? enDigit.toString() : enDigit;

  var newValue="";
  for (var i=0;i<enDigitString.length;i++)
  {
      var ch=enDigitString.charCodeAt(i);
      if (ch>=48 && ch<=57)
      {
          // european digit range
          var newChar=ch+1728;
          newValue=newValue+String.fromCharCode(newChar);
      }
      else
          newValue=newValue+String.fromCharCode(ch);
  }
  return newValue;

  }catch (e) {
    return "";
  }
0

Just a bit more clear snippet:

function numbersToPersian (el) {
    ['۰', '۱', '۲', '۳', '۴', '۵', '۶', '۷', '۸', '۹'].forEach((num, index) => {
        el.textContent = el.textContent.replace(new RegExp(index, 'g'), num);
    });
};

function traverseToLeaf (el) {
    if (el.childNodes.length === 0) return;

    el
        .childNodes
        .forEach(node => {
            if (node.nodeType === 3) {
                numbersToPersian(node);
                } else {
                traverseToLeaf(node);
            }
        });
};

// just write your element class or ID instead of '.primary' in line below
traverseToLeaf(document.querySelector('.primary'));
webgodo
  • 81
  • 8
0
function persianNumber(string) {
    return string.split('0').join('۰')
    .split('1').join('۱')
    .split('2').join('۲')
    .split('3').join('۳')
    .split('4').join('۴')
    .split('5').join('۵')
    .split('6').join('۶')
    .split('7').join('۷')
    .split('8').join('۸')
    .split('9').join('۹')
}
Majid Karimizadeh
  • 98
  • 1
  • 3
  • 10