Modul:category tree/poscatboiler/data/terms by etymology

Daripada Wiktionary

Pendokumenan untuk modul ini boleh diciptakan di Modul:category tree/poscatboiler/data/terms by etymology/doc

local labels = {}
local raw_categories = {}
local handlers = {}
local raw_handlers = {}



-----------------------------------------------------------------------------
--                                                                         --
--                                  LABELS                                 --
--                                                                         --
-----------------------------------------------------------------------------


labels["Kata mengikut etimologi"] = {
	description = "Kata bahasa {{{langname}}} yang dikategorikan mengikut etimologinya.",
	umbrella_parents = "Asas",
	parents = {{name = "{{{langcat}}}", raw = true}},
}

labels["penggandaan jenis AABB"] = {
	description = "Kata bahasa {{{langname}}} yang mengalami [[penggandaan]] dengan corak AABB.",
	breadcrumb = "jenis AABB",
	parents = {"penggandaan"},
}

labels["penggandaan apofoni"] = {
	description = "Kata bahasa {{{langname}}} yang mengalami [[penggandaan]] dengan hanya perubahan pada suatu bunyi vokal.",
	breadcrumb = "apofoni",
	parents = {"penggandaan"},
}

labels["pembentukan terbalik"] = {
	description = "Kata bahasa {{{langname}}} yang terbentuk melalui penterbalikan sesuatu bentuk kata yang sepatutnya nalar, membuang sebahagian dari bentuk kata terdahulu.",
	parents = {"kata mengikut etimologi"},
}

labels["paduan"] = {
	description = "Kata bahasa {{{langname}}} yang terbentuk melalui penggabungan kata-kata lain.",
	parents = {"kata mengikut etimologi"},
}

labels["kata pinjaman"] = {
	description = "Kata bahasa {{{langname}}} yang dimasukkan secara langsung daripada bahasa lain.",
	parents = {"kata mengikut etimologi"},
}

labels["katakresis"] = {
	description = "Kata bahasa {{{langname}}} yang diterbit akibat penyalahgunaan atau pemesongan penggunaan kata lain.",
	parents = {"kata mengikut etimologi by etymology"},
}

labels["ciptaan"] = {
	description = "Kata bahasa {{{langname}}} yang dikenalpasti penciptaannya oleh seseorang, sesuatu organisasi atau entity lain yang berkaitan.",
	parents = {" kata yang berpunca dari sumber yang spesifik "},
	umbrella_parents = {name = "kata yang berpunca dari sumber yang spesifik", is_label = true, sort = " "},
}

labels["setara berpasangan"] = {
	description = "Kata dari bahasa {{{langname}}} yang terdiri daripada pasangan kata yang bersambung melalui suatu [[penghubung setara]].",
	parents = {"kata  by etymology"},
}

labels["setara bertiga"] = {
	description = "Kata dalam bahasa {{{langname}}} yang terdiri daripada tiga kata yang bersambung dengan satu atau lebih banyak [[penghubung setara]].",
	parents = {"kata mengikut etimologi"},
}

labels["setara berempat "] = {
	description = " Kata dalam bahasa {{{langname}}} yang terdiri daripada empat kata yang bersambung dengan satu atau lebih banyak [[penghubung setara]].",
	parents = {"kata mengikut etimologi"},
}

labels["setara berlima "] = {
	description = " Kata dalam bahasa {{{langname}}} yang terdiri daripada lima kata yang bersambung dengan satu atau lebih banyak [[penghubung setara]].",
	parents = {"kata mengikut etimologi"},
}

labels["nyahnama"] = {
	description = "kata bahasa {{{langname}}} yang diterbit dari suatu kata nama.",
	parents = {"kata mengikut etimologi"},
}

labels["nyahkerja"] = {
	description = "kata bahasa {{{langname}}} yang diterbit dari suatu kata kerja.",
	parents = {"kata mengikut etimologi"},
}

labels["dublet"] = {
	description = "kata-kata bahasa {{{langname}}} yang dapat dikesan etimologi mereka daripada sumber yang sama seperti istilah lain dalam bahasa yang sama, tetapi melalui susur galur etimologi yang berbeza, dan selalunya mempunyai makna yang berbeza secara sedikit atau ketara.",
	parents = {"kata mengikut etimologi"},
}

labels["bentuk memanjang"] = {
	description = "kata bahasa {{{langname}}} yang satu atau lebih huruf atau bunyi yang diulang untuk penekanan atau memberi kesan pada makna asalnya.",
	parents = {"kata mengikut etimologi"},
}

labels["eponim"] = {
	description = "kata bahasa {{{langname}}} yang diterbit dari nama orang yang sebenar atau fiksi.",
	parents = {"kata mengikut etimologi"},
}

labels["nama dagang generik"] = {
	description = "kata bahasa {{{langname}}} yang berasal dari [[nama dagang]], nama [[jenama]] dan nama syarikat yang telah menjadi [[generik]]; maksudnya di sini, telah digunapakai secara umum dalam pasaran [[vernakular]] yang disasarkan, sehingga nama tersebut juga mewakili jenama saingan lain.",
	parents = {"kata mengikut etimologi", "nama dagang"},
}

labels["kata hantu"] = {
	description = "kata bahasa {{{langname}}} yang asalnya dari kesilapan atau bersumber rekaan, diterbit dalam karya rujukan seolah-olah kata ini benar-benar wujud tetapi ia merupakan hasil ralat tipografi, salah baca, salah tafsir, atau sebagai [[:w:entri rekaan|entri rekaan]], gurauan, atau tipu helah.",
	parents = {"kata mengikut etimologi "},
}

labels["kata haplologi"] = {
	description = "kata bahasa {{{langname}}} yang mengalami proses [[haplologi]]: maka, asal bentuk kata itu mengalami penyusutan atau penyingkiran bunyi-bunyi yang jujukannya berulang.",
	parents = {"kata mengikut etimologi"},
}

labels["terjemahan homofon"] = {
	description = "Kata bahasa {{{langname}}} yang dipinjam dengan memadankan etimon secara fonetik, tanpa mengambil kira sama ada masuk akal atau tidak; bandingkan [[padanan fonosemantik]] dan [[Hobson-Jobson]].",
	parents = {"kata mengikut etimologi "}
}

labels["hibridisme"] = {
	description = "Kata bahasa {{{langname}}} yang terbentuk melalui beberapa unsur asal linguistic yang berlainan.",
	parents = {"kata mengikut etimologi "},
}

labels["kata turunan"] = {
	description = "Kata bahasa {{{langname}}} yang diturunkan dari tahap perkembangan bahasa yang lebih awal.",
	parents = {"kata mengikut etimologi "},
}

labels["internasionalisme"] = {
	description = "Kata pinjaman bahasa {{{langname}}} yang juga wujud dalam banyak bahasa lain dengan etimologi yang sama atau seakan.",
	additional = "Kata ini sepatutnya di sini hanya apabila sumber tepat pinjaman kata sesuatu bahasa tersebut tidak dapat dikenalpasti dengan jelas. Entri ditambah ke kategori ini melalui [[Templat:internasionalisme]]; sila lihat untuk maklumat lanjut.",
	parents = {"kata mengikut etimologi"},
}

labels["dublet sah "] = {
	description = "[[Dublet]] sah dalam bahasa {{{langname}}} – sesuatu dublet sah merupakan satu frasa piawai yang secara umumnya digunakan dalam dokumen sah, prosiding dan lain-lain yang menggunakan dua perkataan yang hampir sinonim.",
	parents = {"setara berpasangan"},
}

