มอดูล:Currency

require('strict')
local p = {}
local lang = mw.language.getContentLanguage();									-- language object for this wiki
local presentation ={};															-- table of tables that contain currency presentation data
local properties;
----------------------------------------------------------------------------------------------
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
-----------------------------------------------------------------
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, linked, passthrough)
	local symbol;
	local position = propertiescode.position;
	if linked then
		symbol = string.format ('%s', propertiescode.page, propertiescode.symbol);	-- make wikilink of page and symbol
	else
		symbol = propertiescode.symbol;
	end
	if not passthrough then
		amount = lang:formatNum (tonumber(amount));								-- add appropriate comma separators
	end
	
	amount = amount:gsub ('^%-', '−');											-- replace the hyphen with unicode minus
	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 %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 %s', amount, symbol);
	elseif 'd' == position then													-- special case that replaces the decimal separator with symbol (Cifrão for CVE is the only extant case)
		if passthrough then
			return string.format('%s%s', symbol, amount)
		end
		
		local digits, decimals;													-- this code may not work for other currencies or on other language wikis
		if amount:match ('%d,+%.%d+') then									-- with decimal separator and decimals
			digits, decimals = amount:match ('(%d,+)%.(%d+)')
			amount = string.format ('%s%s%s', digits, symbol, decimals);		-- insert symbol
		elseif amount:match ('%d,+%.?$') then									-- with or without decimal separator
			digits = amount:match ('(%d,+)%.?$')
			amount = string.format ('%s%s00', digits, symbol);					-- add symbol and 00 ($00)
		end
		amount = amount:gsub (',', '%.');										-- replace grouping character with period
		return amount;
	end
	return amount .. '  – definition missing position (help)';	-- position not defined
end
--------------------------------------------------------------------------------------
Make a wikilink from the currency's article title and its plural (if provided).  If linked is false, returns only
the article title (unlinked)
local function make_name (linked, page, plural)
	if not linked then
		if not is_set (plural) then
			return page;														-- just the page
		elseif 's' == plural then												-- if the simple plural form
			return string.format ('%ss', page);									-- append an 's'
		else
			return plural;														-- must be the complex plural form (pounds sterling v. dollars)
		end
	else
		if not is_set (plural) then
			return string.format ('%s', page);
		elseif 's' == plural then												-- if the simple plural form
			return string.format ('%ss', page);
		else
			return string.format ('%s', page, plural);					-- must be the complex plural form (pounds sterling v. dollars)
		end
	end
end
-------------------------------------------------------------------
assembles a long-form currency name from amount and name from the properties tables; plural for all values not equal to 1
local function make_long_form_name (amount, code, linked, passthrough)
	local name, formatted;
	
	if not is_set (propertiescode.page) then
		return ' – definition missing page (help)';
	end
	if not passthrough then
		amount = tonumber (amount);												-- make sure it's a number
	end
	if 1 == amount then
		name = make_name (linked, propertiescode.page);						-- the singular form
	elseif is_set (propertiescode.plural) then								-- plural and there is a plural form
		name = make_name (linked, propertiescode.page, propertiescode.plural);
	else
		name = make_name (linked, propertiescode.page);						-- plural but no separate plural form so use the singular form
	end
	
	if not passthrough then
		formatted = lang:formatNum (amount)
	else 
		formatted = amount
	end
	return string.format ('%s %s', formatted, name);							-- put it all together
