Tuesday, July 17, 2012

Number to Persian letters text


Hey

In every financial systems we should show the letter version of numbers particularly in prints. It is also very useful for large numbers to understand it. For example if you see this number in digits "900025235" you can't catch how much it is exactly in short view but not in letters "Nine hundred million twenty five thousand two hundred and thirty five".

I think Persian digits letters are one of the most difficult ones in the different languages. Today we are trying to handle this. First take a look here:






1.Usage
Our mission is to change the input hint whenever its value is changed. To register that to your input you should pass these steps:
  1. include jquery framework to your page
  2. include number.to.persian.letters.js to your page
  3. register your input to the engine on this way:
        <script type="text/javascript"> 
            $(function() { 
                $(".toLetter").numberToLetter(); 
            }); 
        </script> 
    
According above scripts, all inputs with the class of "toLetter" register the "number to Persian letters" engine.

2.How it works
First take a look to the file number.to.persian.letters.js:
1    /** 
2     * number.to.persian.letters.js 
3     * @author: Mostafa Rastgar 
4     * @version: 1.0.0 
5     * 
6     * Created by Mostafa Rastgar on 2012-07-11. Please report any bug at http://prgassist.blogspot.com/ 
7     * 
8     * Copyright (c) 2012 Mostafa Rastgar  http://prgassist.blogspot.com/ 
9     * 
10    * 
11    */ 
12   $(function($) { 
13       var names = {"0": {value:"\u0635\u0641\u0631", scale:false}, 
14           "1": {value:"\u064a\u06a9", scale:false}, 
15           "2": {value:"\u062f\u0648", scale:false}, 
16           "3": {value:"\u0633\u0647", scale:false}, 
17           "4": {value:"\u0686\u0647\u0627\u0631", scale:false}, 
18           "5": {value:"\u067e\u0646\u062c", scale:false}, 
19           "6": {value:"\u0634\u0634", scale:false}, 
20           "7": {value:"\u0647\u0641\u062a", scale:false}, 
21           "8": {value:"\u0647\u0634\u062a", scale:false}, 
22           "9": {value:"\u0646\u0647", scale:false}, 
23           "10": {value:"\u062f\u0647", scale:false}, 
24           "11": {value:"\u064a\u0627\u0632\u062f\u0647", scale:false}, 
25           "12": {value:"\u062f\u0648\u0627\u0632\u062f\u0647", scale:false}, 
26           "13": {value:"\u0633\u064a\u0632\u062f\u0647", scale:false}, 
27           "14": {value:"\u0686\u0647\u0627\u0631\u062f\u0647", scale:false}, 
28           "15": {value:"\u067e\u0627\u0646\u0632\u062f\u0647", scale:false}, 
29           "16": {value:"\u0634\u0627\u0646\u0632\u062f\u0647", scale:false}, 
30           "17": {value:"\u0647\u0641\u062f\u0647", scale:false}, 
31           "18": {value:"\u0647\u062c\u062f\u0647", scale:false}, 
32           "19": {value:"\u0646\u0648\u0632\u062f\u0647", scale:false}, 
33           "20": {value:"\u0628\u064a\u0633\u062a", scale:false}, 
34           "30": {value:"\u0633\u064a", scale:false}, 
35           "40": {value:"\u0686\u0647\u0644", scale:false}, 
36           "50": {value:"\u067e\u0646\u062c\u0627\u0647", scale:false}, 
37           "60": {value:"\u0634\u0635\u062a", scale:false}, 
38           "70": {value:"\u0647\u0641\u062a\u0627\u062f", scale:false}, 
39           "80": {value:"\u0647\u0634\u062a\u0627\u062f", scale:false}, 
40           "90": {value:"\u0646\u0648\u062f", scale:false}, 
41           "100": {value:"\u0635\u062f", scale:false}, 
42           "200": {value:"\u062f\u0648\u064a\u0633\u062a", scale:false}, 
43           "300": {value:"\u0633\u064a\u0635\u062f", scale:false}, 
44           "400": {value:"\u0686\u0647\u0627\u0631\u0635\u062f", scale:false}, 
45           "500": {value:"\u067e\u0627\u0646\u0635\u062f", scale:false}, 
46           "600": {value:"\u0634\u0634\u0635\u062f", scale:false}, 
47           "700": {value:"\u0647\u0641\u062a\u0635\u062f", scale:false}, 
48           "800": {value:"\u0647\u0634\u062a\u0635\u062f", scale:false}, 
49           "900": {value:"\u0646\u0647\u0635\u062f", scale:false}, 
50           "1000": {value:"\u0647\u0632\u0627\u0631", scale:true}, 
51           "1000000": {value:"\u0645\u064a\u0644\u064a\u0648\u0646", scale:true}, 
52           "1000000000": {value:"\u0645\u064a\u0644\u064a\u0627\u0631\u062f", scale:true}, 
53           "1000000000000": {value:"\u062a\u064a\u0644\u064a\u0627\u0631\u062f", scale:true}, 
54           "1000000000000000": {value:"\u06a9\u0627\u062a\u064a\u0644\u064a\u0627\u0631\u062f", scale:true} 
55       }; 
56       $.fn.numberToLetter = function() { 
57           $(this).change(function() { 
58               numberChange($(this)); 
59           }); 
60           numberChange($(".toLetter")); 
61       } 
62       function numberChange(input){ 
63           var value = input.val(); 
64           input.attr({title:numberToLetter(value)}); 
65       } 
66       function numberToLetter(value) { 
67           value = value.replace(/(\d)(?=(\d\d\d)+(?!\d))/g, "$1,"); 
68           var parts = value.split(","); 
69           var textParts = new Array(); 
70           for (var i = 0; i < parts.length; i++) { 
71               var setScale = renderNumber(parts[i], textParts); 
72               if (setScale) { 
73                   renderScale((parts.length - i - 1), textParts); 
74               } 
75           } 
76    
77           var text = concatNumberTexts(textParts); 
78           if (text == "" && value != "") { 
79               return names["0"].value; 
80           } else { 
81               return text; 
82           } 
83       } 
84    
85       function concatNumberTexts(renderRepository) { 
86           var text = ""; 
87           for (var i = 0; i < renderRepository.length; i++) { 
88               if (text != "") { 
89                   text += " "; 
90               } 
91               if (text != "" && allowComman(renderRepository[i])) { 
92                   text += "\u0648 "; 
93               } 
94               text += renderRepository[i]; 
95           } 
96           return text; 
97       } 
98    
99       function renderScale(scaleIndex, renderRepository) { 
100          var section = pow(scaleIndex * 3); 
101          if (section > 1) { 
102              renderRepository.push(names[section.toString()].value); 
103          } 
104      } 
105   
106      function renderTwoLengthNumber(num1, num2, renderRepository, setScale) { 
107          if (num1 == "1") { 
108              renderRepository.push(names[num1 + num2].value); 
109              setScale = true; 
110          } else { 
111              if (num1 != "0") { 
112                  renderRepository.push(names[num1 + "0"].value); 
113                  setScale = true; 
114              } 
115              if (num2 != "0") { 
116                  renderRepository.push(names[num2].value); 
117                  setScale = true; 
118              } 
119          } 
120          return setScale; 
121      } 
122   
123      function renderNumber(digit, renderRepository) { 
124          var setScale = false; 
125          if (digit.length > 2) { 
126              if (digit.charAt(0) != "0") { 
127                  renderRepository.push(names[digit.charAt(0) + "00"].value); 
128                  setScale = true; 
129              } 
130              setScale = renderTwoLengthNumber(digit.charAt(1), digit.charAt(2), renderRepository, setScale); 
131          } else if (digit.length > 1) { 
132              setScale = renderTwoLengthNumber(digit.charAt(0), digit.charAt(1), renderRepository, setScale); 
133          } else if (digit.length > 0) { 
134              if (digit.charAt(0) != "0") { 
135                  renderRepository.push(names[digit.charAt(0)].value); 
136                  setScale = true; 
137              } 
138          } 
139          return setScale; 
140      } 
141   
142      function allowComman(txt) { 
143          for(var key in names){ 
144              if(names[key].value == txt){ 
145                  return !names[key].scale; 
146              } 
147          } 
148          return true; 
149      } 
150   
151      function pow(powNum) { 
152          var pow = 1; 
153          for (var j = 0; j < powNum; j++) { 
154              pow = pow * 10; 
155          } 
156          return pow; 
157      } 
158  }); 
159  
we should have a map with digit key and the value is the written version of the key. This map is for translation usage(line 13).
2.1 numberToLetter function (line 66)
This is the major function to handle the job. In line 67 we categorize every three digits near together into a category. This is the scales like thousand, million, billion, ... . parts in line 68 is an array of 3 digits. textParts in line 69 is the translated version of parts. renderNumber in line 71 will fill textParts(refer to 2.2). finally the textParts items should be joined together in line 77(refer to 2.4).

2.2 renderNumber function (line 123)
This function renders the letter and fill it to the 'renderRepository' parameter. Then returns a boolean to say if the scale should be rendered or not. If 3 zeros are near to each other the scale should not be rendered. For example 324000122.

2.3 renderTwoLengthNumber function (line 106)
This function should render two digits (num1, num2). There is a lot of exceptions between 10-20 in Persian. So we should handle this in here(lines 107,108). This function also fill the rendered text into the 'renderRepository'.

2.4 concatNumberTexts function (line 85)
This function concats the 'renderRepository' parameter items(textParts variable in numberToLetter function) together and returns full text. In Persian we shouldn't put 'and' after scales. So we should handle it here(lines 91,92).

You can download the client sample from here.