<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://maruneko.autumns.page/mediawiki/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://maruneko.autumns.page/mediawiki/index.php?action=history&amp;feed=atom&amp;title=Module%3AConvert%2Fwikidata"/>
	<link rel="alternate" type="text/html" href="https://maruneko.autumns.page/mediawiki/index.php?title=Module:Convert/wikidata&amp;action=history"/>
	<updated>2026-04-19T12:46:19Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.41.0</generator>
	<entry>
		<id>https://maruneko.autumns.page/mediawiki/index.php?title=Module:Convert/wikidata&amp;diff=350&amp;oldid=prev</id>
		<title>imported&gt;Autumn: 1 revision imported</title>
		<link rel="alternate" type="text/html" href="https://maruneko.autumns.page/mediawiki/index.php?title=Module:Convert/wikidata&amp;diff=350&amp;oldid=prev"/>
		<updated>2021-03-09T00:04:49Z</updated>

		<summary type="html">&lt;p&gt;1 revision imported&lt;/p&gt;
&lt;table style=&quot;background-color: #fff; color: #202122;&quot; data-mw=&quot;interface&quot;&gt;
				&lt;tr class=&quot;diff-title&quot; lang=&quot;en&quot;&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;← Older revision&lt;/td&gt;
				&lt;td colspan=&quot;1&quot; style=&quot;background-color: #fff; color: #202122; text-align: center;&quot;&gt;Revision as of 19:04, 8 March 2021&lt;/td&gt;
				&lt;/tr&gt;&lt;tr&gt;&lt;td colspan=&quot;2&quot; class=&quot;diff-notice&quot; lang=&quot;en&quot;&gt;&lt;div class=&quot;mw-diff-empty&quot;&gt;(No difference)&lt;/div&gt;