labels["triplet sah"] = {
	description = "[[Triplet]] sah dalam bahasa {{{langname}}} – sesuatu triplet sah merupakan satu frasa piawai yang secara umumnya digunakan dalam dokumen sah, prosiding dan lain-lain yang menggunakan tiga perkataan yang hampir sinonim.",
	parents = {"setara bertiga "},
}

labels["merisme"] = {
	description = "[[Merisme]] dalam bahasa {{{langname}}} – kata yang merupakan [[kata setara]] yang bergabung, merupakan sinonim untuk makna keseluruhan.",
	parents = {"setara berpasangan "},
}

labels["metonim"] = {
	description = "Kata bahasa {{{langname}}} yang asalnya melibatkan panggilan terhadap sesuatu atau satu konsep bukan dengan namanya sendiri, tetapi dengan nama sesuatu yang berkait rapat dengan benda atau konsep itu.",
	parents = {"kata mengikut etimologi "},
}

labels["neologisme"] = {
	description = "Kata bahasa {{{langname}}} yang baru-baru ini diakui.",
	parents = {"kata mengikut etimologi"},
}

labels["kata karut"] = {
	description = "kata bahasa {{{langname}}} yang telah dicipta untuk satu kali penggunaan.",
	parents = {"kata mengikut etimologi "},
}

labels["numeronim"] = {
	description = "kata bahasa {{{langname}}} yang berfungsi sebagai nama berasaskan nombor.",
	parents = {"kata mengikut etimologi"},
}

labels["onomatopia"] = {
	description = "kata bahasa {{{langname}}} yang dicipta untuk berbunyi seperti apa yang ia wakili.",
	parents = {"kata mengikut etimologi"},
}

labels["dublet sesecebis"] = {
	description = "kata bahasa {{{langname}}} yang merupakan dublet sesecebis.",
	parents = {"kata mengikut etimologi"},
}

for _, ism_and_langname in ipairs({
	{"Anglisisme", "Inggeris"},
	{"Arabisms", "Arabic"},
	{"Gallicisms", "French"},
	{"Germanisms", "German"},
	{"Hispanisms", "Spanish"},
	{"Italianisms", "Italian"},
	{"Latinisms", "Latin"},
	{"Japonisms", "Japanese"},
}) do
	local ism, langname = unpack(ism_and_langname)
	labels["pseudo-" .. ism] = {
		description = "{{{langname}}} terms that appear to be " .. langname .. ", but are not used or have an unrelated meaning in " .. langname .. " itself.",
		parents = {"pseudo-loans"},
		umbrella_parents = {name = "pseudo-loans", is_label = true, sort = " "},
	}
end

labels["rebracketings"] = {
	description = "{{{langname}}} terms that have interacted with another word in such a way that the boundary between the words has been modified.",
	parents = {"kata mengikut etimologi"}
}

labels["reconstructed terms"] = {
	description = "{{{langname}}} terms that are not directly attested, but have been reconstructed through other evidence.",
	parents = {"kata mengikut etimologi"}
}

labels["setara berpasangan gandaan"] = {
	description = "{{{langname}}} gandaan setara berpasangan.",
	breadcrumb = "gandaan",
	parents = {"setara berpasangan", "penggandaan"},
}

labels["setara bertiga gandaan"] = {
	description = "{{{langname}}} gandaan setara bertiga.",
	breadcrumb = "gandaan",
	parents = {"setara bertiga", "penggandaan"},
}

labels["setara berempat gandaan"] = {
	description = "{{{langname}}} gandaan setara berempat.",
	breadcrumb = "gandaan",
	parents = {"setara berempat", "penggandaan"},
}

labels["setara berlima gandaan"] = {
	description = "{{{langname}}} gandaan setara berlima.",
	breadcrumb = "gandaan",
	parents = {"setara berlima", "penggandaan"},
}

labels["penggandaan"] = {
	description = "{{{langname}}} terms that underwent [[reduplication]], so their origin involved a repetition of roots or stems.",
	parents = {"kata mengikut etimologi"},
}

labels["retronim"] = {
	description = "{{{langname}}} terms that serve as new unique names for older objects or concepts whose previous names became ambiguous.",
	parents = {"kata mengikut etimologi"},
}

labels["akar"] = {
	description = "Basic morphemes from which {{{langname}}} words are formed.",
	parents = {"morfem"},
}

labels["akar mengikut bentuk"] = {
	description = "{{{langname}}} roots categorized by their shape.",
	breadcrumb = "mengikut bentuk ",
	parents = {{name = "akar", sort = "bentuk"}},
}

labels["Sanskritic formations"] = {
	description = "{{{langname}}} terms coined from [[tatsama]] [[word]]s and/or [[affix]]es.",
	parents = {"kata mengikut etimologi", "Perkataan diterbitkan daripada Sanskrit"},
}

labels["sound-symbolic terms"] = {
	description = "{{{langname}}} terms that use {{w|sound symbolism}} to express ideas but which are not necessarily strictly speaking [[onomatopoeic]].",
	parents = {"kata mengikut etimologi"},
}

labels["spelled-out initialisms"] = {
	description = "{{{langname}}} initialisms in which the letter names are spelled out.",
	parents = {"kata mengikut etimologi"},
}

labels["spelling pronunciations"] = {
	description = "{{{langname}}} terms whose pronunciation was historically or presently affected by their spelling.",
	parents = {"kata mengikut etimologi"},
}

labels["spoonerisms"] = {
	description = "{{{langname}}} terms in which the initial sounds of component parts have been exchanged, as in \"crook and nanny\" for \"nook and cranny\".",
	parents = {"kata mengikut etimologi"},
}

labels["taxonomic eponyms"] = {
	description = "{{{langname}}} terms derived from names of real or fictitious people, used for [[taxonomy]].",
	parents = {"eponyms"},
}

labels["terms attributed to a specific source"] = {
	description = "{{{langname}}} terms coined by an identifiable person or deriving from a known work.",
	parents = {"kata mengikut etimologi"},
}

labels["terms containing fossilized case endings"] = {
	description = "{{{langname}}} terms which preserve case morphology which is no longer analyzable within the contemporary grammatical system or which has been entirely lost from the language.",
	parents = {"kata mengikut etimologi"},
}

labels["Perkataan diterbitkan daripada area codes"] = {
	description = "{{{langname}}} terms derived from [[area code]]s.",
	parents = {"kata mengikut etimologi"},
}

labels["Perkataan diterbitkan daripada the shape of letters"] = {
	description = "{{{langname}}} terms derived from the shape of letters. This can include terms derived from the shape of any letter in any alphabet.",
	parents = {"Kata mengikut etimologi"},
}

labels["terms by root"] = {
	description = "{{{langname}}} terms categorized by the root they originate from.",
	parents = {"Kata mengikut etimologi", {name = "roots", sort = " "}},
}

labels["Perkataan diterbitkan daripada fiction"] = {
	description = "{{{langname}}} terms that originate from works of [[fiction]].",
	breadcrumb = "fiction",
	parents = {{name = "terms attributed to a specific source", sort = "fiction"}},
}

