--[=[
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: