Jump to content

Module:category tree/poscatboiler/data/lang-specific/jpx

From Wiktionary, the free dictionary

This module handles generating the descriptions and categorization for Japonic category pages of the format "Japonic LABEL" where LABEL can be any text. Examples are Category:Bulgarian conjugation 2.1 verbs and Category:Russian velar-stem neuter-form nouns. This module is part of the poscatboiler system, which is a general framework for generating the descriptions and categorization of category pages.

For more information, see Module:category tree/poscatboiler/data/lang-specific/documentation.

NOTE: If you add a new language-specific module, you must add the language code to the list at the top of Module:category tree/poscatboiler/data/lang-specific in order for the module to be recognized.


local labels = {}
local handlers = {}

local m_str_utils = require("Module:string utilities")

local concat = table.concat
local full_link = require("Module:links").full_link
local insert = table.insert
local Hani_sort = require("Module:Hani-sortkey").makeSortKey
local match = m_str_utils.match
local sort = table.sort
local tag_text = require("Module:script_utilities").tag_text
local ucfirst = m_str_utils.ucfirst

local Hira = require("Module:scripts").getByCode("Hira")
local Jpan = require("Module:scripts").getByCode("Jpan")
local kana_to_romaji = require("Module:Hrkt-translit").tr
local m_numeric = require("Module:ConvertNumeric")

local kana_capture = "([-" .. require("Module:ja/data/range").kana .. "・]+)"
local yomi_data = require("Module:kanjitab/data")

labels["adnominals"] = {
	description = "{{{langname}}} adnominals, or {{ja-r|連%体%詞|れん%たい%し}}, which modify nouns, and do not conjugate or [[predicate#Verb|predicate]].",
	parents = {{name = "{{{langcat}}}", raw = true}},
}

labels["hiragana"] = {
	description = "{{{langname}}} terms with hiragana {{mdash}} {{ja-r|平%仮%名|ひら%が%な}} {{mdash}} forms, sorted by conventional hiragana sequence. The hiragana form is a [[phonetic]] representation of that word. " ..
	"Wiktionary represents {{{langname}}}-language segments in three ways: in normal form (with [[kanji]], if appropriate), in [[hiragana]] " ..
	"form (this differs from kanji form only when the segment contains kanji), and in [[romaji]] form.",
	additional = "''See also'' [[:Category:{{{langname}}} katakana]]",
	toc_template = "categoryTOC-hiragana",
	parents = {
		{name = "{{{langcat}}}", raw = true},
		"Category:Hiragana script characters",
	}
}

labels["historical hiragana"] = {
	description = "{{{langname}}} historical [[hiragana]].",
	additional = "''See also'' [[:Category:{{{langname}}} historical katakana]].",
	toc_template = "categoryTOC-hiragana",
	parents = {
		"hiragana",
		{name = "{{{langcat}}}", raw = true},
		"Category:Hiragana script characters",
	}
}

labels["katakana"] = {
	description = "{{{langname}}} terms with katakana {{mdash}} {{ja-r|片%仮%名|かた%か%な}} {{mdash}} forms, sorted by conventional katakana sequence. Katakana is used primarily for transliterations of foreign words, including old Chinese hanzi not used in [[shinjitai]].",
	additional = "''See also'' [[:Category:{{{langname}}} hiragana]]",
	toc_template = "categoryTOC-katakana",
	parents = {
		{name = "{{{langcat}}}", raw = true},
		"Category:Katakana script characters",
	}
}

labels["historical katakana"] = {
	description = "{{{langname}}} historical [[katakana]].",
	additional = "''See also'' [[:Category:{{{langname}}} historical hiragana]].",
	toc_template = "categoryTOC-katakana",
	parents = {
		"katakana",
		{name = "{{{langcat}}}", raw = true},
		"Category:Katakana script characters",
	}
}

labels["terms spelled with mixed kana"] = {
	description = "{{{langname}}} terms which combine [[hiragana]] and [[katakana]] characters, potentially with [[kanji]] too.",
	parents = {
		{name = "{{{langcat}}}", raw = true},
		"hiragana",
		"katakana",
	},
}

