<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en-GB">
	<id>https://the-democratika.com/wiki/index.php?action=history&amp;feed=atom&amp;title=Module%3AConvert%2Fwikidata</id>
	<title>Module:Convert/wikidata - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://the-democratika.com/wiki/index.php?action=history&amp;feed=atom&amp;title=Module%3AConvert%2Fwikidata"/>
	<link rel="alternate" type="text/html" href="https://the-democratika.com/wiki/index.php?title=Module:Convert/wikidata&amp;action=history"/>
	<updated>2026-04-04T21:07:41Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.43.0</generator>
	<entry>
		<id>https://the-democratika.com/wiki/index.php?title=Module:Convert/wikidata&amp;diff=9799&amp;oldid=prev</id>
		<title>&gt;Johnuniq: update from sandbox per Template talk:Convert#Module version 25</title>
		<link rel="alternate" type="text/html" href="https://the-democratika.com/wiki/index.php?title=Module:Convert/wikidata&amp;diff=9799&amp;oldid=prev"/>
		<updated>2021-05-06T05:08:27Z</updated>

		<summary type="html">&lt;p&gt;update from sandbox per &lt;a href=&quot;/wiki/index.php?title=Template_talk:Convert&amp;amp;action=edit&amp;amp;redlink=1&quot; class=&quot;new&quot; title=&quot;Template talk:Convert (page does not exist)&quot;&gt;Template talk:Convert#Module version 25&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;-- Functions to access Wikidata for Module:Convert.&lt;br /&gt;
