Number To Word (Arabic Version)






4.89/5 (42 votes)
An article that describes how to convert a number to written words in English and Arabic
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 it is in the decimal part
if (Currency.IsCurrencyPartNameFeminine)
return arabicFeminineOnes[digit]; // use feminine field
else
return arabicOnes[digit];
}
else
if (groupLevel == 0)
{
if (Currency.IsCurrencyNameFeminine)
return arabicFeminineOnes[digit];// use feminine field
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)
{
// separate number into groups
int numberToProcess = (int)(tempNumber % 1000);
tempNumber = tempNumber / 1000;
// convert group into its text
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)
{ // This is special case for number 2 when it comes alone in the group
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; // 20's offset
if (ones > 0)
{
if (retVal != String.Empty)
retVal += " ? ";
// Get Feminine status for this digit
retVal += GetDigitFeminineStatus(ones, groupLevel);
}
if (retVal != String.Empty)
retVal += " ? ";
// Get Tens text
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)
{ // here we add currency name depending on _intergerValue : 1 ,2 , 3--->10 , 11--->99
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