模組:Form of doc

維基詞典,自由的多語言詞典


--[=[
	This module contains functions to implement {{form of/*doc}} templates.
	The module contains the actual implementation, meant to be called from other
	Lua code. See [[Module:form of doc/templates]] for the function meant to be
	called directly from templates.

	Author: Benwing2
]=]

local export = {}

local m_template_link = require("Module:template link")
local m_languages = require("Module:languages")
local m_table = require("Module:table")
local strutils = require("Module:string utilities")

local usub = mw.ustring.sub
local uupper = mw.ustring.upper
local rfind = mw.ustring.find
local rsubn = mw.ustring.gsub

-- version of rsubn() that discards all but the first return value
local function rsub(term, foo, bar)
	local retval = rsubn(term, foo, bar)
	return retval
end

local function lang_name(langcode, param)
	local lang = m_languages.getByCode(langcode) or m_languages.err(langcode, param)
	return lang:getCanonicalName()
end

local function ucfirst(text)
	return uupper(usub(text, 1, 1)) .. usub(text, 2)
end

local function template_name(preserve_lang_code)
	-- Fetch the template name, minus the '/doc' suffix that may follow
	-- and without any language-specific prefixes (e.g. 'el-' or 'bsl-ine-pro-')
	-- (unless `preserve_lang_code` is given).
	local PAGENAME =  mw.title.getCurrentTitle().text
	local tempname = rsub(PAGENAME, "/doc$", "")
	if not preserve_lang_code then
		while true do
			-- Repeatedly strip off language code prefixes, in case there are multiple.
			local newname = rsub(tempname, "^[a-z][a-z][a-z]?%-", "")
			if newname == tempname then
				break
			end
			tempname = newname
		end
	end
	return tempname
end

function export.introdoc(args)
	local langname = args.lang and lang_name(args.lang, "lang")
	local exlangnames = {}
	for _, exlang in ipairs(args.exlang) do
		table.insert(exlangnames, lang_name(exlang, "exlang"))
	end
	parts = {}
	table.insert(parts, "此模板生成一行形如“")
	table.insert(parts, args.primaryentrytext or "某單字")
	table.insert(parts, (args.pldesc or rsub(template_name(), " of$", "")) .. "”")
	table.insert(parts, "的簡短定義")
	if args.lang then
		table.insert(parts, " in " .. langname)
	elseif #args.exlang > 0 then
		table.insert(parts, ",例如" .. m_table.serialCommaJoin(exlangnames, {conj = "或"}))
	end
	table.insert(parts, "。")
	if #args.cat > 0 then
		table.insert(parts, "同時也會把頁面按照語言添加到")
		local catparts = {}
		for _, cat in ipairs(args.cat) do
			if args.lang then
				table.insert(catparts, "[[:Category:" .. langname .. cat .. "]]")
			else
				table.insert(catparts, "[[:Category:" .. cat .. "]]的子分類中(例如[[:Category:" .. (exlangnames[1] or "漢語") .. cat .. "]])")
			end
		end
		table.insert(parts, m_table.serialCommaJoin(catparts, {conj = "和"}))
		table.insert(parts, ".")
	end
	if args.addlintrotext then
		table.insert(parts, " ")
		table.insert(parts, args.addlintrotext)
	end
	table.insert(parts, "\n")
	if args.withcap and args.withdot then
		table.insert(parts, [===[

默認情况下,此模板將輸出一個完整的句子,首字母大寫且句末有句號。可通過<code>|nocap=1</code>和/或<code>|nodot=1</code>進行控制(見下)。
]===])
	elseif args.withcap then
		table.insert(parts, [===[

默認情况下,此模板將輸出一個首字母大寫的句子。可通過<code>|nocap=1</code>進行控制(見下)。
]===])
	end
	table.insert(parts, [===[

此模板'''不用於'''詞源章節。]===])
	if args.etymtemp then
		table.insert(parts, "這些章節請使用<code>{{[[Template:" .. args.etymtemp .. "|" .. args.etymtemp .. "]]}}</code>。")
	end
	table.insert(parts, [===[


請注意,用戶可以通過修改monobook.css檔案來控制此模板輸出樣式。詳見[[:Category:之形式模板|之形式模板]]。
]===])
	return table.concat(parts)
end

local function param(params, list, required)
	local paramparts = {}
	if type(params) ~= "table" then
		params = {params}
	end
	for _, p in ipairs(params) do
		local listparts = {}
		table.insert(listparts, "<code>|" .. p .. "=</code>")
		if list then
			table.insert(listparts, "、<code>|" .. p .. "2=</code>")
			table.insert(listparts, "、<code>|" .. p .. "3=</code>")
			table.insert(listparts, "等")
		end
		table.insert(paramparts, table.concat(listparts))
	end
	local reqtext = required and "'''(必填)'''" or "''(選填)''"
	return table.concat(paramparts, "或") .. " " .. reqtext
end

function export.paramdoc(args)
	local parts = {}
	
	local function param_and_doc(params, list, required, doc)
		table.insert(parts, "; ")
		table.insert(parts, param(params, list, required))
		table.insert(parts, "\n")
		table.insert(parts, ": ")
		table.insert(parts, doc)
		table.insert(parts, "\n")
	end

	local tempname = template_name()
	local art = args.art or rfind(tempname, "^[aeiouAEIOU]") and "an" or "a"
	local sgdescof = args.sgdescof or art .. " " .. tempname
	table.insert(parts, "''無名參數:''\n")
	if args.lang then
		param_and_doc("1", false, true, "連結到的單字。若有變音符號(例如俄語重音符號、阿拉伯語母音變音符號、拉丁文表示母音長度的長音符等)應拼寫完整。")
		param_and_doc("2", false, false, "用於顯示連結到的單字的名稱。若為空或省略,則將使用參數2給定的名稱。此參數通常無需填寫。")
	else
		param_and_doc("1", false, true, "連結到的單字的語言代碼。見[[Wiktionary:语言列表]]。<small>參數<code>|lang=</code>已廢棄,僅爲兼容性而保留,請勿使用。如若使用該參數,則所有未命名參數的編號後移一位。</small>")
		param_and_doc("2", false, true, "連結到的單字。若有變音符號(例如俄語重音符號、阿拉伯語母音變音符號、拉丁文表示母音長度的長音符等)應拼寫完整。")
		param_and_doc("3", false, false, "用於顯示連結到的單字的名稱。若為空或省略,則將使用參數2給定的名稱。此參數通常無需填寫。")
	end
	table.insert(parts, "''有名參數:''\n")
	param_and_doc({"t", args.lang and "3" or "4"}, false, false, "與連結到的單字有關的解釋或簡短翻譯。<small>參數<code>|gloss=</code>已廢棄,僅爲兼容性而保留,請勿使用。</small>")
	param_and_doc("tr", false, false, "非拉丁文字詞語的轉寫與自動生成的轉寫不一致時,手動填寫轉寫。")
	param_and_doc("ts", false, false, "非拉丁文字詞語的轉寫與實際發音明顯不一致時,手動填寫轉寫。不應用於國際音標發音。")
	param_and_doc("sc", false, false, "書寫系統自動檢測失敗時,手動填寫書寫系統。")
	if args.withfrom then
		param_and_doc("from", true, false, "A label (see " .. m_template_link.format_link({"label"}) .. ") that gives additional information on the dialect that the term belongs to, the place that it originates from, or something similar.")
	end
	if args.withdot then
		param_and_doc("dot", false, false, "此處填寫的字元可替換掉自動顯示的句末句點。")
		param_and_doc("nodot", false, false, "如若<code>|nodot=1</code>,則不會自動顯示句末句點。")
	end
	if args.withcap then
		param_and_doc("nocap", false, false, "如若 <code>|nocap=1</code>,則句首字母會小寫。")
	end
--[[
	中文沒有{{senseid}}
	param_and_doc("id", false, false, "A sense id for the term, which links to anchors on the page set by the " .. m_template_link.format_link({"senseid"}) .. " template.")
]]--
	return table.concat(parts)
end

function export.usagedoc(args)
	local exlangs = {}
	for _, exlang in ipairs(args.exlang) do
		table.insert(exlangs, exlang)
	end
	table.insert(exlangs, 'zh')
	table.insert(exlangs, 'en')
	table.insert(exlangs, 'ja')
	exlangs = m_table.removeDuplicates(exlangs)
	local sub = {}
	local langparts = {}
	for i, langcode in ipairs(exlangs) do
		table.insert(langparts, '<code>' .. langcode .. '</code>代表' .. lang_name(langcode, "exlang"))
	end
	sub.exlangs = m_table.serialCommaJoin(langparts, {conj = "或"})
	sub.tempname = template_name("preserve lang code")

	if args.lang then
		return strutils.format([===[
==用法==
在定義行中使用,通常如下所示:
 # {\op}{\op}{tempname}|<var><單字名稱></var>{\cl}{\cl}

===參數===
]===], sub) .. export.paramdoc(args)
	else
		return strutils.format([===[
==用法==
在定義行中使用,通常如下所示:
 # {\op}{\op}{tempname}|<var><langcode></var>|<var><單字名稱></var>{\cl}{\cl}
其中<code><var><langcode></var></code>為[[Appendix:语言列表|語言代號]],例如{exlangs}。

===參數===
]===], sub) .. export.paramdoc(args)
	end
end

function export.fulldoc(args)
	local docsubpage = mw.getCurrentFrame():expandTemplate{title="documentation subpage", args={}}
	local shortcuts = #args.shortcut > 0 and require("Module:shortcut box").show(args.shortcut) or ""
	local introdoc = export.introdoc(args)
	local usagedoc = export.usagedoc(args)
	return docsubpage .. "\n" .. shortcuts .. introdoc .. "\n" .. usagedoc
end

function export.infldoc(args)
	args = require("Module:table").shallowcopy(args)
	args.sgdesc = args.sgdesc or (args.art or "the") .. " " ..
		rsub(template_name(), " of$", "") .. (args.form and " " .. args.form or "")
	args.pldesc = args.sgdesc
	args.sgdescof = args.sgdescof or args.sgdesc .. " of"
	args.primaryentrytext = args.primaryentrytext or "of a primary entry"
	return export.fulldoc(args)	
end

local tag_type_to_description = {
	-- If not listed, we just capitalize the first letter
	["tense-aspect"] = "Tense/aspect",
	["voice-valence"] = "Voice/valence",
	["comparison"] = "Degrees of comparison",
	["class"] = "Inflectional class",
	["sound change"] = "Sound changes",
	["grammar"] = "Misc grammar",
	["other"] = "Other tags",
}

local tag_type_order = {
	"person",
	"number",
	"gender",
	"animacy",
	"tense-aspect",
	"mood",
	"voice-valence",
	"non-finite",
	"case",
	"state",
	"comparison",
	"register",
	"deixis",
	"clusivity",
	"class",
	"attitude",
	"sound change",
	"grammar",
	"other",
}

local function sort_by_first(namedata1, namedata2)
	return namedata1[1] < namedata2[1]
end

function export.tagtable()
	m_data = mw.loadData("Module:form of/data")
	m_data2 = mw.loadData("Module:form of/data2")
	m_form_of = require("Module:form of")

	local function organize_data(data_module)
		local tab = {}
		for name, data in pairs(data_module.tags) do
			if not data.tag_type then
				-- Throw an error because hopefully it will get noticed and fixed.
				-- If we just skip it, it may never get fixed.
				error("Tag '" .. name .. "' has no tag_type")
			end
			if not tab[data.tag_type] then
				tab[data.tag_type] = {}
			end
			table.insert(tab[data.tag_type], {name, data})
		end
		local tag_type_order_set = require("Module:table").listToSet(tag_type_order)
		for tag_type, tags_of_type in pairs(tab) do
			if not tag_type_order_set[tag_type] then
				-- See justification above for throwing an error.
				error("Tag type '" .. tag_type .. "' not listed in tag_type_order")
			end
			table.sort(tags_of_type, sort_by_first)
		end
		local multitag_shortcuts = {}
		local list_shortcuts = {}
		local function get_display_form(tags)
			local normtags = m_form_of.normalize_tags(tags)
			local display_forms = {}
			for _, normtag in ipairs(normtags) do
				table.insert(display_forms, m_form_of.get_tag_display_form(normtag))
			end
			return table.concat(display_forms, " ")
		end

		for shortcut, full in pairs(data_module.shortcuts) do
			if type(full) == "table" then
				table.insert(list_shortcuts, {shortcut, get_display_form(full)})
			elseif full:find("//") then
				table.insert(multitag_shortcuts, {shortcut, get_display_form({full})})
			end
		end

		table.sort(list_shortcuts, sort_by_first)
		table.sort(multitag_shortcuts, sort_by_first)

		return tab, multitag_shortcuts, list_shortcuts
	end

	local data_tab, data_multitag_shortcuts, data_list_shortcuts = organize_data(m_data)
	local data2_tab, data2_multitag_shortcuts, data2_list_shortcuts = organize_data(m_data2)

	local parts = {}

	local function insert_group(group)
		for _, namedata in ipairs(group) do
			local sparts = {}
			local name = namedata[1]
			local data = namedata[2]
			table.insert(sparts, "| <code>" .. name .. "</code> || ")
			if data.shortcuts then
				local ssparts = {}
				for _, shortcut in ipairs(data.shortcuts) do
					table.insert(ssparts, "<code>" .. shortcut .. "</code>")
				end
				table.insert(sparts, table.concat(ssparts, ", ") .. " ")
			end
			table.insert(sparts, "|| " .. m_form_of.get_tag_display_form(name))
			table.insert(parts, "|-")
			table.insert(parts, table.concat(sparts))
		end
	end

	local function insert_shortcut_group(shortcuts)
		for _, namedisp in ipairs(shortcuts) do
			local name = namedisp[1]
			local disp = namedisp[2]
			table.insert(parts, "|-")
			table.insert(parts, "| || <code>" .. name .. "</code> || " .. disp)
		end
	end

	table.insert(parts, '{|class="wikitable"')
	table.insert(parts, "! Canonical tag !! Shortcut(s) !! Display form")
	for _, tag_type in ipairs(tag_type_order) do
		local group_tab = data_tab[tag_type]
		if group_tab then
			table.insert(parts, "|-")
			table.insert(parts, '! colspan="3" style="text-align: center; background: #dddddd;" | ' ..
				(tag_type_to_description[tag_type] or m_form_of.ucfirst(tag_type)) .. " (more common)")
			insert_group(group_tab)
		end
		group_tab = data2_tab[tag_type]
		if group_tab then
			table.insert(parts, "|-")
			table.insert(parts, '! colspan="3" style="text-align: center; background: #dddddd;" | ' ..
				(tag_type_to_description[tag_type] or m_form_of.ucfirst(tag_type)) .. " (less common)")
			insert_group(group_tab)
		end
	end
	if #data_multitag_shortcuts > 0 then
		table.insert(parts, "|-")
		table.insert(parts, '! colspan="3" style="text-align: center; background: #dddddd;" | Multitag shortcuts (more common)')
		insert_shortcut_group(data_multitag_shortcuts)
	end
	if #data2_multitag_shortcuts > 0 then
		table.insert(parts, "|-")
		table.insert(parts, '! colspan="3" style="text-align: center; background: #dddddd;" | Multitag shortcuts (less common)')
		insert_shortcut_group(data2_multitag_shortcuts)
	end
	if #data_list_shortcuts > 0 then
		table.insert(parts, "|-")
		table.insert(parts, '! colspan="3" style="text-align: center; background: #dddddd;" | List shortcuts (more common)')
		insert_shortcut_group(data_list_shortcuts)
	end
	if #data2_list_shortcuts > 0 then
		table.insert(parts, "|-")
		table.insert(parts, '! colspan="3" style="text-align: center; background: #dddddd;" | List shortcuts (less common)')
		insert_shortcut_group(data2_list_shortcuts)
	end
	table.insert(parts, "|}")
	return table.concat(parts, "\n")
end

function export.postable()
	m_pos = mw.loadData("Module:form of/pos")
	local shortcut_tab = {}
	for shortcut, full in pairs(m_pos) do
		if not shortcut_tab[full] then
			shortcut_tab[full] = {}
		end
		table.insert(shortcut_tab[full], shortcut)
	end
	local shorcut_list = {}
	for full, shortcuts in pairs(shortcut_tab) do
		table.sort(shortcuts)
		table.insert(shorcut_list, {full, shortcuts})
	end
	table.sort(shorcut_list, function(fs1, fs2) return fs1[1] < fs2[1] end)

	local parts = {}
	table.insert(parts, '{|class="wikitable"')
	table.insert(parts, "! Canonical part of speech !! Shortcut(s)")
	for _, full_shortcuts in ipairs(shorcut_list) do
		local full = full_shortcuts[1]
		local shortcuts = full_shortcuts[2]
		table.insert(parts, "|-")
		local sparts = {}
		for _, shortcut in ipairs(shortcuts) do
			table.insert(sparts, "<code>" .. shortcut .. "</code>")
		end
		table.insert(parts, "| <code>" .. full .. "</code> || " .. table.concat(sparts, ", "))
	end
	table.insert(parts, "|}")
	return table.concat(parts, "\n")
end

function export.cattable()
	local m_cats = mw.loadData("Module:form of/cats")
	local cats_by_lang = {}
	local function find_categories(catstruct)
		local cats = {}
		
		local function process_spec(spec)
			if type(spec) == "string" then
				table.insert(cats, spec)
				return
			elseif not spec or spec == true then
				return
			elseif type(spec) ~= "table" then
				error("Wrong type of condition " .. spec .. ": " .. type(spec))
			end
			local predicate = spec[1]
			if predicate == "multi" or predicate == "cond" then
				-- WARNING! #spec doesn't work for objects loaded from loadData()
				for i, sp in ipairs(spec) do
					if i > 1 then
						process_spec(sp)
					end
				end
			elseif predicate == "pexists" then
				process_spec(spec[2])
				process_spec(spec[3])
			elseif predicate == "has" or predicate == "hasall" or predicate == "hasany" or
				predicate == "tags=" or predicate == "p=" or predicate == "pany" or
				predicate == "not" then
				process_spec(spec[3])
				process_spec(spec[4])
			elseif predicate == "and" or predicate == "or" then
				process_spec(spec[3])
				process_spec(spec[4])
			elseif predicate == "call" then
				return
			else
				error("Unrecognized predicate: " .. predicate)
			end
		end
		
		for _, spec in ipairs(catstruct) do
			process_spec(spec)
		end
		return cats
	end

	for lang, catspecs in pairs(m_cats) do
		local cats = find_categories(catspecs)
		table.insert(cats_by_lang, {lang, cats})
	end
	table.sort(cats_by_lang, sort_by_first)
	
	local lang_independent_cat_index = nil
	for i, langcats in ipairs(cats_by_lang) do
		local lang = langcats[1]
		if lang == "und" then
			lang_independent_cat_index = i
			break
		end
	end
	if lang_independent_cat_index then
		local lang_independent_cats = table.remove(cats_by_lang, lang_independent_cat_index)
		table.insert(cats_by_lang, 1, lang_independent_cats)
	end
	
	local parts = {}	
	table.insert(parts, '{|class="wikitable"')

	for i, langcats in ipairs(cats_by_lang) do
		local langcode = langcats[1]
		local cats = langcats[2]
		if #cats > 0 then
			if i > 1 then
				table.insert(parts, "|-")
			end
			if langcode == "und" then
				table.insert(parts, '! style="text-align: center; background: #dddddd;" | Language-independent')
			else
				local lang = m_languages.getByCode(langcode) or error("Unrecognized language code: " .. langcocde)
				table.insert(parts, '! style="text-align: center; background: #dddddd;" | ' .. lang:getCanonicalName())
			end
			for _, cat in ipairs(cats) do
				table.insert(parts, "|-")
				table.insert(parts, "| <code>" .. cat .. "</code>")
			end
		end
	end
	table.insert(parts, "|}")
	return table.concat(parts, "\n")
end

return export

-- For Vim, so we get 4-space tabs
-- vim: set ts=4 sw=4 noet: