Introduction
In this article, I will talk about converting numbers to written words in English & Arabic (Tafqeet as pronounced in Arabic).
Background
I was in the middle of programming my project when I needed a code to convert a number to written text.
Our project is mulltilingual, so I needed two versions: one for English and one for Arabic.
I googled “Number to Text” or “Number to Word” and I found a lot of code to convert number to written words for English language and many of them worked fine.
I also found code to convert number to words for Arabic language, but unfortunately many of them didn’t work as expected!
So I have decided to go the long way to build a class to do the conversion to Arabic & also added English language support.
Here is a screen shot of a test project that uses the class:
Using the Code
I have to mention that I used code written by Justin Rogers from this link as a start: http://weblogs.asp.net/justin_rogers/articles/151757.aspx
This article contains code that does a good convert for a decimal value into its equivalent English words, but numbers from my application were actually money amounts, so I needed to add currency names to it (our project is also multinational, so I had to build the string dynamically from the country’s currency).
This was the easy part after I made a few small changes to Justin’s code to fulfill my English-Number-To-Words requirements.
Now comes the complicated part: converting numbers to written words in Arabic language!
Arabic language is very complicated, and when it comes to converting it to words, I think it is the most difficult language because it has so many rules that depend on the “number” state and the “counted” state.
For example:
The number one & two matches the counted in its feminine state, where the numbers from 3 to 10 are contrary to the counted, etc.
So I have used one method to determine a digit feminine status using its group level & Currency Name Feminine field status. Here is the method:
private string GetDigitFeminineStatus(int digit, int groupLevel)
{
if (groupLevel == -1)
{ if (Currency.IsCurrencyPartNameFeminine)
return arabicFeminineOnes[digit]; else
return arabicOnes[digit];
}
else
if (groupLevel == 0)
{
if (Currency.IsCurrencyNameFeminine)
return arabicFeminineOnes[digit]; else
return arabicOnes[digit];
}
else
return arabicOnes[digit];
}
The whole convert process depends on dividing the number into Group Levels. Each group contains 3 digits & these levels are numbered as in this example:
Number: 987,654,321.345
345: Group Level -1
321: Group Level 0
654: Group Level 1
987: Group Level 2
This is accomplished by this code:
Byte group = 0;
while (tempNumber >= 1)
{
int numberToProcess = (int)(tempNumber % 1000);
tempNumber = tempNumber / 1000;
string groupDescription = ProcessArabicGroup
(numberToProcess, group, Math.Floor(tempNumber));
group++;
}
Then we have to process each group & convert it to its text depending on its group level.
Inside the ProcesArabicGroup
method, we check for the special case for 200
as it has its special rule in Arabic:
if (hundreds > 0)
{
if (tens == 0 && hundreds == 2) retVal = String.Format("{0}", arabicAppendedTwos[0]);
else retVal = String.Format("{0}", arabicHundreds[hundreds]);
}
Also the number 2 has another special rule which is discussed by this code:
if (tens == 2 && hundreds == 0 && groupLevel > 0)
{ if (_intergerValue == 2000 || _intergerValue == 2000000 ||
_intergerValue == 2000000000 || _intergerValue == 2000000000000 ||
_intergerValue == 2000000000000000 || _intergerValue == 2000000000000000000)
retVal = String.Format("{0}", arabicAppendedTwos[groupLevel]); else
retVal = String.Format("{0}", arabicTwos[groupLevel]);}
For group numbers over 20, it is considered as complicated of 2 digits so each one will get its own converted text.
int ones = tens % 10;
tens = (tens / 10) - 2;
if (ones > 0)
{
if (retVal != String.Empty)
retVal += " ? ";
retVal += GetDigitFeminineStatus(ones, groupLevel);
}
if (retVal != String.Empty)
retVal += " ? ";
retVal += arabicTens[tens];
Also Arabic language is sensitive to currency names which also depends on the number state so I had to add four different names for the same currency name to support all states. Their fields are used at the end of the method when constructing the final String
:
if (_intergerValue != 0)
{ int remaining100 = (int)(_intergerValue % 100);
if (remaining100 == 0)
formattedNumber += Currency.Arabic1CurrencyName;
else
if (remaining100 == 1)
formattedNumber += Currency.Arabic1CurrencyName;
else
if (remaining100 == 2)
{
if (_intergerValue == 2)
formattedNumber += Currency.Arabic2CurrencyName;
else
formattedNumber += Currency.Arabic1CurrencyName;
}
else
if (remaining100 >= 3 && remaining100 <= 10)
formattedNumber += Currency.Arabic310CurrencyName;
else
if (remaining100 >= 11 && remaining100 <= 99)
formattedNumber += Currency.Arabic1199CurrencyName;
I have created one class to simplify the process, it is called CurrencyInfo
which contains the definition for Currency
object. It has the following properties:
CurrencyID = 0;
|
Just ID
|
CurrencyCode = "SYP";
|
Its Code
|
IsCurrencyNameFeminine = true;
|
Is the currency name feminine or no?
|
EnglishCurrencyName = "Syrian Pound";
|
Like: one Syrian Pound
|
EnglishPluralCurrencyName = "Syrian Pounds";
|
Like: thirty four Syrian Pounds
|
EnglishCurrencyPartName = "Piaster";
|
For the decimal part
|
EnglishPluralCurrencyPartName = "Piasteres";
|
For the decimal part
|
Arabic1CurrencyName = "???? ?????";
|
For the number one in Arabic
|
Arabic2CurrencyName = "?????? ???????";
|
For the number two in Arabic
|
Arabic310CurrencyName = "????? ?????";
|
For numbers between 3 and 10 in Arabic
|
Arabic1199CurrencyName = "???? ?????";
|
For numbers above 11 in Arabic
|
Arabic1CurrencyPartName = "???";
|
For the decimal part in Arabic
|
Arabic2CurrencyPartName = "?????";
|
For the decimal part in Arabic
|
Arabic310CurrencyPartName = "????";
|
For the decimal part in Arabic
|
Arabic1199CurrencyPartName = "?????";
|
For the decimal part in Arabic
|
PartPrecision = 2;
|
Part precision, like: 1 Syrian Pound = 100 Piasters ( 2 is number of Zeros)
|
IsCurrencyPartNameFeminine
|
Is the currency part name feminine or no?
|
As you can see from the table, it is very easy to setup any other currency by filling the specified fields.
Points of Interest
I hope this was helpful to you.
Please feel free to correct/suggest any modifications to the code.
History
- 27th September, 2010: Initial post
- 9th April, 2011: Fixed a small issue (noted by one member) in the solution attached to the article
- 5th May, 2011: Added VB.NET equivalent code for the article
- 17th February, 2012: Fixed a small bug in the code