Bước tới nội dung

Mô đun:category tree/lang/jpx

Từ điển mở Wiktionary

Mô đun này xử lý việc tạo ra các mô tả và phân loại cho các trang thể loại ngữ hệ Nhật Bản-Lưu Cầu theo định dạng "LABEL ngữ hệ Nhật Bản-Lưu Cầu" trong đó LABEL có thể là bất kỳ văn bản nào. Ví dụ là Thể loại:Chia động từ 2.1 tiếng BulgariaThể loại:Danh từ trung tính gốc vòm mềm tiếng Nga. Mô đun này là một phần của hệ thống cây thể loại, là một khuôn khổ chung để tạo ra mô tả và phân loại các trang thể loại.

Để biết thêm thông tin, hãy xem Module:category tree/lang/tài liệu.

LƯU Ý: Nếu bạn thêm một mô đun ngôn ngữ cụ thể mới, bạn phải thêm mã ngôn ngữ vào danh sách ở đầu Module:category tree/lang để mô đun được nhận dạng.

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["Danh tính từ"] = {
	description = "{{{langname}}} adnominals, or {{ja-r|連%体%詞|れん%たい%し}}, which modify nouns, and do not conjugate or [[predicate#Verb|predicate]].",
	parents = {{name = "{{{langcat}}}", raw = true}},
}

labels["Ký tự 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 = "''Xem thêm'' [[:Thể loại:Ký tự katakana {{{langname}}}]]",
	toc_template = "categoryTOC-hiragana",
	parents = {
		{name = "{{{langcat}}}", raw = true},
		"Category:Ký tự chữ Hiragana",
	}
}

labels["Ký tự hiragana lịch sử"] = {
	description = "{{{langname}}} historical [[hiragana]].",
	additional = "''See also'' [[:Category:{{{langname}}} historical katakana]].",
	toc_template = "categoryTOC-hiragana",
	parents = {
		"Ký tự hiragana",
		{name = "{{{langcat}}}", raw = true},
		"Category:Ký tự chữ Hiragana",
	}
}

labels["Ký tự 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 = "''Xem thêm'' [[:Thể loại:Ký tự hiragana {{{langname}}}]]",
	toc_template = "categoryTOC-katakana",
	parents = {
		{name = "{{{langcat}}}", raw = true},
		"Category:Ký tự chữ Katakana",
	}
}

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["Từ đánh vần với kana hỗn hợp"] = {
	description = "{{{langname}}} terms which combine [[hiragana]] and [[katakana]] characters, potentially with [[kanji]] too.",
	parents = {
		{name = "{{{langcat}}}", raw = true},
		"Ký tự hiragana",
		"Ký tự katakana",
	},
}

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|枕詞|lang=ja}}",
	description = "{{{langname}}} idioms used in poetry to introduce specific words.",
	parents = {"Thành ngữ"},
}

labels["Từ theo âm đọc kanji"] = {
	description = "{{{langname}}} categories grouped with regard to the readings of the kanji with which they are spelled.",
	parents = {{name = "{{{langcat}}}", raw = true}},
}

labels["Từ theo loại âm đọc"] = {
	description = "{{{langname}}} categories with terms grouped by their reading patterns.",
	parents = {{name = "{{{langcat}}}", raw = true}},
}

labels["Mục từ theo số ký tự kanji"] = {
	description = "{{{langname}}} terms categorized by number of kanji.",
	parents = {"Thuộc tính chính tả"},
}

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, "[[:Thể loại:{{{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(
	"Từ có âm đọc {{{yomi_catname}}}",
	"reading_category",
	"Từ theo loại âm đọc",
	"{{{langname}}} terms read with"
)

add_yomi_category(
	"Từ đánh vần với kanji có âm đọc {{{yomi_catname}}}",
	"kanji_category",
	"Từ đánh vần với kanji theo loại âm đọc",
	"{{{langname}}} categories with terms that are spelled with one or more kanji read with"
)

labels["Từ không có 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 = {"Bảo trì mục từ"},
}