labels["honorifics"] = {
	topright = "{{wp|Honorific speech in Japanese}}",
	description = "{{{langname}}} [[honorific]]s.",
	parents = {{name = "{{{langcat}}}", raw = true}},
}

labels["humble language"] = {
	description = "{{{langname}}} humble terms, or {{ja-r|謙%譲%語|けん%じょう%ご}}, which is a type of honorific speech that lowers the speaker in relation to the listener.",
	parents = "honorifics",
}

labels["respectful language"] = {
	description = "{{{langname}}} respectful terms, or {{ja-r|尊%敬%語|そん%けい%ご}}, which is a type of honorific speech that elevates the listener in relation to the speaker.",
	parents = "honorifics",
}

labels["kanji"] = {
	topright = "{{wp|Kanji}}",
	description = "{{{langname}}} symbols of the Han logographic script, which can represent sounds or convey meanings directly.",
	toc_template = "Hani-categoryTOC",
	umbrella = "Han characters",
	parents = "logograms",
}

labels["kanji by reading"] = {
	description = "{{{langname}}} kanji categorized by reading.",
	parents = {{name = "kanji", sort = "reading"}},
}

labels["makurakotoba"] = {
	topright = "{{wp|Makurakotoba}}",
	description = "{{{langname}}} idioms used in poetry to introduce specific words.",
	parents = {"idioms"},
}

labels["terms by kanji readings"] = {
	description = "{{{langname}}} categories grouped with regard to the readings of the kanji with which they are spelled.",
	parents = {{name = "{{{langcat}}}", raw = true}},
}

labels["terms by reading pattern"] = {
	description = "{{{langname}}} categories with terms grouped by their reading patterns.",
	parents = {{name = "{{{langcat}}}", raw = true}},
}

local function handle_onyomi_list(category, category_type, cat_yomi_type)
	local onyomi, seen = {}, {}
	for _, yomi in pairs(yomi_data) do
		if not seen[yomi] and yomi.onyomi then
			local yomi_catname = yomi[category_type]
			if yomi_catname ~= false then
				local yomi_type = yomi.type
				if yomi_type ~= "on'yomi" and yomi_type ~= cat_yomi_type then
					insert(onyomi, "[[:Category:{{{langname}}} " .. category:gsub("{{{yomi_catname}}}", yomi_catname) .. "]]")
				end
			end
		end
		seen[yomi] = true
	end
	sort(onyomi)
	return onyomi
end

local function add_yomi_category(category, category_type, parent, description)
	for _, yomi in pairs(yomi_data) do
		local yomi_catname = yomi[category_type]
		if yomi_catname ~= false then
			local yomi_type = yomi.type
			local yomi_desc = yomi.link or yomi_catname
			if yomi.description then
				yomi_desc = yomi_desc .. "; " .. yomi.description
			end
			local label = {
				description = description .. " " .. yomi_desc .. ".",
				breadcrumb = yomi_type,
				parents = {{name = parent, sort = yomi_catname}},
			}
			if yomi.onyomi then
				local onyomi = handle_onyomi_list(category, category_type, yomi_type)
				
				label.additional = "Categories of terms with " ..
					(yomi_type == "on'yomi" and "more" or "other") ..
					" specific types of on'yomi readings can be found in the following categories:\n* " .. concat(onyomi, "\n* ")
				
				if yomi_type ~= "on'yomi" then
					insert(label.parents, 1, {
						name = (category:gsub("{{{yomi_catname}}}", yomi_data.on[category_type])),
						sort = yomi_catname
					})
				end
			end
			labels[category:gsub("{{{yomi_catname}}}", yomi_catname)] = label
		end
	end
end

add_yomi_category(
	"terms read with {{{yomi_catname}}}",
	"reading_category",
	"terms by reading pattern",
	"{{{langname}}} terms read with"
)

add_yomi_category(
	"terms spelled with kanji with {{{yomi_catname}}} readings",
	"kanji_category",
	"terms by kanji reading type",
	"{{{langname}}} categories with terms that are spelled with one or more kanji read with"
)

