Module:Wd
-- Original module located at [[:en:Module:Wd]] and [[:en:Module:Wd/i18n]].
 
 require(&quot;strict&quot;)
 local p = {}
 local arg = ...
 local i18n
 
 local function loadI18n(aliasesP, frame)
 	local title
 
 	if frame then
 		-- current module invoked by page/template, get its title from frame
 		title = frame:getTitle()
 	else
 		-- current module included by other module, get its title from ...
 		title = arg
 	end
 
 	if not i18n then
 		i18n = require(title .. &quot;/i18n&quot;).init(aliasesP)
 	end
 end
 
 p.claimCommands = {
 	property   = &quot;property&quot;,
 	properties = &quot;properties&quot;,
 	qualifier  = &quot;qualifier&quot;,
 	qualifiers = &quot;qualifiers&quot;,
 	reference  = &quot;reference&quot;,
 	references = &quot;references&quot;
 }
 
 p.generalCommands = {
 	label       = &quot;label&quot;,
 	title       = &quot;title&quot;,
 	description = &quot;description&quot;,
 	alias       = &quot;alias&quot;,
 	aliases     = &quot;aliases&quot;,
 	badge       = &quot;badge&quot;,
 	badges      = &quot;badges&quot;
 }
 
 p.flags = {
 	linked        = &quot;linked&quot;,
 	short         = &quot;short&quot;,
 	raw           = &quot;raw&quot;,
 	multilanguage = &quot;multilanguage&quot;,
 	unit          = &quot;unit&quot;,
 	-------------
 	preferred     = &quot;preferred&quot;,
 	normal        = &quot;normal&quot;,
 	deprecated    = &quot;deprecated&quot;,
 	best          = &quot;best&quot;,
 	future        = &quot;future&quot;,
 	current       = &quot;current&quot;,
 	former        = &quot;former&quot;,
 	edit          = &quot;edit&quot;,
 	editAtEnd     = &quot;edit@end&quot;,
 	mdy           = &quot;mdy&quot;,
 	single        = &quot;single&quot;,
 	sourced       = &quot;sourced&quot;
 }
 
 p.args = {
 	eid  = &quot;eid&quot;,
 	page = &quot;page&quot;,
 	date = &quot;date&quot;
 }
 
 local aliasesP = {
 	coord                   = &quot;P625&quot;,
 	-----------------------
 	image                   = &quot;P18&quot;,
 	author                  = &quot;P50&quot;,
 	authorNameString        = &quot;P2093&quot;,
 	publisher               = &quot;P123&quot;,
 	importedFrom            = &quot;P143&quot;,
 	statedIn                = &quot;P248&quot;,
 	pages                   = &quot;P304&quot;,
 	language                = &quot;P407&quot;,
 	hasPart                 = &quot;P527&quot;,
 	publicationDate         = &quot;P577&quot;,
 	startTime               = &quot;P580&quot;,
 	endTime                 = &quot;P582&quot;,
 	chapter                 = &quot;P792&quot;,
 	retrieved               = &quot;P813&quot;,
 	referenceURL            = &quot;P854&quot;,
 	sectionVerseOrParagraph = &quot;P958&quot;,
 	archiveURL              = &quot;P1065&quot;,
 	title                   = &quot;P1476&quot;,
 	formatterURL            = &quot;P1630&quot;,
 	quote                   = &quot;P1683&quot;,
 	shortName               = &quot;P1813&quot;,
 	definingFormula         = &quot;P2534&quot;,
 	archiveDate             = &quot;P2960&quot;,
 	inferredFrom            = &quot;P3452&quot;,
 	typeOfReference         = &quot;P3865&quot;,
 	column                  = &quot;P3903&quot;
 }
 
 local aliasesQ = {
 	percentage              = &quot;Q11229&quot;,
 	prolepticJulianCalendar = &quot;Q1985786&quot;,
 	citeWeb                 = &quot;Q5637226&quot;,
 	citeQ                   = &quot;Q22321052&quot;
 }
 
 local parameters = {
 	property  = &quot;%p&quot;,
 	qualifier = &quot;%q&quot;,
 	reference = &quot;%r&quot;,
 	alias     = &quot;%a&quot;,
 	badge     = &quot;%b&quot;,
 	separator = &quot;%s&quot;,
 	general   = &quot;%x&quot;
 }
 
 local formats = {
 	property              = &quot;%p[%s][%r]&quot;,
 	qualifier             = &quot;%q[%s][%r]&quot;,
 	reference             = &quot;%r&quot;,
 	propertyWithQualifier = &quot;%p[ &lt;span style=\&quot;font-size:85\\%\&quot;&gt;(%q)&lt;/span&gt;][%s][%r]&quot;,
 	alias                 = &quot;%a[%s]&quot;,
 	badge                 = &quot;%b[%s]&quot;
 }
 
 local hookNames = {              -- {level_1, level_2}
 	[parameters.property]         = {&quot;getProperty&quot;},
 	[parameters.reference]        = {&quot;getReferences&quot;, &quot;getReference&quot;},
 	[parameters.qualifier]        = {&quot;getAllQualifiers&quot;},
 	[parameters.qualifier..&quot;\\d&quot;] = {&quot;getQualifiers&quot;, &quot;getQualifier&quot;},
 	[parameters.alias]            = {&quot;getAlias&quot;},
 	[parameters.badge]            = {&quot;getBadge&quot;}
 }
 
 -- default value objects, should NOT be mutated but instead copied
 local defaultSeparators = {
 	[&quot;sep&quot;]      = {&quot; &quot;},
 	[&quot;sep%s&quot;]    = {&quot;,&quot;},
 	[&quot;sep%q&quot;]    = {&quot;; &quot;},
 	[&quot;sep%q\\d&quot;] = {&quot;, &quot;},
 	[&quot;sep%r&quot;]    = nil,  -- none
 	[&quot;punc&quot;]     = nil   -- none
 }
 
 local rankTable = {
 	[&quot;preferred&quot;]  = 1,
 	[&quot;normal&quot;]     = 2,
 	[&quot;deprecated&quot;] = 3
 }
 
 local function replaceAlias(id)
 	if aliasesP[id] then
 		id = aliasesP[id]
 	end
 
 	return id
 end
 
 local function errorText(code, param)
 	local text = i18n[&quot;errors&quot;][code]
 	if param then text = mw.ustring.gsub(text, &quot;$1&quot;, param) end
 	return text
 end
 
 local function throwError(errorMessage, param)
 	error(errorText(errorMessage, param))
 end
 
 local function replaceDecimalMark(num)
 	return mw.ustring.gsub(num, &quot;[.]&quot;, i18n['numeric']['decimal-mark'], 1)
 end
 
 local function padZeros(num, numDigits)
 	local numZeros
 	local negative = false
 
 	if num &lt; 0 then
 		negative = true
 		num = num * -1
 	end
 
 	num = tostring(num)
 	numZeros = numDigits - num:len()
 
 	for _ = 1, numZeros do
 		num = &quot;0&quot;..num
 	end
 
 	if negative then
 		num = &quot;-&quot;..num
 	end
 
 	return num
 end
 
 local function replaceSpecialChar(chr)
 	if chr == '_' then
 		-- replace underscores with spaces
 		return ' '
 	else
 		return chr
 	end
 end
 
 local function replaceSpecialChars(str)
 	local chr
 	local esc = false
 	local strOut = &quot;&quot;
 
 	for i = 1, #str do
 		chr = str:sub(i,i)
 
 		if not esc then
 			if chr == '\\' then
 				esc = true
 			else
 				strOut = strOut .. replaceSpecialChar(chr)
 			end
 		else
 			strOut = strOut .. chr
 			esc = false
 		end
 	end
 
 	return strOut
 end
 
 local function buildWikilink(target, label)
 	if not label or target == label then
 		return &quot;[[&quot; .. target .. &quot;]]&quot;
 	else
 		return &quot;[[&quot; .. target .. &quot;|&quot; .. label .. &quot;]]&quot;
 	end
 end
 
 -- used to make frame.args mutable, to replace #frame.args (which is always 0)
 -- with the actual amount and to simply copy tables
 local function copyTable(tIn)
 	if not tIn then
 		return nil
 	end
 
 	local tOut = {}
 
 	for i, v in pairs(tIn) do
 		tOut[i] = v
 	end
 
 	return tOut
 end
 
 -- used to merge output arrays together;
 -- note that it currently mutates the first input array
 local function mergeArrays(a1, a2)
 	for i = 1, #a2 do
 		a1[#a1 + 1] = a2[i]
 	end
 
 	return a1
 end
 
 local function split(str, del)
 	local out = {}
 	local i, j = str:find(del)
 
 	if i and j then
 		out[1] = str:sub(1, i - 1)
 		out[2] = str:sub(j + 1)
 	else
 		out[1] = str
 	end
 
 	return out
 end
 
 local function parseWikidataURL(url)
 	local id
 
 	if url:match('^http[s]?://') then
 		id = split(url, &quot;Q&quot;)
 
 		if id[2] then
 			return &quot;Q&quot; .. id[2]
 		end
 	end
 
 	return nil
 end
 
 local function parseDate(dateStr, precision)
 	precision = precision or &quot;d&quot;
 
 	local i, j, index, ptr
 	local parts = {nil, nil, nil}
 
 	if dateStr == nil then
 		return parts[1], parts[2], parts[3]  -- year, month, day
 	end
 
 	-- 'T' for snak values, '/' for outputs with '/Julian' attached
 	i, j = dateStr:find(&quot;[T/]&quot;)
 
 	if i then
 		dateStr = dateStr:sub(1, i-1)
 	end
 
 	local from = 1
 
 	if dateStr:sub(1,1) == &quot;-&quot; then
 		-- this is a negative number, look further ahead
 		from = 2
 	end
 
 	index = 1
 	ptr = 1
 
 	i, j = dateStr:find(&quot;-&quot;, from)
 
 	if i then
 		-- year
 		parts[index] = tonumber(mw.ustring.gsub(dateStr:sub(ptr, i-1), &quot;^\+(.+)$&quot;, &quot;%1&quot;), 10)  -- remove '+' sign (explicitly give base 10 to prevent error)
 
 		if parts[index] == -0 then
 			parts[index] = tonumber(&quot;0&quot;)  -- for some reason, 'parts[index] = 0' may actually store '-0', so parse from string instead
 		end
 
 		if precision == &quot;y&quot; then
 			-- we're done
 			return parts[1], parts[2], parts[3]  -- year, month, day
 		end
 
 		index = index + 1
 		ptr = i + 1
 
 		i, j = dateStr:find(&quot;-&quot;, ptr)
 
 		if i then
 			-- month
 			parts[index] = tonumber(dateStr:sub(ptr, i-1), 10)
 
 			if precision == &quot;m&quot; then
 				-- we're done
 				return parts[1], parts[2], parts[3]  -- year, month, day
 			end
 
 			index = index + 1
 			ptr = i + 1
 		end
 	end
 
 	if dateStr:sub(ptr) ~= &quot;&quot; then
 		-- day if we have month, month if we have year, or year
 		parts[index] = tonumber(dateStr:sub(ptr), 10)
 	end
 
 	return parts[1], parts[2], parts[3]  -- year, month, day
 end
 
 local function datePrecedesDate(aY, aM, aD, bY, bM, bD)
 	if aY == nil or bY == nil then
 		return nil
 	end
 	aM = aM or 1
 	aD = aD or 1
 	bM = bM or 1
 	bD = bD or 1
 
 	if aY &lt; bY then
 		return true
 	end
 
 	if aY &gt; bY then
 		return false
 	end
 
 	if aM &lt; bM then
 		return true
 	end
 
 	if aM &gt; bM then
 		return false
 	end
 
 	if aD &lt; bD then
 		return true
 	end
 
 	return false
 end
 
 local function getHookName(param, index)
 	if hookNames[param] then
 		return hookNames[param][index]
 	elseif param:len() &gt; 2 then
 		return hookNames[param:sub(1, 2)..&quot;\\d&quot;][index]
 	else
 		return nil
 	end
 end
 
 local function alwaysTrue()
 	return true
 end
 
 -- The following function parses a format string.
 --
 -- The example below shows how a parsed string is structured in memory.
 -- Variables other than 'str' and 'child' are left out for clarity's sake.
 --
 -- Example:
 -- &quot;A %p B [%s[%q1]] C [%r] D&quot;
 --
 -- Structure:
 -- [
 --   {
 --     str = &quot;A &quot;
 --   },
 --   {
 --     str = &quot;%p&quot;
 --   },
 --   {
 --     str = &quot; B &quot;,
 --     child =
 --     [
 --       {
 --         str = &quot;%s&quot;,
 --         child =
 --         [
 --           {
 --             str = &quot;%q1&quot;
 --           }
 --         ]
 --       }
 --     ]
 --   },
 --   {
 --     str = &quot; C &quot;,
 --     child =
 --     [
 --       {
 --         str = &quot;%r&quot;
 --       }
 --     ]
 --   },
 --   {
 --     str = &quot; D&quot;
 --   }
 -- ]
 --
 local function parseFormat(str)
 	local chr, esc, param, root, cur, prev, new
 	local params = {}
 
 	local function newObject(array)
 		local obj = {}  -- new object
 		obj.str = &quot;&quot;
 
 		array[#array + 1] = obj  -- array{object}
 		obj.parent = array
 
 		return obj
 	end
 
 	local function endParam()
 		if param &gt; 0 then
 			if cur.str ~= &quot;&quot; then
 				cur.str = &quot;%&quot;..cur.str
 				cur.param = true
 				params[cur.str] = true
 				cur.parent.req[cur.str] = true
 				prev = cur
 				cur = newObject(cur.parent)
 			end
 			param = 0
 		end
 	end
 
 	root = {}  -- array
 	root.req = {}
 	cur = newObject(root)
 	prev = nil
 
 	esc = false
 	param = 0
 
 	for i = 1, #str do
 		chr = str:sub(i,i)
 
 		if not esc then
 			if chr == '\\' then
 				endParam()
 				esc = true
 			elseif chr == '%' then
 				endParam()
 				if cur.str ~= &quot;&quot; then
 					cur = newObject(cur.parent)
 				end
 				param = 2
 			elseif chr == '[' then
 				endParam()
 				if prev and cur.str == &quot;&quot; then
 					table.remove(cur.parent)
 					cur = prev
 				end
 				cur.child = {}  -- new array
 				cur.child.req = {}
 				cur.child.parent = cur
 				cur = newObject(cur.child)
 			elseif chr == ']' then
 				endParam()
 				if cur.parent.parent then
 					new = newObject(cur.parent.parent.parent)
 					if cur.str == &quot;&quot; then
 						table.remove(cur.parent)
 					end
 					cur = new
 				end
 			else
 				if param &gt; 1 then
 					param = param - 1
 				elseif param == 1 then
 					if not chr:match('%d') then
 						endParam()
 					end
 				end
 
 				cur.str = cur.str .. replaceSpecialChar(chr)
 			end
 		else
 			cur.str = cur.str .. chr
 			esc = false
 		end
 
 		prev = nil
 	end
 
 	endParam()
 
 	-- make sure that at least one required parameter has been defined
 	if not next(root.req) then
 		throwError(&quot;missing-required-parameter&quot;)
 	end
 
 	-- make sure that the separator parameter &quot;%s&quot; is not amongst the required parameters
 	if root.req[parameters.separator] then
 		throwError(&quot;extra-required-parameter&quot;, parameters.separator)
 	end
 
 	return root, params
 end
 
 local function sortOnRank(claims)
 	local rankPos
 	local ranks = {{}, {}, {}, {}}  -- preferred, normal, deprecated, (default)
 	local sorted = {}
 
 	for _, v in ipairs(claims) do
 		rankPos = rankTable[v.rank] or 4
 		ranks[rankPos][#ranks[rankPos] + 1] = v
 	end
 
 	sorted = ranks[1]
 	sorted = mergeArrays(sorted, ranks[2])
 	sorted = mergeArrays(sorted, ranks[3])
 
 	return sorted
 end
 
 local Config = {}
 
 -- allows for recursive calls
 function Config:new()
 	local cfg = {}
 	setmetatable(cfg, self)
 	self.__index = self
 
 	cfg.separators = {
 		-- single value objects wrapped in arrays so that we can pass by reference
 		[&quot;sep&quot;]   = {copyTable(defaultSeparators[&quot;sep&quot;])},
 		[&quot;sep%s&quot;] = {copyTable(defaultSeparators[&quot;sep%s&quot;])},
 		[&quot;sep%q&quot;] = {copyTable(defaultSeparators[&quot;sep%q&quot;])},
 		[&quot;sep%r&quot;] = {copyTable(defaultSeparators[&quot;sep%r&quot;])},
 		[&quot;punc&quot;]  = {copyTable(defaultSeparators[&quot;punc&quot;])}
 	}
 
 	cfg.entity = nil
 	cfg.entityID = nil
 	cfg.propertyID = nil
 	cfg.propertyValue = nil
 	cfg.qualifierIDs = {}
 	cfg.qualifierIDsAndValues = {}
 
 	cfg.bestRank = true
 	cfg.ranks = {true, true, false}  -- preferred = true, normal = true, deprecated = false
 	cfg.foundRank = #cfg.ranks
 	cfg.flagBest = false
 	cfg.flagRank = false
 
 	cfg.periods = {true, true, true}  -- future = true, current = true, former = true
 	cfg.flagPeriod = false
 	cfg.atDate = {parseDate(os.date('!%Y-%m-%d'))}  -- today as {year, month, day}
 
 	cfg.mdyDate = false
 	cfg.singleClaim = false
 	cfg.sourcedOnly = false
 	cfg.editable = false
 	cfg.editAtEnd = false
 
 	cfg.inSitelinks = false
 
 	cfg.langCode = mw.language.getContentLanguage().code
 	cfg.langName = mw.language.fetchLanguageName(cfg.langCode, cfg.langCode)
 	cfg.langObj = mw.language.new(cfg.langCode)
 
 	cfg.siteID = mw.wikibase.getGlobalSiteId()
 
 	cfg.states = {}
 	cfg.states.qualifiersCount = 0
 	cfg.curState = nil
 
 	cfg.prefetchedRefs = nil
 
 	return cfg
 end
 
 local State = {}
 
 function State:new(cfg, type)
 	local stt = {}
 	setmetatable(stt, self)
 	self.__index = self
 
 	stt.conf = cfg
 	stt.type = type
 
 	stt.results = {}
 
 	stt.parsedFormat = {}
 	stt.separator = {}
 	stt.movSeparator = {}
 	stt.puncMark = {}
 
 	stt.linked = false
 	stt.rawValue = false
 	stt.shortName = false
 	stt.anyLanguage = false
 	stt.unitOnly = false
 	stt.singleValue = false
 
 	return stt
 end
 
 -- if id == nil then item connected to current page is used
 function Config:getLabel(id, raw, link, short)
 	local label = nil
 	local prefix, title= &quot;&quot;, nil
 
 	if not id then
 		id = mw.wikibase.getEntityIdForCurrentPage()
 
 		if not id then
 			return &quot;&quot;
 		end
 	end
 
 	id = id:upper()  -- just to be sure
 
 	if raw then
 		-- check if given id actually exists
 		if mw.wikibase.isValidEntityId(id) and mw.wikibase.entityExists(id) then
 			label = id
 		end
 
 		prefix, title = &quot;d:Special:EntityPage/&quot;, label -- may be nil
 	else
 		-- try short name first if requested
 		if short then
 			label = p._property{aliasesP.shortName, [p.args.eid] = id}  -- get short name
 
 			if label == &quot;&quot; then
 				label = nil
 			end
 		end
 
 		-- get label
 		if not label then
 			label = mw.wikibase.getLabelByLang(id, self.langCode) -- XXX: should use fallback labels?
 		end
 	end
 
 	if not label then
 		label = &quot;&quot;
 	elseif link then
 		-- build a link if requested
 		if not title then
 			if id:sub(1,1) == &quot;Q&quot; then
 				title = mw.wikibase.getSitelink(id)
 			elseif id:sub(1,1) == &quot;P&quot; then
 				-- properties have no sitelink, link to Wikidata instead
 				prefix, title = &quot;d:Special:EntityPage/&quot;, id
 			end
 		end
 
 		label = mw.text.nowiki(label) -- escape raw label text so it cannot be wikitext markup
 		if title then
 			label = buildWikilink(prefix .. title, label)
 		end
 	end
 
 	return label
 end
 
 function Config:getEditIcon()
 	local value = &quot;&quot;
 	local prefix = &quot;&quot;
 	local front = &quot;&amp;nbsp;&quot;
 	local back = &quot;&quot;
 
 	if self.entityID:sub(1,1) == &quot;P&quot; then
 		prefix = &quot;Property:&quot;
 	end
 
 	if self.editAtEnd then
 		front = '&lt;span style=&quot;float:'
 
 		if self.langObj:isRTL() then
 			front = front .. 'left'
 		else
 			front = front .. 'right'
 		end
 
 		front = front .. '&quot;&gt;'
 		back = '&lt;/span&gt;'
 	end
 
 	value = &quot;[[File:OOjs UI icon edit-ltr-progressive.svg|frameless|text-top|10px|alt=&quot; .. i18n['info']['edit-on-wikidata'] .. &quot;|link=https://www.wikidata.org/wiki/&quot; .. prefix .. self.entityID .. &quot;?uselang=&quot; .. self.langCode
 
 	if self.propertyID then
 		value = value .. &quot;#&quot; .. self.propertyID
 	elseif self.inSitelinks then
 		value = value .. &quot;#sitelinks-wikipedia&quot;
 	end
 
 	value = value .. &quot;|&quot; .. i18n['info']['edit-on-wikidata'] .. &quot;]]&quot;
 
 	return front .. value .. back
 end
 
 -- used to create the final output string when it's all done, so that for references the
 -- function extensionTag(&quot;ref&quot;, ...) is only called when they really ended up in the final output
 function Config:concatValues(valuesArray)
 	local outString = &quot;&quot;
 	local j, skip
 
 	for i = 1, #valuesArray do
 		-- check if this is a reference
 		if valuesArray[i].refHash then
 			j = i - 1
 			skip = false
 
 			-- skip this reference if it is part of a continuous row of references that already contains the exact same reference
 			while valuesArray[j] and valuesArray[j].refHash do
 				if valuesArray[i].refHash == valuesArray[j].refHash then
 					skip = true
 					break
 				end
 				j = j - 1
 			end
 
 			if not skip then
 				-- add &lt;ref&gt; tag with the reference's hash as its name (to deduplicate references)
 				outString = outString .. mw.getCurrentFrame():extensionTag(&quot;ref&quot;, valuesArray[i][1], {name = valuesArray[i].refHash})
 			end
 		else
 			outString = outString .. valuesArray[i][1]
 		end
 	end
 
 	return outString
 end
 
 function Config:convertUnit(unit, raw, link, short, unitOnly)
 	local space = &quot; &quot;
 	local label = &quot;&quot;
 	local itemID
 
 	if unit == &quot;&quot; or unit == &quot;1&quot; then
 		return nil
 	end
 
 	if unitOnly then
 		space = &quot;&quot;
 	end
 
 	itemID = parseWikidataURL(unit)
 
 	if itemID then
 		if itemID == aliasesQ.percentage then
 			return &quot;%&quot;
 		else
 			label = self:getLabel(itemID, raw, link, short)
 
 			if label ~= &quot;&quot; then
 				return space .. label
 			end
 		end
 	end
 
 	return &quot;&quot;
 end
 
 function State:getValue(snak)
 	return self.conf:getValue(snak, self.rawValue, self.linked, self.shortName, self.anyLanguage, self.unitOnly, false, self.type:sub(1,2))
 end
 
 function Config:getValue(snak, raw, link, short, anyLang, unitOnly, noSpecial, type)
 	if snak.snaktype == 'value' then
 		local datatype = snak.datavalue.type
 		local subtype = snak.datatype
 		local datavalue = snak.datavalue.value
 
 		if datatype == 'string' then
 			if subtype == 'url' and link then
 				-- create link explicitly
 				if raw then
 					-- will render as a linked number like [1]
 					return &quot;[&quot; .. datavalue .. &quot;]&quot;
 				else
 					return &quot;[&quot; .. datavalue .. &quot; &quot; .. datavalue .. &quot;]&quot;
 				end
 			elseif subtype == 'commonsMedia' then
 				if link then
 					return buildWikilink(&quot;c:File:&quot; .. datavalue, datavalue)
 				elseif not raw then
 					return &quot;[[File:&quot; .. datavalue .. &quot;]]&quot;
 				else
 					return datavalue
 				end
 			elseif subtype == 'geo-shape' and link then
 				return buildWikilink(&quot;c:&quot; .. datavalue, datavalue)
 			elseif subtype == 'math' and not raw then
 				local attribute = nil
 
 				if (type == parameters.property or (type == parameters.qualifier and self.propertyID == aliasesP.hasPart)) and snak.property == aliasesP.definingFormula then
 					attribute = {qid = self.entityID}
 				end
 
 				return mw.getCurrentFrame():extensionTag(&quot;math&quot;, datavalue, attribute)
 			elseif subtype == 'external-id' and link then
 				local url = p._property{aliasesP.formatterURL, [p.args.eid] = snak.property}  -- get formatter URL
 
 				if url ~= &quot;&quot; then
 					url = mw.ustring.gsub(url, &quot;$1&quot;, datavalue)
 					return &quot;[&quot; .. url .. &quot; &quot; .. datavalue .. &quot;]&quot;
 				else
 					return datavalue
 				end
 			else
 				return datavalue
 			end
 		elseif datatype == 'monolingualtext' then
 			if anyLang or datavalue['language'] == self.langCode then
 				return datavalue['text']
 			else
 				return nil
 			end
 		elseif datatype == 'quantity' then
 			local value = &quot;&quot;
 			local unit
 
 			if not unitOnly then
 				-- get value and strip + signs from front
 				value = mw.ustring.gsub(datavalue['amount'], &quot;^\+(.+)$&quot;, &quot;%1&quot;)
 
 				if raw then
 					return value
 				end
 
 				-- replace decimal mark based on locale
 				value = replaceDecimalMark(value)
 
 				-- add delimiters for readability
 				value = i18n.addDelimiters(value)
 			end
 
 			unit = self:convertUnit(datavalue['unit'], raw, link, short, unitOnly)
 
 			if unit then
 				value = value .. unit
 			end
 
 			return value
 		elseif datatype == 'time' then
 			local y, m, d, p, yDiv, yRound, yFull, value, calendarID, dateStr
 			local yFactor = 1
 			local sign = 1
 			local prefix = &quot;&quot;
 			local suffix = &quot;&quot;
 			local mayAddCalendar = false
 			local calendar = &quot;&quot;
 			local precision = datavalue['precision']
 
 			if precision == 11 then
 				p = &quot;d&quot;
 			elseif precision == 10 then
 				p = &quot;m&quot;
 			else
 				p = &quot;y&quot;
 				yFactor = 10^(9-precision)
 			end
 
 			y, m, d = parseDate(datavalue['time'], p)
 
 			if y &lt; 0 then
 				sign = -1
 				y = y * sign
 			end
 
 			-- if precision is tens/hundreds/thousands/millions/billions of years
 			if precision &lt;= 8 then
 				yDiv = y / yFactor
 
 				-- if precision is tens/hundreds/thousands of years
 				if precision &gt;= 6 then
 					mayAddCalendar = true
 
 					if precision &lt;= 7 then
 						-- round centuries/millenniums up (e.g. 20th century or 3rd millennium)
 						yRound = math.ceil(yDiv)
 
 						if not raw then
 							if precision == 6 then
 								suffix = i18n['datetime']['suffixes']['millennium']
 							else
 								suffix = i18n['datetime']['suffixes']['century']
 							end
 
 							suffix = i18n.getOrdinalSuffix(yRound) .. suffix
 						else
 							-- if not verbose, take the first year of the century/millennium
 							-- (e.g. 1901 for 20th century or 2001 for 3rd millennium)
 							yRound = (yRound - 1) * yFactor + 1
 						end
 					else
 						-- precision == 8
 						-- round decades down (e.g. 2010s)
 						yRound = math.floor(yDiv) * yFactor
 
 						if not raw then
 							prefix = i18n['datetime']['prefixes']['decade-period']
 							suffix = i18n['datetime']['suffixes']['decade-period']
 						end
 					end
 
 					if raw and sign &lt; 0 then
 						-- if BCE then compensate for &quot;counting backwards&quot;
 						-- (e.g. -2019 for 2010s BCE, -2000 for 20th century BCE or -3000 for 3rd millennium BCE)
 						yRound = yRound + yFactor - 1
 					end
 				else
 					local yReFactor, yReDiv, yReRound
 
 					-- round to nearest for tens of thousands of years or more
 					yRound = math.floor(yDiv + 0.5)
 
 					if yRound == 0 then
 						if precision &lt;= 2 and y ~= 0 then
 							yReFactor = 1e6
 							yReDiv = y / yReFactor
 							yReRound = math.floor(yReDiv + 0.5)
 
 							if yReDiv == yReRound then
 								-- change precision to millions of years only if we have a whole number of them
 								precision = 3
 								yFactor = yReFactor
 								yRound = yReRound
 							end
 						end
 
 						if yRound == 0 then
 							-- otherwise, take the unrounded (original) number of years
 							precision = 5
 							yFactor = 1
 							yRound = y
 							mayAddCalendar = true
 						end
 					end
 
 					if precision &gt;= 1 and y ~= 0 then
 						yFull = yRound * yFactor
 
 						yReFactor = 1e9
 						yReDiv = yFull / yReFactor
 						yReRound = math.floor(yReDiv + 0.5)
 
 						if yReDiv == yReRound then
 							-- change precision to billions of years if we're in that range
 							precision = 0
 							yFactor = yReFactor
 							yRound = yReRound
 						else
 							yReFactor = 1e6
 							yReDiv = yFull / yReFactor
 							yReRound = math.floor(yReDiv + 0.5)
 
 							if yReDiv == yReRound then
 								-- change precision to millions of years if we're in that range
 								precision = 3
 								yFactor = yReFactor
 								yRound = yReRound
 							end
 						end
 					end
 
 					if not raw then
 						if precision == 3 then
 							suffix = i18n['datetime']['suffixes']['million-years']
 						elseif precision == 0 then
 							suffix = i18n['datetime']['suffixes']['billion-years']
 						else
 							yRound = yRound * yFactor
 							if yRound == 1 then
 								suffix = i18n['datetime']['suffixes']['year']
 							else
 								suffix = i18n['datetime']['suffixes']['years']
 							end
 						end
 					else
 						yRound = yRound * yFactor
 					end
 				end
 			else
 				yRound = y
 				mayAddCalendar = true
 			end
 
 			if mayAddCalendar then
 				calendarID = parseWikidataURL(datavalue['calendarmodel'])
 
 				if calendarID and calendarID == aliasesQ.prolepticJulianCalendar then
 					if not raw then
 						if link then
 							calendar = &quot; (&quot;..buildWikilink(i18n['datetime']['julian-calendar'], i18n['datetime']['julian'])..&quot;)&quot;
 						else
 							calendar = &quot; (&quot;..i18n['datetime']['julian']..&quot;)&quot;
 						end
 					else
 						calendar = &quot;/&quot;..i18n['datetime']['julian']
 					end
 				end
 			end
 
 			if not raw then
 				local ce = nil
 
 				if sign &lt; 0 then
 					ce = i18n['datetime']['BCE']
 				elseif precision &lt;= 5 then
 					ce = i18n['datetime']['CE']
 				end
 
 				if ce then
 					if link then
 						ce = buildWikilink(i18n['datetime']['common-era'], ce)
 					end
 					suffix = suffix .. &quot; &quot; .. ce
 				end
 
 				value = tostring(yRound)
 
 				if m then
 					dateStr = self.langObj:formatDate(&quot;F&quot;, &quot;1-&quot;..m..&quot;-1&quot;)
 
 					if d then
 						if self.mdyDate then
 							dateStr = dateStr .. &quot; &quot; .. d .. &quot;,&quot;
 						else
 							dateStr = d .. &quot; &quot; .. dateStr
 						end
 					end
 
 					value = dateStr .. &quot; &quot; .. value
 				end
 
 				value = prefix .. value .. suffix .. calendar
 			else
 				value = padZeros(yRound * sign, 4)
 
 				if m then
 					value = value .. &quot;-&quot; .. padZeros(m, 2)
 
 					if d then
 						value = value .. &quot;-&quot; .. padZeros(d, 2)
 					end
 				end
 
 				value = value .. calendar
 			end
 
 			return value
 		elseif datatype == 'globecoordinate' then
 			-- logic from https://github.com/DataValues/Geo (v4.0.1)
 
 			local precision, unitsPerDegree, numDigits, strFormat, value, globe
 			local latitude, latConv, latValue, latLink
 			local longitude, lonConv, lonValue, lonLink
 			local latDirection, latDirectionN, latDirectionS, latDirectionEN
 			local lonDirection, lonDirectionE, lonDirectionW, lonDirectionEN
 			local degSymbol, minSymbol, secSymbol, separator
 
 			local latDegrees = nil
 			local latMinutes = nil
 			local latSeconds = nil
 			local lonDegrees = nil
 			local lonMinutes = nil
 			local lonSeconds = nil
 
 			local latDegSym = &quot;&quot;
 			local latMinSym = &quot;&quot;
 			local latSecSym = &quot;&quot;
 			local lonDegSym = &quot;&quot;
 			local lonMinSym = &quot;&quot;
 			local lonSecSym = &quot;&quot;
 
 			local latDirectionEN_N = &quot;N&quot;
 			local latDirectionEN_S = &quot;S&quot;
 			local lonDirectionEN_E = &quot;E&quot;
 			local lonDirectionEN_W = &quot;W&quot;
 
 			if not raw then
 				latDirectionN = i18n['coord']['latitude-north']
 				latDirectionS = i18n['coord']['latitude-south']
 				lonDirectionE = i18n['coord']['longitude-east']
 				lonDirectionW = i18n['coord']['longitude-west']
 
 				degSymbol = i18n['coord']['degrees']
 				minSymbol = i18n['coord']['minutes']
 				secSymbol = i18n['coord']['seconds']
 				separator = i18n['coord']['separator']
 			else
 				latDirectionN = latDirectionEN_N
 				latDirectionS = latDirectionEN_S
 				lonDirectionE = lonDirectionEN_E
 				lonDirectionW = lonDirectionEN_W
 
 				degSymbol = &quot;/&quot;
 				minSymbol = &quot;/&quot;
 				secSymbol = &quot;/&quot;
 				separator = &quot;/&quot;
 			end
 
 			latitude = datavalue['latitude']
 			longitude = datavalue['longitude']
 
 			if latitude &lt; 0 then
 				latDirection = latDirectionS
 				latDirectionEN = latDirectionEN_S
 				latitude = math.abs(latitude)
 			else
 				latDirection = latDirectionN
 				latDirectionEN = latDirectionEN_N
 			end
 
 			if longitude &lt; 0 then
 				lonDirection = lonDirectionW
 				lonDirectionEN = lonDirectionEN_W
 				longitude = math.abs(longitude)
 			else
 				lonDirection = lonDirectionE
 				lonDirectionEN = lonDirectionEN_E
 			end
 
 			precision = datavalue['precision']
 
 			if not precision or precision &lt;= 0 then
 				precision = 1 / 3600  -- precision not set (correctly), set to arcsecond
 			end
 
 			-- remove insignificant detail
 			latitude = math.floor(latitude / precision + 0.5) * precision
 			longitude = math.floor(longitude / precision + 0.5) * precision
 
 			if precision &gt;= 1 - (1 / 60) and precision &lt; 1 then
 				precision = 1
 			elseif precision &gt;= (1 / 60) - (1 / 3600) and precision &lt; (1 / 60) then
 				precision = 1 / 60
 			end
 
 			if precision &gt;= 1 then
 				unitsPerDegree = 1
 			elseif precision &gt;= (1 / 60)  then
 				unitsPerDegree = 60
 			else
 				unitsPerDegree = 3600
 			end
 
 			numDigits = math.ceil(-math.log10(unitsPerDegree * precision))
 
 			if numDigits &lt;= 0 then
 				numDigits = tonumber(&quot;0&quot;)  -- for some reason, 'numDigits = 0' may actually store '-0', so parse from string instead
 			end
 
 			strFormat = &quot;%.&quot; .. numDigits .. &quot;f&quot;
 
 			if precision &gt;= 1 then
 				latDegrees = strFormat:format(latitude)
 				lonDegrees = strFormat:format(longitude)
 
 				if not raw then
 					latDegSym = replaceDecimalMark(latDegrees) .. degSymbol
 					lonDegSym = replaceDecimalMark(lonDegrees) .. degSymbol
 				else
 					latDegSym = latDegrees .. degSymbol
 					lonDegSym = lonDegrees .. degSymbol
 				end
 			else
 				latConv = math.floor(latitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
 				lonConv = math.floor(longitude * unitsPerDegree * 10^numDigits + 0.5) / 10^numDigits
 
 				if precision &gt;= (1 / 60) then
 					latMinutes = latConv
 					lonMinutes = lonConv
 				else
 					latSeconds = latConv
 					lonSeconds = lonConv
 
 					latMinutes = math.floor(latSeconds / 60)
 					lonMinutes = math.floor(lonSeconds / 60)
 
 					latSeconds = strFormat:format(latSeconds - (latMinutes * 60))
 					lonSeconds = strFormat:format(lonSeconds - (lonMinutes * 60))
 
 					if not raw then
 						latSecSym = replaceDecimalMark(latSeconds) .. secSymbol
 						lonSecSym = replaceDecimalMark(lonSeconds) .. secSymbol
 					else
 						latSecSym = latSeconds .. secSymbol
 						lonSecSym = lonSeconds .. secSymbol
 					end
 				end
 
 				latDegrees = math.floor(latMinutes / 60)
 				lonDegrees = math.floor(lonMinutes / 60)
 
 				latDegSym = latDegrees .. degSymbol
 				lonDegSym = lonDegrees .. degSymbol
 
 				latMinutes = latMinutes - (latDegrees * 60)
 				lonMinutes = lonMinutes - (lonDegrees * 60)
 
 				if precision &gt;= (1 / 60) then
 					latMinutes = strFormat:format(latMinutes)
 					lonMinutes = strFormat:format(lonMinutes)
 
 					if not raw then
 						latMinSym = replaceDecimalMark(latMinutes) .. minSymbol
 						lonMinSym = replaceDecimalMark(lonMinutes) .. minSymbol
 					else
 						latMinSym = latMinutes .. minSymbol
 						lonMinSym = lonMinutes .. minSymbol
 					end
 				else
 					latMinSym = latMinutes .. minSymbol
 					lonMinSym = lonMinutes .. minSymbol
 				end
 			end
 
 			latValue = latDegSym .. latMinSym .. latSecSym .. latDirection
 			lonValue = lonDegSym .. lonMinSym .. lonSecSym .. lonDirection
 
 			value = latValue .. separator .. lonValue
 
 			if link then
 				globe = parseWikidataURL(datavalue['globe'])
 
 				if globe then
 					globe = mw.wikibase.getLabelByLang(globe, &quot;en&quot;):lower()
 				else
 					globe = &quot;earth&quot;
 				end
 
 				latLink = table.concat({latDegrees, latMinutes, latSeconds}, &quot;_&quot;)
 				lonLink = table.concat({lonDegrees, lonMinutes, lonSeconds}, &quot;_&quot;)
 
 				value = &quot;[https://geohack.toolforge.org/geohack.php?language=&quot;..self.langCode..&quot;&amp;params=&quot;..latLink..&quot;_&quot;..latDirectionEN..&quot;_&quot;..lonLink..&quot;_&quot;..lonDirectionEN..&quot;_globe:&quot;..globe..&quot; &quot;..value..&quot;]&quot;
 			end
 
 			return value
 		elseif datatype == 'wikibase-entityid' then
 			local label
 			local itemID = datavalue['numeric-id']
 
 			if subtype == 'wikibase-item' then
 				itemID = &quot;Q&quot; .. itemID
 			elseif subtype == 'wikibase-property' then
 				itemID = &quot;P&quot; .. itemID
 			else
 				return '&lt;strong class=&quot;error&quot;&gt;' .. errorText('unknown-data-type', subtype) .. '&lt;/strong&gt;'
 			end
 
 			label = self:getLabel(itemID, raw, link, short)
 
 			if label == &quot;&quot; then
 				label = nil
 			end
 
 			return label
 		else
 			return '&lt;strong class=&quot;error&quot;&gt;' .. errorText('unknown-data-type', datatype) .. '&lt;/strong&gt;'
 		end
 	elseif snak.snaktype == 'somevalue' and not noSpecial then
 		if raw then
 			return &quot; &quot;  -- single space represents 'somevalue'
 		else
 			return i18n['values']['unknown']
 		end
 	elseif snak.snaktype == 'novalue' and not noSpecial then
 		if raw then
 			return &quot;&quot;  -- empty string represents 'novalue'
 		else
 			return i18n['values']['none']
 		end
 	else
 		return nil
 	end
 end
 
 function Config:getSingleRawQualifier(claim, qualifierID)
 	local qualifiers
 
 	if claim.qualifiers then qualifiers = claim.qualifiers[qualifierID] end
 
 	if qualifiers and qualifiers[1] then
 		return self:getValue(qualifiers[1], true)  -- raw = true
 	else
 		return nil
 	end
 end
 
 function Config:snakEqualsValue(snak, value)
 	local snakValue = self:getValue(snak, true)  -- raw = true
 
 	if snakValue and snak.snaktype == 'value' and snak.datavalue.type == 'wikibase-entityid' then value = value:upper() end
 
 	return snakValue == value
 end
 
 function Config:setRank(rank)
 	local rankPos
 
 	if rank == p.flags.best then
 		self.bestRank = true
 		self.flagBest = true  -- mark that 'best' flag was given
 		return
 	end
 
 	if rank:sub(1,9) == p.flags.preferred then
 		rankPos = 1
 	elseif rank:sub(1,6) == p.flags.normal then
 		rankPos = 2
 	elseif rank:sub(1,10) == p.flags.deprecated then
 		rankPos = 3
 	else
 		return
 	end
 
 	-- one of the rank flags was given, check if another one was given before
 	if not self.flagRank then
 		self.ranks = {false, false, false}  -- no other rank flag given before, so unset ranks
 		self.bestRank = self.flagBest       -- unsets bestRank only if 'best' flag was not given before
 		self.flagRank = true                -- mark that a rank flag was given
 	end
 
 	if rank:sub(-1) == &quot;+&quot; then
 		for i = rankPos, 1, -1 do
 			self.ranks[i] = true
 		end
 	elseif rank:sub(-1) == &quot;-&quot; then
 		for i = rankPos, #self.ranks do
 			self.ranks[i] = true
 		end
 	else
 		self.ranks[rankPos] = true
 	end
 end
 
 function Config:setPeriod(period)
 	local periodPos
 
 	if period == p.flags.future then
 		periodPos = 1
 	elseif period == p.flags.current then
 		periodPos = 2
 	elseif period == p.flags.former then
 		periodPos = 3
 	else
 		return
 	end
 
 	-- one of the period flags was given, check if another one was given before
 	if not self.flagPeriod then
 		self.periods = {false, false, false}  -- no other period flag given before, so unset periods
 		self.flagPeriod = true                -- mark that a period flag was given
 	end
 
 	self.periods[periodPos] = true
 end
 
 function Config:qualifierMatches(claim, id, value)
 	local qualifiers
 
 	if claim.qualifiers then qualifiers = claim.qualifiers[id] end
 	if qualifiers then
 		for _, v in pairs(qualifiers) do
 			if self:snakEqualsValue(v, value) then
 				return true
 			end
 		end
 	elseif value == &quot;&quot; then
 		-- if the qualifier is not present then treat it the same as the special value 'novalue'
 		return true
 	end
 
 	return false
 end
 
 function Config:rankMatches(rankPos)
 	if self.bestRank then
 		return (self.ranks[rankPos] and self.foundRank &gt;= rankPos)
 	else
 		return self.ranks[rankPos]
 	end
 end
 
 function Config:timeMatches(claim)
 	local startTime = nil
 	local startTimeY = nil
 	local startTimeM = nil
 	local startTimeD = nil
 	local endTime = nil
 	local endTimeY = nil
 	local endTimeM = nil
 	local endTimeD = nil
 
 	if self.periods[1] and self.periods[2] and self.periods[3] then
 		-- any time
 		return true
 	end
 
 	startTime = self:getSingleRawQualifier(claim, aliasesP.startTime)
 	if startTime and startTime ~= &quot;&quot; and startTime ~= &quot; &quot; then
 		startTimeY, startTimeM, startTimeD = parseDate(startTime)
 	end
 
 	endTime = self:getSingleRawQualifier(claim, aliasesP.endTime)
 	if endTime and endTime ~= &quot;&quot; and endTime ~= &quot; &quot; then
 		endTimeY, endTimeM, endTimeD = parseDate(endTime)
 	end
 
 	if startTimeY ~= nil and endTimeY ~= nil and datePrecedesDate(endTimeY, endTimeM, endTimeD, startTimeY, startTimeM, startTimeD) then
 		-- invalidate end time if it precedes start time
 		endTimeY = nil
 		endTimeM = nil
 		endTimeD = nil
 	end
 
 	if self.periods[1] then
 		-- future
 		if startTimeY and datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD) then
 			return true
 		end
 	end
 
 	if self.periods[2] then
 		-- current
 		if (startTimeY == nil or not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], startTimeY, startTimeM, startTimeD)) and
 		   (endTimeY == nil or datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD)) then
 			return true
 		end
 	end
 
 	if self.periods[3] then
 		-- former
 		if endTimeY and not datePrecedesDate(self.atDate[1], self.atDate[2], self.atDate[3], endTimeY, endTimeM, endTimeD) then
 			return true
 		end
 	end
 
 	return false
 end
 
 function Config:processFlag(flag)
 	if not flag then
 		return false
 	end
 
 	if flag == p.flags.linked then
 		self.curState.linked = true
 		return true
 	elseif flag == p.flags.raw then
 		self.curState.rawValue = true
 
 		if self.curState == self.states[parameters.reference] then
 			-- raw reference values end with periods and require a separator (other than none)
 			self.separators[&quot;sep%r&quot;][1] = {&quot; &quot;}
 		end
 
 		return true
 	elseif flag == p.flags.short then
 		self.curState.shortName = true
 		return true
 	elseif flag == p.flags.multilanguage then
 		self.curState.anyLanguage = true
 		return true
 	elseif flag == p.flags.unit then
 		self.curState.unitOnly = true
 		return true
 	elseif flag == p.flags.mdy then
 		self.mdyDate = true
 		return true
 	elseif flag == p.flags.single then
 		self.singleClaim = true
 		return true
 	elseif flag == p.flags.sourced then
 		self.sourcedOnly = true
 		return true
 	elseif flag == p.flags.edit then
 		self.editable = true
 		return true
 	elseif flag == p.flags.editAtEnd then
 		self.editable = true
 		self.editAtEnd = true
 		return true
 	elseif flag == p.flags.best or flag:match('^'..p.flags.preferred..'[+-]?$') or flag:match('^'..p.flags.normal..'[+-]?$') or flag:match('^'..p.flags.deprecated..'[+-]?$') then
 		self:setRank(flag)
 		return true
 	elseif flag == p.flags.future or flag == p.flags.current or flag == p.flags.former then
 		self:setPeriod(flag)
 		return true
 	elseif flag == &quot;&quot; then
 		-- ignore empty flags and carry on
 		return true
 	else
 		return false
 	end
 end
 
 function Config:processFlagOrCommand(flag)
 	local param = &quot;&quot;
 
 	if not flag then
 		return false
 	end
 
 	if flag == p.claimCommands.property or flag == p.claimCommands.properties then
 		param = parameters.property
 	elseif flag == p.claimCommands.qualifier or flag == p.claimCommands.qualifiers then
 		self.states.qualifiersCount = self.states.qualifiersCount + 1
 		param = parameters.qualifier .. self.states.qualifiersCount
 		self.separators[&quot;sep&quot;..param] = {copyTable(defaultSeparators[&quot;sep%q\\d&quot;])}
 	elseif flag == p.claimCommands.reference or flag == p.claimCommands.references then
 		param = parameters.reference
 	else
 		return self:processFlag(flag)
 	end
 
 	if self.states[param] then
 		return false
 	end
 
 	-- create a new state for each command
 	self.states[param] = State:new(self, param)
 
 	-- use &quot;%x&quot; as the general parameter name
 	self.states[param].parsedFormat = parseFormat(parameters.general)  -- will be overwritten for param==&quot;%p&quot;
 
 	-- set the separator
 	self.states[param].separator = self.separators[&quot;sep&quot;..param]  -- will be nil for param==&quot;%p&quot;, which will be set separately
 
 	if flag == p.claimCommands.property or flag == p.claimCommands.qualifier or flag == p.claimCommands.reference then
 		self.states[param].singleValue = true
 	end
 
 	self.curState = self.states[param]
 
 	return true
 end
 
 function Config:processSeparators(args)
 	local sep
 
 	for i, v in pairs(self.separators) do
 		if args[i] then
 			sep = replaceSpecialChars(args[i])
 
 			if sep ~= &quot;&quot; then
 				self.separators[i][1] = {sep}
 			else
 				self.separators[i][1] = nil
 			end
 		end
 	end
 end
 
 function Config:setFormatAndSeparators(state, parsedFormat)
 	state.parsedFormat = parsedFormat
 	state.separator = self.separators[&quot;sep&quot;]
 	state.movSeparator = self.separators[&quot;sep&quot;..parameters.separator]
 	state.puncMark = self.separators[&quot;punc&quot;]
 end
 
 -- determines if a claim has references by prefetching them from the claim using getReferences,
 -- which applies some filtering that determines if a reference is actually returned,
 -- and caches the references for later use
 function State:isSourced(claim)
 	self.conf.prefetchedRefs = self:getReferences(claim)
 	return (#self.conf.prefetchedRefs &gt; 0)
 end
 
 function State:resetCaches()
 	-- any prefetched references of the previous claim must not be used
 	self.conf.prefetchedRefs = nil
 end
 
 function State:claimMatches(claim)
 	local matches, rankPos
 
 	-- first of all, reset any cached values used for the previous claim
 	self:resetCaches()
 
 	-- if a property value was given, check if it matches the claim's property value
 	if self.conf.propertyValue then
 		matches = self.conf:snakEqualsValue(claim.mainsnak, self.conf.propertyValue)
 	else
 		matches = true
 	end
 
 	-- if any qualifier values were given, check if each matches one of the claim's qualifier values
 	for i, v in pairs(self.conf.qualifierIDsAndValues) do
 		matches = (matches and self.conf:qualifierMatches(claim, i, v))
 	end
 
 	-- check if the claim's rank and time period match
 	rankPos = rankTable[claim.rank] or 4
 	matches = (matches and self.conf:rankMatches(rankPos) and self.conf:timeMatches(claim))
 
 	-- if only claims with references must be returned, check if this one has any
 	if self.conf.sourcedOnly then
 		matches = (matches and self:isSourced(claim))  -- prefetches and caches references
 	end
 
 	return matches, rankPos
 end
 
 function State:out()
 	local result  -- collection of arrays with value objects
 	local valuesArray  -- array with value objects
 	local sep = nil  -- value object
 	local out = {}  -- array with value objects
 
 	local function walk(formatTable, result)
 		local valuesArray = {}  -- array with value objects
 
 		for i, v in pairs(formatTable.req) do
 			if not result[i] or not result[i][1] then
 				-- we've got no result for a parameter that is required on this level,
 				-- so skip this level (and its children) by returning an empty result
 				return {}
 			end
 		end
 
 		for _, v in ipairs(formatTable) do
 			if v.param then
 				valuesArray = mergeArrays(valuesArray, result[v.str])
 			elseif v.str ~= &quot;&quot; then
 				valuesArray[#valuesArray + 1] = {v.str}
 			end
 
 			if v.child then
 				valuesArray = mergeArrays(valuesArray, walk(v.child, result))
 			end
 		end
 
 		return valuesArray
 	end
 
 	-- iterate through the results from back to front, so that we know when to add separators
 	for i = #self.results, 1, -1 do
 		result = self.results[i]
 
 		-- if there is already some output, then add the separators
 		if #out &gt; 0 then
 			sep = self.separator[1]  -- fixed separator
 			result[parameters.separator] = {self.movSeparator[1]}  -- movable separator
 		else
 			sep = nil
 			result[parameters.separator] = {self.puncMark[1]}  -- optional punctuation mark
 		end
 
 		valuesArray = walk(self.parsedFormat, result)
 
 		if #valuesArray &gt; 0 then
 			if sep then
 				valuesArray[#valuesArray + 1] = sep
 			end
 
 			out = mergeArrays(valuesArray, out)
 		end
 	end
 
 	-- reset state before next iteration
 	self.results = {}
 
 	return out
 end
 
 -- level 1 hook
 function State:getProperty(claim)
 	local value = {self:getValue(claim.mainsnak)}  -- create one value object
 
 	if #value &gt; 0 then
 		return {value}  -- wrap the value object in an array and return it
 	else
 		return {}  -- return empty array if there was no value
 	end
 end
 
 -- level 1 hook
 function State:getQualifiers(claim, param)
 	local qualifiers
 
 	if claim.qualifiers then qualifiers = claim.qualifiers[self.conf.qualifierIDs[param]] end
 	if qualifiers then
 		-- iterate through claim's qualifier statements to collect their values;
 		-- return array with multiple value objects
 		return self.conf.states[param]:iterate(qualifiers, {[parameters.general] = hookNames[parameters.qualifier..&quot;\\d&quot;][2], count = 1})  -- pass qualifier state with level 2 hook
 	else
 		return {}  -- return empty array
 	end
 end
 
 -- level 2 hook
 function State:getQualifier(snak)
 	local value = {self:getValue(snak)}  -- create one value object
 
 	if #value &gt; 0 then
 		return {value}  -- wrap the value object in an array and return it
 	else
 		return {}  -- return empty array if there was no value
 	end
 end
 
 -- level 1 hook
 function State:getAllQualifiers(claim, param, result, hooks)
 	local out = {}  -- array with value objects
 	local sep = self.conf.separators[&quot;sep&quot;..parameters.qualifier][1]  -- value object
 
 	-- iterate through the output of the separate &quot;qualifier(s)&quot; commands
 	for i = 1, self.conf.states.qualifiersCount do
 
 		-- if a hook has not been called yet, call it now
 		if not result[parameters.qualifier..i] then
 			self:callHook(parameters.qualifier..i, hooks, claim, result)
 		end
 
 		-- if there is output for this particular &quot;qualifier(s)&quot; command, then add it
 		if result[parameters.qualifier..i] and result[parameters.qualifier..i][1] then
 
 			-- if there is already some output, then add the separator
 			if #out &gt; 0 and sep then
 				out[#out + 1] = sep
 			end
 
 			out = mergeArrays(out, result[parameters.qualifier..i])
 		end
 	end
 
 	return out
 end
 
 -- level 1 hook
 function State:getReferences(claim)
 	if self.conf.prefetchedRefs then
 		-- return references that have been prefetched by isSourced
 		return self.conf.prefetchedRefs
 	end
 
 	if claim.references then
 		-- iterate through claim's reference statements to collect their values;
 		-- return array with multiple value objects
 		return self.conf.states[parameters.reference]:iterate(claim.references, {[parameters.general] = hookNames[parameters.reference][2], count = 1})  -- pass reference state with level 2 hook
 	else
 		return {}  -- return empty array
 	end
 end
 
 -- level 2 hook
 function State:getReference(statement)
 	local key, citeWeb, citeQ, label
 	local params = {}
 	local citeParams = {['web'] = {}, ['q'] = {}}
 	local citeMismatch = {}
 	local useCite = nil
 	local useParams = nil
 	local value = &quot;&quot;
 	local ref = {}
 	local referenceEmpty = true  -- will be set to false if at least one parameter is left unremoved
     local numAuthorParameters = 0
     local numAuthorNameStringParameters = 0
 
 	local version = 2  -- increment this each time the below logic is changed to avoid conflict errors
 
 	if statement.snaks then
 		-- don't include &quot;imported from&quot;, which is added by a bot
 		if statement.snaks[aliasesP.importedFrom] then
 			statement.snaks[aliasesP.importedFrom] = nil
 		end
 
 		-- don't include &quot;inferred from&quot;, which is added by a bot
 		if statement.snaks[aliasesP.inferredFrom] then
 			statement.snaks[aliasesP.inferredFrom] = nil
 		end
 
 		-- don't include &quot;type of reference&quot;
 		if statement.snaks[aliasesP.typeOfReference] then
 			statement.snaks[aliasesP.typeOfReference] = nil
 		end
 
 		-- don't include &quot;image&quot; to prevent littering
 		if statement.snaks[aliasesP.image] then
 			statement.snaks[aliasesP.image] = nil
 		end
 
 		-- don't include &quot;language&quot; if it is equal to the local one
 		if self:getReferenceDetail(statement.snaks, aliasesP.language) == self.conf.langName then
 			statement.snaks[aliasesP.language] = nil
 		end
 
 		-- retrieve all the parameters
 		for i in pairs(statement.snaks) do
 			label = &quot;&quot;
 
 			-- multiple authors may be given
 			if i == aliasesP.author or i == aliasesP.authorNameString then
 				params[i] = self:getReferenceDetails(statement.snaks, i, false, self.linked, true)  -- link = true/false, anyLang = true
 			else
 				params[i] = {self:getReferenceDetail(statement.snaks, i, false, (self.linked or (i == aliasesP.statedIn)) and (statement.snaks[i][1].datatype ~= 'url'), true)}  -- link = true/false, anyLang = true
 			end
 
 			if #params[i] == 0 then
 				params[i] = nil
 			else
 				referenceEmpty = false
 				
 				if statement.snaks[i][1].datatype == 'external-id' then
 					key = &quot;external-id&quot;
 					label = self.conf:getLabel(i)
 
 					if label ~= &quot;&quot; then
 						label = label .. &quot; &quot;
 					end
 				else
 					key = i
 				end
 
 				-- add the parameter to each matching type of citation
 				for j in pairs(citeParams) do
 					-- do so if there was no mismatch with a previous parameter
 					if not citeMismatch[j] then
 						-- check if this parameter is not mismatching itself
 						if i18n['cite'][j][key] then
 							-- continue if an option is available in the corresponding cite template
 							if i18n['cite'][j][key] ~= &quot;&quot; then
                                 -- handle non-author properties (and author properties (&quot;author&quot; and &quot;author name string&quot;), if they don't use the same template parameter)
                                 if (i ~= aliasesP.author and i ~= aliasesP.authorNameString) or (i18n['cite'][j][aliasesP.author] ~= i18n['cite'][j][aliasesP.authorNameString]) then
 								    citeParams[j][i18n['cite'][j][key]] = label .. params[i][1]
                                     -- to avoid problems with non-author multiple parameters (if existent), the following old code is retained
 								    for k=2, #params[i] do
                                         citeParams[j][i18n['cite'][j][key]..k] = label .. params[i][k]
 							    	end
                                 -- handle &quot;author&quot; and &quot;author name string&quot; specially if they use the same template parameter
                                 elseif i == aliasesP.author or i == aliasesP.authorNameString then
                                     if params[aliasesP.author] ~= nil then
                                         numAuthorParameters = #params[aliasesP.author]
                                     else
                                         numAuthorParameters = 0
                                     end
 
                                     if params[aliasesP.authorNameString] ~= nil then
                                         numAuthorNameStringParameters = #params[aliasesP.authorNameString]
                                     else
                                         numAuthorNameStringParameters = 0
                                     end
 
                                     -- execute only if both &quot;author&quot; and &quot;author name string&quot; satisfy this condition: the property is both in params and in statement.snaks or it is neither in params nor in statement.snaks
                                     -- reason: parameters are added to params each iteration of the loop, not before the loop
                                     if ((statement.snaks[aliasesP.author] == nil) == (numAuthorParameters == 0)) and ((statement.snaks[aliasesP.authorNameString] == nil) == (numAuthorNameStringParameters == 0)) then
                                         for k=1, numAuthorParameters + numAuthorNameStringParameters do
                                             if k &lt;= numAuthorParameters then  -- now handling the authors from the &quot;author&quot; property
                                                 citeParams[j][i18n['cite'][j][aliasesP.author]..k] = label .. params[aliasesP.author][k]
                                             else  -- now handling the authors from &quot;author name string&quot;
                                                 citeParams[j][i18n['cite'][j][aliasesP.authorNameString]..k] = label .. params[aliasesP.authorNameString][k - numAuthorParameters]
                                             end
 							        	end
                                     end
                                 end
 							end
 						else
 							citeMismatch[j] = true
 						end
 					end
 				end
 			end
 		end
 
 		-- get title of general template for citing web references
 		citeWeb = split(mw.wikibase.getSitelink(aliasesQ.citeWeb) or &quot;&quot;, &quot;:&quot;)[2]  -- split off namespace from front
 
 		-- get title of template that expands stated-in references into citations
 		citeQ = split(mw.wikibase.getSitelink(aliasesQ.citeQ) or &quot;&quot;, &quot;:&quot;)[2]  -- split off namespace from front
 
 		-- (1) use the general template for citing web references if there is a match and if at least both &quot;reference URL&quot; and &quot;title&quot; are present
 		if citeWeb and not citeMismatch['web'] and citeParams['web'][i18n['cite']['web'][aliasesP.referenceURL]] and citeParams['web'][i18n['cite']['web'][aliasesP.title]] then
 			useCite = citeWeb
 			useParams = citeParams['web']
 
 		-- (2) use the template that expands stated-in references into citations if there is a match and if at least &quot;stated in&quot; is present
 		elseif citeQ and not citeMismatch['q'] and citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] then
 			-- we need the raw &quot;stated in&quot; Q-identifier for the this template
 			citeParams['q'][i18n['cite']['q'][aliasesP.statedIn]] = self:getReferenceDetail(statement.snaks, aliasesP.statedIn, true)  -- raw = true
 
 			useCite = citeQ
 			useParams = citeParams['q']
 		end
 
 		if useCite and useParams then
 			-- if this module is being substituted then build a regular template call, otherwise expand the template
 			if mw.isSubsting() then
 				for i, v in pairs(useParams) do
 					value = value .. &quot;|&quot; .. i .. &quot;=&quot; .. v
 				end
 
 				value = &quot;{{&quot; .. useCite .. value .. &quot;}}&quot;
 			else
 				value = mw.getCurrentFrame():expandTemplate{title=useCite, args=useParams}
 			end
 
 		-- (3) if the citation couldn't be displayed using Cite web or Cite Q, but has properties other than the removed ones, throw an error
 		elseif not referenceEmpty then
 			value = &quot;&lt;span style=\&quot;color: crimson\&quot;&gt;&quot; .. errorText(&quot;malformed-reference&quot;) .. &quot;&lt;/span&gt;&quot;
 		end
 	
 		if value ~= &quot;&quot; then
 			value = {value}  -- create one value object
 
 			if not self.rawValue then
 				-- this should become a &lt;ref&gt; tag, so save the reference's hash for later
 				value.refHash = &quot;wikidata-&quot; .. statement.hash .. &quot;-v&quot; .. (tonumber(i18n['cite']['version']) + version)
 			end
 
 			ref = {value}  -- wrap the value object in an array
 		end
 	end
 
 	return ref
 end
 
 -- gets a detail of one particular type for a reference
 function State:getReferenceDetail(snaks, dType, raw, link, anyLang)
 	local switchLang = anyLang
 	local value = nil
 
 	if not snaks[dType] then
 		return nil
 	end
 
 	-- if anyLang, first try the local language and otherwise any language
 	repeat
 		for _, v in ipairs(snaks[dType]) do
 			value = self.conf:getValue(v, raw, link, false, anyLang and not switchLang, false, true)  -- noSpecial = true
 
 			if value then
 				break
 			end
 		end
 
 		if value or not anyLang then
 			break
 		end
 
 		switchLang = not switchLang
 	until anyLang and switchLang
 
 	return value
 end
 
 -- gets the details of one particular type for a reference
 function State:getReferenceDetails(snaks, dType, raw, link, anyLang)
 	local values = {}
 
 	if not snaks[dType] then
 		return {}
 	end
 
 	for _, v in ipairs(snaks[dType]) do
 		-- if nil is returned then it will not be added to the table
 		values[#values + 1] = self.conf:getValue(v, raw, link, false, anyLang, false, true)  -- noSpecial = true
 	end
 
 	return values
 end
 
 -- level 1 hook
 function State:getAlias(object)
 	local value = object.value
 	local title = nil
 
 	if value and self.linked then
 		if self.conf.entityID:sub(1,1) == &quot;Q&quot; then
 			title = mw.wikibase.getSitelink(self.conf.entityID)
 		elseif self.conf.entityID:sub(1,1) == &quot;P&quot; then
 			title = &quot;d:Property:&quot; .. self.conf.entityID
 		end
 
 		if title then
 			value = buildWikilink(title, value)
 		end
 	end
 
 	value = {value}  -- create one value object
 
 	if #value &gt; 0 then
 		return {value}  -- wrap the value object in an array and return it
 	else
 		return {}  -- return empty array if there was no value
 	end
 end
 
 -- level 1 hook
 function State:getBadge(value)
 	value = self.conf:getLabel(value, self.rawValue, self.linked, self.shortName)
 
 	if value == &quot;&quot; then
 		value = nil
 	end
 
 	value = {value}  -- create one value object
 
 	if #value &gt; 0 then
 		return {value}  -- wrap the value object in an array and return it
 	else
 		return {}  -- return empty array if there was no value
 	end
 end
 
 function State:callHook(param, hooks, statement, result)
 	local valuesArray, refHash
 
 	-- call a parameter's hook if it has been defined and if it has not been called before
 	if not result[param] and hooks[param] then
 		valuesArray = self[hooks[param]](self, statement, param, result, hooks)  -- array with value objects
 
 		-- add to the result
 		if #valuesArray &gt; 0 then
 			result[param] = valuesArray
 			result.count = result.count + 1
 		else
 			result[param] = {}  -- an empty array to indicate that we've tried this hook already
 			return true  -- miss == true
 		end
 	end
 
 	return false
 end
 
 -- iterate through claims, claim's qualifiers or claim's references to collect values
 function State:iterate(statements, hooks, matchHook)
 	matchHook = matchHook or alwaysTrue
 
 	local matches = false
 	local rankPos = nil
 	local result, gotRequired
 
 	for _, v in ipairs(statements) do
 		-- rankPos will be nil for non-claim statements (e.g. qualifiers, references, etc.)
 		matches, rankPos = matchHook(self, v)
 
 		if matches then
 			result = {count = 0}  -- collection of arrays with value objects
 
 			local function walk(formatTable)
 				local miss
 
 				for i2, v2 in pairs(formatTable.req) do
 					-- call a hook, adding its return value to the result
 					miss = self:callHook(i2, hooks, v, result)
 
 					if miss then
 						-- we miss a required value for this level, so return false
 						return false
 					end
 
 					if result.count == hooks.count then
 						-- we're done if all hooks have been called;
 						-- returning at this point breaks the loop
 						return true
 					end
 				end
 
 				for _, v2 in ipairs(formatTable) do
 					if result.count == hooks.count then
 						-- we're done if all hooks have been called;
 						-- returning at this point prevents further childs from being processed
 						return true
 					end
 
 					if v2.child then
 						walk(v2.child)
 					end
 				end
 
 				return true
 			end
 			gotRequired = walk(self.parsedFormat)
 
 			-- only append the result if we got values for all required parameters on the root level
 			if gotRequired then
 				-- if we have a rankPos (only with matchHook() for complete claims), then update the foundRank
 				if rankPos and self.conf.foundRank &gt; rankPos then
 					self.conf.foundRank = rankPos
 				end
 
 				-- append the result
 				self.results[#self.results + 1] = result
 
 				-- break if we only need a single value
 				if self.singleValue then
 					break
 				end
 			end
 		end
 	end
 
 	return self:out()
 end
 
 local function getEntityId(arg, eid, page, allowOmitPropPrefix)
 	local id = nil
 	local prop = nil
 
 	if arg then
 		if arg:sub(1,1) == &quot;:&quot; then
 			page = arg
 			eid = nil
 		elseif arg:sub(1,1):upper() == &quot;Q&quot; or arg:sub(1,9):lower() == &quot;property:&quot; or allowOmitPropPrefix then
 			eid = arg
 			page = nil
 		else
 			prop = arg
 		end
 	end
 
 	if eid then
 		if eid:sub(1,9):lower() == &quot;property:&quot; then
 			id = replaceAlias(mw.text.trim(eid:sub(10)))
 
 			if id:sub(1,1):upper() ~= &quot;P&quot; then
 				id = &quot;&quot;
 			end
 		else
 			id = replaceAlias(eid)
 		end
 	elseif page then
 		if page:sub(1,1) == &quot;:&quot; then
 			page = mw.text.trim(page:sub(2))
 		end
 
 		id = mw.wikibase.getEntityIdForTitle(page) or &quot;&quot;
 	end
 
 	if not id then
 		id = mw.wikibase.getEntityIdForCurrentPage() or &quot;&quot;
 	end
 
 	id = id:upper()
 
 	if not mw.wikibase.isValidEntityId(id) then
 		id = &quot;&quot;
 	end
 
 	return id, prop
 end
 
 local function nextArg(args)
 	local arg = args[args.pointer]
 
 	if arg then
 		args.pointer = args.pointer + 1
 		return mw.text.trim(arg)
 	else
 		return nil
 	end
 end
 
 local function claimCommand(args, funcName)
 	local cfg = Config:new()
 	cfg:processFlagOrCommand(funcName)  -- process first command (== function name)
 
 	local lastArg, parsedFormat, formatParams, claims, value
 	local hooks = {count = 0}
 
 	-- set the date if given;
 	-- must come BEFORE processing the flags
 	if args[p.args.date] then
 		cfg.atDate = {parseDate(args[p.args.date])}
 		cfg.periods = {false, true, false}  -- change default time constraint to 'current'
 	end
 
 	-- process flags and commands
 	repeat
 		lastArg = nextArg(args)
 	until not cfg:processFlagOrCommand(lastArg)
 
 	-- get the entity ID from either the positional argument, the eid argument or the page argument
 	cfg.entityID, cfg.propertyID = getEntityId(lastArg, args[p.args.eid], args[p.args.page])
 
 	if cfg.entityID == &quot;&quot; then
 		return &quot;&quot;  -- we cannot continue without a valid entity ID
 	end
 
 	cfg.entity = mw.wikibase.getEntity(cfg.entityID)
 
 	if not cfg.propertyID then
 		cfg.propertyID = nextArg(args)
 	end
 
 	cfg.propertyID = replaceAlias(cfg.propertyID)
 
 	if not cfg.entity or not cfg.propertyID then
 		return &quot;&quot;  -- we cannot continue without an entity or a property ID
 	end
 
 	cfg.propertyID = cfg.propertyID:upper()
 
 	if not cfg.entity.claims or not cfg.entity.claims[cfg.propertyID] then
 		return &quot;&quot;  -- there is no use to continue without any claims
 	end
 
 	claims = cfg.entity.claims[cfg.propertyID]
 
 	if cfg.states.qualifiersCount &gt; 0 then
 		-- do further processing if &quot;qualifier(s)&quot; command was given
 
 		if #args - args.pointer + 1 &gt; cfg.states.qualifiersCount then
 			-- claim ID or literal value has been given
 
 			cfg.propertyValue = nextArg(args)
 		end
 
 		for i = 1, cfg.states.qualifiersCount do
 			-- check if given qualifier ID is an alias and add it
 			cfg.qualifierIDs[parameters.qualifier..i] = replaceAlias(nextArg(args) or &quot;&quot;):upper()
 		end
 	elseif cfg.states[parameters.reference] then
 		-- do further processing if &quot;reference(s)&quot; command was given
 
 		cfg.propertyValue = nextArg(args)
 	end
 
 	-- check for special property value 'somevalue' or 'novalue'
 	if cfg.propertyValue then
 		cfg.propertyValue = replaceSpecialChars(cfg.propertyValue)
 
 		if cfg.propertyValue ~= &quot;&quot; and mw.text.trim(cfg.propertyValue) == &quot;&quot; then
 			cfg.propertyValue = &quot; &quot;  -- single space represents 'somevalue', whereas empty string represents 'novalue'
 		else
 			cfg.propertyValue = mw.text.trim(cfg.propertyValue)
 		end
 	end
 
 	-- parse the desired format, or choose an appropriate format
 	if args[&quot;format&quot;] then
 		parsedFormat, formatParams = parseFormat(args[&quot;format&quot;])
 	elseif cfg.states.qualifiersCount &gt; 0 then  -- &quot;qualifier(s)&quot; command given
 		if cfg.states[parameters.property] then  -- &quot;propert(y|ies)&quot; command given
 			parsedFormat, formatParams = parseFormat(formats.propertyWithQualifier)
 		else
 			parsedFormat, formatParams = parseFormat(formats.qualifier)
 		end
 	elseif cfg.states[parameters.property] then  -- &quot;propert(y|ies)&quot; command given
 		parsedFormat, formatParams = parseFormat(formats.property)
 	else  -- &quot;reference(s)&quot; command given
 		parsedFormat, formatParams = parseFormat(formats.reference)
 	end
 
 	-- if a &quot;qualifier(s)&quot; command and no &quot;propert(y|ies)&quot; command has been given, make the movable separator a semicolon
 	if cfg.states.qualifiersCount &gt; 0 and not cfg.states[parameters.property] then
 		cfg.separators[&quot;sep&quot;..parameters.separator][1] = {&quot;;&quot;}
 	end
 
 	-- if only &quot;reference(s)&quot; has been given, set the default separator to none (except when raw)
 	if cfg.states[parameters.reference] and not cfg.states[parameters.property] and cfg.states.qualifiersCount == 0
 	   and not cfg.states[parameters.reference].rawValue then
 		cfg.separators[&quot;sep&quot;][1] = nil
 	end
 
 	-- if exactly one &quot;qualifier(s)&quot; command has been given, make &quot;sep%q&quot; point to &quot;sep%q1&quot; to make them equivalent
 	if cfg.states.qualifiersCount == 1 then
 		cfg.separators[&quot;sep&quot;..parameters.qualifier] = cfg.separators[&quot;sep&quot;..parameters.qualifier..&quot;1&quot;]
 	end
 
 	-- process overridden separator values;
 	-- must come AFTER tweaking the default separators
 	cfg:processSeparators(args)
 
 	-- define the hooks that should be called (getProperty, getQualifiers, getReferences);
 	-- only define a hook if both its command (&quot;propert(y|ies)&quot;, &quot;reference(s)&quot;, &quot;qualifier(s)&quot;) and its parameter (&quot;%p&quot;, &quot;%r&quot;, &quot;%q1&quot;, &quot;%q2&quot;, &quot;%q3&quot;) have been given
 	for i, v in pairs(cfg.states) do
 		-- e.g. 'formatParams[&quot;%q1&quot;] or formatParams[&quot;%q&quot;]' to define hook even if &quot;%q1&quot; was not defined to be able to build a complete value for &quot;%q&quot;
 		if formatParams[i] or formatParams[i:sub(1, 2)] then
 			hooks[i] = getHookName(i, 1)
 			hooks.count = hooks.count + 1
 		end
 	end
 
 	-- the &quot;%q&quot; parameter is not attached to a state, but is a collection of the results of multiple states (attached to &quot;%q1&quot;, &quot;%q2&quot;, &quot;%q3&quot;, ...);
 	-- so if this parameter is given then this hook must be defined separately, but only if at least one &quot;qualifier(s)&quot; command has been given
 	if formatParams[parameters.qualifier] and cfg.states.qualifiersCount &gt; 0 then
 		hooks[parameters.qualifier] = getHookName(parameters.qualifier, 1)
 		hooks.count = hooks.count + 1
 	end
 
 	-- create a state for &quot;properties&quot; if it doesn't exist yet, which will be used as a base configuration for each claim iteration;
 	-- must come AFTER defining the hooks
 	if not cfg.states[parameters.property] then
 		cfg.states[parameters.property] = State:new(cfg, parameters.property)
 
 		-- if the &quot;single&quot; flag has been given then this state should be equivalent to &quot;property&quot; (singular)
 		if cfg.singleClaim then
 			cfg.states[parameters.property].singleValue = true
 		end
 	end
 
 	-- if the &quot;sourced&quot; flag has been given then create a state for &quot;reference&quot; if it doesn't exist yet, using default values,
 	-- which must exist in order to be able to determine if a claim has any references;
 	-- must come AFTER defining the hooks
 	if cfg.sourcedOnly and not cfg.states[parameters.reference] then
 		cfg:processFlagOrCommand(p.claimCommands.reference)  -- use singular &quot;reference&quot; to minimize overhead
 	end
 
 	-- set the parsed format and the separators (and optional punctuation mark);
 	-- must come AFTER creating the additonal states
 	cfg:setFormatAndSeparators(cfg.states[parameters.property], parsedFormat)
 
 	-- process qualifier matching values, analogous to cfg.propertyValue
 	for i, v in pairs(args) do
 		i = tostring(i)
 
 		if i:match('^[Pp]%d+$') or aliasesP[i] then
 			v = replaceSpecialChars(v)
 
 			-- check for special qualifier value 'somevalue'
 			if v ~= &quot;&quot; and mw.text.trim(v) == &quot;&quot; then
 				v = &quot; &quot;  -- single space represents 'somevalue'
 			end
 
 			cfg.qualifierIDsAndValues[replaceAlias(i):upper()] = v
 		end
 	end
 
 	-- first sort the claims on rank to pre-define the order of output (preferred first, then normal, then deprecated)
 	claims = sortOnRank(claims)
 
 	-- then iterate through the claims to collect values
 	value = cfg:concatValues(cfg.states[parameters.property]:iterate(claims, hooks, State.claimMatches))  -- pass property state with level 1 hooks and matchHook
 
 	-- if desired, add a clickable icon that may be used to edit the returned values on Wikidata
 	if cfg.editable and value ~= &quot;&quot; then
 		value = value .. cfg:getEditIcon()
 	end
 
 	return value
 end
 
 local function generalCommand(args, funcName)
 	local cfg = Config:new()
 	cfg.curState = State:new(cfg)
 
 	local lastArg
 	local value = nil
 
 	repeat
 		lastArg = nextArg(args)
 	until not cfg:processFlag(lastArg)
 
 	-- get the entity ID from either the positional argument, the eid argument or the page argument
 	cfg.entityID = getEntityId(lastArg, args[p.args.eid], args[p.args.page], true)
 
 	if cfg.entityID == &quot;&quot; or not mw.wikibase.entityExists(cfg.entityID) then
 		return &quot;&quot;  -- we cannot continue without an entity
 	end
 
 	-- serve according to the given command
 	if funcName == p.generalCommands.label then
 		value = cfg:getLabel(cfg.entityID, cfg.curState.rawValue, cfg.curState.linked, cfg.curState.shortName)
 	elseif funcName == p.generalCommands.title then
 		cfg.inSitelinks = true
 
 		if cfg.entityID:sub(1,1) == &quot;Q&quot; then
 			value = mw.wikibase.getSitelink(cfg.entityID)
 		end
 
 		if cfg.curState.linked and value then
 			value = buildWikilink(value)
 		end
 	elseif funcName == p.generalCommands.description then
 		value = mw.wikibase.getDescription(cfg.entityID)
 	else
 		local parsedFormat, formatParams
 		local hooks = {count = 0}
 
 		cfg.entity = mw.wikibase.getEntity(cfg.entityID)
 
 		if funcName == p.generalCommands.alias or funcName == p.generalCommands.badge then
 			cfg.curState.singleValue = true
 		end
 
 		if funcName == p.generalCommands.alias or funcName == p.generalCommands.aliases then
 			if not cfg.entity.aliases or not cfg.entity.aliases[cfg.langCode] then
 				return &quot;&quot;  -- there is no use to continue without any aliasses
 			end
 
 			local aliases = cfg.entity.aliases[cfg.langCode]
 
 			-- parse the desired format, or parse the default aliases format
 			if args[&quot;format&quot;] then
 				parsedFormat, formatParams = parseFormat(args[&quot;format&quot;])
 			else
 				parsedFormat, formatParams = parseFormat(formats.alias)
 			end
 
 			-- process overridden separator values;
 			-- must come AFTER tweaking the default separators
 			cfg:processSeparators(args)
 
 			-- define the hook that should be called (getAlias);
 			-- only define the hook if the parameter (&quot;%a&quot;) has been given
 			if formatParams[parameters.alias] then
 				hooks[parameters.alias] = getHookName(parameters.alias, 1)
 				hooks.count = hooks.count + 1
 			end
 
 			-- set the parsed format and the separators (and optional punctuation mark)
 			cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
 
 			-- iterate to collect values
 			value = cfg:concatValues(cfg.curState:iterate(aliases, hooks))
 		elseif funcName == p.generalCommands.badge or funcName == p.generalCommands.badges then
 			if not cfg.entity.sitelinks or not cfg.entity.sitelinks[cfg.siteID] or not cfg.entity.sitelinks[cfg.siteID].badges then
 				return &quot;&quot;  -- there is no use to continue without any badges
 			end
 
 			local badges = cfg.entity.sitelinks[cfg.siteID].badges
 
 			cfg.inSitelinks = true
 
 			-- parse the desired format, or parse the default aliases format
 			if args[&quot;format&quot;] then
 				parsedFormat, formatParams = parseFormat(args[&quot;format&quot;])
 			else
 				parsedFormat, formatParams = parseFormat(formats.badge)
 			end
 
 			-- process overridden separator values;
 			-- must come AFTER tweaking the default separators
 			cfg:processSeparators(args)
 
 			-- define the hook that should be called (getBadge);
 			-- only define the hook if the parameter (&quot;%b&quot;) has been given
 			if formatParams[parameters.badge] then
 				hooks[parameters.badge] = getHookName(parameters.badge, 1)
 				hooks.count = hooks.count + 1
 			end
 
 			-- set the parsed format and the separators (and optional punctuation mark)
 			cfg:setFormatAndSeparators(cfg.curState, parsedFormat)
 
 			-- iterate to collect values
 			value = cfg:concatValues(cfg.curState:iterate(badges, hooks))
 		end
 	end
 
 	value = value or &quot;&quot;
 
 	if cfg.editable and value ~= &quot;&quot; then
 		-- if desired, add a clickable icon that may be used to edit the returned value on Wikidata
 		value = value .. cfg:getEditIcon()
 	end
 
 	return value
 end
 
 -- modules that include this module should call the functions with an underscore prepended, e.g.: p._property(args)
 local function establishCommands(commandList, commandFunc)
 	for _, commandName in pairs(commandList) do
 		local function wikitextWrapper(frame)
 			local args = copyTable(frame.args)
 			args.pointer = 1
 			loadI18n(aliasesP, frame)
 			return commandFunc(args, commandName)
 		end
 		p[commandName] = wikitextWrapper
 
 		local function luaWrapper(args)
 			args = copyTable(args)
 			args.pointer = 1
 			loadI18n(aliasesP)
 			return commandFunc(args, commandName)
 		end
 		p[&quot;_&quot; .. commandName] = luaWrapper
 	end
 end
 
 establishCommands(p.claimCommands, claimCommand)
 establishCommands(p.generalCommands, generalCommand)
 
 -- main function that is supposed to be used by wrapper templates
 function p.main(frame)
 	if not mw.wikibase then return nil end
 
 	local f, args
 
 	loadI18n(aliasesP, frame)
 
 	-- get the parent frame to take the arguments that were passed to the wrapper template
 	frame = frame:getParent() or frame
 
 	if not frame.args[1] then
 		throwError(&quot;no-function-specified&quot;)
 	end
 
 	f = mw.text.trim(frame.args[1])
 
 	if f == &quot;main&quot; then
 		throwError(&quot;main-called-twice&quot;)
 	end
 
 	assert(p[&quot;_&quot;..f], errorText('no-such-function', f))
 
 	-- copy arguments from immutable to mutable table
 	args = copyTable(frame.args)
 
 	-- remove the function name from the list
 	table.remove(args, 1)
 
 	return p[&quot;_&quot;..f](args)
 end