Last modified on 30 March 2015, at 16:05

Module:Languages

see inline documentation


--[=[
Not globally exposed. Internal function only.

language_subpages( frame, transform, options )
Parameters
    frame:     The frame that was passed to the method invoked. The first argument or the page argument will be respected.
    transform: A transform function. Example: function( basepagename, subpagename, code, langname ) end
    options:   An object with options. Example: { abort= { on=function() end, time=0.8 }  }
        Following options are available:
        abort: Aborts iterating over the subpages if one of the conditions is met. If the process is aborted, nil is returned!
            on: Function to be called if an abort-condition was met.
            cycles: The maximum number of subpages to run over.
            time: Maximum time to spend running over the subpages.
        
]=]
function language_subpages( frame, transform, options )
    local args, pargs, options = frame.args, ( frame:getParent() or {} ).args or {}, options or {};
    local title = args.page or args[1] or pargs.page or pargs[1] or "";
    local abort = options.abort or {};
    local at, clock = type( abort.on ), os.clock();
    local ac = function()
        if  at == 'function' or ( at == 'table' and getmetatable(abort.on).__call ) then
            abort.on();
        end
    end
    local tt = type( transform );
    local page = require( 'Module:Page' );
    
    title = page.clean(title);
    
    if tt == 'function' or ( tt == 'table' and getmetatable(transform).__call ) then
        local fetch, pages, langcode, langname = mw.language.fetchLanguageName, {};
        for pg in page.subpages( title, { ignoreNS=true } ) do
            if abort.cycles then
                abort.cycles = abort.cycles - 1
                if 0 == abort.cycles then return ac()  end
            end
            if abort.time then
                if (os.clock() - clock) > abort.time then return ac()  end
            end
            if mw.ustring.len( pg ) <= 12 then
                langcode = string.lower( pg );
                langname = fetch( langcode );
                if langname ~= '' then
                    table.insert( pages, transform( title, pg, langcode, langname ) );
                end
            end
        end
        return pages;
    end
    return {};
end

function cloneArgs(frame)
    local args, pargs = {}, {}
    for k,v in pairs( frame.args ) do args[k] = v end
    if frame:getParent() then
        for k,v in pairs( frame:getParent().args ) do pargs[k] = v end
    end
    return args, pargs;
end



local p = {};