for _, data in ipairs {
	{source="Dickensian works", desc="the works of [[w:Charles Dickens|Charles Dickens]]", topic_parent="Charles Dickens"},
	{source="DC Comics", desc="[[w:DC Comics|DC Comics]]"},
	{source="Doraemon", desc="[[w:Fujiko F. Fujio|Fujiko F. Fujio]]'s ''[[w:Doraemon|Doraemon]]''", displaytitle="''Doraemon''"},
	{source="Dragon Ball", desc="[[w:Akira Toriyama|Akira Toriyama]]'s ''[[w:Dragon Ball|Dragon Ball]]''", displaytitle="''Dragon Ball''"},
	{source="Duckburg and Mouseton", desc="[[w:The Walt Disney Company|Disney]]'s [[w:Duck universe|Duckburg]] and [[w:Mickey Mouse universe|Mouseton]] universe",
		topic_parent="Disney"},
	{source="Harry Potter", desc="the ''[[w:Harry Potter|Harry Potter]]'' series", displaytitle="''Harry Potter''",
		topic_parent="Harry Potter"},
	{source="Nineteen Eighty-Four", desc="[[w:George Orwell|George Orwell]]'s ''[[w:Nineteen Eighty-Four|Nineteen Eighty-Four]]''",
		displaytitle="''Nineteen Eighty-Four''"},
	{source="Star Trek", desc="''[[w:Star Trek|Star Trek]]''", displaytitle="''Star Trek''", topic_parent="Star Trek"},
	{source="Star Wars", desc="''[[w:Star Wars|Star Wars]]''", displaytitle="''Star Wars''", topic_parent="Star Wars"},
	{source="The Simpsons", desc="''[[w:The Simpsons|The Simpsons]]''", displaytitle="''The Simpsons''", topic_parent="The Simpsons", sort="Simpsons"},
	{source="Tolkien's legendarium", desc="the [[legendarium]] of [[w:J. R. R. Tolkien|J. R. R. Tolkien]]", topic_parent="J. R. R. Tolkien"},
} do
	local parents = {{name = "Perkataan diterbitkan daripada fiction", sort = data.sort or data.source}}
	local umbrella_parents = {"Subkategori Kata mengikut etimologi mengikut bahasa"}
	if data.topic_parent then
		table.insert(parents, {module = "topic cat", args = {label = data.topic_parent, code = "{{{langcode}}}"}})
		table.insert(umbrella_parents, {module = "topic cat", args = {label = data.topic_parent}})
	end
	labels["Perkataan diterbitkan daripada " .. data.source] = {
		description = "Perkataan bahasa {{{langname}}} yang diterbitkan daripada " .. data.desc .. ".",
		breadcrumb = data.displaytitle or data.source,
		parents = parents,
		umbrella = {
			parents = umbrella_parents,
			displaytitle = data.displaytitle and "Perkataan diterbitkan daripada " .. data.displaytitle .. " mengikut bahasa" or nil,
			breadcrumb = data.displaytitle and "Perkataan diterbitkan daripada " .. data.displaytitle,
		},
		displaytitle = data.displaytitle and "Perkataan bahasa {{{langname}}} diterbitkan daripada " .. data.displaytitle or nil,
	}
end

labels["Perkataan diterbitkan daripada Greek mythology"] = {
	description = "{{{langname}}} terms derived from Greek mythology which have acquired an idiomatic meaning.",
	breadcrumb = "Greek mythology",
	parents = {{name = "terms attributed to a specific source", sort = "Greek mythology"}},
}

labels["Perkataan diterbitkan daripada occupations"] = {
	description = "{{{langname}}} terms derived from names of occupations.",
	parents = {"Kata mengikut etimologi"},
}

labels["Perkataan diterbitkan daripada bahasa lain"] = {
	description = "{{{langname}}} terms that originate from other languages.",
	parents = {"Kata mengikut etimologi"},
}

labels["Perkataan diterbitkan daripada the Bible"] = {
	description = "{{{langname}}} terms that originate from the [[Bible]].",
	breadcrumb = {name = "the Bible", nocap = true},
	parents = {{name = "terms attributed to a specific source", sort = "Bible"}},
}

labels["Perkataan diterbitkan daripada Aesop's Fables"] = {
	description = "{{{langname}}} terms that originate from [[Aesop]]'s Fables.",
	breadcrumb = "Aesop's Fables",
	parents = {{name = "terms attributed to a specific source", sort = "Aesop's Fables"}},
}

labels["Perkataan diterbitkan daripada toponyms"] = {
	description = "{{{langname}}} terms derived from names of real or fictitious places.",
	parents = {"Kata mengikut etimologi"},
}

labels["terms making reference to character shapes"] = {
	description = "{{{langname}}} terms making reference to character shapes.",
	parents = {"Kata mengikut etimologi"},
}

labels["Perkataan diterbitkan daripada sukan"] = {
	description = "{{{langname}}} terms that originate from sports.",
	breadcrumb = "sports",
	parents = {{name = "terms attributed to a specific source", sort = "sports"}},
}

labels["Perkataan diterbitkan daripada baseball"] = {
	description = "{{{langname}}} terms that originate from baseball.",
	breadcrumb = "baseball",
	parents = {{name = "Perkataan diterbitkan daripada sukan", sort = "baseball"}},
}

labels["terms with Indo-Aryan extensions"] = {
	description = "{{{langname}}} terms extended with particular [[Indo-Aryan]] [[pleonastic]] affixes.",
	parents = {"Kata mengikut etimologi"},
}

labels["terms with lemma and non-lemma form etymologies"] = {
	description = "{{{langname}}} terms consisting of both a lemma and non-lemma form, of different origins.",
	breadcrumb = "lemma and non-lemma form",
	parents = {"terms with multiple etymologies"},
}

labels["terms with multiple etymologies"] = {
	description = "{{{langname}}} terms that are derived from multiple origins.",
	parents = {"Kata mengikut etimologi"},
}

labels["terms with multiple lemma etymologies"] = {
	description = "{{{langname}}} lemmas that are derived from multiple origins.",
	breadcrumb = "multiple lemmas",
	parents = {"terms with multiple etymologies"},
}

labels["terms with multiple non-lemma form etymologies"] = {
	description = "{{{langname}}} non-lemma forms that are derived from multiple origins.",
	breadcrumb = "multiple non-lemma forms",
	parents = {"terms with multiple etymologies"},
}

labels["terms with unknown etymologies"] = {
	description = "{{{langname}}} terms whose etymologies have not yet been established.",
	parents = {{name = "Kata mengikut etimologi", sort = "unknown etymology"}},
}

labels["univerbations"] = {
	description = "{{{langname}}} terms that result from the agglutination of two or more words.",
	parents = {"Kata mengikut etimologi"},
}

labels["words derived through metathesis"] = {
	description = "{{{langname}}} words that were created through [[metathesis]] from another word.",
	parents = {{name = "Kata mengikut etimologi", sort = "metathesis"}},
}

-- Add 'umbrella_parents' key if not already present.
for key, data in pairs(labels) do
	-- NOTE: umbrella.parents overrides umbrella_parents if both are given.
	if not data.umbrella_parents then
		data.umbrella_parents = "Subkategori kata mengikut etimologi mengikut bahasa"
	end
end



-----------------------------------------------------------------------------
--                                                                         --
--                              RAW CATEGORIES                             --
--                                                                         --
-----------------------------------------------------------------------------


raw_categories["Subkategori kata mengikut etimologi mengikut bahasa"] = {
	description = "Umbrella categories covering topics related to terms categorized by their etymologies, such as types of compounds or borrowings.",
	additional = "{{{umbrella_meta_msg}}}",
	parents = {
		"Metakategori payung",
		{name = "Kata mengikut etimologi", is_label = true, sort = " "},
	},
}

raw_categories["Subkategori perkataan pinjaman mengikut bahasa"] = {
	description = "Umbrella categories covering topics related to borrowed terms.",
	additional = "{{{umbrella_meta_msg}}}",
	parents = {
		"Metakategori payung",
		{name = "perkataan pinjaman", is_label = true, sort = " "},
		{name = "Subkategori kata mengikut etimologi mengikut bahasa", sort = " "},
	},
}

