본문으로 이동

모듈:category tree/languages

위키낱말사전, 말과 글의 누리

이 모듈에 대한 설명문서는 모듈:category tree/languages/설명문서에서 만들 수 있습니다

local new_title = mw.title.new
local ucfirst = require("Module:string utilities").ucfirst
local split = require("Module:string utilities").split

local raw_categories = {}
local raw_handlers = {}

local m_languages = require("Module:languages")
local m_sc_getByCode = require("Module:scripts").getByCode
local m_table = require("Module:table")
local parse_utilities_module = "Module:parse utilities"

local concat = table.concat
local insert = table.insert
local reverse_ipairs = m_table.reverseIpairs
local serial_comma_join = m_table.serialCommaJoin
local size = m_table.size
local sorted_pairs = m_table.sortedPairs
local to_json = require("Module:JSON").toJSON
local ko_util = require("Module:ko")

local Hang = m_sc_getByCode("Hang")
local Hani = m_sc_getByCode("Hani")
local Hira = m_sc_getByCode("Hira")
local Hrkt = m_sc_getByCode("Hrkt")
local Kana = m_sc_getByCode("Kana")

local function track(page)
	return require("Module:debug/track")("category tree/languages/" .. page)
end

-- This handles language categories of the form e.g. [[:Category:French language]] and
-- [[:Category:British Sign Language]]; categories like [[:Category:Languages of Indonesia]]; categories like
-- [[:Category:English-based creole or pidgin languages]]; and categories like
-- [[:Category:English-based constructed languages]].


-----------------------------------------------------------------------------
--                                                                         --
--                              원시(RAW) 분류                             --
--                                                                         --
-----------------------------------------------------------------------------


raw_categories["모든 언어"] = {
	topright = "{{위키공용분류|Languages}}\n[[파일:Languages world map-transparent background.svg|섬네일|오른쪽|250px|어족의 대략적인 세계 지도]]",
	description = "이 분류는 위키낱말사전의 모든 언어에 대한 분류를 포함합니다.",
	additional = "위키낱말사전이 인식하는 모든 언어가 여기에 분류로 존재하지는 않을 수 있습니다. " ..
	"모든 위키낱말사전 사용자가 모든 단일 언어에 대해 아는 것은 아니기 때문에 아직 편집자의 관심을 받지 못한 언어가 많습니다. " ..
	"전체 목록은 [[위키낱말사전:언어 목록]]을 참고하십시오.",
	parents = {
		"기본",
	},
}

raw_categories["모든 사어"] = {
	description = "이 분류는 위키낱말사전의 모든 [[사어]]에 대한 분류를 포함합니다.",
	additional = "이 분류를 특정 다른 언어의 사어 이름을 위한 포괄 분류(예: 고대 [[롬바르드어]]에 대한 {{m+|de|Langobardisch}})인 [[:분류:사어]]와 혼동하지 마십시오.",
	parents = {
		"모든 언어",
	},
}

raw_categories["나라별 언어"] = {
	topright = "{{위키공용분류|Languages by continent}}",
	description = "언어를 나라별로 그룹화하는 분류입니다.",
	additional = "{{{umbrella_meta_msg}}}",
	parents = {
		"모든 언어",
	},
}

raw_categories["고립어"] = {
	topright = "{{위키백과|고립어}}\n{{위키공용분류|Language isolates}}",
	description = "알려진 친족 관계가 없는 언어.",
	parents = {
		{name = "어족별 언어", sort = "*고립어"},
		{name = "모든 어족", sort = "고립어"},
	},
}

raw_categories["지역 분류가 없는 언어"] = {
	description = "사용되는 지역을 ({{틀|자동 분류}} 호출에서) 지정하지 않은 언어.",
	additional = "이는 인공어와 재구어는 제외합니다. 결과적으로, 이 분류의 모든 언어는 해당 지역을 {{cd|UNKNOWN}}으로 명시적으로 지정합니다.",
	parents = {
		{name = "요청"},
	},
	hidden = true,
}

-----------------------------------------------------------------------------
--                                                                         --
--                                 RAW 핸들러                              --
--                                                                         --
-----------------------------------------------------------------------------


-- 주어진 분류에서 {{자동 분류}} 틀 호출을 찾아 그 인수를 반환하는 함수.
-- FIXME: [[모듈:category tree/lects]]와 중복됨.
local function scrape_category_for_auto_cat_args(cat)
	local cat_page = mw.title.new("분류:" .. cat)
	if cat_page then
		local contents = cat_page:getContent()
		if contents then
			for template in require("Module:template parser/sandbox").find_templates(contents) do
				if template:get_name() == "자동 분류" or template:get_name() == "auto cat" then
					return template:get_arguments()
				end
			end
		end
	end
	return nil
end