--[=[
Usage:
{{#invoke:languages|internal|Template:Adjective}}
]=]
function p.internal(frame)
    pages = language_subpages( frame, function( title, page, code, name )
        return mw.ustring.format( '<span lang="%s" xml:lang="%s" class="language lang-%s"><bdi>[[%s/%s|%s]]</bdi></span>',
            code, code, code, title, page, name
        );
    end);
    return table.concat( pages, '&nbsp;ยท&nbsp;' );
end

--[=[
Usage:
{{#invoke:languages|external|Template:Adjective}}
]=]
function p.external(frame)
    pages = language_subpages( frame, function( title, page, code, name )
        return mw.ustring.format( '<span lang="%s" xml:lang="%s" class="language lang-%s"><bdi>[%s/%s %s]</bdi></span>',
            code, code, code, tostring( mw.uri.fullUrl( title ) ), page:gsub( ' ', '_' ), name
        );
    end);
    return table.concat( pages, '&nbsp;| ' );
end

--[=[ 
forEachLanguage
 
This function iterates over all language codes known to MediaWiki based on a maintained list
replacing patterns in a pattern-string for each language
 
Usage:
{{#invoke:Languages|forEachLanguage
  |pattern=patternstring
  |before=string to insert before iteration
  |after=string added after iteration
  |sep=separator string between iterations
  |inLang=langcode used for $lnTrP and $lnTrUC1
}}
 
Parameters
    pattern: A pattern string which is processed for each language and which is concatenated at the end and returned as one string
    before: A string that is inserted before the concatenated result
    after: A string that is inserted after the concatenated result
    sep: A string that is inserted between each line created from the pattern while iterating (like ProcessedPattern_sep_ProcessedPattern_sep_ProcessedPattern)
    inLang: Langcode to use for $lnTrP and $lnTrUC1
    
Patterns:
    $lc - language code such as en or de
    $lnP - language name in own language (autonym)
    $lnUC1 - language name in own language (autonym), first letter upper case
    $lnTrP - language name translated to the language requested by language code passed to inLang
    $lnTrUC1 - language name translated to the language requested by language code passed to inLang, first letter upper case
 
Example
   {{#invoke:Languages|forEachLanguage|pattern=<span lang="$lc" xml:lang="$lc" class="language lang-$lc">[[Page/$lc|$lnP]]</span>}}
]=]

-- =p.forEachLanguage({ args= { pattern = "$lc - $lnTrP\n", inLang = "en" } })
function p.forEachLanguage(frame)
    local l = require( "Module:Languages/List" )
    
    local ret = {}
    local lang    = mw.language
    local contentLangInstance = mw.language.getContentLanguage()
    local langInstance = contentLangInstance --Quota hit here otherwise
    local line
    local pattern = frame.args.pattern   or frame.args[1] or ""
    local prefix  = frame.args.before    or frame.args[2] or ""
    local postfix = frame.args.after     or frame.args[3] or ""
    local sep     = frame.args.sep       or frame.args.separator or frame.args[4] or ""
    local inLang  = frame.args.inLang    or frame.args[5] or nil
    local langName
    local langNameUCFirst
    local langNameTranslated
    local langNameTranslatedUCFirst

    local langNameUCFirstReq           = not not pattern:find( "$lnUC1", 1, true )
    local langNameReq                  = not not pattern:find( "$lnP", 1, true ) or langNameUCFirstReq
    local langNameTranslatedUCFirstReq = not not pattern:find( "$lnTrUC1", 1, true )
    local langNameTranslatedReq        = not not pattern:find( "$lnTrP", 1, true ) or langNameTranslatedUCFirstReq
    local l, lTr
    
    if ( langNameReq ) then
    	l = mw.language.fetchLanguageNames()
	end
	if ( langNameTranslatedReq ) then
		lTr = mw.language.fetchLanguageNames( inLang )
	end

	local lcIdList = require( 'Module:Languages/List' ).getSortedList( l or lTr )

    for i, lcId in pairs( lcIdList ) do
         line = pattern:gsub( "$lc", lcId )
        
        if langNameReq then
            line = line:gsub( "$lnP", l[lcId] )
        end
        if langNameUCFirstReq then
            --langInstance = mw.getLanguage( lcId ) --Quota hit here
            langNameUCFirst = langInstance:ucfirst( l[lcId] )
            line = line:gsub( "$lnUC1", langNameUCFirst )
        end
        if langNameTranslatedReq then
            langNameTranslated = lTr[lcId]
            line = line:gsub( "$lnTrP", langNameTranslated )
        end
        if langNameTranslatedUCFirstReq then
            --if not langInstance then langInstance = mw.getLanguage( lcId ) end --Quota hit here
            langNameTranslatedUCFirst = langInstance:ucfirst( langNameTranslated )
            line = line:gsub( "$lnTrUC1", langNameTranslatedUCFirst )
        end
         --langInstance = nil

        table.insert(ret, line)
    end
    return prefix .. table.concat( ret, sep ) .. postfix
end

--[=[
 Provide logic for [[Template:Lle]] (Language Links external, to be substituted)
]=]
function p.lle(frame)
    local ret
    local pattern = "{{<includeonly/>subst:#ifexist:{{{1}}}/$lc|[{{fullurl:{{{1}}}/$lc}} $lnUC1]&nbsp;{{<includeonly/>subst:!}} <!--\n-->}}"
    ret = p.forEachLanguage({ args= { pattern = pattern } })
    ret = frame:preprocess(ret)
    return ret
end

--[=[
 Provide logic for [[Template:Ll]] (Language Links, to be substituted)
]=]
function p.ll(frame)
    local ret
    local pattern = "{{<includeonly/>subst:#ifexist:{{{1}}}/$lc|[[{{{1}}}/$lc{{<includeonly/>subst:!}}$lnUC1]]&nbsp;{{<includeonly/>subst:!}} <!--\n-->}}"
    ret = p.forEachLanguage({ args= { pattern = pattern } })
    ret = frame:preprocess(ret)
    return ret
end


--------------------------------------------------------
--- Different approaches for [[Template:Lang links]] ---
--------------------------------------------------------

--[=[
 Provide logic for [[Template:Lang links]]
 Using a cute Hybrid-Method:
    First check the subpages which is quite fast; if there are too many fall back to checking for each language page individually
]=]

-- =p.langLinksNonExpensive({ args= { page='Commons:Picture of the Year/2010' }, getParent=function() end })
-- =p.langLinksNonExpensive({ args= { page='Main Page' }, getParent=function() end })
-- =p.langLinksNonExpensive({ args= { page='Template:No_source_since' }, getParent=function() end })
-- =p.langLinksNonExpensive({ args= { page='MediaWiki:Gadget-HotCat' }, getParent=function() end })
function p.langLinksNonExpensive(frame)
    local args, pargs = frame.args, ( frame:getParent() or {} ).args or {};
    local title = args.page or args[1] or pargs.page or pargs[1] or "";
    local contentLangInstance = mw.language.getContentLanguage();
    local pages2
    if frame.preprocess == nil then
    	frame = mw.getCurrentFrame()
    end
    --[==[
    local options = { abort= { time=3.5, on=function() 
            pages2 = p.forEachLanguage({ args= { pattern = '{{#ifexist:' .. title .. '/$lc|[[' .. title .. '/$lc|$lnUC1]]&nbsp;&#124;&#32;}}' } })
        end } }
    local pages = language_subpages( frame, function( basepagename, subpagename, code, langname )
        return mw.ustring.format( '<span lang="%s" xml:lang="%s" class="language lang-%s" style="white-space:nowrap"><bdi>[[%s/%s|%s]]</bdi></span>&nbsp;| ',
            code, code, code, basepagename, subpagename, contentLangInstance:ucfirst( langname ) )
    end, options );
    return pages2 and frame:preprocess(pages2) or table.concat(  pages, '' );
    ]==]
    return frame:preprocess( p.forEachLanguage({ args= { pattern = '{{#ifexist:' .. title .. '/$lc|[[' .. title .. '/$lc|$lnUC1]]&nbsp;&#124;&#32;}}' } }) )
end

---------------------------------------------------------
----------------- [[Template:Autolang]] -----------------
---------------------------------------------------------
--[[
  Works like {{autotranslate}} just allowing an unlimited number of arguments, even named arguments.
  It's doing Magic! No arguments should be passed to {{#invoke:}}
]]

function p.autolang(frame)
    local args, pargs = cloneArgs( frame )
    if nil == args.useargs then
        if not args.base then args = pargs end
    elseif 'both' == args.useargs then
        for k,v in pairs(args) do pargs[k] = v end
        args = pargs
    elseif 'parent' == args.useargs then
        args = pargs
        if pargs.base and not args.base then
            args.base = pargs.base
        end
    end
    local base = args.base
    local userlang = frame:preprocess( '{{int:lang}}' )
    local tl, tlns = 'Template:', 10
    local tlb, fallback1, currenttemplate
    local fallback, contentlang = mw.text.split( userlang, '-', true )[1], mw.language.getContentLanguage():getCode()
    
    local createReturn = function(title) 
        local ret
        local tlargs = {}
         -- When LUA is invoked, templates are already expanded. This must be respected.
        return frame:expandTemplate{ title = title, args = args }
    end
    
    if not base then
        return ("'autolang' in [[Module:Languages]] was called but the 'base' parameter could not be found." .. 
            "The base parameter specifies the template that's subpages will be sought for a suitable translation.")
    end
    tlb = tl .. base .. '/'
    
    currenttemplate = tlb .. userlang
    if mw.title.new( currenttemplate, tlns ).exists then
        return createReturn(currenttemplate)
    end
    
    fallback1 = frame:preprocess( '{{Fallback|1=' .. base .. '|2=' .. userlang .. '}}' )
    if fallback1 ~= contentlang then
        return createReturn(tlb .. fallback1)
    end
    
    currenttemplate = tlb .. fallback
    if mw.title.new( currenttemplate, tlns ).exists then
        return createReturn(currenttemplate)
    end
    
    currenttemplate = tlb .. contentlang
    if mw.title.new( currenttemplate, tlns ).exists then
        return createReturn(currenttemplate)
    end
    return createReturn(tl .. base)
end

--[=[
Usage:
{{#invoke:languages|isKnownLanguageTag|gsw}} -> 1
{{#invoke:languages|isKnownLanguageTag|doesNotExist}} ->
]=]
function p.isKnownLanguageTag(frame)
	return mw.language.isKnownLanguageTag( frame.args[1] or frame.args.tag or frame.args.code or '' ) and '1' or ''
end

function p.file_languages(frame)
    local M_link = require( 'Module:Link' )
    local contentLangInstance = mw.language.getContentLanguage()
    local pattern = frame.args.pattern or '%s (%s)'
    local original = frame.args.original or mw.title.getCurrentTitle().text
    local ext_start, _ = string.find( original, '\.%w+$' )
    local file_ext = string.sub( original, ext_start )
    original = string.sub( original, 0, ext_start-1 )
    return frame:preprocess('<gallery>\n'..(table.concat(M_link.forEachLink( p.forEachLanguage( { args= { pattern = '[[$lc]]' } } ), 
        function(linkInfo)
            local filename = mw.ustring.format( pattern, original, linkInfo.text ) .. file_ext
            if mw.title.new( filename, 6 ).exists then
                return mw.ustring.format( '%s|%s', filename, contentLangInstance:ucfirst( mw.language.fetchLanguageName( linkInfo.text ) ) )
            else
                return nil
            end
        end
    ), '\n'))..'\n</gallery>')
end

function p.runTests()
	return p.langLinksNonExpensive({ args= { page='Module:Languages/testcases/test' }, getParent=function() end }) == "[[Module:Languages/testcases/test/de|Deutsch]]&nbsp;&#124;&#32;[[Module:Languages/testcases/test/en|English]]&nbsp;&#124;&#32;"
end

return p;