A documentação para este módulo pode ser criada na página Módulo:Língua/doc

local Lingua = { }

local dadosLingua = mw.loadData 'Módulo:Língua/Dados'
local langErrorMess = '<span class="error">lingua não reconhecido : %s</span>'

-- primeiroParametro retorna o primeiro parâmetro de Frame, que foi passado para o módulo por invocação, diretamente para o modelo,
-- ou dentro de um módulo como uma string em uma matriz ou diretamente como uma string.
-- Se nenhum desses argumentos contiver uma string, a função retornará nil.
-- Se o segundo parâmetro for true, a string será retornada truncada e em minúsculas.
local function primeiroParametro( frame, lowerCase )
    local arg
    if type( frame ) == 'table' then
        arg = ( frame.getParent and ( frame.args[1] or frame:getParent().args[1] ) ) or frame[1]
    elseif type( frame ) == 'string' then
        arg = frame
    end
    if type( arg ) ~= 'string' then
        arg = nil
    end
    if arg and lowerCase then
        arg = mw.ustring.lower( mw.text.trim( arg ) )
    end
    return arg
end

-- O codigodeterminacao retorna uma tabela contendo o código do lingua principal e a lista de subcódigos
-- se o código do lingua principal não for reconhecido, retorna nulo.
function Lingua.codigodeterminacao( lingua )
    if type( lingua ) == 'string' and lingua ~= '' then
        local tabCode = mw.text.split( lingua, '-' )
        local tabLingua = dadosLingua[ mw.ustring.lower( tabCode[1] ) ]
        if tabLingua and tabLingua.code then
            tabCode[1] = tabLingua.code
            if tabLingua.invalido then
                tabCode.invalido=true
            end
            return tabCode
        end
    end
end

-- Ver Modelo:Code lingua
-- Parâmetro :
--     1  : nome de lingua.
function Lingua.codeLingua( frame )
    local arg = primeiroParametro( frame, true )
    local tabCode = Lingua.codigodeterminacao( arg )
    return ( tabCode and table.concat( tabCode, '-' ) ) or arg or ''
end

-- Ver Modelo:Code lingua 2
-- Parâmetro :
--     1  : nome de lingua.
function Lingua.codeLingua2( frame )
    local arg = primeiroParametro( frame, true )
    local tabCode = Lingua.codigodeterminacao( arg )
    return ( tabCode and table.concat( tabCode, '-' ) ) or ''
end


-- Ver Modelo:Direction lingua
-- Parâmetro :
--     1  : nome de lingua ou code IETF.
function Lingua.directionLingua( frame )
    local arg = primeiroParametro( frame, true )
    if type( arg ) ~= 'string' or arg == '' then
        return 'ltr'
    end
    -- separação do código do lingua no código principal e no subcódigo diferente.
    local tabCode = Lingua.codigodeterminacao( arg )
    if tabCode then
        -- nós tentamos saber se a direção é da direita para a esquerda
        local codeScript = tabCode[2]
        if codeScript and string.len( codeScript ) == 4 and dadosLingua[ 'rtl script' ] then
            -- existe um subcódigo de escrita, é ele quem é levado em conta
            codeScript = string.upper( string.sub( codeScript, 1, 1 ) ) .. string.sub( codeScript, 2 )
            if dadosLingua[ 'rtl script' ][ codeScript ] then
                return 'rtl'
            end
        else
            -- não há subcódigo escrito, levamos em conta o código do lingua principal.
            local tabLingua = dadosLingua[ tabCode[1] ]
            if tabLingua and tabLingua.rtl then
                return 'rtl'
            end
        end
    end
   
    -- o lingua não é escrito da direita para a esquerda, então ltr.
    return 'ltr'
end


-- Ver Modelo:Nome lingua
-- Parâmetro :
--     1  : code IETF de lingua.
function Lingua.nomeLingua( frame )
    local arg = primeiroParametro( frame, true )
    if type( arg ) ~= 'string' or arg == '' then
        return '<span class="error">lingua não informado</span>'
    end
    local tabLingua = dadosLingua[ arg ]
    if tabLingua == nil then
        tabLingua = dadosLingua[ mw.ustring.match( arg, '^(%a-)%-' ) ]
    end
    if not tabLingua then
        return string.format( langErrorMess, arg )
    end
    if type( tabLingua.pagina ) == 'string' then
        if tabLingua.pagina ~= '' then
            return '[[' .. tabLingua.pagina .. '|' .. tabLingua.nome .. ']]'
        else
            return tabLingua.nome
        end
    else
        return '[[' .. tabLingua.nome .. ']]'
    end
