Documentação do módulo[ver] [editar] [histórico] [purgar]

Este código é chamado de {{Moeda}}. Todos os parâmetros da predefinição são passados no frame do módulo.

{{#invoke:Moeda|currency|<valor>|<código>|<primeiro>|<link>|<passthrough>}}

Veja o código da predefinição para a descrição dos parâmetros.

Outros módulos podem utilizar este código. O ponto de entrada para outros módulos é _render_currency (amount, code, long_form, linked). Ver a função render_currency() para detalhes.

O ficheiro de dados Módulo:Moeda/dados tem as características necessárias para mostrar as moedas.

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;


--[[--------------------------< 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


--[[--------------------------< 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, linked, passthrough)
    local symbol;
    local position = properties[code].position;

    if linked then
        symbol = string.format ('[[%s|%s]]', properties[code].page, properties[code].symbol);    -- make wikilink of page and symbol
    else
        symbol = properties[code].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&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 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 .. ' <span style="font-size:inherit" class="error">{{currency}} – definição com posição em falta ([[Template:Moeda/doc#Mensagens_de_erro|ajuda]])</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)

]]

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 ('[[%s]]s', page);
        else
            return string.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

]]

local function make_long_form_name (amount, code, linked, passthrough)
    local name, formatted;
    
    if not is_set (properties[code].page) then
        return '<span style="font-size:inherit" class="error">{{currency}} – definição com página em falta ([[Template:Moeda/doc#Mensagens_de_erro|ajuda]])</span>';
    end

    if not passthrough then
        amount = tonumber (amount);                                                -- make sure it's a number
    end

    if 1 == amount then
        name = make_name (linked, properties[code].page);                        -- the singular form
    elseif is_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
    
    if not passthrough then
        formatted = lang:formatNum (amount)
    else
        formatted = amount
    end

    return string.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.

]]

local function render_currency (amount, code, long_form, linked, fmt, passthrough)
    local name;
    local result;

    presentation = mw.loadData ('Module:Moeda/dados');                -- get presentation data

    if presentation.currency_properties[code] then                                -- if code is an iso 4217 code
        properties = presentation.currency_properties;
    elseif presentation.code_translation[code] then                                -- not iso 4217 but can be translated
        code = presentation.code_translation[code];                                -- then translate
        properties = presentation.currency_properties;
    elseif presentation.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}} – código inválido ([[Template:Moeda/doc#Mensagens_de_erro|ajuda]])</span>';
    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?),', '<span style="margin-right:.25em;">%1</span>');    -- replace comma seperators
    elseif fmt and 'commas' ~= fmt then                                            -- if not commas (the default) then error message
        return '<span style="font-size:inherit" class="error">{{currency}} – formato inválido ([[Template:Moeda/doc#Mensagens_de_erro|ajuda]])</span>';
    end

    return result;                                                                -- 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.

]]

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 < count then
        return nil;                                                                -- too many dots
    end

    _, count = amount:gsub(',', '')                                                -- count the number of grouping characters
    if 0 == count then
        return amount;                                                            -- no comma separators so we're done
    end;
    
    if amount: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
    for i, v in ipairs (digits) do                                                -- loop through the groups
        if 1 == i then                                                            -- left-most digit group
            if (3 < v:len() or not is_set (v)) then                                -- first digit group: 1, 2, 3 digits; can't be empty string (first char was a comma)
                return nil;
            end
        else
            if v and 3 ~= v:len() then                                            -- all other groups must be three digits long
                return nil;   
            end
        end
    end

    return sign .. 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.

]]

local function convert_string_to_numeric (amount)
    local quantifiers = {[{'thousand', 'milhar'}] = 1000, [{'million', 'milhão'}] = 1000000, ['m'] = 1000000, [{'billion', 'bilhão', 'bilião'}] = 1000000000, ['b'] = 1000000000, [{'trillion', 'trilhão', 'trilião'}] = 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%.,]+)&nbsp;(%a+)$')                    -- see if there is a quantifier following a number; nbsp html entity ({{format price}} output
    end
    if nil == n then return amount end;                                            -- 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

    if nil == quantifiers[q] then return amount end;                            -- if not a recognized quantifier
    
    return tostring (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

]]

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 (args[1]) then
        return '<span style="font-size:inherit" class="error">{{currency}} – valor inválido ([[Template:Moeda/doc#Mensagens_de_erro|ajuda]])</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}} – valor inválido ([[Template:Moeda/doc#Mensagens_de_erro|ajuda]])</span>';
--    end

    -- This allows us to use {{currency}} 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 (args[1]);
        amount = parse_formatted_number(amount);                                -- if args[1] can't be converted to a number then error
        if not amount then
            return '<span style="font-size:inherit" class="error">{{currency}} – valor inválido ([[Template:Moeda/doc#Mensagens_de_erro|ajuda]])</span>';
        end
    else
        amount = args[1]
    end
    
    if not is_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
    
    if args[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

    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
    }