Jump to content

Module:Currency

Permanently protected module
From Wikipedia, the free encyclopedia
This is an old revision of this page, as edited by Trappist the monk (talk | contribs) at 00:19, 10 April 2016. The present address (URL) is a permanent link to this revision, which may differ significantly from the current revision.

require('Module:No globals')

local p = {}
local presentation ={};															-- table of tables that contain currency presentation data
local lang = mw.language.getContentLanguage();									-- language object for this wiki

--[[--------------------------< I S _ S E T >------------------------------------------------------------------

Whether variable is set or not.  A variable is set when it is not nil and not empty.

]]

local function is_set( var )
	return not (var == nil or var == '');
end


--[[--------------------------< G R O U P I N G >--------------------------------------------------------------

Takes a number string in the form nnnn.nn and adds grouping to it, return n,nnn.nn

Grouping is added by reversing the digits portion of the value:
	123456789.nn
becomes
	987654321
	
Then, string.gsub replaces each group of three digits with the same three digits (the capture) followed by a comma:
	987,654,321,
The string is reversed again:
	,123,456,789
and the leading comma (if present) is removed:
	123,456,789
	
At the end, the digits and decimal portions are reunited with a decimal point which is returned to calling function.

Unrecognized 'numbers' return '0.00'

]]

local function grouping (val)
	local digits;																-- left side of the decimal point
	local decimals;																-- right side of the decimal point
	
	if val:match ('^%d+%.%d+$') then											-- has digits and decimals
		digits, decimals = val:match ('(%d+)%.(%d+)');
	elseif val:match ('^%d+%.$') then											-- has digits and decimal point but no decimals
		digits = val:match ('(%d+)%.')
	elseif val:match ('^%.%d+$') then											-- has no digits but has decimal point and decimals
		decimals = val:match ('%.(%d+)')
	elseif 	val:match ('^%d+$')	then											-- just has digits
		digits = val;
	else
		return '0.00';															-- something that isn't a number; return default  TODO: error message here
	end

	if is_set (digits) then
		digits = digits:reverse():gsub ('%d%d%d', '%1,'):reverse():gsub('^,', '');	-- apply grouping
	else
		digits = '0';															-- otherwise set default value
	end
	
	if not is_set (decimals) then
		decimals = '00';														-- set to default
	end
	
	return digits .. '.' .. decimals;											-- return formatted value
end


--[[--------------------------< M A K E _ S H O R T _ F O R M  _ N A M E >-------------------------------------

Assembles value and symbol according to the order specified in the properties table for this currency code

]]

local function make_short_form_name (amount, code)
	local symbol = string.format ('[[%s|%s]]', presentation.currency_properties[code].page, presentation.currency_properties[code].symbol);	-- make wikilink of page and symbol
	local position = presentation.currency_properties[code].position;
	
--	amount = grouping (amount);
	amount = lang:formatNum (tonumber(amount));

	if 'b' == position then														-- choose appropriate format: unspaced before the amount
		return string.format ('%s%s', symbol, amount);
	elseif 'bs' == position then												-- spaced before the amount
		return string.format ('%s&nbsp;%s', symbol, amount);
	elseif 'a' == position then													-- unspaced after the amount
		return string.format ('%s%s', amount, symbol);
	elseif 'as' == position then												-- spaced after the amount
		return string.format ('%s&nbsp;%s', amount, symbol);
	elseif 'd' == position then													-- special case for CVE which replaces the decimal separator with the Cifrão
		if amount:match ('%d+%.%d+') then										-- with decimal separator and decimals
			amount.gsub ('%.', symbol);											-- replace separator with Cifrão ($)
		elseif amount:match ('%d+%.') then										-- with decimal separator
			amount.gsub ('%.', symbol .. '00');									-- replace separator with Cifrão and 00 ($00)
		else																	-- just digits
			amount = string.format ('%s%s00', amount, symbol);					-- add Cifrão and 00 ($00)
		end
		amount = amount:gsub (',', '%.');										-- replace gouping character with period
		return amount;
	else
		return amount .. ' <span style="font-size:inherit" class="error">{{currency}} – definition missing position</span>';	-- position not defined
	end