raw_categories["Subkategori kata turunan mengikut etimologi mengikut bahasa"] = {
	description = "Umbrella categories covering topics related to inherited terms.",
	additional = "{{{umbrella_meta_msg}}}",
	parents = {
		"Metakategori payung",
		{name = "inherited terms", is_label = true, sort = " "},
		{name = "Subkategori kata mengikut etimologi mengikut bahasa", sort = " "},
	},
}

raw_categories["Indo-Aryan extensions"] = {
	description = "Umbrella categories covering terms extended with particular [[Indo-Aryan]] [[pleonastic]] affixes.",
	additional = "{{{umbrella_meta_msg}}}",
	parents = {
		"Metakategori payung",
		{name = "Subkategori kata mengikut etimologi mengikut bahasa", sort = " "},
	},
}

raw_categories["Subkategori etimologi berbilang mengikut bahasa"] = {
	description = "Umbrella categories covering topics related to terms with multiple etymologies.",
	additional = "{{{umbrella_meta_msg}}}",
	parents = {
		"Metakategori payung",
		{name = "Subkategori kata mengikut etimologi mengikut bahasa", sort = " "},
	},
}

raw_categories["Kata dipinjam balik ke dalam bahasa sama"] = {
	description = "Categories with terms in specific languages that were borrowed from a second language that previously borrowed the term from the first language.",
	additional = "A well-known example is {{m+|en|salaryman}}, a term borrowed from Japanese which in turn was borrowed from the English words [[salary]] and [[man]].\n\n{{{umbrella_msg}}}",
	parents = "Subkategori kata mengikut etimologi mengikut bahasa",
}



-----------------------------------------------------------------------------
--                                                                         --
--                                 HANDLERS                                --
--                                                                         --
-----------------------------------------------------------------------------

-----------------------------------------------------------------------------
------------------------------- word handlers -------------------------------
-----------------------------------------------------------------------------

-- Handlers for 'terms derived from the SOURCE word word' must go *BEFORE* the
-- more general 'terms derived from SOURCE' handler.

local function get_source_and_type_desc(source, term_type)
	if source:getCode() == "ine-pro" and term_type:find("^akar?$") then
		return "[[w:Akar Indo-Europah Purba|".. term_type .. " Indo-Eropah Purba]]"
	else
		return "[[w:" .. source:getWikipediaArticle() .. "|" .. source:getCanonicalName() .. "]] " .. term_type
	end
end

-- Handler for e.g. [[:Category:Yola terms derived from the Proto-Indo-European word *h₂el- (grow)]] and
-- [[:Category:Russian terms derived from the Proto-Indo-European word *swé]], and corresponding umbrella
-- categories [[:Category:Terms derived from the Proto-Indo-European word *h₂el- (grow)]] and
-- [[:Category:Terms derived from the Proto-Indo-European word *swé]]. Replaces the former
-- [[Module:category tree/PIE word cat]], [[Module:category tree/word cat]] and [[Template:PIE word cat]].
table.insert(handlers, function(data)
	local source_name, term_type, term_and_id = data.label:match("^Perkataan diterbitkan daripada (.+) untuk perkataan (.+)$")
	if not source_name then
		source_name, term_type, term_and_id = data.label:match("^Perkataan diterbitkan daripada (.+) untuk perkataan (.+)$")
	end
	if not source_name then
		source_name, term_type, term_and_id = data.label:match("^Perkataan diterbitkan daripada (.+) untuk perkataan (.+)$")
	end

	if source_name then
		local term, id = term_and_id:match("^(.+) %((.-)%)$")
		term = term or term_and_id
		local source = require("Module:languages").getByCanonicalName(source_name, true, "allow etym langs")

		local parents = {
			{ name = "Kata mengikut " .. term_type .. " " .. source_name, sort = (source:makeSortKey(term)) }
		}
		local umbrella_parents = {
			{ name = "Perkataan diterbitkan daripada " .. term_type .. " " .. source_name, sort = (source:makeSortKey(term)) }
		}
		if id then
			table.insert(parents, { name = "Perkataan diterbitkan daripada " .. term .. " " .. term_type .. " " .. source_name, sort = " "})
			table.insert(umbrella_parents, { name = "Perkataan diterbitkan daripada " .. term .. " " .. term_type .. " " .. source_name, is_label = true, sort = " "})
		end
		-- Italicize the word/word in the title.
		local function displaytitle(title, lang)
			return require("Module:string").plain_gsub(title, term, require("Module:script utilities").tag_text(term, source, nil, "term"))
		end
		local breadcrumb = require("Module:script utilities").tag_text(term, source, nil, "term") .. (id and " (" .. id .. ")" or "")
		return {
			description = "Perkataan bahasa {{{langname}}} yang akhirnya berasal daripada " .. get_source_and_type_desc(source, term_type) .. " " ..
				require("Module:links").full_link({ term = term, lang = source, gloss = id, id = id }, "term") .. ".",
			displaytitle = displaytitle,
			breadcrumb = breadcrumb,
			parents = parents,
			umbrella = {
				no_by_language = true,
				displaytitle = displaytitle,
				breadcrumb = breadcrumb,
				parents = umbrella_parents,
			}
		}
	end
end)


table.insert(handlers, function(data)
	local word_and_id = data.label:match("^perkataan dimiliki kata (.+)$")
	if word_and_id then
		local word, id = word_and_id:match("^(.+) %((.-)%)$")
		word = word or word_and_id

		-- See if the language is Semitic.
		local fam = data.lang
		local is_semitic = false
		while true do
			if not fam then
				break
			end
			if fam:getCode() == "qfa-not" then
				-- qfa-not is "not a family" and is its own parent
				break
			end
			if fam:getCode() == "sem" then
				is_semitic = true
				break
			end
			fam = fam:getFamily()
		end
		local word_desc = is_semitic and "[[w:Perkataan Semitik|kata]]" or "kata"
		local parents = {{name = "perkataan mengikut kata", sort = word_and_id}}
		local separators = "־ %-"
		local separator_c = "[" .. separators .. "]"
		local not_separator_c = "[^" .. separators .. "]"
		-- remove any leading or trailing separators (e.g. in PIE-style words)
		local word_no_prefix_suffix =
			mw.ustring.gsub(mw.ustring.gsub(word, separator_c .. "$", ""), "^" .. separator_c, "")
		local num_sep = mw.ustring.len(mw.ustring.gsub(word_no_prefix_suffix, not_separator_c, ""))
		local linked_word = data.lang and require("Module:links").full_link({ term = word, lang = data.lang, gloss = id, id = id }, "term") or word
		if num_sep > 0 then
			table.insert(parents, {name = "" .. (num_sep + 1) .. "-letter words", sort = word_and_id})
		end
		-- Italicize the word/word in the title.
		local function displaytitle(title, lang)
			return require("Module:string").plain_gsub(title, word, require("Module:script utilities").tag_text(word, lang, nil, "term"))
		end
		local breadcrumb = require("Module:script utilities").tag_text(word, data.lang, nil, "term") .. (id and " (" .. id .. ")" or "")
		return {
			description = "Perkataan {{{langname}}} dimiliki " .. word_desc .. " " .. linked_word .. ".",
			displaytitle = displaytitle,
			breadcrumb = breadcrumb,
			parents = parents,
			umbrella = false,
		}
	end
end)

table.insert(handlers, function(data)
	local num_letters = data.label:match("^perkataan ([0-9]+) huruf$")
	if num_letters then
		return {
			description = "Perkataan {{{langname}}} dengan " .. num_letters .. " huruf.",
			parents = {{name = "kata", sort = "#" .. num_letters}},
			umbrella_parents = "Subkategori kata mengikut etimologi mengikut bahasa",
		}
	end
end)

