Modula:Ca:Wikidata

Wikipedia
Jump to navigation Jump to search
[ edit ]

Documentation in Catalan Wikipedia in English.

Pages
Changelog
  • 2019-03-18 Prefixed everything to Module:Ca:Wikidata (diff)
  • 2019-03-19 Fixed references printing bug, added whitelist/blacklist is defined filtering. (diff1, diff2)

Syntax[edit source]

{{#invoke:Ca:Wikidata|claim|property= |qualifier= |value= |list= |tablesort= |formatting= |separator= |conjunction= |item= |lang= |editicon= |showerrors= |default= }}
Additional parameters
{{#invoke:Ca:Wikidata|claim|property= |qualifier= |qualifier2= |...|qualifierx= |formatting=table |tablesort= |sorting= |rowformat= |rowsubformat1= |...|rowsubformatx= |colformat0= |...|colformatx= |case0= |...|casex= |separator= |conjunction= |item= |lang= |editicon= |showerrors= |default= }}

Examples[edit source]

The Beatles: {{#invoke:Ca:Wikidata|claim|item= Q1299 | property = p527 |qualifier = P580 |qualifier2 = P582|formatting=table|tablesort= 1 |rowformat=* $0 <small>($1 - $2)</small>|separator=,  |conjunction= and |colformat1=Y|colformat2=Y}}

The Beatles:

Population: {{#invoke:Ca:Wikidata |claim| item= Q985564| property = P1082|qualifier = P585 |formatting=table|rowformat=* $0 $1|tablesort=1|sorting=-1|colformat1=Y-m-d|references=true}}

  • 19 831 [1] 2007-12-31

Infobox usage[edit source]

Functions[edit source]

claim
Returns with a certain format the value (or values) of a statement or the qualifier of a statement. By default, this refers to the item (Qnnnn) of the active article.
getLabel
Returns a tag in the specified language, or the default language.

Currently not supported functions. See usage from cawiki. getParentValues, linkWithParentLabel, getTAValue, years_old

Parameters[edit source]

Text is blindly Google translated and needs love and care. Please remove this after checking. Source: Catalan Wikipedia (Google Translate)
  • property (mandatory) = Property of the declaration, with format "P" followed by a number.
  • qualifier (optional) = Qualifier property, with format "P" followed by a number. Numbering goes qualifier, qualifier2, qualifier3 ...
  • value (optional) = Preferred value to Wikidata. If value is - then do not display anything.
  • list (optional, deprecated) = default value true is only supported value in sewiki. Other values see cawiki.
  • formatting (optional) = Formatting type. possible values, for each type:
    • Entity format:
      • formatting = raw Identifier number of the element.
      • formatting = label Element tag.
      • formatting = sitelink Title of the Wikipedia page of the item, without a link. If it does not exist, the raw format returns with the wikidata prefix :.
      • formatting = internallink Internal link whenever possible, either on the Wikipedia page (sitelink) or on the label. If there is no link to the Wikipedia element, and there is a tag, it prevents the default link to Wikidata, even if it is a red link.
      • formatting = pattern Format according to a pattern using $ 1 as the parameter to replace.
        You can include templates or parser functions in the format: {{{{{}} template {{!}} Parameter {{!}} Name {{ = }} parameter {{ ) }}}
      • The default format is a linked link, either to sitelink or to a wikited: raw , using label as the link tag.
        • formatting = ucfirst Variant of the default uppercase format on the label. In a list, just capitalize on the first element.
        • formatting = ucinternallink Variant of the "ucfirst" format combined with "internallink".
    • Text format (string)
      • formatting = weblink External link format [http://example.com example.com]
      • formatting = pattern Format according to a pattern using $1 as the parameter to replace. Example: formatting=[http://whc.unesco.org/en/list/$1 $1]. For properties that already have a self-generated WD link, the pattern is found on the property's discussion page.
    • Numeric format (quantity)
      • formatting=unit Numeric format including drive. If it's different from 1, put it in plural according to Module: Wikidata / Units.
      • formatting=unitcode Numeric format including the code or abbreviation of the unit. For a code other than the one defined in Symbol of the unit (P5061) you can add it to Module: Wikidata / Units.
    • Multilingual text format (monolingualtext)
      • formatting = language code in which the value of the parameter is written, in the case of parameters with monolingual text format, such as the official Name Official name (P1448).
      • formatting = text Recovers raw text, without the mark of language.
        By default, the language flag is included when it's different from the local language of the wiki: <span lang="en">United...</span> .
      • formatting = pattern Format according to a pattern using $ language and $ text as variables to be replaced. For example, "formatting = ($ language) $ text" to remove the text with the language code in parenthesis front.FIXME: lost in google translation
      • list = lang Sols removes the values ​​corresponding to the defined language or the default language. See lang parameter.
    • Format coordinates (globecoordinate)
      • formatting = latitude Latitude value in a coordinate declaration, in decimal format
      • formatting = longitude value in a coordinate declaration, in decimal format
      • formatting = dimension value in a coordinate declaration, in meters, equivalent to the dim parameter of GeoHack .
      • formatting = globe (by default) Template globe parameter value : coord , required for coordinates outside the Earth. The data is defined in the Module: Map celestial / data .
    • Date format (time):
      • formatting = format #time Accepts any valid format of the #time parser function . For example: formatting = dmY (03-19-2019), formatting = [[jgg]] ( March 19 )
      • formatting = Y (2019). By default, use the format "j FY" (March 19, 2019)
    • Tableformat (item + qualifiers)
      • formatting=table Properties list formatting=table with its qualifiers. You can indicate the format of each line and each column in the table. The separators are by default line breaks (see separator and conjunction ). Specific parameters for this format:
      • qualifier2 ... qualifierN = Additional qualifier to the first qualifier , with unlimited number of consecutive values. qualifier x = P nnn1 OR P nnn2 Alternative definition of a qualifier when the first one does not exist. A case of typical use is Date (P585) OR Start date (P580) . There is no limit for successive ORs. The blank spaces before and after are optional.
      • qualifierN = P nnn1 /P nnn2 Higher property P nnn2 of the element corresponding to P nnn1 being this one of the qualifiers or the property. With an empty value of P nnn1 (qualifier x = / P nnn2 ) obtains a second property of the same element. Allows you to obtain additional data defined in the corresponding element. For example, to get the subway lines with its icon: qualifier = P81 / P154. It can be combined with the OR option taking the OR priority.
      • rowformat= Format of the most qualifying row of property. The property is indicated with $ 0 and the qualifications $ 1 to $ x. Default is rowformat=$0 ($1, ... $x) . You can include lists * or # and also templates or parser functions in the format: {{{{{}} template | Parameter | Name Málle:= parameter Málle:)}
      • rowsubformat1 ... rowsubformat x = Optional subform to apply to $ 1- $ x defined in rowformat. It may be useful not to break the format in case of an empty qualifier. For example, "rowformat = $ 0 $ 1" and "rowsubformat1 = for $ 1" results "$ 0 for $ 1" or "$ 0" without a qualifier.
      • colformat0 ... colformat x = Optional format to apply to the columns, 0 for the property and 1-x for the qualifiers. Accept the same syntax as formatting for each type of value.
      • case0 ... case x = Grammatical case to apply to each column. See below. The case parameter, without numbering, applies to all values.
      • whitelist0 ... whitelist x = List, separated by a vertical bar (/), of the items to display for a column.
      • blacklist0 ... blacklist x = List, separated by a vertical bar (/), of the items that will not be displayed for a column. If you use a whitelist for a column and a blacklist for another, the results may be unexpected (see a comment on it )
      • tablesort = with values ​​of 0 ax, allows to order the table by the property (0) or the qualifiers (1 ax). Accept multiple keys separating them with / . Ex: tablesort=0/2/1. By default, sorting is by the rank of the property (preferential, normal, obsolete) and by the age of definition in Wikidata.
      • sorting = -1 reverses the sorting by descending it. With any other value, or in its absence, the order is ascending. In the case of tablesort with multiple keys, the sorting criteria will be applied to all equally.
      • list=false (see above) removes only the first row of the table, according to the order indicated or by default.
  • separator(optional) = Separator to use in lists or tables. In the case of lists, the default is MediaWiki: Comma-separator , in Catalan a comma and a blank space ','. In case of default tables is a line break
    . In some cases, if it is included in tags that use strip markers ,
    one
    may not work. The alternative is to use separator = LF for a line feed control character.
  • conjunction (optional) = Conjunction to use as a separate tab between the last two elements in the list. The default is equal to the separator if it is defined and if it is not MediaWiki: And more MediaWiki: Word-separator , in Catalan 'i'. In the case of default tables is a line break
  • case (optional) = Grammatical case to be generated. Defined cases:
    • case = gender, according to the definition of Sex or gender (P21) of the element (current page, item or itemgender) removes the value of Female form of the label (P2521) .
    • case = infoboxlabel, remove the label without complement with prepositions and other corrections defined in Module: Wikidata / labels in the table infoboxlabels. It can be combined with itemgender to add in addition the case = gender.
    • case = infoboxdata, for changes in the tag of the link in values ​​of a infobox, for example to use a simpler alias or abbreviations more appropriately for a infobox. The changes are defined in the table infoboxdata of the Module: Wikidata / labels . It is necessary to consider beforehand if it were necessary to modify the tag to Wikidata.
    • case = smallcaps, remove the label in the poplar case.
  • item(optional) = Allows you to specify an item (Qnnnnn) other than the active item. It is necessary to use it moderately for its high consumption of resources. It can also be used as a global parameter in the template or higher invocation.
  • itemgender (optional) = Element where the female shape is checked according to Sex or gender (P21) , by default item . It is used in combination with case = gender or case = infoboxlabel.
  • lang (optional) = Indicates the code of a given language. It can also be used as a global parameter in the template or higher invocation. By default, use the local wiki language for the main article space or user language defined in preferences for other namespaces. If you do not find the value in this language try it in the alternative languages ​​defined in MediaWiki. For example, for Catalan, the alternative languages ​​are Occitan and English. If the value has not been found in the requested language, add a small pencil as an icon to translate it into Wikidata. This icon can be removed with the editicon parameter.
  • dotranslations (optional) = try to failback to other languages than northern sami. If failback language is used the text is highlighted with yellow and article is categorized to category:Wikipedia:Links_to_Wikidata_element_with_missing_Northern_Sami_label
  • editicon (optional) = Defines if you put a small pencil Translate as an icon to edit Wikited in case the value has not been found in the requested language. Default is true . With editicon = false or editicon = is not false .
  • showerrors (optional) = With any value, it shows the error message, if any. If it is not defined it will display the default parameter in case of error.
  • default (optional) = Text to display in case of error. It has no effect if the showerrors setting is defined. If it is not defined, and neither the showerrors parameter will return an empty value in the event of an error.

  1. Kaupunkien ja kuntien lukumäärät ja väestötiedot: Suomen Kuntaliitto.
-- version 20200123 from master @cawiki

local p = {}

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

local i18n = {						-- internationalisation at [[Module:Wikidata/i18n]]
	["errors"] = {
		["property-not-found"] = "Property not found.",
		["qualifier-not-found"] = "Qualifier not found.",
	},
	
	["datetime"] = {
		-- $1 is a placeholder for the actual number
		["beforenow"] = "$1 BCE",	-- how to format negative numbers for precisions 0 to 5
		["afternow"] = "$1 CE",		-- how to format positive numbers for precisions 0 to 5
		["bc"] = '$1 BCE',			-- how print negative years
		["ad"] = "$1",				-- how print 1st century AD dates
		
		[0] = "$1 billion years",	-- precision: billion years
		[1] = "$100 million years",	-- precision: hundred million years
		[2] = "$10 million years",	-- precision: ten million years
		[3] = "$1 million years",	-- precision: million years
		[4] = "$100000 years",		-- precision: hundred thousand years; thousand separators added afterwards
		[5] = "$10000 years",		-- precision: ten thousand years; thousand separators added afterwards
		[6] = "$1 millennium",		-- precision: millennium
		[7] = "$1 century",			-- precision: century
		[8] = "$1s",				-- precision: decade
		-- the following use the format of #time parser function
		[9] = "Y",					-- precision: year
		[10] = "F Y",				-- precision: month
		[11] = "F j, Y",			-- precision: day
	},
	
	["years-old"] = {
		["singular"] = "",			-- year old, as in {{PLURAL:$1|singular|plural}}
		["plural"] = "",			-- years old
		["paucal"] = "",			-- for languages with 3 plural forms as in {{PLURAL:$1|singular|paucal|plural}}
	},
	
	["cite"] = {					-- Cite web parameters
		["url"]			= "url",
		["title"]		= "title",
		["website"]		= "website",
		["access-date"]	= "access-date",
		["archive-url"]	= "archive-url",
		["archive-date"]= "archive-date",
		["author"]		= "author",
		["publisher"]	= "publisher",
		["quote"]		= "quote",
		["language"]	= "language",
		["date"]		= "date",
		["pages"]		= "pages"
	}
}

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

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

-- Module local functions --------------------------------------------

-- Credit to http://stackoverflow.com/a/1283608/2644759, cc-by-sa 3.0
local function tableMerge(t1, t2)
	for k,v in pairs(t2) do
		if type(v) == "table" then
			if type(t1[k] or false) == "table" then
				tableMerge(t1[k] or {}, t2[k] or {})
			else
				t1[k] = v
			end
		else
			t1[k] = v
		end
	end
	return t1
end

local function loadI18n()
	local exist, res = pcall(require, wiki.module_title .. "/i18n")
	if exist and next(res) ~= nil then
		tableMerge(i18n, res.i18n)
		cases = res.cases
	end
end

loadI18n()

-- Argument is 'set' when it exists (not nil) or when it is not an empty string.
local function isSet(var)
	return not (var == nil or var == '')
end

-- Set local case to a label
local function case(localcase, label, ...)
	if not isSet(label) then return label end
	
	if localcase == "smallcaps" then
		return '<span style="font-variant: small-caps;">' .. label .. '</span>'
	elseif cases[localcase] then
		return cases[localcase](label, ...)
	end
	
	return label
end

-- Table of language codes: requested or default and its fallbacks
local function findLang(langcode)
	if mw.language.isKnownLanguageTag(langcode or '') == false then
		local cframe = mw.getCurrentFrame()
		local pframe = cframe:getParent()
		langcode = pframe and pframe.args.lang
		if mw.language.isKnownLanguageTag(langcode or '') == false then
			if not mw.title.getCurrentTitle().isContentPage then
				langcode = cframe:preprocess('{{int:lang}}')
			end
			if mw.language.isKnownLanguageTag(langcode or '') == false then
				langcode = wiki.langcode
			end
		end
	end
	local languages = mw.language.getFallbacksFor(langcode)
	table.insert(languages, 1, langcode)
	return languages
end

-- mw.wikibase.getLabelWithLang or getLabelByLang with a table of languages
local function getLabelByLangs(id, languages)
	local label
	local lang = languages[1]
	local testlanguages = {'fi', 'sv', 'no','da','de', 'fr','es', 'en'}
	if lang == wiki.langcode then
		-- using getLabelWithLang when possible instead of getLabelByLang
		label, lang = mw.wikibase.getLabelWithLang(id)
	else
		lang = nil
		for _, l in ipairs(languages) do
			label = mw.wikibase.getLabelByLang(id, l)
			if label then
				lang = l
				break
			end
		end
	end
	local lang_ok=1
	if lang ~= 'se' then
		for _, l in ipairs(testlanguages) do
			testlabel = mw.wikibase.getLabelByLang(id, l)
			if testlabel and testlabel~=label then
				lang_ok=0
				break
			end
		end
	end
	
	return label, lang, lang_ok
end

-- Is gender femenine? true or false
local function feminineGender(id)
	local claims = mw.wikibase.getBestStatements(id or mw.wikibase.getEntityIdForCurrentPage(),'P21')
	if #claims == 0 then
		return false
	elseif claims[1].mainsnak.datavalue == nil then -- novalue or somevalue
		return false
	else
		local genderId = claims[1].mainsnak.datavalue.value.id
		if genderId == "Q6581072" or genderId == "Q1052281" or genderId == "Q43445" then -- female, transgender female, female organism
			return true
		end
	end
	return false
end

-- Fetch female form of label
local function feminineForm(id, lang)
	local feminine_claims = mw.wikibase.getBestStatements(id, 'P2521') -- female form of label
	for _, feminine_claim in ipairs(feminine_claims) do
		if feminine_claim.mainsnak.datavalue.value.language == lang then
			return feminine_claim.mainsnak.datavalue.value.text
		end
	end
end

-- 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
			ret_icon = " [[File:Noun Project label icon 1116097 cc mirror.svg|12px|"
				.. mw.message.new('Translate-taction-translate'):inLanguage(uselang):plain()
				.. "|link=https://www.wikidata.org/wiki/Special:EntityPage/" .. label_id .. "?uselang=" .. uselang .. "]]"
		end
	end
	return ret_lang .. ret_icon
end

-- Escape URL escapes to avoid Lua captures
local function urlEscapes(text)
	return mw.ustring.gsub(text, "(%%%d)", "%%%1")
end

-- expandTemplate or callParserFunction
local function expandBraces(text, formatting)
	if text == nil or formatting == nil then return text end
	-- only expand braces if provided in argument, not included in value as in Q1164668
	if mw.ustring.find(formatting, '{{', 1, true) == nil then return text end
	if type(text) ~= "string" then
		text = tostring(text)
	end
	
	for braces in mw.ustring.gmatch(text, "{{(.-)}}") do
		local parts = mw.text.split(braces, "|")
		local title_part = parts[1]
		local parameters = {}
		for i = 2, #parts do
			local subparts = mw.ustring.find(parts[i], "=")
			if subparts then
				parameters[mw.ustring.sub(parts[i], 1, subparts-1)] = mw.ustring.sub(parts[i], subparts+1, -1)
			else
				table.insert(parameters, parts[i])
			end
		end
		
		local braces_expanded
		if mw.ustring.find(title_part, ":")
			and mw.text.split(title_part, ":")[1] ~= mw.site.namespaces[10].name -- not a prefix Template:
			then
			braces_expanded = mw.getCurrentFrame():callParserFunction{name=title_part, args=parameters}
		else
			braces_expanded = mw.getCurrentFrame():expandTemplate{title=title_part, args=parameters}
		end
		braces = mw.ustring.gsub(braces, "([%^%$%(%)%%%.%[%]%*%+%-%?])", "%%%1") -- escape magic characters
		braces_expanded = urlEscapes(braces_expanded)
		text = mw.ustring.gsub(text, "{{" .. braces .. "}}", braces_expanded)
	end
	
	return text
end

-- Resolve Wikidata redirects, pending phab:T157868
local function resolveEntityId(id)
	if not id or not mw.wikibase.isValidEntityId(id) then return id end
	-- if no label in local language nor its fallbacks, maybe it is a redirect
	-- not using mw.title.new(id).isRedirect as it is expensive
	if mw.wikibase.getLabel(id) == nil then
		local entity = mw.wikibase.getEntity(id) -- expensive function
		if not entity then return nil end
		if id ~= entity.id then
			-- Qid redirected to be fixed
			-- see [[Special:WhatLinksHere/Template:Tracking/wikidata/redirect]]
			require(wiki.module_title .. '/debug').tracking('redirect')
			require(wiki.module_title .. '/debug').tracking('redirect/' .. id)
		else
			-- no redirect and no label, fix it to avoid expensive functions
			require(wiki.module_title .. '/debug').tracking('label')
			require(wiki.module_title .. '/debug').tracking('label/' .. id)
		end
		return entity.id
	end
	return id
end

-- format data type math
local function printDatatypeMath(data)
	return mw.getCurrentFrame():callParserFunction('#tag:math', data)
end

-- format data type musical-notation
local function printDatatypeMusical(data, formatting)
	local attr = {}
	if formatting == 'sound' then
		attr.sound = 1
	end
	return mw.getCurrentFrame():extensionTag('score', data, attr)
end

-- format data value string
local function printDatavalueString(data, parameters)
	if 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])
	end
	return data
end

-- format data type url
local function printDatatypeUrl(data, parameters)
	if parameters.formatting == 'weblink' then
		local label_parts = mw.text.split(string.gsub(data, '/$', ''), '/')
		local label = string.gsub(label_parts[3], '^www%.', '')
		if #label_parts > 3 then
			label = label .. '…'
		end
		return '[' .. data .. ' ' .. label .. ']'
	end
	return printDatavalueString(urlEscapes(data), parameters)
end

-- format data type commonsMedia
local function printDatatypeMedia(data, parameters)
	local ret
	if not string.find((parameters.formatting or ''), '$1', 1, true) then
		ret = "media_title"
		if parameters.list then
			require(wiki.module_title .. '/debug').tracking('commonstitlelist')
		end
	end
	return printDatavalueString(data, parameters), ret
end

-- format data value globecoordinate
local function printDatavalueCoordinate(data, parameter)
	local globes = {['Q3134']='callisto',['Q596']='ceres',['Q15040']='dione',['Q2']='earth',['Q3303']='enceladus',
		['Q3143']='europa',['Q17975']='phoebe',['Q3169']='ganymede',['Q3123']='io',['Q17958']='iapetus',
		['Q308']='mercury',['Q15034']='mimas',['Q405']='moon',['Q15050']='rhea',['Q15047']='tethys',
		['Q111']='mars',['Q2565']='titan',['Q3359']='triton',['Q313']='venus',['Q3030']='vesta'}
	if parameter and string.find(parameter, '$lat', 1, true) and string.find(parameter, '$lon', 1, true) then
		local ret = mw.ustring.gsub(parameter, '$l[ao][tn]', {['$lat'] = data.latitude, ['$lon'] = data.longitude})
		if string.find(parameter, '$globe', 1, true) then
			local myglobe = 'earth'
			if isSet(data.globe) then
				local globenum = mw.text.split(data.globe, 'entity/')[2] -- http://www.wikidata.org/wiki/Q2
				myglobe = globes[globenum] or 'earth'
			end
			ret = mw.ustring.gsub(ret, '$globe', myglobe)
		end
		return expandBraces(ret, parameter)
	elseif parameter == 'latitude' then
		return data.latitude
	elseif parameter == 'longitude' then
		return data.longitude
	elseif parameter == 'dimension' then
		return data.dimension
	else --default formatting='globe'
		if data.globe == '' or data.globe == nil or data.globe == 'http://www.wikidata.org/entity/Q2' then
			return 'earth'
		else
			local globenum = mw.text.split(data.globe, 'entity/')[2]
			return globes[globenum] or globenum
		end
	end
end

-- Local 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 = findClaims(id, 'P5061')
		if claims then
			local langclaims = {}
			table.insert(lang, 'mul') -- multilingual as last try
			for _, snak in ipairs(claims) do
				if snak.mainsnak and snak.mainsnak.datavalue and snak.mainsnak.datavalue.value and
					not langclaims[snak.mainsnak.datavalue.value.language] -- just the first one by language
					then
						langclaims[snak.mainsnak.datavalue.value.language] = snak.mainsnak.datavalue.value.text
				end
			end
			for _, l in ipairs(lang) do
				if langclaims[l] then
					return langclaims[l]
				end
			end
		end
	end
	return unit_symbol
end

local function getUnit(amount, id, parameters) -- get unit symbol or name
	local suffix = ''
	if parameters.formatting == "unitcode" then
		-- get unit symbol
		local unit_symbol = unitSymbol(id, parameters.lang)
		if isSet(unit_symbol) then
			suffix = unit_symbol
		end
	end
	if suffix == '' then -- formatting=unit, or formatting=unitcode not found
		-- get unit name
		local unit_label, lang, lang_ok = getLabelByLangs(id, parameters.lang)
		if lang == wiki.langcode and pcall(require, wiki.module_title .. "/Units") then
			suffix = require(wiki.module_title .. "/Units").getUnit(amount, unit_label, id, false)
		else
			suffix = (unit_label or id)
		end
	end
	if suffix ~= '' then
		if lang_ok == 0 then
			suffix = " <span style='background-color:yellow' class='wronglang'>" .. suffix .. "</span>" .. addLabelIcon(id, lang, parameters.lang[1], parameters.editicon)
		else
			suffix = ' ' .. suffix
		end
	end
	return suffix
end

local function roundPrecision(in_num, out_num)
	-- rounds out_num with significant figures of in_num (default precision)
	-- first, count digits after decimal mark, handling cases like '12.345e6'
	local exponent, prec
	local integer, dot, decimals, expstr = in_num:match('^(%d*)(%.?)(%d*)(.*)')
	local e = expstr:sub(1, 1)
	if e == 'e' or e == 'E' then
		exponent = tonumber(expstr:sub(2))
	end
	if dot == '' then
		prec = -integer:match('0*$'):len()
	else
		prec = #decimals
	end
	if exponent then
		-- So '1230' and '1.23e3' both give prec = -1, and '0.00123' and '1.23e-3' give 5.
		prec = prec - exponent
	end
	-- significant figures
	local in_bracket = 10^-prec -- -1 -> 10, 5 -> 0.00001
	local out_bracket = in_bracket * out_num / in_num
	out_bracket = 10^math.floor(math.log10(out_bracket)+.5) -- 1230 -> 1000, 0.00123 -> 0.001
	-- round it (credit to Luc Bloom from http://lua-users.org/wiki/SimpleRound)
	return math.floor(out_num/out_bracket + (out_num >=0 and 1 or -1) * 0.5) * out_bracket
end

-- format data value quantity
local function printDatavalueQuantity(data, parameters)
	local amount = data.amount
	amount = mw.ustring.gsub(amount, "%+", "")
	local suffix = ""
	local conv_amount, conv_suffix
	if string.sub(parameters.formatting or '', 1, 4) == "unit" or parameters.convert then
		-- example "unit": "http://www.wikidata.org/entity/Q174728"
		local unit_id = data.unit
		unit_id = mw.ustring.sub(unit_id, mw.ustring.find(unit_id, "Q"), -1)
		if string.sub(unit_id, 1, 1) == "Q" then
			local convert_to
			if parameters.convert == "default" or parameters.convert == "default2" then
				local exist, units = pcall(require, wiki.module_title .. "/Units")
				if exist 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
			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][parameters.convert], "$1", amount))
					conv_amount = math.floor(tonumber(amount_f) + 0.5)
				else
					local conversions = mw.wikibase.getAllStatements(unit_id, 'P2442') -- conversion to standard unit
					table.insert(conversions, mw.wikibase.getBestStatements(unit_id, 'P2370')[1]) -- conversion to SI unit
					for _, conv in ipairs(conversions) do
						if conv.mainsnak.snaktype == 'value' then -- no somevalue nor novalue
							if conv.mainsnak.datavalue.value.unit == "http://www.wikidata.org/entity/" .. convert_to then
								conv_amount = roundPrecision(amount, amount * tonumber(conv.mainsnak.datavalue.value.amount))
								break
							end
						end
					end
				end
			end
			suffix = getUnit(amount, unit_id, parameters)
			if conv_amount then conv_suffix = getUnit(conv_amount, convert_to, parameters) end
		end
	end
	local lang_obj = mw.language.new(parameters.lang[1])
	local sortkey = string.format("%019d", tonumber(amount) * 1000)
	amount = lang_obj:formatNum(tonumber(amount))
	if conv_amount then
		local conv_sortkey = string.format("%019d", tonumber(conv_amount) * 1000)
		conv_amount = lang_obj:formatNum(tonumber(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
	end
	return amount .. suffix, sortkey
end

-- format data value time
local function printDatavalueTime(data, parameters)
	-- Dates and times are stored in ISO 8601 format
	local timestamp = data.time
	local post_format
	local calendar_add = ""
	
	if string.sub(timestamp, 1, 1) == '-' then
		post_format = i18n.datetime["bc"]
	elseif string.sub(timestamp, 2, 3) == '00' then
		post_format = i18n.datetime["ad"]
	else
		-- calendar model
		local calendar_model = {["Q12138"] = "gregorian", ["Q1985727"] = "gregorian", ["Q11184"] = "julian", ["Q1985786"] = "julian"}
		local calendar_id = mw.text.split(data.calendarmodel, 'entity/')[2]
		if (timestamp < "+1582-10-15T00:00:00Z" and calendar_model[calendar_id] == "gregorian")
			or (timestamp > "+1582-10-04T00:00:00Z" and calendar_model[calendar_id] == "julian")
			then
			calendar_add = " <sup>(" .. mw.message.new('Wikibase-time-calendar-' .. calendar_model[calendar_id]):inLanguage(parameters.lang[1]):plain() .. ")</sup>"
		end
	end
	
	local function d(f, t)
		local ts = t or timestamp
		local form = type(f) == "function" and f(ts) or f -- function in i18n.datetime[precision]
		if string.sub(ts, 1, 1) == '-' then ts = '+' .. string.sub(ts, 2) end -- formatDate() only supports years from 0
		return mw.language.new(parameters.lang[1]):formatDate(form, ts)
	end
	
	local function postFormat(t)
		if post_format and mw.ustring.find(post_format, "$1") then
			return mw.ustring.gsub(post_format, "$1", t)
		end
		return t
	end
	
	local precision = data.precision or 11
	local intyear = tonumber(string.match(timestamp, "[+-](%d+)"))
	local ret = ""
	
	if precision <= 5 then -- precision is 10000 years or more
		local factor = 10 ^ ((5 - precision) + 4)
		local y2 = math.ceil(math.abs(intyear) / factor)
		local relative = mw.ustring.gsub(i18n.datetime[precision], "$1", tostring(y2))
		if post_format == i18n.datetime["bc"] then
			ret = mw.ustring.gsub(i18n.datetime.beforenow, "$1", relative)
		else
			ret = mw.ustring.gsub(i18n.datetime.afternow, "$1", relative)
		end
		local ret_number = string.match(ret, "%d+")
		if ret_number ~= nil then
			ret = mw.ustring.gsub(ret, ret_number, mw.language.new(parameters.lang[1]):formatNum(tonumber(ret_number)))
		end
	elseif precision == 6 then -- millennia
		local card = math.floor((intyear - 1) / 1000) + 1
		if mw.ustring.find(i18n.datetime[6], "$1") then
			ret = mw.ustring.gsub(i18n.datetime[6], "$1", tostring(card))
		else
			ret = d(i18n.datetime[6], string.format("%04d", tostring(card)))
		end
		ret = postFormat(ret)
	elseif precision == 7 then -- centuries
		local card = math.floor((math.abs(intyear) - 1) / 100) + 1
		if mw.ustring.find(i18n.datetime[7], "$1") then
			ret = mw.ustring.gsub(i18n.datetime[7], "$1", tostring(card))
		else
			ret = d(i18n.datetime[7], string.format("%04d", tostring(card)))
		end
		ret = postFormat(ret) .. calendar_add
	elseif precision == 8 then -- decades
		local card = math.floor(math.abs(intyear) / 10) * 10
		ret = postFormat(mw.ustring.gsub(i18n.datetime[8], "$1", tostring(card))) .. calendar_add
	elseif precision == 9 or parameters.formatting == 'Y' then -- precision is year
		ret = postFormat(tostring(intyear)) .. calendar_add
	elseif precision == 10 then -- month
		timestamp = timestamp .. " + 1 day" -- formatDate yyyy-mm-00 returns the previous month
		ret = d(i18n.datetime[10])
		ret = postFormat(ret) .. calendar_add
		ret, _ = string.gsub(ret, "([ %[])0+", "%1") -- supress leading zeros in year, optionally linked
	else -- precision 11, day
		ret = d(parameters.formatting or i18n.datetime[11])
		ret = postFormat(ret) .. calendar_add
		ret, _ = string.gsub(ret, "([ %[])0+", "%1")
	end
	return ret, timestamp
end

-- format data value entity
local function printDatavalueEntity(data, parameters)
	local entity_id = data['id']
	local entity_page = 'Special:EntityPage/' .. entity_id
	if parameters.formatting == 'raw' then
		if data['entity-type'] == 'item' then
			entity_id = resolveEntityId(entity_id)
		end
		return entity_id, entity_id
	end
	local label, lang, lang_ok = getLabelByLangs(entity_id, parameters.lang)
	local sitelink = mw.wikibase.getSitelink(entity_id)
	local parameter = parameters.formatting
	local labelcase = label or sitelink
	if parameters.gender == 'feminineform' then
		labelcase = feminineForm(entity_id, lang) or labelcase
	end
	if parameters.case ~= 'gender' then
		labelcase = case(parameters.case, labelcase, lang, entity_id, parameters.id)
	end
	local ret1, ret2
	if parameter == 'label' then
		ret1 = labelcase or entity_id
		ret2 = labelcase or entity_id
	elseif parameter == 'sitelink' then
		ret1 = (sitelink or 'wikidata:' .. entity_page)
		ret2 = sitelink or entity_id
	elseif mw.ustring.find((parameter or ''), '$1', 1, true) then -- formatting = a pattern
		ret1 = mw.ustring.gsub(parameter, '$1', labelcase or entity_id)
		ret1 = expandBraces(ret1, parameter)
		ret2 = labelcase or entity_id
	else
		if parameter == "ucfirst" or parameter == "ucinternallink" then
			if labelcase and lang then
				labelcase = mw.language.new(lang):ucfirst(labelcase)
			end
			-- only first of a list, reset formatting for next ones
			if parameter == "ucinterlanllink" then
				parameters.formatting = 'internallink'
			else
				parameters.formatting = nil -- default format
			end
		end
		
		if sitelink then
			ret1 = '[[' .. sitelink .. '|' .. labelcase .. ']]'
			ret2 = labelcase
		elseif label and (parameter == 'internallink' or parameter == 'ucinternallink') then
			ret1 = '[[' .. label .. '|' .. labelcase .. ']]'
			ret2 = labelcase
		else
			ret1 = '[[wikidata:' .. entity_page .. '|' .. (labelcase or entity_id) .. ']]'
			ret2 = labelcase or entity_id
		end
	end
	if lang_ok == 0 then
		ret1 = "<span style='background-color:yellow' class='wronglang'>" .. ret1 .."</span>" .. addLabelIcon(entity_id, lang, parameters.lang[1], parameters.editicon)
	end
	
	return ret1, ret2
end

-- format data value monolingualtext
local function printDatavalueMonolingualText(data, parameters)
	-- data fields: language [string], text [string]
	
	if parameters.list == "lang" and data["language"] ~= parameters.lang[1] then
		return
	elseif parameters.formatting == "language" or parameters.formatting == "text" then
		return data[parameters.formatting]
	end
	local result = data["text"]
	if data["language"] ~= wiki.langcode then
		result = mw.ustring.gsub('<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 printError(key)
	return '<span class="error">' .. i18n.errors[key] .. '</span>'
end

-- the "qualifiers" and "snaks" field have a respective "qualifiers-order" and "snaks-order" field
-- use these as the second parameter and this function instead of the built-in "pairs" function
-- to iterate over all qualifiers and snaks in the intended order.
local function orderedpairs(array, order)
	if not order then return pairs(array) end

	-- return iterator function
	local i = 0
	return function()
		i = i + 1
		if order[i] then
			return order[i], array[order[i]]
		end
	end
end

function findClaims(entityId, property)
	if not property or not entityId then return end
	
	if not mw.ustring.match(property, "^P%d+$") then
		-- get property id for the given label
		property = mw.wikibase.resolvePropertyId(property)
		if not property then return end
	end
	local claims = mw.wikibase.getAllStatements(entityId, property)
	if #claims == 0 then
		claims = nil
	end
	return claims
end

local function getSnakValue(snak, parameters)
	if snak.snaktype == 'value' then
		-- call the respective snak parser
		if snak.datatype == 'math' then
			return printDatatypeMath(snak.datavalue.value)
		elseif snak.datatype == 'musical-notation' then
			return printDatatypeMusical(snak.datavalue.value, parameters.formatting)
		elseif snak.datatype == "url" then
			return printDatatypeUrl(snak.datavalue.value, parameters)
		elseif snak.datatype == "commonsMedia" then
			return printDatatypeMedia(snak.datavalue.value, parameters)
		elseif snak.datavalue.type == "string" then
			return printDatavalueString(snak.datavalue.value, parameters)
		elseif snak.datavalue.type == "globecoordinate" then
			return printDatavalueCoordinate(snak.datavalue.value, parameters.formatting)
		elseif snak.datavalue.type == "quantity" then
			return printDatavalueQuantity(snak.datavalue.value, parameters)
		elseif snak.datavalue.type == "time" then
			return printDatavalueTime(snak.datavalue.value, parameters)
		elseif snak.datavalue.type == 'wikibase-entityid' then
			return printDatavalueEntity(snak.datavalue.value, parameters)
		elseif snak.datavalue.type == 'monolingualtext' then
			return printDatavalueMonolingualText(snak.datavalue.value, parameters)
		end
	elseif snak.snaktype == 'novalue' then
		if parameters.formatting == 'raw' then return end
		return mw.message.new('Wikibase-snakview-snaktypeselector-novalue'):inLanguage(parameters.lang[1]):plain()
	elseif snak.snaktype == 'somevalue' then
		if parameters.formatting == 'raw' then return end
		return mw.message.new('Wikibase-snakview-snaktypeselector-somevalue'):inLanguage(parameters.lang[1]):plain()
	end
	return mw.wikibase.renderSnak(snak)
end

local function getQualifierSnak(claim, qualifierId, parameters)
	-- a "snak" is Wikidata terminology for a typed key/value pair
	-- a claim consists of a main snak holding the main information of this claim,
	-- as well as a list of attribute snaks and a list of references snaks
	if qualifierId then
		-- search the attribute snak with the given qualifier as key
		if claim.qualifiers then
			local qualifier = claim.qualifiers[qualifierId]
			if qualifier then
				if qualifier[1].datatype == "monolingualtext" then
					-- iterate over monolingualtext qualifiers to get local language
					for idx in pairs(qualifier) do
						if qualifier[idx].datavalue.value and qualifier[idx].datavalue.value.language == parameters.lang[1] then
							return qualifier[idx]
						end
					end
				elseif parameters.list then
					return qualifier
				else
					return qualifier[1]
				end
			end
		end
		return nil, printError("qualifier-not-found")
	else
		-- otherwise return the main snak
		return claim.mainsnak
	end
end

function getValueOfClaim(claim, qualifierId, parameters)
	local error
	local snak
	snak, error = getQualifierSnak(claim, qualifierId, parameters)
	if not snak then
		return nil, nil, error
	elseif snak[1] then -- a multi qualifier
		local result = {}
		local sortkey = {}
		for idx in pairs(snak) do
			result[#result + 1], sortkey[#sortkey + 1] = getSnakValue(snak[idx], parameters)
		end
		return mw.text.listToText(result, parameters.qseparator, parameters.qconjunction), sortkey[1]
	else -- a property or a qualifier
		return getSnakValue(snak, parameters)
	end
end

local function getValueOfParentClaim(claim, qualifierId, parameters)
	local qids = mw.text.split(qualifierId, '/', true)
	local valueraw, parent_claims, value, sortkey
	if qids[1] == parameters.property then
		valueraw, _, _ = getValueOfClaim(claim, nil, {["formatting"]="raw", ["lang"]=parameters.lang})
	else
		valueraw, _, _ = getValueOfClaim(claim, qids[1], {["formatting"]="raw", ["lang"]=parameters.lang})
	end
	if string.sub(valueraw or '', 1, 1) == "Q" then -- protection for 'no value'
		parent_claims = mw.wikibase.getBestStatements(valueraw, qids[2])
		if parent_claims[1] ~= nil then
			value, sortkey, _ = getValueOfClaim(parent_claims[1], nil, parameters)
			-- raw parent value needed for while/black lists, lang for avoiding an error on types other than entity
			valueraw, _, _ = getValueOfClaim(parent_claims[1], nil, {["formatting"]="raw", ["lang"]=parameters.lang})
		end
	end
	return value, sortkey, valueraw
end

local function getReferences(claim)
	local 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
		-- 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
					refparts = refparts or '' .. getSnakValue(snakval[snakidx], {lang={wiki.langcode}})
				end
				refs[snakkey] = refparts
				refparts = nil
			end
		end
		
		-- get title of general template for citing web references
		local template = mw.wikibase.getSitelink('Q5637226') or ""
		template = mw.text.split(template, ":")[2] -- split off namespace from front
		
		-- if both "reference URL" and "title" (or "stated in") are present, then use local cite web template
		if refs['P854'] and (refs['P1476'] or refs['P248']) and template then
			local citeParams = {}
			citeParams[i18n['cite']['url']] = refs['P854']
			citeParams[i18n['cite']['title']] = refs['P1476'] or refs['P248']:match("^%[%[.-|(.-)%]%]")
			citeParams[i18n['cite']['author']] = refs['P50']
			citeParams[i18n['cite']['website']] = refs['P248']
			citeParams[i18n['cite']['language']] = refs['P2439']
			citeParams[i18n['cite']['publisher']] = refs['P123']
			citeParams[i18n['cite']['date']] = refs['P577']
			citeParams[i18n['cite']['date']] = refs['P577']
			citeParams[i18n['cite']['date']] = refs['P577']
			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, args=citeParams}
		else
			-- raw ouput
			for k, v in orderedpairs(refs or {}, claim.references[ref]["snaks-order"]) do
				if k and v then
					if refparts then refparts = refparts .. ", " else refparts = "" end
					refparts = refparts .. tostring(mw.wikibase.getLabel(k)) .. ": "
					refparts = refparts .. v
				end
			end
		end
		if refparts then result = result .. mw.getCurrentFrame():extensionTag("ref", refparts) end
	end
	return result
end

-- Set whitelist or blacklist values
local function setWhiteOrBlackList(type_list, num_qual, args)
	local listed = false
	local list = {}
	for i = 0, num_qual do
		if isSet(args[type_list .. i]) then
			listed = true
			list[tostring(i)] = {}
			local values = mw.text.split(args[type_list .. i], "/", true)
			for _, v in ipairs(values) do
				list[tostring(i)][v] = true
				list[tostring(i)][resolveEntityId(v)] = true
			end
		end
	end
	return list, listed
end

local function tableParameters(args, parameters, column)
	local column_params = mw.clone(parameters)
	column_params.formatting = args["colformat"..column]; if column_params.formatting == "" then column_params.formatting = nil end
	column_params.convert = args["convert" .. column]
	if args["case" .. column] then
		column_params.case = args["case" .. column]
	end
	return column_params
end

local function getEntityId(frame, unnamed)
	local args = frame.args or frame
	local pargs = frame.args and frame:getParent().args
	local id = args.item or args.from or (unnamed and mw.text.trim(args[1]) or nil)
	if not isSet(id) and pargs then
		id = pargs.item or pargs.from or (unnamed and mw.text.trim(pargs[1]) or nil)
	end
	if not isSet(id) then
		id = mw.wikibase.getEntityIdForCurrentPage()
	end
	return id
end

-- Main function claim ---------------------------------------------
-- on debug console use: =p.claim{item="Q...", property="P...", ...}
function p.claim(frame)
	local args = frame.args or frame -- via invoke or require
	--If a value is already set, use it
	if isSet(args.value) then
		if args.value == 'NONE' then
			return
		else
			return args.value
		end
	end
	
	-- arguments
	local id = getEntityId(frame)
	if id == nil then return end
	local property = string.upper(args.property or "")
	local qualifierId = {}
	qualifierId[1] = isSet(args.qualifier) and string.upper(args.qualifier) or nil
	local i = 2
	while isSet(args["qualifier" .. i]) do
		qualifierId[i] = string.upper(args["qualifier" .. i])
		i = i + 1
	end
	local formatting = args.formatting; if formatting == "" then formatting = nil end
	local convert = args.convert; if convert == "" then convert = nil end
	local case = args.case
	local list = args.list or true; if (list == "false" or list == "no") then list = false end
	local sorting_col = args.tablesort
	local sorting_up = (args.sorting or "") ~= "-1"
	local separator = args.separator
	local conjunction = args.conjunction or args.separator
	local rowformat = args.rowformat
	local references = args.references
	local showerrors = args.showerrors
	local default = args.default
	if default then showerrors = nil end
	local editicon = not (args.editicon == "false" or args.editicon == "no" or formatting == "raw")
	local dotranslations = args["dotranslations"] or false
	
	local parameters = {["id"] = id, ["property"] = property, ["formatting"] = formatting, ["convert"] = convert,
		["list"] = list, ["case"] = case, ["editicon"] = editicon, ["dotranslations"] = dotranslations, 
		["separator"] = separator, ["conjunction"] = conjunction, ["qseparator"] = separator, ["qconjunction"] = conjunction}
	
	-- defaults for table
	local preformat, postformat = "", ""
	local whitelisted, blacklisted = false, false
	local whitelist, blacklist = {}, {}
	if parameters.formatting == "table" then
		parameters.separator = parameters.separator or "<br />"
		parameters.conjunction = parameters.conjunction or "<br />"
		parameters.qseparator = ", "
		parameters.qconjunction = ", "
		if not rowformat then
			rowformat = "$0 ($1"
			i = 2
			while qualifierId[i] do
				rowformat = rowformat .. ", $" .. i
				i = i + 1
			end
			rowformat = rowformat .. ")"
		elseif mw.ustring.find(rowformat, "^[*#]") then
			parameters.separator = "</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, whitelisted = setWhiteOrBlackList("whitelist", #qualifierId, args)
		blacklist, blacklisted = setWhiteOrBlackList("blacklist", #qualifierId, args)
	end
	
	-- fetch property
	local claims
	for p in string.gmatch(property, 'P%d+') do
		claims = findClaims(id, p)
		if claims and claims[1] then
			break
		end
	end
	if not claims or not claims[1] then
		if showerrors then return printError("property-not-found") else return default end
	end
	
	-- find language and defaults
	parameters.lang = findLang(args.lang)
	
	-- set feminine case if gender is requested
	local itemgender = args["itemgender"]
	local idgender
	if itemgender then
		if string.match(itemgender, "^P%d+$") then
			local snak = mw.wikibase.getBestStatements(id, itemgender)[1]
			if snak and snak.mainsnak and snak.mainsnak.datavalue and snak.mainsnak.datavalue.value then
				idgender = snak.mainsnak.datavalue.value.id
			end
		elseif string.match(itemgender, "^Q%d+$") then
			idgender = itemgender
		end
	end
	local gender_requested = false
	if parameters.case == "gender" or idgender then
		gender_requested = true
	elseif parameters.formatting == "table" then
		for i=0, #qualifierId do
			if args["case" .. i] and args["case" .. i] == "gender" then
				gender_requested = true
				break
			end
		end
	end
	if gender_requested then
		if feminineGender(idgender or id) then
			parameters.gender = "feminineform"
		end
	end
	
	-- get initial sort indices
	local sortindices = {}
	for idx in pairs(claims) do
		sortindices[#sortindices + 1] = idx
	end
	-- sort by claim rank
	local comparator = function(a, b)
		local rankmap = { deprecated = 2, normal = 1, preferred = 0 }
		local ranka = rankmap[claims[a].rank or "normal"] .. string.format("%08d", a)
		local rankb = rankmap[claims[b].rank or "normal"] .. string.format("%08d", b)
		return ranka < rankb
	end
	table.sort(sortindices, comparator)
	
	local result
	local error
	if parameters.list or parameters.formatting == "table" then
		-- convert LF to line feed, <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 firstrank = parameters.list == "firstrank" and claims[sortindices[1]].rank or ''
		local rowlist = {} -- rows to list with whitelist or blacklist
		for idx in pairs(claims) do
			local claim = claims[sortindices[idx]]
			local reference = {}
			if not whitelisted then rowlist[idx] = true end
			if firstrank ~= '' and firstrank ~= claim.rank then
				break
			end
			if parameters.formatting == "table" then
				local params = tableParameters(args, parameters, "0")
				value, sortkey, error = getValueOfClaim(claim, nil, params)
				if value then
					values[#values + 1] = {}
					sortkeys[#sortkeys + 1] = {}
					refs[#refs + 1] = {}
					if whitelist["0"] or blacklist["0"] then
						local valueraw, _, _ = getValueOfClaim(claim, nil, {["formatting"]="raw", ["lang"]=params.lang})
						
						-- Whitelist if QID match
						if whitelist["0"] and whitelist["0"][valueraw or ""] then
							rowlist[#values] = true
						-- Blacklist if value is not defined	
						elseif whitelist["0"] and whitelist["0"]["*"] and valueraw then
							rowlist[#values] = true
						-- Blacklist if QID match	
						elseif blacklist["0"] and blacklist["0"][valueraw or ""] then
							rowlist[#values] = false
						-- Blacklist if value is defined	
						elseif blacklist["0"] and blacklist["0"]["*"] and valueraw then
							rowlist[#values] = false
						end
					end

					for i, qual in ipairs(qualifierId) do
						local j = tostring(i)
						params = tableParameters(args, parameters, j)
						local valueq, sortkeyq, valueraw
						if qual == property then -- hack for getting the property with another formatting, i.e. colformat1=raw
							valueq, sortkeyq, _ = getValueOfClaim(claim, nil, params)
						else
							for q in mw.text.gsplit(qual, '%s*OR%s*') do
								if string.find(q, ".+/.+") then
									valueq, sortkeyq, valueraw = getValueOfParentClaim(claim, q, params)
								elseif string.find(q, "^/.+") then
									local claim2 = findClaims(id, string.sub(q, 2))
									if claim2 then
										valueq, sortkeyq, _ = getValueOfClaim(claim2[1], nil, params)
									end
								else
									valueq, sortkeyq, _ = getValueOfClaim(claim, q, params)
								end
								if valueq then break end
							end
						end
						values[#values]["col" .. j] = valueq
						sortkeys[#sortkeys]["col" .. j] = sortkeyq or valueq
						if whitelist[j] or blacklist[j] then
							valueq = valueraw or getValueOfClaim(claim, qual, {["formatting"]="raw", ["lang"]=params.lang})
							-- Whitelist if QID match	
							if whitelist[j] and whitelist[j][valueq or ""] then
								rowlist[#values] = true
							-- Blacklist if value is not defined	
							elseif whitelist[j] and whitelist[j]["*"] and valueq then
								rowlist[#values] = true
							-- Blacklist if QID match	
							elseif blacklist[j] and blacklist[j][valueq or ""] then
								rowlist[#values] = false
							-- Blacklist if value is defined	
							elseif blacklist[j] and blacklist[j]["*"] and valueq then
								rowlist[#values] = false
							end
						end
					end
				end
			else
				value, sortkey, error = getValueOfClaim(claim, qualifierId[1], parameters)
				values[#values + 1] = {}
				sortkeys[#sortkeys + 1] = {}
				refs[#refs + 1] = {}
			end
			if not value and showerrors then value = error end
			if value then
				if references and claim.references then reference = claim.references end
				refs[#refs]["col0"] = reference
				values[#values]["col0"] = value
				sortkeys[#sortkeys]["col0"] = sortkey or value
			end
		end
		-- sort and format results
		sortindices = {}
		for idx in pairs(values) do
			sortindices[#sortindices + 1] = idx
		end
		if sorting_col then
			local sorting_table = mw.text.split(sorting_col, '/', true)
			local comparator = function(a, b)
				local valuea, valueb
				local i = 1
				while valuea == valueb and i <= #sorting_table do
					valuea = sortkeys[a]["col" .. sorting_table[i]] or ''
					valueb = sortkeys[b]["col" .. sorting_table[i]] or ''
					i = i + 1
				end
				
				if sorting_up then
					return valueb > valuea
				end
				return valueb < valuea
			end
			table.sort(sortindices, comparator)
		end
		result = {}
		for idx in pairs(values) do
			local valuerow = values[sortindices[idx]]
			local reference = getReferences({["references"] = refs[sortindices[idx]]["col0"]})
			
			value = valuerow["col0"]
			if parameters.formatting == "table" then
				if not rowlist[sortindices[idx]] then
					value = nil
				else
					value = mw.ustring.gsub(rowformat .. "$", "$0", value) -- fake end character added for easy gsub
					value = mw.ustring.gsub(value, "$R0", reference) -- add reference
					local rowformatting = rowformat .. "$"
					for i, _ in ipairs(qualifierId) do
						local valueq = valuerow["col" .. i]
						if args["rowsubformat" .. i] and valueq then
							-- add fake end character $
							-- gsub $i not followed by a number so $1 doesn't match $10, $11...
							-- remove fake end character
							valueq = mw.ustring.gsub(args["rowsubformat" .. i] .. "$", "$" .. i .. "(%D)", valueq .. "%1")
							valueq = string.sub(valueq, 1, -2)
							rowformatting = mw.ustring.gsub(rowformatting, "$" .. i .. "(%D)", args["rowsubformat" .. i] .. "%1")
						end
						value = mw.ustring.gsub(value, "$" .. i .. "(%D)", (valueq or '') .. "%1") -- version 20200130
					end
					value = string.sub(value, 1, -2) -- remove fake end character
					value = expandBraces(value, rowformatting)
				end
			elseif value then
				value = expandBraces(value, parameters.formatting)
			end
			if isSet(value) then
				if string.find(value, "wronglang") then
					if parameters.dotranslations then
						result[#result + 1] = value .. "[[Category:Wikipedia:Links to Wikidata element with missing Northern Sami label]]"
					end
				else
					result[#result + 1] = value
				end
			end
			if not parameters.list then
				break
			end
		end
		result = preformat .. mw.text.listToText(result, parameters.separator, parameters.conjunction) .. postformat
	else
		-- return first element
		local claim = claims[sortindices[1]]
		result, _, error = getValueOfClaim(claim, qualifierId[1], parameters)
		if result and references then result = result .. getReferences(claim) end
	end
	
	if isSet(result) then
		return result
	else
		if showerrors then return error else return default end
	end
end

-- This is used to get the TA98 (Terminologia Anatomica first edition 1998) values like 'A01.1.00.005' (property P1323)
-- which are then linked to http://www.unifr.ch/ifaa/Public/EntryPage/TA98%20Tree/Entity%20TA98%20EN/01.1.00.005%20Entity%20TA98%20EN.htm
-- uses the newer mw.wikibase calls instead of directly using the snaks
-- formatPropertyValues returns a table with the P1323 values concatenated with ", " so we have to split them out into a table in order to construct the return string
p.getTAValue = function(frame)
	local ent = mw.wikibase.getEntity()
	local props = ent:formatPropertyValues('P1323')
	local out = {}
	local t = {}
	for k, v in pairs(props) do
		if k == 'value' then
			t = mw.text.split( v, ", ")
			for k2, v2 in pairs(t) do
				out[#out + 1] = "[http://www.unifr.ch/ifaa/Public/EntryPage/TA98%20Tree/Entity%20TA98%20EN/" .. string.sub(v2, 2) .. "%20Entity%20TA98%20EN.htm " .. v2 .. "]"
			end
		end
	end
	local ret = table.concat(out, "<br> ")
	if #ret == 0 then
		ret = "Invalid TA"
	end
	return ret
end

-- Local functions for getParentValues -----------------------

local function uc_first(word)
	return mw.ustring.upper(mw.ustring.sub(word, 1, 1)) .. mw.ustring.sub(word, 2)
end

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

local function contains(tab, val)
	for index, value in ipairs(tab) do
		if value == val then
			return true
		end
	end
	
	return false
end

local function getParentObjects(id,
	formatting,
	languages, 
	propertySupString, 
	propertyLabel,
	propertyLink,
	labelShow,
	editicon,
	upto,
	upto_linkId,
	last_only,
	grammatical_case,
	include_self)
	
	if (upto_linkId == nil) then upto_linkId = "" end
	local upto_link_ids = mw.text.split(upto_linkId, '[/%s]+')
	local propertySups = mw.text.split(propertySupString, '[/%s]+')
	
	local lastlabel = uc_first(upto or '')
	local maxloop = tonumber(upto) or (lastlabel == '' and 10 or 50)
	
	local labelFilter = {}
	if labelShow then
		for i, v in ipairs(mw.text.split(labelShow, "/")) do
			labelFilter[uc_first(v)] = true
		end
	end
	
	local label_self
	_, label_self = getPropertyValue(id, propertyLabel, "label", languages)
	
	local result = {}
	
	local label, link, linktext
	
	for iter = 1, maxloop do
		local label, link
		for _, propertySup in pairs(propertySups) do 	
			_id, _link = getPropertyValue(id, propertySup, formatting, languages, editicon, grammatical_case)		
			if _id and _link then id = _id link = _link break end 
		end
		
		if not id or not link then break end
		
		if propertyLink then
			_, linktext = getPropertyValue(id, propertyLink, "label", languages)
			if linktext then
				link = mw.ustring.gsub(link, "%[%[(.*)%|.+%]%]", "[[%1|" .. linktext .. "]]")
			end
		end
		
		_, label = getPropertyValue(id, propertyLabel, "label", languages, editicon, "infoboxlabel")
		
		if labelShow == nil or labelFilter[label] then
			result[#result + 1] = {label, link}
			
			if label then 
				labelFilter[label] = nil -- only first label found
			end
		end
		
		if not tonumber(upto) and label == lastlabel then
			break
		end
		
		if contains(upto_link_ids, id) then
			break
		end
	end
	
	if last_only then
		result = {result[#result]}
	end
	
	if include_self then table.insert(result, 1, {label_self, mw.title.getCurrentTitle().text}) end
	
	return result
end

local function parentObjectsToString(result,
	rowformat,
	separator, 
	cascade,
	sorting)
	
	local ret = {}
	local first = 1
	local last = #result
	local iter = 1
	if sorting == "-1" then first = #result; last = 1; iter = -1 end
	for i = first, last, iter do
		local rowtext = mw.ustring.gsub(rowformat, "$[01]", {["$0"] = result[i][1], ["$1"] = result[i][2]})
		ret[#ret +1] = expandBraces(rowtext, rowformat)
	end
	
	if cascade then
		local prefix = ""
		for i = 1, #ret do
			ret[i] = prefix .. "• " .. ret[i]
			prefix = prefix .. "&nbsp;"
		end
	end
	
	return mw.text.listToText(ret, separator, separator)
end

-- Returns pairs of instance label and property value fetching a recursive tree
function p.getParentValues(frame)
	local args = frame.args or frame -- via invoke or require
	local id = getEntityId(frame)
	if id == nil then return end
	local languages = findLang(args.lang)
	local propertySup = args["property"]; if not isSet(propertySup) then propertySup = "P131" end --administrative entity
	local propertyLabel = args["label"]; if not isSet(propertyLabel) then propertyLabel = "P31" end --instance
	local propertyLink = args["valuetext"]; if propertyLink == "" then propertyLink = nil end --internallink
	local upto = args["upto"]; if upto == "" then upto = nil end
	local last_only = (args.last_only == "true" or args.last_only == "yes")
	local labelShow = args["labelshow"]; if labelShow == "" then labelShow = nil end
	local editicon = not (args.editicon == "false" or args.editicon == "no")
	local include_self = (args.include_self == "true" or args.include_self == "yes")
	local case = args["case"]; if case == "" then case = nil end
	
	if isSet(args.uptolabelid) then
		upto = mw.wikibase.getLabel(args.uptolabelid)
	end
	
	if isSet(args.showlabelid) then
		local showLabelList = {} for substring in mw.text.gsplit(args.showlabelid, '[/%s]+') do table.insert(showLabelList, mw.wikibase.getLabel(substring)) end
		if #showLabelList > 0 then
			labelShow = table.concat(showLabelList,"/")
		end
	end
	
	local result = getParentObjects(id,
		args.formatting,
		languages, 
		propertySup, 
		propertyLabel,
		propertyLink,
		labelShow,
		editicon,
		upto,
		args.uptolinkid,
		last_only,
		case,
		include_self)
	
	local rowformat = args["rowformat"]; if not isSet(rowformat) then rowformat = "$0 = $1" end
	local separator = args["separator"]; if not isSet(separator) then separator = "<br />" end
	local sorting = args["sorting"]; if sorting == "" then sorting = nil end
	local cascade = (args.cascade == "true" or args.cascade == "yes")
	
	return parentObjectsToString(result,
		rowformat,
		separator,
		cascade,
		sorting)
end

-- Link with a parent label --------------------
function p.linkWithParentLabel(frame)
	local args = {}
	if frame.args then
		for k, v in pairs(frame.args) do -- metatable
			args[k] = v
		end
	else
		args = frame -- via require
	end
	if isSet(args.value) then
		return args.value
	end
	
	-- get id value of property/qualifier
	local largs = mw.clone(args)
	largs.list = "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_claims = findClaims(items_table[1], args.parent)
	if parent_claims and parent_claims[1].mainsnak.datatype == 'monolingualtext' then
		largs.formatting = nil
		largs.list = 'lang'
	else
		largs.formatting = "label"
		largs.list = "false"
	end
	largs.property = args.parent
	largs.qualifier = nil
	for i, v in ipairs(items_table) do
		largs.item = v
		local link_label = p.claim(largs)
		if isSet(link_label) then
			link_table[i] = mw.ustring.gsub(link_table[i] or '', "%[%[(.*)%|.+%]%]", "[[%1|" .. link_label .. "]]")
		end
	end
	
	return mw.text.listToText(link_table)
end

-- Calculate number of years old ----------------------------
function p.yearsOld(frame)
	local args = frame.args or frame -- via invoke or require
	local id = getEntityId(frame)
	local lang = mw.language.new('en')
	
	local function getBestValue(id, prop)
		local snak = mw.wikibase.getBestStatements(id, prop)[1]
		if snak and snak.mainsnak and snak.mainsnak.datavalue and snak.mainsnak.datavalue.value then
			return snak.mainsnak.datavalue.value
		end
	end
	
	local birth = getBestValue(id, 'P569')
	if type(birth) ~= 'table' or birth.time == nil or birth.precision == nil or birth.precision < 8 then
		return
	end
	local death = getBestValue(id, 'P570')
	if type(death) ~= 'table' or death.time == nil or death.precision == nil then
		death = {['time'] = lang:formatDate('c'), ['precision'] = 11} -- current date
	elseif death.precision < 8 then
		return
	end
	
	local dates = {}
	dates[1] = {['min'] = {}, ['max'] = {}, ['precision'] = birth.precision}
	dates[1].min.year = tonumber(mw.ustring.match(birth.time, "^[+-]?%d+"))
	dates[1].min.month = tonumber(mw.ustring.match(birth.time, "\-(%d%d)\-"))
	dates[1].min.day = tonumber(mw.ustring.match(birth.time, "\-(%d%d)T"))
	dates[1].max = mw.clone(dates[1].min)
	dates[2] = {['min'] = {}, ['max'] = {}, ['precision'] = death.precision}
	dates[2].min.year = tonumber(mw.ustring.match(death.time, "^[+-]?%d+"))
	dates[2].min.month = tonumber(mw.ustring.match(death.time, "\-(%d%d)\-"))
	dates[2].min.day = tonumber(mw.ustring.match(death.time, "\-(%d%d)T"))
	dates[2].max = mw.clone(dates[2].min)
	
	for i, d in ipairs(dates) do
		if d.precision == 10 then -- month
			d.min.day = 1
			local timestamp = string.format("%04d", tostring(math.abs(d.max.year)))
				.. string.format("%02d", tostring(d.max.month))
				.. "01"
			d.max.day = tonumber(lang:formatDate("j", timestamp .. " + 1 month - 1 day"))
		elseif d.precision < 10 then -- year or decade
			d.min.day = 1
			d.min.month = 1
			d.max.day = 31
			d.max.month = 12
			if d.precision == 8 then -- decade
				d.max.year = d.max.year + 9
			end
		end
	end
	
	local function age(d1, d2)
		local years = d2.year - d1.year
		if d2.month < d1.month or (d2.month == d1.month and d2.day < d1.day) then
			years = years - 1
		end
		if d2.year > 0 and d1.year < 0 then
			years = years - 1 -- no year 0
		end
		return years
	end
	
	local old_min = age(dates[1].max, dates[2].min)
	local old_max = age(dates[1].min, dates[2].max)
	local old, old_expr
	if old_min == 0 and old_max == 0 then
		old = "< 1"
		old_max = 1 -- expression in singular
	elseif old_min == old_max then
		old = old_min
	else
		old = old_min .. "/" .. old_max
	end
	if args.formatting == 'unit' then
		local langs = findLang(args.lang)
		local yo, yo_sg, yo_pl, yo_pau
		if langs[1] == wiki.langcode then
			yo_sg = i18n["years-old"].singular
			yo_pl = i18n["years-old"].plural
			yo_pau = i18n["years-old"].paucal
		end
		if not isSet(yo_pl) then
			yo_pl = getLabelByLangs('Q24564698', langs)
			yo_sg = yo_pl
		end
		if not isSet(yo_pau) then
			yo_pau = yo_pl
		end
		yo = mw.language.new(langs[1]):plural(old_max, {yo_sg, yo_pau, yo_pl})
		if mw.ustring.find(yo, '$1', 1, true) then
			old_expr = mw.ustring.gsub(yo, "$1", old)
		else
			old_expr = old .. '&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 id = mw.text.trim(args[1] or "")
	if not isSet(id) then return end
	local editicon = not (args.editicon == "false" or args.editicon == "no") and mw.wikibase.isValidEntityId(id)
	
	local label_icon = ''
	local label, lang
	local lang_ok=1
	if args.label then
		label = args.label
	else
		local languages = findLang(args.lang)
		-- exceptions or labels fixed
		local exist, labels = pcall(require, wiki.module_title .. "/labels" .. (languages[1] == wiki.langcode and '' or '/' .. languages[1]))
		if exist and next(labels.infoboxLabelsFromId) ~= nil then
			label = labels.infoboxLabelsFromId[id]
		end
		
		if label == nil then
			local new_id = resolveEntityId(id)
			if new_id then
				label, lang, lang_ok = getLabelByLangs(id, languages)
				if label then
					if args.itemgender and feminineGender(args.itemgender) then
						label = feminineForm(new_id, lang) or label
					end
					label = mw.language.new(lang):ucfirst(mw.text.nowiki(label)) -- sanitize
				end
				if lang_ok == 0 then
					label_icon = addLabelIcon(new_id or id, lang, languages[1], editicon)
				end
			end
		end
	end
	
	local linked = args.linked
	local ret = ""
	if isSet(linked) and linked ~= "no" then
		local article = mw.wikibase.getSitelink(id) or ("d:" .. id)
		ret = "[[" .. article .. "|" .. (label or id) .. "]]"
	else
		ret = (label or id)
	end
	if lang_ok == 0 then
		ret = "<span style='background-color:yellow' class='wronglang'>" .. ret .."</span>"
	end
	return ret .. label_icon
end

-- Utilities -----------------------------

-- debugging functions, see module ../debug.
function p.ViewSomething(frame)
	return require(wiki.module_title .. "/debug").ViewSomething(frame)
end

function p.Dump(frame)
	return require(wiki.module_title .. "/debug").Dump(frame)
end

function p.getEntityFromTree(frame)
	return require(wiki.module_title .. "/debug").getEntityFromTree(frame)
end

-- Copied from Module:Wikibase
function p.getSiteLink(frame)
	local id = getEntityId(frame, 1)
	if id == nil then
		return
	end
	return mw.wikibase.getSitelink(id, frame.args[2])
end

-- Helper function for the default language code used
function p.lang(frame)
	local lang = frame and frame.args[1] -- nil via require
	return findLang(lang)[1]
end

-- Number of statements
function p.numStatements(frame)
	local id = getEntityId(frame)
	if id == nil then
		return 0
	end
	local args = frame.args
	local prop = mw.text.trim(args[1])
	local num = mw.wikibase.getBestStatements(id, prop)
	return #num
end

-- Returns the first id or value of given property or nil if not found, not isValidEntityId or novalue/somevalue
-- See Module:Wikibase
function p.validProperty(frame)
	local property = mw.text.trim(frame.args[1])
	local item = getEntityId(frame)
	local type = frame.args.type or "id"
	local entity = mw.wikibase.getEntity(item)
	if not entity then return end
	if not entity.claims then return end
	local hasProp = entity.claims[property]
	if not hasProp then return end
	if not hasProp[1].mainsnak.datavalue then return end
	if type == "value" then return hasProp[1].mainsnak.datavalue.value end
	if not hasProp[1].mainsnak.datavalue.value.id then return end
	if not mw.wikibase.isValidEntityId(hasProp[1].mainsnak.datavalue.value.id) then return end
	return hasProp[1].mainsnak.datavalue.value.id
end

function p.getDescription(frame) -- fonction simpliste, mais bon, les descriptions Wikidata, c'est pas non plus super utile
	return  mw.wikibase.description(frame.args[1])
end

return p