labels["terms with missing yomi"] = {
	description = "{{{langname}}} terms where at least one [[Appendix:Japanese glossary#yomi|yomi]] is missing from {{tl|{{{langcode}}}-kanjitab}}.",
	hidden = true,
	can_be_empty = true,
	parents = {"entry maintenance"},
}

labels["terms with IPA pronunciation missing pitch accent"] = {
	description = "{{{langname}}} terms with pronunciations that do not have a {{w|Japanese pitch accent|pitch accent}} specified.",
	additional = "Pitch accent can be specified in {{tl|{{{langcode}}}-pron}} with the {{code|=acc=}} parameter.",
	hidden = true,
	can_be_empty = true,
	parents = {"entry maintenance"},
}

labels["terms by kanji reading type"] = {
	description = "{{{langname}}} categories with terms grouped with regard to the types of readings of the kanji with which " ..
	"they are spelled; broadly, those of Chinese origin, {{ja-r|音|おん}} readings, and those of non-Chinese origin, {{ja-r|訓|くん}} readings.",
	parents = {{name = "{{{langcat}}}", raw = true}},
}

labels["terms spelled with ateji"] = {
	topright = "{{wp|Ateji}}",
	description = "{{{langname}}} terms containing one or more [[Appendix:Japanese glossary#ateji|ateji]] {{mdash}} {{ja-r|当て字|あてじ}} {{mdash}} which are [[kanji]] used to represent sounds rather than meanings (though meaning may have some influence on which kanji are chosen).",
	parents = {{name = "{{{langcat}}}", raw = true}},
}

labels["terms spelled with daiyōji"] = {
	description = "Japanese terms spelled using [[Appendix:Japanese glossary#daiyouji|daiyōji]], categorized using {{temp|ja-daiyouji}}.",
	parents = {"terms by etymology"},
}

labels["terms spelled with jukujikun"] = {
	description = "{{{langname}}} terms containing one or more [[Appendix:Japanese glossary#jukujikun|jukujikun]] {{mdash}} {{ja-r|熟%字%訓|じゅく%じ%くん}} {{mdash}} which are [[kanji]] used to represent meanings rather than sounds.",
	parents = {{name = "{{{langcat}}}", raw = true}},
}

local function add_grade_categories(grade, desc, wp, only_one, parent, sort)
	local grade_kanji = grade .. " kanji"
	local topright = wp and ("{{wp|%s}}"):format(ucfirst(grade_kanji)) or nil
	
	labels[grade_kanji] = {
		topright = topright,
		description = "{{{langname}}} kanji " .. desc,
		toc_template = "Hani-categoryTOC",
		parents = {{
			name = parent and (parent .. " kanji") or "kanji",
			sort = sort or grade
		}},
	}
	
	labels["terms spelled with " .. grade_kanji] = {
		topright = topright,
		description = "{{{langname}}} terms spelled with " .. (only_one and "at least one " or "") .. "kanji " .. desc,
		parents = {{
			name = parent and ("terms spelled with " .. parent .. " kanji") or "terms by orthographic property",
			sort = sort or grade
		}},
	}
end

for i = 1, 6 do
	local ord = m_numeric.ones_position_ord[i]
	add_grade_categories(
		ord .. " grade",
		"taught in the " .. ord .. " grade of elementary school, as designated by the the official list of {{ja-r|教%育 漢%字|きょう%いく かん%じ|education kanji}}.",
		false,
		false,
		"kyōiku",
		i
	)
end

add_grade_categories(
	"kyōiku",
	"on the official list of {{ja-r|教%育 漢%字|きょう%いく かん%じ|education kanji}}.",
	true,
	false,
	"jōyō"
)

add_grade_categories(
	"secondary school",
	"on the official list of {{ja-r|常%用 漢%字|じょう%よう かん%じ|regular-use characters}} that are generally taught in secondary school.",
	false,
	false,
	"jōyō"
)