table.insert(handlers, function(data)
	local source_name = data.label:match("^perkataan mengikut kata (.+)$")
	if source_name then
		local source = require("Module:languages").getByCanonicalName(source_name, true, "allow etym langs")
		local parents = {"Kata mengikut etimologi"}
		-- In [[:Category:Proto-Indo-Iranian terms by Proto-Indo-Iranian word]],
		-- don't add parent [[:Category:Proto-Indo-Iranian terms derived from Proto-Indo-Iranian]].
		if not data.lang or data.lang:getCode() ~= source:getCode() then
			table.insert(parents, "Perkataan diterbitkan daripada " .. source_name)
		end
		return {
			description = "{{{langname}}} terms categorized by the " .. get_source_and_type_desc(source, "word") .. " they originate from.",
			parents = parents,
			umbrella_parents = "Subkategori kata mengikut etimologi mengikut bahasa",
		}
	end
end)

table.insert(handlers, function(data)
	local word_shape = data.label:match("^perkataan bentuk (.+)$")
	if word_shape then
		local description = "Perkataan {{{langname}}} dengan bentuk ''" .. word_shape .. "''."
		local additional
		if data.lang and data.lang:getCode() == "ine-pro" then
			additional = [=[
* '''e''' stands for the vowel of the word.
* '''C''' stands for any stop or ''s''.
* '''R''' stands for any resonant.
* '''H''' stands for any laryngeal.
* '''M''' stands for ''m'' or ''w'', when followed by a resonant.
* '''s''' stands for ''s'', when next to a stop.]=]
		end
		return {
			description = description,
			additional = additional,
			breadcrumb = word_shape,
parents = {{name = "words by shape", sort = word_shape}},
			umbrella = false,
		}
	end
end)

-----------------------------------------------------------------------------
------------------------------- Root handlers -------------------------------
-----------------------------------------------------------------------------

-- Handlers for 'terms derived from the SOURCE root ROOT' must go *BEFORE* the
-- more general 'terms derived from SOURCE' handler.

local function get_source_and_type_desc(source, term_type)
	if source:getCode() == "ine-pro" and term_type:find("^akar?$") then
		return "[[w:Akar Proto-Indo-Europah|".. term_type .. " Proto-Indo-Eropah]]"
	else
		return "[[w:" .. source:getWikipediaArticle() .. "|" .. source:getCanonicalName() .. "]] " .. term_type
	end
end

-- Handler for e.g. [[:Category:Yola terms derived from the Proto-Indo-European root *h₂el- (grow)]] and
-- [[:Category:Russian terms derived from the Proto-Indo-European word *swé]], and corresponding umbrella
-- categories [[:Category:Terms derived from the Proto-Indo-European root *h₂el- (grow)]] and
-- [[:Category:Terms derived from the Proto-Indo-European word *swé]]. Replaces the former
-- [[Module:category tree/PIE root cat]], [[Module:category tree/root cat]] and [[Template:PIE word cat]].
table.insert(handlers, function(data)
	local source_name, term_type, term_and_id = data.label:match("^perkataan diterbitkan daripada akar (.+) (.+)$")
	if not source_name then
		source_name, term_type, term_and_id = data.label:match("^perkataan diterbitkan daripada kata (.+) (.+)$")
	end
	if not source_name then
		source_name, term_type, term_and_id = data.label:match("^perkataan diterbitkan daripada kata (.+) (.+)$")
	end

	if source_name then
		local term, id = term_and_id:match("^(.+) %((.-)%)$")
		term = term or term_and_id
		local source = require("Module:languages").getByCanonicalName(source_name, true, "allow etym langs")

		local parents = {
			{ name = "perkataan mengikut " .. term_type .. " " .. source_name, sort = (source:makeSortKey(term)) }
		}
		local umbrella_parents = {
			{ name = "Perkataan diterbitkan daripada " .. term_type .. " " .. source_name, sort = (source:makeSortKey(term)) }
		}
		if id then
			table.insert(parents, { name = "perkataan diterbitkan daripada " .. term .. " " .. term_type .. " " .. source_name, sort = " "})
			table.insert(umbrella_parents, { name = "perkataan diterbitkan daripada " .. term .. " " .. term_type .. " " .. source_name, is_label = true, sort = " "})
		end
		-- Italicize the root/word in the title.
		local function displaytitle(title, lang)
			return require("Module:string").plain_gsub(title, term, require("Module:script utilities").tag_text(term, source, nil, "term"))
		end
		local breadcrumb = require("Module:script utilities").tag_text(term, source, nil, "term") .. (id and " (" .. id .. ")" or "")
		return {
			description = "Perkataan {{{langname}}} yang akhirnya berasal daripada " .. get_source_and_type_desc(source, term_type) .. " " ..
				require("Module:links").full_link({ term = term, lang = source, gloss = id, id = id }, "term") .. ".",
			displaytitle = displaytitle,
			breadcrumb = breadcrumb,
			parents = parents,
			umbrella = {
				no_by_language = true,
				displaytitle = displaytitle,
				breadcrumb = breadcrumb,
				parents = umbrella_parents,
			}
		}
	end
end)


table.insert(handlers, function(data)
	local root_and_id = data.label:match("^terms belonging to the root (.+)$")
	if root_and_id then
		local root, id = root_and_id:match("^(.+) %((.-)%)$")
		root = root or root_and_id

		-- See if the language is Semitic.
		local fam = data.lang
		local is_semitic = false
		while true do
			if not fam then
				break
			end
			if fam:getCode() == "qfa-not" then
				-- qfa-not is "not a family" and is its own parent
				break
			end
			if fam:getCode() == "sem" then
				is_semitic = true
				break
			end
			fam = fam:getFamily()
		end
		local root_desc = is_semitic and "[[w:Semitic root|root]]" or "root"
		local parents = {{name = "terms by root", sort = root_and_id}}
		local separators = "־ %-"
		local separator_c = "[" .. separators .. "]"
		local not_separator_c = "[^" .. separators .. "]"
		-- remove any leading or trailing separators (e.g. in PIE-style roots)
		local root_no_prefix_suffix =
			mw.ustring.gsub(mw.ustring.gsub(root, separator_c .. "$", ""), "^" .. separator_c, "")
		local num_sep = mw.ustring.len(mw.ustring.gsub(root_no_prefix_suffix, not_separator_c, ""))
		local linked_root = data.lang and require("Module:links").full_link({ term = root, lang = data.lang, gloss = id, id = id }, "term") or root
		if num_sep > 0 then
			table.insert(parents, {name = "" .. (num_sep + 1) .. "-letter roots", sort = root_and_id})
		end
		-- Italicize the root/word in the title.
		local function displaytitle(title, lang)
			return require("Module:string").plain_gsub(title, root, require("Module:script utilities").tag_text(root, lang, nil, "term"))
		end
		local breadcrumb = require("Module:script utilities").tag_text(root, data.lang, nil, "term") .. (id and " (" .. id .. ")" or "")
		return {
			description = "{{{langname}}} terms that belong to the " .. root_desc .. " " .. linked_root .. ".",
			displaytitle = displaytitle,
			breadcrumb = breadcrumb,
			parents = parents,
			umbrella = false,
		}
	end
end)

table.insert(handlers, function(data)
	local num_letters = data.label:match("^perkataan ([0-9]+) huruf$")
	if num_letters then
		return {
			description = "Perkataan {{{langname}}} dengan " .. num_letters .. " huruf.",
			parents = {{name = "kata", sort = "#" .. num_letters}},
			umbrella_parents = "Subkategori kata mengikut etimologi mengikut bahasa",
		}
	end
end)