end


--[[--------------------------< M A K E _ L O N G _ F O R M  _ N A M E >---------------------------------------

assembles a long-form currency name from amount and name the tables; plural for all values not equal to 1

]]

local function make_long_form_name (amount, code)
	local properties;
	local name;
	
	if presentation.currency_properties[code] then								
		properties = presentation.currency_properties;
	elseif presentation.non_standard_properties[code] then
		properties = presentation.non_standard_properties;
	else
		return '<span style="font-size:inherit" class="error">{{currency}} – cannot find code: ' .. code .. '</span>';	-- should not be here
	end
	
	if not is_set (properties[code].page) then
		return '<span style="font-size:inherit" class="error">{{currency}} – definition missing page</span>';
	end

	amount = tonumber (amount);													-- make sure it's a number
	
	if 1 == amount then
		name = string.format ('[[%s]]', properties[code].page);					-- the singular form
	elseif 's' == properties[code].plural then
		name = string.format ('[[%s]]s', properties[code].page);				-- a plural form
	elseif is_set (properties[code].plural) then								-- plural is not an 's' form
		name = string.format ('[[%s|%s]]', properties[code].page, properties[code].plural);		-- another plural form (pounds sterling v. dollars)
	else
		name = string.format ('[[%s]]', properties[code].page);					-- no plural form so use the singular form
	end

	return lang:formatNum (amount) .. ' ' .. name;	-- put it all together
--	return grouping (amount) .. ' ' .. name;									-- put it all together
end


--[[--------------------------< R E N D E R _ C U R R E N C Y >------------------------------------------------

Renders currency amount with symbol or long-form name.

Also, entry point for other modules.  Assumes that parameters have been vetted; amount is a number, code is upper
case string, long_form is boolean; all are required.

]]

local function render_currency (amount, code, long_form)
	local name;
	local ouput;

	presentation = mw.loadData ('Module:Currency/Presentation');				-- get presentation data
	
	if nil == presentation.currency_properties[code] then						-- if not an iso 4217 code
		if presentation.code_translation[code] then								-- but can be translated
			code = presentation.code_translation[code];							-- then translate
		elseif nil == presentation.non_standard_properties[code] then			-- last chance, is it a non-standard code?
			return '<span style="font-size:inherit" class="error">{{currency}} – invalid code</span>';
		end
	end

	if long_form then
		return make_long_form_name (amount, code);								-- 
	else
		return make_short_form_name (amount, code);
	end

	return amount;
end


--[[--------------------------< C U R R E N C Y >--------------------------------------------------------------

Template:Currency entry point.  The template takes three parameters:
	positional (1st), |amount=, |Amount=	: digits and decimal points only
	positional (2nd), |type=, |Type=		: code that identifies the currency
	|first=									: uses currency name instead of symbol

]]

local function currency (frame)
	local args = require('Module:Arguments').getArgs (frame);

	local amount, code;
	local long_form = false;
	
	
	amount = lang:parseFormattedNumber(args[1]);								-- if args[1] can't be converted to a number then error (this just strips grouping characters)
	if args[1]:find ('[^%d%.]') or not amount then								-- non-digit characters or more than one decimal point (because lag:parse... is broken)
		return '<span style="font-size:inherit" class="error">{{currency}} – invalid amount</span>';
	end
	
	if nil == args[2] then														-- if not provided
		code = USD;																-- default to USD
	else
		code = args[2]:upper();													-- always upper case; used as index into data tables which all use upper case
	end
	
	if args[3] then																-- this is the |first= parameter  TODO: make this value meaningful? y, yes, true?
		long_form = true;
	end
	
--	return render_currency (args[1], code, long_form)
	return render_currency (amount, code, long_form)
end

return {
	currency = currency,														-- template entry point
	_render_currency = render_currency,											-- other modules entry point
	}