local function link_location(location)
	local location_no_the = location:match("^the (.*)$")
	local bare_location = location_no_the or location
	local location_link
	local bare_location_parts = split(bare_location, ", ")
	for i, part in ipairs(bare_location_parts) do
		bare_location_parts[i] = ("[[%s]]"):format(part)
	end
	location_link = concat(bare_location_parts, ", ")
	if location_no_the then
		location_link = "the " .. location_link
	end
	return location_link
end

local function linkbox(lang, setwiki, setwikt, setsister, entryname)
	local wiktionarylinks = {}
	
	local canonicalName = lang:getCanonicalName()
	local wikimediaLanguages = lang:getWikimediaLanguages()
	local wikipediaArticle = setwiki or lang:getWikipediaArticle()
	
	setsister = setsister and ucfirst(setsister) or nil
	
	if setwikt then
		track("setwikt")
		if setwikt == "-" then
			track("setwikt/hyphen")
		end
	end
	
	if setwikt ~= "-" and wikimediaLanguages and wikimediaLanguages[1] then
		for _, wikimedialang in ipairs(wikimediaLanguages) do
			local check = new_title(wikimedialang:getCode() .. ":")
			if check and check.isExternal then
				insert(wiktionarylinks,
					(wikimedialang:getCanonicalName() ~= canonicalName and "(''" .. wikimedialang:getCanonicalName() .. "'') " or "") ..
					"'''[[:" .. wikimedialang:getCode() .. ":|" .. wikimedialang:getCode() .. ".wiktionary.org]]'''")
			end
		end
		wiktionarylinks = concat(wiktionarylinks, "<br/>")
	end
	
	local wikt_plural = (wikimediaLanguages and wikimediaLanguages[2]) and "들" or ""
	
	if #wiktionarylinks == 0 then
		wiktionarylinks = "''없음.''"
	end
	
	if setsister then
		track("setsister")
		if setsister == "-" then
			track("setsister/hyphen")
		else
			setsister = "분류:" .. setsister
		end
	else
		setsister = lang:getCommonsCategory() or "-"
	end
	
	local josa_processed_name = ko_util.allomorphy(canonicalName, "ins")
	
	return concat{
[=[<div class="wikitable" style="float: right; clear: right; margin: 0 0 0.5em 1em; width: 300px; padding: 5px;"><div style="text-align: center; margin-bottom: 10px; margin-top: 5px">''']=], canonicalName, [=[ 언어 링크'''</div>
{| style="font-size: 90%"
|-
| style="vertical-align: top; height: 35px; border-bottom: 1px solid lightgray;" | [[파일:Wikipedia-logo.png|35px|none|위키백과]]
| style="border-bottom: 1px solid lightgray;" | '''한국어 위키백과'''에 다음 문서가 있습니다:
<div style="padding: 5px 10px">]=], (setwiki == "-" and "''없음.''" or "'''[[w:ko:" .. canonicalName .. "|" .. canonicalName .. "]]'''"), [=[</div>
|-
| style="vertical-align: top; height: 35px; border-bottom: 1px solid lightgray;" | [[파일:Wikimedia-logo.svg|35px|none|위키미디어 공용]]
| style="border-bottom: 1px solid lightgray;" | '''위키미디어 공용'''에 자매 프로젝트의 ]=], canonicalName, [=[ 관련 콘텐츠가 있습니다:
<div style="padding: 5px 10px">]=], (setsister == "-" and "''없음.''" or "'''[[commons:" .. setsister .. "|" .. setsister .. "]]'''"), [=[</div>
|-
| style="vertical-align: top; height: 35px; width: 40px; border-bottom: 1px solid lightgray;" | [[파일:Wiktionary-logo-v2.svg|35px|none|위키낱말사전]]
| style="border-bottom: 1px solid lightgray;" | '''다른 언어 위키낱말사전''']=], wikt_plural, [=[ (]=], josa_processed_name, [=[ 작성됨):
<div style="padding: 5px 10px">]=], wiktionarylinks, [=[</div>
|-
| style="vertical-align: top; height: 35px; border-bottom: 1px solid lightgray;" | [[파일:Open book nae 02.svg|35px|none|항목]]
| style="border-bottom: 1px solid lightgray;" | 이 언어의 한국어 이름에 대한 '''위키낱말사전 항목''':
<div style="padding: 5px 10px">''']=], require("Module:also/link").full_link({lang = m_languages.getByCode("ko"), term = entryname or canonicalName}), [=['''</div>
|-
| style="vertical-align: top; height: 35px;" | [[파일:Crystal kfind.png|35px|none|자료]]
| '''위키낱말사전 자료''' (]=], canonicalName, [=[ 항목 기여자용):
<div style="padding: 5px 0">* '''[[위키낱말사전:]=], canonicalName, [=[ 항목 지침]]'''
* '''[[:분류:]=], canonicalName, [=[ 참고 문헌 틀|참고 문헌 틀]] ({{PAGESINCAT:]=], canonicalName, [=[ 참고 문헌 틀}})'''
* '''[[부록:]=], canonicalName, [=[ 참고 문헌|참고 문헌]]'''
|}
</div>]=]
}
end