&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;</summary>
		<author><name>imported&gt;Autumn</name></author>
	</entry>
	<entry>
		<id>https://maruneko.autumns.page/mediawiki/index.php?title=Module:Convert/wikidata&amp;diff=349&amp;oldid=prev</id>
		<title>Wikipedia&gt;Johnuniq: update from sandbox per Template talk:Convert#Module version 24</title>
		<link rel="alternate" type="text/html" href="https://maruneko.autumns.page/mediawiki/index.php?title=Module:Convert/wikidata&amp;diff=349&amp;oldid=prev"/>
		<updated>2019-05-04T03:48:23Z</updated>

		<summary type="html">&lt;p&gt;update from sandbox per &lt;a href=&quot;/mediawiki/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 24&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) == 'string' then&lt;br /&gt;
		return text:match('(%S.-)%s*$')&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'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 == 'm' 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 = 'THz'&lt;br /&gt;
		elseif f &amp;gt;= 1e9 then&lt;br /&gt;
			unit = 'GHz'&lt;br /&gt;
		elseif f &amp;gt;= 1e6 then&lt;br /&gt;
			unit = 'MHz'&lt;br /&gt;
		elseif f &amp;gt;= 1e3 then&lt;br /&gt;
			unit = 'kHz'&lt;br /&gt;
		else&lt;br /&gt;
			unit = 'Hz'&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return true, unit or 'Hz'&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 == 'Hz' 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 = 'Mm'&lt;br /&gt;
		elseif w &amp;gt;= 0.9993e3 then&lt;br /&gt;
			unit = 'km'&lt;br /&gt;
		elseif w &amp;gt;= 0.9993 then&lt;br /&gt;
			unit = 'm'&lt;br /&gt;
		elseif w &amp;gt;= 0.9993e-2 then&lt;br /&gt;
			unit = 'cm'&lt;br /&gt;
		elseif w &amp;gt;= 0.9993e-3 then&lt;br /&gt;
			unit = 'mm'&lt;br /&gt;
		else&lt;br /&gt;
			unit = 'um'&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return true, unit or 'm'&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 '-' as the separator.&lt;br /&gt;
	-- frequencyrange = { frequency_unit, '-' },&lt;br /&gt;
	-- wavelengthrange = { wavelength_unit, '-' },&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) ~= 'table' 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 's'&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..'s'&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 = ''&lt;br /&gt;
	utable.defkey = ''&lt;br /&gt;
	utable.linkey = ''&lt;br /&gt;
	utable.bad_mcode = ''&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'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'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'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) == 'table' then&lt;br /&gt;
		for _, q in ipairs(target) do&lt;br /&gt;
			if type(q) == 'table' 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'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'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'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) == 'table' then&lt;br /&gt;
		local statements = (entity.claims or {})[pid]&lt;br /&gt;
		if type(statements) == 'table' then&lt;br /&gt;
			for _, rank in ipairs({ 'preferred', 'normal' }) do&lt;br /&gt;
				for _, statement in ipairs(statements) do&lt;br /&gt;
					if type(statement) == 'table' 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 'P123',&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 == 'quantity' 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) == '+' 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) == 'string' then&lt;br /&gt;
						unit = unit:match('Q%d+$')  -- 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 == 'preferred' 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;amp;nbsp;', ' '):gsub('%s+', ' ')&lt;br /&gt;
	local pos = text:find(' ', 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) == 'ft ' and uid:sub(-3) == ' in' 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), 'in')&lt;br /&gt;
			insert2(value, 'ft')&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('^P%d+$')&lt;br /&gt;
	local sep = ','&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 { 'cvt_no_output' } or { 'cvt_bad_input', 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 = ''  -- 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 = 'SI base units',&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 = '{| class=&amp;quot;wikitable&amp;quot;\n!si !!symbol !!name1 !!name2 !!link !!note',&lt;br /&gt;
		item = '|-\n|%s ||%s ||%s ||%s ||%s ||%s',&lt;br /&gt;
		footer = '|}',&lt;br /&gt;
	},&lt;br /&gt;
	alias = {&lt;br /&gt;
		title = 'Aliases for convert',&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 = '{| class=&amp;quot;wikitable&amp;quot;\n!alias !!ucode !!base !!note',&lt;br /&gt;
		item = '|-\n|%s ||%s ||%s ||%s',&lt;br /&gt;
		footer = '|}',&lt;br /&gt;
	},&lt;br /&gt;
	known = {&lt;br /&gt;
		title = 'Units known to convert',&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 = '{| class=&amp;quot;wikitable&amp;quot;\n!qid !!ucode !!label !!note',&lt;br /&gt;
		item = '|-\n|%s ||%s ||%s ||%s',&lt;br /&gt;
		footer = '|}',&lt;br /&gt;
	},&lt;br /&gt;
	unknown = {&lt;br /&gt;
		title = 'Units not known to convert',&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 = '{| class=&amp;quot;wikitable&amp;quot;\n!qid !!_ucode !!base !!name1 !!name2 !!link !!label !!note',&lt;br /&gt;
		item = '|-\n|%s ||%s ||%s ||%s ||%s ||%s ||%s ||%s',&lt;br /&gt;
		footer = '|}',&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 '[[kilogram|kg]]', 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('|', 1, true) then&lt;br /&gt;
				v = v:gsub('(%[%[[^%[%]]-)|(.-%]%])', '%1\0%2')  -- replace pipe in piped link with a zero byte&lt;br /&gt;
				v = v:gsub('|', '&amp;amp;#124;')                        -- escape '|'&lt;br /&gt;
				v = v:gsub('%z', '|')                            -- restore pipe in piped link&lt;br /&gt;
			end&lt;br /&gt;
			result[i] = v:gsub('{', '&amp;amp;#123;')                    -- escape '{'&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 = { 'base', 'alias', 'unknown', 'known' }&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('^Q%d+$') then&lt;br /&gt;
			ktext = '[[d:' .. key .. '|' .. key .. ']]'&lt;br /&gt;
			if unit.ucode then&lt;br /&gt;
				sid = 'known'&lt;br /&gt;
			else&lt;br /&gt;
				sid = 'unknown'&lt;br /&gt;
			end&lt;br /&gt;
		elseif unit.ucode then&lt;br /&gt;
			ktext = key&lt;br /&gt;
			sid = 'alias'&lt;br /&gt;
		else&lt;br /&gt;
			ktext = key&lt;br /&gt;
			sid = 'base'&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('duplicate ' .. 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('invalid ' .. 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 == 'si' and not wdunits[value] then&lt;br /&gt;
					note:add('need si ' .. value)&lt;br /&gt;
				end&lt;br /&gt;
				if k == 'label' then&lt;br /&gt;
					local wdl = mw.wikibase.label(key)&lt;br /&gt;
					if wdl ~= value then&lt;br /&gt;
						note:add('label changed to ' .. tostring(wdl))&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			else&lt;br /&gt;
				result[v.seq] = ''&lt;br /&gt;
				if v.mandatory then&lt;br /&gt;
					note:add('missing ' .. 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 = '*' .. note:join('&amp;lt;br /&amp;gt;')&lt;br /&gt;
		end&lt;br /&gt;
		result[spec.noteseq] = text or ''&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 ' note' or ' notes')&lt;br /&gt;
		results:add(&amp;quot;'''Search for * to see &amp;quot; .. text .. &amp;quot;'''\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;quot; .. spec.title .. &amp;quot;'''&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('\n')&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return { _adjustparameters = adjustparameters, _listunits = listunits }&lt;/div&gt;</summary>
		<author><name>Wikipedia&gt;Johnuniq</name></author>
	</entry>
</feed>