I can’t imagine anyone actually needing this function, but someone posted such a handler on the AppleScript-Users list the other day, so I thought I’d have a go at writing one myself! (I notice too today that there were a few attempts last year in this thread in the AppleScript Studio & Xcode forum.)
This effort offers three different ways of rendering larger numbers: in short scale (-illions are powers of a thousand) with "and"s, in short scale without "and"s, or in long scale (-illions are powers of a million) with "and"s. It also handles negative and fractional numbers. It’s only intened to work with values that can be passed as AppleScript numbers and may produce inaccurate results near the limits of their floating-point precision.
-- The 'lang' parameter should be some text ending with:
-- "EN" (say) for short-scale -illions and the word "and" inserted in the appropriate places.
-- "US" for short-scale -illions and no "and"s.
-- "GB" for the older British/French long-scale -illions and "and"s.
on numberToEnglish(n, lang)
script o
property longScale : missing value -- Will be a boolean.
property usingAnd : missing value -- Ditto.
property unitsAndTeens : {"one", "two", "three", "four", "five", "six", "seven", "eight", "nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen", "sixteen", "seventeen", "eighteen", "nineteen"}
property tens : {missing value, "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"}
property landmarks : {"thousand", "million", "billion", "trillion", "quadrillion", "quintillion", "sextillion"}
property longScaleLandmarks : {"thousand", "million", "thousand", "billion", "thousand", "trillion", "thousand"}
property collector : {} -- Words collected here.
-- Recursive handler to deal with the non-fractional part of the number.
on parseNumber(n, landmarkNo)
-- Recurse to deal with the high end of the number first
if (n > 999) then parseNumber(n div 1000, landmarkNo + 1)
-- Deal with the "hundreds" digit from this group of three.
set h to n mod 1000 div 100
if (h > 0) then
set end of o's collector to item h of unitsAndTeens
set end of o's collector to "hundred"
end if
-- Deal with the tens and units.
set tu to n mod 100
if (tu > 0) then
-- Insert "and" first, if required.
if (usingAnd) and (o's collector is not {}) and ((end of o's collector is in {"hundred", "thousand"}) or (landmarkNo is 0)) then set end of o's collector to "and"
if (tu < 20) then
set end of o's collector to item tu of o's unitsAndTeens
else
set end of o's collector to item (tu div 10) of o's tens
set u to tu mod 10
if (u > 0) then set end of o's collector to item u of unitsAndTeens
end if
end if
-- Append any appropriate landmark (10 ^ 3) name.
if (landmarkNo > 0) and (((not longScale) and (n mod 1000 > 0)) or ((longScale) and (n mod 1000000 > 0))) then set end of o's collector to item landmarkNo of o's landmarks
end parseNumber
end script
-- Main handler code. Set up.
-- Adjust if the number is negative.
if (n < 0) then
set end of o's collector to "minus"
set n to -n
end if
if (n div 1 is 0) then
-- Special-case 0.
set end of o's collector to "zero"
else
-- Analyse the "language" parameter.
set o's longScale to (lang ends with "GB")
if (result) then set o's landmarks to o's longScaleLandmarks
set o's usingAnd to (lang does not end with "US")
-- Deal with the non-fractional part of the number.
o's parseNumber(n div 1, 0)
end if
-- Deal with any fractional part.
-- (Vulnerable to floating-point inaccuracies.)
if (n mod 1 > 0.0) then
set end of o's collector to "point"
set n to n * 10
repeat
set u to n mod 10 div 1
if (u is 0) then
set end of o's collector to "zero"
else
set end of o's collector to item (u div 1) of o's unitsAndTeens
end if
set n to n * 10
if (n mod 10 is 0.0) then exit repeat
end repeat
end if
-- Make the assembled words into a single text.
set astid to AppleScript's text item delimiters
set AppleScript's text item delimiters to space
set English to o's collector as text
set AppleScript's text item delimiters to astid
return English
end numberToEnglish
numberToEnglish(-1.23456789012345E+12, "EN")
--> "minus one trillion two hundred and thirty four billion five hundred and sixty seven million eight hundred and ninety thousand one hundred and twenty three point four five"
numberToEnglish(-1.23456789012345E+12, "US")
--> "minus one trillion two hundred thirty four billion five hundred sixty seven million eight hundred ninety thousand one hundred twenty three point four five"
numberToEnglish(-1.23456789012345E+12, "GB")
--> "minus one billion two hundred and thirty four thousand five hundred and sixty seven million eight hundred and ninety thousand one hundred and twenty three point four five"