Lompat ke isi

Modul:Wikidata: Perbedaan antara revisi

Dari Wikipedia bahasa Indonesia, ensiklopedia bebas
Konten dihapus Konten ditambahkan
getLabelWithLang, findLang
Nyilvoskt (bicara | kontrib)
Membalikkan revisi 26004704 oleh Nyilvoskt (bicara)
Tag: Pembatalan Suntingan perangkat seluler Suntingan peramban seluler Suntingan seluler lanjutan
 
(39 revisi perantara oleh 5 pengguna tidak ditampilkan)
Baris 1: Baris 1:
-- version 20231204 from master @cawiki
-- vim: set noexpandtab ft=lua ts=4 sw=4:
-- changes from previous version: whitelist, blacklist, ignorevalue and selectvalue work with multivalue qualifiers
require('Module:No globals')

local p = {}
local p = {}
local debug = false


-- Initialization of variables --------------------


local i18n = { -- internationalisation at [[Module:Wikidata/i18n]]
------------------------------------------------------------------------------
["errors"] = {
-- module local variables and functions
["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
["suppressids"] = {}, -- list of Qid values to suppress
["qidlabels"] = true -- show labels as Qid if no fallback translation is available
}


local cases = {} -- functions for local grammatical cases defined at [[Module:Wikidata/i18n]]
local wiki =

local required = ... -- variadic arguments from require function
local wiki =
{
{
langcode = mw.language.getContentLanguage().code
langcode = mw.language.getContentLanguage().code,
module_title = required or mw.getCurrentFrame():getTitle()
}
}


local untranslated -- used in infobox modules: nil or true
-- internationalisation
local _ -- variable for unused returned values, avoiding globals
local i18n =
{
["errors"] =
{
["property-not-found"] = "Properti tidak ditemukan.",
["entity-not-found"] = "Entitas wikidata tidak ditemukan.",
["unknown-claim-type"] = "Objek klaim tak dikenal.",
["unknown-entity-type"] = "Objek entitas tak dikenal.",
["qualifier-not-found"] = "Kualifikasi tidak ditemukan.",
["site-not-found"] = "Proyek Wikimedia tidak ditemukan.",


-- Module local functions --------------------------------------------
["unknown-datetime-format"] = "Format tanggal/waktu tidak dikenal.",
["local-article-not-found"] = "Artikel belum tersedia di wiki ini."
},
["datetime"] =
{
-- $1 adalah jumlah sebenarnya
[0] = "$1 miliar tahun", -- precision: billion years
[1] = "$100 juta tahun", -- precision: hundred million years
[2] = "$10 juta tahun", -- precision: ten million years
[3] = "$1 juta tahun", -- precision: million years
[4] = "$100.000 tahun", -- precision: hundred thousand years
[5] = "$10.000 tahun", -- precision: ten thousand years
[6] = "$1 milenium", -- precision: millennium
[7] = "$1 abad", -- precision: century
[8] = "$1-an", -- precision: decade
-- the following use the format of #time parser function
[9] = "Y", -- precision: year,
[10] = "F Y", -- precision: month
[11] = "j F Y", -- precision: day
[12] = "j F Y, ga", -- precision: hour
[13] = "j F Y, g:ia", -- precision: minute
[14] = "j F Y, g:i:sa", -- precision: second
["beforenow"] = "$1 SM", -- how to format negative numbers for precisions 0 to 5
["afternow"] = "$1 M", -- how to format positive numbers for precisions 0 to 5
["bc"] = '$1 "SM"', -- how print negative years
["ad"] = "$1", -- how print positive years
-- the following are for function getDateValue() and getQualifierDateValue()
["default-format"] = "dmy", -- default value of the #3 (getDateValue) or
-- #4 (getQualifierDateValue) argument
["default-addon"] = "BC", -- default value of the #4 (getDateValue) or
-- #5 (getQualifierDateValue) argument
["prefix-addon"] = false, -- set to true for languages put "BC" in front of the
-- datetime string; or the addon will be suffixed
["addon-sep"] = " ", -- separator between datetime string and addon (or inverse)
["format"] = -- options of the 3rd argument
{
["mdy"] = "F j, Y",
["my"] = "F Y",
["y"] = "Y",
["dmy"] = "j F Y",
["ymd"] = "Y-m-d",
["ym"] = "Y-m"
}
},
["monolingualtext"] = '<span lang="%language">%text</span>',
["warnDump"] = "[[Kategori:Memanggil fungsi 'Dump' dari modul Wikidata]]",
["ordinal"] =
{
[1] = "st",
[2] = "nd",
[3] = "rd",
["default"] = "th"
}
}


-- Credit to http://stackoverflow.com/a/1283608/2644759, cc-by-sa 3.0
--require("Module:i18n").loadI18n("Module:Wikidata/i18n", i18n)
local function tableMerge(t1, t2)
-- got idea from [[:w:Module:Wd]]
for k, v in pairs(t2) do
local module_title; if ... == nil then
if type(v) == "table" then
module_title = mw.getCurrentFrame():getTitle()
if type(t1[k] or false) == "table" then
else
tableMerge(t1[k] or {}, t2[k] or {})
module_title = ...
else
t1[k] = v
end
else
t1[k] = v
end
end
return t1
end
end
require('Module:i18n').loadI18n(module_title..'/i18n', i18n)


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
i18n.suppress = {}
for _, id in ipairs(i18n.suppressids) do
i18n.suppress[id] = true
end
end


-- Table of language codes: requested or default and its fallbacks
local function findLang(langcode)
local function findLang(langcode)
if langcode == nil or langcode == "" or mw.language.isKnownLanguageTag(langcode) == false then
if mw.language.isKnownLanguageTag(langcode or '') == false then
local myframe = mw.getCurrentFrame()
local cframe = mw.getCurrentFrame()
local pframe = cframe:getParent()
langcode = myframe.args.lang
if langcode == nil or langcode == "" or mw.language.isKnownLanguageTag(langcode) == false then
langcode = pframe and pframe.args.lang
if mw.language.isKnownLanguageTag(langcode or '') == false then
if not mw.title.getCurrentTitle().isContentPage then
langcode = myframe:getParent().args.lang
langcode = cframe:callParserFunction('int', {'lang'})
if langcode == nil or langcode == "" or mw.language.isKnownLanguageTag(langcode) == false then
end
if not mw.title.getCurrentTitle().isContentPage then
if mw.language.isKnownLanguageTag(langcode or '') == false then
langcode = myframe:preprocess( '{{int:lang}}' )
langcode = wiki.langcode
end
if langcode == nil or langcode == "" or mw.language.isKnownLanguageTag(langcode) == false then
langcode = wiki.langcode
end
end
end
end
end
end
end
loadI18n(langcode)
local languages = mw.language.getFallbacksFor(langcode)
local languages = mw.language.getFallbacksFor(langcode)
table.insert(languages, 1, 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
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 '<span style="font-variant: small-caps;">' .. label .. '</span>'
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
end


Baris 115: Baris 183:
local function getLabelByLangs(id, languages)
local function getLabelByLangs(id, languages)
local label
local label
local lang = languages[1]
local lang
for _, l in ipairs(languages) do
if lang == wiki.langcode then
if l == wiki.langcode then
-- using getLabelWithLang when possible instead of getLabelByLang
label, lang = mw.wikibase.getLabelWithLang(id)
-- using getLabelWithLang when possible instead of getLabelByLang
label, l = mw.wikibase.getLabelWithLang(id)
else
else
for _, l in ipairs(languages) do
label = mw.wikibase.getLabelByLang(id, l)
label = mw.wikibase.getLabelByLang(id, l)
end
if label then
lang = l
lang = l
break
if label then
break
end
end
end
end
end
Baris 131: Baris 199:
end
end


-- getBestStatements if bestrank=true, else getAllStatements with no deprecated
-- this function needs to be internationalised along with the above:
local function getStatements(entityId, property, bestrank)
-- takes cardinal numer as a numeric and returns the ordinal as a string
local claims = {}
-- we need three exceptions in English for 1st, 2nd, 3rd, 21st, .. 31st, etc.
if not (entityId and mw.ustring.match(property, "^P%d+$")) then return claims end
local function makeOrdinal (cardinal)
if bestrank then
local ordsuffix = i18n.ordinal.default
claims = mw.wikibase.getBestStatements(entityId, property)
if cardinal % 10 == 1 then
else
ordsuffix = i18n.ordinal[1]
local allclaims = mw.wikibase.getAllStatements(entityId, property)
elseif cardinal % 10 == 2 then
for _, c in ipairs(allclaims) do
ordsuffix = i18n.ordinal[2]
elseif cardinal % 10 == 3 then
if c.rank ~= "deprecated" then
table.insert(claims, c)
ordsuffix = i18n.ordinal[3]
end
end
-- In English, 1, 21, 31, etc. use 'st', but 11, 111, etc. use 'th'
-- similarly for 12 and 13, etc.
if (cardinal % 100 == 11) or (cardinal % 100 == 12) or (cardinal % 100 == 13) then
ordsuffix = i18n.ordinal.default
end
return tostring(cardinal) .. ordsuffix
end

local function printError(code)
return '<span class="error">' .. (i18n.errors[code] or code) .. '</span>'
end
local function parseDateFormat(f, timestamp, addon, prefix_addon, addon_sep)
local year_suffix
local tstr = ""
local lang_obj = mw.language.new(wiki.langcode)
local f_parts = mw.text.split(f, 'Y', true)
for idx, f_part in pairs(f_parts) do
year_suffix = ''
if string.match(f_part, "x[mijkot]$") then
-- for non-Gregorian year
f_part = f_part .. 'Y'
elseif idx < #f_parts then
-- supress leading zeros in year
year_suffix = lang_obj:formatDate('Y', timestamp)
year_suffix = string.gsub(year_suffix, '^0+', '', 1)
end
end
tstr = tstr .. lang_obj:formatDate(f_part, timestamp) .. year_suffix
end
if addon ~= "" and prefix_addon then
return addon .. addon_sep .. tstr
elseif addon ~= "" then
return tstr .. addon_sep .. addon
else
return tstr
end
end
return claims
end
end
local function parseDateValue(timestamp, date_format, date_addon)
local prefix_addon = i18n["datetime"]["prefix-addon"]
local addon_sep = i18n["datetime"]["addon-sep"]
local addon = ""


-- Is gender femenine? true or false
-- check for negative date
local function feminineGender(id)
if string.sub(timestamp, 1, 1) == '-' then
local claims = mw.wikibase.getBestStatements(id or mw.wikibase.getEntityIdForCurrentPage(),'P21')
timestamp = '+' .. string.sub(timestamp, 2)
local gender_id = getSnak(claims, {1, "mainsnak", "datavalue", "value", "id"})
addon = date_addon
if gender_id == "Q6581072" or gender_id == "Q1052281" or gender_id == "Q43445" then -- female, transgender female, female organism
end
return true
local _date_format = i18n["datetime"]["format"][date_format]
if _date_format ~= nil then
return parseDateFormat(_date_format, timestamp, addon, prefix_addon, addon_sep)
else
return printError("unknown-datetime-format")
end
end
return false
end
end


-- Fetch female form of label
-- This local function combines the year/month/day/BC/BCE handling of parseDateValue{}
local function feminineForm(id, lang)
-- with the millennium/century/decade handling of formatDate()
local feminine_claims = getStatements(id, 'P2521')
local function parseDateFull(timestamp, precision, date_format, date_addon)
for _, feminine_claim in ipairs(feminine_claims) do
local prefix_addon = i18n["datetime"]["prefix-addon"]
if getSnak(feminine_claim, {'mainsnak', 'datavalue', 'value', 'language'}) == lang then
local addon_sep = i18n["datetime"]["addon-sep"]
return feminine_claim.mainsnak.datavalue.value.text
local addon = ""

-- check for negative date
if string.sub(timestamp, 1, 1) == '-' then
timestamp = '+' .. string.sub(timestamp, 2)
addon = date_addon
end

-- get the next four characters after the + (should be the year now in all cases)
-- ok, so this is dirty, but let's get it working first
local intyear = tonumber(string.sub(timestamp, 2, 5))
if intyear == 0 and precision <= 9 then
return ""
end

-- precision is 10000 years or more
if precision <= 5 then
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 addon ~= "" then
-- negative date
relative = mw.ustring.gsub(i18n.datetime.beforenow, "$1", relative)
else
relative = mw.ustring.gsub(i18n.datetime.afternow, "$1", relative)
end
end
return relative
end
end
end


-- Add an icon for no label in requested language
-- precision is decades (8), centuries (7) and millennia (6)
local function addLabelIcon(label_id, lang, uselang, icon)
local era, card
local ret_lang, ret_icon = '', ''
if precision == 6 then
if icon then
card = math.floor((intyear - 1) / 1000) + 1
if lang and lang ~= uselang then
era = mw.ustring.gsub(i18n.datetime[6], "$1", makeOrdinal(card))
ret_lang = " <sup>(" .. lang .. ")</sup>"
end
end
if precision == 7 then
if label_id and (lang == nil or lang ~= uselang) then
card = math.floor((intyear - 1) / 100) + 1
local namespace = ''
era = mw.ustring.gsub(i18n.datetime[7], "$1", makeOrdinal(card))
if string.sub(label_id, 1, 1) == 'P' then
end
namespace = 'Property:'
if precision == 8 then
end
era = mw.ustring.gsub(i18n.datetime[8], "$1", tostring(math.floor(math.abs(intyear) / 10) * 10))
ret_icon = " [[File:OOjs_UI_icon_tag-rtl-progressive.svg|10px|baseline|"
end
.. mw.message.new('Translate-taction-translate'):inLanguage(uselang):plain()
if era then
.. "|link=https://www.wikidata.org/wiki/" .. namespace .. label_id .. "?uselang=" .. uselang .. "]]"
if addon ~= "" then
untranslated = true
era = mw.ustring.gsub(mw.ustring.gsub(i18n.datetime.bc, '"', ""), "$1", era)
else
end
if isSet(i18n.categorylabels) and lang ~= uselang and uselang == wiki.langcode then
era = mw.ustring.gsub(mw.ustring.gsub(i18n.datetime.ad, '"', ""), "$1", era)
ret_icon = ret_icon .. '[[' .. i18n.categorylabels .. (lang and ']]' or '/Q]]')
end
end
return era
end
end
return ret_lang .. ret_icon
end


-- editicon values: true/false (no=false), right, void defaults to i18n.addpencil
local _date_format = i18n["datetime"]["format"][date_format]
-- labelicon only by parameter
if _date_format ~= nil then
local function setIcons(arg, parg)
-- check for precision is year and override supplied date_format
local val = arg == nil and parg or arg
if precision == 9 then
local edit_icon, label_icon
_date_format = i18n["datetime"][9]
if not isSet(val) then
end
edit_icon, label_icon = i18n.addpencil, true
return parseDateFormat(_date_format, timestamp, addon, prefix_addon, addon_sep)
elseif val == false or val == "false" or val == "no" then
edit_icon, label_icon = false, false
else
else
edit_icon, label_icon = val, true
return printError("unknown-datetime-format")
end
end
return edit_icon, label_icon
end
end


-- Add an icon for editing a statement with requirements for future Wikidata Bridge
-- the "qualifiers" and "snaks" field have a respective "qualifiers-order" and "snaks-order" field
local function addEditIcon(parameters)
-- use these as the second parameter and this function instead of the built-in "pairs" function
local ret = ''
-- to iterate over all qualifiers and snaks in the intended order.
if parameters.editicon and parameters.id and parameters.property then
local function orderedpairs(array, order)
local bridge_flow = parameters.editbridge and ' data-bridge-edit-flow="single-best-value"' or ''
if not order then return pairs(array) end
local icon_style = parameters.editicon == "right" and ' style="float: right;"' or ''

ret = ' <span class="penicon"' .. bridge_flow .. icon_style .. '>'
-- return iterator function
.. "[[File:OOjs UI icon edit-ltr-progressive.svg|10px|baseline|"
local i = 0
.. string.gsub(mw.message.new('Wikibase-client-data-bridge-bailout-suggestion-go-to-repo-button'):inLanguage(parameters.lang[1]):plain(), '{{WBREPONAME}}', 'Wikidata')
return function()
.. "|link=https://www.wikidata.org/wiki/" .. parameters.id .. "?uselang=" .. parameters.lang[1] .. "#" .. parameters.property .. "]]"
i = i + 1
.. "</span>"
if order[i] then
if isSet(i18n.categoryprop) then
return order[i], array[order[i]]
ret = ret .. "[[" .. string.gsub(i18n.categoryprop, '$1', parameters.property) .. "]]"
end
end
end
end
return ret
end
end
-- add edit icon to the last element of a table

local function addEditIconTable(thetable, parameters)
-- precision: 0 - billion years, 1 - hundred million years, ..., 6 - millennia, 7 - century, 8 - decade, 9 - year, 10 - month, 11 - day, 12 - hour, 13 - minute, 14 - second
if #thetable == 0 or parameters.editicon == false then
local function normalizeDate(date)
return thetable
date = mw.text.trim(date, "+")
end
-- extract year
local last_element = thetable[#thetable]
local yearstr = mw.ustring.match(date, "^\-?%d+")
local year = tonumber(yearstr)
local the_icon = addEditIcon(parameters)
-- add it before last html closing tags
-- remove leading zeros of year
local tags = ''
return year .. mw.ustring.sub(date, #yearstr + 1), year
local rev_element = string.reverse(last_element)
end
for tag in string.gmatch(rev_element, '(>%l+/<)') do

if string.match(rev_element, '^' .. tags .. tag) then
local function formatDate(date, precision, timezone)
tags = tags .. tag
precision = precision or 11
local date, year = normalizeDate(date)
if year == 0 and precision <= 9 then return "" end

-- precision is 10000 years or more
if precision <= 5 then
local factor = 10 ^ ((5 - precision) + 4)
local y2 = math.ceil(math.abs(year) / factor)
local relative = mw.ustring.gsub(i18n.datetime[precision], "$1", tostring(y2))
if year < 0 then
relative = mw.ustring.gsub(i18n.datetime.beforenow, "$1", relative)
else
else
break
relative = mw.ustring.gsub(i18n.datetime.afternow, "$1", relative)
end
end
return relative
end
end
local last_tags = string.reverse(tags)

local offset = string.find(last_element, last_tags .. '$')
-- precision is decades, centuries and millennia
if offset then
local era
thetable[#thetable] = string.sub(last_element, 1, offset - 1) .. the_icon .. last_tags
if precision == 6 then era = mw.ustring.gsub(i18n.datetime[6], "$1", tostring(math.floor((math.abs(year) - 1) / 1000) + 1)) end
else
if precision == 7 then era = mw.ustring.gsub(i18n.datetime[7], "$1", tostring(math.floor((math.abs(year) - 1) / 100) + 1)) end
thetable[#thetable] = last_element .. the_icon
if precision == 8 then era = mw.ustring.gsub(i18n.datetime[8], "$1", tostring(math.floor(math.abs(year) / 10) * 10)) end
if era then
if year < 0 then era = mw.ustring.gsub(mw.ustring.gsub(i18n.datetime.bc, '"', ""), "$1", era)
elseif year > 0 then era = mw.ustring.gsub(mw.ustring.gsub(i18n.datetime.ad, '"', ""), "$1", era) end
return era
end
end
return thetable
end


-- Escape Lua captures
-- precision is year
local function captureEscapes(text)
if precision == 9 then
return mw.ustring.gsub(text, "(%%%d)", "%%%1")
return year
end

-- precision is less than years
if precision > 9 then
--[[ the following code replaces the UTC suffix with the given negated timezone to convert the global time to the given local time
timezone = tonumber(timezone)
if timezone and timezone ~= 0 then
timezone = -timezone
timezone = string.format("%.2d%.2d", timezone / 60, timezone % 60)
if timezone[1] ~= '-' then timezone = "+" .. timezone end
date = mw.text.trim(date, "Z") .. " " .. timezone
end
]]--

local formatstr = i18n.datetime[precision]
if year == 0 then formatstr = mw.ustring.gsub(formatstr, i18n.datetime[9], "")
elseif year < 0 then
-- Mediawiki formatDate doesn't support negative years
date = mw.ustring.sub(date, 2)
formatstr = mw.ustring.gsub(formatstr, i18n.datetime[9], mw.ustring.gsub(i18n.datetime.bc, "$1", i18n.datetime[9]))
elseif year > 0 and i18n.datetime.ad ~= "$1" then
formatstr = mw.ustring.gsub(formatstr, i18n.datetime[9], mw.ustring.gsub(i18n.datetime.ad, "$1", i18n.datetime[9]))
end
return mw.language.new(wiki.langcode):formatDate(formatstr, date)
end
end
end


-- expandTemplate or callParserFunction
local function printDatavalueEntity(data, parameter)
local function expandBraces(text, formatting)
-- data fields: entity-type [string], numeric-id [int, Wikidata id]
if text == nil or formatting == nil then return text end
local id
-- 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 data["entity-type"] == "item" then id = "Q" .. data["numeric-id"]
if type(text) ~= "string" then
elseif data["entity-type"] == "property" then id = "P" .. data["numeric-id"]
text = tostring(text)
else return printError("unknown-entity-type")
end
end

for braces in mw.ustring.gmatch(text, "{{(.-)}}") do
if parameter then
local parts = mw.text.split(braces, "|")
if parameter == "link" then
local linkTarget = mw.wikibase.sitelink(id)
local title_part = parts[1]
local linkName = mw.wikibase.label(id)
local parameters = {}
for i = 2, #parts do
if linkTarget then
local subparts = mw.ustring.find(parts[i], "=")
-- if there is a local Wikipedia article link to it using the label or the article title
if subparts then
return "[[" .. linkTarget .. "|" .. (linkName or linkTarget) .. "]]"
local param_name = mw.ustring.sub(parts[i], 1, subparts - 1)
else
local param_value = mw.ustring.sub(parts[i], subparts + 1, -1)
-- if there is no local Wikipedia article output the label or link to the Wikidata object to let the user input a proper label
-- reconstruct broken links by parts
if linkName then return linkName else return "[[:d:" .. id .. "|" .. id .. "]]" end
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
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
else
braces_expanded = mw.getCurrentFrame():expandTemplate{title=title_part, args=parameters}
return data[parameter]
end
end
braces = mw.ustring.gsub(braces, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- escape magic characters
else
braces_expanded = captureEscapes(braces_expanded)
return mw.wikibase.label(id) or id
text = mw.ustring.gsub(text, "{{" .. braces .. "}}", braces_expanded)
end
end
return text
end
end


-- format data type math
local function printDatavalueTime(data, parameter)
local function printDatatypeMath(data)
-- data fields: time [ISO 8601 time], timezone [int in minutes], before [int], after [int], precision [int], calendarmodel [wikidata URI]
return mw.getCurrentFrame():callParserFunction('#tag:math', data)
-- precision: 0 - billion years, 1 - hundred million years, ..., 6 - millennia, 7 - century, 8 - decade, 9 - year, 10 - month, 11 - day, 12 - hour, 13 - minute, 14 - second
end
-- calendarmodel: e.g. http://www.wikidata.org/entity/Q1985727 for the proleptic Gregorian calendar or http://www.wikidata.org/wiki/Q11184 for the Julian calendar]

if parameter then
-- format data type musical-notation
if parameter == "calendarmodel" then data.calendarmodel = mw.ustring.match(data.calendarmodel, "Q%d+") -- extract entity id from the calendar model URI
local function printDatatypeMusical(data, formatting)
elseif parameter == "time" then data.time = normalizeDate(data.time) end
local attr = {}
return data[parameter]
if formatting == 'sound' then
else
attr.sound = 1
return formatDate(data.time, data.precision, data.timezone)
end
end
return mw.getCurrentFrame():extensionTag('score', data, attr)
end
end


-- format data type string
local function printDatavalueMonolingualText(data, parameter)
local function printDatatypeString(data, parameters)
-- data fields: language [string], text [string]
if mw.ustring.find((parameters.formatting or ''), '$1', 1, true) then -- formatting = a pattern
if parameter then
return expandBraces(mw.ustring.gsub(parameters.formatting, '$1', {['$1'] = data}), parameters.formatting)
return data[parameter]
elseif parameters.case then
else
return case(parameters.case, data, parameters.lang[1], feminineGender(parameters.id))
local result = mw.ustring.gsub(mw.ustring.gsub(i18n.monolingualtext, "%%language", data["language"]), "%%text", data["text"])
return result
end
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
end


-- format data type tabular-data
local function findClaims(entity, property)
local function printDatatypeTabular(data, parameters)
if not property or not entity or not entity.claims then return end
local icon

if mw.ustring.match(property, "^P%d+$") then
if parameters.formatting == 'raw' then
icon = "no-icon"
-- if the property is given by an id (P..) access the claim list by this id
data = string.gsub(data, '^Data:', '') -- remove prefix, i.e. see Module:Tabular data
return entity.claims[property]
else
property = mw.wikibase.resolvePropertyId(property)
if not property then return end

return entity.claims[property]
end
end
return printDatatypeString(data, parameters), icon
end
end


-- format data type url
local function getSnakValue(snak, parameter)
local function printDatatypeUrl(data, parameters)
if snak.snaktype == "value" then
if parameters.formatting == 'weblink' then
-- call the respective snak parser
local label_parts = mw.text.split(string.gsub(data, '/$', ''), '/')
if snak.datavalue.type == "string" then return snak.datavalue.value
local label = string.gsub(label_parts[3], '^www%.', '')
elseif snak.datavalue.type == "globecoordinate" then return printDatavalueCoordinate(snak.datavalue.value, parameter)
if #label_parts > 3 then
elseif snak.datavalue.type == "quantity" then return printDatavalueQuantity(snak.datavalue.value, parameter)
label = label .. '…'
elseif snak.datavalue.type == "time" then return printDatavalueTime(snak.datavalue.value, parameter)
elseif snak.datavalue.type == "wikibase-entityid" then return printDatavalueEntity(snak.datavalue.value, parameter)
elseif snak.datavalue.type == "monolingualtext" then return printDatavalueMonolingualText(snak.datavalue.value, parameter)
end
end
return '[' .. data .. ' ' .. label .. ']'
end
end
return mw.wikibase.renderSnak(snak)
return printDatatypeString(data, parameters)
end
end


-- format data type external-id
local function getQualifierSnak(claim, qualifierId)
local function printDatatypeExternal(data, parameters)
-- a "snak" is Wikidata terminology for a typed key/value pair
if parameters.formatting == 'externalid' then
-- a claim consists of a main snak holding the main information of this claim,
local p_stat = mw.wikibase.getBestStatements(parameters.property, 'P1630') -- formatter URL
-- as well as a list of attribute snaks and a list of references snaks
local p_link_pattern = getSnak(p_stat, {1, "mainsnak", "datavalue", "value"})
if qualifierId then
if p_link_pattern then
-- search the attribute snak with the given qualifier as key
local p_link = mw.ustring.gsub(p_link_pattern, '$1', {['$1'] = data})
if claim.qualifiers then
return '[' .. p_link .. ' ' .. data .. ']'
local qualifier = claim.qualifiers[qualifierId]
if qualifier then return qualifier[1] end
end
end
return nil, printError("qualifier-not-found")
else
-- otherwise return the main snak
return claim.mainsnak
end
end
return printDatatypeString(data, parameters)
end
end


-- format data type commonsMedia and geo-shape
local function getValueOfClaim(claim, qualifierId, parameter)
local function printDatatypeMedia(data, parameters)
local error
local snak
local icon
if not string.find((parameters.formatting or ''), '$1', 1, true) then
snak, error = getQualifierSnak(claim, qualifierId)
icon = "no-icon"
if snak then
if not string.find(data, '^Data:') then
return getSnakValue(snak, parameter)
data = mw.uri.encode(data, 'PATH') -- encode special characters in filename
else
end
return nil, error
end
end
return printDatatypeString(data, parameters), icon
end
end


-- format data type globe-coordinate
local function getReferences(frame, claim)
local function printDatatypeCoordinate(data, formatting)
local result = ""
local function globes(globe_id)
-- traverse through all references
local globes = {['Q3134']='callisto',['Q596']='ceres',['Q15040']='dione',['Q2']='earth',['Q3303']='enceladus',
for ref in pairs(claim.references or {}) do
['Q3143']='europa',['Q17975']='phoebe',['Q3169']='ganymede',['Q3123']='io',['Q17958']='iapetus',
local refparts
['Q308']='mercury',['Q15034']='mimas',['Q405']='moon',['Q15050']='rhea',['Q15047']='tethys',
-- traverse through all parts of the current reference
['Q111']='mars',['Q2565']='titan',['Q3359']='triton',['Q313']='venus',['Q3030']='vesta'}
for snakkey, snakval in orderedpairs(claim.references[ref].snaks or {}, claim.references[ref]["snaks-order"]) do
return globes[globe_id]
if refparts then refparts = refparts .. ", " else refparts = "" end
end
-- output the label of the property of the reference part, e.g. "imported from" for P143
refparts = refparts .. tostring(mw.wikibase.label(snakkey)) .. ": "
local function roundPrecision(num, prec)
-- output all values of this reference part, e.g. "German Wikipedia" and "English Wikipedia" if the referenced claim was imported from both sites
if prec == nil or prec <= 0 then return num end
for snakidx = 1, #snakval do
local sig = 10^math.floor(math.log10(prec)+.5) -- significant figure from sexagesimal precision: 0.00123 -> 0.001
if snakidx > 1 then refparts = refparts .. ", " end
return math.floor(num / sig + 0.5) * sig
refparts = refparts .. getSnakValue(snakval[snakidx])
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
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
if refparts then result = result .. frame:extensionTag("ref", refparts) end
end
end
return result
end
end


-- Local functions for data value quantity
local function parseInput(frame)
local function unitSymbol(id, lang) -- get unit symbol or code
local qid = frame.args.qid
local unit_symbol = ''
if qid and (#qid == 0) then qid = nil end
if lang == wiki.langcode and pcall(require, wiki.module_title .. "/Units") then
local propertyID = mw.text.trim(frame.args[1] or "")
unit_symbol = require(wiki.module_title .. "/Units").getUnit(0, '', id, true)
local input_parm = mw.text.trim(frame.args[2] or "")
if input_parm ~= "FETCH_WIKIDATA" then
return false, input_parm, nil, nil
end
end
if unit_symbol == '' then
local entity = mw.wikibase.getEntityObject(qid)
-- fetch it
local claims
local claims = mw.wikibase.getBestStatements(id, 'P5061')
if entity and entity.claims then
claims = entity.claims[propertyID]
if #claims > 0 then
local langclaims = {}
if not claims then
table.insert(lang, 'mul') -- multilingual as last try
return false, "", nil, nil
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
else
return false, "", nil, nil
end
end
return unit_symbol
return true, entity, claims, propertyID
end
end

local function isType(claims, type)
local function getUnit(amount, id, parameters) -- get unit symbol or name
return claims[1] and claims[1].mainsnak.snaktype == "value" and claims[1].mainsnak.datavalue.type == type
local suffix = ''
end
if string.sub(parameters.formatting or '', 1, 8) == "unitcode" then
local function getValue(entity, claims, propertyID, delim, labelHook)
-- get unit symbol
if labelHook == nil then
local unit_symbol = unitSymbol(id, parameters.lang)
labelHook = function (qnumber)
if isSet(unit_symbol) then
return nil;
if string.sub(parameters.formatting or '', -6) == "linked" then
end
suffix = "[[" .. (mw.wikibase.getSitelink(id) or "d:" .. id) .. "|" .. unit_symbol .. "]]"
end
if isType(claims, "wikibase-entityid") then
local out = {}
for k, v in pairs(claims) do
local qnumber = "Q" .. v.mainsnak.datavalue.value["numeric-id"]
local sitelink = mw.wikibase.sitelink(qnumber)
local label = labelHook(qnumber) or mw.wikibase.label(qnumber) or qnumber
if sitelink then
out[#out + 1] = "[[" .. sitelink .. "|" .. label .. "]]"
else
else
suffix = unit_symbol
out[#out + 1] = "[[:d:" .. qnumber .. "|" .. label .. "]]<abbr title='" .. i18n["errors"]["local-article-not-found"] .. "'>[*]</abbr>"
end
end
end
end
return table.concat(out, delim)
else
-- just return best values
return entity:formatPropertyValues(propertyID).value
end
end
if suffix == '' then -- formatting=unit, or formatting=unitcode not found
end
-- get unit label

local unit_label, lang = getLabelByLangs(id, parameters.lang)
------------------------------------------------------------------------------
if lang == wiki.langcode and pcall(require, wiki.module_title .. "/Units") then
-- module global functions
suffix = require(wiki.module_title .. "/Units").getUnit(amount, unit_label, id, false)

if string.sub(parameters.formatting or '', -6) == "linked" then
if debug then
suffix = "[[" .. (mw.wikibase.getSitelink(id) or "d:" .. id) .. "|" .. suffix .. "]]"
function p.inspectI18n(frame)
end
local val = i18n
else
for _, key in pairs(frame.args) do
suffix = (unit_label or id) .. addLabelIcon(id, lang, parameters.lang[1], parameters.labelicon)
key = mw.text.trim(key)
val = val[key]
end
end
return val
end
end
if suffix ~= '' then
suffix = ' ' .. suffix
end
return suffix
end
end


function p.descriptionIn(frame)
local function roundDefPrecision(in_num, factor)
-- rounds out_num with significant figures of in_num (default precision)
local langcode = frame.args[1]
local id = frame.args[2]
local out_num = in_num * factor
if factor/60 == math.floor(factor/60) or out_num == 0 then -- sexagesimal integer or avoiding NaN
-- return description of a Wikidata entity in the given language or the default language of this Wikipedia site
return out_num
return mw.wikibase.getEntityObject(id).descriptions[langcode or wiki.langcode].value
end

function p.labelIn(frame)
local langcode = frame.args[1]
local id = frame.args[2]
-- return label of a Wikidata entity in the given language or the default language of this Wikipedia site
return mw.wikibase.getEntityObject(id).labels[langcode or wiki.langcode].value
end

-- This is used to get a value, or a comma separated list of them if multiple values exist
p.getValue = function(frame)
local delimdefault = ", " -- **internationalise later**
local delim = frame.args.delimiter or ""
delim = string.gsub(delim, '"', '')
if #delim == 0 then
delim = delimdefault
end
end
-- first, count digits after decimal mark, handling cases like '12.345e6'
local go, errorOrentity, claims, propertyID = parseInput(frame)
local exponent, prec
if not go then
local integer, dot, decimals, expstr = in_num:match('^(%d*)(%.?)(%d*)(.*)')
return errorOrentity
local e = expstr:sub(1, 1)
if e == 'e' or e == 'E' then
exponent = tonumber(expstr:sub(2))
end
end
if dot == '' then
return getValue(errorOrentity, claims, propertyID, delim)
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
end


-- format data type quantity
-- Same as above, but uses the short name property for label if available.
local function printDatatypeQuantity(data, parameters)
p.getValueShortName = function(frame)
local amount = data.amount
local go, errorOrentity, claims, propertyID = parseInput(frame)
amount = mw.ustring.gsub(amount, "%+", "")
if not go then
local suffix = ""
return errorOrentity
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
if conv_amount and parameters.formatting == 'raw' then
amount = conv_amount
suffix = ""
conv_amount = nil
end
end
end
end
local lang_obj = mw.language.new(parameters.lang[1])
local entity = errorOrentity
local sortkey = string.format("%019d", tonumber(amount) * 1000)
-- if wiki-linked value output as link if possible
if string.sub(parameters.formatting or '', 1, 8) == "duration" then
local function labelHook (qnumber)
local sec = tonumber(conv_amount or amount)
local label
if parameters.formatting == 'duration' then
local claimEntity = mw.wikibase.getEntity(qnumber)
return lang_obj:formatDuration(sec)
if claimEntity ~= nil then
elseif parameters.formatting == 'durationm:s' then
if claimEntity.claims.P1813 then
local mm = math.floor(sec / 60)
for k2, v2 in pairs(claimEntity.claims.P1813) do
local ss = sec - (mm * 60)
if v2.mainsnak.datavalue.value.language == "en" then
return string.format("%02d:%02d", mm, ss)
label = v2.mainsnak.datavalue.value.text
else -- durationhms or durationh:m:s
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
end
elseif sec2table[v] then
duration = duration .. lang_obj:formatNum(sec2table[v]) .. i18n.datetime.hms[v] .. (i < 3 and " " or "")
end
end
end
end
return duration
end
end
if label == nil or label == "" then return nil end
return label
end
end
if parameters.case then
return getValue(errorOrentity, claims, propertyID, ", ", labelHook);
amount = case(parameters.case, amount, parameters.lang[1], feminineGender(parameters.id))
end
elseif parameters.formatting ~= 'raw' then

if parameters.numformat then
-- This is used to get a value, or a comma separated list of them if multiple values exist
amount = lang_obj:formatNum(tonumber(string.format(parameters.numformat, amount)))
-- from an arbitrary entry by using its QID.
else
-- Use : {{#invoke:Wikidata|getValueFromID|<ID>|<Property>|FETCH_WIKIDATA}}
amount = lang_obj:formatNum(tonumber(amount))
-- E.g.: {{#invoke:Wikidata|getValueFromID|Q151973|P26|FETCH_WIKIDATA}} - to fetch value of 'spouse' (P26) from 'Richard Burton' (Q151973)
-- Please use sparingly - this is an *expensive call*.
p.getValueFromID = function(frame)
local itemID = mw.text.trim(frame.args[1] or "")
local propertyID = mw.text.trim(frame.args[2] or "")
local input_parm = mw.text.trim(frame.args[3] or "")
if input_parm == "FETCH_WIKIDATA" then
local entity = mw.wikibase.getEntity(itemID)
local claims
if entity and entity.claims then
claims = entity.claims[propertyID]
end
end
end
if claims then
if conv_amount then
return getValue(entity, claims, propertyID, ", ")
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
else
return conv_amount .. conv_suffix, conv_sortkey
return ""
end
end
elseif mw.ustring.find((parameters.formatting or ''), '$1', 1, true) then -- formatting with pattern
else
amount = mw.ustring.gsub(parameters.formatting, '$1', {['$1'] = amount})
return input_parm
end
end
return amount .. suffix, sortkey
end
end

local function getQualifier(frame, outputHook)
-- format data type time
local propertyID = mw.text.trim(frame.args[1] or "")
local function printDatatypeTime(data, parameters)
local qualifierID = mw.text.trim(frame.args[2] or "")
-- Dates and times are stored in ISO 8601 format
local input_parm = mw.text.trim(frame.args[3] or "")
local timestamp = data.time
if input_parm == "FETCH_WIKIDATA" then
local post_format
local entity = mw.wikibase.getEntityObject()
local calendar_add = ""
if entity.claims[propertyID] ~= nil then
local out = {}
local precision = data.precision or 11
for k, v in pairs(entity.claims[propertyID]) do
if string.sub(timestamp, 1, 1) == '-' then
for k2, v2 in pairs(v.qualifiers[qualifierID]) do
post_format = i18n.datetime["bc"]
if v2.snaktype == 'value' then
elseif string.sub(timestamp, 2, 3) == '00' then
out[#out + 1] = outputHook(v2);
post_format = i18n.datetime["ad"]
end
elseif precision > 8 then
end
-- calendar model
end
local calendar_model = {["Q12138"] = "gregorian", ["Q1985727"] = "gregorian", ["Q11184"] = "julian", ["Q1985786"] = "julian"}
return table.concat(out, ", "), true
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 = " <sup>(" .. mw.message.new('Wikibase-time-calendar-' .. calendar_model[calendar_id]):inLanguage(parameters.lang[1]):plain() .. ")</sup>"
end
end
local function formatTime(form, stamp)
local pattern
if type(form) == "function" then
pattern = form(stamp)
else
else
return "", false
pattern = form
end
end
stamp = tostring(stamp)
else
if mw.ustring.find(pattern, "$1") then
return input_parm, false
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") -- suppress leading zeros
ret = string.gsub(ret, "( %[?%[?)0+", "%1")
return ret
end
end
end
p.getQualifierValue = function(frame)
local function postFormat(t)
if post_format and mw.ustring.find(post_format, "$1") then
local function outputValue(value)
return mw.ustring.gsub(post_format, "$1", t)
local qnumber = "Q" .. value.datavalue.value["numeric-id"]
end
if (mw.wikibase.sitelink(qnumber)) then
return t
return "[[" .. mw.wikibase.sitelink(qnumber) .. "]]"
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
else
ret = mw.ustring.gsub(i18n.datetime.afternow, "$1", relative)
return "[[:d:" .. qnumber .. "|" ..qnumber .. "]]<abbr title='" .. i18n["errors"]["local-article-not-found"] .. "'>[*]</abbr>"
end
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
end
return (getQualifier(frame, outputValue))
return ret, timestamp
end
end


-- format data value wikibase-entityid with data types wikibase-item or wikibase-property
-- This is used to get a value like 'male' (for property p21) which won't be linked and numbers without the thousand separators
local function printDatatypeEntity(data, parameters)
p.getRawValue = function(frame)
local entity_id = data['id']
local go, errorOrentity, claims, propertyID = parseInput(frame)
if not go then
if parameters.formatting == 'raw' then
return errorOrentity
return entity_id, entity_id
end
end
local entity_page = 'Special:EntityPage/' .. entity_id
local entity = errorOrentity
local label, lang = getLabelByLangs(entity_id, parameters.lang)
local result = entity:formatPropertyValues(propertyID, mw.wikibase.entity.claimRanks).value
local sitelink = mw.wikibase.getSitelink(entity_id)
-- if number type: remove thousand separators, bounds and units
local parameter = parameters.formatting
if isType(claims, "quantity") then
local labelcase = label or sitelink
result = mw.ustring.gsub(result, "(%d),(%d)", "%1%2")
if parameters.gender == 'feminineform' then
result = mw.ustring.gsub(result, "(%d)±.*", "%1")
labelcase = feminineForm(entity_id, lang) or labelcase
end
end
if parameters.case ~= 'gender' then
return result
labelcase = case(parameters.case, labelcase, lang, parameters.lang[1], entity_id, parameters.id)
end
if labelcase == nil and i18n.qidlabels == false then
return
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 .. '|<span style="color:#5f9cbb;">' .. (labelcase or entity_id) .. '</span>]]'
ret2 = labelcase or entity_id
end
end
return ret1 .. addLabelIcon(entity_id, lang, parameters.lang[1], parameters.labelicon), ret2
end
end


-- format data type wikibase-lexeme
-- This is used to get the unit name for the numeric value returned by getRawValue
local function printDatatypeLexeme(data, parameters)
p.getUnits = function(frame)
local entity_id = data['id']
local go, errorOrentity, claims, propertyID = parseInput(frame)
if not go then
if parameters.formatting == 'raw' then
return errorOrentity
return entity_id, entity_id
end
end
local lemmas = mw.wikibase.getEntity(entity_id):getLemmas()
local entity = errorOrentity
if parameters.list == 'lang' and lemmas[1][2] ~= parameters.lang[1] then
local result = entity:formatPropertyValues(propertyID, mw.wikibase.entity.claimRanks).value
return
if isType(claims, "quantity") then
result = mw.ustring.sub(result, mw.ustring.find(result, " ")+1, -1)
end
end
local ret = '[[d:Special:EntityPage/' .. entity_id .. '|<span style="color:#5f9cbb;">' .. lemmas[1][1] .. '</span>]]'
return result
if parameters.list ~= 'lang' or (parameters.list == 'lang' and lemmas[1][2] ~= wiki.langcode) then
ret = ret .. " <sup>(" .. lemmas[1][2] .. ")</sup>"
end
return ret, entity_id
end
end


-- format data type monolingualtext
-- This is used to get the unit's QID to use with the numeric value returned by getRawValue
local function printDatatypeMonolingual(data, parameters)
p.getUnitID = function(frame)
-- data fields: language [string], text [string]
local go, errorOrentity, claims = parseInput(frame)
if not go then
if parameters.list == "lang" and data["language"] ~= parameters.lang[1] then
return errorOrentity
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
end
local entity = errorOrentity
local result = data["text"]
if data["language"] ~= wiki.langcode then
local result
result = mw.ustring.gsub('<span lang="$1">$2</span>', '$[12]', {["$1"]=data["language"], ["$2"]=data["text"]})
if isType(claims, "quantity") then
end
-- get the url for the unit entry on Wikidata:
if mw.ustring.find((parameters.formatting or ''), '$', 1, true) then
result = claims[1].mainsnak.datavalue.value.unit
-- output format defined with $text, $language
-- and just reurn the last bit from "Q" to the end (which is the QID):
result = mw.ustring.sub(result, mw.ustring.find(result, "Q"), -1)
result = mw.ustring.gsub(parameters.formatting, '$text', result)
result = mw.ustring.gsub(result, '$language', data["language"])
end
end
return result
return result
end
end


local function getSnakValue(snak, parameters)
p.getRawQualifierValue = function(frame)
parameters.editbridge = false
local function outputHook(value)
if snak.snaktype == 'value' then -- see Special:ListDatatypes
if value.datavalue.value["numeric-id"] then
-- data value string
return mw.wikibase.label("Q" .. value.datavalue.value["numeric-id"])
if snak.datatype == "string" then
else
parameters.editbridge = true -- Wikidata Bridge currently only for string values
return value.datavalue.value
return printDatatypeString(snak.datavalue.value, parameters)
elseif snak.datatype == "commonsMedia" or snak.datatype == "geo-shape" then
return printDatatypeMedia(snak.datavalue.value, parameters)
elseif snak.datatype == "tabular-data" then
return printDatatypeTabular(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)
-- data types other than string value
elseif snak.datatype == 'wikibase-item' or snak.datatype == 'wikibase-property' then
if i18n.suppress[snak.datavalue.value.id] then
return
end
return printDatatypeEntity(snak.datavalue.value, parameters)
elseif snak.datatype == 'wikibase-lexeme' then
return printDatatypeLexeme(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
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' or parameters.showsomevalue == false then return end
return mw.message.new('Wikibase-snakview-snaktypeselector-somevalue'):inLanguage(parameters.lang[1]):plain()
end
end
return mw.wikibase.renderSnak(snak)
local ret, gotData = getQualifier(frame, outputHook)
if gotData then
ret = string.upper(string.sub(ret, 1, 1)) .. string.sub(ret, 2)
end
return ret
end
end


local function printError(key)
-- This is used to get a date value for date_of_birth (P569), etc. which won't be linked
return '<span class="error">' .. i18n.errors[key] .. '</span>'
-- Dates and times are stored in ISO 8601 format (sort of).
end
-- At present the local formatDate(date, precision, timezone) function doesn't handle timezone

-- So I'll just supply "Z" in the call to formatDate below:
local function getQualifierSnak(claim, qualifierId, parameters)
p.getDateValue = function(frame)
-- a "snak" is Wikidata terminology for a typed key/value pair
local date_format = mw.text.trim(frame.args[3] or i18n["datetime"]["default-format"])
-- a claim consists of a main snak holding the main information of this claim,
local date_addon = mw.text.trim(frame.args[4] or i18n["datetime"]["default-addon"])
-- as well as a list of attribute snaks and a list of references snaks
local go, errorOrentity, claims = parseInput(frame)
if not go then
if qualifierId then
-- search the attribute snak with the given qualifier as key
return errorOrentity
if claim.qualifiers then
end
local qualifier = claim.qualifiers[qualifierId]
local entity = errorOrentity
if qualifier then
local out = {}
if qualifier[1].datatype == "monolingualtext" then
for k, v in pairs(claims) do
-- iterate over monolingualtext qualifiers to get local language
if v.mainsnak.datavalue.type == 'time' then
for idx in pairs(qualifier) do
local timestamp = v.mainsnak.datavalue.value.time
if getSnak(qualifier[idx], {"datavalue", "value", "language"}) == parameters.lang[1] then
local dateprecision = v.mainsnak.datavalue.value.precision
return qualifier[idx]
-- A year can be stored like this: "+1872-00-00T00:00:00Z",
end
-- which is processed here as if it were the day before "+1872-01-01T00:00:00Z",
end
-- and that's the last day of 1871, so the year is wrong.
elseif parameters.list then
-- So fix the month 0, day 0 timestamp to become 1 January instead:
return qualifier
timestamp = timestamp:gsub("%-00%-00T", "-01-01T")
else
out[#out + 1] = parseDateFull(timestamp, dateprecision, date_format, date_addon)
return qualifier[1]
end
end
end
end
return nil, printError("qualifier-not-found")
else
-- otherwise return the main snak
return claim.mainsnak
end
end
return table.concat(out, ", ")
end
end

p.getQualifierDateValue = function(frame)
local function getValueOfClaim(claim, qualifierId, parameters)
local date_format = mw.text.trim(frame.args[4] or i18n["datetime"]["default-format"])
local snak, error = getQualifierSnak(claim, qualifierId, parameters)
local date_addon = mw.text.trim(frame.args[5] or i18n["datetime"]["default-addon"])
if not snak then
local function outputHook(value)
return nil, nil, error
local timestamp = value.datavalue.value.time
elseif snak[1] then -- a multi qualifier
return parseDateValue(timestamp, date_format, date_addon)
local result, sortkey = {}, {}
local maxvals = tonumber(parameters.listmax)
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
return (getQualifier(frame, outputHook))
end
end


local function getValueOfParentClaim(claim, qualifierId, parameters)
-- This is used to fetch all of the images with a particular property, e.g. image (P18), Gene Atlas Image (P692), etc.
local qids = mw.text.split(qualifierId, '/', true)
-- Parameters are | propertyID | value / FETCH_WIKIDATA / nil | separator (default=space) | size (default=frameless)
local value, sortkey, valueraw = {}, {}, {}
-- It will return a standard wiki-markup [[File:Filename | size]] for each image with a selectable size and separator (which may be html)
local parent_raw, value_text
-- e.g. {{#invoke:Wikidata|getImages|P18|FETCH_WIKIDATA}}
if qids[1] == parameters.property then
-- e.g. {{#invoke:Wikidata|getImages|P18|FETCH_WIKIDATA|<br>|250px}}
parent_raw, _, _ = getValueOfClaim(claim, nil, {["formatting"]="raw", ["lang"]=parameters.lang})
-- If a property is chosen that is not of type "commonsMedia", it will return empty text.
p.getImages = function(frame)
local sep = mw.text.trim(frame.args[3] or " ")
local imgsize = mw.text.trim(frame.args[4] or "frameless")
local go, errorOrentity, claims = parseInput(frame)
if not go then
return errorOrentity
end
local entity = errorOrentity
if (claims[1] and claims[1].mainsnak.datatype == "commonsMedia") then
local out = {}
for k, v in pairs(claims) do
local filename = v.mainsnak.datavalue.value
out[#out + 1] = "[[File:" .. filename .. "|" .. imgsize .. "]]"
end
return table.concat(out, sep)
else
else
parent_raw, _, _ = getValueOfClaim(claim, qids[1], {["formatting"]="raw", ["lang"]=parameters.lang, ["list"]=true, ["qseparator"]='/', ["qconjunction"]='/'})
return ""
end
end
if string.sub(parent_raw or '', 1, 1) == "Q" then -- protection for 'no value'
end
local parent_qids = mw.text.split(parent_raw, '/', true)

for idx, p_qid in ipairs(parent_qids) do
-- This is used to get the TA98 (Terminologia Anatomica first edition 1998) values like 'A01.1.00.005' (property P1323)
local parent_claims = mw.wikibase.getBestStatements(p_qid, qids[2])
-- 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
if parent_claims[1] then
-- uses the newer mw.wikibase calls instead of directly using the snaks
value[idx], sortkey[idx], _ = getValueOfClaim(parent_claims[1], nil, parameters)
-- 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
-- raw parent value needed for while/black lists, lang for avoiding an error on types other than entity
p.getTAValue = function(frame)
valueraw[idx], _, _ = getValueOfClaim(parent_claims[1], nil, {["formatting"]="raw", ["lang"]=parameters.lang})
local ent = mw.wikibase.getEntityObject()
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
end
end
end
if value[1] then
local ret = table.concat(out, "<br> ")
value_text = mw.text.listToText(value, parameters.qseparator, parameters.qconjunction)
if #ret == 0 then
ret = "Invalid TA"
end
end
return value_text, sortkey[1], valueraw[1]
return ret
end
end


-- see d:Help:Sources
--[[
local function getReferences(claim, parameters)
This is used to return an image legend from Wikidata
if not (parameters.references or parameters.onlysourced) then
image is property P18
return '', false
image legend is property P2096

Call as {{#invoke:Wikidata |getImageLegend | <PARAMETER> | lang=<ISO-639code> |id=<QID>}}
Returns PARAMETER, unless it is equal to "FETCH_WIKIDATA", from Item QID (expensive call)
If QID is omitted or blank, the current article is used (not an expensive call)
If lang is omitted, it uses the local wiki language, otherwise it uses the provided ISO-639 language code
ISO-639: https://docs.oracle.com/cd/E13214_01/wli/docs92/xref/xqisocodes.html#wp1252447

Ranks are: 'preferred' > 'normal'
This returns the label from the first image with 'preferred' rank
Or the label from the first image with 'normal' rank if preferred returns nothing
Ranks: https://www.mediawiki.org/wiki/Extension:Wikibase_Client/Lua
]]

p.getImageLegend = function(frame)
-- look for named parameter id; if it's blank make it nil
local id = frame.args.id
if id and (#id == 0) then
id = nil
end
end
local lang = parameters.lang

local maxrefs = tonumber(parameters.references) or 1
-- look for named parameter lang
local notproperref = {
-- it should contain a two-character ISO-639 language code
["P143"] = true, -- imported from
-- if it's blank fetch the language of the local wiki
["P3452"] = true, -- inferred from
local lang = frame.args.lang
["P887"] = true, -- based on heuristic
if (not lang) or (#lang < 2) then
["P4656"] = true -- Wikimedia import URL
lang = mw.language.getContentLanguage().code
end
}
local result = {}

-- traverse through all references
-- first unnamed parameter is the local parameter, if supplied
for ref in pairs(claim.references or {}) do
local input_parm = mw.text.trim(frame.args[1] or "")
local refparts
if input_parm == "FETCH_WIKIDATA" then
local ent = mw.wikibase.getEntityObject(id)
local refs = {}
local imgs
local validref = true
local ref_id
if ent and ent.claims then
-- traverse through all parts of the current reference
imgs = ent.claims.P18
for snakkey, snakval in pairs(claim.references[ref].snaks or {}) do
end
for partkey, _ in pairs(claim.references[ref].snaks[snakkey] or {}) do
local imglbl
if notproperref[snakkey] then -- not a proper reference
if imgs then
validref = false
-- look for an image with 'preferred' rank
break
for k1, v1 in pairs(imgs) do
if v1.rank == "preferred" and v1.qualifiers and v1.qualifiers.P2096 then
local imglbls = v1.qualifiers.P2096
for k2, v2 in pairs(imglbls) do
if v2.datavalue.value.language == lang then
imglbl = v2.datavalue.value.text
break
end
end
end
end
end
end
if validref then
-- if we don't find one, look for an image with 'normal' rank
for snakidx = 1, #snakval do
if (not imglbl) then
if snakidx > 1 then refparts = refparts .. ", " end
for k1, v1 in pairs(imgs) do
if snakval[snakidx].datatype == 'external-id' then
if v1.rank == "normal" and v1.qualifiers and v1.qualifiers.P2096 then
refparts = refparts or '' .. (getSnakValue(snakval[snakidx], {formatting='externalid', property=snakval[snakidx].property, lang=lang}) or '')
local imglbls = v1.qualifiers.P2096
else
for k2, v2 in pairs(imglbls) do
refparts = refparts or '' .. (getSnakValue(snakval[snakidx], {lang=lang}) or '')
if v2.datavalue.value.language == lang then
imglbl = v2.datavalue.value.text
break
end
end
end
end
end
refs[snakkey] = refparts
refparts = nil
if snakkey == "P248" then -- stated in
ref_id = getSnak(snakval, {1, "datavalue", "value", "id"})
end
end
end
end
end
end
return imglbl
-- fill missing values with parent item
else
if ref_id then
return input_parm
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
end
if #result > 0 then
if parameters.references then
if isSet(i18n.categoryref) then
result[#result + 1] = "[[" ..i18n.categoryref .. "]]"
end
return table.concat(result), true
else
return '', true
end
end
return '', false
end
end


-- Set whitelist or blacklist values
-- This is used to get the QIDs of all of the values of a property, as a comma separated list if multiple values exist
local function setWhiteOrBlackList(num_qual, args)
-- Usage: {{#invoke:Wikidata |getPropertyIDs |<PropertyID> |FETCH_WIKIDATA}}
local lists = {['whitelist']={}, ['blacklist']={}, ['ignorevalue']={}, ['selectvalue']={}}
-- Usage: {{#invoke:Wikidata |getPropertyIDs |<PropertyID> |<InputParameter> |qid=<QID>}}
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)
p.getPropertyIDs = function(frame)
pargs = pargs or {}
local go, errorOrentity, propclaims = parseInput(frame)
local id = args.item or args.from or (unnamed and mw.text.trim(args[1] or '') or nil)
if not go then
if not isSet(id) then
return errorOrentity
id = pargs.item or pargs.from or (unnamed and mw.text.trim(pargs[1] or '') or nil)
end
end
if isSet(id) then
local entity = errorOrentity
if string.find(id, ":") then -- remove prefix as Property:Pid
-- if wiki-linked value collect the QID in a table
id = mw.text.split(id, ":")[2]
if (propclaims[1] and propclaims[1].mainsnak.snaktype == "value" and propclaims[1].mainsnak.datavalue.type == "wikibase-entityid") then
local out = {}
for k, v in pairs(propclaims) do
out[#out + 1] = "Q" .. v.mainsnak.datavalue.value["numeric-id"]
end
end
return table.concat(out, ", ")
else
else
-- not a wikibase-entityid, so return empty
id = mw.wikibase.getEntityIdForCurrentPage()
return ""
end
end
return id
end
end


local function getArg(value, default, aliases)
-- returns the page id (Q...) of the current page or nothing of the page is not connected to Wikidata
if type(value) == 'boolean' then return value
function p.pageId(frame)
elseif value == "false" or value == "no" then return false
local entity = mw.wikibase.getEntityObject()
elseif value == "true" or value == "yes" then return true
if not entity then return nil else return entity.id end
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
end


-- Main function claim ---------------------------------------------
-- on debug console use: =p.claim{item="Q...", property="P...", ...}
function p.claim(frame)
function p.claim(frame)
local property = frame.args[1] or ""
local args = frame.args or frame -- via invoke or require
local id = frame.args["id"]
local pargs = frame.args and frame:getParent().args or {}
local qualifierId = frame.args["qualifier"]
local is_sandbox = isSet(pargs.sandbox)
if not required and is_sandbox then
local parameter = frame.args["parameter"]
return require(wiki.module_title .. "/" .. mw.message.new('Sandboxlink-subpage-name'):inLanguage(wiki.langcode):plain()).claim(frame)
local list = frame.args["list"]
end
local references = frame.args["references"]
--If a value is already set, use it
local showerrors = frame.args["showerrors"]
if isSet(args.value) then
local default = frame.args["default"]
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 args.qualifier1 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.numformat = getArg(args.numformat)
parameters.case = args.case
parameters.list = getArg(args.list, true, {firstrank='bestrank'})
parameters.listmax = args.listmax
parameters.listrank = getArg(args.listrank)
if type(parameters.list) == "number" then -- backwards compatibility
parameters.listmax = parameters.listmax or parameters.list
parameters.list = true
elseif parameters.list == "bestrank" then
parameters.listrank = parameters.listrank or "bestrank"
parameters.list = true
end
parameters.shownovalue = getArg(args.shownovalue, true)
parameters.showsomevalue = getArg(args.showsomevalue, true)
parameters.separator = getArg(args.separator)
parameters.conjunction = getArg(args.conjunction, parameters.separator)
parameters.qseparator = getArg(args.qseparator, parameters.separator)
parameters.qconjunction = getArg(args.qconjunction, parameters.conjunction)
local sorting_col = args.tablesort
local sorting_up = (args.sorting or "") ~= "-1"
local rowformat = args.rowformat
parameters.references = getArg(args.references, false)
parameters.onlysourced = getArg(args.onlysourced, false)
local showerrors = args.showerrors
local default = args.default
if default then showerrors = nil end
if default then showerrors = nil end
parameters.lang = findLang(args.lang)

if parameters.formatting == "raw" then
-- get wikidata entity
parameters.editicon, parameters.labelicon = false, false
local entity = mw.wikibase.getEntityObject(id)
else
if not entity then
parameters.editicon, parameters.labelicon = setIcons(args.editicon, pargs.editicon) -- needs loadI18n by findLand
if showerrors then return printError("entity-not-found") else return default end
end
end
-- fetch the first claim of satisfying the given property
local claims = findClaims(entity, property)
-- fetch property
if not claims or not claims[1] then
local claims = {}
local bestrank = parameters.listrank == 'bestrank' and parameters.list ~= 'lang'
if showerrors then return printError("property-not-found") else return default end
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
end
if #claims == 0 then

local ret = showerrors and printError("property-not-found") or default
return ret, args.query == 'num' and 0 or ''
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 "<br />"
parameters.conjunction = parameters.conjunction or "<br />"
parameters.qseparator = getArg(args.qseparator, mw.message.new('Comma-separator'):inLanguage(parameters.lang[1]):plain())
parameters.qconjunction = getArg(args.qconjunction, parameters.qseparator)
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 = "</li><li>"
parameters.conjunction = "</li><li>"
if mw.ustring.match(rowformat, "^[*#]") == "*" then
preformat = "<ul><li>"
postformat = "</li></ul>"
else
preformat = "<ol><li>"
postformat = "</li></ol>"
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
-- get initial sort indices
local sortindices = {}
local sortindices = {}
Baris 942: Baris 1.373:
end
end
table.sort(sortindices, comparator)
table.sort(sortindices, comparator)

local result
local result, result2, result_query
local error
local error
if list then
if parameters.list or parameters.formatting == "table" then
-- convert LF to line feed, <br /> may not work on some cases
local value
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)
-- iterate over all elements and return their value (if existing)
local value, valueq
result = {}
local sortkey, sortkeyq
local values = {}
local sortkeys = {}
local refs = {}
local rowlist = {} -- rows to list with whitelist or blacklist
for idx in pairs(claims) do
for idx in pairs(claims) do
local claim = claims[sortindices[idx]]
local claim = claims[sortindices[idx]]
local reference = {}
value, error = getValueOfClaim(claim, qualifierId, parameter)
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
-- only first value of a property as alternative to a qualifier
-- multiple values may not be related to a given raw of the table
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, ["list"]=params.list})
if valueq then
if whitelist[j] then
for k, v in pairs(whitelist[j]) do
if v and string.find(valueq, k, 1, true) then
rowlist[#values] = true
end
end
elseif blacklist[j] then
for k, v in pairs(blacklist[j]) do
if v and string.find(valueq, k, 1, true) then
rowlist[#values] = false
end
end
elseif ignorevalue[j] then
for k, v in pairs(ignorevalue[j]) do
if v and string.find(valueq, k, 1, true) then
values[#values]["col" .. j] = nil
end
end
elseif selectvalue[j] then
local selected
for k, v in pairs(selectvalue[j]) do
if v and string.find(valueq, k, 1, true) then
selected = true
end
end
if selected == nil then
values[#values]["col" .. j] = nil
end
end
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 not value and showerrors then value = error end
if value then
if value and references then value = value .. getReferences(frame, claim) end
if (parameters.references or parameters.onlysourced) and claim.references then
result[#result + 1] = value
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.listmax)
result = {}
for idx in pairs(values) do
local valuerow = values[sortindices[idx]]
local reference, valid_ref = 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) and (not parameters.onlysourced or (parameters.onlysourced and valid_ref)) 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 = #result
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
end
result = table.concat(result, list)
else
else
-- return first element
-- return first element
local claim = claims[sortindices[1]]
local claim = claims[sortindices[1]]
result, error = getValueOfClaim(claim, qualifierId, parameter)
result, result2, error = getValueOfClaim(claim, qualifierId[1], parameters)
if result then
if result and references then result = result .. getReferences(frame, claim) end
local ref, valid_ref = getReferences(claim, parameters)
end
if parameters.onlysourced and valid_ref == false then

if result then return result else
result = nil
if showerrors then return error else return default end
end
end

-- look into entity object
function p.ViewSomething(frame)
local f = (frame.args[1] or frame.args.id) and frame or frame:getParent()
local id = f.args.id
if id and (#id == 0) then
id = nil
end
local data = mw.wikibase.getEntityObject(id)
if not data then
return nil
end

local i = 1
while true do
local index = f.args[i]
if not index then
if type(data) == "table" then
return mw.text.jsonEncode(data, mw.text.JSON_PRESERVE_KEYS + mw.text.JSON_PRETTY)
else
else
result = result .. ref
return tostring(data)
end
end
end
end
if args.query == 'num' then result_query = result and 1 or 0 end

end
data = data[index] or data[tonumber(index)]
if not data then
if isSet(result) then
return
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
end
else

if showerrors then result = error else result = default end
i = i + 1
end
end
if args.query == 'untranslated' and required and not is_sandbox then
end
result_query = untranslated

-- getting sitelink of a given wiki
-- get sitelink of current item if qid not supplied
function p.getSiteLink(frame)
local qid = frame.args.qid
if qid == "" then qid = nil end
local f = mw.text.trim( frame.args[1] or "")
local entity = mw.wikibase.getEntity(qid)
if not entity then
return
end
local link = entity:getSitelink( f )
if not link then
return
end
end
return link
return result, result_query or ''
end
end


-- Local functions for getParentValues -----------------------
function p.Dump(frame)
local f = (frame.args[1] or frame.args.id) and frame or frame:getParent()
local data = mw.wikibase.getEntityObject(f.args.id)
if not data then
return i18n.warnDump
end


local i = 1
local function uc_first(word)
if word == nil then return end
while true do
return mw.ustring.upper(mw.ustring.sub(word, 1, 1)) .. mw.ustring.sub(word, 2)
local index = f.args[i]
if not index then
return "<pre>"..mw.dumpObject(data).."</pre>".. i18n.warnDump
end

data = data[index] or data[tonumber(index)]
if not data then
return i18n.warnDump
end

i = i + 1
end
end
end


local function getPropertyValue(id, property, parameter, langs, labelicon, case)

local function getPropertyValue(id, property, parameter, langs, editicon)
local snaks = mw.wikibase.getBestStatements(id, property)
local snaks = mw.wikibase.getBestStatements(id, property)
local mysnak
local mysnak = getSnak(snaks, {1, "mainsnak"})
if snaks and snaks[1] and snaks[1].mainsnak then
if mysnak == nil then
mysnak = snaks[1].mainsnak
else
return
return
end
end
local entityId
local entity_id
local result = '-' -- default for 'no value'
local result = '-' -- default for 'no value'
if mysnak.datavalue then
if mysnak.datavalue then
entityId = "Q" .. tostring(mysnak.datavalue.value['numeric-id'])
entity_id = "Q" .. tostring(mysnak.datavalue.value['numeric-id'])
result, _ = getSnakValue(mysnak, {formatting=parameter, lang=langs, editicon=editicon})
result, _ = getSnakValue(mysnak, {formatting=parameter, lang=langs, labelicon=labelicon, case=case})
end
end
return entityId, result
return entity_id, result
end
end


function p.getParentValues(frame)
local function getParentObjects(id,
prop_format,
local args = frame.args
label_format,
local id = args.item or frame:getParent().args.item
languages,
if id == "" or id == nil then
propertySupString,
id = mw.wikibase.getEntityIdForCurrentPage()
propertyLabel,
if id == nil then return end
propertyLink,
end
label_show,
local languages = findLang(args.lang)
labelicon0,
local propertySup = args["property"]; if (propertySup == nil or propertySup == "") then propertySup = "P131" end --administrative entity
labelicon1,
local propertyLabel = args["label"]; if (propertyLabel == nil or propertyLabel == "") then propertyLabel = "P31" end --instance
upto_number,
local propertyLink = args["valuetext"]; if propertyLink == "" then propertyLink = nil end --internallink
upto_label,
local upto = args["upto"]; if upto == "" then upto = nil end
upto_value,
local labelShow = args["labelshow"]; if labelShow == "" then labelShow = nil end
last_only,
local rowformat = args["rowformat"]; if (rowformat == nil or rowformat == "") then rowformat = "$0 = $1" end
grammatical_case,
local separator = args["separator"]; if (separator == nil or separator == "") then separator = "<br />" end
include_self)
local sorting = args["sorting"]; if sorting == "" then sorting = nil end
local editicon = not (args.editicon == "false" or args.editicon == "no")
local lastlabel = uc_first(upto or '')
local propertySups = mw.text.split(propertySupString, '[^P%d]')
local maxloop = tonumber(upto) or (lastlabel == '' and 10 or 50)
local labelFilter = {}
local maxloop = 10
if labelShow then
if upto_number then
maxloop = tonumber(upto_number)
for i, v in ipairs(mw.text.split(labelShow, "/")) do
elseif next(upto_label) or next(upto_value) then
labelFilter[uc_first(v)] = true
maxloop = 50
end
end
end
local result = {}
local labels_filter = next(label_show)
local label, link, linktext
local result = {}
local id_value = id
for iter = 1, maxloop do
for iter = 1, maxloop do
local label, link
local link, label, labelwicon, linktext, id_label
for _, propertySup in pairs(propertySups) do
id, link = getPropertyValue(id, propertySup, args.formatting, languages, editicon)
local _id_value, _link = getPropertyValue(id_value, propertySup, prop_format, languages, labelicon1, grammatical_case)
if id then
if _id_value and _link then id_value = _id_value; link = _link break end
_, label = getPropertyValue(id, propertyLabel, "label", languages)
end
if label and link then
if propertyLink then
if not id_value or not link then break end
_, linktext = getPropertyValue(id, propertyLink, "label", languages)
if linktext then
if propertyLink then
link = mw.ustring.gsub(link, "%[%[(.*)%|.+%]%]", "[[%1|" .. linktext .. "]]")
_, linktext = getPropertyValue(id_value, propertyLink, "label", languages)
end
if linktext then
end
link = mw.ustring.gsub(link, "%[%[(.*)%|.+%]%]", "[[%1|" .. linktext .. "]]")
label = case(label, "infoboxlabel", languages[1])
if labelShow == nil or labelFilter[label] then
result[#result + 1] = {label, link}
labelFilter[label] = nil -- only first label found
end
if label == lastlabel then
break
end
else
break
end
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
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
break
end
end
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 ret = {}
Baris 1.125: Baris 1.706:
local iter = 1
local iter = 1
if sorting == "-1" then first = #result; last = 1; iter = -1 end
if sorting == "-1" then first = #result; last = 1; iter = -1 end

for i = first, last, iter do
for i = first, last, iter do
local rowtext = mw.ustring.gsub(rowformat, "$[01]", {["$0"] = result[i][1], ["$1"] = result[i][2]})
local rowtext = mw.ustring.gsub(rowformat, "$[01]", {["$0"] = result[i][1], ["$1"] = result[i][2]})
ret[#ret +1] = expandBraces(rowtext, rowformat)
ret[#ret + 1] = expandBraces(rowtext, rowformat)
end
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] = '<ul style="line-height:100%; margin-' .. direction .. ':0.45em; padding-' .. direction .. ':0;"><li>' .. ret[i]
suffix = suffix .. '</li></ul>'
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 = "<br />" 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)
return mw.text.listToText(ret, separator, separator)
end
end


-- Link with a parent label --------------------
function p.linkWithParentLabel(frame)
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 = {}
local args = {}
for k, v in pairs(frame.args) do -- metatable
if frame.args then
for k, v in pairs(frame.args) do -- metatable
args[k] = v
args[k] = v
end
else
args = frame -- via require
end
end
if isSet(args.value) then
return args.value
-- get internal link of property/qualifier
args.list = "true"
args.formatting = "internallink"
args.separator = "/·/"
local pargs = frame:getParent().args
local link_list = p._main(args, pargs)
if link_list == nil or link_list == "" then
return
end
end
local link_table = mw.text.split(link_list, "/·/", true)
-- get id value of property/qualifier
-- get id value of property/qualifier
local largs = mw.clone(args)
args.formatting = "raw"
largs.list = tonumber(args.list) and args.list or true
local items_list = p._main(args, pargs)
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)
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
-- get label of parent property
local parent_claim = getSnak(getStatements(items_table[1], args.parent, true), {1, "mainsnak", "datatype"})
args.property = args.parent
if parent_claim == 'monolingualtext' then
args.qualifier = nil
largs.formatting = nil
local parent_claims = findClaims(mw.wikibase.getEntity(items_table[1]), args.parent)
largs.list = 'lang'
if parent_claims and parent_claims[1].mainsnak.datatype == 'monolingualtext' then
args.formatting = nil
args.list = 'lang'
else
else
args.formatting = "label"
largs.formatting = "label"
args.list = "false"
largs.list = false
end
end
largs.property = args.parent
largs.qualifier = nil
for i, v in ipairs(items_table) do
for i, v in ipairs(items_table) do
args.item = v
largs.item = v
local link_label = p._main(args, pargs)
local link_label, _ = p.claim(largs)
if link_label and link_label ~= '' then
if isSet(link_label) then
link_table[i] = mw.ustring.gsub(link_table[i] or '', "%[%[(.*)%|.+%]%]", "[[%1|" .. link_label .. "]]")
link_table[i] = mw.ustring.gsub(link_table[i] or '', "%[%[(.*)%|.+%]%]", "[[%1|" .. link_label .. "]]")
end
end
end
end
args.editicon, _ = setIcons(args.editicon, pargs.editicon)
args.id = getEntityId(args, pargs)
return mw.text.listToText(link_table)
args.lang = findLang(args.lang)
return mw.text.listToText(link_table) .. addEditIcon(args)
end
end


-- Calculate number of years old ----------------------------
function p.years_old(frame)
function p.yearsOld(frame)
local args = frame.args
if not required and frame.args and isSet(frame:getParent().args.sandbox) then
local id = args.item
return require(wiki.module_title .. "/" .. mw.message.new('Sandboxlink-subpage-name'):inLanguage(wiki.langcode):plain()).yearsOld(frame)
if id == '' or id == nil then
id = mw.wikibase.getEntityIdForCurrentPage()
end
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 lang = mw.language.new('en')
local function getBestValue(id, prop)
local function getBestValue(id, prop)
local snak = mw.wikibase.getBestStatements(id, prop)[1]
return getSnak(mw.wikibase.getBestStatements(id, prop), {1, "mainsnak", "datavalue", "value"})
if snak and snak.mainsnak and snak.mainsnak.datavalue and snak.mainsnak.datavalue.value then
return snak.mainsnak.datavalue.value
end
end
end
Baris 1.206: Baris 1.893:
dates[1] = {['min'] = {}, ['max'] = {}, ['precision'] = birth.precision}
dates[1] = {['min'] = {}, ['max'] = {}, ['precision'] = birth.precision}
dates[1].min.year = tonumber(mw.ustring.match(birth.time, "^[+-]?%d+"))
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.month = tonumber(mw.ustring.match(birth.time, "-(%d%d)-"))
dates[1].min.day = tonumber(mw.ustring.match(birth.time, "\-(%d%d)T"))
dates[1].min.day = tonumber(mw.ustring.match(birth.time, "-(%d%d)T"))
dates[1].max = mw.clone(dates[1].min)
dates[1].max = mw.clone(dates[1].min)
dates[2] = {['min'] = {}, ['max'] = {}, ['precision'] = death.precision}
dates[2] = {['min'] = {}, ['max'] = {}, ['precision'] = death.precision}
dates[2].min.year = tonumber(mw.ustring.match(death.time, "^[+-]?%d+"))
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.month = tonumber(mw.ustring.match(death.time, "-(%d%d)-"))
dates[2].min.day = tonumber(mw.ustring.match(death.time, "\-(%d%d)T"))
dates[2].min.day = tonumber(mw.ustring.match(death.time, "-(%d%d)T"))
dates[2].max = mw.clone(dates[2].min)
dates[2].max = mw.clone(dates[2].min)
Baris 1.246: Baris 1.933:
local old_min = age(dates[1].max, dates[2].min)
local old_min = age(dates[1].max, dates[2].min)
local old_max = age(dates[1].min, dates[2].max)
local old_max = age(dates[1].min, dates[2].max)
local old, old_expr
local old = old_min == old_max and old_min or old_min .. "/" .. old_max
if args.formatting then
if old_min == 0 and old_max == 0 then
old = "< 1"
old = expandBraces(mw.ustring.gsub(args.formatting, '$1', old), args.formatting)
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 .. '&nbsp;' .. yo
end
elseif args.formatting then
old_expr = expandBraces(mw.ustring.gsub(args.formatting, '$1', old), args.formatting)
else
old_expr = old
end
end
return old
return old_expr
end
end


Baris 1.257: Baris 1.971:
function p.getLabel(frame)
function p.getLabel(frame)
local args = frame.args or frame -- via invoke or require
local args = frame.args or frame -- via invoke or require
local id = mw.text.trim(args[1] or "")
local pargs = frame.args and frame:getParent().args or {}
if not required and isSet(pargs.sandbox) then
if id == "" then return end
return require(wiki.module_title .. "/" .. mw.message.new('Sandboxlink-subpage-name'):inLanguage(wiki.langcode):plain()).getLabel(frame)
local editicon = not (args.editicon == "false" or args.editicon == "no")
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 pencil = ''
local label_icon = ''
local label, lang
local label, lang
if args.label then
if args.label then
label = args.label
label = args.label
else
else
-- exceptions or labels fixed
local languages = findLang(args.lang)
if languages[1] == wiki.langcode then
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
-- exceptions or labels fixed
label = labels.infoboxLabelsFromId[id]
local exist, labels = pcall(require, wiki.module_title .. "/labels")
if exist and next(labels.infoboxLabelsFromId) ~= nil then
label = labels.infoboxLabelsFromId[id]
end
end
end
Baris 1.278: Baris 1.997:
label, lang = getLabelByLangs(id, languages)
label, lang = getLabelByLangs(id, languages)
if label then
if label then
if args.itemgender and feminineGender(args.itemgender) then
if isSet(args.itemgender) and feminineGender(args.itemgender) then
label = feminineForm(id, lang) or label
label = feminineForm(id, lang) or label
end
end
label = mw.language.new(lang):ucfirst(mw.text.nowiki(label)) -- sanitize
label = mw.language.new(lang):ucfirst(mw.text.nowiki(label)) -- sanitize
if args.case then
label = case(args.case, label, lang)
end
end
end
pencil = addEditIcon(id, lang, languages[1], editicon)
label_icon = addLabelIcon(id, lang, languages[1], labelicon)
end
end
end
end
local linked = args.linked
local linked = args.linked
local ret2 = required and untranslated or ''
if linked and linked ~= "" and linked ~= "no" then
if isSet(linked) and linked ~= "no" then
local article = mw.wikibase.getSitelink(id) or ("d:" .. id)
return "[[" .. article .. "|" .. (label or id) .. "]]" .. pencil
local article = mw.wikibase.getSitelink(id) or ("d:Special:EntityPage/" .. id)
return "[[" .. article .. "|" .. (label or id) .. "]]" .. label_icon, ret2
else
else
return (label or id) .. pencil
return (label or id) .. label_icon, ret2
end
end
end
end


-- Utilities -----------------------------
-- mw.wikibase.getLabelWithLang or getLabelByLang with a table of languages
-- See also module ../debug.
local function getLabelByLangs(id, languages)

local label
-- Copied from Module:Wikibase
local lang = languages[1]
function p.getSiteLink(frame)
if lang == wiki.langcode then
local args = frame.args or frame -- via invoke or require
-- using getLabelWithLang when possible instead of getLabelByLang
local pargs = frame.args and frame:getParent().args or {}
label, lang = mw.wikibase.getLabelWithLang(id)
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) then
local largs = {}
for k, v in pairs(pargs) do
largs[k] = v
end
for k, v in pairs(args) do
largs[k] = v
end
largs.query = 'num'
_, num = p.claim(largs)
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
else
num = mw.wikibase.getBestStatements(id, prop)
for _, l in ipairs(languages) do
end
label = mw.wikibase.getLabelByLang(id, l)
return #num
lang = l
end
if label then

break
-- 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

-- [[Modul:Wikidata/Legacy]] compatibility
-- look into entity object
function p.ViewSomething(frame)
local f = (frame.args[1] or frame.args.id) and frame or frame:getParent()
local id = f.args.id
if id and (#id == 0) then
id = nil
end
local data = mw.wikibase.getEntity(id)
if not data then
return nil
end

local i = 1
while true do
local index = f.args[i]
if not index then
if type(data) == "table" then
return mw.text.jsonEncode(data, mw.text.JSON_PRESERVE_KEYS + mw.text.JSON_PRETTY)
else
return tostring(data)
end
end
end
end

data = data[index] or data[tonumber(index)]
if not data then
return
end

i = i + 1
end
end
return label, lang
end
end


function p.getImageLegend(frame)
-- Return default language used
-- look for named parameter id; if it's blank make it nil
function p.lang(frame)
return findLang(frame.args[1])[1]
local id = frame.args.id
if id and (#id == 0) then
id = nil
end

-- look for named parameter lang
-- it should contain a two-character ISO-639 language code
-- if it's blank fetch the language of the local wiki
local lang = frame.args.lang
if (not lang) or (#lang < 2) then
lang = mw.language.getContentLanguage().code
end

-- first unnamed parameter is the local parameter, if supplied
local input_parm = mw.text.trim(frame.args[1] or "")
if input_parm == "FETCH_WIKIDATA" then
local ent = mw.wikibase.getEntity(id)
local imgs
if ent and ent.claims then
imgs = ent.claims.P18
end
local imglbl
if imgs then
-- look for an image with 'preferred' rank
for k1, v1 in pairs(imgs) do
if v1.rank == "preferred" and v1.qualifiers and v1.qualifiers.P2096 then
local imglbls = v1.qualifiers.P2096
for k2, v2 in pairs(imglbls) do
if v2.datavalue.value.language == lang then
imglbl = v2.datavalue.value.text
break
end
end
end
end
-- if we don't find one, look for an image with 'normal' rank
if (not imglbl) then
for k1, v1 in pairs(imgs) do
if v1.rank == "normal" and v1.qualifiers and v1.qualifiers.P2096 then
local imglbls = v1.qualifiers.P2096
for k2, v2 in pairs(imglbls) do
if v2.datavalue.value.language == lang then
imglbl = v2.datavalue.value.text
break
end
end
end
end
end
end
return imglbl
else
return input_parm
end
end

-- returns the page id (Q...) of the current page or nothing of the page is not connected to Wikidata
function p.pageId(frame)
return mw.wikibase.getEntityIdForCurrentPage()
end
end



Revisi terkini sejak 8 Juli 2024 11.42

-- version 20231204 from master @cawiki
-- changes from previous version: whitelist, blacklist, ignorevalue and selectvalue work with multivalue qualifiers
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
	["suppressids"] = {}, -- list of Qid values to suppress
	["qidlabels"] = true -- show labels as Qid if no fallback translation is available
}

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
	i18n.suppress = {}
	for _, id in ipairs(i18n.suppressids) do
		i18n.suppress[id] = true
	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:callParserFunction('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 '<span style="font-variant: small-caps;">' .. label .. '</span>'
	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 = " <sup>(" .. lang .. ")</sup>"
		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:OOjs_UI_icon_tag-rtl-progressive.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 future Wikidata Bridge
local function addEditIcon(parameters)
	local ret = ''
	if parameters.editicon and parameters.id and parameters.property then
		local bridge_flow = parameters.editbridge and ' data-bridge-edit-flow="single-best-value"' or ''
		local icon_style = parameters.editicon == "right" and ' style="float: right;"' or ''
		ret = ' <span class="penicon"' .. bridge_flow .. icon_style .. '>'
			.. "[[File:OOjs UI icon edit-ltr-progressive.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 .. "]]"
			.. "</span>"
		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 tabular-data
local function printDatatypeTabular(data, parameters)
	local icon
	if parameters.formatting == 'raw' then
		icon = "no-icon"
		data = string.gsub(data, '^Data:', '') -- remove prefix, i.e. see Module:Tabular data
	end
	return printDatatypeString(data, parameters), icon
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 string.sub(parameters.formatting or '', 1, 8) == "unitcode" then
		-- get unit symbol
		local unit_symbol = unitSymbol(id, parameters.lang)
		if isSet(unit_symbol) then
			if string.sub(parameters.formatting or '', -6) == "linked" then
				suffix = "[[" .. (mw.wikibase.getSitelink(id) or "d:" .. id) .. "|" .. unit_symbol .. "]]"
			else
				suffix = unit_symbol
			end
		end
	end
	if suffix == '' then -- formatting=unit, or formatting=unitcode not found
		-- get unit label
		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)
			if string.sub(parameters.formatting or '', -6) == "linked" then
				suffix = "[[" .. (mw.wikibase.getSitelink(id) or "d:" .. id) .. "|" .. suffix .. "]]"
			end
		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
			if conv_amount and parameters.formatting == 'raw' then
				amount = conv_amount
				suffix = ""
				conv_amount = nil
			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 == 'duration' then
			return lang_obj:formatDuration(sec)
		elseif parameters.formatting == 'durationm:s' then
			local mm = math.floor(sec / 60)
			local ss = sec - (mm * 60)
			return string.format("%02d:%02d", mm, ss)
		else -- durationhms or durationh:m:s
			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
		end
	end
	if parameters.case then
		amount = case(parameters.case, amount, parameters.lang[1], feminineGender(parameters.id))
	elseif parameters.formatting ~= 'raw' then
		if parameters.numformat then
			amount = lang_obj:formatNum(tonumber(string.format(parameters.numformat, amount)))
		else
			amount = lang_obj:formatNum(tonumber(amount))
		end
	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 = " <sup>(" .. mw.message.new('Wikibase-time-calendar-' .. calendar_model[calendar_id]):inLanguage(parameters.lang[1]):plain() .. ")</sup>"
		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") -- suppress 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 with data types wikibase-item or 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
	if labelcase == nil and i18n.qidlabels == false then
		return
	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 .. '|<span style="color:#5f9cbb;">' .. (labelcase or entity_id) .. '</span>]]'
			ret2 = labelcase or entity_id
		end
	end
	
	return ret1 .. addLabelIcon(entity_id, lang, parameters.lang[1], parameters.labelicon), ret2
end

-- format data type wikibase-lexeme
local function printDatatypeLexeme(data, parameters)
	local entity_id = data['id']
	if parameters.formatting == 'raw' then
		return entity_id, entity_id
	end
	local lemmas = mw.wikibase.getEntity(entity_id):getLemmas()
	if parameters.list == 'lang' and lemmas[1][2] ~= parameters.lang[1] then
		return
	end
	local ret = '[[d:Special:EntityPage/' .. entity_id .. '|<span style="color:#5f9cbb;">' .. lemmas[1][1] .. '</span>]]'
	if parameters.list ~= 'lang' or (parameters.list == 'lang' and lemmas[1][2] ~= wiki.langcode) then
		ret = ret .. " <sup>(" .. lemmas[1][2] .. ")</sup>"
	end
	return ret, entity_id
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('<span lang="$1">$2</span>', '$[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)
	parameters.editbridge = false
	if snak.snaktype == 'value' then -- see Special:ListDatatypes
		-- data value string
		if snak.datatype == "string" then
			parameters.editbridge = true -- Wikidata Bridge currently only for string values
			return printDatatypeString(snak.datavalue.value, parameters)
		elseif snak.datatype == "commonsMedia" or snak.datatype == "geo-shape" then
			return printDatatypeMedia(snak.datavalue.value, parameters)
		elseif snak.datatype == "tabular-data" then
			return printDatatypeTabular(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)
		-- data types other than string value
		elseif snak.datatype == 'wikibase-item' or snak.datatype == 'wikibase-property' then
			if i18n.suppress[snak.datavalue.value.id] then
				return
			end
			return printDatatypeEntity(snak.datavalue.value, parameters)
		elseif snak.datatype == 'wikibase-lexeme' then
			return printDatatypeLexeme(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' or parameters.showsomevalue == false 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 '<span class="error">' .. i18n.errors[key] .. '</span>'
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.listmax)
		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 (parameters.references or parameters.onlysourced) then
		return '', false
	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 -- Wikimedia 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, lang=lang}) 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 #result > 0 then
		if parameters.references then
			if isSet(i18n.categoryref) then
				result[#result + 1] = "[[" ..i18n.categoryref .. "]]"
			end
			return table.concat(result), true
		else
			return '', true
		end
	end
	return '', false
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 args.qualifier1 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.numformat = getArg(args.numformat)
	parameters.case = args.case
	parameters.list = getArg(args.list, true, {firstrank='bestrank'})
	parameters.listmax = args.listmax
	parameters.listrank = getArg(args.listrank)
	if type(parameters.list) == "number" then -- backwards compatibility
		parameters.listmax = parameters.listmax or parameters.list
		parameters.list = true
	elseif parameters.list == "bestrank" then
		parameters.listrank = parameters.listrank or "bestrank"
		parameters.list = true
	end
	parameters.shownovalue = getArg(args.shownovalue, true)
	parameters.showsomevalue = getArg(args.showsomevalue, true)
	parameters.separator = getArg(args.separator)
	parameters.conjunction = getArg(args.conjunction, parameters.separator)
	parameters.qseparator = getArg(args.qseparator, parameters.separator)
	parameters.qconjunction = getArg(args.qconjunction, parameters.conjunction)
	local sorting_col = args.tablesort
	local sorting_up = (args.sorting or "") ~= "-1"
	local rowformat = args.rowformat
	parameters.references = getArg(args.references, false)
	parameters.onlysourced = getArg(args.onlysourced, false)
	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.listrank == '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
		local ret = showerrors and printError("property-not-found") or default
		return ret, args.query == 'num' and 0 or ''
	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 "<br />"
		parameters.conjunction = parameters.conjunction or "<br />"
		parameters.qseparator = getArg(args.qseparator, mw.message.new('Comma-separator'):inLanguage(parameters.lang[1]):plain())
		parameters.qconjunction = getArg(args.qconjunction, parameters.qseparator)
		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 = "</li><li>"
			parameters.conjunction = "</li><li>"
			if mw.ustring.match(rowformat, "^[*#]") == "*" then
				preformat = "<ul><li>"
				postformat = "</li></ul>"
			else
				preformat = "<ol><li>"
				postformat = "</li></ol>"
			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, <br /> 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
										-- only first value of a property as alternative to a qualifier
										-- multiple values may not be related to a given raw of the table
										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, ["list"]=params.list})
							if valueq then
								if whitelist[j] then
									for k, v in pairs(whitelist[j]) do
										if v and string.find(valueq, k, 1, true) then
											rowlist[#values] = true
										end
									end
								elseif blacklist[j] then
									for k, v in pairs(blacklist[j]) do
										if v and string.find(valueq, k, 1, true) then
											rowlist[#values] = false
										end
									end
								elseif ignorevalue[j] then
									for k, v in pairs(ignorevalue[j]) do
										if v and string.find(valueq, k, 1, true) then
											values[#values]["col" .. j] = nil
										end
									end
								elseif selectvalue[j] then
									local selected
									for k, v in pairs(selectvalue[j]) do
										if v and string.find(valueq, k, 1, true) then
											selected = true
										end
									end
									if selected == nil then
										values[#values]["col" .. j] = nil
									end
								end
							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 (parameters.references or parameters.onlysourced) 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.listmax)
		result = {}
		for idx in pairs(values) do
			local valuerow = values[sortindices[idx]]
			local reference, valid_ref = 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) and (not parameters.onlysourced or (parameters.onlysourced and valid_ref)) 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 = #result
		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 then
			local ref, valid_ref = getReferences(claim, parameters)
			if parameters.onlysourced and valid_ref == false then
				result = nil
			else
				result = result .. ref
			end
		end
		if args.query == 'num' then result_query = result and 1 or 0 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] = '<ul style="line-height:100%; margin-' .. direction .. ':0.45em; padding-' .. direction .. ':0;"><li>' .. ret[i]
			suffix = suffix .. '</li></ul>'
		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 = "<br />" 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 .. '&nbsp;' .. 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) then
		local largs = {}
		for k, v in pairs(pargs) do
			largs[k] = v
		end
		for k, v in pairs(args) do
			largs[k] = v
		end
		largs.query = 'num'
		_, num = p.claim(largs)
		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

-- [[Modul:Wikidata/Legacy]] compatibility
-- look into entity object
function p.ViewSomething(frame)
	local f = (frame.args[1] or frame.args.id) and frame or frame:getParent()
	local id = f.args.id
	if id and (#id == 0) then
		id = nil
	end
	local data = mw.wikibase.getEntity(id)
	if not data then
		return nil
	end

	local i = 1
	while true do
		local index = f.args[i]
		if not index then
			if type(data) == "table" then
				return mw.text.jsonEncode(data, mw.text.JSON_PRESERVE_KEYS + mw.text.JSON_PRETTY)
			else
				return tostring(data)
			end
		end

		data = data[index] or data[tonumber(index)]
		if not data then
			return
		end

		i = i + 1
	end
end

function p.getImageLegend(frame)
	-- look for named parameter id; if it's blank make it nil
	local id = frame.args.id
	if id and (#id == 0) then
		id = nil
	end

	-- look for named parameter lang
	-- it should contain a two-character ISO-639 language code
	-- if it's blank fetch the language of the local wiki
	local lang = frame.args.lang
	if (not lang) or (#lang < 2) then
		lang = mw.language.getContentLanguage().code
	end

	-- first unnamed parameter is the local parameter, if supplied
	local input_parm = mw.text.trim(frame.args[1] or "")
	if input_parm == "FETCH_WIKIDATA" then
		local ent = mw.wikibase.getEntity(id)
		local imgs
		if ent and ent.claims then
			imgs = ent.claims.P18
		end
		local imglbl
		if imgs then
			-- look for an image with 'preferred' rank
			for k1, v1 in pairs(imgs) do
				if v1.rank == "preferred" and v1.qualifiers and v1.qualifiers.P2096 then
					local imglbls = v1.qualifiers.P2096
					for k2, v2 in pairs(imglbls) do
						if v2.datavalue.value.language == lang then
							imglbl = v2.datavalue.value.text
							break
						end
					end
				end
			end
			-- if we don't find one, look for an image with 'normal' rank
			if (not imglbl) then
				for k1, v1 in pairs(imgs) do
					if v1.rank == "normal" and v1.qualifiers and v1.qualifiers.P2096 then
						local imglbls = v1.qualifiers.P2096
						for k2, v2 in pairs(imglbls) do
							if v2.datavalue.value.language == lang then
								imglbl = v2.datavalue.value.text
								break
							end
						end
					end
				end
			end
		end
		return imglbl
	else
		return input_parm
	end
end

-- returns the page id (Q...) of the current page or nothing of the page is not connected to Wikidata
function p.pageId(frame)
	return mw.wikibase.getEntityIdForCurrentPage()
end

return p