add_grade_categories(
	"jōyō",
	"on the official list of {{ja-r|常%用 漢%字|じょう%よう かん%じ|regular-use characters}}.",
	true,
	false
)

add_grade_categories(
	"tōyō",
	"on the official list of {{ja-r|当%用 漢%字|とう%よう かん%じ|general-use characters}}, which was used from 1946{{ndash}}1981 until the publication of the list of {{ja-r|常%用 漢%字|じょう%よう かん%じ|regular-use characters}}.",
	true,
	false
)

add_grade_categories(
	"jinmeiyō",
	"on the official list of {{ja-r|人%名%用 漢%字|じん%めい%-よう かん%じ|kanji for use in personal names}}.",
	true,
	true
)

add_grade_categories(
	"hyōgai",
	"not included on the official list of {{ja-r|常%用 漢%字|じょう%よう かん%じ|regular-use characters}} or {{ja-r|人%名%用 漢%字|じん%めい%-よう かん%じ|kanji for use in personal names}}, known as {{ja-r|表%外 漢%字|ひょう%がい かん%じ}} or {{ja-r|表%外%字|ひょう%がい%じ|unlisted characters}}.",
	true,
	true
)

labels["terms with multiple readings"] = {
	description = "{{{langname}}} terms with multiple pronunciations (hence multiple [[kana]] spellings).",
	parents = {{name = "{{{langcat}}}", raw = true}},
}

labels["kanji readings by number of morae"] = {
	description = "{{{langname}}} categories grouped with regard to the number of morae in their kanji readings.",
	parents = {{name = "{{{langcat}}}", raw = true}},
}

labels["single-kanji terms"] = {
	description = "{{{langname}}} terms written as a single kanji.",
	parents = {"terms by orthographic property", {name = "character counts", sort = " "}},
}

labels["kanji with kun readings missing okurigana designation"] = {
	breadcrumb = "Kanji missing okurigana designation",
	description = "{{{langname}}} kanji entries in which one or more kun readings entered into {{tl|{{{langcode}}}-readings}} is missing a hyphen denoting okurigana.",
	toc_template = "Hani-categoryTOC",
	hidden = true,
	can_be_empty = true,
	parents = {"entry maintenance"},
}

labels["terms by the individual characters in their historical spellings"] = {
	breadcrumb = "Historical",
	description = "{{{langname}}} terms categorized by whether their spellings in the {{w|historical kana orthography}} included certain individual characters.",
	parents = {{name = "terms by their individual characters", sort = " "}},
}

labels["verbs without transitivity"] = {
	description = "{{{langname}}} verbs missing the {{code|=tr=}} parameter from their headword templates.",
	hidden = true,
	can_be_empty = true,
	parents = {"entry maintenance"},
}

labels["yojijukugo"] = {
	topright = "{{wp|Yojijukugo}}",
	description = "{{{langname}}} four-[[kanji]] compound terms, {{ja-r|四%字 熟%語|よ%じ じゅく%ご}}, with idiomatic meanings; typically derived from Classical Chinese, Buddhist scripture or traditional Japanese proverbs.",
	additional = "Compare Chinese {{w|chengyu}} and Korean {{w|sajaseong-eo}}.",
	umbrella = "four-character idioms",
	parents = {"idioms"},
}

-- FIXME: Only works for 0 through 19.
local word_to_number = {}
for k, v in pairs(m_numeric.ones_position) do
	word_to_number[v] = k
end

local periods = {
	historical = true,
	ancient = true,
}

local function get_period_text_and_reading_type_link(period, reading_type)
	if period and not periods[period] then
		return nil
	end
	local period_text = period and period .. " " or nil

	-- Allow periods (historical or ancient) by themselves; they will parse as reading types.
	if not period and periods[reading_type] then
		return nil, reading_type
	end

	local reading_type_link = "[[Appendix:Japanese glossary#" .. reading_type .. "|" .. reading_type .. "]]"
	return period_text, reading_type_link
end

local function get_sc(str)
	return match(str:gsub("[%s%p]+", ""), "[^" .. Hira:getCharacters() .. "]") and Jpan or Hira
