Ero sivun ”Moduuli:Viikon kilpailu kriteerit” versioiden välillä

Wikipediasta
Siirry navigaatioon Siirry hakuun
[katsottu versio][katsottu versio]
Poistettu sisältö Lisätty sisältö
+max_depth
+distinct
(31 välissä olevaa versiota 6 käyttäjän tekeminä ei näytetä)
Rivi 2: Rivi 2:


local messages = {
local messages = {
['or'] = 'tai',
['and'] = 'ja',
['and'] = 'ja',
['error_no_x_given'] = 'Yhtäkään %s ei annettu',
['or'] = 'tai',
['page_at_site'] = '%(page)s at %(site)s',
['error_no_valid_criterion_given'] = 'Virheellinen/tuntematon kriteeri',
['argument_missing'] = 'Argumentti puuttuu: %s',
['anon_argument_missing'] = 'Arvoja ei annettu: %s',
['invalid_criterion'] = '«%s» ei ole sallittu kriteeri',
['invalid_rule'] = '«%s» ei ole sallittu pisteytyssääntö',

-- Criteria


['templates'] = 'mallinetta',
['templates'] = 'mallinetta',
['template_criterion_singular'] = 'jotka on merkitty mallineella %s',
['templates_criterion_singular'] = 'jotka on merkitty mallineella %s',
['template_criterion_plural'] = 'jotka on merkitty mallineilla %s',
['templates_criterion_plural'] = 'jotka on merkitty mallineilla %s',


['categories'] = 'luokkaa',
['categories'] = 'luokkaa',
['category_criterion_singular'] = 'jotka sijaitsevat luokassa %s',
['categories_criterion_singular'] = 'jotka sijaitsevat luokassa %s',
['category_criterion_plural'] = 'jotka sijaitsevat luokissa %s',
['categories_criterion_plural'] = 'jotka sijaitsevat luokissa %s',
['categories_criterion_ignore'] = ', mutta eivät kuitenkaan luokissa %s',
['ignores'] = 'poissuljettavaa luokkaa',
['ignore_criterion_singular'] = ', mutta eivät kuitenkaan luokassa %s',
['ignore_criterion_plural'] = ', mutta eivät kuitenkaan luokissa %s',
['max_depth_criterion_singular'] = 'sen alaluokissa <small>(maksimisyvyys %d)</small>',
['max_depth_criterion_plural'] = 'niiden alaluokissa <small>(maksimisyvyys %d)</small>',


['backlinks'] = 'takaisinlinkitystä',
['backlinks'] = 'takaisinlinkitystä',
['backlink_criterion_singular'] = 'joihin johtaa linkki sivulta %s',
['backlinks_criterion_singular'] = 'joihin johtaa linkki sivulta %s',
['backlink_criterion_plural'] = 'joihin johtaa linkki sivuilta %s',
['backlinks_criterion_plural'] = 'joihin johtaa linkki sivuilta %s',


['forwardlinks'] = 'edelleenlinkitystä',
['forwardlinks'] = 'edelleenlinkitystä',
['forwardlink_criterion_singular'] = 'joissa on linkki sivulle %s',
['forwardlinks_criterion_singular'] = 'joissa on linkki sivulle %s',
['forwardlink_criterion_plural'] = 'joissa on linkki sivuille %s',
['forwardlinks_criterion_plural'] = 'joissa on linkki sivuille %s',


['pages'] = 'sivua',
['pages'] = 'sivua',
['page_criterion_singular'] = 'yksittäiseen sivuun %s',
['pages_criterion_singular'] = 'yksittäiseen sivuun %s',
['page_criterion_plural'] = 'sivuihin %s',
['pages_criterion_plural'] = 'sivuihin %s',

['sparql_criterion'] = 'jotka löytyvät [%(queryLink)s tällä SPARQL-kyselyllä]',
['sparql_criterion_with_explanation'] = '%(description)s ([%(queryLink)s näytä SPARQL-kysely])',


['bytes_criterion'] = 'joita laajennetaan vähintään %s tavulla',
['bytes_criterion'] = 'joita laajennetaan vähintään %s tavulla',

['namespaces_criterion_singular'] = 'jotka ovat %s', -- %s will be the name of a namespace, e.g. "luokka"
['namespaces'] = 'nimiavaruutta',
['namespaces_criterion_plural'] = 'jotka ovat %s', -- %s will be the name of a namespace, e.g. "luokka"
['namespace_criterion_singular'] = '%s',
['namespace_criterion_plural'] = '%s',
['article'] = 'artikkeleita',

['namespace_criterion_articles'] = 'artikkeleihin',
['new_criterion'] = 'jotka aloitetaan kilpailun aikana',
['new_criterion'] = 'jotka aloitetaan kilpailun aikana',
['new_criterion_with_redirects'] = 'jotka aloitetaan kilpailun aikana (sisältää myös ohjaussivut)',
['new_criterion_with_redirects'] = 'jotka aloitetaan kilpailun aikana (sisältää myös ohjaussivut)',

['existing_criterion'] = 'jotka ovat olemassa ennen kilpailun aloituspäivämäärää',
['existing_criterion'] = 'jotka ovat olemassa ennen kilpailun aloituspäivämäärää',
['stub_criterion'] = 'joista poistetaan tynkämalline',


-- Rules

['base_rule_max'] = '%(baserule)s, mutta enintään %(maxpoints)s pistettä per artikkeli',

['newpage_rule'] = 'Jokaisesta uudesta artikkelista annetaan %(points)s pistettä',
['newredirect_rule'] = 'Jokaisesta luodusta ohjaussivusta annetaan %(points)s pistettä',
['page_rule'] = 'Jokaisesta hyväksytystä sivusta annetaan %(points)s pistettä',
['edit_rule'] = 'Jokaisesta muokkauksesta annetaan %(points)s pistettä',
['byte_rule'] = 'Jokaisesta lisätystä tavusta annetaan %(points)s pistettä',
['listbyte_rule'] = 'Jokaisesta luettelo-artikkeliin lisätystä tavusta annetaan %(points)s pistettä',
['word_rule'] = 'Jokaisesta lisätystä sanasta annetaan %(points)s pistettä',

['image_rule'] = 'Jokaisesta lisätystä kuvasta annetaan %(points)s pistettä',
['image_rule_limited'] = 'Jokaisesta lisätystä kuvasta tai muusta tiedostosta, joka lisätään sivulle, jolla oli aikaisemmin enintään %(initialimagelimit)s tiedostoa, annetaan %(points)s pistettä',
['image_rule_own'] = '(%(ownimage)s itse ladatuista)',

['reference_rule'] = 'Jokaisesta lisätystä lähteestä annetaan %(points)s pistettä ja %(refpoints)s pistettä viittauksesta jo olemassa olevaan lähteeseen',
['templateremoval_rule'] = 'Jokaisesta mallineen %(templates)s poistosta annetaan %(points)s pistettä',
['categoryremoval_rule'] = '.. %(categories)s ... annetaan %(points)s pistettä',
['exlink_rule'] = 'Jokaisesta aiheesta muualla-linkin lisäämisestä annetaan %(points)s pistettä',

['section_rule'] = 'Jokaisesta lisätystä %(sections)s -osiosta annetaan %(points)s pistettä',
['section_rule_desc'] = 'Jokaisesta lisätystä %(description)s annetaan %(points)s pistettä ',

['wikidata_rule_first'] = '%(points)s points are awarded for adding %(thing)s to items that did not have such a claim from before',
['wikidata_rule_all'] = '%(points)s points are awarded for adding %(thing)s to items',
['wikidata_rule_require_reference'] = '(only referenced)',
['properties'] = 'properties',
['labels'] = 'labels',
['aliases'] = 'aliases',
['descriptions'] = 'descriptions',
['label'] = 'Wikidata label',
['alias'] = 'Wikidata alias',
['description'] = 'Wikidata description',

['bytebonus_rule'] = 'Jokaisesta vähintään %(bytes)s tavua tuottaneesta lisäyksestä annetaan %(points)s bonuspistettä',
['wordbonus_rule'] = 'Jokaisesta vähintään %(words)s sanaa tuottaneesta lisäyksestä annetaan %(points)s bonuspistettä',
}
}