end
----------------------------------------------------------------------------
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, linked, fmt, passthrough)
	local name;
	local result;
	presentation = mw.loadData ('Module:Currency/Presentation');				-- get presentation data
	if presentation.currency_propertiescode then								-- if code is an iso 4217 code
		properties = presentation.currency_properties;
	elseif presentation.code_translationcode then								-- not iso 4217 but can be translated
		code = presentation.code_translationcode;								-- then translate
		properties = presentation.currency_properties;
	elseif presentation.non_standard_propertiescode then						-- last chance, is it a non-standard code?
		properties = presentation.non_standard_properties;
	else
		return ' – invalid code (help)';
	end
	if long_form then
		result = make_long_form_name (amount, code, linked, passthrough);								-- 
	else
		result = make_short_form_name (amount, code, linked, passthrough);
	end
	
	if 'none' == fmt then														-- no group separation
		result = result:gsub ('(%d%d?%d?),', '%1');								-- strip comma separators
	elseif 'gaps' == fmt then													-- use narrow gaps
		result = result:gsub ('(%d%d?%d?),', '%1');	-- replace comma seperators
	elseif fmt and 'commas' ~= fmt then											-- if not commas (the default) then error message
		return ' – invalid format (help)';
	end
	return result;																-- done
end
--------------------------------------------------------------
replacement for lang:parseFormattedNumber() which doesn't work; all it does is strip commas.
This function returns a string where all comma separators have been removed from the source string.  If the source
is malformed: has characters other than digits, commas, and decimal points; has too many decimal points; has commas
in in appropriate locations; then the function returns nil.
local function parse_formatted_number (amount)
	local count;
	local parts = {};
	local digits = {};
	local decimals;
	local sign = ;
	local _;
	
	if amount:find ('^%-−%d%.,')	then										-- anything but sign, digits, decimal points, or commas
		return nil;
	end
	
	amount = amount:gsub ('−', '-');											-- replace unicode minus with hyphen
	
	_, count = amount:gsub('%.', )											-- count the number of decimal point characters 
	if 1 ------------------------------------------------
Converts quantified number/string combinations to a number e.g. 1 thousand to 1000.
local function convert_string_to_numeric (amount)
	local quantifiers = {'thousand' = 1000, 'million' = 1000000, 'm' = 1000000, 'billion' = 1000000000, 'b' = 1000000000, 'trillion' = 1000000000000};
	
	local n, q = amount:match ('(%-−?%d%.,+)%s*(%a+)$');					-- see if there is a quantifier following a number; zero or more space characters
	if nil == n then
		n, q = amount:match ('(%-−?%d%.,+) (%a+)$')					-- see if there is a quantifier following a number; nbsp html entity ( output
	end
	if nil == n then return amount end;											-- if not  return amount unmolested
	
	n = n:gsub (',', );														-- strip comma separators if present
	q = q:lower();																-- set the quantifier to lower case
	if nil == quantifiersq then return amount end;							-- if not a recognized quantifier
	
	return tostring (n * quantifiersq);										-- return a string, not a number
end
--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;
	local linked = true;
	local passthrough = false;
	if not is_set (args1) then
		return ' – invalid amount (help)';
	end
	
--	amount = lang:parseFormattedNumber(args1);								-- if args1 can't be converted to a number then error (this just strips grouping characters)
--	if args1:find ('^%d%.') or not amount then								-- non-digit characters or more than one decimal point (because lag:parse... is broken)
--		return ' – invalid amount (help)';
--	end
	-- This allows us to use  while actually following MOS:CURRENCY as regards "billion", "million", "M", "bn", etc.
	if not (args'passthrough' == 'yes') then -- just pass whatever string is given through.
		amount = convert_string_to_numeric (args1);
		amount = parse_formatted_number(amount);								-- if args1 can't be converted to a number then error
		if not amount then
			return ' – invalid amount (help)';
		end
	else
		amount = args1
	end
	
	if not is_set(args2) then													-- if not provided
		code = 'USD';															-- default to USD
	else
		code = args2:upper();													-- always upper case; used as index into data tables which all use upper case
	end
	
	if args3 then																-- this is the |first= parameter  TODO: make this value meaningful? y, yes, true?
		long_form = true;
	end
	
	if 'no' == args4 then														-- this is the |linked= parameter; defaults to 'yes'; any value but 'no' means yes
		linked = false;
	end
	return render_currency (amount, code, long_form, linked, args'fmt', (args'passthrough' == 'yes'))
end
return {
	currency = currency,														-- template entry point
	_render_currency = render_currency,											-- other modules entry point
	}