&lt;br /&gt;
local Collection = {}&lt;br /&gt;
Collection.__index = Collection&lt;br /&gt;
do&lt;br /&gt;
	function Collection:add(item)&lt;br /&gt;
		if item ~= nil then&lt;br /&gt;
			self.n = self.n + 1&lt;br /&gt;
			self[self.n] = item&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	function Collection:join(sep)&lt;br /&gt;
		return table.concat(self, sep)&lt;br /&gt;
	end&lt;br /&gt;
	function Collection:remove(pos)&lt;br /&gt;
		if self.n &amp;gt; 0 and (pos == nil or (0 &amp;lt; pos and pos &amp;lt;= self.n)) then&lt;br /&gt;
			self.n = self.n - 1&lt;br /&gt;
			return table.remove(self, pos)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	function Collection:sort(comp)&lt;br /&gt;
		table.sort(self, comp)&lt;br /&gt;
	end&lt;br /&gt;
	function Collection.new()&lt;br /&gt;
		return setmetatable({n = 0}, Collection)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function strip_to_nil(text)&lt;br /&gt;
	-- If text is a non-empty string, return its trimmed content,&lt;br /&gt;
	-- otherwise return nothing (empty string or not a string).&lt;br /&gt;
	if type(text) == &amp;#039;string&amp;#039; then&lt;br /&gt;
		return text:match(&amp;#039;(%S.-)%s*$&amp;#039;)&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function frequency_unit(value, unit_table)&lt;br /&gt;
	-- For use when converting m to Hz.&lt;br /&gt;
	-- Return true, s where s = name of unit&amp;#039;s default output unit,&lt;br /&gt;
	-- or return false, t where t is an error message table.&lt;br /&gt;
	-- However, for simplicity a valid result is always returned.&lt;br /&gt;
	local unit&lt;br /&gt;
	if unit_table._symbol == &amp;#039;m&amp;#039; then&lt;br /&gt;
		-- c = speed of light in a vacuum = 299792458 m/s&lt;br /&gt;
		-- frequency = c / wavelength&lt;br /&gt;
		local w = value * (unit_table.scale or 1)&lt;br /&gt;
		local f = 299792458 / w  -- if w == 0, f = math.huge which works here&lt;br /&gt;
		if f &amp;gt;= 1e12 then&lt;br /&gt;
			unit = &amp;#039;THz&amp;#039;&lt;br /&gt;
		elseif f &amp;gt;= 1e9 then&lt;br /&gt;
			unit = &amp;#039;GHz&amp;#039;&lt;br /&gt;
		elseif f &amp;gt;= 1e6 then&lt;br /&gt;
			unit = &amp;#039;MHz&amp;#039;&lt;br /&gt;
		elseif f &amp;gt;= 1e3 then&lt;br /&gt;
			unit = &amp;#039;kHz&amp;#039;&lt;br /&gt;
		else&lt;br /&gt;
			unit = &amp;#039;Hz&amp;#039;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return true, unit or &amp;#039;Hz&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function wavelength_unit(value, unit_table)&lt;br /&gt;
	-- Like frequency_unit but for use when converting Hz to m.&lt;br /&gt;
	local unit&lt;br /&gt;
	if unit_table._symbol == &amp;#039;Hz&amp;#039; then&lt;br /&gt;
		-- Using 0.9993 rather than 1 avoids rounding which would give results&lt;br /&gt;
		-- like converting 300 MHz to 100 cm instead of 1 m.&lt;br /&gt;
		local w = 1 / (value * (unit_table.scale or 1))  -- Hz scale is inverted&lt;br /&gt;
		if w &amp;gt;= 0.9993e6 then&lt;br /&gt;
			unit = &amp;#039;Mm&amp;#039;&lt;br /&gt;
		elseif w &amp;gt;= 0.9993e3 then&lt;br /&gt;
			unit = &amp;#039;km&amp;#039;&lt;br /&gt;
		elseif w &amp;gt;= 0.9993 then&lt;br /&gt;
			unit = &amp;#039;m&amp;#039;&lt;br /&gt;
		elseif w &amp;gt;= 0.9993e-2 then&lt;br /&gt;
			unit = &amp;#039;cm&amp;#039;&lt;br /&gt;
		elseif w &amp;gt;= 0.9993e-3 then&lt;br /&gt;
			unit = &amp;#039;mm&amp;#039;&lt;br /&gt;
		else&lt;br /&gt;
			unit = &amp;#039;um&amp;#039;&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return true, unit or &amp;#039;m&amp;#039;&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local specials = {&lt;br /&gt;
	frequency = { frequency_unit },&lt;br /&gt;
	wavelength = { wavelength_unit },&lt;br /&gt;
	--------------------------------------------------------------------------------&lt;br /&gt;
	-- Following is a removed experiment to show two values as a range&lt;br /&gt;
	-- using &amp;#039;-&amp;#039; as the separator.&lt;br /&gt;
	-- frequencyrange = { frequency_unit, &amp;#039;-&amp;#039; },&lt;br /&gt;
	-- wavelengthrange = { wavelength_unit, &amp;#039;-&amp;#039; },&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function make_unit(units, parms, uid)&lt;br /&gt;
	-- Return a unit code for convert or nil if unit unknown.&lt;br /&gt;
	-- If necessary, add a dummy unit to parms so convert will use it&lt;br /&gt;
	-- for the input without attempting a conversion since nothing&lt;br /&gt;
	-- useful is available (for example, with unit volt).&lt;br /&gt;
	local unit = units[uid]&lt;br /&gt;
	if type(unit) ~= &amp;#039;table&amp;#039; then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	local ucode = unit.ucode&lt;br /&gt;
	if ucode and not unit.si then&lt;br /&gt;
		return ucode                -- a unit known to convert&lt;br /&gt;
	end&lt;br /&gt;
	parms.opt_ignore_error = true&lt;br /&gt;
	ucode = ucode or unit._ucode    -- must be a non-empty string&lt;br /&gt;
	local ukey, utable&lt;br /&gt;
	if unit.si then&lt;br /&gt;
		local base = units[unit.si]&lt;br /&gt;
		ukey = base.symbol          -- must be a non-empty string&lt;br /&gt;
		local n1 = base.name1&lt;br /&gt;
		local n2 = base.name2&lt;br /&gt;
		if not n1 then&lt;br /&gt;
			n1 = ukey&lt;br /&gt;
			n2 = n2 or n1           -- do not append &amp;#039;s&amp;#039;&lt;br /&gt;
		end&lt;br /&gt;
		utable = {&lt;br /&gt;
			_symbol = ukey,&lt;br /&gt;
			_name1 = n1,&lt;br /&gt;
			_name2 = n2,&lt;br /&gt;
			link = unit.link or base.link,&lt;br /&gt;
			utype = n1,&lt;br /&gt;
			prefixes = 1,&lt;br /&gt;
		}&lt;br /&gt;
	else&lt;br /&gt;
		ukey = ucode&lt;br /&gt;
		utable = {&lt;br /&gt;
			symbol = ucode,         -- must be a non-empty string&lt;br /&gt;
			name1 = unit.name1,     -- if nil, uses symbol&lt;br /&gt;
			name2 = unit.name2,     -- if nil, uses name1..&amp;#039;s&amp;#039;&lt;br /&gt;
			link = unit.link,       -- if nil, uses name1&lt;br /&gt;
			utype = unit.name1 or ucode,&lt;br /&gt;
		}&lt;br /&gt;
	end&lt;br /&gt;
	utable.scale = 1&lt;br /&gt;
	utable.default = &amp;#039;&amp;#039;&lt;br /&gt;
	utable.defkey = &amp;#039;&amp;#039;&lt;br /&gt;
	utable.linkey = &amp;#039;&amp;#039;&lt;br /&gt;
	utable.bad_mcode = &amp;#039;&amp;#039;&lt;br /&gt;
	parms.unittable = { [ukey] = utable }&lt;br /&gt;
	return ucode&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function matches_qualifier(statement, qual)&lt;br /&gt;
	-- Return:&lt;br /&gt;
	--   false, nil : if statement does not match specification&lt;br /&gt;
	--   true, nil  : if matches, and statement has no qualifier&lt;br /&gt;
	--   true, sq   : if matches, where sq is the statement&amp;#039;s qualifier&lt;br /&gt;
	-- A match means that no qualifier was specified (qual == nil), or that&lt;br /&gt;
	-- the statement has a qualifier matching the specification.&lt;br /&gt;
	-- If a match occurs, the caller needs the statement&amp;#039;s qualifier (if any)&lt;br /&gt;
	-- so statements that duplicate the qualifier are not used, after the first.&lt;br /&gt;
	-- Then, if convert is showing all values for a property such as the diameter&lt;br /&gt;
	-- of a telescope&amp;#039;s mirror (diameters of primary and secondary mirrors), it&lt;br /&gt;
	-- will not show alternative values that could in principle be present for the&lt;br /&gt;
	-- same item (telescope) and property (diameter) and qualifier (primary/secondary).&lt;br /&gt;
	local target = (statement.qualifiers or {}).P518  -- P518 is &amp;quot;applies to part&amp;quot;&lt;br /&gt;
	if type(target) == &amp;#039;table&amp;#039; then&lt;br /&gt;
		for _, q in ipairs(target) do&lt;br /&gt;
			if type(q) == &amp;#039;table&amp;#039; then&lt;br /&gt;
				local value = (q.datavalue or {}).value&lt;br /&gt;
				if value then&lt;br /&gt;
					if qual == nil or qual == value.id then&lt;br /&gt;
						return true, value.id&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if qual == nil then&lt;br /&gt;
		return true, nil  -- only occurs if statement has no qualifier&lt;br /&gt;
	end&lt;br /&gt;
	return false, nil  -- statement&amp;#039;s qualifier is not relevant because statement will be skipped&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function get_statements(parms, pid)&lt;br /&gt;
	-- Get specified item and return a list of tables with each statement for property pid.&lt;br /&gt;
	-- Each table is of form {statqual=sq, stmt=statement} where sq = statement qualifier (nil if none).&lt;br /&gt;
	-- Statements are in Wikidata&amp;#039;s order except that those with preferred rank&lt;br /&gt;
	-- are first, then normal rank. Any other rank is ignored.&lt;br /&gt;
	local stored = {}  -- qualifiers of statements that are first for the qualifier, and will be returned&lt;br /&gt;
	local qid = strip_to_nil(parms.qid)  -- nil for current page&amp;#039;s item, or an item id (expensive)&lt;br /&gt;
	local qual = strip_to_nil(parms.qual)  -- nil or id of wanted P518 (applies to part) item in qualifiers&lt;br /&gt;
	local result = Collection.new()&lt;br /&gt;
	local entity = mw.wikibase.getEntity(qid)&lt;br /&gt;
	if type(entity) == &amp;#039;table&amp;#039; then&lt;br /&gt;
		local statements = (entity.claims or {})[pid]&lt;br /&gt;
		if type(statements) == &amp;#039;table&amp;#039; then&lt;br /&gt;
			for _, rank in ipairs({ &amp;#039;preferred&amp;#039;, &amp;#039;normal&amp;#039; }) do&lt;br /&gt;
				for _, statement in ipairs(statements) do&lt;br /&gt;
					if type(statement) == &amp;#039;table&amp;#039; and rank == statement.rank then&lt;br /&gt;
						local is_match, statqual = matches_qualifier(statement, qual)&lt;br /&gt;
						if is_match then&lt;br /&gt;
							result:add({ statqual = statqual, stmt = statement })&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return result&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function input_from_property(tdata, parms, pid)&lt;br /&gt;
	-- Given that pid is a Wikidata property identifier like &amp;#039;P123&amp;#039;,&lt;br /&gt;
	-- return a collection of {amount, ucode} pairs (two strings)&lt;br /&gt;
	-- for each matching item/property, or return nothing.&lt;br /&gt;
	--------------------------------------------------------------------------------&lt;br /&gt;
	-- There appear to be few restrictions on how Wikidata is organized so it is&lt;br /&gt;
	-- very likely that any decision a module makes about how to handle data&lt;br /&gt;
	-- will be wrong for some cases at some time. This meets current requirements.&lt;br /&gt;
	-- For each qualifier (or if no qualifier), if there are any preferred&lt;br /&gt;
	-- statements, use them and ignore any normal statements.&lt;br /&gt;
	-- For each qualifier, for the preferred statements if any, or for&lt;br /&gt;
	-- the normal statements (but not both):&lt;br /&gt;
	-- * Accept each statement if it has no qualifier (this will not occur&lt;br /&gt;
	--   if qual=x is specified because other code already ensures that in that&lt;br /&gt;
	--   case, only statements with a qualifier matching x are considered).&lt;br /&gt;
	-- * Ignore any statements after the first if it has a qualifier.&lt;br /&gt;
	-- The rationale is that for the diameter at [[South Pole Telescope]], want&lt;br /&gt;
	-- convert to show the diameters for both the primary and secondary mirrors&lt;br /&gt;
	-- if the convert does not specify which diameter is wanted.&lt;br /&gt;
	-- However, if convert is given the wanted qualifier, only one value&lt;br /&gt;
	-- (_the_ diameter) is wanted. For simplicity/consistency, that is also done&lt;br /&gt;
	-- even if no qual=x is specified. Unclear what should happen.&lt;br /&gt;
	-- For the wavelength at [[Nançay Radio Telescope]], want to show all three&lt;br /&gt;
	-- values, and the values have no qualifiers.&lt;br /&gt;
	--------------------------------------------------------------------------------&lt;br /&gt;
	local result = Collection.new()&lt;br /&gt;
	local done = {}&lt;br /&gt;
	local skip_normal&lt;br /&gt;
	for _, t in ipairs(get_statements(parms, pid)) do&lt;br /&gt;
		local statement = t.stmt&lt;br /&gt;
		if statement.mainsnak and statement.mainsnak.datatype == &amp;#039;quantity&amp;#039; then&lt;br /&gt;
			local value = (statement.mainsnak.datavalue or {}).value&lt;br /&gt;
			if value then&lt;br /&gt;
				local amount = value.amount&lt;br /&gt;
				if amount then&lt;br /&gt;
					amount = tostring(amount)  -- in case amount is ever a number&lt;br /&gt;
					if amount:sub(1, 1) == &amp;#039;+&amp;#039; then&lt;br /&gt;
						amount = amount:sub(2)&lt;br /&gt;
					end&lt;br /&gt;
					local unit = value.unit&lt;br /&gt;
					if type(unit) == &amp;#039;string&amp;#039; then&lt;br /&gt;
						unit = unit:match(&amp;#039;Q%d+$&amp;#039;)  -- unit item id is at end of URL&lt;br /&gt;
						local ucode = make_unit(tdata.wikidata_units, parms, unit)&lt;br /&gt;
						if ucode then&lt;br /&gt;
							local skip&lt;br /&gt;
							if t.statqual then&lt;br /&gt;
								if done[t.statqual] then&lt;br /&gt;
									skip = true&lt;br /&gt;
								else&lt;br /&gt;
									done[t.statqual] = true&lt;br /&gt;
								end&lt;br /&gt;
							else&lt;br /&gt;
								if statement.rank == &amp;#039;preferred&amp;#039; then&lt;br /&gt;
									skip_normal = true&lt;br /&gt;
								elseif skip_normal then&lt;br /&gt;
									skip = true&lt;br /&gt;
								end&lt;br /&gt;
							end&lt;br /&gt;
							if not skip then&lt;br /&gt;
								result:add({ amount, ucode })&lt;br /&gt;
							end&lt;br /&gt;
						end&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return result&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function input_from_text(tdata, parms, text, insert2)&lt;br /&gt;
	-- Given string should be of form &amp;quot;&amp;lt;value&amp;gt;&amp;lt;space&amp;gt;&amp;lt;unit&amp;gt;&amp;quot; or&lt;br /&gt;
	-- &amp;quot;&amp;lt;value1&amp;gt;&amp;lt;space&amp;gt;ft&amp;lt;space&amp;gt;&amp;lt;value2&amp;gt;&amp;lt;space&amp;gt;in&amp;quot; for a special case (feet and inches).&lt;br /&gt;
	-- Return true if values/units were extracted and inserted, or return nothing.&lt;br /&gt;
	text = text:gsub(&amp;#039;&amp;amp;nbsp;&amp;#039;, &amp;#039; &amp;#039;):gsub(&amp;#039;%s+&amp;#039;, &amp;#039; &amp;#039;)&lt;br /&gt;
	local pos = text:find(&amp;#039; &amp;#039;, 1, true)&lt;br /&gt;
	if pos then&lt;br /&gt;
		-- Leave checking of value to convert which can handle fractions.&lt;br /&gt;
		local value = text:sub(1, pos - 1)&lt;br /&gt;
		local uid = text:sub(pos + 1)&lt;br /&gt;
		if uid:sub(1, 3) == &amp;#039;ft &amp;#039; and uid:sub(-3) == &amp;#039; in&amp;#039; then&lt;br /&gt;
			-- Special case for enwiki to allow {{convert|input=5 ft 10+1/2 in}}&lt;br /&gt;
			insert2(uid:sub(4, -4), &amp;#039;in&amp;#039;)&lt;br /&gt;
			insert2(value, &amp;#039;ft&amp;#039;)&lt;br /&gt;
		else&lt;br /&gt;
			insert2(value, make_unit(tdata.wikidata_units, parms, uid) or uid)&lt;br /&gt;
		end&lt;br /&gt;
		return true&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function adjustparameters(tdata, parms, index)&lt;br /&gt;
	-- For Module:Convert, adjust parms (a table of {{convert}} parameters).&lt;br /&gt;
	-- Return true if successful or return false, t where t is an error message table.&lt;br /&gt;
	-- This is intended mainly for use in infoboxes where the input might be&lt;br /&gt;
	--    &amp;lt;value&amp;gt;&amp;lt;space&amp;gt;&amp;lt;unit&amp;gt;    or&lt;br /&gt;
	--    &amp;lt;wikidata-property-id&amp;gt;&lt;br /&gt;
	-- If successful, insert values and units in parms, before given index.&lt;br /&gt;
	local text = parms.input  -- should be a trimmed, non-empty string&lt;br /&gt;
	local pid = text:match(&amp;#039;^P%d+$&amp;#039;)&lt;br /&gt;
	local sep = &amp;#039;,&amp;#039;&lt;br /&gt;
	local special = specials[parms[index]]&lt;br /&gt;
	if special then&lt;br /&gt;
		parms.out_unit = special[1]&lt;br /&gt;
		sep = special[2] or sep&lt;br /&gt;
		table.remove(parms, index)&lt;br /&gt;
	end&lt;br /&gt;
	local function quit()&lt;br /&gt;
		return false, pid and { &amp;#039;cvt_no_output&amp;#039; } or { &amp;#039;cvt_bad_input&amp;#039;, text }&lt;br /&gt;
	end&lt;br /&gt;
	local function insert2(first, second)&lt;br /&gt;
		table.insert(parms, index, second)&lt;br /&gt;
		table.insert(parms, index, first)&lt;br /&gt;
	end&lt;br /&gt;
	if pid then&lt;br /&gt;
		parms.input_text = &amp;#039;&amp;#039;  -- output an empty string if an error occurs&lt;br /&gt;
		local result = input_from_property(tdata, parms, pid)&lt;br /&gt;
		if result.n == 0 then&lt;br /&gt;
			return quit()&lt;br /&gt;
		end&lt;br /&gt;
		local ucode&lt;br /&gt;
		for i, t in ipairs(result) do&lt;br /&gt;
			-- Convert requires each input unit to be identical.&lt;br /&gt;
			if i == 1 then&lt;br /&gt;
				ucode = t[2]&lt;br /&gt;
			elseif ucode ~= t[2] then&lt;br /&gt;
				return quit()&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		local item = ucode&lt;br /&gt;
		if item == parms[index] then&lt;br /&gt;
			-- Remove specified output unit if it is the same as the Wikidata unit.&lt;br /&gt;
			-- For example, {{convert|input=P2044|km}} with property &amp;quot;12 km&amp;quot;.&lt;br /&gt;
			table.remove(parms, index)&lt;br /&gt;
		end&lt;br /&gt;
		for i = result.n, 1, -1 do&lt;br /&gt;
			insert2(result[i][1], item)&lt;br /&gt;
			item = sep&lt;br /&gt;
		end&lt;br /&gt;
		return true&lt;br /&gt;
	else&lt;br /&gt;
		if input_from_text(tdata, parms, text, insert2) then&lt;br /&gt;
			return true&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return quit()&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
--- List units and check syntax of definitions ---------------------------------&lt;br /&gt;
--------------------------------------------------------------------------------&lt;br /&gt;
local specifications = {&lt;br /&gt;
	-- seq = sequence in which fields are displayed&lt;br /&gt;
	base = {&lt;br /&gt;
		title = &amp;#039;SI base units&amp;#039;,&lt;br /&gt;
		fields = {&lt;br /&gt;
			symbol = { seq = 2, mandatory = true },&lt;br /&gt;
			name1  = { seq = 3, mandatory = true },&lt;br /&gt;
			name2  = { seq = 4 },&lt;br /&gt;
			link   = { seq = 5 },&lt;br /&gt;
		},&lt;br /&gt;
		noteseq = 6,&lt;br /&gt;
		header = &amp;#039;{| class=&amp;quot;wikitable&amp;quot;\n!si !!symbol !!name1 !!name2 !!link !!note&amp;#039;,&lt;br /&gt;
		item = &amp;#039;|-\n|%s ||%s ||%s ||%s ||%s ||%s&amp;#039;,&lt;br /&gt;
		footer = &amp;#039;|}&amp;#039;,&lt;br /&gt;
	},&lt;br /&gt;
	alias = {&lt;br /&gt;
		title = &amp;#039;Aliases for convert&amp;#039;,&lt;br /&gt;
		fields = {&lt;br /&gt;
			ucode  = { seq = 2, mandatory = true },&lt;br /&gt;
			si     = { seq = 3 },&lt;br /&gt;
		},&lt;br /&gt;
		noteseq = 4,&lt;br /&gt;
		header = &amp;#039;{| class=&amp;quot;wikitable&amp;quot;\n!alias !!ucode !!base !!note&amp;#039;,&lt;br /&gt;
		item = &amp;#039;|-\n|%s ||%s ||%s ||%s&amp;#039;,&lt;br /&gt;
		footer = &amp;#039;|}&amp;#039;,&lt;br /&gt;
	},&lt;br /&gt;
	known = {&lt;br /&gt;
		title = &amp;#039;Units known to convert&amp;#039;,&lt;br /&gt;
		fields = {&lt;br /&gt;
			ucode  = { seq = 2, mandatory = true },&lt;br /&gt;
			label  = { seq = 3, mandatory = true },&lt;br /&gt;
		},&lt;br /&gt;
		noteseq = 4,&lt;br /&gt;
		header = &amp;#039;{| class=&amp;quot;wikitable&amp;quot;\n!qid !!ucode !!label !!note&amp;#039;,&lt;br /&gt;
		item = &amp;#039;|-\n|%s ||%s ||%s ||%s&amp;#039;,&lt;br /&gt;
		footer = &amp;#039;|}&amp;#039;,&lt;br /&gt;
	},&lt;br /&gt;
	unknown = {&lt;br /&gt;
		title = &amp;#039;Units not known to convert&amp;#039;,&lt;br /&gt;
		fields = {&lt;br /&gt;
			_ucode = { seq = 2, mandatory = true },&lt;br /&gt;
			si     = { seq = 3 },&lt;br /&gt;
			name1  = { seq = 4 },&lt;br /&gt;
			name2  = { seq = 5 },&lt;br /&gt;
			link   = { seq = 6 },&lt;br /&gt;
			label  = { seq = 7, mandatory = true },&lt;br /&gt;
		},&lt;br /&gt;
		noteseq = 8,&lt;br /&gt;
		header = &amp;#039;{| class=&amp;quot;wikitable&amp;quot;\n!qid !!_ucode !!base !!name1 !!name2 !!link !!label !!note&amp;#039;,&lt;br /&gt;
		item = &amp;#039;|-\n|%s ||%s ||%s ||%s ||%s ||%s ||%s ||%s&amp;#039;,&lt;br /&gt;
		footer = &amp;#039;|}&amp;#039;,&lt;br /&gt;
	},&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
local function listunits(tdata, ulookup)&lt;br /&gt;
	-- For Module:Convert, make wikitext to list the built-in Wikidata units.&lt;br /&gt;
	-- Return true, wikitext if successful or return false, t where t is an&lt;br /&gt;
	-- error message table. Currently, an error return never occurs.&lt;br /&gt;
	-- The syntax of each unit definition is checked and a note is added if&lt;br /&gt;
	-- a problem is detected.&lt;br /&gt;
	local function safe_cells(t)&lt;br /&gt;
		-- This is not currently needed, but in case definitions ever use wikitext&lt;br /&gt;
		-- like &amp;#039;[[kilogram|kg]]&amp;#039;, escape the text so it works in a table cell.&lt;br /&gt;
		local result = {}&lt;br /&gt;
		for i, v in ipairs(t) do&lt;br /&gt;
			if v:find(&amp;#039;|&amp;#039;, 1, true) then&lt;br /&gt;
				v = v:gsub(&amp;#039;(%[%[[^%[%]]-)|(.-%]%])&amp;#039;, &amp;#039;%1\0%2&amp;#039;)  -- replace pipe in piped link with a zero byte&lt;br /&gt;
				v = v:gsub(&amp;#039;|&amp;#039;, &amp;#039;&amp;amp;#124;&amp;#039;)                        -- escape &amp;#039;|&amp;#039;&lt;br /&gt;
				v = v:gsub(&amp;#039;%z&amp;#039;, &amp;#039;|&amp;#039;)                            -- restore pipe in piped link&lt;br /&gt;
			end&lt;br /&gt;
			result[i] = v:gsub(&amp;#039;{&amp;#039;, &amp;#039;&amp;amp;#123;&amp;#039;)                    -- escape &amp;#039;{&amp;#039;&lt;br /&gt;
		end&lt;br /&gt;
		return unpack(result)&lt;br /&gt;
	end&lt;br /&gt;
	local wdunits = tdata.wikidata_units&lt;br /&gt;
	local speckeys = { &amp;#039;base&amp;#039;, &amp;#039;alias&amp;#039;, &amp;#039;unknown&amp;#039;, &amp;#039;known&amp;#039; }&lt;br /&gt;
	for _, sid in ipairs(speckeys) do&lt;br /&gt;
		specifications[sid].units = Collection.new()&lt;br /&gt;
	end&lt;br /&gt;
	local keys = Collection.new()&lt;br /&gt;
	for k, v in pairs(wdunits) do&lt;br /&gt;
		keys:add(k)&lt;br /&gt;
	end&lt;br /&gt;
	table.sort(keys)&lt;br /&gt;
	local note_count = 0&lt;br /&gt;
	for _, key in ipairs(keys) do&lt;br /&gt;
		local unit = wdunits[key]&lt;br /&gt;
		local ktext, sid&lt;br /&gt;
		if key:match(&amp;#039;^Q%d+$&amp;#039;) then&lt;br /&gt;
			ktext = &amp;#039;[[d:&amp;#039; .. key .. &amp;#039;|&amp;#039; .. key .. &amp;#039;]]&amp;#039;&lt;br /&gt;
			if unit.ucode then&lt;br /&gt;
				sid = &amp;#039;known&amp;#039;&lt;br /&gt;
			else&lt;br /&gt;
				sid = &amp;#039;unknown&amp;#039;&lt;br /&gt;
			end&lt;br /&gt;
		elseif unit.ucode then&lt;br /&gt;
			ktext = key&lt;br /&gt;
			sid = &amp;#039;alias&amp;#039;&lt;br /&gt;
		else&lt;br /&gt;
			ktext = key&lt;br /&gt;
			sid = &amp;#039;base&amp;#039;&lt;br /&gt;
		end&lt;br /&gt;
		local result = { ktext }&lt;br /&gt;
		local spec = specifications[sid]&lt;br /&gt;
		local fields = spec.fields&lt;br /&gt;
		local note = Collection.new()&lt;br /&gt;
		for k, v in pairs(unit) do&lt;br /&gt;
			if fields[k] then&lt;br /&gt;
				local seq = fields[k].seq&lt;br /&gt;
				if result[seq] then&lt;br /&gt;
					note:add(&amp;#039;duplicate &amp;#039; .. k)  -- cannot happen since keys are unique&lt;br /&gt;
				else&lt;br /&gt;
					result[seq] = v&lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				note:add(&amp;#039;invalid &amp;#039; .. k)&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		for k, v in pairs(fields) do&lt;br /&gt;
			local value = result[v.seq]&lt;br /&gt;
			if value then&lt;br /&gt;
				if k == &amp;#039;si&amp;#039; and not wdunits[value] then&lt;br /&gt;
					note:add(&amp;#039;need si &amp;#039; .. value)&lt;br /&gt;
				end&lt;br /&gt;
				if k == &amp;#039;label&amp;#039; then&lt;br /&gt;
					local wdl = mw.wikibase.getLabel(key)&lt;br /&gt;
					if wdl ~= value then&lt;br /&gt;
						note:add(&amp;#039;label changed to &amp;#039; .. tostring(wdl))&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				result[v.seq] = &amp;#039;&amp;#039;&lt;br /&gt;
				if v.mandatory then&lt;br /&gt;
					note:add(&amp;#039;missing &amp;#039; .. k)&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		local text&lt;br /&gt;
		if note.n &amp;gt; 0 then&lt;br /&gt;
			note_count = note_count + 1&lt;br /&gt;
			text = &amp;#039;*&amp;#039; .. note:join(&amp;#039;&amp;lt;br /&amp;gt;&amp;#039;)&lt;br /&gt;
		end&lt;br /&gt;
		result[spec.noteseq] = text or &amp;#039;&amp;#039;&lt;br /&gt;
		spec.units:add(result)&lt;br /&gt;
	end&lt;br /&gt;
	local results = Collection.new()&lt;br /&gt;
	if note_count &amp;gt; 0 then&lt;br /&gt;
		local text = note_count .. (note_count == 1 and &amp;#039; note&amp;#039; or &amp;#039; notes&amp;#039;)&lt;br /&gt;
		results:add(&amp;quot;&amp;#039;&amp;#039;&amp;#039;Search for * to see &amp;quot; .. text .. &amp;quot;&amp;#039;&amp;#039;&amp;#039;\n&amp;quot;)&lt;br /&gt;
	end&lt;br /&gt;
	for _, sid in ipairs(speckeys) do&lt;br /&gt;
		local spec = specifications[sid]&lt;br /&gt;
		results:add(&amp;quot;&amp;#039;&amp;#039;&amp;#039;&amp;quot; .. spec.title .. &amp;quot;&amp;#039;&amp;#039;&amp;#039;&amp;quot;)&lt;br /&gt;
		results:add(spec.header)&lt;br /&gt;
		local fmt = spec.item&lt;br /&gt;
		for _, unit in ipairs(spec.units) do&lt;br /&gt;
			results:add(string.format(fmt, safe_cells(unit)))&lt;br /&gt;
		end&lt;br /&gt;
		results:add(spec.footer)&lt;br /&gt;
	end&lt;br /&gt;
	return true, results:join(&amp;#039;\n&amp;#039;)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return { _adjustparameters = adjustparameters, _listunits = listunits }&lt;/div&gt;</summary>
		<author><name>&gt;Johnuniq</name></author>
	</entry>
</feed>