630
edits
(split off rare error handling code paths) |
m (1 revision imported) Tag: Reverted |
||
Line 1: | Line 1: | ||
--[=[ | |||
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. | |||
local | ]=] | ||
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, {}; | |||
--[==[ | |||
function | / \ | ||
/ | \ | |||
/ · \ | |||
¯¯¯¯¯¯¯ | |||
Page.subpages() no longer works because it attempted to parse the HTML content generated by | |||
calling the parser function "Special:Prefixindex:" which is no longer expanded in Lua but | |||
converted to a "stripped tag" (containing a unique identifier surrounded by ASCII DEL characters) | |||
representing the tag name and its parameters. | |||
The actual expansion of stripped tags can no longer be performed in Lua. | |||
Now unstripping these tags just kills ALL these tags (except "wiki" tags) instead of performing | |||
their expansion by running the extension code. Only MediaWiki can unstrip these tags in texts after | |||
they have been returned by Lua. | |||
For this reason, page.subpages() is now completely empty (Module:Page no longer works). | |||
This cannot be bypassed, except by using a Scribunto extension library if lifting the limits set by mw.unstrip. | |||
Note that "Special:Prefixindex:" is also costly, even if it just requires a single database query to | |||
get all subpages, instead of one costly #ifexist or one costly mw.title() property reading per | |||
tested subpage to know if it exists. | |||
For now there's still no reliable way to get a list of subpages, or performing queries similar to | |||
the [[Special:Prefixindex]] page or list members of a category like when viewing a category page. | |||
Ideally, there should exist a method for such queries on Title objects returned by the mw.title library; | |||
but for now there's none. | |||
In Lua now, the only expansion possible with an immediate effect is the expansion of standard templates, | |||
all special tags or special pages, or parser function extensions do not work (Only the #expr parser | |||
function is supported by using an external Scribunto library). | |||
--]==] | |||
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 | 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 | end | ||
local p = {}; | |||
--[=[ | |||
Usage: | |||
{{#invoke:languages|internal|Template:Adjective}} | |||
]=] | |||
function p.internal(frame) | |||
return table.concat( | |||
function | language_subpages( frame, | ||
function( title, page, code, name ) | |||
return mw.ustring.format( | |||
'<bdi class="language lang-%s" lang="%s">[[%s/%s|%s]]</bdi>', | |||
code, code, | |||
title, page, | |||
function | name | ||
); | |||
end | |||
), | |||
' <b>·</b> ' | |||
); | |||
end | |||
end | end | ||
function | --[=[ | ||
Usage: | |||
{{#invoke:languages|external|Template:Adjective}} | |||
]=] | |||
function p.external(frame) | |||
return table.concat( | |||
language_subpages( frame, | |||
function( title, page, code, name ) | |||
return mw.ustring.format( | |||
'<bdi class="language lang-%s" lang="%s">[%s/%s %s]</bdi>', | |||
code, code, | |||
tostring( mw.uri.fullUrl( title ) ), page:gsub( ' ', '_' ), | |||
name | |||
); | |||
end | |||
), | |||
' <b>·</b> ' | |||
); | |||
end | end | ||
--[=[ | |||
forEachLanguage | |||
function | 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 | |||
preprocess: if set to a non-empty value, the output will be preprocessed before being returned. | |||
Warning | |||
The output is still not prepreprocessed by default: so parser functions and magic keywords generated by the pattern are still not executed and replaced, | |||
and template transclusions are still not expanded (see examples in other functions in this module). | |||
When using this function directly from a MediaWiki page or template, this means it is only possible to use patterns generating basic MediaWiki formatting | |||
or HTML tags. It you want the output to be preprocessed (in the given frame), set the preprocess parameter to a non-empty string. | |||
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 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 preprocess = frame.args.preprocess or frame.args[6] or "" | |||
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 contentLangInstance = mw.language.getContentLanguage() | |||
local inLangLangInstance | |||
local l = mw.language.fetchLanguageNames() -- autonyms | |||
local lTr | |||
local lcIdList = require('Module:Languages/List').getSortedList( l ) | |||
if langNameTranslatedReq then | |||
inLangLangInstance = --[==[ | |||
mw.getLanguage( inLang ) -- Quota hit in :ucfirst() if using too many langInstances | |||
--]==] contentLangInstance | |||
lTr = mw.language.fetchLanguageNames( inLang ) -- translated names | |||
end | |||
for _, lcId in pairs( lcIdList ) do | |||
local subst = lcId:gsub('%%', '%%%%') | |||
line = pattern:gsub( "%$lc", subst ) | |||
local langName, langInstance | |||
-- autonym (name of lcId in locale lcId) | |||
if langNameReq then | |||
langName = l[lcId] | |||
subst = langName:gsub('%%', '%%%%') | |||
line = line:gsub( "%$lnP", subst ) | |||
end | |||
if langNameUCFirstReq then | |||
end | langInstance = --[==[ | ||
mw.getLanguage( lcId ) -- Quota hit in :ucfirst() if using too many langInstances | |||
--]==] contentLangInstance | |||
langName = langInstance:ucfirst( langName ) | |||
subst = langName:gsub('%%', '%%%%') | |||
line = line:gsub( "%$lnUC1", subst ) | |||
end | |||
-- translated name (name of lcId in locale inLang) | |||
if langNameTranslatedReq then | |||
langName = lTr[lcId] | |||
subst = langName:gsub('%%', '%%%%') | |||
line = line:gsub( "%$lnTrP", subst ) | |||
end | |||
if langNameTranslatedUCFirstReq then | |||
langName = inLangLangInstance:ucfirst( langName ) | |||
subst = langName:gsub('%%', '%%%%') | |||
line = line:gsub( "%$lnTrUC1", subst ) | |||
end | |||
table.insert(ret, line) | |||
end | |||
ret = prefix .. table.concat( ret, sep ) .. postfix | |||
if preprocess ~= '' then | |||
ret = frame:preprocess(ret) | |||
end | |||
return ret | |||
end | end | ||
--[=[ | |||
Provide logic for [[Template:Lle]] (Language Links external, to be substituted, language names written exactly as #language would provide them) | |||
Warning: may expands too many costly #ifexist without limitation (if not substituted into a separate "/lang" template) | |||
]=] | |||
function p.lle(frame) | |||
return frame:preprocess( | |||
p.forEachLanguage({ | |||
args = { | |||
pattern = '{{subst:#ifexist:{{{1}}}/$lc|[{{subst:fullurl:{{{1}}}/$lc}} <bdi class="language lang-$lc" lang="$lc">$lnP</bdi>] <b>∙</b> <!--\n-->}}' | |||
} | |||
}) | |||
) | |||
end | end | ||
--[=[ | |||
Provide logic for [[Template:Ll]] (Language Links internal, to be substituted, language names written exactly as #language would provide them) | |||
Warning: may expands too many costly #ifexist without limitation (if not substituted into a separate "/lang" template) | |||
]=] | |||
function p.ll(frame) | |||
return frame:preprocess( | |||
p.forEachLanguage({ | |||
args = { | |||
pattern = '{{subst:#ifexist:{{{1}}}/$lc|[[{{{1}}}/$lc|<bdi class="language lang-$lc" lang="$lc">$lnP</bdi>]] <b>∙</b> <!--\n-->}}' | |||
} | |||
}) | |||
) | |||
end | 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 }) | ||
function | -- =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|<bdi lang="$lc">$lnP</bdi>]] <b>∙</b> }}' | |||
} | |||
}) | |||
end | |||
} | |||
} | |||
local pages = language_subpages( frame, | |||
function( title, page, code, langname ) | |||
return mw.ustring.format( | |||
'[[%s/%s|<bdi lang="%s">%s</bdi>]]</span> <b>∙</b> ', | |||
title, page, code, 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|<bdi lang="$lc">$lnP</bdi>]] <b>∙</b> }}' | |||
} | |||
}) | |||
) | |||
end | 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 | 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() | |||
function | 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 | |||
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 | |||
local ok, exists = pcall( function() | |||
return mw.title.new( currenttemplate, tlns ).exists | |||
end ) | |||
if ok and exists then | |||
return createReturn(currenttemplate) | |||
end | |||
end | |||
fallback1 = frame:preprocess( '{{Fallback|1=' .. base .. '|2=' .. userlang .. '}}' ) | |||
if fallback1 ~= contentlang then | |||
end | return createReturn(tlb .. fallback1) | ||
end | |||
currenttemplate = tlb .. fallback | |||
local ok, exists = pcall( function() | |||
return mw.title.new( currenttemplate, tlns ).exists | |||
end ) | |||
if ok and exists then | |||
return createReturn(currenttemplate) | |||
end | |||
function | currenttemplate = tlb .. contentlang | ||
local ok, exists = pcall( function() | |||
return mw.title.new( currenttemplate, tlns ).exists | |||
end ) | |||
if ok and exists then | |||
return createReturn(currenttemplate) | |||
end | |||
return createReturn(tl .. base) | |||
end | 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 | 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 | |||
local ok, exists = pcall( function() | |||
return mw.title.new( filename, 6 ).exists | |||
end ) | |||
if ok and exists then | |||
return mw.ustring.format( '%s|%s', | |||
filename, | |||
mw.language.fetchLanguageName( linkInfo.text ) | |||
) | |||
else | |||
return nil | |||
end | |||
end | |||
), '\n' | |||
)) .. | |||
'\n</gallery>' | |||
) | |||
end | end | ||
function p.runTests() | |||
function | return p.langLinksNonExpensive({ | ||
args = { | |||
page = 'Module:Languages/testcases/test' | |||
}, | |||
getParent = function() end | |||
}) == | |||
'[[Module:Languages/testcases/test/de|<bdi lang="de">Deutsch</bdi>]] <b>∙</b> ' .. | |||
'[[Module:Languages/testcases/test/en|<bdi lang="en">English</bdi>]] <b>∙</b> ' | |||
end | end | ||
return | return p; |