end


-- Ver Modelo:Lingua
-- Parâmetros :
--    1 : code IETF de lingua ;
--    texto ou 2 : texto neste lingua ;
--    trans : transliteração do texto ;
--    dir : direção do lingua (obsoleto: pode estar no parâmetro 1, com código em 2 e texto em 3).
function Lingua.lingua( frame )
    local args = ( frame.getParent and frame:getParent().args ) or frame         -- chamar preparação por modelo ou direto.
    local code = mw.ustring.lower( mw.text.trim( args[1] or '' ) )
    local texto = args.texto or ''
    if texto == '' then
        texto = args[2] or ''
    end
    local dir = args.dir
    local namespaceCategorizacao = { [0] = true, [4] = true, [10] = true, [12] = true, [14] = true, [100] = true }
    local categorizacao = namespaceCategorizacao[ mw.title.getCurrentTitle().namespace ] and not args.nocat
   
    -- Parâmetros de deslocamento se o código contiver a direção do texto (obsoleto, mas ainda possível).
    if code == 'ltr' or code == 'rtl' then
        dir = code
        code = mw.ustring.lower( mw.text.trim( args[2] ) or '' )
        texto = args[3] or ''
    end
    local codeArg = code
   
    -- saída imediata se não houver texto
    if texto == '' then
        if categorizacao then
            return '<span class="error">erro da predefinição [[Predefinição:Lingua|{{lingua}}]] : texto em falta</span>[[Categoria:!Páginas com erros no destaque de sintaxe|Lingua]]'
        else
            return ''
        end
    end
   
    -- récursion si texto contient des blocs
    if texto:match('\n *\n') or texto:match('\n[*#:]') then
        -- les parenthèses permettent d'éliminer la seconde valeur retournée par gsub (nombre de remplacement)
        return ( texto:gsub(
             '(\n?[*#:]?%s*)([^\n]+)',
             function ( init, linha )
                 return init .. Lingua.lingua{ code, linha }
             end
         ) )
    end
   
    -- Se o lingua é reconhecido, o valor da tabela é levado em conta (torna possível corrigir os nomes do lingua em letras inteiras).
    local tabCode = Lingua.codigodeterminacao( code )
    local tabLingua
    if tabCode then
        code = table.concat( tabCode, '-' )
        tabLingua = dadosLingua[ tabCode[1] ]
       
        local codeScript = tabCode[2]
        -- Se codeScript é um estilo de escrita (len = 4) aplicamos sua direção
        if codeScript and  string.len( codeScript ) == 4 and dadosLingua[ 'rtl script' ] then
            -- Formatação de tipo latim correspondente ao formato em data Language ['rtl script']
            codeScript = string.upper( string.sub(  codeScript, 1, 1 ) ) .. string.sub(  codeScript, 2 )
            tabLingua = { code = tabLingua.code,
                rtl = dadosLingua[ 'rtl script' ][ codeScript ],
                invalido = tabLingua.invalido
            }
        end
    end
       
    -- Preparação da renderização de direção de texto.
    dir = dir and dir:lower()
    if dir ~= 'ltr' and dir ~= 'rtl' then
        dir = ( tabLingua and tabLingua.rtl and 'rtl' )
    end

    -- Compilação do texto para retornar.
    local html = mw.html.create( '' )
    if code == '' then
        html:wikitext( texto )
    else
        html:tag( 'span' )
                :addClass( 'lang-' .. code )
                :addClass( args.class )
                :attr{ lang = code, dir = dir }
                :wikitext( texto )
                :done()
       
        -- Transliteração.
        if ( args.trans or '' ) ~= '' then
            local trans = args.trans:gsub( "^''([^'].*[^'])''$", "%1" )
            html:wikitext( " (''" )
                :tag( 'span' )
                    :addClass( 'transcription lang-' .. code )
                    :attr( 'lang', code .. '-Latn' )
                    :attr( 'dir', 'ltr' )
                    :wikitext( trans )
                    :done()
                :wikitext( "'')" )
        end

    end

    -- Adição da categoria Pagina com código de lingua inválido se o código do lingua não for reconhecido ou inválido.
    if categorizacao and ( type( tabLingua ) ~= 'table' or tabCode.invalido ) then
        local erreur = string.format( langErrorMess, codeArg )
        if codeArg == '' then
            erreur = '<span class="error">lingua não informado</span>'
        end
        html:wikitext( '[[Categoria:!Erro no código de língua|Lingua]] ' .. erreur )
    end

    return tostring( html )