table.insert(handlers, function(data)
	local source_name = data.label:match("^terms by (.+) root$")
	if source_name then
		local source = require("Module:languages").getByCanonicalName(source_name, true, "allow etym langs")
		local parents = {"Kata mengikut etimologi"}
		-- In [[:Category:Proto-Indo-Iranian terms by Proto-Indo-Iranian root]],
		-- don't add parent [[:Category:Proto-Indo-Iranian terms derived from Proto-Indo-Iranian]].
		if not data.lang or data.lang:getCode() ~= source:getCode() then
			table.insert(parents, "Perkataan diterbitkan daripada " .. source_name)
		end
		return {
			description = "{{{langname}}} terms categorized by the " .. get_source_and_type_desc(source, "root") .. " they originate from.",
			parents = parents,
			umbrella_parents = "Subkategori kata mengikut etimologi mengikut bahasa",
		}
	end
end)

table.insert(handlers, function(data)
	local root_shape = data.label:match("^(.+)-shape roots$")
	if root_shape then
		local description = "{{{langname}}} roots with the shape ''" .. root_shape .. "''."
		local additional
		if data.lang and data.lang:getCode() == "ine-pro" then
			additional = [=[
* '''e''' stands for the vowel of the root.
* '''C''' stands for any stop or ''s''.
* '''R''' stands for any resonant.
* '''H''' stands for any laryngeal.
* '''M''' stands for ''m'' or ''w'', when followed by a resonant.
* '''s''' stands for ''s'', when next to a stop.]=]
		end
		return {
			description = description,
			additional = additional,
			breadcrumb = root_shape,
			parents = {{name = "roots by shape", sort = root_shape}},
			umbrella = false,
		}
	end
end)


-----------------------------------------------------------------------------
-------------------- Derived/inherited/borrowed handlers --------------------
-----------------------------------------------------------------------------

-- Handler for categories of the form "LANG terms derived from SOURCE", where SOURCE is a language, etymology language
-- or family (e.g. "Indo-European languages"), along with corresponding umbrella categories of the form
-- "Terms derived from SOURCE".
table.insert(handlers, function(data)
	local source_name = data.label:match("^Perkataan diterbitkan daripada (.+)$")
	if source_name then
		-- FIXME, should we allow 'terms derived from taxonomic names' when mul-tax has canonical name
		-- 'taxonomic name'? This is equivalent to what [[Module:category tree/derived cat]] did.
		-- Maybe fix mul-tax instead.
		local source = require("Module:languages").getByCanonicalName(source_name, true,
			"allow etym langs", "allow families")
		local source_desc = source:makeCategoryLink()

		-- Compute description.
		local desc = "{{{langname}}} terms that originate from " .. source_desc .. "."
		local additional
		if source:hasType("family") then
			additional = "This category should, ideally, contain only other categories. Entries can be categorized here, too, when the proper subcategory is unclear. " ..
				"If you know the exact language from which an entry categorized here is derived, please edit its respective entry."
		end

		-- Compute parents.
		local derived_from_variety_of_self = false
		local parent
		local sortkey = source:getDisplayForm()
		if source:hasType("etymology-only") then
			-- By default, `parent` is the source's parent.
			parent = source:getParent()
			-- Check if the source is a variety (or subvariety) of the language.
			if data.lang and source:hasParent(data.lang) then
				derived_from_variety_of_self = true
			end
			-- If the language is the direct parent of the source or the parent is "und", then we use the family of the source as `parent` instead.
			if data.lang and (parent:getCode() == data.lang:getCode() or parent:getCode() == "und") then
				parent = source:getFamily()
			end
		-- Regular language or family.
		else
			local fam = source:getFamily()
			if fam then
				parent = fam
			end
		end
		-- If `parent` does not exist, is the same as `source`, or would be "isolate languages" or "not a family", then we discard it.
		if (not parent) or parent:getCode() == source:getCode() or parent:getCode() == "qfa-iso" or parent:getCode() == "qfa-not" then
			parent = nil
			derived_from_variety_of_self = false
		-- Otherwise, get the display form.
		else
			parent = parent:getDisplayForm()
		end
		parent = parent and "Perkataan diterbitkan daripada " .. parent or "Perkataan diterbitkan daripada bahasa lain"
		local parents = {{name = parent, sort = sortkey}}
		if derived_from_variety_of_self then
			table.insert(parents, "Category:Categories for terms in a language derived from a term in a subvariety of that language")
		end

		-- Compute umbrella parents.
		local umbrella_parents = {
			source:hasType("family") and {name = source:getCategoryName(), raw = true, sort = " "} or
			source:hasType("etymology-only") and {name = "Kategori:" .. source:getCategoryName(), sort = "Perkataan diterbitkan daripada"} or
			{name = source:getCategoryName(), raw = true, sort = "Perkataan diterbitkan daripada"}
		}

		return {
			description = desc,
			additional = additional,
			breadcrumb = source_name,
			parents = parents,
			umbrella = {
				no_by_language = true,
				description = "Categories with terms that originate from " .. source_desc .. ".",
				parents = umbrella_parents,
			},
		}
	end
end)


local function get_source_and_source_desc(source_name)
	local source = require("Module:languages").getByCanonicalName(source_name, true, "allow etym langs", "allow families")
	local source_desc = source:makeCategoryLink()
	if source:hasType("family") then
		source_desc = "unknown " .. source_desc
	end
	return source, source_desc
end


-- Handler for categories of the form "LANG terms inherited/borrowed from SOURCE", where SOURCE is a language,
-- etymology language or family (e.g. "Indo-European languages"). Also handles umbrella categories of the form
-- "Terms inherited/borrowed from SOURCE".
local function inherited_borrowed_handler(etymtype)
	return function(data)
		local source_name = data.label:match("^terms " .. etymtype .. " from (.+)$")
		if source_name then
			local source, source_desc = get_source_and_source_desc(source_name)
			return {
				description = "{{{langname}}} terms " .. etymtype .. " from " .. source_desc .. ".",
				breadcrumb = source_name,
				parents = {
					{ name = etymtype .. " terms", sort = source_name },
					{ name = "Perkataan diterbitkan daripada " .. source_name, sort = " "},
				},
				umbrella = {
					no_by_language = true,
					parents = {
						{ name = "Perkataan diterbitkan daripada " .. source_name, is_label = true, sort = " " },
						etymtype == "inherited" and
							{ name = "Subkategori kata mengikut etimologi mengikut bahasa", sort = source_name }
						-- There are several types of borrowings mixed into the following holding category,
						-- so keep these ones sorted under 'Terms borrowed from SOURCE_NAME' instead of just
						-- 'SOURCE_NAME'.
						or "Subkategori perkataan pinjaman mengikut bahasa",
					}
				},
			}
		end
	end
end

table.insert(handlers, inherited_borrowed_handler("borrowed"))
table.insert(handlers, inherited_borrowed_handler("inherited"))


-----------------------------------------------------------------------------
------------------------ Borrowing subtype handlers -------------------------
-----------------------------------------------------------------------------

