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

Este módulo é destinado principalmente a implementar o modelo {{Unidades}}.

Utilização

Funções exportáveis

  • unidade( frame ) – implementa o modelo unidades. Os parâmetros estão tomados seja ao nível do modelo que chama o módulo via #invoke, seja directamente na tabela proporcionada quando a função está chamada desde outro módulo. Tenta fazer parser a ambos primeiros parâmetros para facilitar o reconhecimento (por exemplo função com p.unidade{ '1.23 ±0.05 e5 m s-2' }) ;
  • _unidade( args ) – anuncia a unidade a partir dos parâmetros clássicos do modelo Unidades (exemplo p._unidade{ '1.23', 'm , '/s , '-2', ['±'] = '0.05', e='5' }) ;
  • formatNumeros( texto ) – formata todos os números da cadeia proporcionada que segue as convenções do português ;
  • formatNumero( número ) – transforma um número formatado ou não em cadeia formatada que segue as convenções do português ; se a cadeia não é reconhecida como um número, não é modificada ;
  • _formatNum( num ) – transforma um número, ou uma cadeia correspondente a um número em cadeia formatada que segue as convenções do português ; se o parâmetro não representa um número lua está devolvido sem modificação ;
  • parseNumero( numero ) – transforma se possível uma cadeia formatada num cadeia interpretável por tonumber() (devolve uma cadeia para evitar o arredondamento eventual de lua) ; as cadeias não reconhecidas estão devolvidas sem modificação.

Outras funções

  • sanitizeNum( numero ) – transforma os sinais menos em hífen, os espaços indivisíveis em espaços simples (simplifica os padrões posteriores) ;
  • parseUnit( texto ) – tenta separar uma cadeia em diferentes parâmetros do modelo unidade ;
  • nomeUnit( unit, exponente ) – devolve se possível o nome da unidade e seu exponente com todas as letras.

Módulos externos e outros elementos que este módulo tem necessidade para funcionar

  • Módulo:Unidade/Dados – Lista de unidades e de múltiplos, com a sua abreviatura e o seu nome em toda as letras.
  • Módulo:Delink – Utilizado para suprimir os vínculos das unidades para tentar de as reconhecer.

Exemplos

Outra documentação:

local p = {}

-- local Delink = require( 'Módulo:Delink' ) -- carregado apenas se necessário

-- Carregando o banco de dados de nomes de dispositivos com tratamento de erros.
local moduleData = 'Módulo:Unidade/Dados'
local dataSuccess, Data = pcall ( mw.loadData, moduleData )
if dataSuccess and type( Data ) == 'table' then
    dataSuccess = type( Data.unit ) == 'table'
        and type( Data.prefix ) == 'table'
        and type( Data.expoente ) == 'table'
end

local errorCat = '[[Categoria:!Uso de parâmetros defasados]]'
local addErrorCat = false

local supUnicode = { ['0'] = '⁰', ['1'] = '¹', ['2'] = '²', ['3'] = '³', ['4'] = '⁴', ['5'] = '⁵', ['6'] = '⁶', ['7'] = '⁷', ['8'] = '⁸', ['9'] = '⁹',
    ['+'] = '⁺', ['-'] = '⁻', ['='] = '⁼', ['('] = '⁽', [')'] = '⁾', ['n'] = 'ⁿ' }
local subUnicode = { ['0'] = '₀', ['1'] = '₁', ['2'] = '₂', ['3'] = '₃', ['4'] = '₄', ['5'] = '₅', ['6'] = '₆', ['7'] = '₇', ['8'] = '₈', ['9'] = '₉',
    ['+'] = '₊', ['-'] = '₋', ['='] = '₌', ['('] = '₍', [')'] = '₎',
    ['a'] = 'ₐ', ['e'] = 'ₑ', ['o'] = 'ₒ', ['x'] = 'ₓ', ['h'] = 'ₕ', ['k'] = 'ₖ', ['l'] = 'ₗ',
    ['m'] = 'ₘ', ['n'] = 'ₙ', ['p'] = 'ₚ', ['s'] = 'ₛ', ['t'] = 'ₜ',
    }
--- Cópia de Tools.trim aceitando números.
local function trim( texto )
    if type( texto ) == 'string' then
        texto = texto:gsub( '^%s*(%S?.-)%s*$', '%1' )
        if texto ~= '' then
            return texto
        end
    elseif type( texto ) == 'number' then
        return tostring( texto )
    end
end

function p.sanitizeNum( numero )
    if type( numero ) == 'number' then
        return tostring( numero )
    elseif type( numero ) == 'string' then
        if numero:match( '^%-?[%d.,]+$' ) then
            return numero
        end
        local result = numero
            -- substituindo os sinais menos ou en por um traço
            :gsub( '%−%f[%d]', '-')  -- U+2212
            :gsub( '−%f[%d]', '-')  -- html −
            :gsub( '\226\128[\146\147]%f[%d]', '-') -- U+2212, U+2213 (tiret numérique et demi-cadratin)
            -- substituindo espaços não separáveis por espaços simples
            :gsub( '\194\160', ' ' )
            :gsub( ' ', ' ' )
            :gsub( '\226\128[\128-\138\175]', ' ' ) -- U+2002 à U+200A et U+202F
            -- trim
            :gsub( '^%s*(%S?.-)%s*$', '%1' )
        return result
    else
        return ''
    end
end

---
-- parseNum se possível, converta uma string formatada em uma string interpretável para tonumber()
-- Devolva uma corrente para evitar qualquer arredondamento de lua.
-- se "número" for uma cadeia não reconhecida como um número pela função, retorne "número".
-- se "número" não é um número ou uma string retorna uma string vazia.
function p.parseNumero( numero )
    local result
    if type( numero ) == 'number' then
        return tostring( numero )
    else
        -- substituindo os sinais menos ou en por um traço
        result = p.sanitizeNum( numero )
        if result == '' then
            return ''
        elseif not result:match( '^%-?[%d., ]*%d$' ) then
            return numero
        end
    end

    -- excluir espaços
    result = result:gsub( ' ', '' )

    -- gerenciamento de pontos e vírgulas
    if result:match( '[.,]' ) then
        if result:match( '%d%.%d%d%d%.%d' ) then
            -- type 12.345.678
            result = result:gsub( '%.', '' ):gsub( ',', '.' )
        elseif result:match( '%d,%d%d%d,%d' ) -- type 1,234,567 ou 1.234,567,8
            or result:match( '%d,%d%d%d%.%d' )  -- formato anglo-saxon tipo 1,234.5
            or result:match( '%d%.%d%d%d,%d' ) -- tipo 1.123,56 (usado como um exemplo para separar decimais com a unidade do modelo antigo ou formatnum)
        then
            result = result:gsub( ',', '' )
        else
            result = result:gsub( ',', '.' )
        end
    end

    return result
end

