<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>https://www.thegoonshow.co.uk/wiki/index.php?action=history&amp;feed=atom&amp;title=Module%3ATechnique</id>
	<title>Module:Technique - Revision history</title>
	<link rel="self" type="application/atom+xml" href="https://www.thegoonshow.co.uk/wiki/index.php?action=history&amp;feed=atom&amp;title=Module%3ATechnique"/>
	<link rel="alternate" type="text/html" href="https://www.thegoonshow.co.uk/wiki/index.php?title=Module:Technique&amp;action=history"/>
	<updated>2026-05-14T10:05:31Z</updated>
	<subtitle>Revision history for this page on the wiki</subtitle>
	<generator>MediaWiki 1.38.2</generator>
	<entry>
		<id>https://www.thegoonshow.co.uk/wiki/index.php?title=Module:Technique&amp;diff=24461&amp;oldid=prev</id>
		<title>Kurt: 1 revision imported</title>
		<link rel="alternate" type="text/html" href="https://www.thegoonshow.co.uk/wiki/index.php?title=Module:Technique&amp;diff=24461&amp;oldid=prev"/>
		<updated>2022-10-23T22:34:53Z</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 17:34, 23 October 2022&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>Kurt</name></author>
	</entry>
	<entry>
		<id>https://www.thegoonshow.co.uk/wiki/index.php?title=Module:Technique&amp;diff=24460&amp;oldid=prev</id>
		<title>commons&gt;Marsupium: fix bug in function getIsSimple(): don't produce error if first element in a list of 'default' or 'on' groups is left out like Special:Permalink/532561043</title>
		<link rel="alternate" type="text/html" href="https://www.thegoonshow.co.uk/wiki/index.php?title=Module:Technique&amp;diff=24460&amp;oldid=prev"/>
		<updated>2022-05-20T03:52:44Z</updated>

		<summary type="html">&lt;p&gt;fix bug in function getIsSimple(): don&amp;#039;t produce error if first element in a list of &amp;#039;default&amp;#039; or &amp;#039;on&amp;#039; groups is left out like &lt;a href=&quot;/wiki/index.php?title=Special:PermanentLink/532561043&quot; title=&quot;Special:PermanentLink/532561043&quot;&gt;Special:Permalink/532561043&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;--[=[&lt;br /&gt;
This module is intended to be the engine behind &amp;quot;Template:Technique&amp;quot;.&lt;br /&gt;
It can also be used directly from Lua using the function p._technique().&lt;br /&gt;
&lt;br /&gt;
Setting &amp;quot;compat=yes&amp;quot; makes the functions p.technique() and p._technique()&lt;br /&gt;
behave more like the old &amp;quot;Template:Technique&amp;quot;.&lt;br /&gt;
Differences to the old &amp;quot;Template:Technique&amp;quot; also in compat mode:&lt;br /&gt;
* if following non-fitting parameters are set without their predecessors&lt;br /&gt;
  special cases aren't used (unlike in &amp;quot;Template:Technique&amp;quot; as of 2019-08-11T12)&lt;br /&gt;
* use of [[Module:Linguistic]] with different order of words in&lt;br /&gt;
  function p.noungroup() in gl, vi&lt;br /&gt;
]=]&lt;br /&gt;
&lt;br /&gt;
local p = {}&lt;br /&gt;
&lt;br /&gt;
require('Module:No globals') -- used for debugging purposes as it detects cases of unintended global variables&lt;br /&gt;
local linguistic = require('Module:Linguistic')&lt;br /&gt;
local synonyms = mw.loadData('Module:Technique/synonyms')&lt;br /&gt;
local declension = require('Module:Declension')&lt;br /&gt;
local fallback = require('Module:Fallback')&lt;br /&gt;
&lt;br /&gt;
local function getSingularTerm(term)&lt;br /&gt;
	local plurals = mw.loadData('Module:Technique/WikidataLUT').plurals&lt;br /&gt;
	local singular = nil&lt;br /&gt;
	if plurals[term] then&lt;br /&gt;
		singular = plurals[term]&lt;br /&gt;
	end&lt;br /&gt;
	return singular&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getWDmapping(term, wordtype)&lt;br /&gt;
	term = synonyms[wordtype][term] or term&lt;br /&gt;
	local qid_LUT = mw.loadData('Module:Technique/WikidataLUT')[wordtype]&lt;br /&gt;
	return qid_LUT[term]&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- return table of fallback languages and their existing subpages for the language&lt;br /&gt;
local function findLang(lang)&lt;br /&gt;
	local langList = mw.language.getFallbacksFor(lang)&lt;br /&gt;
	table.insert(langList,1,lang)&lt;br /&gt;
	for i,l in ipairs(langList) do&lt;br /&gt;
		local page = mw.title.new('Technique/' .. l, 'Module')&lt;br /&gt;
		langList[i] = {lang = l, subpage = page.exists and 'Module:Technique/' .. l or nil}&lt;br /&gt;
		--[[page.exist checking could be swapped out from here to reduce&lt;br /&gt;
		its expensive access ]]&lt;br /&gt;
	end&lt;br /&gt;
	return langList&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getDeprecatedCat(term, wordtype)&lt;br /&gt;
	local result = ''&lt;br /&gt;
	local map = getWDmapping(term, wordtype)&lt;br /&gt;
	if map and map.deprecated then&lt;br /&gt;
		local sortTerm = synonyms[wordtype][term] or term&lt;br /&gt;
		local adjectiveMark = (wordtype == 'adjectives') and '!' or ''&lt;br /&gt;
		result = ('[[Category:Pages using Template:Technique with deprecated term|'&lt;br /&gt;
			.. adjectiveMark .. sortTerm .. ']]')&lt;br /&gt;
	end&lt;br /&gt;
	return result&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function makeQIDnoLabelText(term, qid)&lt;br /&gt;
	return '&amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;[[d:' .. qid .. '|' .. term .. ']]&amp;lt;/span&amp;gt;'&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- if the reference item isn't a material, but is a technique and has one&lt;br /&gt;
