(This is a repost from my abandoned tumblr blog; dated 2016-11-17)
A client requested that we spell out numbers on their website in order to clarify some financial totals. I initially planned on using the ColdFusion NumberAsString UDF from 2002. After reviewing the results, I thought it was worth a unit test comparison to review the results against ICU4J (java).
In the end, I decided to use ICU4J because:
- ICU4J converts text using 180+ locales. Numbers are translated to each language (Chinese, Thai, French, Spanish, etc)
- UDF adds title case capitalization to all number strings. (if needed, you can do this separately.)
- UDF doesn't support negative values and will throw a CF error.
- UDF doesn't use hyphenized numbers ("Forty Three" versus "forty-three")
- UDF uses "cardinal-verbose" format w/o hyphened numbers (Adds the word "and" where commas should be.)
- UDF decimal places are treated as positive-type number words (instead of using "point four three" for ".43")
123 = "one hundred twenty-three"
100001 = "one hundred thousand one"
9 = "nine"
64578.25 = "sixty-four thousand five hundred seventy-eight point two five"
0.333 = "zero point three three three"
Here's the CFML source code:
https://gist.github.com/JamoCA/cd343524e417f382d5c706954e52dfaf
<!--- 2016-11-17 SpellOutNumberUS UDF | |
Blog: https://dev.to/gamesover/convert-numbers-to-text-using-coldfusion-and-icu4j-33kn | |
Tweet: https://x.com/gamesover/status/1816618788944773584 | |
Requires icu4j-55_1.jar from http://site.icu-project.org/home/why-use-icu4j (Copy to Java Path or use JavaLoader) | |
Number Formats (Availability determined based on locale) | |
%spellout-ordinal-verbose: one hundred and twenty-third | |
%spellout-ordinal: one hundred twenty-third | |
%spellout-cardinal-verbose: one hundred and twenty-three | |
%spellout-cardinal: one hundred twenty-three | |
%spellout-numbering-verbose: one hundred and twenty-three | |
%spellout-numbering: one hundred twenty-three | |
%spellout-numbering-year: one hundred twenty-three | |
Some locales may need <cfprocessingDirective pageencoding="utf-8"> and setLocale("LOCALE_CODE") in order to render | |
---> | |
<cfscript> | |
function spellOutNumberUS(myNumber){ | |
var response = "unknown numeric value"; | |
if (!StructKeyExists(Request, "spellOutUlocale")){ | |
Request.spellOutUlocale = createObject("java","com.ibm.icu.util.ULocale"); | |
Request.spellOutthisLocale = Request.spellOutUlocale.init("en_US"); | |
Request.spellOutruleBased = createObject("java","com.ibm.icu.text.RuleBasedNumberFormat").init(Request.spellOutthisLocale, 1); | |
} | |
if (isNumeric(arguments.myNumber)){ | |
if (int(arguments.myNumber) IS arguments.myNumber){ | |
response = Request.spellOutruleBased.format(javacast("long", val(trim(arguments.myNumber))), "%spellout-numbering"); | |
} else { | |
response = Request.spellOutruleBased.format(javacast("double", val(trim(arguments.myNumber))), "%spellout-numbering"); | |
} | |
} | |
return response; | |
} | |
</cfscript> | |
<!--- Run some random number tests. | |
NOTE: I compared with NumberAsString UDF. http://cflib.org/udf/NumberAsString Some differences: | |
- ICU4J converts text using 180+ locales. Numbers are translated to each language (Chinese, Thai, French, Spanish, etc) | |
- UDF adds title case capitalization to all number strings. (if needed, you can do this separately.) | |
- UDF doesn't support negative values and will throw a CF error. | |
- UDF doesn't use hyphenized numbers ("Forty Three" versus "forty-three") | |
- UDF uses "cardinal-verbose" format w/o hyphened numbers (Adds the word "and" where commas should be.) | |
- UDF decimal places are treated as positive-type number words (instead of using "point four three" for ".43") | |
---> | |
<h1>ICU4J - Spell Out Numbers (US)</h1> | |
<ol> | |
<CFLOOP FROM="1" to="100" INDEX="thisNum"> | |
<CFSET n = val(RandRange(1, 2147483646, "SHA1PRNG"))> | |
<CFSET NDec = 0> | |
<CFIF RandRange(1,2,"SHA1PRNG") eq 1> | |
<CFSET NDec = val(RandRange(0, 99, "SHA1PRNG")) / 100> | |
<CFSET N = N + NDec> | |
</CFIF> | |
<CFIF RandRange(1,2,"SHA1PRNG") eq 1> | |
<CFSET N = N * -1> | |
</CFIF> | |
<li><h3>#N# = <b>#DecimalFormat(N)#</b></h3><!--- | |
UDF = <CFTRY>#NumberAsString(N)#<CFCATCH><b>ERROR - #CFCATCH.Message#</b></CFCATCH></CFTRY><br>---> | |
ICU4J = <CFTRY>#spellOutNumberUS(N)#<CFCATCH><b>ERROR - #CFCATCH.Message#</b></CFCATCH></CFTRY></li> | |
</CFLOOP> | |
</ol> |
Top comments (0)