---
-- _formantNum transforma um número ou uma string representando um número em uma string formatada de acordo com as convenções do português
-- si le paramètre ne représente pas un nombre lua il est retourné sans modification
-- se o parâmetro não representa um número lua ele é retornado sem modificação :
-- * round: arredondado para n casas decimais (pode ser negativo)
-- * casas decimais: número de casas decimais exibidas (pode ser negativo, neste caso equivalente a round)
-- * noHtml : não usa uma tag HTML para exibir o poder de 10 (para ser usado como um título)
function p.formatNum( num )
    local params = {}
    if type( num ) == 'table' then
        params = num
        num = params[1]
    end
    if type( num ) == 'number' then
        num = tostring( num )
    elseif type( num ) ~= 'string' or num == '' then
        return num
    end

    -- separação expoente
    local n, exponent = num:match( '^([-%d.]+)[eE]([+-]?%d+)$' )
    if exponent then
        num = n
        if params.noHtml then
            exponent = exponent:gsub('+?%f[%d]0', '' )
                :gsub( '[%d-]', supUnicode )
        else
            exponent = '<sup>' .. exponent:gsub('^%+?(%-?)0?', { ['-'] = '−', [''] = '' } ) .. '</sup>'
        end
        exponent = ' ×10' .. exponent
    else
        exponent = ''
    end

    -- arrendondar
    local decimals = tonumber( params.decimals )
    local round = tonumber( params.round ) or decimals
    if round and tonumber( num ) then
        local mult = 10 ^ round
        num = tostring( math.floor( num * mult + 0.5 ) / mult )
    end

    local menos, inteiro, fracao = num:match( '^(%-?)(%d*)%.?(%d*)$' )
    if not inteiro then
        return num
    end

    if menos == '-' then
        menos = '−' -- sinal menos (U+2212)
    end

    if inteiro == '' then
        inteiro = '0'
    elseif inteiro:len() > 3 then
        local ini = math.fmod( inteiro:len() - 1, 3 ) + 1
        inteiro = ( inteiro:sub( 1, ini ) or '') .. inteiro:sub( ini + 1 ):gsub( '(%d%d%d)', '\194\160%1' )
    end
    if fracao ~= '' or ( decimals and decimals > 0 ) then
        if decimals and decimals > #fracao then
            fracao = fracao .. string.rep( '0', decimals - #fracao )
        end
        if #fracao > 3 then
            fracao = ',' .. fracao:gsub( '(%d%d%d)', '%1\194\160' ):gsub( '\194\160$', '' )
        else
            fracao = ',' .. fracao
        end
    end

    return menos .. inteiro .. fracao .. exponent
end

---
-- formatNumber transforma um número formatado ou não formatado em uma string formatada de acordo com as convenções do português.
-- se a string não for reconhecida como um número, ela não será modificada.
function p.formatNumero( num, round, decimals )
    return p.formatNum{ p.parseNumero( num ), round = round, decimals = decimals }
end

--- formatNumeros transforma todos os números em uma string em um número formatado de acordo com as convenções do francês.
function p.formatNumeros( numeros, round, decimals )
    if type( numeros ) == 'number' then
        return p.formatNum( numeros, round, decimals )
    elseif type( numeros ) == 'string' then
        -- remova os números do marcador de tira
        local strip, i = {}, 0
        numeros = numeros:gsub(
            'UNIQ%-%-%a+%-%x%x%x%x%x%x%x%x%-QINU',
            function ( marker )
                i = i + 1
                strip[ tostring( i ) ] = marker
                return 'UNIQ^' .. i .. '¤QINU'
            end
        )
        --  formatação adequada
        numeros = p.sanitizeNum( numeros )
        local formatN = function ( n )
            return p.formatNumero( n, round, decimals )
        end
        numeros = numeros
            :gsub( '%-?%f[%d.,][%d., ]*%de[+-]?%d+', formatN )
            :gsub( '%-?%f[%d.,][%d., ]*%d', formatN )

        -- reintrodução do marcador de tira
        numeros = numeros:gsub( 'UNIQ^(%d+)¤QINU', strip )
        return numeros
    else
        return ''
    end
end

function p.parseUnit( texto )
    local toParse = p.sanitizeNum( texto )
    if toParse ~= '' then
        local result
        local specificArgs = {
            ['a'] = 'a',
            e = 'e',
            ou = 'ou',
            ['/'] = '/',
            ['–'] = '–', ['-'] = '–', -- meio em e traço
            ['±'] = '±', ['+-'] = '±', ['+/-'] = '±',
            ['+'] = '+',
            ['−'] = '−', -- sinal menos
            ['×'] = '×', x = '×', ['*'] = '×',
            ['××'] = '××', xx = '××', ['**'] = '××',
        }

        -- valor numérico
        local match, capture = toParse:match( '^(([%d., ]+%f[^d%(])%s*)' )
        local prefix
        if not match then
            -- caso em que o número é substituído por um ou mais pontos de interrogação
            match, prefix = toParse:match( '^((%?+)%s*)' )
        end
        if not match then
            -- caso ou um tipo de palavra "para", "aproximadamente" precede o número (palavra simples, sem acentuação para não complicar para casos de minoria)
            match, prefix, capture = toParse:match( '^(([%a]+[.,]?[: ]* )([+-]?%f[%d.,][%d., ]*%d%f[%D])%s*)' )
        end
        if not match then
            -- caso em que o número é precedido por um sinal, um símbolo ASCII ou seguido por uma inconsistência entre parênteses
            match, prefix, capture = toParse:match( '^(([(<>=~ ]*)([+-]?%f[%d.,][%d., ]*%d%(?%d*%)?)%s*)' )
        end
        if not match then
            -- caso em que o número é precedido por um símbolo ≤, ≥ ou ≈
            match, prefix, capture = toParse:match( '^((\226\137[\164\165\136] ?)([+-]?%f[%d.,][%d., ]*%d%f[%D])%s*)' )
        end
        result = { capture or false, prefix = prefix }
        if match then
            toParse = toParse:sub( match:len() + 1 )

            -- ponto de suspensões (ex π = 3.14159...)
            match = toParse:match( '^…%s*' )
            if not match then
                match, capture = toParse:match( '^%.%.%.%s*' )
            end
            if match then
                result[1] = result[1] .. '…'
                toParse = toParse:sub( match:len() + 1 )
            end

            -- fracao
            match, capture = toParse:match( '^((%d*/%d+)%s*)' )
            if not match then
                match, capture = toParse:match( '^((\194[\188-\190])%s*)' ) -- ¼ à ¾
            end
            if not match then
                match, capture = toParse:match( '^((\226\133[\144-\158])%s*)' ) -- ⅐ à ⅞
            end
            if match then
                if capture:match( '^/' ) then
                    local n = result[1]:match( ' %d+$' ) or result[1]:match( '^%d+$' )  or ''
                    result[1] = result[1]:sub( 1, -1 - #n )
                    result.fracao = n:gsub( '^ ', '' ) .. capture
                else
                    result.fracao = capture
                end
                toParse = toParse:sub( match:len() + 1 )
            end

            -- link com um segundo número
            local match2, conj, num = mw.ustring.match( toParse, '^(([àetouM+/−x*×±–-]+) ?(%-?%f[%d.,][%d., ]*%d%f[%D]%)?)%s*)' )
            if match2 and specificArgs[ conj ]
                and not ( specificArgs[ conj ] == '×' and mw.ustring.match( toParse, '^[×x] ?10 ?e') ) then
                result[ specificArgs[ conj ] ] = num
                toParse = toParse:sub( match2:len() + 1 )
            end
            if result['+'] or result['×'] then
                match2, conj, num = mw.ustring.match( toParse, '^(([x*×−-]) ?(%-?%f[%d.,][%d., ]*%d%f[%D])%s*)' )
                if match2 then
                    if specificArgs[ conj ] == '×' then
                        result['××'] = num
                    else
                        result['−'] = num
                    end
                    toParse = toParse:sub( match2:len() + 1 )
                end
            end
        end

        -- 10 ao poder   ( \195\151 = ×, sinal multiplicar)
        match, capture = toParse:match( '^(%s*e(%-?%d+)%s*)' )
        if not match then
            match, capture = toParse:match( '^(%s*[x\195]\151?10e(%-?%d+)%s*)' )
        end
        if match then
            result.e = capture
            toParse = toParse:sub( match:len() + 1 )
        end

        -- unidade
        if Data.unit[ toParse ] or mw.ustring.match( toParse, '^%a+$' ) or toParse:match( '%b<>' ) then
            table.insert( result, toParse )
            toParse = ''
        elseif toParse ~= '' then
            local unit, exp
            toParse = toParse:gsub( '²', '2' ):gsub( '³', '3' )
            repeat
                -- unidade contendo um link
                match, unit, exp = mw.ustring.match( toParse, '^((/?[^%s%d/%[%]]*%b[][^%s%d/]*) ?(%-?%d*)%s*)' )
                if not match then
                    -- unidade que não contém um link
                    match, unit, exp = mw.ustring.match( toParse, '^((/?[^%s%d/]+) ?(%-?%d*)%s*)' )
                end
                if not match then
                    -- l/100 km
                    match, unit, exp = mw.ustring.match( toParse, '^((/100 ?[^%s%d/]+) ?(%-?%d*)%s*)' )
                end
                if match then
                    if unit:match( '%-$' ) and exp ~= '' then
                        unit = unit:gsub( '%-$', '' )
                        exp = '-' .. exp
                    elseif exp == '-' then
                        unit = match
                        exp = ''
                    end
                    if Data.unit[ unit ] or mw.ustring.match( unit, '[%a€£$«»]' ) then
                        table.insert( result, unit )
                        table.insert( result, exp )
                        toParse = toParse:sub( match:len() + 1 )
                    else
                        break
                    end
                end
            until toParse == '' or not match
        end

        if toParse == '' then
            if #result > 1 and result[ #result ] == '' then
                result[ #result ] = nil
            end
            return result
        else
            -- parte da cadeia não pode ser decodificada, retornamos a cadeia original
            addErrorCat = true
            return { texto }
        end
    else
        return { }
    end
end

---
-- nomeUnit retorna o nome português do código de uma unidade e seu expoente.
-- Se o código da unidade não for reconhecido, retorne 1 e false, de forma que false seja adicionado à primeira posição de uma tabela.
function p.nomeUnit( unit, expoente )
    if not dataSuccess or type( unit ) ~= 'string' then
        return 1, false
    end

    -- limpeza de link e tag HTML
    unit = unit:gsub( '^/' , '' )
    if unit:match( '%[' ) then
        local Delink = require( 'Módulo:Delink' )
        unit = Delink._delink{ unit }
    end
    if unit:match( '<' ) then
        unit = unit:gsub( '%b<>', '' )
    end

    -- obter o nome da unidade
    local unitTab = Data.unit[ unit ]
    local unitPrefix = { nome = '' }
    if not unitTab then
        unitTab = Data.unit[ unit:sub( 2 ) ]
        unitPrefix = Data.prefix[ unit:sub( 1, 1 ) ]
        if not ( unitTab and unitPrefix ) then
            -- para µ, Ki, Mi, Gi... que são de byte duplo
            unitTab = Data.unit[ unit:sub( 3 ) ]
            unitPrefix = Data.prefix[ unit:sub( 1, 2 ) ]
            if not ( unitTab and unitPrefix ) then
                unitTab = false
            end
        end
    end

    -- obter o nome do expositor
    if trim( expoente ) then
        local exp = tonumber( expoente )
        exp = exp and Data.expoente[ math.abs( exp ) ]
        expoente = exp or ' poder ' .. expoente
    else
        expoente = ''
    end

    -- monte as duas partes
    if type( unitTab ) == 'table' and type( unitTab.nome ) == 'string' then
        return unitPrefix.nome .. unitTab.nome .. expoente
    elseif unit:match( '[/%d]' ) then
        -- não é um texto simples, nós cancelamos a dica de ferramenta
        return 1, false
    else
        return unit .. expoente
    end
end

function p._unidade( args )
    -- substituindo certos caracteres, para simplificar o padrão
    local numero = p.sanitizeNum( args[1] )
    if numero == '' then
        numero = nil
    else
        -- formatação de números
        numero = p.formatNumeros( numero, args.arrondi, args['decimais'] )
    end

    local wiki = { args.prefix or '', numero }   -- prefix é um parâmetro interno definido por p.parseUnit, especialmente útil quando {{unidade}} é usado em infoboxes
    -- fraction
    if args.fraction then
        local nome, den = args.fraction:match( '^(.-)/(.+)$' )
        if nome then
            if nome:match( '^[ %dn()=+-]+$' ) and den:match( '^[ %daeoxhklmnpst()=+-]$' ) then
                nome = nome:gsub( '[%dn()=+-]', supUnicode )
                den = den:gsub( '[%daeoxhklmnpst()=+-]', subUnicode )
            else
                nome = '<sup style="font-size: 70%; vertical-align: 0.4em;">' .. nome .. '</sup>'
                den = '<sub style="font-size: 70%; vertical-align: 0em;">' .. den .. '</sub>'
            end
            args.fraction = nome .. '⁄' .. den
        end

        if numero then
            table.insert( wiki, '\194\160' )
        end
        table.insert( wiki, args.fraction )
    end

    -- à, e, ou, ×, – (em traço)
    local specificArgs = { '–', 'à', 'e', 'ou', '/', '×', '××', '±'    }
    for _, name in ipairs( specificArgs ) do
        local v = trim( args[ name ] )
        if v then
            v = p.formatNumeros( v )
            if name == '–' and numero and numero:match( '^[^−]' ) and v:match( '^[^−]' ) then
                -- não há espaço para o traço entre dois números positivos
                table.insert( wiki, '–' )
            elseif name == '/' then
                -- espaço en (U+2002 ou &ensp;) com /
                table.insert( wiki, ' / ' )
            elseif name == '××' then
                table.insert( wiki, '\194\160×\194\160' )
            elseif name == '×' or name == '±' then
                table.insert( wiki, '\194\160' .. name .. '\194\160' )
            else
                table.insert( wiki, ' ' .. name .. ' ' )
            end
            table.insert( wiki, v )
        end
    end

    -- análise de unidade para conversão (mas será exibida somente após a incerteza + e -)
    local i = 1
    local unit = trim( args[ 2 * i ] )
    local units = ''
    local nomeUnits, par = {}, false
    while unit do
        local exp = p.parseNumero( args[ 2 * i + 1 ] )
        local sep = ''
        -- gestion des expoentes
        local expUnit = ''
        if exp == '' then
            if unit:sub( -2 ) == '²' then
                exp = '2'
                unit = unit:sub( 1, -3 )
            elseif unit:sub( -2 ) == '³' then
                exp = '3'
                unit = unit:sub( 1, -3 )
            end
        end
        if #exp > 0 then
            expUnit = '<sup>' .. exp:gsub( '^-', '−') .. '</sup>'  -- substitua o traço por um sinal de menos verdadeiro
        end
        -- gestão da separação de unidades e unidades no denominador
        if units ~= '' then
            if unit:sub( 1, 1 ) == '/' then
                sep = '/'
                unit = unit:sub( 2 )
                if not par then
                    par = true
                    table.insert( nomeUnits, 'por' )
                end
            else
                sep = '\194\160'  -- point médian désactivé : '⋅\194\160'
            end
        end
        if exp:match( '^-' ) and not par then
            par = true
            table.insert( nomeUnits, 'por' )
        end
        -- substituindo a unidade por seu símbolo
        if Data.unit[ unit ] then
            -- unit = Data.unit[ unit ].simbolo
            -- desativado porque não manipula múltiplos como mL
        end
        units = units .. sep .. unit .. expUnit
        table.insert( nomeUnits, p.nomeUnit( unit, exp ) )
        i = i + 1
        unit = trim( args[ 2 * i ] )
    end

    -- conversão
    local unitNameString = nomeUnits[1] and table.concat( nomeUnits, ' ' ) or ''
    unitNameString = mw.ustring.gsub( unitNameString, '(%a)s%f[%A]', '%1' )
    local multiple = 1
    local convertTable = Data.convert[ unitNameString ]
    if not convertTable and #unitNameString > 5 then
        -- gestão de múltiplos (Kilo, mega, mili ...)
        local prefix = Data.prefix[ unitNameString:sub( 1, 4 ) ] or Data.prefix[ unitNameString:sub( 1, 5 ) ]
        local _, par = unitNameString:find( ' por ' )
        local prefix2
        if par then
            prefix2 = Data.prefix[ unitNameString:sub( par + 1, 4 ) ] or Data.prefix[ unitNameString:sub( par + 1, 5 ) ]
        end
        if prefix and Data.convert[ unitNameString:gsub( '^' .. prefix.nome, '' ) ] then
            convertTable = Data.convert[ unitNameString:gsub( '^' .. prefix.nome, '' ) ]
            multiple = 10 ^ prefix.poder
        elseif prefix2 and  Data.convert[ unitNameString:gsub( ' por ' .. prefix2.nome, '' ) ] then
            convertTable = Data.convert[ unitNameString:gsub( ' por ' .. prefix2.nome, '' ) ]
            multiple = 1 / 10 ^ prefix2.poder
        elseif prefix and prefix2 and Data.convert[ unitNameString:gsub( '^' .. prefix.nome, '' ):gsub( ' por ' .. prefix2.nome, '' ) ] then
            convertTable = Data.convert[ unitNameString:gsub( '^' .. prefix.nome, '' ):gsub( ' por ' .. prefix2.nome, '' ) ]
            multiple = 10 ^ prefix.poder / 10 ^ prefix2.poder
        end
    end
    if convertTable then
        if type( convertTable[1] ) ~= 'table' then
            convertTable = { convertTable }
        end
        for i, v in ipairs( wiki ) do
            local n = tonumber( p.parseNumero( v ) )
            if n then
                n = n * 10 ^ ( tonumber( p.parseNumero( args.e ) ) or 0 )
                local converted = {}
                for _, c in ipairs( convertTable ) do
                    local nConverted = n
                    if c.inverse then
                        nConverted = 1 / n
                    end
                    if c.M then
                        -- M = masse molaire
                        local M = tonumber( args.M )
                        if not M then
                            break
                        end
                        if c.M == '*' then
                            nConverted = nConverted * M
                        elseif c.M == '/' then
                            nConverted = nConverted / M
                        end
                    end
                    nConverted = nConverted * multiple * c[2] + ( c[3] or 0 )
                    -- format
                    nConverted = p.formatNum{ nConverted, round = c.round or 6, noHtml = true }
                    local sep = ' '
                    if mw.ustring.sub( c[1], 1, 1 ) == '°' then
                        sep = ''
                    end
                    mw.log( c[1], mw.ustring.codepoint( sep, 1, 1))
                    table.insert( converted, nConverted .. sep.. c[1] )
                end
                wiki[ i ] = '<span title="' .. table.concat( converted, ' ou ' ) ..'">' .. v ..'</span>'
            end
        end
    end

    -- incerteza com + e - separados
    if trim( args['+'] ) then
        local approximation = '+' .. p.formatNumero( args['+'] ) .. ''
        if trim( args['−'] ) then
            approximation = approximation .. '<br> −' .. p.formatNumero( args['−'] )
        end
        table.insert( wiki, '<span class="nowrap"><span style="display:inline-block; padding-left:0.2em; vertical-align:top; line-height:1em; font-size:80%; text-align:left;">' )
        table.insert( wiki, approximation .. '</span></span>' )
    end

    -- poder de 10
    local expoente = trim( args.e )
    if expoente then
        expoente = p.formatNumero( expoente )
        if numero then
            if trim( args['±'] ) and not numero:match( '^%(' ) then
                table.insert( wiki, 1, '(' )
                table.insert( wiki, ')' )
            end
            table.insert( wiki, '\194\160× 10<sup>' .. expoente .. '</sup>' )
        else
            table.insert( wiki, '10<sup>' .. expoente .. '</sup>' )
        end
    end

    if units ~= '' then
        local sep = '\194\160'
        if not ( numero or args.fraction or expoente ) then
            sep = ''
        else
            local simbolo = Data.unit[ units ] and Data.unit[ units ].simbolo
            if simbolo == '°' or simbolo == '′' or simbolo == '″' then
                sep = ''
            end
        end
        -- adicione uma abreviação se o nome da unidade for diferente da unidade (removendo os espaços que podem se tornar indivisíveis)
        if nomeUnits[1] and table.concat( nomeUnits ):gsub( ' ', '' ):gsub( '\194\160', '' ) ~= units:gsub( ' ', '' ):gsub( '\194\160', '' ) then
            units = string.format( '<abbr class=abbr title="%s">%s</abbr>', table.concat( nomeUnits, ' ' ), units )
        end
        table.insert( wiki, sep .. units )
    end

    if #wiki > 0 then
        return table.concat( wiki )
    end
end

function p.unidade( frame )
    local args
    if type( frame ) == 'table' then
        if type( frame.getParent ) == 'function' then
            args = frame:getParent().args;
        else
            args = frame
        end
    end
    if args then
        args[1] = trim( args[1] ) or false
        if args[1] then
            if args[1]:match('[^%d,. -]') then
                local tempArgs = p.parseUnit( args[1] )
                if not ( args[2] and tempArgs[2] ) then
                    for k, v in pairs( tempArgs ) do
                        args[k] = v
                    end
                end
            end
            args[2] = trim( args[2] ) or false
            if args[2] and not args[3] and args[2]:match('/') then
                local tempArgs = p.parseUnit( args[2] )
                args[2] = false
                if tempArgs[1] ~= false then
                    table.insert( tempArgs, 1, false )
                end
                for k, v in pairs( tempArgs ) do
                    if args[k] and v then
                        addErrorCat = true
                    end
                    args[k] = args[k] or v
                end
            end
        end
        -- args alias
        args['×'] = args['×'] or args['x']  -- letra x → sinal multiplicação
        args['±'] = args['±'] or args['+-'] or args['+/-']
        if args['+'] then
            args['−'] = args['−'] or args['-'] -- traço → sinal menos
        else
            args['–'] = args['–'] or args['-'] -- traço → traço
        end
        local cat = ''
        if addErrorCat then
            cat = errorCat
        end
        return p._unidade( args ) .. cat
    end
end

return p