-- valid product statement then return the qid of the product (for labeling)&lt;br /&gt;
function p.getProductQid(map)&lt;br /&gt;
	local productQid&lt;br /&gt;
	if not map.material and map.process then&lt;br /&gt;
		local productStatements = mw.wikibase.getBestStatements(map.qid, 'P1056')&lt;br /&gt;
		if #productStatements == 1 then&lt;br /&gt;
			productQid = productStatements[1].mainsnak.datavalue.value.id&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return productQid&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getUnsupportedLink(lang, wordtype, label, system)&lt;br /&gt;
	local translationTargetPage&lt;br /&gt;
	if system == 'modules' then&lt;br /&gt;
		if wordtype == 'adjectives' then&lt;br /&gt;
			translationTargetPage = 'Module:Technique/' .. lang&lt;br /&gt;
		else&lt;br /&gt;
			translationTargetPage = 'Module:Technique/WikidataLUT'&lt;br /&gt;
		end&lt;br /&gt;
	else&lt;br /&gt;
		translationTargetPage = 'Template:Technique/' .. lang .. ((wordtype == 'adjectives') and '/adjectives' or '')&lt;br /&gt;
	end&lt;br /&gt;
	local url = ('//commons.wikimedia.org/w/index.php?title='&lt;br /&gt;
		.. translationTargetPage&lt;br /&gt;
		.. '&amp;amp;action=edit')&lt;br /&gt;
	local link = mw.ustring.format(&lt;br /&gt;
		'&amp;lt;span class=&amp;quot;plainlinks&amp;quot;&amp;gt;[%s &amp;lt;span style=&amp;quot;color:red&amp;quot;&amp;gt;%s&amp;lt;/span&amp;gt;]&amp;lt;/span&amp;gt;',&lt;br /&gt;
		url, label)&lt;br /&gt;
	return link&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getUnsupportedCatLink(term, wordtype)&lt;br /&gt;
	local adjectiveMark = (wordtype == 'adjectives') and '!' or ''&lt;br /&gt;
	return '[[Category:Unsupported technique|' .. adjectiveMark .. term .. ']]'&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getUnsupportedString(term, wordtype, lang, label, system, compat)&lt;br /&gt;
	local compatUnsupportedLinkSystem = system&lt;br /&gt;
	if compat == 'yes' then&lt;br /&gt;
		compatUnsupportedLinkSystem = 'templates'&lt;br /&gt;
	end&lt;br /&gt;
	local unsupportedLink = getUnsupportedLink(lang, wordtype, label, compatUnsupportedLinkSystem)&lt;br /&gt;
	local unsupportedCatLink = (lang == 'en') and getUnsupportedCatLink(term, wordtype) or ''&lt;br /&gt;
	return unsupportedLink .. unsupportedCatLink&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- fallback chain: Commons module subpage -&amp;gt; Wikidata label -&amp;gt; those two in next language -&amp;gt; …&lt;br /&gt;
function p.getLabelTranslationFallback(term, wordtype, langList, system, compat, lenient)&lt;br /&gt;
	local labelQid, map, label, usedLang&lt;br /&gt;
	if string.find(term, '^q%d+$') then&lt;br /&gt;
		-- term is a Wikidata Q-ID, so we use it to create the label&lt;br /&gt;
		labelQid = string.upper(term)&lt;br /&gt;
	end&lt;br /&gt;
	local canonizedTerm = synonyms[wordtype][term]&lt;br /&gt;
	-- loop over language fallback list looking for label in the specific language&lt;br /&gt;
	for i,langTable in pairs(langList) do&lt;br /&gt;
		if langTable.subpage and i&amp;gt;1 then&lt;br /&gt;
			local langData = require(langTable.subpage)&lt;br /&gt;
			local termData = langData[wordtype][term] or (canonizedTerm and langData[wordtype][canonizedTerm])&lt;br /&gt;
			label = termData and ((type(termData) == 'string') and termData or termData.default or termData.n)&lt;br /&gt;
			-- TODO: adapt for adjectives&lt;br /&gt;
			if label then&lt;br /&gt;
				usedLang = langTable.lang&lt;br /&gt;
				break -- label found and we are done&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		-- if for given language no noun label was found on Commons look at Wikidata:&lt;br /&gt;
		if wordtype == 'nouns' and not labelQid then&lt;br /&gt;
			-- only run if not already run before&lt;br /&gt;
			map = getWDmapping(term, 'nouns')&lt;br /&gt;
			labelQid = (map and map['qid']) and (p.getProductQid(map) or map['qid']) or '0'&lt;br /&gt;
		end&lt;br /&gt;
		if wordtype == 'nouns' and labelQid ~= 0 then&lt;br /&gt;
			label = mw.wikibase.getLabelByLang(labelQid, langTable.lang)&lt;br /&gt;
			-- gives nil if not found&lt;br /&gt;
			if label then&lt;br /&gt;
				usedLang = langTable.lang&lt;br /&gt;
				break -- label found and we are done&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if label and usedLang ~= langList[1].lang then&lt;br /&gt;
		label = mw.ustring.format('&amp;lt;span lang=&amp;quot;%s&amp;quot;&amp;gt;\'\'%s\'\'&amp;lt;/span&amp;gt;', usedLang, label)&lt;br /&gt;
	end&lt;br /&gt;
	if not label and map and map['qid'] then&lt;br /&gt;
		label = makeQIDnoLabelText(term, map['qid'])&lt;br /&gt;
		return label&lt;br /&gt;
	end&lt;br /&gt;
	if not label or (usedLang ~= langList[1].lang and wordtype == 'adjectives') then&lt;br /&gt;
		if lenient then&lt;br /&gt;
			label = term&lt;br /&gt;
		else&lt;br /&gt;
			label = getUnsupportedString(term, wordtype, langList[1].lang,&lt;br /&gt;
				label or term, system, compat)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	-- now label should always be a string&lt;br /&gt;
	return label&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