end
		
local function get_tagged_reading(reading, lang)
	return tag_text(reading, lang, get_sc(reading))
end

local function get_reading_link(reading, lang, period, link)
	local hist = periods[period]
	reading = reading:gsub("[%.%-%s]+", "")
	return full_link({
		lang = lang,
		sc = get_sc(reading),
		term = link or reading:gsub("・", ""),
		-- If we have okurigana, demarcate furigana.
		alt = reading:gsub("^(.-)・", "<span style=\"border-top:1px solid;position:relative;padding:1px;\">%1<span style=\"position:absolute;top:0;bottom:67%%;right:0%%;border-right:1px solid;\"></span></span>"),
		tr = kana_to_romaji((reading:gsub("・", ".")), lang:getCode(), nil, {keep_dot = true, hist = hist})
			:gsub("^(.-)%.", "<u>%1</u>"),
		pos = reading:find("・", 1, true) and get_tagged_reading((reading:gsub("^.-・", "~")), lang) or nil
	}, "term")
end

local function is_on_subtype(reading_type)
	return reading_type:find(".on$")
end

insert(handlers, function(data)
	local number = data.label:match("^terms with ([1-9]%d*) kanji$")
	if not number then
		return
	end
	return {
		description = "{{{langname}}} terms containing exactly " .. number .. " kanji.",
		breadcrumb = number,
		parents = {{
			name = "character counts",
			sort = ("#%03d"):format(number)
		}}
	}
end)


insert(handlers, function(data)
	local label_pref, kana = data.label:match("^(terms historically spelled with )" .. kana_capture .. "$")
	
	if not kana then
		return
	end
	
	local lang = data.lang
	
	return {
		description = "{{{langname}}} terms spelled with " .. get_reading_link(kana, lang, "historical") .. " in the {{w|historical kana orthography}}.",
		displaytitle = "{{{langname}}} " .. label_pref .. get_tagged_reading(kana, lang),
		breadcrumb = "historical",
		parents = {
			{name = "terms spelled with " .. kana, sort = " "},
			{name = "terms by the individual characters in their historical spellings", sort = lang:makeSortKey(kana)}
		},
	}
end)


insert(handlers, function(data)
	local count, plural = data.label:match("^kanji readings with (.+) mora(e?)$")
	
	-- Make sure 'one' goes with singular and other numbers with plural.
	if not count or (count == "one") ~= (plural == "") then
		return
	end
	
	local num = word_to_number[count]
	if not num then
		return nil
	end
	
	return {
		description = "{{{langname}}} kanji readings containing " .. count .. " mora" .. plural .. ".",
		breadcrumb = num,
		parents = {{name = "kanji readings by number of morae", sort = num}},
	}
end)


insert(handlers, function(data)
	local label_pref, period, reading_type, reading = match(data.label, "^(kanji with ([a-z]-) ?([%a']+) reading )" .. kana_capture .. "$")
	
	if not period then
		return
	end
	
	period = period ~= "" and period or nil
	local period_text, reading_type_link = get_period_text_and_reading_type_link(period, reading_type)
	
	if not reading_type_link then
		return
	end
	
	local lang = data.lang
	
	-- Compute parents.
	local parents, breadcrumb = {}
	if reading:find("・", 1, true) then
		local okurigana = reading:match("・(.*)")
		insert(parents, {
			name = "kanji with " .. (period_text or "") .. reading_type .. " reading " .. reading:match("(.-)・"),
			-- Sort by okurigana, since all coordinate categories will have the same furigana.
			sort = (lang:makeSortKey(okurigana))
		})
		breadcrumb = "~" .. okurigana
	else
		insert(parents, {
			name = "kanji by " .. (period_text or "") .. reading_type .. " reading",
			sort = (lang:makeSortKey(reading))
		})
		breadcrumb = reading
	end
	if is_on_subtype(reading_type) then
		insert(parents, {name = "kanji with " .. (period_text or "") .. "on reading " .. reading, sort = reading_type})
	elseif period_text then
		insert(parents, {name = "kanji with " .. period_text .. "reading " .. reading, sort = reading_type})
	end
	if not period_text then
		insert(parents, {name = "kanji read as " .. reading, sort = reading_type})
	end
	
	return {
		description = "{{{langname}}} [[kanji]] with the " .. (period_text or "") .. reading_type_link .. " reading " ..
			get_reading_link(reading, lang, period or reading_type) .. ".",
		displaytitle = "{{{langname}}} " .. label_pref .. get_tagged_reading(reading, lang),
		breadcrumb = get_tagged_reading(breadcrumb, lang),
		parents = parents,
	}
end)