labels["Mục từ có cách phát âm IPA với trọng âm âm vực"] = {
	description = "{{{langname}}} terms with pronunciations that have {{w|Japanese pitch accent|pitch accent}} specified.",
	additional = "Pitch accent can be specified in {{tl|{{{langcode}}}-pron}} with the {{code|=acc=}} parameter.",
	can_be_empty = true,
	parents = {"Bảo trì mục từ", "Trọng âm âm vực"},
}

labels["Mục từ có cách phát âm IPA thiếu trọng âm âm vực"] = {
	description = "Thể loại này chứa các mục từ {{{langname}}} có cách phát âm chưa xác định {{w|Trọng âm âm vực tiếng Nhật|trọng âm âm vực}}.",
	additional = "Trọng âm âm vực có thể xác định trong bản mẫu {{tl|{{{langcode}}}-pron}} khi thêm tham số {{code|=acc=}}.",
	hidden = true,
	can_be_empty = true,
	parents = {"Bảo trì mục từ", "Trọng âm âm vực"},
}

labels["Trọng âm âm vực"] = {
	description = "Trọng âm âm vực trong cách phát âm {{{langname}}}.",
	can_be_empty = true,
	parents = {{name = "{{{langcat}}}", raw = true}},
}

labels["Mục từ có trọng âm âm vực loại Heiban (Tōkyō)"] = {
	description = "Mục từ có trọng âm âm vực loại [[平板型|Heiban]] (Tōkyō) {{{langname}}}.",
	breadcrumb = "Heiban (Tōkyō)",	
	can_be_empty = true,
	parents = {"Trọng âm âm vực"}
}

labels["Mục từ có trọng âm âm vực loại Atamadaka (Tōkyō)"] = {
	description = "Mục từ có trọng âm âm vực loại [[頭高型|Atamadaka]] (Tōkyō) {{{langname}}}.",
	breadcrumb = "Atamadaka (Tōkyō)",	
	can_be_empty = true,
	parents = {"Trọng âm âm vực"}
}

labels["Mục từ có trọng âm âm vực loại Nakadaka (Tōkyō)"] = {
	description = "Mục từ có trọng âm âm vực loại [[中高型|Nakadaka]] (Tōkyō) {{{langname}}}.",
	breadcrumb = "Nakadaka (Tōkyō)",
	can_be_empty = true,
	parents = {"Trọng âm âm vực"}
}

labels["Mục từ có trọng âm âm vực loại Odaka (Tōkyō)"] = {
	description = "Mục từ có trọng âm âm vực loại [[尾高型|Odaka]] (Tōkyō) {{{langname}}}.",
	breadcrumb = "Odaka (Tōkyō)",
	can_be_empty = true,
	parents = {"Trọng âm âm vực"}
}

labels["pitch accent deaccenting before の"] = {
	description = "{{{langname}}} terms with {{w|Japanese pitch accent|pitch accent}} pronunciations that have exceptional deaccenting or lack thereof before の ({{ja-deaccenting-before-no}}).",
	can_be_empty = true,
	parents = {"pitch accent"}
}

labels["terms with Odaka pitch accent not deaccented before の (Tōkyō)"] = {
	description = "{{{langname}}} terms with pronunciations that are (Tōkyō) [[尾高型|Odaka]] {{w|Japanese pitch accent|pitch accent}} and do not become deaccented before の ({{ja-deaccenting-before-no}}).",
	can_be_empty = true,
	parents = {"pitch accent deaccenting before の"}
}

labels["terms with Nakadaka pitch accent deaccented before の (Tōkyō)"] = {
	description = "{{{langname}}} terms with pronunciations that are (Tōkyō) [[中高型|Nakadaka]] {{w|Japanese pitch accent|pitch accent}} and become deaccented before の ({{ja-deaccenting-before-no}}).",
	can_be_empty = true,
	parents = {"pitch accent deaccenting before の"}
}

labels["Từ đánh vần với kanji theo loại âm đọc"] = {
	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 = "kanji " .. grade
	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["Từ đánh vần với " .. grade_kanji] = {
		topright = topright,
		description = "{{{langname}}} terms spelled with " .. (only_one and "at least one " or "") .. "kanji " .. desc,
		parents = {{
			name = parent and ("Từ đánh vần với " .. parent .. " kanji") or "Thuộc tính chính tả",
			sort = sort or grade
		}},
	}
end

for i = 1, 6 do
	local ord = m_numeric.ones_position_ord[i]
	add_grade_categories(
		ord,
		"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(
	"bậc trung học",
	"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["Từ có âm đọc kanji theo số 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 = "terms with 1 kanji", 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 = "[[Phụ lục:Từ điển thuật ngữ tiếng Nhật#" .. 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 n =data.label:match("^Mục từ có ([1-9]%d*) ký tự kanji$")
	if not n then
		return
	end
	local sortkey = require("Module:category tree").numeral_sortkey(n, 2097152)
	return {
		breadcrumb = n,
		description = ("{{{langname}}} terms containing exactly %d kanji."):format(n),
		-- TODO: implement this using the same mechanism used to implement parents (i.e. avoiding the need for raw categories).
--		umbrella = {
--			breadcrumb = ("%d kanji"):format(n),
--			parents = {{name = "terms by number of kanji subcategories by language", sort = sortkey}},
--		},
		parents = {{name = ("Mục từ theo số ký tự kanji"), sort = sortkey}}
	}
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("^Từ có âm đọc kanji với (.+) morae$")
	
	-- Make sure 'one' goes with singular and other numbers with plural.
	if not count or (count == "một") ~= (plural == "hai") 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 = "Từ có âm đọc kanji theo số 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, "^(Từ đánh vần với kanji là )" .. 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 = "Từ theo âm đọc kanji", sort = sort_key},
		{name = "Từ có âm đọc kanji với " .. mora_count_words .. " morae", 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, "^Từ đánh vần với (.) là " .. 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 = "Từ đánh vần với kanji có âm đọc " .. category,
			sort = (lang:makeSortKey(reading))
		})
	end
	
	insert(parents, 1, {name = "Từ đánh vần với " .. kanji, sort = (lang:makeSortKey(reading))})
	insert(parents, 2, {name = "Từ đánh vần với kanji là " .. reading, sort = Hani_sort(kanji)})
	
	yomi_types = (#yomi_types > 1 and "one of " or "") .. " " ..
		require("Module:table").serialCommaJoin(yomi_types, {conj = "or"}) ..
		" " .. (#yomi_types > 1 and "s" or "")
	
	local tagged_kanji = get_tagged_reading(kanji, lang)
	local tagged_reading = get_tagged_reading(reading, lang)
	return {
		description = "Từ {{{langname}}} đánh vần {{l|{{{langcode}}}|" .. kanji .. "}} là " .. get_reading_link(reading, lang) .. " với âm đọc " ..
			yomi_types .. ".",
		displaytitle = "Từ đánh vần với " .. tagged_kanji .. " là " .. tagged_reading .. " {{{langname}}}",
		breadcrumb = "Từ đánh vần với " .. tagged_kanji .. " là " .. tagged_reading,
		parents = parents,
	}, true
end)


insert(handlers, function(data)
	local affix, kanji, reading = data.label:match("^terms ([a-z]+fix)ed with (.+) read as "  .. kana_capture .. "$")
	
	if not affix or not kanji or not reading 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 AFFIXed 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 = {}
	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)
	end
	
	yomi_types = (#yomi_types > 1 and "" or "") .. " " ..
		require("Module:table").serialCommaJoin(yomi_types, {conj = "or"}) ..
		" " .. (#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 " .. affix .. "ed with {{l|{{{langcode}}}|" .. kanji .. "}} with " ..
			yomi_types .. " of " .. get_reading_link(reading, lang) .. ".",
		displaytitle = "{{{langname}}} terms " .. affix .. "ed with " .. tagged_kanji .. " read as " .. tagged_reading,
		breadcrumb = "read as " .. reading,
		parents = {
			{name = "terms " .. affix .. "ed with " .. kanji, sort = (lang:makeSortKey(reading))},
			{name = "terms spelled with " .. kanji .. " read as " .. reading, sort = (lang:makeSortKey(reading)), args=data.args}
		},
	}, 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}