local function borrowing_subtype_handler(dest, source_name, parent_cat, desc, categorizing_templates, no_by_language)
	local source, source_desc = get_source_and_source_desc(source_name)
	local extra_templates = {}
	local extra_template_text
	for i, template in ipairs(categorizing_templates) do
		if i > 1 then
			table.insert(extra_templates, ("{{tl|%s|...}}"):format(template))
		end
	end
	if #extra_templates > 0 then
		extra_template_text = (" (or %s, using the same syntax)"):format(
			require("Module:table").serialCommaJoin(extra_templates, {conj = "or"}))
	else
		extra_template_text = ""
	end
	local additional, umbrella_additional
	if dest then
		additional = ("To categorize a term into this category, use {{tl|%s|%s|%s|<var>source_term</var>}}%s, " ..
			"where <code><var>source_term</var></code> is the source-language term that the term in question was " ..
			"borrowed from."):format(categorizing_templates[1], dest:getCode(), source:getCode(), extra_template_text)
	else
		umbrella_additional = ("To categorize a term into a language-specific subcategory, use " ..
			"{{tl|%s|<var>destcode</var>|%s|<var>source_term</var>}}%s, where <code><var>destcode</var></code> is " ..
			"the language code of the language in question (see [[Wiktionary:List of languages]]), and " ..
			"<code><var>source_term</var></code> is the source-language term that the term in question was " ..
			"borrowed from."):format(categorizing_templates[1], source:getCode(), extra_template_text)
	end

	return {
		description = "{{{langname}}} " .. desc:gsub("SOURCE", source_desc),
		additional = additional,
		breadcrumb = source_name,
		parents = {
			{ name = parent_cat, sort = source_name },
			{ name = "terms borrowed from " .. source_name, sort = " " },
		},
		umbrella = {
			no_by_language = no_by_language,
			additional = umbrella_additional,
			parents = {
				{ name = "terms borrowed from " .. source_name, is_label = true, sort = " " },
				"Subkategori perkataan pinjaman mengikut bahasa",
			}
		},
	}
end

-- Specs describing types of borrowings.
-- `from_source_desc` is the English description used in categories of the form "LANGUAGE BORTYPE from SOURCE",
--    e.g. "Arabic semantic loans from English". "SOURCE" in the description is replaced by the source language.
-- `umbrella_desc` is the English description used in categories of the form "LANGUAGE BORTYPE", e.g.
--    "Arabic semantic loans". This is an umbrella category grouping all the source-language-specific categories.
-- `uses_subtype_handler`, if true, means that the handler for "LANGUAGE BORTYPE from SOURCE" categories is
--    implemented by a generic "TYPE borrowings" handler (at the bottom of this section), so we don't need to
--    create a BORTYPE-specific handler.
-- `umbrella_parent`, if given, is the parent category of the umbrella categories of the form "LANGUAGE BORTYPE".
--    By default it is "borrowed terms". Some borrowing types replace this with "terms by etymology". (FIXME:
--    Review whether this is correct.)
-- `label_pattern`, if given, is a Lua pattern that matches the category name minus the language at the beginning.
--    It should have one capture, which is the source language. An example is "^terms partially calqued from (.+)$".
--    If omitted, it is generated from BORTYPE.
-- `no_by_language`, if true, means that the umbrella category grouping borrowings of the appropriate type from a
--    specific source language is named "BORTYPE from SOURCE" in place of "BORTYPE from SOURCE by language"
--    (e.g. "Semantic loans from English" in place of "Semantic loans from English by language").
--
local borrowing_specs = {
	["learned borrowings"] = {
		from_source_desc = "terms that are learned [[loanword]]s from SOURCE, that is, terms that were directly incorporated from SOURCE instead of through normal language contact.",
		umbrella_desc = "terms that are learned [[loanword]]s, that is, terms that were directly incorporated from another language instead of through normal language contact.",
		uses_subtype_handler = true,
		categorizing_templates = {"lbor", "learned borrowing"},
	},
	["semi-learned borrowings"] = {
		from_source_desc = "terms that are [[semi-learned borrowing|semi-learned]] [[loanword]]s from SOURCE, that is, terms borrowed from SOURCE (a [[classical language]]) into the target language (a modern language) and partly reshaped based on later [[sound change]]s or by analogy with [[inherit]]ed terms in the language.",
		umbrella_desc = "terms that are [[semi-learned borrowing|semi-learned]] [[loanword]]s, that is, terms borrowed from a [[classical language]] into a modern language and partly reshaped based on later [[sound change]]s or by analogy with [[inherit]]ed terms in the language.",
		uses_subtype_handler = true,
		categorizing_templates = {"slbor", "semi-learned borrowing"},
	},
	["orthographic borrowings"]	= {
		from_source_desc = "orthographic loans from SOURCE, i.e. terms that were borrowed from SOURCE in their script forms, not their pronunciations.",
		umbrella_desc = "orthographic loans, i.e. terms that were borrowed in their script forms, not their pronunciations.",
		uses_subtype_handler = true,
		categorizing_templates = {"obor", "orthographic borrowing"},
	},
	["unadapted borrowings"] = {
		from_source_desc = "[[loanword]]s from SOURCE that have not been conformed to the morpho-syntactic, phonological and/or phonotactical rules of the target language.",
		umbrella_desc = "[[loanword]]s that have not been conformed to the morpho-syntactic, phonological and/or phonotactical rules of the target language.",
		uses_subtype_handler = true,
		categorizing_templates = {"ubor", "unadapted borrowing"},
	},
	["semantic loans"] = {
		from_source_desc = "[[Appendix:Glossary#semantic loan|semantic loans]] from SOURCE, i.e. terms one or more of whose definitions was borrowed from a term in SOURCE.",
		umbrella_desc = "[[Appendix:Glossary#semantic loan|semantic loans]], i.e. terms one or more of whose definitions was borrowed from a term in another language.",
		umbrella_parent = "Kata mengikut etimologi",
		no_by_language = true,
		categorizing_templates = {"sl", "semantic loan"},
	},
	["partial calques"] = {
		from_source_desc = "terms that were [[Appendix:Glossary#partial calque|partially calqued]] from SOURCE, i.e. terms formed partly by piece-by-piece translations of SOURCE terms and partly by direct borrowing.",
		umbrella_desc = "[[Appendix:Glossary#partial calque|partial calques]], i.e. terms formed partly by piece-by-piece translations of terms from other languages and partly by direct borrowing.",
		umbrella_parent = "terms by etymology",
		label_pattern = "^terms partially calqued from (.+)$",
		no_by_language = true,
		categorizing_templates = {"pcal", "pclq", "partial calque"},
	},
	["calques"] = {
		from_source_desc = "terms that were [[Appendix:Glossary#calque|calqued]] from SOURCE, i.e. terms formed by piece-by-piece translations of SOURCE terms.",
		umbrella_desc = "[[Appendix:Glossary#calque|calques]], i.e. terms formed by piece-by-piece translations of terms from other languages.",
		umbrella_parent = "Kata mengikut etimologi",
		label_pattern = "^terms calqued from (.+)$",
		no_by_language = true,
		categorizing_templates = {"cal", "clq", "calque"},
	},
	["phono-semantic matchings"] = {
		from_source_desc = "[[Appendix:Glossary#phono-semantic matching|phono-semantic matchings]] from SOURCE, i.e. terms that were borrowed by matching the etymon phonetically and semantically.",
		umbrella_desc = "[[Appendix:Glossary#phono-semantic matching|phono-semantic matchings]], i.e. terms that were borrowed by matching the etymon phonetically and semantically.",
		no_by_language = true,
		categorizing_templates = {"psm", "phono-semantic matching"},
	},
	["pseudo-loans"] = {
		from_source_desc = "[[Appendix:Glossary#pseudo-loan|pseudo-loans]] from SOURCE, i.e. terms that appear to be SOURCE, but are not used or have an unrelated meaning in SOURCE itself.",
		umbrella_desc = "[[Appendix:Glossary#pseudo-loan|pseudo-loans]], i.e. terms that appear to be derived from another language, but are not used or have an unrelated meaning in that language itself.",
		categorizing_templates = {"pl", "pseudo-loan"},
	},
}

