wikidata

Documentation for this module may be created at Module:Wikidata/doc

-- version 20210527 from master @cawiki  local p = {}  -- Initialization of variables --------------------  local i18n = {-- internationalisation at [[Module:Wikidata/i18n]] ["errors"] = { ["property-not-found"] = "Property not found.", ["qualifier-not-found"] = "Qualifier not found.", },  ["datetime"] = { -- $1 is a placeholder for the actual number ["beforenow"] = "$1 BCE",-- how to format negative numbers for precisions 0 to 5 ["afternow"] = "$1 CE",-- how to format positive numbers for precisions 0 to 5 ["bc"] = "$1 BCE",-- how print negative years ["ad"] = "$1",-- how print 1st century AD dates  [0] = "$1 billion years",-- precision: billion years [1] = "$100 million years",-- precision: hundred million years [2] = "$10 million years",-- precision: ten million years [3] = "$1 million years",-- precision: million years [4] = "$100000 years",-- precision: hundred thousand years; thousand separators added afterwards [5] = "$10000 years",-- precision: ten thousand years; thousand separators added afterwards [6] = "$1 millennium",-- precision: millennium [7] = "$1 century",-- precision: century [8] = "$1s",-- precision: decade -- the following use the format of #time parser function [9] = "Y",-- precision: year,  [10] = "F Y",-- precision: month [11] = "F j, Y",-- precision: day  ["hms"] = {["hours"] = "h", ["minutes"] = "m", ["seconds"] = "s"},-- duration: xh xm xs },  ["years-old"] = {"", ""},-- year(s) old, as in {{PLURAL:$1|singular|plural}} -- two values for most languages, up to six values for some languages -- see documentation of PLURAL magic word in your language, examples: -- ["years-old"] = {"singular", "paucal", "plural"} in Russian and other Slavic languages -- ["years-old"] = {"zero", "one", "two", "few 3-10", "many 11-99", "other 100-102"} in Arabic  ["cite"] = {-- cite parameters ["title"] = "title", ["author"] = "author", ["date"] = "date", ["pages"] = "pages", ["language"] = "language", -- cite web parameters ["url"] = "url", ["website"] = "website", ["access-date"] = "access-date", ["archive-url"] = "archive-url", ["archive-date"] = "archive-date", ["publisher"] = "publisher", ["quote"] = "quote", -- cite journal parameters ["work"] = "work", ["issue"] = "issue", ["issn"] = "issn", ["doi"] = "doi" },  -- default local wiki settings ["addpencil"] = false, -- adds a pencil icon linked to Wikidata statement, planned to overwrite by Wikidata Bridge ["categorylabels"] = "", -- Category:Pages with Wikidata labels not translated (void for no local category) ["categoryprop"] = "", -- Category:Pages using Wikidata property $1 (void for no local category) ["categoryref"] = "", -- Category:Pages with references from Wikidata (void for no local category) ["addfallback"] = {} -- additional fallback language codes }  local cases = {} -- functions for local grammatical cases defined at [[Module:Wikidata/i18n]]  local required = ... -- variadic arguments from require function local wiki =  { langcode = mw.language.getContentLanguage().code, module_title = required or mw.getCurrentFrame():getTitle() }  local untranslated -- used in infobox modules: nil or true local _ -- variable for unused returned values, avoiding globals  -- Module local functions --------------------------------------------  -- Credit to http://stackoverflow.com/a/1283608/2644759, cc-by-sa 3.0 local function tableMerge(t1, t2) for k, v in pairs(t2) do if type(v) == "table" then if type(t1[k] or false) == "table" then tableMerge(t1[k] or {}, t2[k] or {}) else t1[k] = v end else t1[k] = v end end return t1 end  local function loadI18n(lang) local exist, res = pcall(require, wiki.module_title .. "/i18n") if exist and next(res) ~= nil then tableMerge(i18n, res.i18n) cases = res.cases end if lang ~= wiki.langcode then exist, res = pcall(require, wiki.module_title .. "/i18n/" .. lang) if exist and next(res) ~= nil then tableMerge(i18n, res.i18n) tableMerge(cases, res.cases) end end end  -- Table of language codes: requested or default and its fallbacks local function findLang(langcode) if mw.language.isKnownLanguageTag(langcode or '') == false then local cframe = mw.getCurrentFrame() local pframe = cframe:getParent() langcode = pframe and pframe.args.lang if mw.language.isKnownLanguageTag(langcode or '') == false then if not mw.title.getCurrentTitle().isContentPage then langcode = cframe:preprocess('{{int:lang}}') end if mw.language.isKnownLanguageTag(langcode or '') == false then langcode = wiki.langcode end end end  loadI18n(langcode)  local languages = mw.language.getFallbacksFor(langcode) table.insert(languages, 1, langcode) if langcode == wiki.langcode then for _, l in ipairs(i18n.addfallback) do table.insert(languages, l) end end  return languages end  -- Argument is 'set' when it exists (not nil) or when it is not an empty string. local function isSet(var) return not (var == nil or (type(var) == 'string' and mw.text.trim(var) == '')) end  -- Set local case to a label local function case(localcase, label, ...) if not isSet(label) then return label end  if type(localcase) == "function" then return localcase(label) elseif localcase == "smallcaps" then return '' .. label .. '' elseif cases[localcase] then return cases[localcase](label, ...) end  return label end  -- get safely a serialized snak local function getSnak(statement, snaks) local ret = statement for i, v in ipairs(snaks) do if not ret then return end ret = ret[v] end return ret end  -- mw.wikibase.getLabelWithLang or getLabelByLang with a table of languages local function getLabelByLangs(id, languages) local label local lang for _, l in ipairs(languages) do if l == wiki.langcode then -- using getLabelWithLang when possible instead of getLabelByLang label, l = mw.wikibase.getLabelWithLang(id) else label = mw.wikibase.getLabelByLang(id, l) end if label then lang = l break end end return label, lang end  -- getBestStatements if bestrank=true, else getAllStatements with no deprecated local function getStatements(entityId, property, bestrank) local claims = {} if not (entityId and mw.ustring.match(property, "^P%d+$")) then return claims end if bestrank then claims = mw.wikibase.getBestStatements(entityId, property) else local allclaims = mw.wikibase.getAllStatements(entityId, property) for _, c in ipairs(allclaims) do if c.rank ~= "deprecated" then table.insert(claims, c) end end end return claims end  -- Is gender femenine? true or false local function feminineGender(id) local claims = mw.wikibase.getBestStatements(id or mw.wikibase.getEntityIdForCurrentPage(),'P21') local gender_id = getSnak(claims, {1, "mainsnak", "datavalue", "value", "id"}) if gender_id == "Q6581072" or gender_id == "Q1052281" or gender_id == "Q43445" then -- female, transgender female, female organism return true end return false end  -- Fetch female form of label local function feminineForm(id, lang) local feminine_claims = getStatements(id, 'P2521') for _, feminine_claim in ipairs(feminine_claims) do if getSnak(feminine_claim, {'mainsnak', 'datavalue', 'value', 'language'}) == lang then return feminine_claim.mainsnak.datavalue.value.text end end end  -- Add an icon for no label in requested language local function addLabelIcon(label_id, lang, uselang, icon) local ret_lang, ret_icon = '', '' if icon then if lang and lang ~= uselang then ret_lang = " (" .. lang .. ")" end if label_id and (lang == nil or lang ~= uselang) then local namespace = '' if string.sub(label_id, 1, 1) == 'P' then namespace = 'Property:' end ret_icon = " [[File:Noun Project label icon 1116097 cc mirror.svg|10px|baseline|" .. mw.message.new('Translate-taction-translate'):inLanguage(uselang):plain() .. "|link=https://www.wikidata.org/wiki/" .. namespace .. label_id .. "?uselang=" .. uselang .. "]]" untranslated = true end if isSet(i18n.categorylabels) and lang ~= uselang and uselang == wiki.langcode then ret_icon = ret_icon .. '[[' .. i18n.categorylabels .. (lang and ']]' or '/Q]]') end end return ret_lang .. ret_icon end  -- editicon values: true/false (no=false), right, void defaults to i18n.addpencil -- labelicon only by parameter local function setIcons(arg, parg) local val = arg == nil and parg or arg local edit_icon, label_icon if not isSet(val) then edit_icon, label_icon = i18n.addpencil, true elseif val == false or val == "false" or val == "no" then edit_icon, label_icon = false, false else edit_icon, label_icon = val, true end return edit_icon, label_icon end  -- Add an icon for editing a statement with requirements for Wikidata Bridge local function addEditIcon(parameters) local ret = '' if parameters.editicon and parameters.id and parameters.property then local icon_style = parameters.editicon == "right" and ' style="float: right;"' or '' ret = '  .. icon_style .. '>' .. "[[File:Arbcom ru editing.svg|10px|baseline|" .. string.gsub(mw.message.new('Wikibase-client-data-bridge-bailout-suggestion-go-to-repo-button'):inLanguage(parameters.lang[1]):plain(), '{{WBREPONAME}}', 'Wikidata') .. "|link=https://www.wikidata.org/wiki/" .. parameters.id .. "?uselang=" .. parameters.lang[1] .. "#" .. parameters.property .. "]]" .. "" if isSet(i18n.categoryprop) then ret = ret .. "[[" .. string.gsub(i18n.categoryprop, '$1', parameters.property) .. "]]" end end return ret end -- add edit icon to the last element of a table local function addEditIconTable(thetable, parameters) if #thetable == 0 or parameters.editicon == false then return thetable end local last_element = thetable[#thetable] local the_icon = addEditIcon(parameters) -- add it before last html closing tags local tags = '' local rev_element = string.reverse(last_element) for tag in string.gmatch(rev_element, '(>%l+/<)') do if string.match(rev_element, '^' .. tags .. tag) then tags = tags .. tag else break end end local last_tags = string.reverse(tags) local offset = string.find(last_element, last_tags .. '$') if offset then thetable[#thetable] = string.sub(last_element, 1, offset - 1) .. the_icon .. last_tags else thetable[#thetable] = last_element .. the_icon end return thetable end  -- Escape Lua captures local function captureEscapes(text) return mw.ustring.gsub(text, "(%%%d)", "%%%1") end  -- expandTemplate or callParserFunction local function expandBraces(text, formatting) if text == nil or formatting == nil then return text end -- only expand braces if provided in argument, not included in value as in Q1164668 if mw.ustring.find(formatting, '{{', 1, true) == nil then return text end if type(text) ~= "string" then text = tostring(text) end  for braces in mw.ustring.gmatch(text, "{{(.-)}}") do local parts = mw.text.split(braces, "|") local title_part = parts[1] local parameters = {} for i = 2, #parts do local subparts = mw.ustring.find(parts[i], "=") if subparts then local param_name = mw.ustring.sub(parts[i], 1, subparts - 1) local param_value = mw.ustring.sub(parts[i], subparts + 1, -1) -- reconstruct broken links by parts if i < #parts and mw.ustring.find(param_value, "[[", 1, true) and not mw.ustring.find(param_value, "]]", 1, true) then parameters[param_name] = param_value local part_next = i + 1 while parts[part_next] and mw.ustring.find(parts[part_next], "]]", 1, true) do parameters[param_name] = parameters[param_name] .. "|" .. parts[part_next] part_next = part_next + 1 end else parameters[param_name] = param_value end elseif not mw.ustring.find(parts[i], "]]", 1, true) then table.insert(parameters, parts[i]) end end  local braces_expanded if mw.ustring.find(title_part, ":") and mw.text.split(title_part, ":")[1] ~= mw.site.namespaces[10].name -- not a prefix Template: then braces_expanded = mw.getCurrentFrame():callParserFunction{name=title_part, args=parameters} else braces_expanded = mw.getCurrentFrame():expandTemplate{title=title_part, args=parameters} end braces = mw.ustring.gsub(braces, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- escape magic characters braces_expanded = captureEscapes(braces_expanded) text = mw.ustring.gsub(text, "{{" .. braces .. "}}", braces_expanded) end  return text end  -- format data type math local function printDatatypeMath(data) return mw.getCurrentFrame():callParserFunction('#tag:math', data) end  -- format data type musical-notation local function printDatatypeMusical(data, formatting) local attr = {} if formatting == 'sound' then attr.sound = 1 end return mw.getCurrentFrame():extensionTag('score', data, attr) end  -- format data type string local function printDatatypeString(data, parameters) if mw.ustring.find((parameters.formatting or ''), '$1', 1, true) then -- formatting = a pattern return expandBraces(mw.ustring.gsub(parameters.formatting, '$1', {['$1'] = data}), parameters.formatting) elseif parameters.case then return case(parameters.case, data, parameters.lang[1], feminineGender(parameters.id)) end local data_number = string.match(data, "^%d+") if data_number then -- sort key by initial number and remaining string local sortkey = string.format("%019d", data_number * 1000) return data, sortkey .. string.sub(data, #data_number + 1) end return data end  -- format data type url local function printDatatypeUrl(data, parameters) if parameters.formatting == 'weblink' then local label_parts = mw.text.split(string.gsub(data, '/$', ''), '/') local label = string.gsub(label_parts[3], '^www%.', '') if #label_parts > 3 then label = label .. '…' end return '[' .. data .. ' ' .. label .. ']' end return printDatatypeString(data, parameters) end  -- format data type external-id local function printDatatypeExternal(data, parameters) if parameters.formatting == 'externalid' then local p_stat = mw.wikibase.getBestStatements(parameters.property, 'P1630') -- formatter URL local p_link_pattern = getSnak(p_stat, {1, "mainsnak", "datavalue", "value"}) if p_link_pattern then local p_link = mw.ustring.gsub(p_link_pattern, '$1', {['$1'] = data}) return '[' .. p_link .. ' ' .. data .. ']' end end return printDatatypeString(data, parameters) end  -- format data type commonsMedia and geo-shape local function printDatatypeMedia(data, parameters) local icon if not string.find((parameters.formatting or ''), '$1', 1, true) then icon = "no-icon" if not string.find(data, '^Data:') then data = mw.uri.encode(data, 'PATH') -- encode special characters in filename end end return printDatatypeString(data, parameters), icon end  -- format data type globe-coordinate local function printDatatypeCoordinate(data, formatting) local function globes(globe_id) local globes = {['Q3134']='callisto',['Q596']='ceres',['Q15040']='dione',['Q2']='earth',['Q3303']='enceladus', ['Q3143']='europa',['Q17975']='phoebe',['Q3169']='ganymede',['Q3123']='io',['Q17958']='iapetus', ['Q308']='mercury',['Q15034']='mimas',['Q405']='moon',['Q15050']='rhea',['Q15047']='tethys', ['Q111']='mars',['Q2565']='titan',['Q3359']='triton',['Q313']='venus',['Q3030']='vesta'} return globes[globe_id] end  local function roundPrecision(num, prec) if prec == nil or prec <= 0 then return num end local sig = 10^math.floor(math.log10(prec)+.5) -- significant figure from sexagesimal precision: 0.00123 -> 0.001 return math.floor(num / sig + 0.5) * sig end  local precision = data.precision local latitude = roundPrecision(data.latitude, precision) local longitude = roundPrecision(data.longitude, precision) if formatting and string.find(formatting, '$lat', 1, true) and string.find(formatting, '$lon', 1, true) then local ret = mw.ustring.gsub(formatting, '$l[ao][tn]', {['$lat'] = latitude, ['$lon'] = longitude}) if string.find(formatting, '$globe', 1, true) then local myglobe = 'earth' if isSet(data.globe) then local globenum = mw.text.split(data.globe, 'entity/')[2] -- http://www.wikidata.org/wiki/Q2 myglobe = globes(globenum) or 'earth' end ret = mw.ustring.gsub(ret, '$globe', myglobe) end return expandBraces(ret, formatting) elseif formatting == 'latitude' then return latitude, "no-icon" elseif formatting == 'longitude' then return longitude, "no-icon" elseif formatting == 'dimension' then return data.dimension, "no-icon" else --default formatting='globe' if isSet(data.globe) == false or data.globe == 'http://www.wikidata.org/entity/Q2' then return 'earth', "no-icon" else local globenum = mw.text.split(data.globe, 'entity/')[2] return globes(globenum) or globenum, "no-icon" end end end  -- Local functions for data value quantity local function unitSymbol(id, lang) -- get unit symbol or code local unit_symbol = '' if lang == wiki.langcode and pcall(require, wiki.module_title .. "/Units") then unit_symbol = require(wiki.module_title .. "/Units").getUnit(0, '', id, true) end if unit_symbol == '' then -- fetch it local claims = mw.wikibase.getBestStatements(id, 'P5061') if #claims > 0 then local langclaims = {} table.insert(lang, 'mul') -- multilingual as last try for _, snak in ipairs(claims) do local snak_language = getSnak(snak, {"mainsnak", "datavalue", "value", "language"}) if snak_language and not langclaims[snak_language] then -- just the first one by language langclaims[snak_language] = snak.mainsnak.datavalue.value.text end end for _, l in ipairs(lang) do if langclaims[l] then return langclaims[l] end end end end return unit_symbol end  local function getUnit(amount, id, parameters) -- get unit symbol or name local suffix = '' if parameters.formatting == "unitcode" then -- get unit symbol local unit_symbol = unitSymbol(id, parameters.lang) if isSet(unit_symbol) then suffix = unit_symbol end end if suffix == '' then -- formatting=unit, or formatting=unitcode not found -- get unit name local unit_label, lang = getLabelByLangs(id, parameters.lang) if lang == wiki.langcode and pcall(require, wiki.module_title .. "/Units") then suffix = require(wiki.module_title .. "/Units").getUnit(amount, unit_label, id, false) else suffix = (unit_label or id) .. addLabelIcon(id, lang, parameters.lang[1], parameters.labelicon) end end if suffix ~= '' then suffix = ' ' .. suffix end return suffix end  local function roundDefPrecision(in_num, factor) -- rounds out_num with significant figures of in_num (default precision) local out_num = in_num * factor if factor/60 == math.floor(factor/60) or out_num == 0 then -- sexagesimal integer or avoiding NaN return out_num end -- first, count digits after decimal mark, handling cases like '12.345e6' local exponent, prec local integer, dot, decimals, expstr = in_num:match('^(%d*)(%.?)(%d*)(.*)') local e = expstr:sub(1, 1) if e == 'e' or e == 'E' then exponent = tonumber(expstr:sub(2)) end if dot == '' then prec = -integer:match('0*$'):len() else prec = #decimals end if exponent then -- So '1230' and '1.23e3' both give prec = -1, and '0.00123' and '1.23e-3' give 5. prec = prec - exponent end -- significant figures local in_bracket = 10^-prec -- -1 -> 10, 5 -> 0.00001 local out_bracket = in_bracket * out_num / in_num out_bracket = 10^math.floor(math.log10(out_bracket)+.5) -- 1230 -> 1000, 0.00123 -> 0.001 -- round it (credit to Luc Bloom from http://lua-users.org/wiki/SimpleRound) return math.floor(out_num/out_bracket + (out_num >=0 and 1 or -1) * 0.5) * out_bracket end  -- format data type quantity local function printDatatypeQuantity(data, parameters) local amount = data.amount amount = mw.ustring.gsub(amount, "%+", "") local suffix = "" local conv_amount, conv_suffix if string.sub(parameters.formatting or '', 1, 4) == "unit" or string.sub(parameters.formatting or '', 1, 8) == "duration" or parameters.convert then local unit_id = data.unit unit_id = mw.ustring.sub(unit_id, mw.ustring.find(unit_id, "Q"), -1) if string.sub(unit_id, 1, 1) == "Q" then suffix = getUnit(amount, unit_id, parameters) local convert_to if parameters.convert == "default" or parameters.convert == "default2" then local exist, units = pcall(require, wiki.module_title .. "/Units") if exist and units.convert_default and next(units.convert_default) ~= nil then convert_to = units.convert_default[unit_id] end elseif string.sub(parameters.convert or '', 1, 1) == "Q" then convert_to = parameters.convert elseif string.sub(parameters.formatting or '', 1, 8) == "duration" then convert_to = 'Q11574' -- seconds end if convert_to and convert_to ~= unit_id then -- convert units local conv_temp = { -- formulae for temperatures ºC, ºF, ªK: [from] = {[to] = 'formula'} ['Q25267'] = {['Q42289'] = '$1*1.8+32', ['Q11597'] = '$1+273.15'}, ['Q42289'] = {['Q25267'] = '($1-32)/1.8', ['Q11597'] = '($1+459.67)*5/9'}, ['Q11597'] = {['Q25267'] = '$1-273.15', ['Q42289'] = '($1-273.15)*1.8000+32.00'} } if conv_temp[unit_id] and conv_temp[unit_id][convert_to] then local amount_f = mw.getCurrentFrame():callParserFunction('#expr', mw.ustring.gsub(conv_temp[unit_id][convert_to], "$1", amount)) conv_amount = math.floor(tonumber(amount_f) + 0.5) else local conversions = getStatements(unit_id, 'P2442') -- conversion to standard unit table.insert(conversions, mw.wikibase.getBestStatements(unit_id, 'P2370')[1]) -- conversion to SI unit for _, conv in ipairs(conversions) do if conv.mainsnak.snaktype == 'value' then -- no somevalue nor novalue if conv.mainsnak.datavalue.value.unit == "http://www.wikidata.org/entity/" .. convert_to then conv_amount = roundDefPrecision(amount, tonumber(conv.mainsnak.datavalue.value.amount)) break end end end end if conv_amount then conv_suffix = getUnit(conv_amount, convert_to, parameters) end elseif parameters.convert == 'M' and tonumber(amount) > 10^8 then conv_amount = math.floor(amount/10^6 + 0.5) conv_suffix = ' M' .. string.sub(suffix, 2) end end end local lang_obj = mw.language.new(parameters.lang[1]) local sortkey = string.format("%019d", tonumber(amount) * 1000) if string.sub(parameters.formatting or '', 1, 8) == "duration" then local sec = tonumber(conv_amount or amount) if parameters.formatting == 'durationhms' or parameters.formatting == 'durationh:m:s' then local intervals = {"hours", "minutes", "seconds"} local sec2table = lang_obj:getDurationIntervals(sec, intervals) sec2table["seconds"] = (sec2table["seconds"] or 0) + tonumber("." .. (tostring(sec):match("%.(%d+)") or "0")) -- add decimals local duration = '' for i, v in ipairs(intervals) do if parameters.formatting == 'durationh:m:s' then if i == 1 and sec2table[v] then duration = duration .. sec2table[v] .. ":" elseif i == 2 then duration = duration .. string.format("%02d", sec2table[v] or 0) .. ":" elseif i == 3 then local sec_str = tostring(lang_obj:formatNum(sec2table[v] or 0)) duration = duration .. (sec2table[v] < 10 and "0" or "") .. sec_str end elseif sec2table[v] then duration = duration .. lang_obj:formatNum(sec2table[v]) .. i18n.datetime.hms[v] .. (i < 3 and " " or "") end end return duration else return lang_obj:formatDuration(sec) end end if parameters.case then amount = case(parameters.case, amount, parameters.lang[1]) elseif parameters.formatting ~= 'raw' then amount = lang_obj:formatNum(tonumber(amount)) end if conv_amount then local conv_sortkey = string.format("%019d", conv_amount * 1000) conv_amount = lang_obj:formatNum(conv_amount) if parameters.convert == 'default2' then return conv_amount .. conv_suffix .. ' (' .. amount .. suffix .. ')', conv_sortkey else return conv_amount .. conv_suffix, conv_sortkey end elseif mw.ustring.find((parameters.formatting or ''), '$1', 1, true) then -- formatting with pattern amount = mw.ustring.gsub(parameters.formatting, '$1', {['$1'] = amount}) end return amount .. suffix, sortkey end  -- format data type time local function printDatatypeTime(data, parameters) -- Dates and times are stored in ISO 8601 format local timestamp = data.time local post_format local calendar_add = "" local precision = data.precision or 11  if string.sub(timestamp, 1, 1) == '-' then post_format = i18n.datetime["bc"] elseif string.sub(timestamp, 2, 3) == '00' then post_format = i18n.datetime["ad"] elseif precision > 8 then -- calendar model local calendar_model = {["Q12138"] = "gregorian", ["Q1985727"] = "gregorian", ["Q11184"] = "julian", ["Q1985786"] = "julian"} local calendar_id = mw.text.split(data.calendarmodel, 'entity/')[2] if (timestamp < "+1582-10-15T00:00:00Z" and calendar_model[calendar_id] == "gregorian") or (timestamp > "+1582-10-04T00:00:00Z" and calendar_model[calendar_id] == "julian") then calendar_add = " (" .. mw.message.new('Wikibase-time-calendar-' .. calendar_model[calendar_id]):inLanguage(parameters.lang[1]):plain() .. ")" end end  local function formatTime(form, stamp) local pattern if type(form) == "function" then pattern = form(stamp) else pattern = form end stamp = tostring(stamp) if mw.ustring.find(pattern, "$1") then return mw.ustring.gsub(pattern, "$1", stamp) elseif string.sub(stamp, 1, 1) == '-' then -- formatDate() only supports years from 0 stamp = '+' .. string.sub(stamp, 2) elseif string.sub(stamp, 1, 1) ~= '+' then -- not a valid timestamp, it is a number stamp = string.format("%04d", stamp) end local ret = mw.language.new(parameters.lang[1]):formatDate(pattern, stamp) ret = string.gsub(ret, "^(%[?%[?)0+", "%1") -- supress leading zeros ret = string.gsub(ret, "( %[?%[?)0+", "%1") return ret end  local function postFormat(t) if post_format and mw.ustring.find(post_format, "$1") then return mw.ustring.gsub(post_format, "$1", t) end return t end  local intyear = tonumber(string.match(timestamp, "[+-](%d+)")) local ret = ""  if precision <= 5 then -- precision is 10000 years or more local factor = 10 ^ ((5 - precision) + 4) local y2 = math.ceil(math.abs(intyear) / factor) local relative = formatTime(i18n.datetime[precision], y2) if post_format == i18n.datetime["bc"] then ret = mw.ustring.gsub(i18n.datetime.beforenow, "$1", relative) else ret = mw.ustring.gsub(i18n.datetime.afternow, "$1", relative) end local ret_number = string.match(ret, "%d+") if ret_number ~= nil then ret = mw.ustring.gsub(ret, ret_number, mw.language.new(parameters.lang[1]):formatNum(tonumber(ret_number))) end elseif precision == 6 or precision == 7 then -- millennia or centuries local card = math.floor((intyear - 1) / 10^(9 - precision)) + 1 ret = formatTime(i18n.datetime[precision], card) ret = postFormat(ret) elseif precision == 8 then -- decades local card = math.floor(math.abs(intyear) / 10) * 10 ret = formatTime(i18n.datetime[8], card) ret = postFormat(ret) elseif intyear > 9999 then -- not a valid timestamp return elseif precision == 9 or parameters.formatting == 'Y' then -- precision is year ret = formatTime(i18n.datetime[9], intyear) ret = postFormat(ret) .. calendar_add elseif precision == 10 then -- month ret = formatTime(i18n.datetime[10], timestamp .. " + 1 day") -- formatDate yyyy-mm-00 returns the previous month ret = postFormat(ret) .. calendar_add else -- precision 11, day ret = formatTime(parameters.formatting or i18n.datetime[11], timestamp) ret = postFormat(ret) .. calendar_add end return ret, timestamp end  -- format data value wikibase-entityid: types wikibase-item, wikibase-property local function printDatatypeEntity(data, parameters) local entity_id = data['id'] if parameters.formatting == 'raw' then return entity_id, entity_id end local entity_page = 'Special:EntityPage/' .. entity_id local label, lang = getLabelByLangs(entity_id, parameters.lang) local sitelink = mw.wikibase.getSitelink(entity_id) local parameter = parameters.formatting local labelcase = label or sitelink if parameters.gender == 'feminineform' then labelcase = feminineForm(entity_id, lang) or labelcase end if parameters.case ~= 'gender' then labelcase = case(parameters.case, labelcase, lang, parameters.lang[1], entity_id, parameters.id) end local ret1, ret2 if parameter == 'label' then ret1 = labelcase or entity_id ret2 = labelcase or entity_id elseif parameter == 'sitelink' then ret1 = (sitelink or 'd:' .. entity_page) ret2 = sitelink or entity_id elseif mw.ustring.find((parameter or ''), '$1', 1, true) then -- formatting = a pattern ret1 = mw.ustring.gsub(parameter, '$1', labelcase or entity_id) ret1 = expandBraces(ret1, parameter) ret2 = labelcase or entity_id else if parameter == "ucfirst" or parameter == "ucinternallink" then if labelcase and lang then labelcase = mw.language.new(lang):ucfirst(labelcase) end -- only first of a list, reset formatting for next ones if parameter == "ucinterlanllink" then parameters.formatting = 'internallink' else parameters.formatting = nil -- default format end end  if sitelink then ret1 = '[[' .. sitelink .. '|' .. labelcase .. ']]' ret2 = labelcase elseif label and string.match(parameter or '', 'internallink$') and not mw.wikibase.getEntityIdForTitle(label) then ret1 = '[[' .. label .. '|' .. labelcase .. ']]' ret2 = labelcase else ret1 = '[[d:' .. entity_page .. '|' .. (labelcase or entity_id) .. ']]' ret2 = labelcase or entity_id end end  return ret1 .. addLabelIcon(entity_id, lang, parameters.lang[1], parameters.labelicon), ret2 end  -- format data type monolingualtext local function printDatatypeMonolingual(data, parameters) -- data fields: language [string], text [string]  if parameters.list == "lang" and data["language"] ~= parameters.lang[1] then return elseif parameters.list == "notlang" and data["language"] == parameters.lang[1] then return elseif parameters.formatting == "language" or parameters.formatting == "text" then return data[parameters.formatting] end local result = data["text"] if data["language"] ~= wiki.langcode then result = mw.ustring.gsub('$2', '$[12]', {["$1"]=data["language"], ["$2"]=data["text"]}) end if mw.ustring.find((parameters.formatting or ''), '$', 1, true) then -- output format defined with $text, $language result = mw.ustring.gsub(parameters.formatting, '$text', result) result = mw.ustring.gsub(result, '$language', data["language"]) end return result end  local function getSnakValue(snak, parameters) if snak.snaktype == 'value' then -- see Special:ListDatatypes if snak.datatype == "string" then return printDatatypeString(snak.datavalue.value, parameters) -- other data value string, tabular-data not implemented elseif snak.datatype == "commonsMedia" or snak.datatype == "geo-shape" then return printDatatypeMedia(snak.datavalue.value, parameters) elseif snak.datatype == "url" then return printDatatypeUrl(snak.datavalue.value, parameters) elseif snak.datatype == "external-id" then return printDatatypeExternal(snak.datavalue.value, parameters) elseif snak.datatype == 'math' then return printDatatypeMath(snak.datavalue.value) elseif snak.datatype == 'musical-notation' then return printDatatypeMusical(snak.datavalue.value, parameters.formatting) -- other data types elseif snak.datatype == 'wikibase-item' or snak.datatype == 'wikibase-property' then return printDatatypeEntity(snak.datavalue.value, parameters) elseif snak.datatype == 'monolingualtext' then return printDatatypeMonolingual(snak.datavalue.value, parameters) elseif snak.datatype == "globe-coordinate" then return printDatatypeCoordinate(snak.datavalue.value, parameters.formatting) elseif snak.datatype == "quantity" then return printDatatypeQuantity(snak.datavalue.value, parameters) elseif snak.datatype == "time" then return printDatatypeTime(snak.datavalue.value, parameters) end elseif snak.snaktype == 'novalue' then if parameters.formatting == 'raw' or parameters.shownovalue == false then return end return mw.message.new('Wikibase-snakview-snaktypeselector-novalue'):inLanguage(parameters.lang[1]):plain() elseif snak.snaktype == 'somevalue' then if parameters.formatting == 'raw' then return end return mw.message.new('Wikibase-snakview-snaktypeselector-somevalue'):inLanguage(parameters.lang[1]):plain() end return mw.wikibase.renderSnak(snak) end  local function printError(key) return ' .. i18n.errors[key] .. '' end  local function getQualifierSnak(claim, qualifierId, parameters) -- a "snak" is Wikidata terminology for a typed key/value pair -- a claim consists of a main snak holding the main information of this claim, -- as well as a list of attribute snaks and a list of references snaks if qualifierId then -- search the attribute snak with the given qualifier as key if claim.qualifiers then local qualifier = claim.qualifiers[qualifierId] if qualifier then if qualifier[1].datatype == "monolingualtext" then -- iterate over monolingualtext qualifiers to get local language for idx in pairs(qualifier) do if getSnak(qualifier[idx], {"datavalue", "value", "language"}) == parameters.lang[1] then return qualifier[idx] end end elseif parameters.list then return qualifier else return qualifier[1] end end end return nil, printError("qualifier-not-found") else -- otherwise return the main snak return claim.mainsnak end end  local function getValueOfClaim(claim, qualifierId, parameters) local snak, error = getQualifierSnak(claim, qualifierId, parameters) if not snak then return nil, nil, error elseif snak[1] then -- a multi qualifier local result, sortkey = {}, {} local maxvals = tonumber(parameters.list) for idx in pairs(snak) do result[#result + 1], sortkey[#sortkey + 1] = getSnakValue(snak[idx], parameters) if maxvals and maxvals == #result then break end end return mw.text.listToText(result, parameters.qseparator, parameters.qconjunction), sortkey[1] else -- a property or a qualifier return getSnakValue(snak, parameters) end end  local function getValueOfParentClaim(claim, qualifierId, parameters) local qids = mw.text.split(qualifierId, '/', true) local value, sortkey, valueraw = {}, {}, {} local parent_raw, value_text if qids[1] == parameters.property then parent_raw, _, _ = getValueOfClaim(claim, nil, {["formatting"]="raw", ["lang"]=parameters.lang}) else parent_raw, _, _ = getValueOfClaim(claim, qids[1], {["formatting"]="raw", ["lang"]=parameters.lang, ["list"]=true, ["qseparator"]='/', ["qconjunction"]='/'}) end if string.sub(parent_raw or '', 1, 1) == "Q" then -- protection for 'no value' local parent_qids = mw.text.split(parent_raw, '/', true) for idx, p_qid in ipairs(parent_qids) do local parent_claims = mw.wikibase.getBestStatements(p_qid, qids[2]) if parent_claims[1] then value[idx], sortkey[idx], _ = getValueOfClaim(parent_claims[1], nil, parameters) -- raw parent value needed for while/black lists, lang for avoiding an error on types other than entity valueraw[idx], _, _ = getValueOfClaim(parent_claims[1], nil, {["formatting"]="raw", ["lang"]=parameters.lang}) end end end if value[1] then value_text = mw.text.listToText(value, parameters.qseparator, parameters.qconjunction) end return value_text, sortkey[1], valueraw[1] end  -- see d:Help:Sources local function getReferences(claim, parameters) if not isSet(parameters.references) then return '' end local lang = parameters.lang local maxrefs = tonumber(parameters.references) or 1 local notproperref = { ["P143"] = true, -- imported from ["P3452"] = true, -- inferred from ["P887"] = true, -- based on heuristic ["P4656"] = true -- Wiki import URL } local result = {} -- traverse through all references for ref in pairs(claim.references or {}) do local refparts local refs = {} local validref = true local ref_id -- traverse through all parts of the current reference for snakkey, snakval in pairs(claim.references[ref].snaks or {}) do for partkey, _ in pairs(claim.references[ref].snaks[snakkey] or {}) do if notproperref[snakkey] then -- not a proper reference validref = false break end end if validref then for snakidx = 1, #snakval do if snakidx > 1 then refparts = refparts .. ", " end if snakval[snakidx].datatype == 'external-id' then refparts = refparts or '' .. (getSnakValue(snakval[snakidx], {formatting='externalid',property=snakval[snakidx].property}) or '') else refparts = refparts or '' .. (getSnakValue(snakval[snakidx], {lang=lang}) or '') end end refs[snakkey] = refparts refparts = nil if snakkey == "P248" then -- stated in ref_id = getSnak(snakval, {1, "datavalue", "value", "id"}) end end end  -- fill missing values with parent item if ref_id then local function refParent(qid, pid, formatting) local snak = getSnak(mw.wikibase.getBestStatements(qid, pid), {1, "mainsnak"}) return snak and getSnakValue(snak, {formatting=formatting, lang=lang}) end  refs['P50'] = refs['P50'] or refParent(ref_id, 'P50', 'label') -- author refs['P407'] = refs['P407'] or refParent(ref_id, 'P407', 'label') -- language of work refs['P123'] = refs['P123'] or refParent(ref_id, 'P123', 'label') -- publisher refs['P577'] = refs['P577'] or refParent(ref_id, 'P577') -- date refs['P1433'] = refs['P1433'] or refParent(ref_id, 'P1433', 'label') -- published in refs['P304'] = refs['P304'] or refParent(ref_id, 'P304') -- page(s) refs['P433'] = refs['P433'] or refParent(ref_id, 'P433') -- issue refs['P236'] = refs['P236'] or refParent(ref_id, 'P236') -- ISSN refs['P356'] = refs['P356'] or refParent(ref_id, 'P356') -- DOI end  -- get title of local templates for citing references local template_web = mw.wikibase.getSitelink('Q5637226') or "" template_web = mw.text.split(template_web, ":")[2] -- split off namespace from front local template_journal = mw.wikibase.getSitelink('Q5624899') or "" template_journal = mw.text.split(template_journal, ":")[2]  local citeParams = {} if refs['P854'] and (refs['P1476'] or refs['P248']) and template_web then -- if both "reference URL" and "title" (or "stated in") are present, then use cite web template citeParams[i18n['cite']['url']] = refs['P854'] if refs['P248'] and refs['P1476'] == nil then citeParams[i18n['cite']['title']] = refs['P248']:match("^%[%[.-|(.-)%]%]") else citeParams[i18n['cite']['title']] = refs['P1476'] citeParams[i18n['cite']['website']] = refs['P248'] end citeParams[i18n['cite']['author']] = refs['P50'] citeParams[i18n['cite']['language']] = refs['P407'] citeParams[i18n['cite']['publisher']] = refs['P123'] citeParams[i18n['cite']['date']] = refs['P577'] citeParams[i18n['cite']['pages']] = refs['P304'] citeParams[i18n['cite']['access-date']] = refs['P813'] citeParams[i18n['cite']['archive-url']] = refs['P1065'] citeParams[i18n['cite']['archive-date']] = refs['P2960'] citeParams[i18n['cite']['quote']] = refs['P1683'] refparts = mw.getCurrentFrame():expandTemplate{title=template_web, args=citeParams} elseif refs['P1433'] and (refs['P1476'] or refs['P248']) and template_journal then -- if both "published in" and "title" (or "stated in") are present, then use cite journal template citeParams[i18n['cite']['work']] = refs['P1433'] citeParams[i18n['cite']['title']] = refs['P1476'] or refs['P248'] citeParams[i18n['cite']['author']] = refs['P50'] citeParams[i18n['cite']['date']] = refs['P577'] citeParams[i18n['cite']['issue']] = refs['P433'] citeParams[i18n['cite']['pages']] = refs['P304'] citeParams[i18n['cite']['language']] = refs['P407'] citeParams[i18n['cite']['issn']] = refs['P236'] citeParams[i18n['cite']['doi']] = refs['P356'] refparts = mw.getCurrentFrame():expandTemplate{title=template_journal, args=citeParams} elseif validref then -- raw ouput local snaksorder = claim.references[ref]["snaks-order"] local function indexed(a) for _, b in ipairs(snaksorder) do if b == a then return true end end return false end for k, _ in pairs(refs or {}) do if not indexed(k) then table.insert(snaksorder, k) end end local italics = "''" for _, k in ipairs(snaksorder) do if refs[k] then refparts = refparts and refparts .. " " or "" refparts = refparts .. mw.ustring.gsub(getLabelByLangs(k, lang), "^%l", mw.ustring.upper) .. ": " refparts = refparts .. italics .. refs[k] .. italics .. "." italics = "" end end end  if refparts then local ref_name = claim.references[ref].hash result[#result + 1] = mw.getCurrentFrame():extensionTag("ref", refparts, {name=ref_name}) if maxrefs and maxrefs == #result then break end end end if type(result) == 'table' and #result > 0 and isSet(i18n.categoryref) then return table.concat(result) .. "[[" ..i18n.categoryref .. "]]" end return table.concat(result) end  -- Set whitelist or blacklist values local function setWhiteOrBlackList(num_qual, args) local lists = {['whitelist']={}, ['blacklist']={}, ['ignorevalue']={}, ['selectvalue']={}} for i = 0, num_qual do for k, _ in pairs(lists) do if isSet(args[k .. i]) then lists[k][tostring(i)] = {} local pattern = 'Q%d+' if string.sub(args[k .. i], 1, 1) ~= 'Q' then pattern = '[^%p%s]+' end for q in string.gmatch(args[k .. i], pattern) do lists[k][tostring(i)][q] = true end end end end return lists['whitelist'], lists['blacklist'], lists['ignorevalue'], lists['selectvalue'] end  local function tableParameters(args, parameters, column) local column_params = mw.clone(parameters) column_params.formatting = args["colformat"..column]; if column_params.formatting == "" then column_params.formatting = nil end column_params.convert = args["convert" .. column] if args["case" .. column] then column_params.case = args["case" .. column] end return column_params end  local function getEntityId(args, pargs, unnamed) pargs = pargs or {} local id = args.item or args.from or (unnamed and mw.text.trim(args[1] or '') or nil) if not isSet(id) then id = pargs.item or pargs.from or (unnamed and mw.text.trim(pargs[1] or '') or nil) end if isSet(id) then if string.find(id, ":") then -- remove prefix as Property:Pid id = mw.text.split(id, ":")[2] end else id = mw.wikibase.getEntityIdForCurrentPage() end return id end  local function getArg(value, default, aliases) if type(value) == 'boolean' then return value elseif value == "false" or value == "no" then return false elseif value == "true" or value == "yes" then return true elseif value and aliases and aliases[value] then return aliases[value] elseif isSet(value) then return value elseif default then return default else return nil end end  -- Main function claim --------------------------------------------- -- on debug console use: =p.claim{item="Q...", property="P...", ...} function p.claim(frame) local args = frame.args or frame -- via invoke or require local pargs = frame.args and frame:getParent().args or {} local is_sandbox = isSet(pargs.sandbox) if not required and is_sandbox then return require(wiki.module_title .. "/" .. mw.message.new('Sandboxlink-subpage-name'):inLanguage(wiki.langcode):plain()).claim(frame) end --If a value is already set, use it if isSet(args.value) then if args.value == 'NONE' then return else return args.value end end  -- arguments local parameters = {} parameters.id = getEntityId(args, pargs) if parameters.id == nil then return end parameters.property = string.upper(args.property or "") local qualifierId = {} qualifierId[1] = getArg(string.upper(args.qualifier or "")) local i = 2 while isSet(args["qualifier" .. i]) do qualifierId[i] = string.upper(args["qualifier" .. i]) i = i + 1 end parameters.formatting = getArg(args.formatting) parameters.convert = getArg(args.convert) parameters.case = args.case parameters.list = getArg(args.list, true, {firstrank='bestrank'}) parameters.shownovalue = getArg(args.shownovalue, true) parameters.separator = getArg(args.separator) parameters.conjunction = getArg(args.conjunction, parameters.separator) parameters.qseparator = parameters.separator parameters.qconjunction = parameters.conjunction local sorting_col = args.tablesort local sorting_up = (args.sorting or "") ~= "-1" local rowformat = args.rowformat parameters.references = args.references local showerrors = args.showerrors local default = args.default if default then showerrors = nil end parameters.lang = findLang(args.lang) if parameters.formatting == "raw" then parameters.editicon, parameters.labelicon = false, false else parameters.editicon, parameters.labelicon = setIcons(args.editicon, pargs.editicon) -- needs loadI18n by findLand end  -- fetch property local claims = {} local bestrank = (parameters.list == false or parameters.list == 'bestrank') and parameters.list ~= 'lang' for p in string.gmatch(parameters.property, 'P%d+') do claims = getStatements(parameters.id, p, bestrank) if #claims > 0 then parameters.property = p break end end if #claims == 0 then if showerrors then return printError("property-not-found") else return default end end  -- defaults for table local preformat, postformat = "", "" local whitelisted = false local whitelist, blacklist, ignorevalue, selectvalue = {}, {}, {}, {} if parameters.formatting == "table" then parameters.separator = parameters.separator or "
"
parameters.conjunction = parameters.conjunction or "
"
parameters.qseparator = ", " parameters.qconjunction = ", " if not rowformat then rowformat = "$0 ($1" i = 2 while qualifierId[i] do rowformat = rowformat .. ", $" .. i i = i + 1 end rowformat = rowformat .. ")" elseif mw.ustring.find(rowformat, "^[*#]") then parameters.separator = "
  • " parameters.conjunction = "
  • " if mw.ustring.match(rowformat, "^[*#]") == "*" then preformat = "
    • " postformat = "
    "
    else preformat = "
    1. " postformat = "
    "
    end rowformat = mw.ustring.gsub(rowformat, "^[*#] ?", "") end -- set whitelist and blacklist values whitelist, blacklist, ignorevalue, selectvalue = setWhiteOrBlackList(#qualifierId, args) local next = next if next(whitelist) ~= nil then whitelisted = true end end -- set feminine case if gender is requested local itemgender = args.itemgender local idgender if itemgender then if string.match(itemgender, "^P%d+$") then local snak_id = getSnak(mw.wikibase.getBestStatements(parameters.id, itemgender), {1, "mainsnak", "datavalue", "value", "id"}) if snak_id then idgender = snak_id end elseif string.match(itemgender, "^Q%d+$") then idgender = itemgender end end local gender_requested = false if parameters.case == "gender" or idgender then gender_requested = true elseif parameters.formatting == "table" then for i=0, #qualifierId do if args["case" .. i] and args["case" .. i] == "gender" then gender_requested = true break end end end if gender_requested then if feminineGender(idgender or parameters.id) then parameters.gender = "feminineform" end end -- get initial sort indices local sortindices = {} for idx in pairs(claims) do sortindices[#sortindices + 1] = idx end -- sort by claim rank local comparator = function(a, b) local rankmap = { deprecated = 2, normal = 1, preferred = 0 } local ranka = rankmap[claims[a].rank or "normal"] .. string.format("%08d", a) local rankb = rankmap[claims[b].rank or "normal"] .. string.format("%08d", b) return ranka < rankb end table.sort(sortindices, comparator) local result, result2, result_query local error if parameters.list or parameters.formatting == "table" then -- convert LF to line feed,
    may not work on some cases
    parameters.separator = parameters.separator == "LF" and "\010" or parameters.separator parameters.conjunction = parameters.conjunction == "LF" and "\010" or parameters.conjunction -- i18n separators parameters.separator = parameters.separator or mw.message.new('Comma-separator'):inLanguage(parameters.lang[1]):plain() parameters.conjunction = parameters.conjunction or (mw.message.new('And'):inLanguage(parameters.lang[1]):plain() .. mw.message.new('Word-separator'):inLanguage(parameters.lang[1]):plain()) -- iterate over all elements and return their value (if existing) local value, valueq local sortkey, sortkeyq local values = {} local sortkeys = {} local refs = {} local rowlist = {} -- rows to list with whitelist or blacklist for idx in pairs(claims) do local claim = claims[sortindices[idx]] local reference = {} if not whitelisted then rowlist[idx] = true end if parameters.formatting == "table" then local params = tableParameters(args, parameters, "0") value, sortkey, error = getValueOfClaim(claim, nil, params) if value then values[#values + 1] = {} sortkeys[#sortkeys + 1] = {} refs[#refs + 1] = {} if whitelist["0"] or blacklist["0"] then local valueraw, _, _ = getValueOfClaim(claim, nil, {["formatting"]="raw", ["lang"]=params.lang}) if whitelist["0"] and whitelist["0"][valueraw or ""] then rowlist[#values] = true elseif blacklist["0"] and blacklist["0"][valueraw or ""] then rowlist[#values] = false end end for i, qual in ipairs(qualifierId) do local j = tostring(i) params = tableParameters(args, parameters, j) local valueq, sortkeyq, valueraw if qual == parameters.property then -- hack for getting the property with another formatting, i.e. colformat1=raw valueq, sortkeyq, _ = getValueOfClaim(claim, nil, params) else for q in mw.text.gsplit(qual, '%s*OR%s*') do if string.find(q, ".+/.+") then valueq, sortkeyq, valueraw = getValueOfParentClaim(claim, q, params) elseif string.find(q, "^/.+") then local claim2 = getStatements(parameters.id, string.sub(q, 2), bestrank) if #claim2 > 0 then valueq, sortkeyq, _ = getValueOfClaim(claim2[1], nil, params) end else valueq, sortkeyq, _ = getValueOfClaim(claim, q, params) end if valueq then qual = q break end end end values[#values]["col" .. j] = valueq sortkeys[#sortkeys]["col" .. j] = sortkeyq or valueq if whitelist[j] or blacklist[j] or ignorevalue[j] or selectvalue[j] then valueq = valueraw or getValueOfClaim(claim, qual, {["formatting"]="raw", ["lang"]=params.lang}) if whitelist[j] and whitelist[j][valueq or ""] then rowlist[#values] = true elseif blacklist[j] and blacklist[j][valueq or ""] then rowlist[#values] = false elseif ignorevalue[j] and ignorevalue[j][valueq or ""] then values[#values]["col" .. j] = nil elseif selectvalue[j] and not selectvalue[j][valueq or ""] then values[#values]["col" .. j] = nil end end end end else value, sortkey, error = getValueOfClaim(claim, qualifierId[1], parameters) values[#values + 1] = {} sortkeys[#sortkeys + 1] = {} refs[#refs + 1] = {} end if not value and showerrors then value = error end if value then if isSet(parameters.references) and claim.references then reference = claim.references end refs[#refs]["col0"] = reference values[#values]["col0"] = value sortkeys[#sortkeys]["col0"] = sortkey or value end end -- sort and format results sortindices = {} for idx in pairs(values) do sortindices[#sortindices + 1] = idx end if sorting_col then local sorting_table = mw.text.split(sorting_col, '%D+') local comparator = function(a, b) local valuea, valueb local i = 1 while valuea == valueb and i <= #sorting_table do valuea = sortkeys[a]["col" .. sorting_table[i]] or '' valueb = sortkeys[b]["col" .. sorting_table[i]] or '' i = i + 1 end if sorting_up then return valueb > valuea end return valueb < valuea end table.sort(sortindices, comparator) end local maxvals = tonumber(parameters.list) result = {} for idx in pairs(values) do local valuerow = values[sortindices[idx]] local reference = getReferences({["references"] = refs[sortindices[idx]]["col0"]}, parameters) value = valuerow["col0"] if parameters.formatting == "table" then if not rowlist[sortindices[idx]] then value = nil else local rowformatting = rowformat .. "$" -- fake end character added for easy gsub value = mw.ustring.gsub(rowformatting, "$0", {["$0"] = value}) value = mw.ustring.gsub(value, "$R0", reference) -- add reference for i, _ in ipairs(qualifierId) do local valueq = valuerow["col" .. i] if args["rowsubformat" .. i] and isSet(valueq) then -- add fake end character $ -- gsub $i not followed by a number so $1 doesn't match $10, $11... -- remove fake end character valueq = captureEscapes(valueq) valueq = mw.ustring.gsub(args["rowsubformat" .. i] .. "$", "$" .. i .. "(%D)", valueq .. "%1") valueq = string.sub(valueq, 1, -2) rowformatting = mw.ustring.gsub(rowformatting, "$" .. i .. "(%D)", args["rowsubformat" .. i] .. "%1") end valueq = valueq and captureEscapes(valueq) or '' value = mw.ustring.gsub(value, "$" .. i .. "(%D)", valueq .. "%1") end value = string.sub(value, 1, -2) -- remove fake end character value = expandBraces(value, rowformatting) end elseif value then value = expandBraces(value, parameters.formatting) value = value .. reference end if isSet(value) then result[#result + 1] = value if not parameters.list or (maxvals and maxvals == #result) then break end end end if args.query == 'num' then result_query = 0 for _, v in pairs(rowlist) do result_query = result_query + (v and 1 or 0) end end if #result > 0 then if parameters.formatting == 'table' then result = addEditIconTable(result, parameters) -- in a table, add edit icon on last element end result = preformat .. mw.text.listToText(result, parameters.separator, parameters.conjunction) .. postformat else result = '' end else -- return first element local claim = claims[sortindices[1]] result, result2, error = getValueOfClaim(claim, qualifierId[1], parameters) if result and isSet(parameters.references) then result = result .. getReferences(claim, parameters) end if args.query == 'num' then result_query = 1 end end if isSet(result) then if not (parameters.formatting == 'table' or (result2 and result2 == 'no-icon')) then -- add edit icon, except table added previously and except explicit no-icon internal flag result = result .. addEditIcon(parameters) end else if showerrors then result = error else result = default end end if args.query == 'untranslated' and required and not is_sandbox then result_query = untranslated end return result, result_query or '' end -- Local functions for getParentValues ----------------------- local function uc_first(word) if word == nil then return end return mw.ustring.upper(mw.ustring.sub(word, 1, 1)) .. mw.ustring.sub(word, 2) end local function getPropertyValue(id, property, parameter, langs, labelicon, case) local snaks = mw.wikibase.getBestStatements(id, property) local mysnak = getSnak(snaks, {1, "mainsnak"}) if mysnak == nil then return end local entity_id local result = '-' -- default for 'no value' if mysnak.datavalue then entity_id = "Q" .. tostring(mysnak.datavalue.value['numeric-id']) result, _ = getSnakValue(mysnak, {formatting=parameter, lang=langs, labelicon=labelicon, case=case}) end return entity_id, result end local function getParentObjects(id, prop_format, label_format, languages, propertySupString, propertyLabel, propertyLink, label_show, labelicon0, labelicon1, upto_number, upto_label, upto_value, last_only, grammatical_case, include_self) local propertySups = mw.text.split(propertySupString, '[^P%d]') local maxloop = 10 if upto_number then maxloop = tonumber(upto_number) elseif next(upto_label) or next(upto_value) then maxloop = 50 end local labels_filter = next(label_show) local result = {} local id_value = id for iter = 1, maxloop do local link, label, labelwicon, linktext, id_label for _, propertySup in pairs(propertySups) do local _id_value, _link = getPropertyValue(id_value, propertySup, prop_format, languages, labelicon1, grammatical_case) if _id_value and _link then id_value = _id_value; link = _link break end end if not id_value or not link then break end if propertyLink then _, linktext = getPropertyValue(id_value, propertyLink, "label", languages) if linktext then link = mw.ustring.gsub(link, "%[%[(.*)%|.+%]%]", "[[%1|" .. linktext .. "]]") end end id_label, label = getPropertyValue(id_value, propertyLabel, label_format, languages, false, "infoboxlabel") if labelicon0 then _, labelwicon = getPropertyValue(id_value, propertyLabel, label_format, languages, labelicon0, "infoboxlabel") else labelwicon = label end if labels_filter == nil or (label_show[id_label] or label_show[label]) then result[#result + 1] = {labelwicon, link} label_show[id_label or 'none'], label_show[label or 'none'] = nil, nil -- only first label found end if upto_label[id_label] or upto_label[label] or upto_value[id_value] then break end end if last_only then result = {result[#result]} end if include_self then local label_self, link_self _, label_self = getPropertyValue(id, propertyLabel, label_format, languages, labelicon0, "infoboxlabel") link_self, _ = getLabelByLangs(id, languages) table.insert(result, 1, {label_self, link_self}) end return result end local function parentObjectsToString(result, rowformat, cascade, sorting) local ret = {} local first = 1 local last = #result local iter = 1 if sorting == "-1" then first = #result; last = 1; iter = -1 end for i = first, last, iter do local rowtext = mw.ustring.gsub(rowformat, "$[01]", {["$0"] = result[i][1], ["$1"] = result[i][2]}) ret[#ret + 1] = expandBraces(rowtext, rowformat) end if cascade then local direction = mw.language.new(wiki.langcode):isRTL() and "right" or "left" local suffix = "" for i = 1, #ret do ret[i] = '
      .. direction .. ':0.45em; padding-' .. direction .. ':0;">
    • ' .. ret[i] suffix = suffix .. '
    '
    end ret[#ret] = ret[#ret] .. suffix end return ret end -- Returns pairs of parent label and property value fetching a recursive tree function p.getParentValues(frame) local args = frame.args or frame -- via invoke or require local pargs = frame.args and frame:getParent().args or {} if not required and isSet(pargs.sandbox) then return require(wiki.module_title .. "/" .. mw.message.new('Sandboxlink-subpage-name'):inLanguage(wiki.langcode):plain()).getParentValues(frame) end local id = getEntityId(args, pargs) if id == nil then return end local languages = findLang(args.lang) local propertySup = getArg(args.property, "P131") --administrative entity local propertyLabel = getArg(args.label, "P31") --instance local propertyLink = getArg(args.valuetext) local property_format = getArg(args.formatting) local label_format = getArg(args.labelformat, "label") local upto_number = getArg(args.upto) local last_only = getArg(args.last_only, false) local editicon, labelicon = setIcons(args.editicon, pargs.editicon) local include_self = getArg(args.include_self, false) local case = getArg(args.case) local upto_label = {} for q in string.gmatch(args.uptolabelid or '', 'Q%d+') do upto_label[q] = true end if type(upto_number) == 'string' then upto_label[uc_first(upto_number)] = true upto_number = nil require(wiki.module_title .. '/debug').track('upto') -- replace upto by uptolabelid end local upto_value = {} for q in string.gmatch(args.uptovalueid or args.uptolinkid or '', 'Q%d+') do upto_value[q] = true end local label_show = {} for q in string.gmatch(args.showlabelid or '', 'Q%d+') do label_show[q] = true end for _, v in ipairs(mw.text.split(args.labelshow or '', "/")) do if v ~= '' then label_show[uc_first(v)] = true require(wiki.module_title .. '/debug').track('labelshow') -- replace labelshow by showlabelid end end local rowformat = args.rowformat; if not isSet(rowformat) then rowformat = "$0 = $1" end local labelicon0, labelicon1 = labelicon, labelicon if string.find(label_format, '{{.*$0.*}}') or (string.find(rowformat, '{{.*$0.*}}') and label_format ~= 'raw') then labelicon0 = false end local result = getParentObjects(id, property_format, label_format, languages, propertySup, propertyLabel, propertyLink, label_show, labelicon0, labelicon1, upto_number, upto_label, upto_value, last_only, case, include_self) if #result == 0 then return end local separator = args.separator; if not isSet(separator) then separator = "
    "
    end local sorting = args.sorting; if sorting == "" then sorting = nil end local cascade = (args.cascade == "true" or args.cascade == "yes") local ret = parentObjectsToString(result, rowformat, cascade, sorting) ret = addEditIconTable(ret, {property=propertySup, editicon=editicon, id=id, lang=languages}) return mw.text.listToText(ret, separator, separator) end -- Link with a parent label -------------------- function p.linkWithParentLabel(frame) local pargs = frame.args and frame:getParent().args or {} if not required and isSet(pargs.sandbox) then return require(wiki.module_title .. "/" .. mw.message.new('Sandboxlink-subpage-name'):inLanguage(wiki.langcode):plain()).linkWithParentLabel(frame) end local args = {} if frame.args then for k, v in pairs(frame.args) do -- metatable args[k] = v end else args = frame -- via require end if isSet(args.value) then return args.value end -- get id value of property/qualifier local largs = mw.clone(args) largs.list = tonumber(args.list) and args.list or true largs.formatting = "raw" largs.separator = "/·/" largs.editicon = false local items_list, _ = p.claim(largs) if not isSet(items_list) then return end local items_table = mw.text.split(items_list, "/·/", true) -- get internal link of property/qualifier largs.formatting = "internallink" local link_list, _ = p.claim(largs) local link_table = mw.text.split(link_list, "/·/", true) -- get label of parent property local parent_claim = getSnak(getStatements(items_table[1], args.parent, true), {1, "mainsnak", "datatype"}) if parent_claim == 'monolingualtext' then largs.formatting = nil largs.list = 'lang' else largs.formatting = "label" largs.list = false end largs.property = args.parent largs.qualifier = nil for i, v in ipairs(items_table) do largs.item = v local link_label, _ = p.claim(largs) if isSet(link_label) then link_table[i] = mw.ustring.gsub(link_table[i] or '', "%[%[(.*)%|.+%]%]", "[[%1|" .. link_label .. "]]") end end args.editicon, _ = setIcons(args.editicon, pargs.editicon) args.id = getEntityId(args, pargs) args.lang = findLang(args.lang) return mw.text.listToText(link_table) .. addEditIcon(args) end -- Calculate number of years old ---------------------------- function p.yearsOld(frame) if not required and frame.args and isSet(frame:getParent().args.sandbox) then return require(wiki.module_title .. "/" .. mw.message.new('Sandboxlink-subpage-name'):inLanguage(wiki.langcode):plain()).yearsOld(frame) end local args = frame.args or frame -- via invoke or require local pargs = frame.args and frame:getParent().args or {} local id = getEntityId(args, pargs) if id == nil then return end local lang = mw.language.new('en') local function getBestValue(id, prop) return getSnak(mw.wikibase.getBestStatements(id, prop), {1, "mainsnak", "datavalue", "value"}) end local birth = getBestValue(id, 'P569') if type(birth) ~= 'table' or birth.time == nil or birth.precision == nil or birth.precision < 8 then return end local death = getBestValue(id, 'P570') if type(death) ~= 'table' or death.time == nil or death.precision == nil then death = {['time'] = lang:formatDate('c'), ['precision'] = 11} -- current date elseif death.precision < 8 then return end local dates = {} dates[1] = {['min'] = {}, ['max'] = {}, ['precision'] = birth.precision} dates[1].min.year = tonumber(mw.ustring.match(birth.time, "^[+-]?%d+")) dates[1].min.month = tonumber(mw.ustring.match(birth.time, "\-(%d%d)\-")) dates[1].min.day = tonumber(mw.ustring.match(birth.time, "\-(%d%d)T")) dates[1].max = mw.clone(dates[1].min) dates[2] = {['min'] = {}, ['max'] = {}, ['precision'] = death.precision} dates[2].min.year = tonumber(mw.ustring.match(death.time, "^[+-]?%d+")) dates[2].min.month = tonumber(mw.ustring.match(death.time, "\-(%d%d)\-")) dates[2].min.day = tonumber(mw.ustring.match(death.time, "\-(%d%d)T")) dates[2].max = mw.clone(dates[2].min) for i, d in ipairs(dates) do if d.precision == 10 then -- month d.min.day = 1 local timestamp = string.format("%04d", tostring(math.abs(d.max.year))) .. string.format("%02d", tostring(d.max.month)) .. "01" d.max.day = tonumber(lang:formatDate("j", timestamp .. " + 1 month - 1 day")) elseif d.precision < 10 then -- year or decade d.min.day = 1 d.min.month = 1 d.max.day = 31 d.max.month = 12 if d.precision == 8 then -- decade d.max.year = d.max.year + 9 end end end local function age(d1, d2) local years = d2.year - d1.year if d2.month < d1.month or (d2.month == d1.month and d2.day < d1.day) then years = years - 1 end if d2.year > 0 and d1.year < 0 then years = years - 1 -- no year 0 end return years end local old_min = age(dates[1].max, dates[2].min) local old_max = age(dates[1].min, dates[2].max) local old, old_expr if old_min == 0 and old_max == 0 then old = "< 1" old_max = 1 -- expression in singular elseif old_min == old_max then old = old_min else old = old_min .. "/" .. old_max end if args.formatting == 'unit' then local langs = findLang(args.lang) local yo local yo_pl = {} if langs[1] == wiki.langcode then yo_pl = i18n["years-old"] end if not isSet(yo_pl[2]) then local yo_label, _ = getLabelByLangs('Q24564698', langs) yo_pl = {yo_label, yo_label} end yo = mw.language.new(langs[1]):plural(old_max, yo_pl) if mw.ustring.find(yo, '$1', 1, true) then old_expr = mw.ustring.gsub(yo, "$1", old) else old_expr = old .. ' ' .. yo end elseif args.formatting then old_expr = expandBraces(mw.ustring.gsub(args.formatting, '$1', old), args.formatting) else old_expr = old end return old_expr end -- Gets a label in a given language (content language by default) or its fallbacks, optionnally linked. function p.getLabel(frame) local args = frame.args or frame -- via invoke or require local pargs = frame.args and frame:getParent().args or {} if not required and isSet(pargs.sandbox) then return require(wiki.module_title .. "/" .. mw.message.new('Sandboxlink-subpage-name'):inLanguage(wiki.langcode):plain()).getLabel(frame) end local id = getEntityId(args, pargs, 1) if id == nil then return end local languages = findLang(args.lang) local labelicon = false if mw.wikibase.isValidEntityId(id) then _, labelicon = setIcons(args.editicon, pargs.editicon) end local label_icon = '' local label, lang if args.label then label = args.label else -- exceptions or labels fixed local exist, labels = pcall(require, wiki.module_title .. "/labels" .. (languages[1] == wiki.langcode and '' or '/' .. languages[1])) if exist and labels.infoboxLabelsFromId and next(labels.infoboxLabelsFromId) ~= nil then label = labels.infoboxLabelsFromId[id] end if label == nil then label, lang = getLabelByLangs(id, languages) if label then if isSet(args.itemgender) and feminineGender(args.itemgender) then label = feminineForm(id, lang) or label end label = mw.language.new(lang):ucfirst(mw.text.nowiki(label)) -- sanitize if args.case then label = case(args.case, label, lang) end end label_icon = addLabelIcon(id, lang, languages[1], labelicon) end end local linked = args.linked local ret2 = required and untranslated or '' if isSet(linked) and linked ~= "no" then local article = mw.wikibase.getSitelink(id) or ("d:Special:EntityPage/" .. id) return "[[" .. article .. "|" .. (label or id) .. "]]" .. label_icon, ret2 else return (label or id) .. label_icon, ret2 end end -- Utilities ----------------------------- -- See also module ../debug. -- Copied from Module:Wikibase function p.getSiteLink(frame) local args = frame.args or frame -- via invoke or require local pargs = frame.args and frame:getParent().args or {} local id = getEntityId(args, pargs, 1) if id == nil then return end return mw.wikibase.getSitelink(id, mw.text.trim(args[2] or '')) end -- Helper function for the default language code used function p.lang(frame) local lang = frame and frame.args[1] -- nil via require return findLang(lang)[1] end -- Number of statements function p.numStatements(frame) local args = frame.args or frame -- via invoke or require local pargs = frame.args and frame:getParent().args or {} local id = getEntityId(args, pargs) if id == nil then return 0 end local prop = mw.text.trim(args[1] or '') local num = {} if not isSet(prop) and frame.args then args = {} for k, v in pairs(pargs) do args[k] = v end for k, v in pairs(frame.args) do args[k] = v end args.query = 'num' _, num = p.claim(args) return num elseif args[2] then -- qualifier local qual = mw.text.trim(args[2]) local values = p.claim{item=id, property=prop, qualifier=qual, formatting='raw', separator='/·/'} if values then num = mw.text.split(values, '/·/') end else num = mw.wikibase.getBestStatements(id, prop) end return #num end -- Returns true if property datavalue is found excluding novalue/somevalue function p.validProperty(frame) local args = frame.args or frame -- via invoke or require local pargs = frame.args and frame:getParent().args or {} local item = getEntityId(args, pargs) if item == nil then return end local property = mw.text.trim(args[1]) local prop_data = getSnak(mw.wikibase.getBestStatements(item, property), {1, "mainsnak", "datavalue"}) return prop_data and true or nil end function p.editAtWikidata(frame) local args = frame.args or frame -- via invoke or require local pargs = frame.args and frame:getParent().args or {} local value = isSet(args[1]) if value then return end local param = {} param.id = getEntityId(args, pargs) param.property = args.property param.lang = findLang(args.lang) param.editicon, _ = setIcons(args.editicon) return addEditIcon(param) end function p.formatNum(frame) local num = tonumber(mw.text.trim(frame.args[1])) local lang = findLang(mw.text.trim(frame.args[2])) return mw.language.new(lang[1]):formatNum(num) end return p
  • Tags:

    🔥 Trending searches on Wiki Xitsonga:

    ThailandYuropaBelarusGeorgiaMatiKhosaTongaXifenukaniAlgeriaSwivongo swa XitsongaAfrikaEstoniaAfrika-DzongaB.K.M MtombeniVutsariNghunghunyanaMudyaxihi 7FurhwaBadamaRikhotsoSunguti 1InthanetiMudyaxihi 17Washington, DCVutshilaMalamuleleNorgeMudyaxihi 27Riphabliki ya CongoMuringaDyondzo-TinyeletiTogoKayeIthaliNghwevuXikwembuSlovakiaLwandleVuyudaGa-MashishimaleIsraelDuluNdzhatiSteve BikoBilaVuhinduNhlampfurhaMatimu ya misavaVukarhiChadVuswaPhan ThietMudyaxihi 22GibitaSunguti 20Lithuania🡆 More