insert(handlers, function(data)
	local period, reading_type = match(data.label, "^kanji by ([a-z]-) ?([%a']+) reading$")
	
	if not period then
		return
	end
	
	period = period ~= "" and period or nil
	local period_text, reading_type_link = get_period_text_and_reading_type_link(period, reading_type)
	
	if not reading_type_link then
		return nil
	end

	-- Compute parents.
	local parents = {
		is_on_subtype(reading_type) and {name = "kanji by " .. (period_text or "") .. "on reading", sort = reading_type} or
		period_text and {name = "kanji by " .. reading_type .. " reading", sort = period} or
		{name = "kanji by reading", sort = reading_type}
	}
	if period_text then
		insert(parents, {name = "kanji by " .. period_text .. "reading", sort = reading_type})
	end

	-- Compute description.
	local description = "{{{langname}}} [[kanji]] categorized by " .. (period_text or "") .. reading_type_link .. " reading."
	return {
		description = description,
		breadcrumb = (period_text or "") .. reading_type,
		parents = parents,
	}
end)


insert(handlers, function(data)
	local label_pref, reading = match(data.label, "^(kanji read as )" .. kana_capture .. "$")
	
	if not reading then
		return
	end
	
	local args = require("Module:parameters").process(data.args, {
		["histconsol"] = true,
	})
	local lang = data.lang
	
	local parents, breadcrumb = {}
	if reading:find("・", 1, true) then
		local okurigana = reading:match("・(.*)")
		insert(parents, {
			name = "kanji read as " .. reading:match("(.-)・"),
			-- Sort by okurigana, since all coordinate categories will have the same furigana.
			sort = (lang:makeSortKey(okurigana))
		})
		breadcrumb = "~" .. okurigana
	else
		insert(parents, {
			name = "kanji by reading",
			sort = (lang:makeSortKey(reading))
		})
		breadcrumb = reading
	end
	
	local addl
	local period_text
	if args.histconsol then
		period_text = "historical"
		addl = ("This is a [[Wikipedia:Historical kana orthography|historical]] [[Wikipedia:Kanazukai|reading]], now " ..
		"consolidated with the [[Wikipedia:Modern kana usage|modern reading]] of " ..
		get_reading_link(args.histconsol, lang, nil, ("Category:Japanese kanji read as %s"):format(args.histconsol)) .. ".")
	end
	
	return {
		description = "{{{langname}}} [[kanji]] read as " .. get_reading_link(reading, lang, period_text) .. ".",
		additional = addl,
		displaytitle = "{{{langname}}} " .. label_pref .. get_tagged_reading(reading, lang),
		breadcrumb = get_tagged_reading(breadcrumb, lang),
		parents = parents,
	}, true
end)


insert(handlers, function(data)
	local label_pref, reading = match(data.label, "^(terms spelled with kanji read as )" .. kana_capture .. "$")
	
	if not reading then
		return
	end
	
	-- Compute parents.
	local lang = data.lang
	local sort_key = (lang:makeSortKey(reading))
	local mora_count = require("Module:ja").count_morae(reading)
	local mora_count_words = m_numeric.spell_number(tostring(mora_count))
	local parents = {
		{name = "terms by kanji readings", sort = sort_key},
		{name = "kanji readings with " .. mora_count_words .. " mora" .. (mora_count > 1 and "e" or ""), sort = sort_key},
		{name = "kanji read as " .. reading, sort = " "},
	}

	local tagged_reading = get_tagged_reading(reading, lang)
	return {
		description = "{{{langname}}} terms that contain kanji that exhibit a reading of " .. get_reading_link(reading, lang) ..
		" in those terms prior to any sound changes.",
		displaytitle = "{{{langname}}} " .. label_pref .. tagged_reading,
		breadcrumb = tagged_reading,
		parents = parents,
	}
end)


