Documentation for this module may be created at Module:Wikidata/doc
-- version 20200118 from master @cawiki local p = {} -- Initialization of variables -------------------- local i18n = {-- internationalisation at [[Module:Wikidata/i18n]] ["errors"] = { ["property-not-found"] = "Property not found.", ["entity-not-found"] = "Wikidata entity 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 }, ["years-old"] = { ["singular"] = "",-- year old, as in {{PLURAL:$1|singular|plural}} ["plural"] = "",-- years old ["paucal"] = "",-- for languages with 3 plural forms as in {{PLURAL:$1|singular|paucal|plural}} }, ["cite"] = {-- Cite web parameters ["url"]= "url", ["title"]= "title", ["website"]= "website", ["access-date"]= "access-date", ["archive-url"]= "archive-url", ["archive-date"]= "archive-date", ["author"]= "author", ["publisher"]= "publisher", ["quote"]= "quote", ["language"]= "language", ["date"]= "date", ["pages"]= "pages" } } 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() } -- 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() local exist, res = pcall(require, wiki.module_title .. "/i18n") if exist and next(res) ~= nil then tableMerge(i18n, res.i18n) cases = res.cases end end loadI18n() -- 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 var == '') end -- Set local case to a label local function case(localcase, label, ...) if not isSet(label) then return label end if localcase == "smallcaps" then return '' .. label .. '' elseif cases[localcase] then return cases[localcase](label, ...) end return label 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 local languages = mw.language.getFallbacksFor(langcode) table.insert(languages, 1, langcode) return languages end -- mw.wikibase.getLabelWithLang or getLabelByLang with a table of languages local function getLabelByLangs(id, languages) local label local lang = languages[1] if lang == wiki.langcode then -- using getLabelWithLang when possible instead of getLabelByLang label, lang = mw.wikibase.getLabelWithLang(id) else lang = nil for _, l in ipairs(languages) do label = mw.wikibase.getLabelByLang(id, l) if label then lang = l break end end end return label, lang end -- Is gender femenine? true or false local function feminineGender(id) local claims = mw.wikibase.getBestStatements(id or mw.wikibase.getEntityIdForCurrentPage(),'P21') if #claims == 0 then return false elseif claims[1].mainsnak.datavalue == nil then -- novalue or somevalue return false else local genderId = claims[1].mainsnak.datavalue.value.id if genderId == "Q6581072" or genderId == "Q1052281" or genderId == "Q43445" then -- female, transgender female, female organism return true end end return false end -- Fetch female form of label local function feminineForm(id, lang) local feminine_claims = mw.wikibase.getBestStatements(id, 'P2521') -- female form of label for _, feminine_claim in ipairs(feminine_claims) do if feminine_claim.mainsnak.datavalue.value.language == lang then return feminine_claim.mainsnak.datavalue.value.text end end end -- Fetch unit symbol local function unitSymbol(id, lang) local claims = findClaims(id, 'P5061') local langclaims = {} if claims then for _, snak in ipairs(claims) do if snak.mainsnak and snak.mainsnak.datavalue and snak.mainsnak.datavalue.value and not langclaims[snak.mainsnak.datavalue.value.language] -- just the first one by language then langclaims[snak.mainsnak.datavalue.value.language] = snak.mainsnak.datavalue.value.text end end for _, l in ipairs(lang) do if langclaims[l] then return langclaims[l] end end end return langclaims["mul"] -- last try 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 ret_icon = " [[File:Noun Project label icon 1116097 cc mirror.svg|12px|" .. mw.message.new('Translate-taction-translate'):inLanguage(uselang):plain() .. "|link=https://www.wikidata.org/wiki/Special:EntityPage/" .. label_id .. "?uselang=" .. uselang .. "]]" end end return ret_lang .. ret_icon end -- Escape URL escapes to avoid Lua captures local function urlEscapes(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 parameters[mw.ustring.sub(parts[i], 1, subparts-1)] = mw.ustring.sub(parts[i], subparts+1, -1) else 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 = urlEscapes(braces_expanded) text = mw.ustring.gsub(text, "{{" .. braces .. "}}", braces_expanded) end return text end -- Resolve Wikidata redirects, pending phab:T157868 local function resolveEntityId(id) if not id or not mw.wikibase.isValidEntityId(id) then return id end -- if no label in local language nor its fallbacks, maybe it is a redirect -- not using mw.title.new(id).isRedirect as it is expensive if mw.wikibase.getLabel(id) == nil then local entity = mw.wikibase.getEntity(id) -- expensive function if not entity then return nil end if id ~= entity.id then -- Qid redirected to be fixed -- see [[Special:WhatLinksHere/Template:Track/wikidata/redirect]] require(wiki.module_title .. '/debug').track('redirect') require(wiki.module_title .. '/debug').track('redirect/' .. id) else -- no redirect and no label, fix it to avoid expensive functions require(wiki.module_title .. '/debug').track('label') require(wiki.module_title .. '/debug').track('label/' .. id) end return entity.id end return id 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 value string local function printDatavalueString(data, parameters) if parameters.formatting == 'weblink' then return '[' .. data .. ' ' .. mw.text.split(data, '//' )[2] .. ']' elseif 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]) else return data end end -- format data type url local function printDatatypeUrl(data, parameters) return printDatavalueString(urlEscapes(data), parameters) end -- format data value globecoordinate local function printDatavalueCoordinate(data, parameter) 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'} if parameter and string.find(parameter, '$lat', 1, true) and string.find(parameter, '$lon', 1, true) then local ret = mw.ustring.gsub(parameter, '$l[ao][tn]', {['$lat'] = data.latitude, ['$lon'] = data.longitude}) if string.find(parameter, '$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, parameter) elseif parameter == 'latitude' then return data.latitude elseif parameter == 'longitude' then return data.longitude elseif parameter == 'dimension' then return data.dimension else --default formatting='globe' if data.globe == '' or data.globe == nil or data.globe == 'http://www.wikidata.org/entity/Q2' then return 'earth' else local globenum = mw.text.split(data.globe, 'entity/')[2] return globes[globenum] or globenum end end end local function roundPrecision(in_num, out_num) -- rounds out_num with significant figures of in_num (default precision) -- 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 value quantity local function printDatavalueQuantity(data, parameters) local amount = data.amount amount = mw.ustring.gsub(amount, "%+", "") local sortkey = string.format("%09d", amount) local suffix = "" if string.sub(parameters.formatting or '', 1, 4) == "unit" or parameters.convert then -- example "unit": "http://www.wikidata.org/entity/Q174728" 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 local convert_to = parameters.convert 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)) amount = math.floor(tonumber(amount_f) + 0.5) unit_id = convert_to else local conversions = mw.wikibase.getAllStatements(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 amount = roundPrecision(amount, amount * tonumber(conv.mainsnak.datavalue.value.amount)) unit_id = convert_to break end end end end end if parameters.formatting == "unitcode" then -- get unit symbol local unit_symbol = '' if parameters.lang[1] == wiki.langcode and pcall(require, wiki.module_title .. "/Units") then unit_symbol = require(wiki.module_title .. "/Units").getUnit(amount, '', unit_id, true, '') end if unit_symbol == '' then unit_symbol = unitSymbol(unit_id, parameters.lang) end if unit_symbol then suffix = " " .. unit_symbol end end if suffix == '' then -- get unit name local unit_label, lang = getLabelByLangs(unit_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, unit_id, false, '') else suffix = " " .. (unit_label or unit_id) .. addLabelIcon(unit_id, lang, parameters.lang[1], parameters.editicon) end end end end amount = mw.language.new(parameters.lang[1]):formatNum(tonumber(amount)) return amount .. suffix, sortkey end -- format data value time local function printDatavalueTime(data, parameters) -- Dates and times are stored in ISO 8601 format local timestamp = data.time local post_format local calendar_add = "" 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"] else -- 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 d(f, t) local ts = t or timestamp local form = type(f) == "function" and f(ts) or f -- function in i18n.datetime[precision] if string.sub(ts, 1, 1) == '-' then ts = '+' .. string.sub(ts, 2) end -- formatDate() only supports years from 0 return mw.language.new(parameters.lang[1]):formatDate(form, ts) 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 precision = data.precision or 11 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 = mw.ustring.gsub(i18n.datetime[precision], "$1", tostring(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 then -- millennia local card = math.floor((intyear - 1) / 1000) + 1 if mw.ustring.find(i18n.datetime[6], "$1") then ret = mw.ustring.gsub(i18n.datetime[6], "$1", tostring(card)) else ret = d(i18n.datetime[6], string.format("%04d", tostring(card))) end ret = postFormat(ret) elseif precision == 7 then -- centuries local card = math.floor((math.abs(intyear) - 1) / 100) + 1 if mw.ustring.find(i18n.datetime[7], "$1") then ret = mw.ustring.gsub(i18n.datetime[7], "$1", tostring(card)) else ret = d(i18n.datetime[7], string.format("%04d", tostring(card))) end ret = postFormat(ret) .. calendar_add elseif precision == 8 then -- decades local card = math.floor(math.abs(intyear) / 10) * 10 ret = postFormat(mw.ustring.gsub(i18n.datetime[8], "$1", tostring(card))) .. calendar_add elseif precision == 9 or parameters.formatting == 'Y' then -- precision is year ret = postFormat(tostring(intyear)) .. calendar_add elseif precision == 10 then -- month timestamp = timestamp .. " + 1 day" -- formatDate yyyy-mm-00 returns the previous month ret = d(i18n.datetime[10]) ret = postFormat(ret) .. calendar_add ret, _ = string.gsub(ret, "([ %[])0+", "%1") -- supress leading zeros in year, optionally linked else -- precision 11, day ret = d(parameters.formatting or i18n.datetime[11]) ret = postFormat(ret) .. calendar_add ret, _ = string.gsub(ret, "([ %[])0+", "%1") end return ret, timestamp end -- format data value entity local function printDatavalueEntity(data, parameters) local entity_id = data['id'] local entity_page = 'Special:EntityPage/' .. entity_id if parameters.formatting == 'raw' then if data['entity-type'] == 'item' then entity_id = resolveEntityId(entity_id) end return entity_id, entity_id end 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, 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 'wikidata:' .. 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 (parameter == 'internallink' or parameter == 'ucinternallink') then ret1 = '[[' .. label .. '|' .. labelcase .. ']]' ret2 = labelcase else ret1 = '[[wikidata:' .. entity_page .. '|' .. (labelcase or entity_id) .. ']]' ret2 = labelcase or entity_id end end return ret1 .. addLabelIcon(entity_id, lang, parameters.lang[1], parameters.editicon), ret2 end -- format data value monolingualtext local function printDatavalueMonolingualText(data, parameters) -- data fields: language [string], text [string] if parameters.list == "lang" 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 printError(key) return ' .. i18n.errors[key] .. '' end -- the "qualifiers" and "snaks" field have a respective "qualifiers-order" and "snaks-order" field -- use these as the second parameter and this function instead of the built-in "pairs" function -- to iterate over all qualifiers and snaks in the intended order. local function orderedpairs(array, order) if not order then return pairs(array) end -- return iterator function local i = 0 return function() i = i + 1 if order[i] then return order[i], array[order[i]] end end end function findClaims(entityId, property) if not property or not entityId then return end if not mw.ustring.match(property, "^P%d+$") then -- get property id for the given label property = mw.wikibase.resolvePropertyId(property) if not property then return end end local claims = mw.wikibase.getAllStatements(entityId, property) if #claims == 0 then claims = nil end return claims end local function getSnakValue(snak, parameters) if snak.snaktype == 'value' then -- call the respective snak parser if snak.datatype == 'math' then return printDatatypeMath(snak.datavalue.value) elseif snak.datatype == 'musical-notation' then return printDatatypeMusical(snak.datavalue.value, parameters.formatting) elseif snak.datatype == "url" then return printDatatypeUrl(snak.datavalue.value, parameters) elseif snak.datavalue.type == "string" then return printDatavalueString(snak.datavalue.value, parameters) elseif snak.datavalue.type == "globecoordinate" then return printDatavalueCoordinate(snak.datavalue.value, parameters.formatting) elseif snak.datavalue.type == "quantity" then return printDatavalueQuantity(snak.datavalue.value, parameters) elseif snak.datavalue.type == "time" then return printDatavalueTime(snak.datavalue.value, parameters) elseif snak.datavalue.type == 'wikibase-entityid' then return printDatavalueEntity(snak.datavalue.value, parameters) elseif snak.datavalue.type == 'monolingualtext' then return printDatavalueMonolingualText(snak.datavalue.value, parameters) end elseif snak.snaktype == 'novalue' then if parameters.formatting == 'raw' 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 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 qualifier[idx].datavalue.value and 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 function getValueOfClaim(claim, qualifierId, parameters) local error local snak snak, error = getQualifierSnak(claim, qualifierId, parameters) if not snak then return nil, nil, error elseif snak[1] then -- a multi qualifier local result = {} local sortkey = {} for idx in pairs(snak) do result[#result + 1], sortkey[#sortkey + 1] = getSnakValue(snak[idx], parameters) 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 valueraw, parent_claims, value, sortkey if qids[1] == parameters.property then valueraw, _, _ = getValueOfClaim(claim, nil, {["formatting"]="raw", ["lang"]=parameters.lang}) else valueraw, _, _ = getValueOfClaim(claim, qids[1], {["formatting"]="raw", ["lang"]=parameters.lang}) end if string.sub(valueraw or '', 1, 1) == "Q" then -- protection for 'no value' parent_claims = mw.wikibase.getBestStatements(valueraw, qids[2]) if parent_claims[1] ~= nil then value, sortkey, _ = 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, _, _ = getValueOfClaim(parent_claims[1], nil, {["formatting"]="raw", ["lang"]=parameters.lang}) end end return value, sortkey, valueraw end local function getReferences(claim) local refaliases = { citeWeb= "Q5637226", author= "P50", publisher= "P123", importedFrom= "P143", statedIn= "P248", pages= "P304", publicationDate= "P577", startTime= "P580", endTime= "P582", chapter= "P792", retrieved= "P813", referenceURL= "P854", archiveURL= "P1065", title= "P1476", quote= "P1683", shortName= "P1813", language= "P2439", archiveDate= "P2960" } local result = "" -- traverse through all references for ref in pairs(claim.references or {}) do local refparts local refs = {} -- traverse through all parts of the current reference for snakkey, snakval in pairs(claim.references[ref].snaks or {}) do if snakkey ~= refaliases.importedFrom then -- "imported from" is not a proper reference for snakidx = 1, #snakval do if snakidx > 1 then refparts = refparts .. ", " end refparts = refparts or '' .. getSnakValue(snakval[snakidx], {lang={wiki.langcode}}) end refs[snakkey] = refparts refparts = nil end end -- get title of general template for citing web references local template = mw.wikibase.getSitelink(refaliases.citeWeb) or "" template = mw.text.split(template, ":")[2] -- split off namespace from front -- (1) if both "reference URL" and "title" are present, then use the general template for citing web references if refs[refaliases.referenceURL] and (refs[refaliases.title] or refs[refaliases.statedIn]) and template then local citeParams = {} citeParams[i18n['cite']['url']] = refs[refaliases.referenceURL] citeParams[i18n['cite']['title']] = refs[refaliases.title] or refs[refaliases.statedIn]:match("^%[%[.-|(.-)%]%]") citeParams[i18n['cite']['website']] = refs[refaliases.statedIn] citeParams[i18n['cite']['language']] = refs[refaliases.language] citeParams[i18n['cite']['date']] = refs[refaliases.publicationDate] citeParams[i18n['cite']['access-date']] = refs[refaliases.retrieved] citeParams[i18n['cite']['archive-url']] = refs[refaliases.archiveURL] citeParams[i18n['cite']['archive-date']] = refs[refaliases.archiveDate] citeParams[i18n['cite']['publisher']] = refs[refaliases.publisher] citeParams[i18n['cite']['quote']] = refs[refaliases.quote] citeParams[i18n['cite']['pages']] = refs[refaliases.pages] citeParams[i18n['cite']['author']] = refs[refaliases.author] refparts = mw.getCurrentFrame():expandTemplate{title=template, args=citeParams} else -- raw ouput for k, v in orderedpairs(refs or {}, claim.references[ref]["snaks-order"]) do if k and v then if refparts then refparts = refparts .. ", " else refparts = "" end refparts = refparts .. tostring(mw.wikibase.getLabel(k)) .. ": " refparts = refparts .. v end end end if refparts then result = result .. mw.getCurrentFrame():extensionTag("ref", refparts) end end return result end -- Set whitelist or blacklist values local function setWhiteOrBlackList(type_list, num_qual, args) local listed = false local list = {} for i = 0, num_qual do if isSet(args[type_list .. i]) then listed = true list[tostring(i)] = {} local values = mw.text.split(args[type_list .. i], "/", true) for _, v in ipairs(values) do list[tostring(i)][v] = true list[tostring(i)][resolveEntityId(v)] = true end end end return list, listed 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 -- 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 --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 pargs = frame.args and frame:getParent().args local id = args.item or (pargs and pargs.item) if not isSet(id) then id = mw.wikibase.getEntityIdForCurrentPage() if id == nil then return end end local property = string.upper(args.property or "") local qualifierId = {} qualifierId[1] = isSet(args.qualifier) and string.upper(args.qualifier) or nil local i = 2 while isSet(args["qualifier" .. i]) do qualifierId[i] = string.upper(args["qualifier" .. i]) i = i + 1 end local formatting = args.formatting; if formatting == "" then formatting = nil end local convert = args.convert; if convert == "" then convert = nil end if convert and string.sub(convert, 1, 1) ~= "Q" then convert = nil end local case = args.case local list = args.list or true; if (list == "false" or list == "no") then list = false end local sorting_col = args.tablesort local sorting_up = (args.sorting or "") ~= "-1" local separator = args.separator local conjunction = args.conjunction or args.separator local rowformat = args.rowformat local references = args.references local showerrors = args.showerrors local default = args.default if default then showerrors = nil end local editicon = not (args.editicon == "false" or args.editicon == "no") local parameters = {["id"] = id, ["property"] = property, ["formatting"] = formatting, ["convert"] = convert, ["list"] = list, ["case"] = case, ["editicon"] = editicon, ["separator"] = separator, ["conjunction"] = conjunction, ["qseparator"] = separator, ["qconjunction"] = conjunction} -- defaults for table local preformat, postformat = "", "" local whitelisted, blacklisted = false, false local whitelist, blacklist = {}, {} 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 = "- "
postformat = "" end rowformat = mw.ustring.gsub(rowformat, "^[*#] ?", "") end -- set whitelist and blacklist values whitelist, whitelisted = setWhiteOrBlackList("whitelist", #qualifierId, args) blacklist, blacklisted = setWhiteOrBlackList("blacklist", #qualifierId, args) end -- fetch property local claims for p in string.gmatch(property, 'P%d+') do claims = findClaims(id, p) if claims and claims[1] then break end end if not claims or not claims[1] then if showerrors then return printError("property-not-found") else return default end end -- find language and defaults parameters.lang = findLang(args.lang) -- 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 = mw.wikibase.getBestStatements(id, itemgender)[1] if snak and snak.mainsnak and snak.mainsnak.datavalue and snak.mainsnak.datavalue.value then idgender = snak.mainsnak.datavalue.value.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 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 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 firstrank = parameters.list == "firstrank" and claims[sortindices[1]].rank or '' 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 firstrank ~= '' and firstrank ~= claim.rank then break 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 == 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 = findClaims(id, string.sub(q, 2)) if claim2 then valueq, sortkeyq, _ = getValueOfClaim(claim2[1], nil, params) end else valueq, sortkeyq, _ = getValueOfClaim(claim, q, params) end if valueq then break end end end values[#values]["col" .. j] = valueq sortkeys[#sortkeys]["col" .. j] = sortkeyq or valueq if whitelist[j] or blacklist[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 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 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, '/', true) 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 result = {} for idx in pairs(values) do local valuerow = values[sortindices[idx]] local reference = getReferences({["references"] = refs[sortindices[idx]]["col0"]}) value = valuerow["col0"] if parameters.formatting == "table" then if not rowlist[sortindices[idx]] then value = nil else value = mw.ustring.gsub(rowformat .. "$", "$0", value) -- fake end character added for easy gsub value = mw.ustring.gsub(value, "$R0", reference) -- add reference local rowformatting = rowformat .. "$" for i, _ in ipairs(qualifierId) do local valueq = valuerow["col" .. i] if args["rowsubformat" .. i] and 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 = 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 urlEscapes(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 if isSet(value) then result[#result + 1] = value end end if not parameters.list then break end end result = preformat .. mw.text.listToText(result, parameters.separator, parameters.conjunction) .. postformat else -- return first element local claim = claims[sortindices[1]] result, _, error = getValueOfClaim(claim, qualifierId[1], parameters) if result and references then result = result .. getReferences(claim) end end if isSet(result) then return result else if showerrors then return error else return default end end end -- This is used to get the TA98 (Terminologia Anatomica first edition 1998) values like 'A01.1.00.005' (property P1323) -- which are then linked to http://www.unifr.ch/ifaa/Public/EntryPage/TA98%20Tree/Entity%20TA98%20EN/01.1.00.005%20Entity%20TA98%20EN.htm -- uses the newer mw.wikibase calls instead of directly using the snaks -- formatPropertyValues returns a table with the P1323 values concatenated with ", " so we have to split them out into a table in order to construct the return string p.getTAValue = function(frame) local ent = mw.wikibase.getEntity() local props = ent:formatPropertyValues('P1323') local out = {} local t = {} for k, v in pairs(props) do if k == 'value' then t = mw.text.split( v, ", ") for k2, v2 in pairs(t) do out[#out + 1] = "[http://www.unifr.ch/ifaa/Public/EntryPage/TA98%20Tree/Entity%20TA98%20EN/" .. string.sub(v2, 2) .. "%20Entity%20TA98%20EN.htm " .. v2 .. "]" end end end local ret = table.concat(out, "
") if #ret == 0 then ret = "Invalid TA" end return ret end -- Local functions for getParentValues ----------------------- local function uc_first(word) return mw.ustring.upper(mw.ustring.sub(word, 1, 1)) .. mw.ustring.sub(word, 2) end local function getPropertyValue(id, property, parameter, langs, editicon, case) local snaks = mw.wikibase.getBestStatements(id, property) local mysnak if snaks and snaks[1] and snaks[1].mainsnak then mysnak = snaks[1].mainsnak else 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, editicon=editicon, case=case}) end return entity_id, result end local function contains(tab, val) for index, value in ipairs(tab) do if value == val then return true end end return false end local function getParentObjects(id, formatting, languages, propertySupString, propertyLabel, propertyLink, labelShow, editicon, upto, upto_linkId, last_only, grammatical_case, include_self) if (upto_linkId == nil) then upto_linkId = "" end local upto_link_ids = mw.text.split(upto_linkId, '[/%s]+') local propertySups = mw.text.split(propertySupString, '[/%s]+') local lastlabel = uc_first(upto or '') local maxloop = tonumber(upto) or (lastlabel == '' and 10 or 50) local labelFilter = {} if labelShow then for i, v in ipairs(mw.text.split(labelShow, "/")) do labelFilter[uc_first(v)] = true end end local label_self _, label_self = getPropertyValue(id, propertyLabel, "label", languages) local result = {} local label, link, linktext for iter = 1, maxloop do local label, link for _, propertySup in pairs(propertySups) do _id, _link = getPropertyValue(id, propertySup, formatting, languages, editicon, grammatical_case) if _id and _link then id = _id link = _link break end end if not id or not link then break end if propertyLink then _, linktext = getPropertyValue(id, propertyLink, "label", languages) if linktext then link = mw.ustring.gsub(link, "%[%[(.*)%|.+%]%]", "[[%1|" .. linktext .. "]]") end end --_, label = getPropertyValue(id, propertyLabel, "label", languages, editicon, "infoboxlabel") _, label = getPropertyValue(id, propertyLabel, "label", languages, false, "infoboxlabel") -- check Template:Automatic taxobox if labelShow == nil or labelFilter[label] then result[#result + 1] = {label, link} if label then labelFilter[label] = nil -- only first label found end end if not tonumber(upto) and label == lastlabel then break end if contains(upto_link_ids, id) then break end end if last_only then result = {result[#result]} end if include_self then table.insert(result, 1, {label_self, mw.title.getCurrentTitle().text}) end return result end local function parentObjectsToString(result, rowformat, separator, 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 prefix = "" for i = 1, #ret do ret[i] = prefix .. "• " .. ret[i] prefix = prefix .. " " end end return mw.text.listToText(ret, separator, separator) end -- Returns pairs of instance 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 local id = args.item or (pargs and pargs.item) if not isSet(id) then id = mw.wikibase.getEntityIdForCurrentPage() if id == nil then return end end local languages = findLang(args.lang) local propertySup = args["property"]; if not isSet(propertySup) then propertySup = "P131" end --administrative entity local propertyLabel = args["label"]; if not isSet(propertyLabel) then propertyLabel = "P31" end --instance local propertyLink = args["valuetext"]; if propertyLink == "" then propertyLink = nil end --internallink local upto = args["upto"]; if upto == "" then upto = nil end local last_only = (args.last_only == "true" or args.last_only == "yes") local labelShow = args["labelshow"]; if labelShow == "" then labelShow = nil end local editicon = not (args.editicon == "false" or args.editicon == "no") local include_self = (args.include_self == "true" or args.include_self == "yes") local case = args["case"]; if case == "" then case = nil end if isSet(args.uptolabelid) then upto = mw.wikibase.getLabel(args.uptolabelid) end if isSet(args.showlabelid) then local showLabelList = {} for substring in mw.text.gsplit(args.showlabelid, '[/%s]+') do table.insert(showLabelList, mw.wikibase.getLabel(substring)) end if #showLabelList > 0 then labelShow = table.concat(showLabelList,"/") end end local result = getParentObjects(id, args.formatting, languages, propertySup, propertyLabel, propertyLink, labelShow, editicon, upto, args.uptolinkid, last_only, case, include_self) local rowformat = args["rowformat"]; if not isSet(rowformat) then rowformat = "$0 = $1" 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") return parentObjectsToString(result, rowformat, separator, cascade, sorting) end -- Link with a parent label -------------------- function p.linkWithParentLabel(frame) 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 internal link of property/qualifier local largs = mw.clone(args) largs.list = "true" largs.formatting = "internallink" largs.separator = "/·/" largs.editicon = "false" local link_list = p.claim(largs) if not isSet(link_list) then return end local link_table = mw.text.split(link_list, "/·/", true) -- get id value of property/qualifier largs.formatting = "raw" local items_list = p.claim(largs) local items_table = mw.text.split(items_list, "/·/", true) -- get label of parent property local parent_claims = findClaims(items_table[1], args.parent) if parent_claims and parent_claims[1].mainsnak.datatype == '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 = not (args.editicon == "false" or args.editicon == "no") args.id = args.item or mw.wikibase.getEntityIdForCurrentPage() args.lang = findLang(args.lang) return mw.text.listToText(link_table) end -- Calculate number of years old ---------------------------- function p.yearsOld(frame) local args = frame.args or frame -- via invoke or require local id = args.item if not isSet(id) then id = mw.wikibase.getEntityIdForCurrentPage() end local lang = mw.language.new('en') local function getBestValue(id, prop) local snak = mw.wikibase.getBestStatements(id, prop)[1] if snak and snak.mainsnak and snak.mainsnak.datavalue and snak.mainsnak.datavalue.value then return snak.mainsnak.datavalue.value end 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, yo_sg, yo_pl, yo_pau if langs[1] == wiki.langcode then yo_sg = i18n["years-old"].singular yo_pl = i18n["years-old"].plural yo_pau = i18n["years-old"].paucal end if not isSet(yo_pl) then yo_pl = getLabelByLangs('Q24564698', langs) yo_sg = yo_pl end if not isSet(yo_pau) then yo_pau = yo_pl end yo = mw.language.new(langs[1]):plural(old_max, {yo_sg, yo_pau, 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 id = mw.text.trim(args[1] or "") if not isSet(id) then return end local editicon = not (args.editicon == "false" or args.editicon == "no") and mw.wikibase.isValidEntityId(id) local label_icon = '' local label, lang if args.label then label = args.label else local languages = findLang(args.lang) -- exceptions or labels fixed local exist, labels = pcall(require, wiki.module_title .. "/labels" .. (languages[1] == wiki.langcode and '' or '/' .. languages[1])) if exist and next(labels.infoboxLabelsFromId) ~= nil then label = labels.infoboxLabelsFromId[id] end if label == nil then local new_id = resolveEntityId(id) if new_id then label, lang = getLabelByLangs(new_id, languages) if label then if args.itemgender and feminineGender(args.itemgender) then label = feminineForm(new_id, lang) or label end label = mw.language.new(lang):ucfirst(mw.text.nowiki(label)) -- sanitize end label_icon = addLabelIcon(new_id or id, lang, languages[1], editicon) end end end local linked = args.linked if isSet(linked) and linked ~= "no" then local article = mw.wikibase.getSitelink(id) or ("d:" .. id) return "[[" .. article .. "|" .. (label or id) .. "]]" .. label_icon else return (label or id) .. label_icon end end -- Utilities ----------------------------- -- debugging functions, see module ../debug. function p.ViewSomething(frame) return require(wiki.module_title .. "/debug").ViewSomething(frame) end function p.Dump(frame) return require(wiki.module_title .. "/debug").Dump(frame) end function p.getEntityFromTree(frame) return require(wiki.module_title .. "/debug").getEntityFromTree(frame) end -- Copied from Module:Wikibase function p.getSiteLink(frame) local id = frame.args[1] if not isSet(id) then id = mw.wikibase.getEntityIdForCurrentPage() if id == nil then return end end return mw.wikibase.getSitelink(id, frame.args[2]) 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 local id = args.item if id == '' or id == nil then id = mw.wikibase.getEntityIdForCurrentPage() if id == nil then return 0 end end local prop = mw.text.trim(args[1]) local num = mw.wikibase.getBestStatements(id, prop) return #num end -- Returns the first id or value of given property or nil if not found, not isValidEntityId or novalue/somevalue -- See Module:Wikibase function p.validProperty(frame) local property = mw.text.trim(frame.args[1]) local item = frame.args.item or frame.args.from; if item == '' then item = nil end local type = frame.args.type or "id" local entity = mw.wikibase.getEntity(item) if not entity then return end if not entity.claims then return end local hasProp = entity.claims[property] if not hasProp then return end if not hasProp[1].mainsnak.datavalue then return end if type == "value" then return hasProp[1].mainsnak.datavalue.value end if not hasProp[1].mainsnak.datavalue.value.id then return end if not mw.wikibase.isValidEntityId(hasProp[1].mainsnak.datavalue.value.id) then return end return hasProp[1].mainsnak.datavalue.value.id end function p.labelOf(frame) local id = frame.args[1] -- returns the label of the given entity/property id -- if no id is given, the one from the entity associated with the calling Wikipedia article is used if not id then local entity = mw.wikibase.getEntity() if not entity then return printError("entity-not-found") end id = entity.id end return mw.wikibase.getLabel(id) end local function bestranked(claims) if not claims then return nil end local preferred, normal = {}, {} for i, j in pairs(claims) do if j.rank == 'preferred' then table.insert(preferred, j) elseif j.rank == 'normal' then table.insert(normal, j) end end if #preferred > 0 then return preferred else return normal end end function p.labelIn(frame) local langcode = frame.args[1] or wiki.langcode local id = frame.args[2] local property_id = frame.args["p"] local entity = mw.wikibase.getEntity(id) if property_id ~= nil then local claims = findClaims(entity.id, property_id) if not claims or not claims[1] then return nil end claims = bestranked(claims) local claim_id = getValueOfClaim(claims[1], nil, {["formatting"]="raw"}) entity = mw.wikibase.getEntity(claim_id) end -- return label of a Wikidata entity in the given language or the default language of this Wikipedia site return entity.labels[langcode].value end function p.internalLinkOf(frame) local id = frame.args[1] if id == nil then return nil end local entity = mw.wikibase.getEntity(id) if entity == nil then return nil end local res = printDatavalueEntity(entity, { formatting ="ucinternallink", lang = findLang()}) return res end function p.claimsCount(frame) local property = frame.args["property"] local id = frame.args["id"] -- get wikidata entity local entity = mw.wikibase.getEntity(id) if not entity then if showerrors then return printError("entity-not-found") else return default end end -- fetch the first claim of satisfying the given property local claims = findClaims(id, property) if not claims or not claims[1] then if showerrors then return printError("property-not-found") else return default end end return #claims end function p.processMarkup(frame) local f = (frame.args[1] or frame.args.item) and frame or frame:getParent() return f:preprocess(f.args[1]) end return p