end

-- Alias nome de função
Lingua.lang = Lingua.lingua


-- Ver Modelo:Languagueicon
-- Parâmetros :
--    1 : nome de lingua ;
--    2 : código IETF ;
--    texto : texto neste lingua ;
--    dir : direção do lingua.
function Lingua.languagueIcon( frame )
    local args = ( frame.getParent and frame:getParent().args ) or frame
    local nomeLingua = mw.text.trim( args[1] or '' )
    local code = mw.text.trim( args[2] or '' )
    local texto = args.texto
    local dir = args.dir
    local wikiText = ''
    -- Caso em que o primeiro e / ou o segundo parâmetro está vazio.
    if code == '' and nomeLingua == '' then
        return texto
    elseif nomeLingua == '' then
        nomeLingua = dadosLingua[ mw.ustring.lower( code ) ]
        nomeLingua = (nomeLingua and nomeLingua.nome or '???')
    elseif code == '' then
        code = dadosLingua[ nomeLingua ]
        code = ( code and code.code or '' )
        if code == '' then
            return texto
        end
    end
    -- Gestão do texto.
    if texto and texto ~= '' then
        texto = '\194\160' .. Lingua.lang{ code, dir = dir, texto = texto }
    else
        texto = ''
    end
    -- Compilação do indicador de lingua e da mensagem de texto.
    local html = mw.html.create()
    html:tag( 'abbr' )
            :addClass( 'abbr' )
            :addClass( 'indicador-lingua' )
            :attr( 'title', 'Língua : ' .. nomeLingua )
            :wikitext( '(' .. code .. ')' )
            :done()
        :wikitext( texto )
   
    return tostring( html )
end

-- Ver Modelo:Mul
-- Parâmetros : Códigos IETF ou nomes de linguas, em números indefinidos (apenas string ou nula).
function Lingua.indicationMultilingue( frame )
    local args = (frame.getParent and frame:getParent().args) or frame
    local listeNome = { }
    local listeCode = { }
    local tabLingua
    -- Valor padrão do primeiro parâmetro = 'mul'.
    local code = mw.text.trim( args[1] or '' )
    if code == '' then
        code = 'mul'
    end
   
    if not args[2] and not dadosLingua[ mw.ustring.lower( code ) ] then
        local split = mw.text.split( code, '[+,;:/ .]+' )
        if #split > 1 then
            return Lingua.indicationMultilingue( split )
        end
    end
    -- Adicionado nomes e códigos de lingua para cada parâmetro em listName e ListCode.
    local i = 1
    repeat
        code = mw.ustring.lower( code )
        local tabLingua = dadosLingua[ code ]
        if not tabLingua then
            code = mw.text.split( code, '-' )[1]
            tabLingua = dadosLingua[ code ]
        end
        if type( tabLingua ) == 'table' then
            table.insert( listeNome, tabLingua.nome )
            table.insert( listeCode, tabLingua.code )
        else
            table.insert( listeNome, '???' )
            local erros = string.format( langErrorMess, code )
            table.insert( listeCode, erros )
        end
        i = i + 1
        code = mw.text.trim( args[i] or '' )
    until code == ''
   
    -- Preparação e devolução do texto.
    local n = #listeCode
    if n == 0 then
        return ''
    end
    local varios = ( n > 1 )
   
    local html = mw.html.create( 'abbr' )
        :addClass( 'abbr' )
        :addClass( 'indicador-lingua' )
        :attr( 'title', 'Língua' .. ( varios and 's' or '' ) .. ' : ' .. mw.text.listToText( listeNome ) )
        :wikitext( '(' .. table.concat( listeCode, '\194\160+\194\160' ) .. ')' )
   
    return tostring( html )
end


-- Ver Modelo:Lingua com nome
-- Parâmetros :
--    1 : code IETF de lingua ;
--    texto ou 2 : texto neste lingua ;
--    trans : transliteração do texto ;
--    dir : direção  do lingua.
function Lingua.linguaAvecNome( frame )
    local args = ( frame.getParent and frame:getParent().args ) or frame
    local code = mw.ustring.lower( mw.text.trim( args [1] or '') )
    local texto = args.texto or args[2] or ''
    local trans = args.trans
    local dir = args.dir
    local wikiText = ''

    -- Detectando a direção da mensagem de texto.
    if code == 'ltr' or code == 'rtl' then
        dir = code
        code = mw.ustring.lower( mw.text.trim( args[2] ) )
        texto = args[3] or ''
    end

    -- Definição do nome do lingua em francês.
    local nome = Lingua.nomeLingua{ code }

    if texto ~= '' then
        texto = '\194\160' .. Lingua.lang{ code, dir = dir, texto = texto, trans = trans }
    end

    wikiText = nome .. ' :' .. texto

    return wikiText