local config = {
local config = {
['decimal_separator'] = ',',
['template_link_template'] = 'Malline',
['template_link_template'] = 'Malline',
['error_message_template'] = 'Virhe',
['error_message_template'] = 'Virhe',
-- Map localized argument values for the criterion template
['default_max_depth'] = 5,
['arguments'] = {
['criteria'] = {
['new'] = 'uusi',
['uusi'] = 'new',
['existing'] = 'löytyvä',
['löytyvä'] = 'existing',
['stub'] = 'tynkä',
['tynkä'] = 'stub',
['bytes'] = 'tavut',
['tavut'] = 'bytes',
['namespace'] = 'nimiavaruus',
['nimiavaruus'] = 'namespaces',
['category'] = 'luokka',
['luokka'] = 'categories',
['template'] = 'malline',
['malline'] = 'templates',
['backlink'] = 'takaisinlinkitys',
['takaisinlinkitys'] = 'backlinks',
['forwardlink'] = 'edelleenlinkitys',
['edelleenlinkitys'] = 'forwardlinks',
['page'] = 'sivut',
['sivut'] = 'pages',
['sparql'] = 'sparql',
},
-- Localized argument values for the rule template
['rules'] = {
['uusi'] = 'newpage',
['ohjaus'] = 'newredirect',
['qualified'] = 'page',
['edit'] = 'edit',
-- ['stub'] = '(deprecated)',
['tavu'] = 'byte',
['listtavu'] = 'listbyte',
['sana'] = 'word',
['kuva'] = 'image',
['ref'] = 'reference',
['tavubonus'] = 'bytebonus',
['sanabonus'] = 'wordbonus',
['mallineen poisto'] = 'templateremoval',
['osio'] = 'section',
['categoryremoval'] = 'categoryremoval',
['exlink'] = 'exlink',
['wikidata'] = 'wikidata'
}
}
}
}
Rivi 70: Rivi 132:
['nn'] = 'nn:Kategori',
['nn'] = 'nn:Kategori',
['no'] = 'no:Kategori',
['no'] = 'no:Kategori',
['olo'] = 'olo:Category',
['smn'] = 'smn:Category',
['default'] = 'Luokka'
['default'] = 'Luokka'
}
local to_namespace = {
['0'] = 'artikkeleihin',
['4'] = 'projektisivuihin',
['6'] = 'tiedostoihin',
['10'] = 'mallineisiin',
['12'] = 'ohjesivuihin',
['14'] = 'luokkiin',
['100'] = 'teemasivuihin',
['104'] = 'kirjoihin'
}
}


--[ Helper methods ] ------------------------------------------------------------------
-- format_kriterium

function uk.format_criterion(singular, plural, items, conjunction, extra, extra_value)
--[[ Named Parameters with Formatting Codes
conjunction = (conjunction and messages[conjunction] or messages['or'])
Source: <http://lua-users.org/wiki/StringInterpolation>, author:RiciLake ]]
if #items > 1 then
local function sprintf(s, tab)
if extra then
return (s:gsub('%%%((%a%w*)%)([-0-9%.]*[cdeEfgGiouxXsq])',
if extra_value then
function(k, fmt) return tab[k] and ("%"..fmt):format(tab[k]) or
table.insert(items, string.format(messages[extra .. '_criterion_plural'], extra_value))
'%('..k..')'..fmt end))
else
table.insert(items, messages[extra .. '_criterion_plural'])
end
end
return string.format(messages[singular .. '_criterion_plural'],
mw.text.listToText(items, ', ', ' ' .. conjunction .. ' '))
elseif #items == 1 then
if extra then
if extra_value then
table.insert(items, string.format(messages[extra .. '_criterion_singular'], extra_value))
else
table.insert(items, messages[extra .. '_criterion_singular'])
end
end
return string.format(messages[singular .. '_criterion_singular'],
mw.text.listToText(items, ', ', ' ' .. conjunction .. ' '))
else
return string.format('{{%s|' .. messages['error_no_x_given'] .. '}}',
config['error_message_template'], messages[plural])
end
end
end


function uk.pagelist(args)
local function make_error(template, arg)
return string.format(
local r = {}
for i, v in ipairs(args) do
'{{%s|%s}}',
config['error_message_template'],
v = mw.text.trim(v)
string.format(messages[template], arg)
)
end

local function parse_args(frame)
local args = {}
local kwargs = {}
for k, v in pairs(frame.args) do
v = mw.text.trim(frame:preprocess(v))
if v ~= '' then
if v ~= '' then
local lang, page = string.match(v, '^([a-z\]+):(.+)$')
if type(k) == 'number' then
if lang then
args[k] = v
table.insert(r, string.format('[[:%s:%s|%s]]', lang, page, page))
else
else
table.insert(r, string.format('[[:%s]]', v))
kwargs[k] = v
end
end
end
end
end
end
return r
return args, kwargs
end
end


function uk.nslist(args)
local function shift_args(in_args)
local r = {}
local args = {}
for i, v in ipairs(args) do
for i, v in ipairs(in_args) do
v = mw.text.trim(v)
if i > 1 then
if v ~= '' then
args[i - 1] = v
if to_namespace[v] ~= nil then
table.insert(r, to_namespace[v])
else
table.insert(r, 'nimiavaruuden ' .. v .. ' sivuihin')
end
end
end
end
end
return r
return in_args[1], args
end
end



-- kriterium_maler
local function format_plural(items, item_type)
function uk.criterion_templates(args)
local r = {}
if #items == 0 then
return make_error('anon_argument_missing', messages[item_type])
end
if #items == 1 then
return items[1]
end
return mw.text.listToText(items, ', ', ' ' .. messages['or'] .. ' ')
end

local function format_plural_criterion(items, item_type)
local value = format_plural(items, item_type)
if #items == 0 then
return value
end
if #items == 1 then
return string.format(messages[item_type .. '_criterion_singular'], value)
end
return string.format(messages[item_type .. '_criterion_plural'], value)
end

local function make_template_list(args)
local templates = {}
for i, v in ipairs(args) do
for i, v in ipairs(args) do
v = mw.text.trim(v)
local lang, link = string.match(v, '^([a-z]+):(.+)$')
if v ~= '' then
if lang then
local m, n = string.match(v, '^([a-z\]+):(.+)$')
table.insert(templates, string.format('{{%s|%s|%s}}', config['template_link_template'], link, lang))
if m then
else
table.insert(r, string.format('{{%s|%s|%s}}', config['template_link_template'], n, m))
table.insert(templates, string.format('{{%s|%s}}', config['template_link_template'], v))
else
table.insert(r, string.format('{{%s|%s}}', config['template_link_template'], v))
end
end
end
end
end
return uk.format_criterion('template', 'templates', r)
return templates
end
end


function uk.make_category_link(v)
local function make_category_link(v)
local lang = 'default'
local lang = 'default'
local name = v
local name = v
local m, n = string.match(v, '^([a-z\]+):(.+)$')
local m, n = string.match(v, '^([a-z]+):(.+)$')
if m then
if m then
lang = m
lang = m
name = n
name = n
end
end
return string.format('[[:%s:%s|%s]]', category_prefix[lang], name, name, name)
return string.format('[[:%s:%s|%s]]', category_prefix[lang], name, name)
end
end


local function make_category_list(args)
local category_links = {}
for i, v in ipairs(args) do
v = mw.text.trim(v)
if v ~= '' then
table.insert(category_links, make_category_link(v))
end
end
return category_links
end


local function pagelist(args)
-- kriterium_kategorier
function uk.criterion_categories(args, ignore, max_depth)
local r = {}
local r = {}
for i, v in ipairs(args) do
for i, v in ipairs(args) do
v = mw.text.trim(v)
v = mw.text.trim(v)
if v ~= '' then
if v ~= '' then
local lang, page = string.match(v, '^([a-z]+):(.+)$')
table.insert(r, uk.make_category_link(v))
if lang then
table.insert(r, string.format('[[:%s:%s|%s]]', lang, page, page))
else
table.insert(r, string.format('[[:%s]]', v))
end
end
end
end
end
return r
end
max_depth = max_depth:expand()

local msg = uk.format_criterion('category', 'categories', r, 'or', 'max_depth', (max_depth and tonumber(max_depth) or config['default_max_depth']))
local function nslist(args)
ignore = ignore:expand()
local r = {}
local namespaceName = messages['article']
if ignore ~= nil and mw.text.trim(ignore) ~= '' then
for i, namespaceId in ipairs(args) do
r = mw.text.split(ignore, ',')
namespaceId = mw.text.trim(namespaceId)
for i, v in ipairs(r) do
if namespaceId ~= '' then
v = mw.text.trim(v)
if namespaceId ~= "0" then
r[i] = uk.make_category_link(v)
namespaceName = '{{lc:{{ns:' .. namespaceId .. '}}}}'
end
end
msg = msg .. uk.format_criterion('ignore', 'ignores', r)
table.insert(r, namespaceName)
end
end
end
return r
end

--[ Criterion format methods ]-------------------------------------------------------------

local criterion = {}

function criterion.backlinks(args, kwargs, frame)
return format_plural_criterion(pagelist(args), 'backlinks')
end

function criterion.bytes(args, kwargs, frame)
return string.format(messages['bytes_criterion'], args[1])
end

function criterion.categories(args, kwargs, frame)
local msg = format_plural_criterion(make_category_list(args), 'categories')

if args.ignore ~= nil then
r = mw.text.split(args.ignore, ',')
for i, v in ipairs(r) do
v = mw.text.trim(v)
r[i] = make_category_link(v)
end
msg = msg .. string.format(messages['category_criterion_ignore'], mw.text.listToText(r, ', ', ' ' .. messages['or'] .. ' '))
end


return msg
return msg
end
end


function criterion.existing(args, kwargs, frame)
-- kriterium_tilbakelenke
return messages['existing_criterion']
function uk.criterion_backlinks(args)
return uk.format_criterion('backlink', 'backlinks', uk.pagelist(args))
end
end


function criterion.forwardlinks(args, kwargs, frame)
-- kriterium_fremlenke
return format_plural_criterion(pagelist(args), 'forwardlinks')
function uk.criterion_forwardlinks(args)
return uk.format_criterion('forwardlink', 'forwardlinks', uk.pagelist(args))
end
end


function criterion.namespaces(args, kwargs, frame)
-- kriterium_enkeltsider
local site = kwargs.site
function uk.criterion_pages(args)
local msg = format_plural_criterion(nslist(args, site), 'namespaces')
return uk.format_criterion('page', 'pages', uk.pagelist(args))
if site ~= nil then
return sprintf(messages['page_at_site'], {
['page'] = msg,
['site'] = string.format('[https://%s %s]', site, site),
})
end
return msg
end
end


function criterion.new(args, kwargs, frame)
-- kriterium_bytes
local msg = messages['new_criterion']
function uk.criterion_bytes(args)
if kwargs.redirects ~= nil then
return string.format(messages['bytes_criterion'], args[1])
msg = messages['new_criterion_with_redirects']
end
return msg
end
end


function criterion.pages(args, kwargs, frame)
-- kriterium_navnerom
return format_plural_criterion(pagelist(args), 'pages')
function uk.criterion_namespaces(args)
return uk.format_criterion('namespace', 'namespaces', uk.nslist(args), 'and')
end
end


function criterion.sparql(args, kwargs, frame)
-- kriterium_ny
local query = 'SELECT DISTINCT ?item WHERE {\n ' .. kwargs.query .. '\n}'
function uk.criterion_new(redirects)
local queryToolQuery='SELECT ?item ?itemLabel ?itemDescription WITH {' .. query ..'} AS %items WHERE { include %items . SERVICE wikibase:label { bd:serviceParam wikibase:language "fi,se,smn,en". } }'
redirects = redirects:expand()

local msg = messages['new_criterion']
local url = 'http://query.wikidata.org/#' .. frame:callParserFunction('urlencode', { queryToolQuery, 'PATH' })
if redirects ~= nil and mw.text.trim(redirects) ~= '' then
local vizUrl = 'https://tools.wmflabs.org/hay/vizquery/#' .. frame:callParserFunction('urlencode', { query, 'PATH' })
msg = messages['new_criterion_with_redirects']

end
if kwargs.description ~= nil then
return msg
return sprintf(messages['sparql_criterion_with_explanation'], {
description = kwargs.description,
queryLink = url,
vizQueryLink = vizUrl
})
end

return sprintf(messages['sparql_criterion'], {
queryLink=url,
vizQueryLink=vizUrl
})
end
end


function criterion.stub(args, kwargs, frame)
--kriterium
-- deprecated
function uk.criterion(frame)
return messages['stub_criterion']
local t = {
end
[config['arguments']['new']] = function(args) return uk.criterion_new(frame:getArgument('redirects')) end,

[config['arguments']['existing']] = messages['existing_criterion'],
function criterion.templates(args, kwargs, frame)
[config['arguments']['stub']] = messages['stub_criterion'],
return format_plural_criterion(make_template_list(args), 'templates')
[config['arguments']['bytes']] = function(args) return uk.criterion_bytes(args) end,
end
[config['arguments']['namespace']] = function(args) return uk.criterion_namespaces(args) end,

[config['arguments']['template']] = function(args) return uk.criterion_templates(args) end,
function criterion.format(frame)
[config['arguments']['category']] = function(args) return uk.criterion_categories(args, frame:getArgument('ignore'), frame:getArgument('max_depth')) end,
local args, kwargs = parse_args(frame)
[config['arguments']['backlink']] = function(args) return uk.criterion_backlinks(args) end,
local criterion_arg, args = shift_args(args)
[config['arguments']['forwardlink']] = function(args) return uk.criterion_forwardlinks(args) end,

[config['arguments']['page']] = function(args) return uk.criterion_pages(args) end
-- Try to find the corresponding formatter or bail out if not found
if criterion_arg == nil then
return frame:preprocess(make_error('argument_missing', 'criterion'))
end
local formatter = config.criteria[criterion_arg]
if formatter == nil or criterion[formatter] == nil then
return frame:preprocess(make_error('invalid_criterion', criterion_arg))
end

-- Use manual description if given
if kwargs.description ~= nil and formatter ~= 'sparql' then
return kwargs.description
end

-- Generate auto-generated description
return frame:preprocess(criterion[formatter](args, kwargs, frame))
end

--[ Rule format methods ]-------------------------------------------------------------

local rule = {}

function rule.image(points, args, kwargs)
local out
local tplargs = {
['points'] = points,
}
}
if kwargs.initialimagelimit ~= nil then
local args = {}
out = messages['image_rule_limited']
for i, v in ipairs(frame.args) do
tplargs['initialimagelimit'] = kwargs.initialimagelimit
if tonumber(i) ~= nil then
else
args[i] = v
out = messages['image_rule']
end
if kwargs.ownimage ~= nil then
out = out .. ' ' .. messages['image_rule_own']
tplargs['ownimage'] = kwargs.ownimage
end
return sprintf(out, tplargs)
end

function rule.wikidata(points, args, kwargs)
local out
local params
local arg_types = { messages['properties'], messages['labels'], messages['aliases'], messages['descriptions'] }
local results = {}
if kwargs.properties == nil and kwargs.labels == nil and kwargs.aliases == nil and kwargs.descriptions == nil then
return make_error(
'argument_missing',
mw.text.listToText( arg_types, ', ', ' ' .. messages['or'] .. ' ' )
)
end
if kwargs.properties ~= nil then
params = mw.text.split(kwargs.properties, ',')
for k, v in pairs(params) do
params[k] = string.format('[[:d:Property:%s|%s]]', v, v)
end
end
table.insert(results, mw.text.listToText( params, ', ', ' ' .. messages['or'] .. ' ' ))
end
end
if kwargs.labels ~= nil then
local k = frame.args.criterion
params = mw.text.split(kwargs.labels, ',')
local s = type(t[k]) == "function" and t[k](args) or t[k] or string.format('{{%s|%s}}', config['error_message_template'], messages['error_no_valid_criterion_given'])
table.insert(results, messages['label'] .. ' (' .. mw.text.listToText( params, ', ', ' ' .. messages['or'] .. ' ' ) .. ')')
return frame:preprocess(s)
end
if kwargs.aliases ~= nil then
params = mw.text.split(kwargs.aliases, ',')
table.insert(results, messages['alias'] .. ' (' .. mw.text.listToText( params, ', ', ' ' .. messages['or'] .. ' ' ) .. ')')
end
if kwargs.descriptions ~= nil then
params = mw.text.split(kwargs.descriptions, ',')
table.insert(results, messages['description'] .. ' (' .. mw.text.listToText( params, ', ', ' ' .. messages['or'] .. ' ' ) .. ')')
end
results = table.concat( results, ' ' .. messages['and'] .. ' ' )
if kwargs.all ~= nil then
out = messages['wikidata_rule_all']
else
out = messages['wikidata_rule_first']
end
if kwargs.require_reference ~= nil then
out = out .. ' ' .. messages['wikidata_rule_require_reference']
end
return sprintf(out, {
['points'] = points,
['thing'] = results,
})
end
end


function rule.reference(points, args, kwargs)
return uk
return sprintf(messages['reference_rule'], {
['points'] = points,
['refpoints'] = args[1],
})
end

function rule.templateremoval(points, args, kwargs)
local templates = format_plural(make_template_list(args), 'templates')
return sprintf(messages['templateremoval_rule'], {
['points'] = points,
['templates'] = templates,
})
end

function rule.categoryremoval(points, args, kwargs)
local categories = format_plural(make_category_list(args), 'categories')
return sprintf(messages['categoryremoval_rule'], {
['points'] = points,
['categories'] = categories,
})
end

function rule.bytebonus(points, args, kwargs)
return sprintf(messages['bytebonus_rule'], {
['points'] = points,
['bytes'] = args[1],
})
end

function rule.section(points, args, kwargs)
if kwargs.description ~= nil then
return sprintf(messages['section_rule_desc'], {
['points'] = points,
['description'] = kwargs.description,
})
end
local sections = format_plural(args, 'sections')
return sprintf(messages['section_rule'], {
['points'] = points,
['sections'] = sections,
})
end

function rule.wordbonus(points, args, kwargs)
return sprintf(messages['wordbonus_rule'], {
['points'] = points,
['words'] = args[1],
})
end

function rule.format(frame)
-- Make tables of anonymous and named arguments
local args, kwargs = parse_args(frame)
rule_arg, args = shift_args(args)
points, args = shift_args(args)

-- Try to find the corresponding formatter or bail out if not found
if rule_arg == nil then
return frame:preprocess(make_error('argument_missing', 'rule'))
end
local formatter = config.rules[rule_arg]
if formatter == nil then
return frame:preprocess(make_error('invalid_rule', rule_arg))
end

-- All rules requires argument 1: number of points awarded
if points == nil then
return frame:preprocess(make_error('argument_missing', '1 (number of points)'))
end

points = points:gsub( '%.', config['decimal_separator'])

-- If there's a rule formatter function, use it.
-- Otherwise, use the string from the messages table.
local out
if rule[formatter] ~= nil then
out = rule[formatter](points, args, kwargs)
else
out = sprintf(messages[formatter .. '_rule'], {
['points'] = points,
})
end

if kwargs.max ~= nil then
out = sprintf(messages['base_rule_max'], {
['baserule'] = out,
['maxpoints'] = kwargs.max:gsub( '%.', config['decimal_separator']),
})
end

return frame:preprocess(out)
end

-- Export
return {
['criterion'] = criterion.format,
['rule'] = rule.format,
}

Versio 28. kesäkuuta 2021 kello 12.31

Code Result
{{#invoke:Viikon kilpailu kriteerit|criterion|uusi}} jotka aloitetaan kilpailun aikana
{{#invoke:Viikon kilpailu kriteerit|criterion|uusi|redirects=1}} jotka aloitetaan kilpailun aikana (sisältää myös ohjaussivut)
{{#invoke:Viikon kilpailu kriteerit|criterion|löytyvä}} jotka ovat olemassa ennen kilpailun aloituspäivämäärää
{{#invoke:Viikon kilpailu kriteerit|criterion|tynkä}} nil
{{#invoke:Viikon kilpailu kriteerit|criterion|tavut|1000}} joita laajennetaan vähintään 1000 tavulla
{{#invoke:Viikon kilpailu kriteerit|criterion|nimiavaruus|14}} jotka ovat luokka
{{#invoke:Viikon kilpailu kriteerit|criterion|nimiavaruus|0|10}} jotka ovat artikkeleita tai malline
{{#invoke:Viikon kilpailu kriteerit|criterion|takaisinlinkitys|Luettelo Nobel-palkituista henkilöistä}} joihin johtaa linkki sivulta Luettelo Nobel-palkituista henkilöistä
{{#invoke:Viikon kilpailu kriteerit|criterion|edelleenlinkitys|Bergen}} joissa on linkki sivulle Bergen
{{#invoke:Viikon kilpailu kriteerit|criterion|malline|Selvennä}} jotka on merkitty mallineella {{Selvennä}}
{{#invoke:Viikon kilpailu kriteerit|criterion|malline|Viitteetön|Lähteetön}} jotka on merkitty mallineilla {{Viitteetön}} tai {{Lähteetön}}
{{#invoke:Viikon kilpailu kriteerit|criterion|luokka|Islanti}} jotka sijaitsevat luokassa Islanti
{{#invoke:Viikon kilpailu kriteerit|criterion|luokka|Islanti|se:Islánda|be:Ісландыя}} jotka sijaitsevat luokissa Islanti, Islánda tai Ісландыя
{{#invoke:Viikon kilpailu kriteerit|criterion|luokka|Nisäkkäät|ignore=Ihminen,Sukupuuttoon kuolleet nisäkkäät}} jotka sijaitsevat luokassa Nisäkkäät
{{#invoke:Viikon kilpailu kriteerit|criterion|sivut|Islanti|Grönlanti}} sivuihin Islanti tai Grönlanti
{{#invoke:Viikon kilpailu kriteerit|criterion|sparql|query=?item wdt:P31 wd:Q146. }} jotka löytyvät tällä SPARQL-kyselyllä
{{#invoke:Viikon kilpailu kriteerit|criterion|sparql|query=?item wdt:P31 wd:Q146. |description=about cats }} about cats (näytä SPARQL-kysely)

Methods for {{Viikon kilpailu pisteet}}

Code Result
{{#invoke:Viikon kilpailu kriteerit|rule|uusi|20}} Jokaisesta uudesta artikkelista annetaan 20 pistettä
{{#invoke:Viikon kilpailu kriteerit|rule|ohjaus|1}} Jokaisesta luodusta ohjaussivusta annetaan 1 pistettä
{{#invoke:Viikon kilpailu kriteerit|rule|qualified|2}} Jokaisesta hyväksytystä sivusta annetaan 2 pistettä
{{#invoke:Viikon kilpailu kriteerit|rule|edit|1}} Jokaisesta muokkauksesta annetaan 1 pistettä
{{#invoke:Viikon kilpailu kriteerit|rule|tavu|0.1}} Jokaisesta lisätystä tavusta annetaan 0,1 pistettä
{{#invoke:Viikon kilpailu kriteerit|rule|tavu|1|max=100}} Jokaisesta lisätystä tavusta annetaan 1 pistettä, mutta enintään 100 pistettä per artikkeli
{{#invoke:Viikon kilpailu kriteerit|rule|tavubonus|20|3000}} Jokaisesta vähintään 3000 tavua tuottaneesta lisäyksestä annetaan 20 bonuspistettä
{{#invoke:Viikon kilpailu kriteerit|rule|sana|3}} Jokaisesta lisätystä sanasta annetaan 3 pistettä
{{#invoke:Viikon kilpailu kriteerit|rule|sana|3|max=100}} Jokaisesta lisätystä sanasta annetaan 3 pistettä, mutta enintään 100 pistettä per artikkeli
{{#invoke:Viikon kilpailu kriteerit|rule|sanabonus|20|3000}} Jokaisesta vähintään 3000 sanaa tuottaneesta lisäyksestä annetaan 20 bonuspistettä
{{#invoke:Viikon kilpailu kriteerit|rule|kuva|3}} Jokaisesta lisätystä kuvasta annetaan 3 pistettä
{{#invoke:Viikon kilpailu kriteerit|rule|kuva|3|max=100}} Jokaisesta lisätystä kuvasta annetaan 3 pistettä, mutta enintään 100 pistettä per artikkeli
{{#invoke:Viikon kilpailu kriteerit|rule|kuva|3|max=100|ownimage=10}} Jokaisesta lisätystä kuvasta annetaan 3 pistettä (10 itse ladatuista), mutta enintään 100 pistettä per artikkeli
{{#invoke:Viikon kilpailu kriteerit|rule|kuva|3|max=100|ownimage=10|initialimagelimit=0}} Jokaisesta lisätystä kuvasta tai muusta tiedostosta, joka lisätään sivulle, jolla oli aikaisemmin enintään 0 tiedostoa, annetaan 3 pistettä (10 itse ladatuista), mutta enintään 100 pistettä per artikkeli
{{#invoke:Viikon kilpailu kriteerit|rule|ref|3|1}} Jokaisesta lisätystä lähteestä annetaan 3 pistettä ja 1 pistettä viittauksesta jo olemassa olevaan lähteeseen
{{#invoke:Viikon kilpailu kriteerit|rule|ref|3|1|max=100}} Jokaisesta lisätystä lähteestä annetaan 3 pistettä ja 1 pistettä viittauksesta jo olemassa olevaan lähteeseen, mutta enintään 100 pistettä per artikkeli
{{#invoke:Viikon kilpailu kriteerit|rule|mallineen poisto|10| Viitteetön | Lähteetön }} Jokaisesta mallineen {{Viitteetön}} tai {{Lähteetön}} poistosta annetaan 10 pistettä
{{#invoke:Viikon kilpailu kriteerit|rule|exlink|3}} Jokaisesta aiheesta muualla-linkin lisäämisestä annetaan 3 pistettä
{{#invoke:Viikon kilpailu kriteerit|rule|exlink|2|max=10}} Jokaisesta aiheesta muualla-linkin lisäämisestä annetaan 2 pistettä, mutta enintään 10 pistettä per artikkeli
{{#invoke:Viikon kilpailu kriteerit|rule|osio|10|Lähteet|Viitteet}} Jokaisesta lisätystä Lähteet tai Viitteet -osiosta annetaan 10 pistettä
{{#invoke:Viikon kilpailu kriteerit|rule|osio|10|Lähteet|Viitteet|description=lähdeosiosta}} Jokaisesta lisätystä lähdeosiosta annetaan 10 pistettä
{{#invoke:Viikon kilpailu kriteerit|rule|wikidata|2|properties=P18,P2096}} 2 points are awarded for adding P18 tai P2096 to items that did not have such a claim from before
{{#invoke:Viikon kilpailu kriteerit|rule|wikidata|2|properties=P569|require_reference=yes}} 2 points are awarded for adding P569 to items that did not have such a claim from before (only referenced)
{{#invoke:Viikon kilpailu kriteerit|rule|wikidata|2|max=10|properties=P569|all=yes}} 2 points are awarded for adding P569 to items, mutta enintään 10 pistettä per artikkeli
{{#invoke:Viikon kilpailu kriteerit|rule|wikidata|2|labels=nb,nn,se}} 2 points are awarded for adding Wikidata label (nb, nn tai se) to items that did not have such a claim from before
{{#invoke:Viikon kilpailu kriteerit|rule|wikidata|2|aliases=nb,nn,se}} 2 points are awarded for adding Wikidata alias (nb, nn tai se) to items that did not have such a claim from before
{{#invoke:Viikon kilpailu kriteerit|rule|wikidata|2|descriptions=nb,nn,se}} 2 points are awarded for adding Wikidata description (nb, nn tai se) to items that did not have such a claim from before

uk = {}

local messages = {
    ['and'] = 'ja',
    ['or'] = 'tai',
    ['page_at_site'] = '%(page)s at %(site)s',
    ['argument_missing'] = 'Argumentti puuttuu: %s',
    ['anon_argument_missing'] = 'Arvoja ei annettu: %s',
    ['invalid_criterion'] = '«%s» ei ole sallittu kriteeri',
    ['invalid_rule'] = '«%s» ei ole sallittu pisteytyssääntö',

    -- Criteria

    ['templates'] = 'mallinetta',
    ['templates_criterion_singular'] = 'jotka on merkitty mallineella %s',
    ['templates_criterion_plural'] = 'jotka on merkitty mallineilla %s',

    ['categories'] = 'luokkaa',
    ['categories_criterion_singular'] = 'jotka sijaitsevat luokassa %s',
    ['categories_criterion_plural'] = 'jotka sijaitsevat luokissa %s',
    ['categories_criterion_ignore'] = ', mutta eivät kuitenkaan luokissa %s',

    ['backlinks'] = 'takaisinlinkitystä',
    ['backlinks_criterion_singular'] = 'joihin johtaa linkki sivulta %s',
    ['backlinks_criterion_plural'] = 'joihin johtaa linkki sivuilta %s',

    ['forwardlinks'] = 'edelleenlinkitystä',
    ['forwardlinks_criterion_singular'] = 'joissa on linkki sivulle %s',
    ['forwardlinks_criterion_plural'] = 'joissa on linkki sivuille %s',

    ['pages'] = 'sivua',
    ['pages_criterion_singular'] = 'yksittäiseen sivuun %s',
    ['pages_criterion_plural'] = 'sivuihin %s',

    ['sparql_criterion'] = 'jotka löytyvät [%(queryLink)s tällä SPARQL-kyselyllä]',
    ['sparql_criterion_with_explanation'] = '%(description)s ([%(queryLink)s näytä SPARQL-kysely])',

    ['bytes_criterion'] = 'joita laajennetaan vähintään %s tavulla',

    ['namespaces_criterion_singular'] = 'jotka ovat %s',  -- %s will be the name of a namespace, e.g. "luokka"
    ['namespaces_criterion_plural'] = 'jotka ovat %s',  -- %s will be the name of a namespace, e.g. "luokka"
    ['article'] = 'artikkeleita',

    ['new_criterion'] = 'jotka aloitetaan kilpailun aikana',
    ['new_criterion_with_redirects'] = 'jotka aloitetaan kilpailun aikana (sisältää myös ohjaussivut)',

    ['existing_criterion'] = 'jotka ovat olemassa ennen kilpailun aloituspäivämäärää',

    -- Rules

    ['base_rule_max'] = '%(baserule)s, mutta enintään %(maxpoints)s pistettä per artikkeli',

    ['newpage_rule'] = 'Jokaisesta uudesta artikkelista annetaan %(points)s pistettä',
    ['newredirect_rule'] = 'Jokaisesta luodusta ohjaussivusta annetaan %(points)s pistettä',
    ['page_rule'] = 'Jokaisesta hyväksytystä sivusta annetaan %(points)s pistettä',
    ['edit_rule'] = 'Jokaisesta muokkauksesta annetaan %(points)s pistettä',
    ['byte_rule'] = 'Jokaisesta lisätystä tavusta annetaan %(points)s pistettä',
    ['listbyte_rule'] = 'Jokaisesta luettelo-artikkeliin lisätystä tavusta annetaan %(points)s pistettä',
    ['word_rule'] = 'Jokaisesta lisätystä sanasta annetaan %(points)s pistettä',

    ['image_rule'] = 'Jokaisesta lisätystä kuvasta annetaan %(points)s pistettä',
    ['image_rule_limited'] = 'Jokaisesta lisätystä kuvasta tai muusta tiedostosta, joka lisätään sivulle, jolla oli aikaisemmin enintään %(initialimagelimit)s tiedostoa, annetaan %(points)s pistettä',
    ['image_rule_own'] = '(%(ownimage)s itse ladatuista)',

    ['reference_rule'] = 'Jokaisesta lisätystä lähteestä annetaan %(points)s pistettä ja %(refpoints)s pistettä viittauksesta jo olemassa olevaan lähteeseen',
   
    ['templateremoval_rule'] = 'Jokaisesta mallineen %(templates)s poistosta annetaan %(points)s pistettä',
    ['categoryremoval_rule'] = '.. %(categories)s ... annetaan %(points)s pistettä',
    ['exlink_rule'] = 'Jokaisesta aiheesta muualla-linkin lisäämisestä annetaan %(points)s pistettä',

    ['section_rule'] = 'Jokaisesta lisätystä %(sections)s -osiosta annetaan %(points)s pistettä',
    ['section_rule_desc'] = 'Jokaisesta lisätystä %(description)s annetaan %(points)s pistettä ',

    ['wikidata_rule_first'] = '%(points)s points are awarded for adding %(thing)s to items that did not have such a claim from before',
    ['wikidata_rule_all'] = '%(points)s points are awarded for adding %(thing)s to items',
    ['wikidata_rule_require_reference'] = '(only referenced)',
    ['properties'] = 'properties',
    ['labels'] = 'labels',
    ['aliases'] = 'aliases',
    ['descriptions'] = 'descriptions',
    ['label'] = 'Wikidata label',
    ['alias'] = 'Wikidata alias',
    ['description'] = 'Wikidata description',

    ['bytebonus_rule'] = 'Jokaisesta vähintään %(bytes)s tavua tuottaneesta lisäyksestä annetaan %(points)s bonuspistettä',
    ['wordbonus_rule'] = 'Jokaisesta vähintään %(words)s sanaa tuottaneesta lisäyksestä annetaan %(points)s bonuspistettä',
}

local config = {
    ['decimal_separator'] = ',',
    ['template_link_template'] = 'Malline',
    ['error_message_template'] = 'Virhe',
    -- Map localized argument values for the criterion template
    ['criteria'] = {
        ['uusi'] = 'new',
        ['löytyvä'] = 'existing',
        ['tynkä'] = 'stub',
        ['tavut'] = 'bytes',
        ['nimiavaruus'] = 'namespaces',
        ['luokka'] = 'categories',
        ['malline'] = 'templates',
        ['takaisinlinkitys'] = 'backlinks',
        ['edelleenlinkitys'] = 'forwardlinks',
        ['sivut'] = 'pages',
        ['sparql'] = 'sparql',
    },
    -- Localized argument values for the rule template
    ['rules'] = {
        ['uusi'] = 'newpage',
        ['ohjaus'] = 'newredirect',
        ['qualified'] = 'page',
        ['edit'] = 'edit',
        -- ['stub'] = '(deprecated)',
        ['tavu'] = 'byte',
        ['listtavu'] = 'listbyte',
        ['sana'] = 'word',
        ['kuva'] = 'image',
        ['ref'] = 'reference',
        ['tavubonus'] = 'bytebonus',
        ['sanabonus'] = 'wordbonus',
        ['mallineen poisto'] = 'templateremoval',
        ['osio'] = 'section',
        ['categoryremoval'] = 'categoryremoval',
        ['exlink'] = 'exlink',
        ['wikidata'] = 'wikidata'
    }
}

local category_prefix = {
    ['be'] = 'be:Катэгорыя',
    ['se'] = 'se:Kategoriija',
    ['nn'] = 'nn:Kategori',
    ['no'] = 'no:Kategori',
    ['olo'] = 'olo:Category',
    ['smn'] = 'smn:Category',
    ['default'] = 'Luokka'
}

--[ Helper methods ] ------------------------------------------------------------------

--[[ Named Parameters with Formatting Codes
     Source: <http://lua-users.org/wiki/StringInterpolation>, author:RiciLake ]]
local function sprintf(s, tab)
    return (s:gsub('%%%((%a%w*)%)([-0-9%.]*[cdeEfgGiouxXsq])',
            function(k, fmt) return tab[k] and ("%"..fmt):format(tab[k]) or
                '%('..k..')'..fmt end))
end

local function make_error(template, arg)
    return string.format(
        '{{%s|%s}}',
        config['error_message_template'],
        string.format(messages[template], arg)
    )
end

local function parse_args(frame)
    local args = {}
    local kwargs = {}
    for k, v in pairs(frame.args) do
        v = mw.text.trim(frame:preprocess(v))
        if v ~= '' then
            if type(k) == 'number' then
                args[k] = v
            else
                kwargs[k] = v
            end
        end
    end
    return args, kwargs
end

local function shift_args(in_args)
    local args = {}
    for i, v in ipairs(in_args) do
        if i > 1 then
            args[i - 1] = v
        end
    end
    return in_args[1], args
end


local function format_plural(items, item_type)
    if #items == 0 then
        return make_error('anon_argument_missing', messages[item_type])
    end
    if #items == 1 then
        return items[1]
    end
    return mw.text.listToText(items, ', ', ' ' .. messages['or'] .. ' ')
end

local function format_plural_criterion(items, item_type)
    local value = format_plural(items, item_type)
    if #items == 0 then
        return value
    end
    if #items == 1 then
        return string.format(messages[item_type .. '_criterion_singular'], value)
    end
    return string.format(messages[item_type .. '_criterion_plural'], value)
end

local function make_template_list(args)
    local templates = {}
    for i, v in ipairs(args) do
        local lang, link = string.match(v, '^([a-z]+):(.+)$')
        if lang then
            table.insert(templates, string.format('{{%s|%s|%s}}', config['template_link_template'], link, lang))
        else
            table.insert(templates, string.format('{{%s|%s}}', config['template_link_template'], v))
        end
    end
    return templates
end

local function make_category_link(v)
    local lang = 'default'
    local name = v
    local m, n = string.match(v, '^([a-z]+):(.+)$')
    if m then
        lang = m
        name = n
    end
    return string.format('[[:%s:%s|%s]]', category_prefix[lang], name, name)
end

local function make_category_list(args)
    local category_links = {}
    for i, v in ipairs(args) do
        v = mw.text.trim(v)
        if v ~= '' then
            table.insert(category_links, make_category_link(v))
        end
    end
    return category_links
end

local function pagelist(args)
    local r = {}
    for i, v in ipairs(args) do
        v = mw.text.trim(v)
        if v ~= '' then
            local lang, page = string.match(v, '^([a-z]+):(.+)$')
            if lang then
                table.insert(r, string.format('[[:%s:%s|%s]]', lang, page, page))
            else
                table.insert(r, string.format('[[:%s]]', v))
            end
        end
    end
    return r
end

local function nslist(args)
    local r = {}
    local namespaceName = messages['article']
    for i, namespaceId in ipairs(args) do
        namespaceId = mw.text.trim(namespaceId)
        if namespaceId ~= '' then
            if namespaceId ~= "0" then
                namespaceName = '{{lc:{{ns:' .. namespaceId .. '}}}}'
            end
            table.insert(r, namespaceName)
        end
    end
    return r
end

--[ Criterion format methods ]-------------------------------------------------------------

local criterion = {}

function criterion.backlinks(args, kwargs, frame)
    return format_plural_criterion(pagelist(args), 'backlinks')
end

function criterion.bytes(args, kwargs, frame)
   return string.format(messages['bytes_criterion'], args[1])
end

function criterion.categories(args, kwargs, frame)
    local msg = format_plural_criterion(make_category_list(args), 'categories')

    if args.ignore ~= nil then
        r = mw.text.split(args.ignore, ',')
        for i, v in ipairs(r) do
            v = mw.text.trim(v)
            r[i] = make_category_link(v)
        end
        msg = msg .. string.format(messages['category_criterion_ignore'], mw.text.listToText(r, ', ', ' ' .. messages['or'] .. ' '))
    end

    return msg
end

function criterion.existing(args, kwargs, frame)
    return messages['existing_criterion']
end

function criterion.forwardlinks(args, kwargs, frame)
    return format_plural_criterion(pagelist(args), 'forwardlinks')
end

function criterion.namespaces(args, kwargs, frame)
    local site = kwargs.site
    local msg = format_plural_criterion(nslist(args, site), 'namespaces')
    if site ~= nil then
        return sprintf(messages['page_at_site'], {
            ['page'] = msg,
            ['site'] = string.format('[https://%s %s]', site, site),
        })
    end
    return msg
end

function criterion.new(args, kwargs, frame)
    local msg = messages['new_criterion']
    if kwargs.redirects ~= nil then
        msg = messages['new_criterion_with_redirects']
    end
    return msg
end

function criterion.pages(args, kwargs, frame)
    return format_plural_criterion(pagelist(args), 'pages')
end

function criterion.sparql(args, kwargs, frame)
    local query = 'SELECT DISTINCT ?item WHERE {\n  ' .. kwargs.query .. '\n}'
    local queryToolQuery='SELECT ?item ?itemLabel ?itemDescription WITH {' .. query ..'} AS %items WHERE { include %items .   SERVICE wikibase:label { bd:serviceParam wikibase:language "fi,se,smn,en". } }'

    local url = 'http://query.wikidata.org/#' .. frame:callParserFunction('urlencode', { queryToolQuery, 'PATH' })
    local vizUrl = 'https://tools.wmflabs.org/hay/vizquery/#' .. frame:callParserFunction('urlencode', { query, 'PATH' })

    if kwargs.description ~= nil then
        return sprintf(messages['sparql_criterion_with_explanation'], {
            description = kwargs.description,
            queryLink = url,
            vizQueryLink = vizUrl
        })
    end

    return sprintf(messages['sparql_criterion'], {
        queryLink=url,
        vizQueryLink=vizUrl
    })
end

function criterion.stub(args, kwargs, frame)
    -- deprecated
    return messages['stub_criterion']
end

function criterion.templates(args, kwargs, frame)
    return format_plural_criterion(make_template_list(args), 'templates')
end

function criterion.format(frame)
    local args, kwargs = parse_args(frame)
    local criterion_arg, args = shift_args(args)

    -- Try to find the corresponding formatter or bail out if not found
    if criterion_arg == nil then
        return frame:preprocess(make_error('argument_missing', 'criterion'))
    end
    local formatter = config.criteria[criterion_arg]
    if formatter == nil or criterion[formatter] == nil then
        return frame:preprocess(make_error('invalid_criterion', criterion_arg))
    end

    -- Use manual description if given
    if kwargs.description ~= nil and formatter ~= 'sparql' then
        return kwargs.description
    end

    -- Generate auto-generated description
    return frame:preprocess(criterion[formatter](args, kwargs, frame))
end

--[ Rule format methods ]-------------------------------------------------------------

local rule = {}

function rule.image(points, args, kwargs)
    local out
    local tplargs = {
        ['points'] = points,
    }
    if kwargs.initialimagelimit ~= nil then
        out = messages['image_rule_limited']
        tplargs['initialimagelimit'] = kwargs.initialimagelimit
    else
        out = messages['image_rule']
    end
    if kwargs.ownimage ~= nil then
        out = out .. ' ' .. messages['image_rule_own']
        tplargs['ownimage'] = kwargs.ownimage
    end
    return sprintf(out, tplargs)
end

function rule.wikidata(points, args, kwargs)
    local out
    local params
    local arg_types = { messages['properties'], messages['labels'], messages['aliases'], messages['descriptions'] }
    local results = {}
    if kwargs.properties == nil and kwargs.labels == nil and kwargs.aliases == nil and kwargs.descriptions == nil then
        return make_error(
            'argument_missing',
            mw.text.listToText( arg_types, ', ', ' ' .. messages['or'] .. ' ' )
        )
    end
    if kwargs.properties ~= nil then
        params = mw.text.split(kwargs.properties, ',')
        for k, v in pairs(params) do
            params[k] = string.format('[[:d:Property:%s|%s]]', v, v)
        end
        table.insert(results, mw.text.listToText( params, ', ', ' ' .. messages['or'] .. ' ' ))
    end
    if kwargs.labels ~= nil then
        params = mw.text.split(kwargs.labels, ',')
        table.insert(results, messages['label'] .. ' (' .. mw.text.listToText( params, ', ', ' ' .. messages['or'] .. ' ' ) .. ')')
    end
    if kwargs.aliases ~= nil then
        params = mw.text.split(kwargs.aliases, ',')
        table.insert(results, messages['alias'] .. ' (' .. mw.text.listToText( params, ', ', ' ' .. messages['or'] .. ' ' ) .. ')')
    end
    if kwargs.descriptions ~= nil then
        params = mw.text.split(kwargs.descriptions, ',')
        table.insert(results, messages['description'] .. ' (' .. mw.text.listToText( params, ', ', ' ' .. messages['or'] .. ' ' ) .. ')')
    end
    results = table.concat( results, ' ' .. messages['and'] .. ' ' )
    if kwargs.all ~= nil then
        out = messages['wikidata_rule_all']
    else
        out = messages['wikidata_rule_first']
    end
    if kwargs.require_reference ~= nil then
        out = out .. ' ' .. messages['wikidata_rule_require_reference']
    end
    return sprintf(out, {
        ['points'] = points,
        ['thing'] = results,
    })
end

function rule.reference(points, args, kwargs)
    return sprintf(messages['reference_rule'], {
        ['points'] = points,
        ['refpoints'] = args[1],
    })
end

function rule.templateremoval(points, args, kwargs)
    local templates = format_plural(make_template_list(args), 'templates')
    return sprintf(messages['templateremoval_rule'], {
        ['points'] = points,
        ['templates'] = templates,
    })
end

function rule.categoryremoval(points, args, kwargs)
    local categories = format_plural(make_category_list(args), 'categories')
    return sprintf(messages['categoryremoval_rule'], {
        ['points'] = points,
        ['categories'] = categories,
    })
end

function rule.bytebonus(points, args, kwargs)
    return sprintf(messages['bytebonus_rule'], {
        ['points'] = points,
        ['bytes'] = args[1],
    })
end

function rule.section(points, args, kwargs)
    if kwargs.description ~= nil then
        return sprintf(messages['section_rule_desc'], {
            ['points'] = points,
            ['description'] = kwargs.description,
        })
    end
    local sections = format_plural(args, 'sections')
    return sprintf(messages['section_rule'], {
        ['points'] = points,
        ['sections'] = sections,
    })
end

function rule.wordbonus(points, args, kwargs)
    return sprintf(messages['wordbonus_rule'], {
        ['points'] = points,
        ['words'] = args[1],
    })
end

function rule.format(frame)
    -- Make tables of anonymous and named arguments
    local args, kwargs = parse_args(frame)
    rule_arg, args = shift_args(args)
    points, args = shift_args(args)

    -- Try to find the corresponding formatter or bail out if not found
    if rule_arg == nil then
        return frame:preprocess(make_error('argument_missing', 'rule'))
    end
    local formatter = config.rules[rule_arg]
    if formatter == nil then
        return frame:preprocess(make_error('invalid_rule', rule_arg))
    end

    -- All rules requires argument 1: number of points awarded
    if points == nil then
        return frame:preprocess(make_error('argument_missing', '1 (number of points)'))
    end

    points = points:gsub( '%.', config['decimal_separator'])

    -- If there's a rule formatter function, use it.
    -- Otherwise, use the string from the messages table.
    local out
    if rule[formatter] ~= nil then
        out = rule[formatter](points, args, kwargs)
    else
        out = sprintf(messages[formatter .. '_rule'], {
            ['points'] = points,
        })
    end

    if kwargs.max ~= nil then
        out = sprintf(messages['base_rule_max'], {
            ['baserule'] = out,
            ['maxpoints'] = kwargs.max:gsub( '%.', config['decimal_separator']),
        })
    end

    return frame:preprocess(out)
end

-- Export
return {
    ['criterion'] = criterion.format,
    ['rule'] = rule.format,
}