It may happen that we need to display the numbers in words, for instance 1= un, 2016 = deux mille seize, etc.
The objective of this document is to provide a way to translate numbers into French words.
The formula described in this document supports number until 15 digits so the maximum value supported is 999999999999999.
So it’s large enough and if you need to have more digits then you can enrich the formula by adding new positions. Moreover, the formula supports positive and negative numbers.
To summarize, this Web Intelligence formula support units, hundreds, thousands, millions, billions, and trillions.
Prior to describe the formulas used in the document here are screenshots of translated numbers.

To do it we need 2 variables, but we can create more variables to simplify the formula that does the translation.
The first variable describes the units and is named [French units]. The variable is similar to a table of string where there is the corresponding word for a number. “zero” is not used because it only appears where the number is equal to 0 so there is a specific condition for it.
Here is the formula:
="#0##1# un#01# un#2# deux#02# deux#3# trois#03# trois#4# quatre#04# quatre#5# cinq#05# cinq#6# six#06# six#7# sept#07# sept#8# huit#08# huit#9# neuf#09# neuf#10# dix#11# onze#12# douze#13# treize#14# quatorze#15# quinze#16# seize#17# dix-sept#18# dix-huit#19# dix-neuf#20# vingt#21# vingt-et-un#22# vingt-deux#23# vingt-trois#24# vingt-quatre#25# vingt-cinq#26# vingt-six#27# vingt-sept#28# vingt-huit#29# vingt-neuf#30# trente#31# trente-et-un#32# trente-deux#33# trente-trois#34# trente-quatre#35# trente-cinq#36# trente-six#37# trente-sept#38# trente-huit#39# trente-neuf#40# quarante#41# quarante-et-un#42# quarante-deux#43# quarante-trois#44# quarante-quatre#45# quarante-cinq#46# quarante-six#47# quarante-sept#48# quarante-huit#49# quarante-neuf#50# cinquante#51# cinquante-et-un#52# cinquante-deux#53# cinquante-trois#54# cinquante-quatre#55# cinquante-cinq#56# cinquante-six#57# cinquante-sept#58# cinquante-huit#59# cinquante-neuf#60# soixante#61# soixante-et-un#62# soixante-deux#63# soixante-trois#64# soixante-quatre#65# soixante-cinq#66# soixante-six#67# soixante-sept#68# soixante-huit#69# soixante-neuf#70# soixante-dix#71# soixante-et-onze#72# soixante-douze#73# soixante-treize#74# soixante-quatorze#75# soixante-quinze#76# soixante-seize#77# soixante-dix-sept#78# soixante-dix-huit#79# soixante-dix-neuf#80# quatre-vingt#81# quatre-vingt-un#82# quatre-vingt-deux#83# quatre-vingt-trois#84# quatre-vingt-quatre#85# quatre-vingt-cinq#86# quatre-vingt-six#87# quatre-vingt-sept#88# quatre-vingt-huit#89# quatre-vingt-neuf#90# quatre-vingt-dix#91# quatre-vingt-onze#92# quatre-vingt-douze#93# quatre-vingt-treize#94# quatre-vingt-quatorze#95# quatre-vingt-quinze#96# quatre-vingt-seize#97# quatre-vingt-dix-sept#98# quatre-vingt-dix-huit#99# quatre-vingt-dix-neuf#"
As you can notice there is separators (#) between each number and its associated word.
Now we just need the variable that does the conversion. This formula is complex so I added comments to explain what is the need of each paragraph (Main if statement).
To simplify the formula, you can create an intermediate variable and replace the following formula by the variable name you have created. This formula is used to format the number and remove the potential decimals and minus sign. The formula is used 84 times in the numbers to words translation:
FormatNumber(Abs([My Number]);"0")
[My Number] is the numeric variable to translate into words.
Now here is the formula used for the translation:
=if Length(FormatNumber(Abs([My Number]);"0")) > 15 then "*****" /* Too large */
else if FormatNumber(Abs([My Number]);"0") = "0" then "zero" /* Zero */
else (if [My Number] < 0 then "moins ")
+ Trim(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(
/* hundred of trillion */
(if Length(FormatNumber(Abs([My Number]);"0")) > 14 then
if Left(Right(FormatNumber(Abs([My Number]);"0");15);1) = "1" then " cent"
else if Left(Right(FormatNumber(Abs([My Number]);"0");15);1) <> "0" then
Substr(Substr([French units]; Pos([French units];"#" + Left(Right(FormatNumber(Abs([My Number]);"0");15);1) + "#") + Length(Left(Right(FormatNumber(Abs([My Number]);"0");15);1))+2; Length([French units]));1 ; Pos(Substr([French units]; Pos([French units];"#" + Left(Right(FormatNumber(Abs([My Number]);"0");15);1) + "#") + Length(Left(Right(FormatNumber(Abs([My Number]);"0");15);1))+2 ; Length([French units])); "#")-1 ) + " cent"
else "")
/* trillion */
+ (if Length(FormatNumber(Abs([My Number]);"0")) > 13 then
if Left(Right(FormatNumber(Abs([My Number]);"0");14);2) <> "00" then
Substr(Substr([French units]; Pos([French units];"#" + Left(Right(FormatNumber(Abs([My Number]);"0");14);2) + "#") + Length(Left(Right(FormatNumber(Abs([My Number]);"0");14);2))+2; Length([French units]));1 ; Pos(Substr([French units]; Pos([French units];"#" + Left(Right(FormatNumber(Abs([My Number]);"0");14);2) + "#") + Length(Left(Right(FormatNumber(Abs([My Number]);"0");14);2))+2 ; Length([French units])); "#")-1 ) + " billions"
else " billions"
else if Length(FormatNumber(Abs([My Number]);"0")) > 12 then
if Left(FormatNumber(Abs([My Number]);"0");1) <> "0" then
Substr(Substr([French units]; Pos([French units];"#" + Left(FormatNumber(Abs([My Number]);"0");1) + "#") + Length(Left(FormatNumber(Abs([My Number]);"0");1))+2; Length([French units]));1 ; Pos(Substr([French units]; Pos([French units];"#" + Left(FormatNumber(Abs([My Number]);"0");1) + "#") + Length(Left(FormatNumber(Abs([My Number]);"0");1))+2 ; Length([French units])); "#")-1 ) + (if Left(FormatNumber(Abs([My Number]);"0");1) = "1" then " billion" else " billions")
else "")
/* hundred of billion */
+ (if Length(FormatNumber(Abs([My Number]);"0")) > 11 then
if Left(Right(FormatNumber(Abs([My Number]);"0");12);1) = "1" then " cent"
else if Left(Right(FormatNumber(Abs([My Number]);"0");12);1) <> "0" then
Substr(Substr([French units]; Pos([French units];"#" + Left(Right(FormatNumber(Abs([My Number]);"0");12);1) + "#") + Length(Left(Right(FormatNumber(Abs([My Number]);"0");12);1))+2; Length([French units]));1 ; Pos(Substr([French units]; Pos([French units];"#" + Left(Right(FormatNumber(Abs([My Number]);"0");12);1) + "#") + Length(Left(Right(FormatNumber(Abs([My Number]);"0");12);1))+2 ; Length([French units])); "#")-1 ) + " cent"
else "")
/* billion */
+ (if Length(FormatNumber(Abs([My Number]);"0")) > 10 then
if Left(Right(FormatNumber(Abs([My Number]);"0");11);2) <> "00" then
Substr(Substr([French units]; Pos([French units];"#" + Left(Right(FormatNumber(Abs([My Number]);"0");11);2) + "#") + Length(Left(Right(FormatNumber(Abs([My Number]);"0");11);2))+2; Length([French units]));1 ; Pos(Substr([French units]; Pos([French units];"#" + Left(Right(FormatNumber(Abs([My Number]);"0");11);2) + "#") + Length(Left(Right(FormatNumber(Abs([My Number]);"0");11);2))+2 ; Length([French units])); "#")-1 ) + " milliards"
else " milliards"
else if Length(FormatNumber(Abs([My Number]);"0")) > 9 then
if Left(FormatNumber(Abs([My Number]);"0");1) <> "0" then
Substr(Substr([French units]; Pos([French units];"#" + Left(FormatNumber(Abs([My Number]);"0");1) + "#") + Length(Left(FormatNumber(Abs([My Number]);"0");1))+2; Length([French units]));1 ; Pos(Substr([French units]; Pos([French units];"#" + Left(FormatNumber(Abs([My Number]);"0");1) + "#") + Length(Left(FormatNumber(Abs([My Number]);"0");1))+2 ; Length([French units])); "#")-1 ) + (if Left(FormatNumber(Abs([My Number]);"0");1) = "1" then " milliard" else " milliards")
else "")
/* hundred of million */
+ (if Length(FormatNumber(Abs([My Number]);"0")) > 8 then
if Left(Right(FormatNumber(Abs([My Number]);"0");9);1) = "1" then " cent"
else if Left(Right(FormatNumber(Abs([My Number]);"0");9);1) <> "0" then
Substr(Substr([French units]; Pos([French units];"#" + Left(Right(FormatNumber(Abs([My Number]);"0");9);1) + "#") + Length(Left(Right(FormatNumber(Abs([My Number]);"0");9);1))+2; Length([French units]));1 ; Pos(Substr([French units]; Pos([French units];"#" + Left(Right(FormatNumber(Abs([My Number]);"0");9);1) + "#") + Length(Left(Right(FormatNumber(Abs([My Number]);"0");9);1))+2 ; Length([French units])); "#")-1 ) + " cent"
else "")
/* million */
+ (if Length(FormatNumber(Abs([My Number]);"0")) > 7 then
if Left(Right(FormatNumber(Abs([My Number]);"0");8);2) <> "00" then
Substr(Substr([French units]; Pos([French units];"#" + Left(Right(FormatNumber(Abs([My Number]);"0");8);2) + "#") + Length(Left(Right(FormatNumber(Abs([My Number]);"0");8);2))+2; Length([French units]));1 ; Pos(Substr([French units]; Pos([French units];"#" + Left(Right(FormatNumber(Abs([My Number]);"0");8);2) + "#") + Length(Left(Right(FormatNumber(Abs([My Number]);"0");8);2))+2 ; Length([French units])); "#")-1 ) + " millions"
else " millions"
else if Length(FormatNumber(Abs([My Number]);"0")) > 6 then
if Left(FormatNumber(Abs([My Number]);"0");1) <> "0" then
Substr(Substr([French units]; Pos([French units];"#" + Left(FormatNumber(Abs([My Number]);"0");1) + "#") + Length(Left(FormatNumber(Abs([My Number]);"0");1))+2; Length([French units]));1 ; Pos(Substr([French units]; Pos([French units];"#" + Left(FormatNumber(Abs([My Number]);"0");1) + "#") + Length(Left(FormatNumber(Abs([My Number]);"0");1))+2 ; Length([French units])); "#")-1 ) + (if Left(FormatNumber(Abs([My Number]);"0");1) = "1" then " million" else " millions")
else "")
/* hundred of thousand */
+ (if Length(FormatNumber(Abs([My Number]);"0")) > 5 then
if Left(Right(FormatNumber(Abs([My Number]);"0");6);1) = "1" then " cent"
else if Left(Right(FormatNumber(Abs([My Number]);"0");6);1) <> "0" then
Substr(Substr([French units]; Pos([French units];"#" + Left(Right(FormatNumber(Abs([My Number]);"0");6);1) + "#") + Length(Left(Right(FormatNumber(Abs([My Number]);"0");6);1))+2; Length([French units]));1 ; Pos(Substr([French units]; Pos([French units];"#" + Left(Right(FormatNumber(Abs([My Number]);"0");6);1) + "#") + Length(Left(Right(FormatNumber(Abs([My Number]);"0");6);1))+2 ; Length([French units])); "#")-1 ) + " cent"
else "")
/* thousand */
+ (if Length(FormatNumber(Abs([My Number]);"0")) > 4 then
if Left(Right(FormatNumber(Abs([My Number]);"0");5);2) <> "00" then
Substr(Substr([French units]; Pos([French units];"#" + Left(Right(FormatNumber(Abs([My Number]);"0");5);2) + "#") + Length(Left(Right(FormatNumber(Abs([My Number]);"0");5);2))+2; Length([French units]));1 ; Pos(Substr([French units]; Pos([French units];"#" + Left(Right(FormatNumber(Abs([My Number]);"0");5);2) + "#") + Length(Left(Right(FormatNumber(Abs([My Number]);"0");5);2))+2 ; Length([French units])); "#")-1 ) + " mille"
else " mille"
else if Length(FormatNumber(Abs([My Number]);"0")) > 3 then
if Left(FormatNumber(Abs([My Number]);"0");1) = "1" then " mille"
else if Left(FormatNumber(Abs([My Number]);"0");1) <> "0" then
Substr(Substr([French units]; Pos([French units];"#" + Left(FormatNumber(Abs([My Number]);"0");1) + "#") + Length(Left(FormatNumber(Abs([My Number]);"0");1))+2; Length([French units]));1 ; Pos(Substr([French units]; Pos([French units];"#" + Left(FormatNumber(Abs([My Number]);"0");1) + "#") + Length(Left(FormatNumber(Abs([My Number]);"0");1))+2 ; Length([French units])); "#")-1 ) + " mille"
else "")
/* hundred */
+ (if Length(FormatNumber(Abs([My Number]);"0")) > 2 then
if Left(Right(FormatNumber(Abs([My Number]);"0");3);1) = "1" then " cent"
else if Left(Right(FormatNumber(Abs([My Number]);"0");3);1) <> "0" then
Substr(Substr([French units]; Pos([French units];"#" + Left(Right(FormatNumber(Abs([My Number]);"0");3);1) + "#") + Length(Left(Right(FormatNumber(Abs([My Number]);"0");3);1))+2; Length([French units]));1 ; Pos(Substr([French units]; Pos([French units];"#" + Left(Right(FormatNumber(Abs([My Number]);"0");3);1) + "#") + Length(Left(Right(FormatNumber(Abs([My Number]);"0");3);1))+2 ; Length([French units])); "#")-1 ) + " cent"
else "")
/* units */
+ Substr(Substr([French units]; Pos([French units];"#" + Right(FormatNumber(Abs([My Number]);"0");2) + "#") + Length(Right(FormatNumber(Abs([My Number]);"0");2))+2; Length([French units]));1 ; Pos(Substr([French units]; Pos([French units];"#" + Right(FormatNumber(Abs([My Number]);"0");2) + "#") + Length(Right(FormatNumber(Abs([My Number]);"0");2))+2 ; Length([French units])); "#")-1 )
;" billion milliards";" billion");" billions milliards";" billions");" billion milliard";" billion");" billions milliard";" billions");" billion millions";" billion");" billions millions";" billions");" billion million";" billion");" billions million";" billions");" billion mille";" billion");" billions mille";" billions");" milliard millions";" milliard");" milliards millions";" milliards");" milliard million";" milliard");" milliards million";" milliards");" milliard mille";" milliard");" milliards mille";" milliards");" million mille";" million");" millions mille";" millions");" milliard un mille";" milliard mille");" milliards un mille";" milliards mille");" million un mille";" million mille");" millions un mille";" millions mille");" billion un mille";" billion mille");" billions un mille";" billions mille") )
As I said the formula is a bit complex but can be easily explained.
There is no performance issue. In the dataset I used there are 33413 different numbers all unique and it takes less than 2 seconds to translate all numbers.
I also extended the translation formula to manage the decimals (2 decimals maximum).
This formula can be used with currencies to display amount in words such as deux cent quarante-trois euros et dix-huit cents
Here is a screenshot of such translation:

Now here is the formula used for the translation. The core of the formula does not change except for the number formatting: we just need to remove all decimals.
So instead of having FormatNumber(Abs([My Number]);"0"), the new formula is FormatNumber(Truncate(Abs([My Decimal]);0);"0")
Now the translation formula has changed. I added end replaced the red part in the first part of the formula:
=if Length(FormatNumber(Abs([My Decimal]);"0")) > 15 then "*****" /* Too large */
else (if [My Decimal] < 0 then "moins " else "")
+ if FormatNumber(Truncate(Abs([My Decimal]);0);"0") = "0" or FormatNumber(Truncate(Abs([My Decimal]);0);"0") = "1" Then /* Zero or One */
(if FormatNumber(Truncate(Abs([My Decimal]);0);"0") = "0" Then "zero euro" else "un euro")
/* decimals */
+ (if Abs([My Decimal]) <> Truncate(Abs([My Decimal]);0) Then " et"
+ Substr(Substr([French units]; Pos([French units];"#" + Substr(FormatNumber(Abs([My Decimal]) - Truncate(Abs([My Decimal]);0);"#.##")+"0";2;2) + "#") + Length(Substr(FormatNumber(Abs([My Decimal]) - Truncate(Abs([My Decimal]);0);"#.##")+"0";2;2))+2; Length([French units]));1 ; Pos(Substr([French units]; Pos([French units];"#" + Substr(FormatNumber(Abs([My Decimal]) - Truncate(Abs([My Decimal]);0);"#.##")+"0";2;2) + "#") + Length(Substr(FormatNumber(Abs([My Decimal]) - Truncate(Abs([My Decimal]);0);"#.##")+"0";2;2))+2 ; Length([French units])); "#")-1 ) + if (Substr(FormatNumber(Abs([My Decimal]) - Truncate(Abs([My Decimal]);0);"#.##")+"0";2;2) = "01") then " centime" else " centimes" else "")
else Trim(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(Replace(
And I added the new part (in red) at the end of the formula.
/* units */
+ Substr(Substr([French units]; Pos([French units];"#" + Right(FormatNumber(Truncate(Abs([My Decimal]);0);"0");2) + "#") + Length(Right(FormatNumber(Truncate(Abs([My Decimal]);0);"0");2))+2; Length([French units]));1 ; Pos(Substr([French units]; Pos([French units];"#" + Right(FormatNumber(Truncate(Abs([My Decimal]);0);"0");2) + "#") + Length(Right(FormatNumber(Truncate(Abs([My Decimal]);0);"0");2))+2 ; Length([French units])); "#")-1 ) + " euros"
/* decimals */
+ (if Abs([My Decimal]) <> Truncate(Abs([My Decimal]);0) Then " et"
+ Substr(Substr([French units]; Pos([French units];"#" + Substr(FormatNumber(Abs([My Decimal]) - Truncate(Abs([My Decimal]);0);"#.##")+"0";2;2) + "#") + Length(Substr(FormatNumber(Abs([My Decimal]) - Truncate(Abs([My Decimal]);0);"#.##")+"0";2;2))+2; Length([French units]));1 ; Pos(Substr([French units]; Pos([French units];"#" + Substr(FormatNumber(Abs([My Decimal]) - Truncate(Abs([My Decimal]);0);"#.##")+"0";2;2) + "#") + Length(Substr(FormatNumber(Abs([My Decimal]) - Truncate(Abs([My Decimal]);0);"#.##")+"0";2;2))+2 ; Length([French units])); "#")-1 ) + if (Substr(FormatNumber(Abs([My Decimal]) - Truncate(Abs([My Decimal]);0);"#.##")+"0";2;2) = "01") then " centime" else " centimes" else "")
...
For the decimals translation I used the same technique than the units.
In conclusion, we have now a powerful way to translate any number in French words.
To use it you just need to replace [My number] or [My decimal] by your measure
You can download the Web Intelligence report attached to that publication.
Didier MAZOUE
You must be a registered user to add a comment. If you've already registered, sign in. Otherwise, register and sign in.
| User | Count |
|---|---|
| 29 | |
| 25 | |
| 24 | |
| 19 | |
| 14 | |
| 13 | |
| 12 | |
| 11 | |
| 11 | |
| 10 |