end

---
-- latinOnly determina se a string fornecida contém apenas caracteres latinos (extended, unicode <880)
function Lingua.nonLatin( frame )
    local texto = primeiroParametro( frame )
    for codepoint in mw.ustring.gcodepoint( texto ) do
        if codepoint > 879 and not (                  --  > 036F
            codepoint > 7423 and codepoint < 7936 or  -- suplementos fonéticos, diacríticos e latinos (1D00 a 1EFF)
            codepoint > 8191 and codepoint < 11392 or -- espaço, índices, monaies e vários símbolos (2000 a 2C7F)
            codepoint > 42783 and codepoint < 43008   -- latim Lat. Extenso D (A720 a A7FF)
        ) then
            return true
        end
    end
    return false
end

-- exibe uma mensagem de erro se o Módulo: Lingua / Dados não foi carregado corretamente,
-- para a página de discussão do banco de dados e para aqueles que desejam monitorar esta página.
function Lingua.errosModuleData()
    if type( dadosLingua ) ~= 'table'  then
        local message = [[<strong class="error">O carregamento de módulo:Língua/Dados gerou um erro : </strong> <br>%s <br>

<span class="error">Este erro deve ser corrigido tão rapidamente quanto milhares de páginas não são exibidas corretamente.</span>
]]
        return string.format( message, resultado )
    end
end

-- tabelaLinguas gera uma matriz classificável da lista de linguas disponíveis em Module:lingua/Dados.
function Lingua.tabelaLinguas()
    local tabela = { }
    local cabecalho = [[{| class="wikitable alternance sortable"
|-
!scope=col|Alias
!scope=col|Código IETF
!scope=col|Nome principal
!scope=col|Página (se diferente do nome)
!scope=col|RTL
!scope=col|Inválido]]

    local linhaTab, linhaSrt
    for i, v in pairs( dadosLingua ) do
        if v.code then
            linhaTab = {
                i,
                v.cod,
                v.nome,
                v.pagina or '',
                v.rtl and '[[Image:Yes check.svg|15px|oui|lien=]]' or '',
                v.invalido and '[[Image:Yes check.svg|15px|oui|lien=]]' or '',
            }
            linhaSrt = table.concat( linhaTab, '\n|' )
            table.insert( tabela, linhaSrt )
        end
    end
    table.sort( tabela )
    table.insert( tabela, 1, cabecalho )
    table.insert( tabela, '}' )
   
    return table.concat( tabela, '\n|-\n|' )
end

-- listAliasCode gera uma lista; os linguas são da seguinte forma: * code: alias1, alias2
function Lingua.listeCodeAlias()
    local linguasTab, listeCodesAlias = { }, { }
    local code, alias, codeAlias
    for i, v in pairs( dadosLingua ) do
        -- nós construímos uma tabela com os códigos idiomáticos como índices, e para valores uma tabela com a lista de aliases
        code = v.code
        if code and not v.invalido then
            linguasTab[code] = linguasTab[code] or { }
            if i ~= mw.ustring.lower( code ) then
                table.insert( linguasTab[code], i )
            end
        end
    end
    for i, v in pairs( linguasTab ) do
        -- transformação em uma tabela de seqüência, com o valor de uma string correspondente a uma linha da lista
        alias = table.concat( v, ', ' )
        if alias == '' then
            codeAlias = '* <code>' .. i .. '</code>'
        else
            codeAlias = '* <code>' .. i .. '</code> : ' .. alias
        end
        table.insert( listeCodesAlias, codeAlias )
    end
    table.sort( listeCodesAlias )
   
    return table.concat( listeCodesAlias, '\n' )
end

-- listAliasCode gera uma lista; os linguas são da seguinte forma: * alias: code
function Lingua.listeAliasCode()
    local linguasTab = { }
    local code
    for i, v in pairs( dadosLingua ) do
        -- nós construímos uma tabela com os códigos idiomáticos como índices, e para valores uma tabela com a lista de aliases
        code = v.code
        if code and i ~= code and not v.invalido then
            table.insert( linguasTab, '* ' .. i .. ' = ' .. code )
        end
    end
    table.sort( linguasTab )
   
    return table.concat( linguasTab, '\n' )
end

return Lingua