function p._wikidataLinkFallback(lang, term, wikitext)&lt;br /&gt;
	if not (mw.ustring.find(wikitext, '%[%[') or mw.ustring.find(wikitext, '//')) then&lt;br /&gt;
		local map&lt;br /&gt;
		if string.find(term, '^q%d+$') then&lt;br /&gt;
			-- term is a Wikidata Q-ID, so we use it to create the link&lt;br /&gt;
			map = {qid = string.upper(term)}&lt;br /&gt;
		else&lt;br /&gt;
			term = synonyms.nouns[term] or term&lt;br /&gt;
			term = getSingularTerm(term) or term&lt;br /&gt;
			map = getWDmapping(term, 'nouns')&lt;br /&gt;
		end&lt;br /&gt;
		if map and map['qid'] then&lt;br /&gt;
			local qids = {map['qid']}&lt;br /&gt;
			if map['altQids'] then&lt;br /&gt;
				for _,qid in ipairs(map['altQids']) do&lt;br /&gt;
					qids[#qids+1] = qid&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			-- use primary language subtag for link creation&lt;br /&gt;
			local lang = mw.text.split(lang, '-', true)[1]&lt;br /&gt;
			local sitelink, interwikiPrefix&lt;br /&gt;
			for _,qid in ipairs(qids) do&lt;br /&gt;
				sitelink = mw.wikibase.getSitelink(qid, lang .. 'wiki')&lt;br /&gt;
				if sitelink then&lt;br /&gt;
					interwikiPrefix = lang .. ':'&lt;br /&gt;
					break&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			if not sitelink then&lt;br /&gt;
				for _,qid in ipairs(qids) do&lt;br /&gt;
					sitelink = mw.wikibase.getSitelink(qid, 'commonswiki')&lt;br /&gt;
					if sitelink then&lt;br /&gt;
						interwikiPrefix = ''&lt;br /&gt;
						break&lt;br /&gt;
					end&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
			if sitelink then&lt;br /&gt;
				wikitext = '[[:' .. interwikiPrefix .. sitelink .. '|' .. wikitext .. ']]'&lt;br /&gt;
			else&lt;br /&gt;
				wikitext = '[[d:' .. map['qid'] .. '|' .. wikitext .. ']]'&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return wikitext&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getAgreement(lang, noun, query, caseletter)&lt;br /&gt;
	local agreement = mw.getCurrentFrame():expandTemplate{&lt;br /&gt;
		title = 'technique/' .. lang,&lt;br /&gt;
		args={noun, query = 'gender'}}&lt;br /&gt;
	-- give a default gender and number agreement for some languages&lt;br /&gt;
	-- that don't specify a default ending&lt;br /&gt;
	-- [[Template:Technique/ca]] doesn't have switches for all terms which means&lt;br /&gt;
	-- translations are given as agreement&lt;br /&gt;
	-- --&amp;gt; also give a default gender and number agreement for&lt;br /&gt;
	-- cases where the agreement doesn't match '^[a-z][a-z]$'&lt;br /&gt;
	local defaultGenderNumber = {&lt;br /&gt;
		ca='m', da='c', fr='m', gl='m', it='m', pl='ms', pt='m', ro='m', scn='m'}&lt;br /&gt;
	if defaultGenderNumber[lang] and not string.find(agreement, '^[a-z][a-z]?$') then&lt;br /&gt;
		agreement = defaultGenderNumber[lang]&lt;br /&gt;
	end&lt;br /&gt;
	agreement = agreement .. (caseletter or '')&lt;br /&gt;
	return agreement&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function termToTemplateWikitext(wordtype, lang, term, query, agreement)&lt;br /&gt;
	local frame = mw.getCurrentFrame()&lt;br /&gt;
	local wikitext&lt;br /&gt;
	if wordtype == 'nouns' then&lt;br /&gt;
		local queryCase = frame:expandTemplate{title='technique/' .. lang, args={'case', query=query}}&lt;br /&gt;
		wikitext = frame:expandTemplate{title='technique/' .. lang, args={term, query=queryCase}}&lt;br /&gt;
	elseif wordtype == 'adjectives' then&lt;br /&gt;
		wikitext = frame:expandTemplate{title='technique/' .. lang .. '/adjectives', args={term, agreement=agreement}}&lt;br /&gt;
	end&lt;br /&gt;
	return wikitext&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getTermWikitextWithTemplates(term, wordtype, lang, query, noun, caseletter, lenient)&lt;br /&gt;
	if term == '' then&lt;br /&gt;
		return ''&lt;br /&gt;
	end&lt;br /&gt;
	-- find out agreement for adjectives:&lt;br /&gt;
	local agreement = noun and getAgreement(lang, noun, query, caseletter)&lt;br /&gt;
	local wikitext = termToTemplateWikitext(wordtype, lang, term, query, agreement)&lt;br /&gt;
	&lt;br /&gt;
	if wikitext == '' then&lt;br /&gt;
		local canonicalTerm = synonyms[wordtype][term]&lt;br /&gt;
		if canonicalTerm then&lt;br /&gt;
			local agreement = getAgreement(lang, canonicalTerm, query, caseletter)&lt;br /&gt;
			wikitext = termToTemplateWikitext(wordtype, lang, canonicalTerm, query, agreement)&lt;br /&gt;
		end&lt;br /&gt;
		if wikitext == '' then&lt;br /&gt;
			local langList = findLang(lang)&lt;br /&gt;
			local lenient = lenient ~= '' and lenient&lt;br /&gt;
			wikitext = p.getLabelTranslationFallback(&lt;br /&gt;
				term, wordtype, langList, 'templates', 'yes', lenient)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if wordtype == 'nouns' then&lt;br /&gt;
		wikitext = p._wikidataLinkFallback(lang, term, wikitext)&lt;br /&gt;
	end&lt;br /&gt;
	wikitext = wikitext .. getDeprecatedCat(term, wordtype)&lt;br /&gt;
	return wikitext&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.wikidataLinkFallback(frame)&lt;br /&gt;
	local args = frame.args&lt;br /&gt;
	local wordtype = args.wordtype or 'nouns'&lt;br /&gt;
	return getTermWikitextWithTemplates(args.term, wordtype, args.lang,&lt;br /&gt;
		args.query, args.noun, args.caseletter, args.lenient)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
local function processAdjective(adj, nounData, langData, langList, case, compat, lenient)&lt;br /&gt;
	if adj == '' then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local adj0 = adj&lt;br /&gt;
	adj = synonyms.adjectives[adj] or adj&lt;br /&gt;
	local adjData = langData.adjectives[adj0] or langData.adjectives[adj]&lt;br /&gt;
	if adj and not langData.adjectives[adj] then&lt;br /&gt;
		adjData = p.getLabelTranslationFallback(adj0, 'adjectives', langList, 'modules', compat, lenient)&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local gender, number, decl&lt;br /&gt;
	-- adjectives with declension&lt;br /&gt;
	if type(adjData) == 'table' then&lt;br /&gt;
		local genderConvert = {m=1, f=2, n=3, c=1}&lt;br /&gt;
		gender = nounData and nounData.gender and genderConvert[nounData.gender]&lt;br /&gt;
		number = nounData and nounData.number&lt;br /&gt;
		decl = langData.declension[case]&lt;br /&gt;
		&lt;br /&gt;
		local parts = adjData.parts&lt;br /&gt;
		for i = 1,#parts do&lt;br /&gt;
			if type(parts[i]) == 'table' then&lt;br /&gt;
				parts[i] = declension.selectAdjectiveForm(parts[i], {number=number, case=decl, gender=gender})&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		adj = table.concat(parts)&lt;br /&gt;
	-- invariable adjectives&lt;br /&gt;
	elseif type(adjData) == 'string' then&lt;br /&gt;
		adj = adjData&lt;br /&gt;
	end&lt;br /&gt;
	adj = adj .. getDeprecatedCat(adj0, 'adjectives')&lt;br /&gt;
	return adj&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function makegroupWithTemplates(noun, adj, lang, case, compat, lenient, beforeMountedGrammarNoun)&lt;br /&gt;
	local frame = mw.getCurrentFrame()&lt;br /&gt;
	if case == 'default' then&lt;br /&gt;
		case = 'basic'&lt;br /&gt;
	end&lt;br /&gt;
	local caseletter = frame:expandTemplate{title='technique/' .. lang, args={&lt;br /&gt;
		'case', query=case}}&lt;br /&gt;
	adj = adj or {}&lt;br /&gt;
	local adjCounter = #adj&lt;br /&gt;
	while adjCounter &amp;gt; 0 do&lt;br /&gt;
		adj[adjCounter] = getTermWikitextWithTemplates(adj[adjCounter], 'adjectives', lang, case, noun, caseletter, lenient)&lt;br /&gt;
		adjCounter = adjCounter - 1&lt;br /&gt;
	end&lt;br /&gt;
	adj = linguistic.conj(adj, lang)&lt;br /&gt;
	local noun = noun and getTermWikitextWithTemplates(noun, 'nouns', lang, case, nil, caseletter, lenient)&lt;br /&gt;
	local gender_on = (case == 'mounted' and beforeMountedGrammarNoun&lt;br /&gt;
		and frame:expandTemplate{title='technique/' .. lang, args={&lt;br /&gt;
		beforeMountedGrammarNoun, query='gender'}})&lt;br /&gt;
	local wikitext = frame:expandTemplate{title='technique/' .. lang, args={&lt;br /&gt;
		case, noun = noun, adj = adj, ['gender on'] = gender_on}}&lt;br /&gt;
	return wikitext&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- turn a adj + noun group into a human-readable string&lt;br /&gt;
local function makegroup(noun, adj, langData, lang, case, compat, lenient, beforeMountedGrammar)&lt;br /&gt;
	local langList = findLang(lang)&lt;br /&gt;
	&lt;br /&gt;
	if noun == '' then noun = nil end&lt;br /&gt;
	noun = synonyms.nouns[noun] or noun&lt;br /&gt;
	local canonicalNoun = noun&lt;br /&gt;
	local nounData = langData.nouns[noun] or noun and p.getLabelTranslationFallback(&lt;br /&gt;
		noun, 'nouns', langList, 'modules', compat, lenient)&lt;br /&gt;
	&lt;br /&gt;
	adj = adj or {}&lt;br /&gt;
	local adjCounter = #adj&lt;br /&gt;
	while adjCounter &amp;gt; 0 do&lt;br /&gt;
		adj[adjCounter] = processAdjective(&lt;br /&gt;
			adj[adjCounter], nounData, langData, langList, case, compat, lenient)&lt;br /&gt;
		adjCounter = adjCounter - 1&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	if not noun and #adj == 0 then&lt;br /&gt;
		return nil&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	adj = linguistic.conj(adj, lang)&lt;br /&gt;
	&lt;br /&gt;
	-- process noun&lt;br /&gt;
	local group = ''&lt;br /&gt;
	if noun then&lt;br /&gt;
		local nounLabel&lt;br /&gt;
		if type(nounData) == 'table' then -- complex languages&lt;br /&gt;
			nounLabel = nounData[langData.declension[case] or 'default']&lt;br /&gt;
				or nounData.default&lt;br /&gt;
				or nounData.n&lt;br /&gt;
			-- if langData doesn't define cases use 'default'&lt;br /&gt;
			-- if desired case isn't available, fall back to 'default' then nominative&lt;br /&gt;
			-- TODO: use nominative/'n' at all (here)?&lt;br /&gt;
			if not nounLabel then&lt;br /&gt;
				-- TODO: cleanup! ((use for adjectives and nouns or neither?))&lt;br /&gt;
				nounLabel = p.getLabelTranslationFallback(noun, 'nouns', langList, 'modules', compat)&lt;br /&gt;
			end&lt;br /&gt;
		elseif type(nounData) == 'string' then -- languages without declension&lt;br /&gt;
			nounLabel = nounData&lt;br /&gt;
		end&lt;br /&gt;
		local outNoun&lt;br /&gt;
		if nounData.link and not mw.ustring.find(nounLabel, '%[%[') then&lt;br /&gt;
		-- TODO: remove the 2nd part! (once not needed any more)&lt;br /&gt;
			outNoun = '[[:' .. nounData.link .. '|' .. nounLabel .. ']]'&lt;br /&gt;
		else&lt;br /&gt;
			outNoun = p._wikidataLinkFallback(lang, canonicalNoun, nounLabel)&lt;br /&gt;
		end&lt;br /&gt;
		outNoun = outNoun .. getDeprecatedCat(canonicalNoun, 'nouns')&lt;br /&gt;
		group = linguistic.noungroup(outNoun, adj, lang)&lt;br /&gt;
	else&lt;br /&gt;
		group = adj .. '[[Category:Pages with incorrect template usage/Technique]]'&lt;br /&gt;
		-- add maintenance category if noun is missing&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- finalize&lt;br /&gt;
	return langData.nomgroup(group, case, beforeMountedGrammar)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function makeQSstatementCore(term, case, compat)&lt;br /&gt;
	-- currently only supports default and &amp;quot;on&amp;quot; cases&lt;br /&gt;
	local qid&lt;br /&gt;
	local map = getWDmapping(term, 'nouns')&lt;br /&gt;
	local core&lt;br /&gt;
	if compat == 'yes' then&lt;br /&gt;
		qid = mw.getCurrentFrame():expandTemplate{title='Technique/WikidataLUT', args={term}}&lt;br /&gt;
	elseif map then&lt;br /&gt;
		qid = map['qid']&lt;br /&gt;
	end&lt;br /&gt;
	if qid and qid ~= '' then&lt;br /&gt;
		local prop&lt;br /&gt;
		if map['material'] or compat == 'yes' then&lt;br /&gt;
			prop = 'P186'&lt;br /&gt;
		elseif map['process'] then&lt;br /&gt;
			prop = 'P2079'&lt;br /&gt;
		end&lt;br /&gt;
		if prop then&lt;br /&gt;
			core = prop .. ',' .. qid&lt;br /&gt;
			if case and case == 'on' then&lt;br /&gt;
				core = core .. ',P518,Q861259'&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return core&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function makeQSstring(args, isSimple)&lt;br /&gt;
	local QScode = ''&lt;br /&gt;
	if not (isSimple and args.caseGroups.default[1]) then&lt;br /&gt;
		return ''&lt;br /&gt;
	end&lt;br /&gt;
	local fragments = {'default', 'on'}&lt;br /&gt;
	local statements = {}&lt;br /&gt;
	local isAllFound = true&lt;br /&gt;
	for _,f in ipairs(fragments) do&lt;br /&gt;
		if args.caseGroups[f][1] and args.caseGroups[f][1].noun then&lt;br /&gt;
			-- only continue if the term is given&lt;br /&gt;
			local case&lt;br /&gt;
			if f == 'on' then&lt;br /&gt;
				case = 'on'&lt;br /&gt;
			end&lt;br /&gt;
			local statement = makeQSstatementCore(args.caseGroups[f][1].noun, case, args.compat)&lt;br /&gt;
			if statement and statement ~= '' then&lt;br /&gt;
				table.insert(statements, statement)&lt;br /&gt;
			else&lt;br /&gt;
				isAllFound = false -- TODO: simply return '' and get rid of this variable??&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	if isAllFound then&lt;br /&gt;
		QScode = '&amp;lt;div style=&amp;quot;display: none;&amp;quot;&amp;gt;medium QS:' .. table.concat(statements, ';') .. '&amp;lt;/div&amp;gt;'&lt;br /&gt;
--		QScode = '&amp;lt;i&amp;gt;medium QS:' .. table.concat(statements, ';') .. '&amp;lt;/i&amp;gt;' -- for debugging&lt;br /&gt;
	end&lt;br /&gt;
	return QScode&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getBeforeMountedGrammarNoun(caseGroups)&lt;br /&gt;
	local cases = {'on', 'over', 'default'}&lt;br /&gt;
	for _,case in ipairs(cases) do&lt;br /&gt;
		if #caseGroups[case] &amp;gt; 0 then&lt;br /&gt;
			return caseGroups[case][#caseGroups[case]].noun&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getBeforeMountedGrammar(noun, langData)&lt;br /&gt;
	local beforeMountedData&lt;br /&gt;
	if noun then&lt;br /&gt;
		beforeMountedData = langData.nouns[noun]&lt;br /&gt;
	end&lt;br /&gt;
	local beforeMountedGender = beforeMountedData and beforeMountedData.gender&lt;br /&gt;
	local genderConvert = {m=1, f=2, n=3, c=1}&lt;br /&gt;
	beforeMountedGender = beforeMountedGender and genderConvert[beforeMountedGender]&lt;br /&gt;
	local beforeMountedNumber = beforeMountedData and beforeMountedData.number&lt;br /&gt;
	local beforeMountedGrammar = {&lt;br /&gt;
		gender = beforeMountedGender,&lt;br /&gt;
		number = beforeMountedNumber}&lt;br /&gt;
	return beforeMountedGrammar&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getIsSimple(caseGroups)&lt;br /&gt;
	local isSimple = false&lt;br /&gt;
	local defaultOnOnly = (#caseGroups.over == 0 and #caseGroups.mounted == 0)&lt;br /&gt;
	local defaultOnOneGroup = (#caseGroups.default &amp;lt;= 1 and #caseGroups.on &amp;lt;=1)&lt;br /&gt;
	local default1NoAdj = (#caseGroups.default == 0 or caseGroups.default[1] and caseGroups.default[1].adj == nil)&lt;br /&gt;
	local on1NoAdj = (#caseGroups.on == 0 or caseGroups.on[1] and caseGroups.on[1].adj == nil)&lt;br /&gt;
	return defaultOnOnly and defaultOnOneGroup and default1NoAdj and on1NoAdj&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getQidToEnglishTermTable(wordtype)&lt;br /&gt;
	local qid_LUT = mw.loadData('Module:Technique/WikidataLUT')[wordtype]&lt;br /&gt;
	local QidToEnglishTermTable = {}&lt;br /&gt;
	for term,termTable in pairs(qid_LUT) do&lt;br /&gt;
		if termTable.qid then&lt;br /&gt;
			QidToEnglishTermTable[termTable.qid] = term&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return QidToEnglishTermTable&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function qidsToEnglishTerms(caseGroups)&lt;br /&gt;
	local qidToNounTable = getQidToEnglishTermTable('nouns')&lt;br /&gt;
	local qidToAdjTable = getQidToEnglishTermTable('adjectives')&lt;br /&gt;
	for case,caseGroup in pairs(caseGroups) do&lt;br /&gt;
		for i,group in pairs(caseGroup) do&lt;br /&gt;
			if group.noun then&lt;br /&gt;
				caseGroups[case][i].noun = qidToNounTable[string.upper(group.noun)] or group.noun&lt;br /&gt;
			end&lt;br /&gt;
			if group.adj then&lt;br /&gt;
				for j,adj in ipairs(group.adj) do&lt;br /&gt;
					caseGroups[case][i].adj[j] = qidToAdjTable[string.upper(adj)] or adj&lt;br /&gt;
				end&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return caseGroups&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function getCaseStrings(caseGroups, langData, lang, system, compat, lenient, beforeMountedGrammar, beforeMountedGrammarNoun)&lt;br /&gt;
	local caseStrings = {}&lt;br /&gt;
	for case, caseData in pairs(caseGroups) do&lt;br /&gt;
		local caseGroupStrings = {}&lt;br /&gt;
		for _,group in pairs(caseData or {}) do&lt;br /&gt;
			-- caseData should always be a table, &amp;quot;or&amp;quot; just for compatibility with noncompliant input&lt;br /&gt;
			local groupString = nil&lt;br /&gt;
			if system == 'modules' then&lt;br /&gt;
				groupString = (&lt;br /&gt;
					makegroup(group.noun, group.adj, langData, lang, case,&lt;br /&gt;
						compat, lenient, beforeMountedGrammar))&lt;br /&gt;
			elseif system == 'templates' then&lt;br /&gt;
				groupString = (&lt;br /&gt;
					makegroupWithTemplates(group.noun, group.adj, lang, case,&lt;br /&gt;
						compat, lenient, beforeMountedGrammarNoun))&lt;br /&gt;
			end&lt;br /&gt;
			caseGroupStrings[#caseGroupStrings+1] = groupString&lt;br /&gt;
		end&lt;br /&gt;
		caseStrings[case] = linguistic.conj(caseGroupStrings, lang)&lt;br /&gt;
	end&lt;br /&gt;
	return caseStrings&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- main function used by the module&lt;br /&gt;
function p._technique(args)&lt;br /&gt;
	local lang = args.lang&lt;br /&gt;
	local caseGroups = qidsToEnglishTerms(args.caseGroups)&lt;br /&gt;
	local isSimple = getIsSimple(caseGroups)&lt;br /&gt;
	&lt;br /&gt;
	local function vOrNil(tab, key)&lt;br /&gt;
		return tab and tab[key]&lt;br /&gt;
	end&lt;br /&gt;
	if isSimple and vOrNil(caseGroups.default[1], 'noun') == 'oil' and vOrNil(caseGroups.on[1], 'noun') == 'canvas' then&lt;br /&gt;
		local QScode = makeQSstring(args, isSimple)&lt;br /&gt;
		return fallback.translatelua({args={'I18n/oil on canvas', lang=lang}}) .. QScode&lt;br /&gt;
	elseif isSimple and vOrNil(caseGroups.default[1], 'noun') == 'oil' and (vOrNil(caseGroups.on[1], 'noun') == 'wood' or vOrNil(caseGroups.on[1], 'noun') == 'panel') then&lt;br /&gt;
		local QScode = makeQSstring(args, isSimple)&lt;br /&gt;
		return fallback.translatelua({args={'I18n/oil on panel', lang=lang}}) .. QScode&lt;br /&gt;
	elseif isSimple and vOrNil(caseGroups.default[1], 'noun') == 'unknown' and not vOrNil(caseGroups.on[1], 'noun') then&lt;br /&gt;
		return mw.getCurrentFrame():expandTemplate{title='unknown', args={'technique'}}&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local beforeMountedGrammarNoun = getBeforeMountedGrammarNoun(caseGroups)&lt;br /&gt;
	&lt;br /&gt;
	local result = nil&lt;br /&gt;
	if args.system == 'templates' then&lt;br /&gt;
		local frame = mw.getCurrentFrame()&lt;br /&gt;
		local templateLang = frame:expandTemplate{title='fallback', args={'Technique', lang}}&lt;br /&gt;
		local caseStrings = getCaseStrings(caseGroups, nil, templateLang,&lt;br /&gt;
			'templates', args.compat, args.lenient, nil, beforeMountedGrammarNoun)&lt;br /&gt;
		result = frame:expandTemplate{title='technique/' .. templateLang, args={&lt;br /&gt;
			'order', A=caseStrings.default, over=caseStrings.over,&lt;br /&gt;
			on=caseStrings.on, mounted=caseStrings.mounted}}&lt;br /&gt;
	else -- system 'modules'&lt;br /&gt;
		local langData&lt;br /&gt;
		local langList = findLang(lang)&lt;br /&gt;
		for _,t in pairs(langList) do&lt;br /&gt;
			if t.subpage then&lt;br /&gt;
				langData = require(t.subpage)&lt;br /&gt;
				break&lt;br /&gt;
			end&lt;br /&gt;
		end&lt;br /&gt;
		local beforeMountedGrammar = getBeforeMountedGrammar(beforeMountedGrammarNoun, langData)&lt;br /&gt;
		local caseStrings = getCaseStrings(caseGroups, langData, lang,&lt;br /&gt;
			'modules', args.compat, args.lenient, beforeMountedGrammar, nil)&lt;br /&gt;
		result = langData.grouporder(caseStrings.default, caseStrings.over, caseStrings.on, caseStrings.mounted)&lt;br /&gt;
		&lt;br /&gt;
		result = mw.text.trim(result)&lt;br /&gt;
		-- maybe useful for compatibility:&lt;br /&gt;
		result = mw.ustring.gsub(result, '%s+', ' ')&lt;br /&gt;
		-- the following should improve word order in some situations with mixed&lt;br /&gt;
		-- RTL and LTR text&lt;br /&gt;
		if mw.language.new(lang):isRTL() then&lt;br /&gt;
			result = string.format('&amp;lt;span dir=&amp;quot;rtl&amp;quot;&amp;gt;%s&amp;lt;/span&amp;gt;', result)&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	local QScode = makeQSstring(args, isSimple)&lt;br /&gt;
	result = result .. QScode&lt;br /&gt;
	&lt;br /&gt;
	if not isSimple then&lt;br /&gt;
		result = result .. '[[Category:Pages with complex technique templates]]'&lt;br /&gt;
	end&lt;br /&gt;
	return result&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
local function stringStartsWith(inString, matchTable)&lt;br /&gt;
	local start&lt;br /&gt;
	local rest = inString&lt;br /&gt;
	for _,m in ipairs(matchTable) do&lt;br /&gt;
		if string.sub(inString, 1, #m) == m then&lt;br /&gt;
			start = m&lt;br /&gt;
			rest = string.sub(inString, #m + 1)&lt;br /&gt;
			break&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	return start, rest&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- provide the keys of a table in sorted order&lt;br /&gt;
local function getSortedKeys(inTable)&lt;br /&gt;
	local sortedKeys = {}&lt;br /&gt;
	for key,_ in pairs(inTable) do&lt;br /&gt;
		table.insert(sortedKeys, key)&lt;br /&gt;
	end&lt;br /&gt;
	table.sort(sortedKeys)&lt;br /&gt;
	return sortedKeys&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
function p.read_input_parameters(templateargs)&lt;br /&gt;
	local templateargs2 = {}&lt;br /&gt;
	for key, value in pairs(templateargs) do&lt;br /&gt;
		if value ~= '' then -- nuke empty strings&lt;br /&gt;
			templateargs2[key] = string.lower(mw.text.trim(value))&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- move keys without explicit case&lt;br /&gt;
	templateargs2.and0 = templateargs2[1]&lt;br /&gt;
	templateargs2.adjand0 = templateargs2.adj or templateargs2.color&lt;br /&gt;
	templateargs2.and1 = templateargs2['and']&lt;br /&gt;
	templateargs2.adjand1 = templateargs2.adjand or templateargs2.colorand&lt;br /&gt;
	templateargs2.on1 = templateargs2.on or templateargs2[2]&lt;br /&gt;
	templateargs2[1] = nil&lt;br /&gt;
	templateargs2.adj = nil&lt;br /&gt;
	templateargs2.color = nil&lt;br /&gt;
	templateargs2['and'] = nil&lt;br /&gt;
	templateargs2.adjand = nil&lt;br /&gt;
	templateargs2.colorand = nil&lt;br /&gt;
	templateargs2.on = nil&lt;br /&gt;
	templateargs2[2] = nil&lt;br /&gt;
	&lt;br /&gt;
	local args2 = {&lt;br /&gt;
		compat = templateargs2.compat,&lt;br /&gt;
		system = templateargs2.system,&lt;br /&gt;
		lang = templateargs2.lang,&lt;br /&gt;
		lenient = templateargs2.lenient,&lt;br /&gt;
		-- a search for &amp;quot;all: insource:/\| *lenient/ insource:technique -intitle:technique&amp;quot;&lt;br /&gt;
		-- finds the templates where this parameter is used&lt;br /&gt;
		caseGroups = {default = {}, over = {}, on = {}, mounted = {}}}&lt;br /&gt;
	for key, value in pairs(templateargs2) do&lt;br /&gt;
		if value == '' then&lt;br /&gt;
			break&lt;br /&gt;
		end&lt;br /&gt;
		value = mw.text.trim(value)&lt;br /&gt;
		local adjPart, rest = stringStartsWith(key, {'adj', 'color'})&lt;br /&gt;
		local casePart, rest = stringStartsWith(rest, {'and', 'on', 'over', 'mounted'})&lt;br /&gt;
		if casePart and rest == nil or rest == '' then rest = 1 end&lt;br /&gt;
		local counter = tonumber(rest)&lt;br /&gt;
		if casePart and type(counter) == 'number' and counter == math.abs(math.floor(counter)) then&lt;br /&gt;
			-- counter is 0 or positive integer&lt;br /&gt;
			if casePart == 'and' then&lt;br /&gt;
				casePart = 'default'&lt;br /&gt;
				counter = counter + 1&lt;br /&gt;
			end&lt;br /&gt;
			local wordtype = 'noun'&lt;br /&gt;
			if adjPart == 'adj' or adjPart == 'color' then&lt;br /&gt;
				wordtype = 'adj'&lt;br /&gt;
				value = mw.text.split(value, ';')&lt;br /&gt;
			end&lt;br /&gt;
			if not args2.caseGroups[casePart][counter] then&lt;br /&gt;
				args2.caseGroups[casePart][counter] = {}&lt;br /&gt;
			end&lt;br /&gt;
			args2.caseGroups[casePart][counter][wordtype] = value&lt;br /&gt;
		end&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	-- TODO: is the following needed? for coping with left out numbers? write test case&lt;br /&gt;
	for _,case in ipairs(args2.caseGroups) do&lt;br /&gt;
		local sortedCaseKeys = getSortedKeys(case)&lt;br /&gt;
		local gaplessCaseGroups = {}&lt;br /&gt;
		for _,key in ipairs(sortedCaseKeys) do&lt;br /&gt;
			table.insert(gaplessCaseGroups, case[key])&lt;br /&gt;
		end&lt;br /&gt;
		args2.caseGroups[case] = gaplessCaseGroups&lt;br /&gt;
	end&lt;br /&gt;
	&lt;br /&gt;
	return args2&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
-- function to be called from template namespace&lt;br /&gt;
function p.technique(frame)&lt;br /&gt;
	local templateargs = frame:getParent().args&lt;br /&gt;
	local args = p.read_input_parameters(templateargs)&lt;br /&gt;
	args.system = frame.args.system&lt;br /&gt;
	args.compat = args.compat or frame.args.compat&lt;br /&gt;
	if not args.lang or args.lang == '' then&lt;br /&gt;
		-- get user's chosen language&lt;br /&gt;
		args.lang = frame:callParserFunction('int', 'lang')&lt;br /&gt;
	end&lt;br /&gt;
	return p._technique(args)&lt;br /&gt;
end&lt;br /&gt;
&lt;br /&gt;
return p&lt;/div&gt;</summary>
		<author><name>commons&gt;Marsupium</name></author>
	</entry>
</feed>