AppleScript numbers to English words

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"

Hi,

Was looking for a script to convert dollars to words (English). Found your script while doing a search for a similar script. Ending up modifying yours to to do what I needed. The modified version now outputs dollar values in english. Useful if you need to print outs check in excel 2008 / iWorks Numbers.

Anyhow, Though I would share:


--testscript----------Un-comment-to-test------------------------------
--display dialog "Enter a dollar value" default answer ""
--try
--	set the requested_number to the text returned of the result as string
--	set english to dollarsToEnglish(requested_number, "gb")
--	display dialog english
--	return english
--end try

--call to MS Excel to take a dollar value from one cell value and out put english text of the dollar value in another.
on run
	tell application "Microsoft Excel"
		tell active sheet of active workbook
			set dollarAmount to the value of cell "G4" as string
		end tell
	end tell
	set english to dollarsToEnglish(dollarAmount, "us")
	tell application "Microsoft Excel"
		tell active sheet of active workbook
			set value of cell "B6" to english
		end tell
	end tell
end run

--Main script
on dollarsToEnglish(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
		--reverse the sign
		set n to -n
	end if
	
	--case 0 dollars.
	if (n div 1 is equal to 0) then
		set end of o's collector to "Zero"
	end if
	
	-- 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)
	if n div 1 is equal to 1 then
		set end of o's collector to "Dollar"
	else
		set end of o's collector to "Dollars"
	end if
	
	-- Deal with any fractional part of the number.
	if (n mod 1 > 0) then
		set m to n div 1
		set p to n * 100
		o's parseNumber(p - (m * 100), 0)
		if p - (m * 100) is equal to 1 then
			set end of o's collector to "Cent"
		else
			set end of o's collector to "Cents"
		end if
	else
		set end of o's collector to "and Zero Cents"
	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 dollarsToEnglish

Hi, ariaaria. Welcome to MacScripter.

I’m glad someone found a use for my code. :slight_smile:

Thanks for posting your adaptation. It seems to work very well!