| 👁 Warning | This Lua module is used on approximately 5,200 pages and changes may be widely noticed. Test changes in the module's /sandbox or /testcases subpages, or in your own module sandbox. Consider discussing changes on the talk page before implementing them. |
| Related pages |
|---|
This code is invoked from {{Currency}}. All of the template parameters are passed in the module's frame.
{{#invoke:Currency|currency|<amount>|<code>|<first>|<linked>|<passthrough>}}
See the template code for a description of the parameters.
Other modules may use this code. The entry point for other modules is _render_currency (amount, code, long_form, linked). See the function render_currency() for detail.
The data file Module:Currency/Presentation holds required currency presentation characteristics.
require('strict') localp={} locallang=mw.language.getContentLanguage();-- language object for this wiki localpresentation={};-- table of tables that contain currency presentation data localproperties; --[[--------------------------< I S _ S E T >------------------------------------------------------------------ Whether variable is set or not. A variable is set when it is not nil and not empty. ]] localfunctionis_set(var) returnnot(var==nilorvar==''); 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 ]] localfunctionmake_short_form_name(amount,code,linked,passthrough) localsymbol; localposition=properties[code].position; iflinkedthen symbol=string.format('[[%s|%s]]',properties[code].page,properties[code].symbol);-- make wikilink of page and symbol else symbol=properties[code].symbol; end ifnotpassthroughthen amount=lang:formatNum(tonumber(amount));-- add appropriate comma separators end amount=amount:gsub('^%-','−');-- replace the hyphen with unicode minus if'b'==positionthen-- choose appropriate format: unspaced before the amount returnstring.format('%s%s',symbol,amount); elseif'bs'==positionthen-- spaced before the amount returnstring.format('%s %s',symbol,amount); elseif'a'==positionthen-- unspaced after the amount returnstring.format('%s%s',amount,symbol); elseif'as'==positionthen-- spaced after the amount returnstring.format('%s %s',amount,symbol); elseif'd'==positionthen-- special case that replaces the decimal separator with symbol (Cifrão for CVE is the only extant case) ifpassthroughthen returnstring.format('%s%s',symbol,amount) end localdigits,decimals;-- this code may not work for other currencies or on other language wikis ifamount: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 elseifamount: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 returnamount; end returnamount..' <span style="font-size:inherit" class="error">{{currency}} – definition missing position ([[Template:Currency/doc#Error_messages|help]])</span>';-- position not defined end --[[--------------------------< M A K E _ N A M E >---------------------------------------------------------- Make a wikilink from the currency's article title and its plural (if provided). If linked is false, returns only the article title (unlinked) ]] localfunctionmake_name(linked,page,plural) ifnotlinkedthen ifnotis_set(plural)then returnpage;-- just the page elseif's'==pluralthen-- if the simple plural form returnstring.format('%ss',page);-- append an 's' else returnplural;-- must be the complex plural form (pounds sterling v. dollars) end else ifnotis_set(plural)then returnstring.format('[[%s]]',page); elseif's'==pluralthen-- if the simple plural form returnstring.format('[[%s]]s',page); else returnstring.format('[[%s|%s]]',page,plural);-- must be the complex plural form (pounds sterling v. dollars) end 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 from the properties tables; plural for all values not equal to 1 ]] localfunctionmake_long_form_name(amount,code,linked,passthrough) localname,formatted; ifnotis_set(properties[code].page)then return'<span style="font-size:inherit" class="error">{{currency}} – definition missing page ([[Template:Currency/doc#Error_messages|help]])</span>'; end ifnotpassthroughthen amount=tonumber(amount);-- make sure it's a number end if1==amountthen name=make_name(linked,properties[code].page);-- the singular form elseifis_set(properties[code].plural)then-- plural and there is a plural form name=make_name(linked,properties[code].page,properties[code].plural); else name=make_name(linked,properties[code].page);-- plural but no separate plural form so use the singular form end ifnotpassthroughthen formatted=lang:formatNum(amount) else formatted=amount end returnstring.format('%s %s',formatted,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. ]] localfunctionrender_currency(amount,code,long_form,linked,fmt,passthrough) localname; localresult; presentation=mw.loadData('Module:Currency/Presentation');-- get presentation data ifpresentation.currency_properties[code]then-- if code is an iso 4217 code properties=presentation.currency_properties; elseifpresentation.code_translation[code]then-- not iso 4217 but can be translated code=presentation.code_translation[code];-- then translate properties=presentation.currency_properties; elseifpresentation.non_standard_properties[code]then-- last chance, is it a non-standard code? properties=presentation.non_standard_properties; else return'<span style="font-size:inherit" class="error">{{currency}} – invalid code ([[Template:Currency/doc#Error_messages|help]])</span>'; end iflong_formthen result=make_long_form_name(amount,code,linked,passthrough);-- else result=make_short_form_name(amount,code,linked,passthrough); end if'none'==fmtthen-- no group separation result=result:gsub('(%d%d?%d?),','%1');-- strip comma separators elseif'gaps'==fmtthen-- use narrow gaps result=result:gsub('(%d%d?%d?),','<span style="margin-right:.25em;">%1</span>');-- replace comma seperators elseiffmtand'commas'~=fmtthen-- if not commas (the default) then error message return'<span style="font-size:inherit" class="error">{{currency}} – invalid format ([[Template:Currency/doc#Error_messages|help]])</span>'; end returnresult;-- done end --[[--------------------------< P A R S E _ F O R M A T T E D _ N U M B E R >---------------------------------- 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. ]] localfunctionparse_formatted_number(amount) localcount; localparts={}; localdigits={}; localdecimals; localsign=''; local_; ifamount:find('[^%-−%d%.,]')then-- anything but sign, digits, decimal points, or commas returnnil; end amount=amount:gsub('−','-');-- replace unicode minus with hyphen _,count=amount:gsub('%.','')-- count the number of decimal point characters if1<countthen returnnil;-- too many dots end _,count=amount:gsub(',','')-- count the number of grouping characters if0==countthen returnamount;-- no comma separators so we're done end; ifamount:match('[%-][%d%.,]+')then-- if the amount is negative sign,amount=amount:match('([%-])([%d%.,]+)');-- strip off and save the sign end parts=mw.text.split(amount,'.',true);-- split amount into digits and decimals decimals=table.remove(parts,2)or'';-- if there was a decimal portion, remove from the table and save it digits=mw.text.split(parts[1],',')-- split amount into groups fori,vinipairs(digits)do-- loop through the groups if1==ithen-- left-most digit group if(3<v:len()ornotis_set(v))then-- first digit group: 1, 2, 3 digits; can't be empty string (first char was a comma) returnnil; end else ifvand3~=v:len()then-- all other groups must be three digits long returnnil; end end end returnsign..table.concat(digits)..'.'..decimals;-- reassemble without commas and return end --[[--------------------------< C O N V E R T _ S T R I N G _ T O _ N U M E R I C >------------------------------------------------ Converts quantified number/string combinations to a number e.g. 1 thousand to 1000. ]] localfunctionconvert_string_to_numeric(amount) localquantifiers={['thousand']=1000,['million']=1000000,['m']=1000000,['billion']=1000000000,['b']=1000000000,['trillion']=1000000000000}; localn,q=amount:match('([%-−]?[%d%.,]+)%s*(%a+)$');-- see if there is a quantifier following a number; zero or more space characters ifnil==nthen n,q=amount:match('([%-−]?[%d%.,]+) (%a+)$')-- see if there is a quantifier following a number; nbsp html entity ({{format price}} output end ifnil==nthenreturnamountend;-- if not <number><space><quantifier> return amount unmolested n=n:gsub(',','');-- strip comma separators if present q=q:lower();-- set the quantifier to lower case ifnil==quantifiers[q]thenreturnamountend;-- if not a recognized quantifier returntostring(n*quantifiers[q]);-- return a string, not a number 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 ]] localfunctioncurrency(frame) localargs=require('Module:Arguments').getArgs(frame); localamount,code; locallong_form=false; locallinked=true; localpassthrough=false; ifnotis_set(args[1])then return'<span style="font-size:inherit" class="error">{{currency}} – invalid amount ([[Template:Currency/doc#Error_messages|help]])</span>'; end -- 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 ([[Template:Currency/doc#Error_messages|help]])</span>'; -- end -- This allows us to use {{currency}} while actually following [[MOS:CURRENCY]] as regards "billion", "million", "M", "bn", etc. ifnot(args['passthrough']=='yes')then-- just pass whatever string is given through. amount=convert_string_to_numeric(args[1]); amount=parse_formatted_number(amount);-- if args[1] can't be converted to a number then error ifnotamountthen return'<span style="font-size:inherit" class="error">{{currency}} – invalid amount ([[Template:Currency/doc#Error_messages|help]])</span>'; end else amount=args[1] end ifnotis_set(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 ifargs[3]then-- this is the |first= parameter TODO: make this value meaningful? y, yes, true? long_form=true; end if'no'==args[4]then-- this is the |linked= parameter; defaults to 'yes'; any value but 'no' means yes linked=false; end returnrender_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 }
Hidden categories:
