Modul:UKB: Forskjell mellom sideversjoner
Hopp til navigering
Hopp til søk
m (Én sideversjon ble importert) |
(oppdatering fra sandkasse) |
||
Linje 1: | Linje 1: | ||
require('strict') |
|||
--[ Localization and config ]----------------------------------------------------------------- |
|||
local |
local p = {} |
||
['and'] = 'og', |
|||
['or'] = 'eller', |
|||
['page_at_site'] = '%(page)s på %(site)s', |
|||
['argument_missing'] = 'Argument mangler: %s', |
|||
['anon_argument_missing'] = 'Ingen %s ble angitt', |
|||
['invalid_criterion'] = '«%s» er ikke et gyldig kriterium', |
|||
['invalid_rule'] = '«%s» er ikke en gyldig poengregel', |
|||
local TNT = require('Module:TNT') |
|||
-- Criteria |
|||
local I18NDATASET = 'I18n/UKB.tab' |
|||
local getArgs = require('Module:Arguments').getArgs |
|||
--- Get a localized message. |
|||
['templates'] = 'maler', |
|||
-- @param key The message key |
|||
['templates_criterion_singular'] = 'merket med %s', |
|||
-- @param ... Parameters to be passed to the message ($1, $2, etc.) |
|||
['templates_criterion_plural'] = 'merket med %s', |
|||
-- @return localized string |
|||
local function msg( key, ... ) |
|||
return TNT.format( I18NDATASET, key, ... ) |
|||
end |
|||
--- Reverse a mapping to get a list of localized names => canonical names |
|||
['categories'] = 'kategorier', |
|||
-- @param mapping A table containing key-value pairs where the key is the canonical name and the value is an array table of aliases |
|||
['categories_criterion_singular'] = 'ligger i kategorien %s', |
|||
-- @return A table of localized names => canonical names |
|||
['categories_criterion_plural'] = 'ligger i minst en av kategoriene %s', |
|||
local function mappingReverser(mapping) |
|||
['categories_criterion_ignore'] = ', men ikke i %s', |
|||
local ret = {} |
|||
for canonical, synonyms in pairs(mapping) do |
|||
for _, synonym in ipairs(synonyms) do |
|||
ret[synonym] = canonical |
|||
end |
|||
local keyIsPresent, translations = pcall(msg, 'arg-' .. canonical) |
|||
if keyIsPresent then |
|||
translations = mw.text.split(translations, '|') |
|||
for _, translation in ipairs(translations) do |
|||
ret[translation] = canonical |
|||
end |
|||
end |
|||
end |
|||
return ret |
|||
end |
|||
--- Get the argument mapping for a type of item |
|||
['backlinks'] = 'tilbakelenker', |
|||
-- @param itemType The mapping subtype to get. Either 'criteria' or 'rules' |
|||
['backlinks_criterion_singular'] = 'lenkes fra %s', |
|||
-- @param returnType Which mapping to get; 'canonical' or 'translated' |
|||
['backlinks_criterion_plural'] = 'lenkes fra %s', |
|||
-- @return A table of mappings |
|||
local function getArgumentMapping(itemType, returnType) |
|||
-- if a new argument is added, it should also be added to the i18n module |
|||
-- in [[c:Data:I18n/UKB.tab]] |
|||
local argumentMapping = { |
|||
['criteria'] = { |
|||
['backlinks'] = { 'backlink' }, |
|||
['bytes'] = { 'byte' }, |
|||
['categories'] = { 'category' }, |
|||
['forwardlinks'] = { 'forwardlink' }, |
|||
['new'] = {}, |
|||
['existing'] = {}, |
|||
['namespaces'] = { 'namespace' }, |
|||
['pages'] = { 'page' }, |
|||
['sparql'] = {}, |
|||
['stub'] = {}, -- deprecated, not in i18n |
|||
['templates'] = { 'template' } |
|||
}, |
|||
['rules'] = { |
|||
['bytes'] = { 'byte' }, |
|||
['bytebonus'] = {}, |
|||
['categoryremoval'] = {}, |
|||
['edit'] = {}, |
|||
['eligiblepage'] = {}, |
|||
['extlink'] = { 'exlink', 'externallink' }, |
|||
['image'] = { 'images' }, |
|||
['listbyte'] = { 'listbytes' }, |
|||
['newpage'] = {}, |
|||
['newredirect'] = {}, |
|||
['reference'] = { 'ref' }, |
|||
['section'] = {}, |
|||
['templateremoval'] = {}, |
|||
['wikidata'] = {}, |
|||
['word'] = { 'words' }, |
|||
['wordbonus'] = {} |
|||
}, |
|||
['modifiers'] = { |
|||
['aliases'] = {}, |
|||
['all'] = {}, |
|||
['description'] = {}, |
|||
['descriptions'] = {}, |
|||
['distinct'] = {}, |
|||
['ignore'] = {}, |
|||
['initialimagelimit'] = {}, |
|||
['labels'] = {}, |
|||
['max'] = {}, |
|||
['ownimage'] = {}, |
|||
['properties'] = {}, |
|||
['query'] = {}, |
|||
['requirereference'] = { 'require reference', 'require_reference' }, |
|||
['redirects'] = { 'redirect' }, |
|||
['site'] = {}, |
|||
} |
|||
} |
|||
if returnType == 'canonical' then |
|||
['forwardlinks'] = 'framlenker', |
|||
return argumentMapping[itemType] |
|||
['forwardlinks_criterion_singular'] = 'lenker til %s', |
|||
end |
|||
['forwardlinks_criterion_plural'] = 'lenker til %s', |
|||
local translatedMap = { |
|||
['pages'] = 'sider', |
|||
['criteria'] = mappingReverser(argumentMapping.criteria), |
|||
['pages_criterion_singular'] = '%s', |
|||
['rules'] = mappingReverser(argumentMapping.rules), |
|||
['pages_criterion_plural'] = '%s', |
|||
['modifiers'] = mappingReverser(argumentMapping.modifiers) |
|||
} |
|||
return translatedMap[itemType] |
|||
['sparql_criterion'] = 'har et wikidata-element som matcher [%(queryLink)s denne SPARQL-spørringen]', |
|||
end |
|||
['sparql_criterion_with_explanation'] = '%(description)s ([%(queryLink)s Wikidata-spørring])', |
|||
['bytes_criterion'] = 'utvides med minst %s byte', |
|||
['namespaces_criterion_singular'] = 'er en %s', |
|||
['namespaces_criterion_plural'] = 'er en %s', |
|||
['article'] = 'artikkel', |
|||
['new_criterion'] = 'opprettes i løpet av konkurransen', |
|||
['new_criterion_with_redirects'] = 'opprettes i løpet av konkurransen (inkludert omdirigeringer)', |
|||
['existing_criterion'] = 'ble opprettet før konkurransen startet', |
|||
-- Rules |
|||
['rule_site'] = '%(baserule)s på %(site)s', |
|||
['base_rule_max'] = '%(baserule)s, men maks %(maxpoints)s poeng per side', |
|||
['custom_rule'] = 'Det gis %(points)s poeng for %(description)s', |
|||
['newpage_rule'] = 'Det gis %(points)s poeng for opprettelse av side (ikke omdirigeringsside)', |
|||
['newredirect_rule'] = 'Det gis %(points)s poeng for opprettelse av omdirigeringsside', |
|||
['page_rule'] = 'Det gis %(points)s poeng for hver kvalifiserte side', |
|||
['edit_rule'] = 'Det gis %(points)s poeng for hver redigering', |
|||
['byte_rule'] = 'Det gis %(points)s poeng for hver tilføyde byte', |
|||
['listbyte_rule'] = 'Det gis %(points)s poeng for hver tilføyde byte i en listeartikkel', |
|||
['word_rule'] = 'Det gis %(points)s poeng for hvert tilføyde ord i brødtekst (ikke i maler, tabeller, o.l.)', |
|||
['image_rule'] = 'Det gis %(points)s poeng for hvert tilføyde bilde', |
|||
['image_rule_limited'] = 'Det gis %(points)s poeng for hvert tilføyde bilde til sider som hadde maks %(initialimagelimit)s bilder fra før', |
|||
['image_rule_own'] = '(%(ownimage)s for egenopplastede)', |
|||
['reference_rule'] = 'Det gis %(points)s poeng for hver tilføyde kilde og %(refpoints)s for hver henvisning til eksisterende (navngitt) kilde', |
|||
['templateremoval_rule'] = 'Det gis %(points)s poeng for fjerning av %(templates)s', |
|||
['categoryremoval_rule'] = 'Det gis %(points)s poeng for fjerning av %(categories)s', |
|||
['exlink_rule'] = 'Det gis %(points)s poeng for innlegging av [[WP:EL|ekstern lenke]]', |
|||
['section_rule'] = 'Det gis %(points)s poeng for tilføyelse av seksjonen «%(sections)s»', |
|||
['section_rule_desc'] = 'Det gis %(points)s poeng for tilføyelse av %(description)s', |
|||
['wikidata_rule_first'] = 'Det gis %(points)s poeng for tilføyelse av %(thing)s på elementer som ikke har dette fra før', |
|||
['wikidata_rule_all'] = 'Det gis %(points)s poeng for hver tilføyde %(thing)s', |
|||
['wikidata_rule_require_reference'] = '(kun referansebelagte utsagn teller)', |
|||
['properties'] = 'egenskaper', |
|||
['labels'] = 'etiketter', |
|||
['aliases'] = 'alias', |
|||
['descriptions'] = 'beskrivelser', |
|||
['label'] = 'Wikidata-etikett', |
|||
['alias'] = 'Wikidata-alias', |
|||
['description'] = 'Wikidata-beskrivelse', |
|||
['bytebonus_rule'] = 'Det gis %(points)s bonuspoeng når det tilføres mer enn %(bytes)s bytes til en side', |
|||
['wordbonus_rule'] = 'Det gis %(points)s bonuspoeng når det tilføres mer enn %(words)s ord til en side', |
|||
} |
|||
local config = { |
|||
['decimal_separator'] = ',', |
|||
['template_link_template'] = 'Mal', |
|||
['error_message_template'] = 'Feil', |
|||
-- Map localized argument values for the criterion template |
|||
['criteria'] = { |
|||
['ny'] = 'new', |
|||
['eksisterende'] = 'existing', |
|||
['stubb'] = 'stub', -- deprecated |
|||
['bytes'] = 'bytes', |
|||
['navnerom'] = 'namespaces', |
|||
['kategori'] = 'categories', |
|||
['mal'] = 'templates', |
|||
['tilbakelenke'] = 'backlinks', |
|||
['fremlenke'] = 'forwardlinks', |
|||
['enkeltsider'] = 'pages', |
|||
['sparql'] = 'sparql', |
|||
['distinct'] = 'distinct', |
|||
}, |
|||
-- Localized argument values for the rule template |
|||
['rules'] = { |
|||
['ny'] = 'newpage', |
|||
['omdirigering'] = 'newredirect', |
|||
['kvalifisert'] = 'page', |
|||
['endring'] = 'edit', |
|||
['stubb'] = 'stubremoval', |
|||
['byte'] = 'byte', |
|||
['listebyte'] = 'listbyte', |
|||
['ord'] = 'word', |
|||
['bilde'] = 'image', |
|||
['ref'] = 'reference', |
|||
['bytebonus'] = 'bytebonus', |
|||
['ordbonus'] = 'wordbonus', |
|||
['malfjerning'] = 'templateremoval', |
|||
['seksjon'] = 'section', |
|||
['kategorifjerning'] = 'categoryremoval', |
|||
['ekstern lenke'] = 'exlink', |
|||
['wikidata'] = 'wikidata' |
|||
} |
|||
} |
|||
local category_prefix = { |
|||
['smn'] = 'smn:Luokka', |
|||
['se'] = 'se:Kategoriija', |
|||
['nn'] = 'nn:Kategori', |
|||
['no'] = 'Kategori', |
|||
['commons'] = 'commons:Category', |
|||
['default'] = 'Kategori' |
|||
} |
|||
--[ Helper methods ] ------------------------------------------------------------------ |
--[ Helper methods ] ------------------------------------------------------------------ |
||
--- Make an error string |
|||
--[[ Named Parameters with Formatting Codes |
|||
-- @tparam string text Text to be wrapped in an error class |
|||
Source: <http://lua-users.org/wiki/StringInterpolation>, author:RiciLake ]] |
|||
-- @treturn string The text wrapped in an error class |
|||
local function sprintf(s, tab) |
|||
local function makeErrorString(text) |
|||
return (s:gsub('%%%((%a%w*)%)([-0-9%.]*[cdeEfgGiouxXsq])', |
|||
local html = mw.html.create('strong') |
|||
function(k, fmt) return tab[k] and ("%"..fmt):format(tab[k]) or |
|||
:addClass('error') |
|||
'%('..k..')'..fmt end)) |
|||
:wikitext(text) |
|||
return tostring(html) |
|||
end |
end |
||
--- Get an error string |
|||
local function make_error(template, arg) |
|||
-- @tparam string key A message key (from i18n) |
|||
return string.format( |
|||
-- @tparam string arg An argument to pass along to the message function |
|||
'{{%s|%s}}', |
|||
-- @treturn string An error message |
|||
config['error_message_template'], |
|||
local function getErrorString(key, arg) |
|||
string.format(messages[template], arg) |
|||
return makeErrorString(msg(key, arg)) |
|||
) |
|||
end |
end |
||
--- Parse and translate anonymous and named arguments |
|||
local function parse_args(frame) |
|||
-- @tparam table frame A frame object |
|||
local args = {} |
|||
-- @tparam string|nil itemType An item type to return ('criteria', 'rules' or nil) |
|||
local kwargs = {} |
|||
-- @treturn table A table of anonymous arguments (args) |
|||
-- @treturn table A table of named arguments (kwargs) |
|||
v = mw.text.trim(frame:preprocess(v)) |
|||
local function parseArgs(frame, itemType, translate) |
|||
if v ~= '' then |
|||
local args = {} |
|||
if type(k) == 'number' then |
|||
local kwargs = {} |
|||
args[k] = v |
|||
local canonicalMap = getArgumentMapping(itemType, 'translated') |
|||
else |
|||
if itemType == nil then |
|||
kwargs[k] = v |
|||
canonicalMap = {} |
|||
end |
|||
end |
|||
local kwargsMap = getArgumentMapping('modifiers', 'translated') |
|||
end |
|||
for k, v in pairs(getArgs(frame)) do |
|||
return args, kwargs |
|||
v = mw.text.trim(frame:preprocess(v)) |
|||
if v ~= '' then |
|||
if type(k) == 'number' then |
|||
if k == 1 and canonicalMap[v] ~= nil and translate then |
|||
args[1] = canonicalMap[v] |
|||
else |
|||
args[k] = v |
|||
end |
|||
else |
|||
if kwargsMap[k] ~= nil and translate then |
|||
kwargs[kwargsMap[k]] = v |
|||
else |
|||
kwargs[k] = v |
|||
end |
|||
end |
|||
end |
|||
end |
|||
return args, kwargs |
|||
end |
end |
||
--- Turn an array table into a string in list form |
|||
local function shift_args(in_args) |
|||
-- @tparam table items An array of items |
|||
local args = {} |
|||
-- @tparam string itemType Maybe unnecessary? |
|||
for i, v in ipairs(in_args) do |
|||
-- @tparam string word The strings 'or' or 'and' (representing i18n message keys) |
|||
if i > 1 then |
|||
-- @treturn string A string with the table returned as a list |
|||
args[i - 1] = v |
|||
local function listify(items, itemType, word) |
|||
end |
|||
word = word or 'or' |
|||
end |
|||
if #items == 0 then |
|||
return in_args[1], args |
|||
return getErrorString('anon-argument-missing', itemType) |
|||
end |
|||
if #items == 1 then |
|||
return items[1] |
|||
end |
|||
return mw.text.listToText(items, ', ', ' ' .. msg(word) .. ' ' ) |
|||
end |
end |
||
--- Get link data for a link to a page in a specific namespace |
|||
-- @tparam table frame A frame object |
|||
-- @tparam string ns A canonical (English) namespace name; 'Template' and 'Category' supported |
|||
-- @tparam string page A page name |
|||
-- @treturn table A table containing: language code, link target and page name |
|||
local function makeNsLink(frame, ns, page) |
|||
local linkTarget |
|||
local nsNumbers = { |
|||
['Template'] = 10, |
|||
['Category'] = 14 |
|||
} |
|||
local lang, pageName = mw.ustring.match(page, '^([a-z]+):(.+)$') -- FIXME: Better language code detection |
|||
if lang then |
|||
-- English namespace name is guaranteed to work, avoids need to maintain |
|||
-- lists of namespace names in the module |
|||
linkTarget = mw.ustring.format(':%s:%s:%s', lang, ns, pageName) |
|||
else |
|||
linkTarget = mw.ustring.format(':%s:%s', frame:callParserFunction('ns', nsNumbers[ns]), page) |
|||
end |
|||
return { |
|||
['lang'] = lang, |
|||
['linkTarget'] = linkTarget, |
|||
['pageName'] = pageName or page |
|||
} |
|||
end |
|||
--- Make a link to a single template, wrapped in curly brace syntax |
|||
local function format_plural(items, item_type) |
|||
-- @tparam table frame A frame object |
|||
if #items == 0 then |
|||
-- @tparam template Name of a template (optionally with an interlanguage prefix) |
|||
return make_error('anon_argument_missing', messages[item_type]) |
|||
-- @treturn string An HTML string linking to the template in question |
|||
end |
|||
local function makeTemplateLink(frame, template) |
|||
if #items == 1 then |
|||
local nsLink = makeNsLink(frame, 'Template', template) |
|||
return items[1] |
|||
local wikitext = mw.text.nowiki('{{') .. mw.ustring.format('[[%s|%s]]', nsLink['linkTarget'], nsLink['pageName']) .. mw.text.nowiki('}}') |
|||
end |
|||
local html = mw.html.create('span') |
|||
return mw.text.listToText(items, ', ', ' ' .. messages['or'] .. ' ') |
|||
:addClass('template-link') |
|||
:css('font-family', 'monospace,monospace') |
|||
:wikitext(wikitext) |
|||
return tostring(html) |
|||
end |
end |
||
--- Make a link to a single category |
|||
local function format_plural_criterion(items, item_type) |
|||
-- @tparam table frame A frame object |
|||
local value = format_plural(items, item_type) |
|||
-- @tparam category Name of a category (optionally with an interlanguage prefix) |
|||
if #items == 0 then |
|||
-- @treturn string An HTML string linking to the category in question |
|||
return value |
|||
local function makeCategoryLink(frame, category) |
|||
end |
|||
local nsLink = makeNsLink(frame, 'Category', category) |
|||
if #items == 1 then |
|||
return mw.ustring.format('[[%s|%s]]', nsLink['linkTarget'], nsLink['pageName']) |
|||
end |
|||
return string.format(messages[item_type .. '_criterion_plural'], value) |
|||
end |
end |
||
--- Make a list of templates |
|||
local function make_template_list(args) |
|||
-- @tparam table frame A frame object |
|||
-- @tparam table args An array of template names (optionally with interlanguage prefixes) |
|||
-- @treturn table A table of template links |
|||
local function makeTemplateList(frame, args) |
|||
local templates = {} |
local templates = {} |
||
for i, v in ipairs(args) do |
for i, v in ipairs(args) do |
||
table.insert(templates, makeTemplateLink(frame, v)) |
|||
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 |
end |
||
setmetatable(templates, { |
|||
__tostring = function(self) |
|||
return listify(templates, 'templates') |
|||
end |
|||
}) |
|||
return templates |
return templates |
||
end |
end |
||
--- Make a list of categories |
|||
local function make_category_link(v) |
|||
-- @tparam table frame A frame object |
|||
local lang = 'default' |
|||
-- @tparam table args An array of category names (optionally with interlanguage prefixes) |
|||
local name = v |
|||
-- @treturn table A table of category links |
|||
local m, n = string.match(v, '^([a-z]+):(.+)$') |
|||
local function makeCategoryList(frame, args) |
|||
if m then |
|||
local categories = {} |
|||
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 |
for i, v in ipairs(args) do |
||
v = mw.text.trim(v) |
v = mw.text.trim(v) |
||
if v ~= '' then |
if v ~= '' then |
||
table.insert( |
table.insert(categories, makeCategoryLink(frame, v)) |
||
end |
end |
||
end |
end |
||
setmetatable(categories, { |
|||
return category_links |
|||
__tostring = function(self) |
|||
return listify(categories, 'categories') |
|||
end |
|||
}) |
|||
return categories |
|||
end |
end |
||
--- Make a list of templates |
|||
local function pagelist(args) |
|||
-- @tparam table args An array of page names (optionally with interlanguage prefixes) |
|||
local r = {} |
|||
-- @treturn table A table of page links |
|||
local function makePageList(args) |
|||
local pages = {} |
|||
for i, v in ipairs(args) do |
for i, v in ipairs(args) do |
||
v = mw.text.trim(v) |
v = mw.text.trim(v) |
||
Linje 248: | Linje 272: | ||
local lang, page = string.match(v, '^([a-z]+):(.+)$') |
local lang, page = string.match(v, '^([a-z]+):(.+)$') |
||
if lang then |
if lang then |
||
table.insert( |
table.insert(pages, string.format('[[:%s:%s|%s]]', lang, page, page)) |
||
else |
else |
||
table.insert( |
table.insert(pages, string.format('[[:%s]]', v)) |
||
end |
end |
||
end |
end |
||
end |
end |
||
setmetatable(pages, { |
|||
return r |
|||
__tostring = function(self) |
|||
return listify(pages, 'pages') |
|||
end |
|||
}) |
|||
return pages |
|||
end |
end |
||
--- Make a list of namespaces |
|||
local function nslist(args) |
|||
-- @tparam table args An array of namespace IDs |
|||
local r = {} |
|||
-- @treturn table A table of namespace names |
|||
local namespaceName = messages['article'] |
|||
local function makeNsList(args) |
|||
for i, namespaceId in ipairs(args) do |
|||
local namespaces = {} |
|||
local namespaceName = msg('article') |
|||
for _, namespaceId in ipairs(args) do |
|||
namespaceId = mw.text.trim(namespaceId) |
namespaceId = mw.text.trim(namespaceId) |
||
if namespaceId ~= '' then |
if namespaceId ~= '' then |
||
Linje 266: | Linje 298: | ||
namespaceName = '{{lc:{{ns:' .. namespaceId .. '}}}}' |
namespaceName = '{{lc:{{ns:' .. namespaceId .. '}}}}' |
||
end |
end |
||
table.insert( |
table.insert(namespaces, namespaceName) |
||
end |
end |
||
end |
end |
||
setmetatable(namespaces, { |
|||
return r |
|||
__tostring = function(self) |
|||
return listify(namespaces, 'namespaces') |
|||
end |
|||
}) |
|||
return namespaces |
|||
end |
end |
||
Linje 276: | Linje 313: | ||
local criterion = {} |
local criterion = {} |
||
--- Formatter function for the backlinks criterion |
|||
-- @tparam table args Anonymous arguments to the module |
|||
-- @tparam table kwargs Keyword arguments to the module |
|||
-- @treturn string A message corresponding to the criterion |
|||
function criterion.backlinks(args, kwargs, frame) |
function criterion.backlinks(args, kwargs, frame) |
||
local pageList = makePageList(args) |
|||
return format_plural_criterion(pagelist(args), 'backlinks') |
|||
return msg('criterion-backlinks', #pageList, tostring(pageList)) |
|||
end |
end |
||
--- Formatter function for the bytes criterion |
|||
-- @tparam table args Anonymous arguments to the module |
|||
-- @tparam table kwargs Keyword arguments to the module |
|||
-- @treturn string A message corresponding to the criterion |
|||
function criterion.bytes(args, kwargs, frame) |
function criterion.bytes(args, kwargs, frame) |
||
return msg('criterion-bytes', args[1]) |
|||
end |
end |
||
--- Formatter function for the categories criterion |
|||
-- @tparam table args Anonymous arguments to the module |
|||
-- @tparam table kwargs Keyword arguments to the module |
|||
-- @treturn string A message corresponding to the criterion |
|||
function criterion.categories(args, kwargs, frame) |
function criterion.categories(args, kwargs, frame) |
||
local categoryList = makeCategoryList(frame, args) |
|||
local msg = format_plural_criterion(make_category_list(args), 'categories') |
|||
local ret = msg('criterion-categories', #categoryList, tostring(categoryList)) |
|||
if kwargs.ignore ~= nil then |
|||
local ignoredCats = mw.text.split(kwargs.ignore, ',') |
|||
ignoredCats = makeCategoryList(frame, ignoredCats) |
|||
for i, v in ipairs(r) do |
|||
ret = ret .. msg('categories-except', #ignoredCats, tostring(ignoredCats)) |
|||
v = mw.text.trim(v) |
|||
end |
|||
r[i] = make_category_link(v) |
|||
end |
|||
msg = msg .. string.format(messages['category_criterion_ignore'], mw.text.listToText(r, ', ', ' ' .. messages['or'] .. ' ')) |
|||
end |
|||
return |
return ret |
||
end |
end |
||
--- Formatter function for the existing criterion |
|||
-- @tparam table args Anonymous arguments to the module |
|||
-- @tparam table kwargs Keyword arguments to the module |
|||
-- @treturn string A message corresponding to the criterion |
|||
function criterion.existing(args, kwargs, frame) |
function criterion.existing(args, kwargs, frame) |
||
return msg('criterion-existing') |
|||
end |
end |
||
--- Formatter function for the forwardlinks criterion |
|||
-- @tparam table args Anonymous arguments to the module |
|||
-- @tparam table kwargs Keyword arguments to the module |
|||
-- @treturn string A message corresponding to the criterion |
|||
function criterion.forwardlinks(args, kwargs, frame) |
function criterion.forwardlinks(args, kwargs, frame) |
||
local pages = makePageList(args) |
|||
return format_plural_criterion(pagelist(args), 'forwardlinks') |
|||
return msg('criterion-forwardlinks', #pages, tostring(pages)) |
|||
end |
end |
||
--- Formatter function for the namespaces criterion |
|||
-- @tparam table args Anonymous arguments to the module |
|||
-- @tparam table kwargs Keyword arguments to the module |
|||
-- @treturn string A message corresponding to the criterion |
|||
function criterion.namespaces(args, kwargs, frame) |
function criterion.namespaces(args, kwargs, frame) |
||
local nsList = makeNsList(args) |
|||
local message |
|||
local msg = format_plural_criterion(nslist(args, site), 'namespaces') |
|||
if site ~= nil then |
|||
if #nsList == 1 and args[1] == '0' then |
|||
return sprintf(messages['page_at_site'], { |
|||
message = msg('criterion-namespace-0') |
|||
['page'] = msg, |
|||
else |
|||
['site'] = string.format('[https://%s %s]', site, site), |
|||
message = msg('criterion-namespace', #nsList, tostring(nsList)) |
|||
}) |
|||
end |
|||
return msg |
|||
if kwargs.site ~= nil then |
|||
return msg('page-at-site', message, mw.ustring.format('[https://%s %s]', kwargs.site, kwargs.site)) |
|||
end |
|||
return message |
|||
end |
end |
||
--- Formatter function for the new page criterion |
|||
-- @tparam table args Anonymous arguments to the module |
|||
-- @tparam table kwargs Keyword arguments to the module |
|||
-- @treturn string A message corresponding to the criterion |
|||
function criterion.new(args, kwargs, frame) |
function criterion.new(args, kwargs, frame) |
||
if kwargs.redirects ~= nil then |
|||
local msg = messages['new_criterion'] |
|||
return msg('criterion-new-with-redirects') |
|||
if kwargs.redirects ~= nil then |
|||
end |
|||
msg = messages['new_criterion_with_redirects'] |
|||
return msg('criterion-new') |
|||
end |
|||
return msg |
|||
end |
end |
||
--- Formatter function for the pages (page list) criterion |
|||
-- @tparam table args Anonymous arguments to the module |
|||
-- @tparam table kwargs Keyword arguments to the module |
|||
-- @treturn string A message corresponding to the criterion |
|||
function criterion.pages(args, kwargs, frame) |
function criterion.pages(args, kwargs, frame) |
||
local pages = makePageList(args) |
|||
return format_plural_criterion(pagelist(args), 'pages') |
|||
return msg('criterion-pages', #pages, tostring(pages)) |
|||
end |
end |
||
--- Formatter function for the SPARQL criterion |
|||
-- @tparam table args Anonymous arguments to the module |
|||
-- @tparam table kwargs Keyword arguments to the module |
|||
-- @treturn string A message corresponding to the criterion |
|||
function criterion.sparql(args, kwargs, frame) |
function criterion.sparql(args, kwargs, frame) |
||
local query = '' |
local query = '' |
||
Linje 338: | Linje 416: | ||
query = 'SELECT ?item WHERE {\n ' .. kwargs.query .. '\n}' |
query = 'SELECT ?item WHERE {\n ' .. kwargs.query .. '\n}' |
||
end |
end |
||
local url = 'http://query.wikidata.org/#' .. |
local url = 'http://query.wikidata.org/#' .. mw.uri.encode(query, 'PATH') |
||
local vizUrl = 'https://tools.wmflabs.org/hay/vizquery/#' .. frame:callParserFunction('urlencode', { query, 'PATH' }) |
|||
if kwargs.description ~= nil then |
if kwargs.description ~= nil then |
||
return |
return msg('criterion-sparql-with-explanation', kwargs.description, url) |
||
description = kwargs.description, |
|||
queryLink = url, |
|||
vizQueryLink = vizUrl |
|||
}) |
|||
end |
end |
||
return |
return msg('criterion-sparql', url) |
||
queryLink=url, |
|||
vizQueryLink=vizUrl |
|||
}) |
|||
end |
|||
function criterion.stub(args, kwargs, frame) |
|||
-- deprecated |
|||
return messages['stub_criterion'] |
|||
end |
end |
||
--- Formatter function for the templates criterion |
|||
-- @tparam table args Anonymous arguments to the module |
|||
-- @tparam table kwargs Keyword arguments to the module |
|||
-- @treturn string A message corresponding to the criterion |
|||
function criterion.templates(args, kwargs, frame) |
function criterion.templates(args, kwargs, frame) |
||
local templates = makeTemplateList(frame, args) |
|||
return format_plural_criterion(make_template_list(args), 'templates') |
|||
return msg('criterion-templates', #templates, tostring(templates)) |
|||
end |
end |
||
function criterion |
--- Main function for getting criterion messages |
||
-- @tparam table frame A frame object |
|||
local args, kwargs = parse_args(frame) |
|||
-- @treturn string A string representing the criterion (or an error message string) |
|||
local criterion_arg, args = shift_args(args) |
|||
function p.criterion(frame) |
|||
local args, kwargs = parseArgs(frame, 'criteria', true) |
|||
local criterionArg = table.remove(args, 1) |
|||
local permittedCriteria = getArgumentMapping('criteria', 'canonical') |
|||
if criterionArg == nil or criterionArg == '' then |
|||
-- Try to find the corresponding formatter or bail out if not found |
|||
return frame:preprocess(getErrorString('argument-missing', 'criterion')) |
|||
if criterion_arg == nil then |
|||
elseif permittedCriteria[criterionArg] == nil or criterion[criterionArg] == nil then |
|||
return frame:preprocess(make_error('argument_missing', 'criterion')) |
|||
return frame:preprocess(getErrorString('invalid-criterion', criterionArg)) |
|||
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 |
end |
||
-- Use manual description if given |
-- Use manual description if given |
||
if kwargs.description ~= nil and |
if kwargs.description ~= nil and criterionArg ~= 'sparql' then |
||
return kwargs.description |
return kwargs.description |
||
end |
end |
||
return frame:preprocess(criterion[criterionArg](args, kwargs, frame)) |
|||
-- Generate auto-generated description |
|||
return frame:preprocess(criterion[formatter](args, kwargs, frame)) |
|||
end |
end |
||
Linje 390: | Linje 460: | ||
local rule = {} |
local rule = {} |
||
--- Formatter function for custom rules |
|||
function rule.custom(points, args, kwargs) |
|||
-- @tparam number points A number of points; may by a float |
|||
return sprintf(messages['custom_rule'], { |
|||
-- @tparam table args Anonymous arguments to the module |
|||
['points'] = points, |
|||
-- @tparam table kwargs Keyword arguments to the module |
|||
['description'] = kwargs.description, |
|||
-- @treturn string A message corresponding to the rule |
|||
}) |
|||
function rule.custom(points, args, kwargs, frame) |
|||
return msg('rule-custom', points, kwargs.description) |
|||
end |
end |
||
--- Formatter function for image rules |
|||
function rule.image(points, args, kwargs) |
|||
-- @tparam number points A number of points; may by a float |
|||
-- @tparam table args Anonymous arguments to the module |
|||
-- @tparam table kwargs Keyword arguments to the module |
|||
-- @treturn string A message corresponding to the rule |
|||
function rule.image(points, args, kwargs, frame) |
|||
local out |
local out |
||
local tplargs = { |
local tplargs = { |
||
Linje 403: | Linje 480: | ||
} |
} |
||
if kwargs.initialimagelimit ~= nil then |
if kwargs.initialimagelimit ~= nil then |
||
out = msg('rule-image-limited', points, kwargs.initialimagelimit) |
|||
out = messages['image_rule_limited'] |
|||
tplargs['initialimagelimit'] = kwargs.initialimagelimit |
|||
else |
else |
||
out = |
out = msg('rule-image', points) |
||
end |
end |
||
if kwargs.ownimage ~= nil then |
if kwargs.ownimage ~= nil then |
||
out = out .. ' ' .. |
out = out .. ' ' .. msg('rule-image-own', kwargs.ownimage) |
||
tplargs['ownimage'] = kwargs.ownimage |
|||
end |
end |
||
return |
return out |
||
end |
end |
||
--- Formatter function for Wikidata rules |
|||
function rule.wikidata(points, args, kwargs) |
|||
-- @tparam number points A number of points; may by a float |
|||
-- @tparam table args Anonymous arguments to the module |
|||
-- @tparam table kwargs Keyword arguments to the module |
|||
-- @treturn string A message corresponding to the rule |
|||
function rule.wikidata(points, args, kwargs, frame) |
|||
local out |
local out |
||
local params |
local params |
||
local |
local argTypes = { msg('properties'), msg('labels'), msg('aliases'), msg('descriptions') } |
||
local results = {} |
local results = {} |
||
if kwargs.properties == nil and kwargs.labels == nil and kwargs.aliases == nil and kwargs.descriptions == nil then |
if kwargs.properties == nil and kwargs.labels == nil and kwargs.aliases == nil and kwargs.descriptions == nil then |
||
return |
return getErrorString('argument-missing', listify(argTypes)) |
||
'argument_missing', |
|||
mw.text.listToText( arg_types, ', ', ' ' .. messages['or'] .. ' ' ) |
|||
) |
|||
end |
end |
||
if kwargs.properties ~= nil then |
if kwargs.properties ~= nil then |
||
Linje 431: | Linje 508: | ||
params[k] = string.format('[[:d:Property:%s|%s]]', v, v) |
params[k] = string.format('[[:d:Property:%s|%s]]', v, v) |
||
end |
end |
||
table.insert(results, |
table.insert(results, listify(params)) |
||
end |
end |
||
if kwargs.labels ~= nil then |
if kwargs.labels ~= nil then |
||
params = mw.text.split(kwargs.labels, ',') |
params = mw.text.split(kwargs.labels, ',') |
||
table.insert(results, |
table.insert(results, msg('label') .. ' (' .. listify(params) .. ')') |
||
end |
end |
||
if kwargs.aliases ~= nil then |
if kwargs.aliases ~= nil then |
||
params = mw.text.split(kwargs.aliases, ',') |
params = mw.text.split(kwargs.aliases, ',') |
||
table.insert(results, |
table.insert(results, msg('alias') .. ' (' .. listify(params) .. ')') |
||
end |
end |
||
if kwargs.descriptions ~= nil then |
if kwargs.descriptions ~= nil then |
||
params = mw.text.split(kwargs.descriptions, ',') |
params = mw.text.split(kwargs.descriptions, ',') |
||
table.insert(results, |
table.insert(results, msg('description') .. ' (' .. listify(params) .. ')') |
||
end |
end |
||
results = table.concat( results, ' ' .. |
results = table.concat( results, ' ' .. msg('and') .. ' ' ) |
||
if kwargs.all ~= nil then |
if kwargs.all ~= nil then |
||
out = |
out = msg('rule-wikidata-all', points, results) |
||
else |
else |
||
out = |
out = msg('rule-wikidata-first', points, results) |
||
end |
end |
||
if kwargs. |
if kwargs.requireReference ~= nil then |
||
out = out .. ' ' .. |
out = out .. ' ' .. msg('rule-wikidata-require-reference') |
||
end |
end |
||
return |
return out |
||
['points'] = points, |
|||
['thing'] = results, |
|||
}) |
|||
end |
end |
||
--- Formatter function for reference rules |
|||
function rule.reference(points, args, kwargs) |
|||
-- @tparam number points A number of points; may by a float |
|||
return sprintf(messages['reference_rule'], { |
|||
-- @tparam table args Anonymous arguments to the module |
|||
['points'] = points, |
|||
-- @tparam table kwargs Keyword arguments to the module |
|||
['refpoints'] = args[1], |
|||
-- @treturn string A message corresponding to the rule |
|||
}) |
|||
function rule.reference(points, args, kwargs, frame) |
|||
return msg('rule-reference', points, args[1]) |
|||
end |
end |
||
--- Formatter function for template removal rules |
|||
function rule.templateremoval(points, args, kwargs) |
|||
-- @tparam number points A number of points; may by a float |
|||
local templates = format_plural(make_template_list(args), 'templates') |
|||
-- @tparam table args Anonymous arguments to the module |
|||
return sprintf(messages['templateremoval_rule'], { |
|||
-- @tparam table kwargs Keyword arguments to the module |
|||
['points'] = points, |
|||
-- @treturn string A message corresponding to the rule |
|||
['templates'] = templates, |
|||
function rule.templateremoval(points, args, kwargs, frame) |
|||
}) |
|||
local templateList = makeTemplateList(frame, args) |
|||
return msg('rule-templateremoval', points, #templateList, tostring(templateList)) |
|||
end |
end |
||
--- Formatter function for category removal rules |
|||
function rule.categoryremoval(points, args, kwargs) |
|||
-- @tparam number points A number of points; may by a float |
|||
local categories = format_plural(make_category_list(args), 'categories') |
|||
-- @tparam table args Anonymous arguments to the module |
|||
return sprintf(messages['categoryremoval_rule'], { |
|||
-- @tparam table kwargs Keyword arguments to the module |
|||
['points'] = points, |
|||
-- @treturn string A message corresponding to the rule |
|||
['categories'] = categories, |
|||
function rule.categoryremoval(points, args, kwargs, frame) |
|||
}) |
|||
local categoryList = makeCategoryList(args) |
|||
return msg('rule-categoryremoval', points, #categoryList, tostring(categoryList)) |
|||
end |
end |
||
function |
--- Formatter function for section adding rules |
||
-- @tparam number points A number of points; may by a float |
|||
-- @tparam table args Anonymous arguments to the module |
|||
-- @tparam table kwargs Keyword arguments to the module |
|||
-- @treturn string A message corresponding to the rule |
|||
function rule.section(points, args, kwargs, frame) |
|||
if kwargs.description ~= nil then |
if kwargs.description ~= nil then |
||
return msg('rule-section-desc', points, kwargs.description) |
|||
return sprintf(messages['section_rule_desc'], { |
|||
['points'] = points, |
|||
['description'] = kwargs.description, |
|||
}) |
|||
end |
end |
||
return msg('rule-section', points, #args, listify(args)) |
|||
local sections = format_plural(args, 'sections') |
|||
return sprintf(messages['section_rule'], { |
|||
['points'] = points, |
|||
['sections'] = sections, |
|||
}) |
|||
end |
end |
||
--- Formatter function for byte bonus rules |
|||
function rule.bytebonus(points, args, kwargs) |
|||
-- @tparam number points A number of points; may by a float |
|||
return sprintf(messages['bytebonus_rule'], { |
|||
-- @tparam table args Anonymous arguments to the module |
|||
['points'] = points, |
|||
-- @tparam table kwargs Keyword arguments to the module |
|||
['bytes'] = args[1], |
|||
-- @treturn string A message corresponding to the rule |
|||
}) |
|||
function rule.bytebonus(points, args, kwargs, frame) |
|||
return msg('rule-bytebonus', points, args[1]) |
|||
end |
end |
||
--- Formatter function for word bonus rules |
|||
function rule.wordbonus(points, args, kwargs) |
|||
-- @tparam number points A number of points; may by a float |
|||
return sprintf(messages['wordbonus_rule'], { |
|||
-- @tparam table args Anonymous arguments to the module |
|||
['points'] = points, |
|||
-- @tparam table kwargs Keyword arguments to the module |
|||
['words'] = args[1], |
|||
-- @treturn string A message corresponding to the rule |
|||
}) |
|||
function rule.wordbonus(points, args, kwargs, frame) |
|||
return msg('rule-wordbonus', points, args[1]) |
|||
end |
end |
||
--- Main function for getting criterion messages |
|||
function rule.format(frame) |
|||
-- @tparam table frame A frame object |
|||
-- Make tables of anonymous and named arguments |
|||
-- @treturn string A string representing the rule (or an error message string) |
|||
local args, kwargs = parse_args(frame) |
|||
function p.rule(frame) |
|||
rule_arg, args = shift_args(args) |
|||
local args, kwargs = parseArgs(frame, 'rules', true) |
|||
points, args = shift_args(args) |
|||
local ruleArg = table.remove(args, 1) |
|||
local points = table.remove(args, 1) |
|||
local permittedRules = getArgumentMapping('rules', 'canonical') |
|||
if ruleArg == nil or ruleArg == '' then |
|||
-- Try to find the corresponding formatter or bail out if not found |
|||
return frame:preprocess(getErrorString('argument-missing', 'rule')) |
|||
if rule_arg == nil then |
|||
elseif permittedRules[ruleArg] == nil then |
|||
return frame:preprocess(make_error('argument_missing', 'rule')) |
|||
return frame:preprocess(getErrorString('invalid-rule', ruleArg)) |
|||
end |
|||
local formatter = config.rules[rule_arg] |
|||
if formatter == nil then |
|||
return frame:preprocess(make_error('invalid_rule', rule_arg)) |
|||
end |
end |
||
if kwargs.description ~= nil then |
if kwargs.description ~= nil then |
||
ruleArg = 'custom' |
|||
end |
end |
||
-- All rules requires argument 1: number of points awarded |
-- All rules requires argument 1: number of points awarded |
||
if points == nil then |
if points == nil then |
||
return frame:preprocess( |
return frame:preprocess(getErrorString('argument-missing', '1 (number of points)')) |
||
end |
end |
||
points = mw.language.getContentLanguage():formatNum(tonumber(points)) |
|||
points = points:gsub( '%.', config['decimal_separator']) |
|||
-- If there's a rule formatter function, use it. |
-- If there's a rule formatter function, use it. |
||
-- Otherwise, use the string from the messages table. |
-- Otherwise, use the string from the messages table. |
||
local out |
local out |
||
if rule[ |
if rule[ruleArg] ~= nil then |
||
out = rule[ |
out = rule[ruleArg](points, args, kwargs, frame) |
||
else |
else |
||
-- It shouldn't be necessary to check if the message exists here, because |
|||
out = sprintf(messages[formatter .. '_rule'], { |
|||
-- of the previous check against permittedRules above |
|||
['points'] = points, |
|||
out = msg('rule-' .. ruleArg, points) |
|||
}) |
|||
end |
end |
||
if kwargs.site ~= nil then |
if kwargs.site ~= nil then |
||
out = msg('rule-site', out, mw.ustring.format('[https://%s %s]', kwargs.site, kwargs.site)) |
|||
out = sprintf(messages['rule_site'], { |
|||
['baserule'] = out, |
|||
['site'] = string.format('[https://%s %s]', kwargs.site, kwargs.site), |
|||
}) |
|||
end |
end |
||
if kwargs.max ~= nil then |
if kwargs.max ~= nil then |
||
out = msg('base-rule-max', out, mw.language.getContentLanguage():formatNum(tonumber(kwargs.max))) |
|||
out = sprintf(messages['base_rule_max'], { |
|||
['baserule'] = out, |
|||
['maxpoints'] = kwargs.max:gsub( '%.', config['decimal_separator']), |
|||
}) |
|||
end |
end |
||
Linje 564: | Linje 641: | ||
end |
end |
||
--- Function to generate documentation for a module or template using this module |
|||
-- Export |
|||
-- Not implemented yet |
|||
return { |
|||
function p.generateDocs(frame) |
|||
['criterion'] = criterion.format, |
|||
-- Generate documentation subpage for templates using the module |
|||
['rule'] = rule.format, |
|||
end |
|||
} |
|||
--- Function to get warnings about duplicate or invalid i18n values |
|||
-- Not implemented yet |
|||
function p.getI18nWarnings(frame) |
|||
-- Function to be used on /doc page, to report any duplicate arguments |
|||
-- from the i18n, and potentially other things that should be fixed in the |
|||
-- i18n for the current language. |
|||
end |
|||
--- Get a single message string from the module's i18n, localized into the page |
|||
--- if possible |
|||
-- @tparam table frame A frame object |
|||
-- @treturn string A formatted message (or an HTML error string if the key doesn't exist) |
|||
function p.getMessage(frame) |
|||
local args, kwargs = parseArgs(frame, nil, false) |
|||
local key = table.remove(args, 1) |
|||
local exists, message = pcall(msg, key, args) |
|||
if exists then |
|||
if mw.isSubsting() then |
|||
-- substitute magic words etc. if the module proper is being substed |
|||
message = mw.ustring.gsub( message, '{{(#?%a+):', '{{subst:%1:' ) |
|||
end |
|||
return frame:preprocess(message) |
|||
else |
|||
return getErrorString('message-key-missing', key) |
|||
end |
|||
end |
|||
--- Function to get i18n data for use by the bot |
|||
-- @treturn string A JSON-encoded string of all keys and (localized) values from the i18n dataset |
|||
function p.getAllI18n() |
|||
local lang = mw.title.getCurrentTitle().pageLang:getCode() |
|||
local sensible = {} |
|||
local i18n = mw.ext.data.get(I18NDATASET, lang)['data'] |
|||
for _,v in ipairs(i18n) do |
|||
-- turn the array of message objects into a sensible key->value mapping |
|||
sensible[v[1]] = v[2] |
|||
end |
|||
return mw.text.jsonEncode(sensible) |
|||
end |
|||
return p |
Sideversjonen fra 10. apr. 2025 kl. 23:02
Dokumentasjon for denne modulen kan opprettes på Modul:UKB/dok
require('strict') local p = {} local TNT = require('Module:TNT') local I18NDATASET = 'I18n/UKB.tab' local getArgs = require('Module:Arguments').getArgs --- Get a localized message. -- @param key The message key -- @param ... Parameters to be passed to the message ($1, $2, etc.) -- @return localized string local function msg( key, ... ) return TNT.format( I18NDATASET, key, ... ) end --- Reverse a mapping to get a list of localized names => canonical names -- @param mapping A table containing key-value pairs where the key is the canonical name and the value is an array table of aliases -- @return A table of localized names => canonical names local function mappingReverser(mapping) local ret = {} for canonical, synonyms in pairs(mapping) do for _, synonym in ipairs(synonyms) do ret[synonym] = canonical end local keyIsPresent, translations = pcall(msg, 'arg-' .. canonical) if keyIsPresent then translations = mw.text.split(translations, '|') for _, translation in ipairs(translations) do ret[translation] = canonical end end end return ret end --- Get the argument mapping for a type of item -- @param itemType The mapping subtype to get. Either 'criteria' or 'rules' -- @param returnType Which mapping to get; 'canonical' or 'translated' -- @return A table of mappings local function getArgumentMapping(itemType, returnType) -- if a new argument is added, it should also be added to the i18n module -- in [[c:Data:I18n/UKB.tab]] local argumentMapping = { ['criteria'] = { ['backlinks'] = { 'backlink' }, ['bytes'] = { 'byte' }, ['categories'] = { 'category' }, ['forwardlinks'] = { 'forwardlink' }, ['new'] = {}, ['existing'] = {}, ['namespaces'] = { 'namespace' }, ['pages'] = { 'page' }, ['sparql'] = {}, ['stub'] = {}, -- deprecated, not in i18n ['templates'] = { 'template' } }, ['rules'] = { ['bytes'] = { 'byte' }, ['bytebonus'] = {}, ['categoryremoval'] = {}, ['edit'] = {}, ['eligiblepage'] = {}, ['extlink'] = { 'exlink', 'externallink' }, ['image'] = { 'images' }, ['listbyte'] = { 'listbytes' }, ['newpage'] = {}, ['newredirect'] = {}, ['reference'] = { 'ref' }, ['section'] = {}, ['templateremoval'] = {}, ['wikidata'] = {}, ['word'] = { 'words' }, ['wordbonus'] = {} }, ['modifiers'] = { ['aliases'] = {}, ['all'] = {}, ['description'] = {}, ['descriptions'] = {}, ['distinct'] = {}, ['ignore'] = {}, ['initialimagelimit'] = {}, ['labels'] = {}, ['max'] = {}, ['ownimage'] = {}, ['properties'] = {}, ['query'] = {}, ['requirereference'] = { 'require reference', 'require_reference' }, ['redirects'] = { 'redirect' }, ['site'] = {}, } } if returnType == 'canonical' then return argumentMapping[itemType] end local translatedMap = { ['criteria'] = mappingReverser(argumentMapping.criteria), ['rules'] = mappingReverser(argumentMapping.rules), ['modifiers'] = mappingReverser(argumentMapping.modifiers) } return translatedMap[itemType] end --[ Helper methods ] ------------------------------------------------------------------ --- Make an error string -- @tparam string text Text to be wrapped in an error class -- @treturn string The text wrapped in an error class local function makeErrorString(text) local html = mw.html.create('strong') :addClass('error') :wikitext(text) return tostring(html) end --- Get an error string -- @tparam string key A message key (from i18n) -- @tparam string arg An argument to pass along to the message function -- @treturn string An error message local function getErrorString(key, arg) return makeErrorString(msg(key, arg)) end --- Parse and translate anonymous and named arguments -- @tparam table frame A frame object -- @tparam string|nil itemType An item type to return ('criteria', 'rules' or nil) -- @treturn table A table of anonymous arguments (args) -- @treturn table A table of named arguments (kwargs) local function parseArgs(frame, itemType, translate) local args = {} local kwargs = {} local canonicalMap = getArgumentMapping(itemType, 'translated') if itemType == nil then canonicalMap = {} end local kwargsMap = getArgumentMapping('modifiers', 'translated') for k, v in pairs(getArgs(frame)) do v = mw.text.trim(frame:preprocess(v)) if v ~= '' then if type(k) == 'number' then if k == 1 and canonicalMap[v] ~= nil and translate then args[1] = canonicalMap[v] else args[k] = v end else if kwargsMap[k] ~= nil and translate then kwargs[kwargsMap[k]] = v else kwargs[k] = v end end end end return args, kwargs end --- Turn an array table into a string in list form -- @tparam table items An array of items -- @tparam string itemType Maybe unnecessary? -- @tparam string word The strings 'or' or 'and' (representing i18n message keys) -- @treturn string A string with the table returned as a list local function listify(items, itemType, word) word = word or 'or' if #items == 0 then return getErrorString('anon-argument-missing', itemType) end if #items == 1 then return items[1] end return mw.text.listToText(items, ', ', ' ' .. msg(word) .. ' ' ) end --- Get link data for a link to a page in a specific namespace -- @tparam table frame A frame object -- @tparam string ns A canonical (English) namespace name; 'Template' and 'Category' supported -- @tparam string page A page name -- @treturn table A table containing: language code, link target and page name local function makeNsLink(frame, ns, page) local linkTarget local nsNumbers = { ['Template'] = 10, ['Category'] = 14 } local lang, pageName = mw.ustring.match(page, '^([a-z]+):(.+)$') -- FIXME: Better language code detection if lang then -- English namespace name is guaranteed to work, avoids need to maintain -- lists of namespace names in the module linkTarget = mw.ustring.format(':%s:%s:%s', lang, ns, pageName) else linkTarget = mw.ustring.format(':%s:%s', frame:callParserFunction('ns', nsNumbers[ns]), page) end return { ['lang'] = lang, ['linkTarget'] = linkTarget, ['pageName'] = pageName or page } end --- Make a link to a single template, wrapped in curly brace syntax -- @tparam table frame A frame object -- @tparam template Name of a template (optionally with an interlanguage prefix) -- @treturn string An HTML string linking to the template in question local function makeTemplateLink(frame, template) local nsLink = makeNsLink(frame, 'Template', template) local wikitext = mw.text.nowiki('{{') .. mw.ustring.format('[[%s|%s]]', nsLink['linkTarget'], nsLink['pageName']) .. mw.text.nowiki('}}') local html = mw.html.create('span') :addClass('template-link') :css('font-family', 'monospace,monospace') :wikitext(wikitext) return tostring(html) end --- Make a link to a single category -- @tparam table frame A frame object -- @tparam category Name of a category (optionally with an interlanguage prefix) -- @treturn string An HTML string linking to the category in question local function makeCategoryLink(frame, category) local nsLink = makeNsLink(frame, 'Category', category) return mw.ustring.format('[[%s|%s]]', nsLink['linkTarget'], nsLink['pageName']) end --- Make a list of templates -- @tparam table frame A frame object -- @tparam table args An array of template names (optionally with interlanguage prefixes) -- @treturn table A table of template links local function makeTemplateList(frame, args) local templates = {} for i, v in ipairs(args) do table.insert(templates, makeTemplateLink(frame, v)) end setmetatable(templates, { __tostring = function(self) return listify(templates, 'templates') end }) return templates end --- Make a list of categories -- @tparam table frame A frame object -- @tparam table args An array of category names (optionally with interlanguage prefixes) -- @treturn table A table of category links local function makeCategoryList(frame, args) local categories = {} for i, v in ipairs(args) do v = mw.text.trim(v) if v ~= '' then table.insert(categories, makeCategoryLink(frame, v)) end end setmetatable(categories, { __tostring = function(self) return listify(categories, 'categories') end }) return categories end --- Make a list of templates -- @tparam table args An array of page names (optionally with interlanguage prefixes) -- @treturn table A table of page links local function makePageList(args) local pages = {} 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(pages, string.format('[[:%s:%s|%s]]', lang, page, page)) else table.insert(pages, string.format('[[:%s]]', v)) end end end setmetatable(pages, { __tostring = function(self) return listify(pages, 'pages') end }) return pages end --- Make a list of namespaces -- @tparam table args An array of namespace IDs -- @treturn table A table of namespace names local function makeNsList(args) local namespaces = {} local namespaceName = msg('article') for _, namespaceId in ipairs(args) do namespaceId = mw.text.trim(namespaceId) if namespaceId ~= '' then if namespaceId ~= "0" then namespaceName = '{{lc:{{ns:' .. namespaceId .. '}}}}' end table.insert(namespaces, namespaceName) end end setmetatable(namespaces, { __tostring = function(self) return listify(namespaces, 'namespaces') end }) return namespaces end --[ Criterion format methods ]------------------------------------------------------------- local criterion = {} --- Formatter function for the backlinks criterion -- @tparam table args Anonymous arguments to the module -- @tparam table kwargs Keyword arguments to the module -- @treturn string A message corresponding to the criterion function criterion.backlinks(args, kwargs, frame) local pageList = makePageList(args) return msg('criterion-backlinks', #pageList, tostring(pageList)) end --- Formatter function for the bytes criterion -- @tparam table args Anonymous arguments to the module -- @tparam table kwargs Keyword arguments to the module -- @treturn string A message corresponding to the criterion function criterion.bytes(args, kwargs, frame) return msg('criterion-bytes', args[1]) end --- Formatter function for the categories criterion -- @tparam table args Anonymous arguments to the module -- @tparam table kwargs Keyword arguments to the module -- @treturn string A message corresponding to the criterion function criterion.categories(args, kwargs, frame) local categoryList = makeCategoryList(frame, args) local ret = msg('criterion-categories', #categoryList, tostring(categoryList)) if kwargs.ignore ~= nil then local ignoredCats = mw.text.split(kwargs.ignore, ',') ignoredCats = makeCategoryList(frame, ignoredCats) ret = ret .. msg('categories-except', #ignoredCats, tostring(ignoredCats)) end return ret end --- Formatter function for the existing criterion -- @tparam table args Anonymous arguments to the module -- @tparam table kwargs Keyword arguments to the module -- @treturn string A message corresponding to the criterion function criterion.existing(args, kwargs, frame) return msg('criterion-existing') end --- Formatter function for the forwardlinks criterion -- @tparam table args Anonymous arguments to the module -- @tparam table kwargs Keyword arguments to the module -- @treturn string A message corresponding to the criterion function criterion.forwardlinks(args, kwargs, frame) local pages = makePageList(args) return msg('criterion-forwardlinks', #pages, tostring(pages)) end --- Formatter function for the namespaces criterion -- @tparam table args Anonymous arguments to the module -- @tparam table kwargs Keyword arguments to the module -- @treturn string A message corresponding to the criterion function criterion.namespaces(args, kwargs, frame) local nsList = makeNsList(args) local message if #nsList == 1 and args[1] == '0' then message = msg('criterion-namespace-0') else message = msg('criterion-namespace', #nsList, tostring(nsList)) end if kwargs.site ~= nil then return msg('page-at-site', message, mw.ustring.format('[https://%s %s]', kwargs.site, kwargs.site)) end return message end --- Formatter function for the new page criterion -- @tparam table args Anonymous arguments to the module -- @tparam table kwargs Keyword arguments to the module -- @treturn string A message corresponding to the criterion function criterion.new(args, kwargs, frame) if kwargs.redirects ~= nil then return msg('criterion-new-with-redirects') end return msg('criterion-new') end --- Formatter function for the pages (page list) criterion -- @tparam table args Anonymous arguments to the module -- @tparam table kwargs Keyword arguments to the module -- @treturn string A message corresponding to the criterion function criterion.pages(args, kwargs, frame) local pages = makePageList(args) return msg('criterion-pages', #pages, tostring(pages)) end --- Formatter function for the SPARQL criterion -- @tparam table args Anonymous arguments to the module -- @tparam table kwargs Keyword arguments to the module -- @treturn string A message corresponding to the criterion function criterion.sparql(args, kwargs, frame) local query = '' if kwargs.distinct ~= nil then query = 'SELECT DISTINCT ?item WHERE {\n ' .. kwargs.query .. '\n}' else query = 'SELECT ?item WHERE {\n ' .. kwargs.query .. '\n}' end local url = 'http://query.wikidata.org/#' .. mw.uri.encode(query, 'PATH') if kwargs.description ~= nil then return msg('criterion-sparql-with-explanation', kwargs.description, url) end return msg('criterion-sparql', url) end --- Formatter function for the templates criterion -- @tparam table args Anonymous arguments to the module -- @tparam table kwargs Keyword arguments to the module -- @treturn string A message corresponding to the criterion function criterion.templates(args, kwargs, frame) local templates = makeTemplateList(frame, args) return msg('criterion-templates', #templates, tostring(templates)) end --- Main function for getting criterion messages -- @tparam table frame A frame object -- @treturn string A string representing the criterion (or an error message string) function p.criterion(frame) local args, kwargs = parseArgs(frame, 'criteria', true) local criterionArg = table.remove(args, 1) local permittedCriteria = getArgumentMapping('criteria', 'canonical') if criterionArg == nil or criterionArg == '' then return frame:preprocess(getErrorString('argument-missing', 'criterion')) elseif permittedCriteria[criterionArg] == nil or criterion[criterionArg] == nil then return frame:preprocess(getErrorString('invalid-criterion', criterionArg)) end -- Use manual description if given if kwargs.description ~= nil and criterionArg ~= 'sparql' then return kwargs.description end return frame:preprocess(criterion[criterionArg](args, kwargs, frame)) end --[ Rule format methods ]------------------------------------------------------------- local rule = {} --- Formatter function for custom rules -- @tparam number points A number of points; may by a float -- @tparam table args Anonymous arguments to the module -- @tparam table kwargs Keyword arguments to the module -- @treturn string A message corresponding to the rule function rule.custom(points, args, kwargs, frame) return msg('rule-custom', points, kwargs.description) end --- Formatter function for image rules -- @tparam number points A number of points; may by a float -- @tparam table args Anonymous arguments to the module -- @tparam table kwargs Keyword arguments to the module -- @treturn string A message corresponding to the rule function rule.image(points, args, kwargs, frame) local out local tplargs = { ['points'] = points, } if kwargs.initialimagelimit ~= nil then out = msg('rule-image-limited', points, kwargs.initialimagelimit) else out = msg('rule-image', points) end if kwargs.ownimage ~= nil then out = out .. ' ' .. msg('rule-image-own', kwargs.ownimage) end return out end --- Formatter function for Wikidata rules -- @tparam number points A number of points; may by a float -- @tparam table args Anonymous arguments to the module -- @tparam table kwargs Keyword arguments to the module -- @treturn string A message corresponding to the rule function rule.wikidata(points, args, kwargs, frame) local out local params local argTypes = { msg('properties'), msg('labels'), msg('aliases'), msg('descriptions') } local results = {} if kwargs.properties == nil and kwargs.labels == nil and kwargs.aliases == nil and kwargs.descriptions == nil then return getErrorString('argument-missing', listify(argTypes)) 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, listify(params)) end if kwargs.labels ~= nil then params = mw.text.split(kwargs.labels, ',') table.insert(results, msg('label') .. ' (' .. listify(params) .. ')') end if kwargs.aliases ~= nil then params = mw.text.split(kwargs.aliases, ',') table.insert(results, msg('alias') .. ' (' .. listify(params) .. ')') end if kwargs.descriptions ~= nil then params = mw.text.split(kwargs.descriptions, ',') table.insert(results, msg('description') .. ' (' .. listify(params) .. ')') end results = table.concat( results, ' ' .. msg('and') .. ' ' ) if kwargs.all ~= nil then out = msg('rule-wikidata-all', points, results) else out = msg('rule-wikidata-first', points, results) end if kwargs.requireReference ~= nil then out = out .. ' ' .. msg('rule-wikidata-require-reference') end return out end --- Formatter function for reference rules -- @tparam number points A number of points; may by a float -- @tparam table args Anonymous arguments to the module -- @tparam table kwargs Keyword arguments to the module -- @treturn string A message corresponding to the rule function rule.reference(points, args, kwargs, frame) return msg('rule-reference', points, args[1]) end --- Formatter function for template removal rules -- @tparam number points A number of points; may by a float -- @tparam table args Anonymous arguments to the module -- @tparam table kwargs Keyword arguments to the module -- @treturn string A message corresponding to the rule function rule.templateremoval(points, args, kwargs, frame) local templateList = makeTemplateList(frame, args) return msg('rule-templateremoval', points, #templateList, tostring(templateList)) end --- Formatter function for category removal rules -- @tparam number points A number of points; may by a float -- @tparam table args Anonymous arguments to the module -- @tparam table kwargs Keyword arguments to the module -- @treturn string A message corresponding to the rule function rule.categoryremoval(points, args, kwargs, frame) local categoryList = makeCategoryList(args) return msg('rule-categoryremoval', points, #categoryList, tostring(categoryList)) end --- Formatter function for section adding rules -- @tparam number points A number of points; may by a float -- @tparam table args Anonymous arguments to the module -- @tparam table kwargs Keyword arguments to the module -- @treturn string A message corresponding to the rule function rule.section(points, args, kwargs, frame) if kwargs.description ~= nil then return msg('rule-section-desc', points, kwargs.description) end return msg('rule-section', points, #args, listify(args)) end --- Formatter function for byte bonus rules -- @tparam number points A number of points; may by a float -- @tparam table args Anonymous arguments to the module -- @tparam table kwargs Keyword arguments to the module -- @treturn string A message corresponding to the rule function rule.bytebonus(points, args, kwargs, frame) return msg('rule-bytebonus', points, args[1]) end --- Formatter function for word bonus rules -- @tparam number points A number of points; may by a float -- @tparam table args Anonymous arguments to the module -- @tparam table kwargs Keyword arguments to the module -- @treturn string A message corresponding to the rule function rule.wordbonus(points, args, kwargs, frame) return msg('rule-wordbonus', points, args[1]) end --- Main function for getting criterion messages -- @tparam table frame A frame object -- @treturn string A string representing the rule (or an error message string) function p.rule(frame) local args, kwargs = parseArgs(frame, 'rules', true) local ruleArg = table.remove(args, 1) local points = table.remove(args, 1) local permittedRules = getArgumentMapping('rules', 'canonical') if ruleArg == nil or ruleArg == '' then return frame:preprocess(getErrorString('argument-missing', 'rule')) elseif permittedRules[ruleArg] == nil then return frame:preprocess(getErrorString('invalid-rule', ruleArg)) end if kwargs.description ~= nil then ruleArg = 'custom' end -- All rules requires argument 1: number of points awarded if points == nil then return frame:preprocess(getErrorString('argument-missing', '1 (number of points)')) end points = mw.language.getContentLanguage():formatNum(tonumber(points)) -- If there's a rule formatter function, use it. -- Otherwise, use the string from the messages table. local out if rule[ruleArg] ~= nil then out = rule[ruleArg](points, args, kwargs, frame) else -- It shouldn't be necessary to check if the message exists here, because -- of the previous check against permittedRules above out = msg('rule-' .. ruleArg, points) end if kwargs.site ~= nil then out = msg('rule-site', out, mw.ustring.format('[https://%s %s]', kwargs.site, kwargs.site)) end if kwargs.max ~= nil then out = msg('base-rule-max', out, mw.language.getContentLanguage():formatNum(tonumber(kwargs.max))) end return frame:preprocess(out) end --- Function to generate documentation for a module or template using this module -- Not implemented yet function p.generateDocs(frame) -- Generate documentation subpage for templates using the module end --- Function to get warnings about duplicate or invalid i18n values -- Not implemented yet function p.getI18nWarnings(frame) -- Function to be used on /doc page, to report any duplicate arguments -- from the i18n, and potentially other things that should be fixed in the -- i18n for the current language. end --- Get a single message string from the module's i18n, localized into the page --- if possible -- @tparam table frame A frame object -- @treturn string A formatted message (or an HTML error string if the key doesn't exist) function p.getMessage(frame) local args, kwargs = parseArgs(frame, nil, false) local key = table.remove(args, 1) local exists, message = pcall(msg, key, args) if exists then if mw.isSubsting() then -- substitute magic words etc. if the module proper is being substed message = mw.ustring.gsub( message, '{{(#?%a+):', '{{subst:%1:' ) end return frame:preprocess(message) else return getErrorString('message-key-missing', key) end end --- Function to get i18n data for use by the bot -- @treturn string A JSON-encoded string of all keys and (localized) values from the i18n dataset function p.getAllI18n() local lang = mw.title.getCurrentTitle().pageLang:getCode() local sensible = {} local i18n = mw.ext.data.get(I18NDATASET, lang)['data'] for _,v in ipairs(i18n) do -- turn the array of message objects into a sensible key->value mapping sensible[v[1]] = v[2] end return mw.text.jsonEncode(sensible) end return p