local function edit_link(title, text)
	return '<span class="plainlinks">['
		.. tostring(mw.uri.fullUrl(title, { action = "edit" }))
		.. ' ' .. text .. ']</span>'
end

-- Should perhaps use wiki syntax.
local function infobox(lang)
	local ret = {}
	
	insert(ret, '<table class="wikitable language-category-info"')
	
	local raw_data = lang:getData("extra")
	if raw_data then
		local replacements = {
			[1] = "canonical-name",
			[2] = "wikidata-item",
			[3] = "family",
			[4] = "scripts",
		}
		local function replacer(letter1, letter2)
			return letter1:lower() .. "-" .. letter2:lower()
		end
		-- For each key in the language data modules, returns a descriptive
		-- kebab-case version (containing ASCII lowercase words separated
		-- by hyphens).
		local function kebab_case(key)
			key = replacements[key] or key
			key = key:gsub("(%l)(%u)", replacer):gsub("(%l)_(%l)", replacer)
			return key
		end
		local compress = {compress = true}
		local function html_attribute_encode(str)
			str = to_json(str, compress)
				:gsub('"', "&quot;")
				-- & in attributes is automatically escaped.
				-- :gsub("&", "&amp;")
				:gsub("<", "&lt;")
				:gsub(">", "&gt;")
			return str
		end
		insert(ret, ' data-code="' .. lang:getCode() .. '"')
		for k, v in sorted_pairs(raw_data) do
			insert(ret, " data-" .. kebab_case(k)
			.. '="'
			.. html_attribute_encode(v)
			.. '"')
		end
	end
	insert(ret, '>\n')
	insert(ret, '<tr class="language-category-data">\n<th colspan="2">'
		.. edit_link(lang:getDataModuleName(), "언어 데이터 편집")
		.. "</th>\n</tr>\n")
	insert(ret, "<tr>\n<th>정식 명칭</th><td>" .. lang:getCanonicalName() .. "</td>\n</tr>\n")

	local otherNames = lang:getOtherNames()
	if otherNames then
		local names = {}
		
		for _, name in ipairs(otherNames) do
			insert(names, "<li>" .. name .. "</li>")
		end
		
		if #names > 0 then
			insert(ret, "<tr>\n<th>다른 이름</th><td><ul>" .. concat(names, "\n") .. "</ul></td>\n</tr>\n")
		end
	end
	
	local aliases = lang:getAliases()
	if aliases then
		local names = {}
		
		for _, name in ipairs(aliases) do
			insert(names, "<li>" .. name .. "</li>")
		end
		
		if #names > 0 then
			insert(ret, "<tr>\n<th>별칭</th><td><ul>" .. concat(names, "\n") .. "</ul></td>\n</tr>\n")
		end
	end

	local varieties = lang:getVarieties()
	if varieties then
		local names = {}
		
		for _, name in ipairs(varieties) do
			if type(name) == "string" then
				insert(names, "<li>" .. name .. "</li>")
			else
				assert(type(name) == "table")
				local first_var
				local subvars = {}
				for i, var in ipairs(name) do
					if i == 1 then
						first_var = var
					else
						insert(subvars, "<li>" .. var .. "</li>")
					end
				end
				if #subvars > 0 then
					insert(names, "<li><dl><dt>" .. first_var .. "</dt>\n<dd><ul>" .. concat(subvars, "\n") .. "</ul></dd></dl></li>")
				elseif first_var then
					insert(names, "<li>" .. first_var .. "</li>")
				end
			end
		end
		
		if #names > 0 then
			insert(ret, "<tr>\n<th>Varieties</th><td><ul>" .. concat(names, "\n") .. "</ul></td>\n</tr>\n")
		end
	end

	insert(ret, "<tr>\n<th>[[위키낱말사전:언어|언어 코드]]</th><td><code>" .. lang:getCode() .. "</code></td>\n</tr>\n")
	insert(ret, "<tr>\n<th>[[위키낱말사전:어족|어족]]</th>\n")
	
	local fam = lang:getFamily()
	local famCode = fam and fam:getCode()
	
	if not fam then
		insert(ret, "<td>unclassified</td>")
	elseif famCode == "qfa-iso" then
		insert(ret, "<td>[[:분류:고립어|고립어]]</td>")
	elseif famCode == "qfa-mix" then
		insert(ret, "<td>[[:분류:혼합어]]</td>")
	elseif famCode == "sgn" then
		insert(ret, "<td>[[:분류:수어]]</td>")
	elseif famCode == "crp" then
		insert(ret, "<td>[[:분류:크리올 또는 피진]]</td>")
	elseif famCode == "art" then
		insert(ret, "<td>[[:분류:인공어]]</td>")
	else
		insert(ret, "<td>" .. fam:makeCategoryLink() .. "</td>")
	end
	
	insert(ret, "\n</tr>\n<tr>\n<th>조상 언어</th>\n<td>")
	
	local ancestors = lang:getAncestors()
	if ancestors[2] then
		local ancestorList = {}
		for i, anc in ipairs(ancestors) do
			ancestorList[i] = "<li>" .. anc:makeCategoryLink() .. "</li>"
		end
		insert(ret, "<ul>\n" .. concat(ancestorList, "\n") .. "</ul>")
	else
		local ancestorChain = lang:getAncestorChainOld()
		if ancestorChain[1] then
			local chain = {}
			for _, anc in reverse_ipairs(ancestorChain) do
				insert(chain, "<li>" .. anc:makeCategoryLink() .. "</li>")
			end
			insert(ret, "<ul>\n" .. concat(chain, "\n<ul>\n") .. ("</ul>"):rep(#chain))
		else
			insert(ret, "unknown")
		end
	end
	
	insert(ret, "</td>\n</tr>\n")
	
	local scripts = lang:getScripts()
	
	if scripts[1] then
		local script_text = {}
		
		local function makeScriptLine(sc)
			local code = sc:getCode()
			local url = tostring(mw.uri.fullUrl('Special:Search', {
				search = 'contentmodel:css insource:"' .. code
					.. '" insource:/\\.' .. code .. '/',
				ns8 = '1'
			}))
			return sc:makeCategoryLink()
				.. ' (<span class="plainlinks" title="Search for stylesheets referencing this script">[' .. url .. ' <code>' .. code .. '</code>]</span>)'
		end
		
		local function add_Hrkt(text)
			insert(text, "<li>" .. makeScriptLine(Hrkt))
			insert(text, "<ul>")
			insert(text, "<li>" .. makeScriptLine(Hira) .. "</li>")
			insert(text, "<li>" .. makeScriptLine(Kana) .. "</li>")
			insert(text, "</ul>")
			insert(text, "</li>")
		end
		
		for _, sc in ipairs(scripts) do
			local text = {}
			local code = sc:getCode()
			
			if code == "Hrkt" then
				add_Hrkt(text)
			else
				insert(text, "<li>" .. makeScriptLine(sc))
				if code == "Jpan" then
					insert(text, "<ul>")
					insert(text, "<li>" .. makeScriptLine(Hani) .. "</li>")
					add_Hrkt(text)
					insert(text, "</ul>")
				elseif code == "Kore" then
					insert(text, "<ul>")
					insert(text, "<li>" .. makeScriptLine(Hang) .. "</li>")
					insert(text, "<li>" .. makeScriptLine(Hani) .. "</li>")
					insert(text, "</ul>")
				end
				insert(text, "</li>")
			end
			
			insert(script_text, concat(text, "\n"))
		end
		
		insert(ret, "<tr>\n<th>[[위키낱말사전:스크립트|문자 체계]]</th>\n<td><ul>\n" .. concat(script_text, "\n") .. "</ul></td>\n</tr>\n")
	else
		insert(ret, "<tr>\n<th>[[위키낱말사전:스크립트|문자 체계]]</th>\n<td>지정되지 않음</td>\n</tr>\n")
	end
	
	local function add_module_info(raw_data, heading)
		if raw_data then
			local scripts = lang:getScriptCodes()
			local module_info, add = {}, false
			if type(raw_data) == "string" then
				insert(module_info,
					("[[Module:%s]]"):format(raw_data))
				add = true
			else
				local raw_data_type = type(raw_data)
				if raw_data_type == "table" and size(scripts) == 1 and type(raw_data[scripts[1]]) == "string" then
					insert(module_info,
						("[[Module:%s]]"):format(raw_data[scripts[1]]))
					add = true
				elseif raw_data_type == "table" then
					insert(module_info, "<ul>")
					for script, data in sorted_pairs(raw_data) do
						if type(data) == "string" and m_sc_getByCode(script) then
							insert(module_info, ("<li><code>%s</code>: [[Module:%s]]</li>"):format(script, data))
						end
					end
					insert(module_info, "</ul>")
					add = size(module_info) > 2
				end
			end
			
			if add then
				insert(ret, [=[
<tr>
<th>]=] .. heading .. [=[</th>
<td>]=] .. concat(module_info) .. [=[</td>
</tr>
]=])
			end
		end
	end
	
	add_module_info(raw_data.generate_forms, "Form-generating<br>module")
	add_module_info(raw_data.translit, "음역 모듈")
	add_module_info(raw_data.display_text, "Display text<br>module")
	add_module_info(raw_data.entry_name, "Entry name<br>module")
	add_module_info(raw_data.sort_key, "[[sortkey|Sortkey]]<br>module")
	
	local wikidataItem = lang:getWikidataItem()
	if lang:getWikidataItem() and mw.wikibase then
		local URL = mw.wikibase.getEntityUrl(wikidataItem)
		local link
		if URL then
			link = '[' .. URL .. ' ' .. wikidataItem .. ']'
		else
			link = '<span class="error">Invalid Wikidata item: <code>' .. wikidataItem .. '</code></span>'
		end
		insert(ret, "<tr><th>위키데이터</th><td>" .. link .. "</td></tr>")
	end
	
	insert(ret, "</table>")
	
	return concat(ret)
end

local function NavFrame(content, title)
	return '<div class="NavFrame"><div class="NavHead">'
		.. (title or '{{{title}}}') .. '</div>'
		.. '<div class="NavContent" style="text-align: left;">'
		.. content
		.. '</div></div>'
end


local function get_description_topright_additional(lang, locations, extinct, setwiki, setwikt, setsister, entryname)
	local nameWithLanguage = lang:getCategoryName("nocap")
	if lang:getCode() == "und" then
		local description = "이 분류는 위키낱말사전에서 '''" .. lang:getCode() .. "''' 코드로 대표되는 '''" .. nameWithLanguage .. "'''의 주 분류입니다. " ..
			"이 언어는 역사적 기록에 나타나지만 그 의미가 아직 학자들에 의해 결정되지 않은 용어를 포함합니다."
		return description, nil, nil
	end
	
	local canonicalName = lang:getCanonicalName()
	
	local topright = linkbox(lang, setwiki, setwikt, setsister, entryname)

	local the_prefix
	if canonicalName:find(" Language$") then
		the_prefix = ""
	else
		the_prefix = "the "
	end
	local description = "이 분류는 '''" .. nameWithLanguage .. "'''의 주 분류입니다."

	local location_links = {}
	local prep
	local saw_embedded_comma = false
	for _, location in ipairs(locations) do
		local this_prep
		if location == "전 세계" then
			this_prep = "전역에서" 
			insert(location_links, location)
		elseif location ~= "UNKNOWN" then
			this_prep = "에서" 
			if location:find(",") then
				saw_embedded_comma = true end
			insert(location_links, link_location(location))
		end
		if this_prep then
			if prep and this_prep ~= prep then
				error("장소 '전 세계'와 다른 장소를 함께 처리할 수 없습니다 (조사 충돌)") end
			prep = this_prep
		end
	end
	
	local location_desc
	if #location_links > 0 then
		local location_link_text
		if saw_embedded_comma and #location_links >= 3 then
			location_link_text = mw.text.listToText(location_links, ", ", " 및 ")
		else
			location_link_text = mw.text.listToText(location_links, ", ", " 및 ")
		end
		location_desc = ("이 언어는 %s%s에서 %s.\n\n"):format(
			location_link_text,
			(prep == "전역" and "" or ""),
			extinct and "과거에 사용되었던 [[사어]]입니다" or "사용됩니다")
	elseif extinct then
		location_desc = "이 언어는 [[사어]]입니다.\n\n"
	else
		location_desc = ""
	end

	local add = location_desc .. canonicalName .. "에 대한 정보:\n\n" .. infobox(lang)
	
	if lang:hasType("reconstructed") then
		add = add .. "\n\n" .. ucfirst(canonicalName) .. "은(는) 재구된 언어입니다. " ..
			"이 언어의 단어와 어근은 어떤 문헌에서도 직접적으로 증명되지 않았지만, 우연이나 단어 차용으로 설명할 수 없는 언어 간의 규칙적인 유사점을 찾는 ''비교 방법론''을 통해 재구성되었습니다." ..
			"\n\n[[위키낱말사전:등재 기준|등재 기준]]에 따라, " .. canonicalName ..
			"의 낱말은 주 이름공간의 항목에 있어서는 '''안 되며''', 재구: 이름공간에 추가될 수 있습니다."
	elseif lang:hasType("appendix-constructed") then
		add = add .. "\n\n" .. ucfirst(canonicalName) .. "은(는) 산발적으로만 사용되는 인공어입니다. " ..
			"[[위키낱말사전:등재 기준|등재 기준]]에 따라, " .. canonicalName ..
			"의 낱말은 주 이름공간의 항목에 있어서는 '''안 되며''', 부록: 이름공간에 추가될 수 있습니다. " ..
			"이 언어의 모든 낱말은 [[부록:" .. ucfirst(canonicalName) .. "]]에서 찾아볼 수 있습니다."
	end
	
	local about = new_title("Wiktionary:About " .. canonicalName)
	
	if about.exists then
		add = add .. "\n\n" ..
			"Please see '''[[Wiktionary:About " .. canonicalName .. "]]''' for information and special considerations for creating " .. nameWithLanguage .. " entries."
	end
	
	local ok, tree_of_descendants = pcall(
		require("Module:family tree").print_children,
		lang:getCode(), {
			protolanguage_under_family = true,
			must_have_descendants = true
		})
	
	if ok then
		if tree_of_descendants then
			add = add .. NavFrame(
				tree_of_descendants,
				"Family tree")
		else
			add = add .. "\n\n" .. ucfirst(lang:getCanonicalName())
				.. " has no descendants or varieties listed in Wiktionary's language data modules."
		end
	else
		mw.log("error while generating tree: " .. tostring(tree_of_descendants))
	end

	return description, topright, add
end


local function get_parents(lang, locations, extinct)
	local canonicalName = lang:getCanonicalName()
	
	local sortkey = {sort_base = canonicalName, lang = "ko"}
	local ret = {{name = "모든 언어", sort = sortkey}}
	
	local fam = lang:getFamily()
	local famCode = fam and fam:getCode()
	
	-- FIXME: Some of the following categories should be added to this module.
	if not fam then
		insert(ret, {name = "분류:미분류 언어", sort = sortkey})
	elseif famCode == "qfa-iso" then
		insert(ret, {name = "분류:고립어", sort = sortkey})
	elseif famCode == "qfa-mix" then
		insert(ret, {name = "분류:혼합어", sort = sortkey})
	elseif famCode == "sgn" then
		insert(ret, {name = "분류:모든 수어", sort = sortkey})
	elseif famCode == "crp" then
		insert(ret, {name = "분류:크리올 또는 피진", sort = sortkey})
		for _, anc in ipairs(lang:getAncestors()) do
			-- Avoid Haitian Creole being categorised in [[:Category:Haitian Creole-based creole or pidgin languages]], as one of its ancestors is an etymology-only variety of it.
			-- Use that ancestor's ancestors instead.
			if anc:getFullCode() == lang:getCode() then
				for _, anc_extra in ipairs(anc:getAncestors()) do
					insert(ret, {name = "분류:" .. ucfirst(anc_extra:getFullName()) .. " 기반 크리올 또는 피진", sort = sortkey})
				end
			else
				insert(ret, {name = "분류:" .. ucfirst(anc:getFullName()) .. " 기반 크리올 또는 피진", sort = sortkey})
			end
		end
	elseif famCode == "art" then
		if lang:hasType("appendix-constructed") then
			insert(ret, {name = "분류:부록 전용 인공어", sort = sortkey})
		else
			insert(ret, {name = "분류:인공어", sort = sortkey})
		end
		for _, anc in ipairs(lang:getAncestors()) do
			if anc:getFullCode() == lang:getCode() then
				for _, anc_extra in ipairs(anc:getAncestors()) do
					insert(ret, {name = "분류:" .. ucfirst(anc_extra:getFullName()) .. " 기반 인공어", sort = sortkey})
				end
			else
				insert(ret, {name = "분류:" .. ucfirst(anc:getFullName()) .. " 기반 인공어", sort = sortkey})
			end
		end
	else
		insert(ret, {name = "분류:" .. fam:getCategoryName(), sort = sortkey})
		if lang:hasType("reconstructed") then
			insert(ret, {
				name = "분류:재구어",
				sort = {sort_base = canonicalName:gsub("^원시 ", ""), lang = "ko"}
			})
		end
	end
	
	local function add_sc_cat(sc)
		insert(ret, {name = "분류:" .. sc:getCanonicalName() .. " 사용 언어", sort = sortkey})
	end
	
	local function add_Hrkt()
		add_sc_cat(Hrkt)
		add_sc_cat(Hira)
		add_sc_cat(Kana)
	end
	
	for _, sc in ipairs(lang:getScripts()) do
		if sc:getCode() == "Hrkt" then
			add_Hrkt()
		else
			add_sc_cat(sc)
			if sc:getCode() == "Jpan" then
				add_sc_cat(Hani)
				add_Hrkt()
			elseif sc:getCode() == "Kore" then
				add_sc_cat(Hang)
				add_sc_cat(Hani)
			end
		end
	end
	
	if lang:hasTranslit() then
		insert(ret, {name = "분류:자동 음역 기능이 있는 언어", sort = sortkey})
	end

	local function insert_location_language_cat(location)
		-- "Languages of " .. location -> location .. "의 언어"
		local cat = location .. "의 언어"
		insert(ret, {name = "분류:" .. cat, sort = sortkey})
		local auto_cat_args = scrape_category_for_auto_cat_args(cat)
		local location_parent = auto_cat_args and auto_cat_args.parent
		if location_parent then
			local split_parents = require(parse_utilities_module).split_on_comma(location_parent)
			for _, parent in ipairs(split_parents) do
				parent = parent:match("^(.-):.*$") or parent
				insert_location_language_cat(parent)
			end
		end
	end

	local saw_location = false
	for _, location in ipairs(locations) do
		if location ~= "UNKNOWN" then
			saw_location = true
			insert_location_language_cat(location)
		end
	end

	if extinct then
		insert(ret, {name = "분류:모든 사어", sort = sortkey})
	end

	if not saw_location and not (lang:hasType("reconstructed") or (fam and fam:getCode() == "art")) then
		-- Constructed and reconstructed languages don't need a location specified and often won't have one,
		-- so don't put them in this maintenance category.
		insert(ret, {name = "분류:지역 분류가 없는 언어", sort = sortkey})
	end

	return ret
end


local function get_children()
	local ret = {}

	-- FIXME: We should work on the children mechanism so it isn't necessary to manually specify these.
	for _, label in ipairs({"부록", "문서 관리", "표제어", "이름", "어구", "운율", "기호", "틀", "어원", "용법"}) do
		insert(ret, {name = label, is_label = true})
	end

	insert(ret, {name = "{{{langname}}}에서 파생된 낱말", is_label = true, lang = false})
	insert(ret, {name = "분류:{{{langcode}}}:모든 주제", sort = "모든 주제"})
	insert(ret, {name = "분류:{{{langname}}}의 변종"})
	insert(ret, {name = "분류:{{{langname}}} 관련 요청"}) -- "분류:" 추가
	insert(ret, {name = "운율:{{{langname}}}", description = "운율별 {{{langname}}} 단어 목록."})
	insert(ret, {name = "분류:사용자 {{{langcode}}}", description = "{{{langdisp}}}의 유창성 수준별 위키낱말사전 사용자."})
	return ret
end


-- '프랑스어', '영국 수어' 같은 언어 분류를 처리하는 핸들러.
insert(raw_handlers, function(data)
	local category = data.category
	
	local lang = m_languages.getByCanonicalName(category)
	
	-- 만약 찾지 못했다면, 뒤에 붙은 ' 언어'를 떼고 다시 시도
	if not lang then
		local langname = category:match("^(.*) 언어$")
		if langname then
			lang = m_languages.getByCanonicalName(langname)
		end
	end
	
	-- 최종적으로 언어를 찾지 못했다면, 이 핸들러는 해당 분류를 처리하지 않음.
	if not lang then
		return nil
	end
	
	local args = require("Module:parameters").process(data.args, {
		[1] = {list = true},
		["setwiki"] = true,
		["setwikt"] = true,
		["setsister"] = true,
		["entryname"] = true,
		["extinct"] = {type = "boolean"},
	})
	-- If called from inside, don't require any arguments, as they can't be known
	-- in general and aren't needed just to generate the first parent (used for
	-- breadcrumbs).
	if #args[1] == 0 and not data.called_from_inside then
		-- At least one location must be specified unless the language is constructed (e.g. Esperanto) or reconstructed (e.g. Proto-Indo-European).
		local fam = lang:getFamily()
		if not (lang:hasType("reconstructed") or (fam and fam:getCode() == "art")) then
			error("언어 '" .. lang:getCanonicalName() .. "' (코드 '" .. lang:getCode() .. "')에 대해 적어도 하나의 지역(1= 매개변수)을 지정해야 합니다. " ..
				"언어의 위치를 정말로 모르는 경우 UNKNOWN 값을 사용하십시오.")
		end
	end
	local description, topright, additional = "", "", ""
	-- If called from inside the category tree system, it's called when generating
	-- parents or children, and we don't need to generate the description or additional
	-- text (which is very expensive in terms of memory because it calls [[Module:family tree]],
	-- which calls [[Module:languages/data/all]]).
	if not data.called_from_inside then
		description, topright, additional = get_description_topright_additional(
			lang, args[1], args.extinct, args.setwiki, args.setwikt, args.setsister, args.entryname
		)
	end
	return {
		canonical_name = lang:getCategoryName(),
		description = description,
		lang = lang:getCode(),
		topright = topright,
		additional = additional,
		breadcrumb = lang:getCanonicalName(),
		parents = get_parents(lang, args[1], args.extinct),
		extra_children = get_children(lang),
		umbrella = false,
		can_be_empty = true,
	}, true
end)


-- '인도네시아의 언어' 같은 분류를 처리하는 핸들러.
insert(raw_handlers, function(data)
	local location = data.category:match("^(.+)의 언어$")
	if location then
		local args = require("Module:parameters").process(data.args, {
			["flagfile"] = true,
			["commonscat"] = true,
			["wp"] = true,
			["basename"] = true,
			["parent"] = true,
			["locationcat"] = true,
			["locationlink"] = true,
		})
		local topright
		local basename = args.basename or location:gsub(", .*", "")
		if args.flagfile ~= "-" then
			local flagfile_arg = args.flagfile or ("Flag of %s.svg"):format(basename)
			local files = require(parse_utilities_module).split_on_comma(flagfile_arg)
			local topright_parts = {}
			for _, file in ipairs(files) do
				local flagfile = "File:" .. file
				local flagfile_page = new_title(flagfile)
				if flagfile_page and flagfile_page.file.exists then
					insert(topright_parts, ("[[%s|right|100px|border]]"):format(flagfile))
				elseif args.flagfile then
					error(("Explicit flagfile '%s' doesn't exist"):format(flagfile))
				end
			end
			topright = concat(topright_parts)
		end

		if args.wp then
			local wp = require("Module:yesno")(args.wp, "+")
			if wp == "+" or wp == true then
				wp = data.category
			end
			if wp then
				local wp_topright = ("{{wikipedia|%s}}"):format(wp)
				if topright then
					topright = topright .. wp_topright
				else
					topright = wp_topright
				end
			end
		end

		if args.commonscat then
			local commonscat = require("Module:yesno")(args.commonscat, "+")
			if commonscat == "+" or commonscat == true then
				commonscat = data.category
			end
			if commonscat then
				local commons_topright = ("{{위키공용분류|%s}}"):format(commonscat)
				if topright then
					topright = topright .. commons_topright
				else
					topright = commons_topright
				end
			end
		end

		local bare_location = location:match("^the (.*)$") or location
		local location_link = args.locationlink or link_location(location)
		local bare_basename = basename:match("^the (.*)$") or basename

		local parents = {}
		if args.parent then
			local explicit_parents = require(parse_utilities_module).split_on_comma(args.parent)
			for i, parent in ipairs(explicit_parents) do
				local actual_parent, sort_key = parent:match("^(.-):(.*)$")
				if actual_parent then
					parent = actual_parent
					sort_key = sort_key:gsub("%+", bare_location)
				else
					sort_key = " " .. bare_location
				end
				insert(parents, {name = "Languages of " .. parent, sort = sort_key})
			end
		else
			insert(parents, {name = "Languages by country", sort = {sort_base = bare_location, lang = "en"}})
		end
		if args.locationcat then
			local explicit_location_cats = require(parse_utilities_module).split_on_comma(args.locationcat)
			for i, locationcat in ipairs(explicit_location_cats) do
				insert(parents, {name = "Category:" .. locationcat, sort = " Languages"})
			end
		else
			local location_cat = ("Category:%s"):format(bare_location)
			local location_page = new_title(location_cat)
			if location_page and location_page.exists then
				insert(parents, {name = location_cat, sort = "Languages"})
			end
		end
		local description = ("%s의 언어(하위 언어 포함)에 대한 분류."):format(location_link)

		return {
			topright = topright,
			description = description,
			parents = parents,
			breadcrumb = bare_basename,
			additional = "{{{umbrella_msg}}}",
		}, true
	end
end)


-- '영어를 기반으로 한 크리올 또는 피진' 같은 분류를 처리하는 핸들러.
insert(raw_handlers, function(data)
	local langname = data.category:match("^(.+) 기반 크리올 또는 피진$")
	if langname then
		local lang = m_languages.getByCanonicalName(langname)
		if lang then
			return {
				lang = lang:getCode(),
				description = lang:makeCategoryLink() .. "에서 [[크리올]] 또는 [[피진]]으로 발전한 언어.",
				parents = {{name = "크리올 또는 피진", sort = {sort_base = "*" .. langname, lang = "ko"}}},
				breadcrumb = lang:getCanonicalName() .. " 기반",
			}
		end
	end
end)


-- '영어를 기반으로 한 인공어' 같은 분류를 처리하는 핸들러.
insert(raw_handlers, function(data)
	local langname = data.category:match("^(.+) 기반 인공어$")
	if langname then
		local lang = m_languages.getByCanonicalName(langname)
		if lang then
			return {
				lang = lang:getCode(),
				description = lang:makeCategoryLink() .. "에 기반을 둔 인공어.",
				parents = {{name = "인공어", sort = {sort_base = "*" .. langname, lang = "ko"}}},
				breadcrumb = lang:getCanonicalName() .. " 기반",
			}
		end
	end
end)


return {RAW_CATEGORIES = raw_categories, RAW_HANDLERS = raw_handlers}