for bortype, spec in pairs(borrowing_specs) do
	labels[bortype] = {
		description = "{{{langname}}} " .. spec.umbrella_desc,
		parents = {spec.umbrella_parent or "perkataan pinjaman"},
		umbrella_parents = "Subkategori kata mengikut etimologi mengikut bahasa",
	}
	if not spec.uses_subtype_handler then
		-- If the label pattern isn't specifically given, generate it from the `bortype`; but make sure to
		-- escape hyphens in the pattern.
		local label_pattern =
			spec.label_pattern or "^" .. require("Module:pattern utilities").pattern_escape(bortype) .. " from (.+)$"
		table.insert(handlers, function(data)
			local source_name = data.label:match(label_pattern)
			if source_name then
				return borrowing_subtype_handler(data.lang, source_name, bortype, spec.from_source_desc,
					spec.categorizing_templates, spec.no_by_language)
			end
		end)
	end
end

table.insert(handlers, function(data)
	local borrowing_type, source_name = data.label:match("^(.+ borrowings) from (.+)$")
	if borrowing_type then
		local spec = borrowing_specs[borrowing_type]
		return borrowing_subtype_handler(data.lang, source_name, borrowing_type, spec.from_source_desc,
			spec.categorizing_templates, false)
	end
end)


-----------------------------------------------------------------------------
---------------------- Indo-Aryan extension handlers ------------------------
-----------------------------------------------------------------------------

table.insert(handlers, function(data)
	local labelpref, extension = data.label:match("^(terms extended with Indo%-Aryan )(.+)$")
	if extension then
		local lang_inc_ash = require("Module:languages").getByCode("inc-ash")
		local linked_term = require("Module:links").full_link({lang = lang_inc_ash, term = extension}, "term")
		local tagged_term = require("Module:script utilities").tag_text(extension, lang_inc_ash, nil, "term")
		return {
			description = "{{{langname}}} terms extended with the [[Indo-Aryan]] [[pleonastic]] affix " .. linked_term .. ".",
			displaytitle = "{{{langname}}} " .. labelpref .. tagged_term,
			breadcrumb = tagged_term,
			parents = {{name = "terms with Indo-Aryan extensions", sort = extension}},
			umbrella = {
				no_by_language = true,
				parents = "Indo-Aryan extensions",
				displaytitle = "Terms extended with Indo-Aryan " .. tagged_term,
			}
		}
	end
end)


-----------------------------------------------------------------------------
---------------------------- Coined-by handlers -----------------------------
-----------------------------------------------------------------------------

table.insert(handlers, function(data)
	local coiner = data.label:match("^terms coined by (.+)$")
	if coiner then
		-- Sort by last name per request from [[User:Metaknowledge]]
		local last_name = coiner:match(".* ([^ ]+)$")
		return {
			description = "{{{langname}}} terms coined by " .. coiner .. ".",
			breadcrumb = coiner,
			parents = {{
				name = "coinages",
				sort = last_name and last_name .. ", " .. coiner or coiner,
			}},
			umbrella = false,
		}
	end
end)


-----------------------------------------------------------------------------
------------------------ Multiple etymology handlers ------------------------
-----------------------------------------------------------------------------

table.insert(handlers, function(data)
	local pos = data.label:match("^terms with multiple (.+) etymologies$")
	if pos and pos ~= "lemma" and pos ~= "non-lemma form" then
		local plpos = require("Module:string utilities").pluralize(pos)
		local postype = require("Module:headword").pos_lemma_or_nonlemma(plpos, "guess")
		return {
			description = "{{{langname}}} " .. plpos .. " that are derived from multiple origins.",
			umbrella_parents = "Subkategori etimologi berbilang mengikut bahasa",
			breadcrumb = "multiple " .. plpos,
			parents = {{
				name = "terms with multiple " .. postype .. " etymologies",
				sort = pos,
			}},
		}
	end
end)

table.insert(handlers, function(data)
	local pos1, pos2 = data.label:match("^terms with (.+) and (.+) etymologies$")
	if pos1 and pos1 ~= "lemma" and pos2 ~= "non-lemma form" then
		local m_strutil = require("Module:string utilities")
		local m_headword = require("Module:headword")
		local plpos1 = m_strutil.pluralize(pos1)
		local plpos2 = m_strutil.pluralize(pos2)
		local pos1type = m_headword.pos_lemma_or_nonlemma(plpos1, "guess")
		local pos2type = m_headword.pos_lemma_or_nonlemma(plpos2, "guess")
		local a_pos1 = m_strutil.add_indefinite_article(pos1)
		local a_pos2 = m_strutil.add_indefinite_article(pos2)
		
		return {
			description = "{{{langname}}} terms consisting of " .. a_pos1 .." of one origin and " ..
				a_pos2 .. " of a different origin.",
			umbrella_parents = "Subkategori etimologi berbilang mengikut bahasa",
			breadcrumb = pos1 .. " and " .. pos2,
			parents = {{
				name = pos1type == pos2type and "terms with multiple " .. pos1type .. " etymologies" or
					"terms with lemma and non-lemma form etymologies",
				sort = pos1 .. " and " .. pos2,
			}},
		}
	end
end)


-----------------------------------------------------------------------------
--------------------------- Borrowed-back handlers --------------------------
-----------------------------------------------------------------------------

-- Handler for categories of the form e.g. [[:Category:English terms borrowed back into English]]. We need to use a handler
-- because the category's language occurs inside the label itself. For the same reason, the umbrella category has a
-- nonstandard name "Kata dipinjam balik ke dalam bahasa sama", so we handle it as a regular parent and disable the
-- built-in umbrella mechanism.
table.insert(handlers, function(data)
	local right_side_lang = data.label:match("^terms borrowed back into (.+)$")
	if data.lang and right_side_lang == data.lang:getCanonicalName() then
		return {
			description = "{{{langname}}} terms that were borrowed from another language that originally borrowed the term from {{{langname}}}.",
			parents = {"Kata mengikut etimologi", "perkataan pinjaman",
				{name = "Kata dipinjam balik ke dalam bahasa sama", raw = true, sort = "{{{langname}}}"}
			},
			umbrella = false, -- Umbrella has a nonstandard name so we treat it as a raw category
		}
	end
end)



-----------------------------------------------------------------------------
--                                                                         --
--                                RAW HANDLERS                             --
--                                                                         --
-----------------------------------------------------------------------------


-- Handler for umbrella metacategories of the form e.g. [[:Category:Terms derived from Proto-Indo-Iranian roots]]
-- and [[:Category:Terms derived from Proto-Indo-European words]]. Replaces the former
-- [[Module:category tree/PIE root cat]], [[Module:category tree/root cat]] and [[Template:PIE word cat]].
table.insert(raw_handlers, function(data)
	local source_name, terms_type = data.category:match("^Perkataan diterbitkan daripada akar (.+)$")
	if not source_name then
		source_name, terms_type = data.category:match("^Perkataan diterbitkan daripada kata (.+)$")
	end
	if not source_name then
		source_name, terms_type = data.category:match("^Perkataan diterbitkan daripada perkataan (.+)$")
	end
	if source_name then
		local source = require("Module:languages").getByCanonicalName(source_name, true, "allow etym langs")

		return {
			description = "Umbrella categories covering terms derived from particular " .. get_source_and_type_desc(source, terms_type) .. ".",
			additional = "{{{umbrella_meta_msg}}}",
			parents = {
				"Metakategori payung",
				{ name = terms_type == "roots" and "roots" or "lemmas", is_label = true, lang = source:getCode(), sort = " " },
				{ name = "Perkataan diterbitkan daripada " .. source_name, is_label = true, sort = " " .. terms_type },
			},
		}
	end
end)

return {LABELS = labels, RAW_CATEGORIES = raw_categories, HANDLERS = handlers, RAW_HANDLERS = raw_handlers}