insert(handlers, function(data)
	local kanji, reading = match(data.label, "^terms spelled with (.) read as " .. kana_capture .. "$")
	
	if not kanji then
		return nil
	end
	
	local args = require("Module:parameters").process(data.args, {
		[1] = {list = true},
	})
	local lang = data.lang
	
	if #args[1] == 0 then
		error("For categories of the form \"" .. lang:getCanonicalName() ..
			" terms spelled with KANJI read as READING\", at least one reading type (e.g. <code>kun</code> or <code>on</code>) must be specified using <code>1=</code>, <code>2=</code>, <code>3=</code>, etc.")
	end
	local yomi_types, parents = {}, {}
	for _, yomi, category in ipairs(args[1]) do
		local yomi_data = yomi_data[yomi]
		if not yomi_data then
			error("The yomi type \"" .. yomi .. "\" is not recognized.")
		end
		category = yomi_data.kanji_category
		if not category then
			error("The yomi type \"" .. yomi .. "\" is not valid for this type of category.")
		end
		insert(yomi_types, yomi_data.link)
		insert(parents, {
			name = "terms spelled with kanji with " .. category .. " readings",
			sort = (lang:makeSortKey(reading))
		})
	end
	
	insert(parents, 1, {name = "terms spelled with " .. kanji, sort = (lang:makeSortKey(reading))})
	insert(parents, 2, {name = "terms spelled with kanji read as " .. reading, sort = Hani_sort(kanji)})
	
	yomi_types = (#yomi_types > 1 and "one of " or "") .. "its " ..
		require("Module:table").serialCommaJoin(yomi_types, {conj = "or"}) ..
		" reading" .. (#yomi_types > 1 and "s" or "")
	
	local tagged_kanji = get_tagged_reading(kanji, lang)
	local tagged_reading = get_tagged_reading(reading, lang)
	return {
		description = "{{{langname}}} terms spelled with {{l|{{{langcode}}}|" .. kanji .. "}} with " ..
			yomi_types .. " of " .. get_reading_link(reading, lang) .. ".",
		displaytitle = "{{{langname}}} terms spelled with " .. tagged_kanji .. " read as " .. tagged_reading,
		breadcrumb = "read as " .. tagged_reading,
		parents = parents,
	}, true
end)


insert(handlers, function(data)
	local kanji, daiyoji = match(data.label, "^terms with (.) replaced by daiyōji (.)$")
	
	if not kanji then
		return nil
	end
	
	local args = require("Module:parameters").process(data.args, {
		["sort"] = true,
	})
	local lang = data.lang
	
	if not args.sort then
		error("For categories of the form \"" .. lang:getCanonicalName() ..
			" terms with KANJI replaced by daiyōji DAIYOJI\", the sort key must be specified using sort=")
	end

	local tagged_kanji = get_tagged_reading(kanji, lang)
	local tagged_daiyoji = get_tagged_reading(daiyoji, lang)
	return {
		description = "{{{langname}}} terms with {{l|{{{langcode}}}|" .. kanji .. "}} replaced by [[Appendix:Japanese glossary#daiyouji|daiyōji]] {{l|{{{langcode}}}|" .. daiyoji .. "}}.",
		displaytitle = "{{{langname}}} terms with " .. tagged_kanji .. " replaced by daiyōji " .. tagged_daiyoji,
		breadcrumb = tagged_kanji .. " replaced by daiyōji " .. tagged_daiyoji,
		parents = {{name = "terms spelled with daiyōji", sort = args.sort}},
	}, true
end)

return {LABELS = labels, HANDLERS = handlers}