跳转到内容

模組:Vgtn

本页使用了标题或全文手工转换
维基百科,自由的百科全书

require("strict")
local getArgs = require("Module:Arguments").getArgs
local p = {}

local rule_data = require("Module:Vgtn/data")

local function urlencode(text)
	-- Return equivalent of {{urlencode:text}}.
	local function byte(char)
		return string.format('%%%02X', string.byte(char))
	end
	return text:gsub('[^%w%-._]', byte)
end

local function expand_template(title, args)
    --[==[展开维基模板。
    
    :param title: 模板名。
    :param args: 参数表。
    :return: 解析后的维基模板代码。
    
    示例
    ----
    
    >>> expand_template("lang", { "en", "test" })
    {{lang|en|text}}

    >>> expand_template("vgname", {cn="最终幻想", tw="Final Fantasy"})
    {{vgname|cn=最终幻想|tw=Final Fantasy}}
    ]==]

    return mw.getCurrentFrame():expandTemplate {title = title, args = args}
end

local function unnest_table(data)
    --[==[將嵌套的定義規則扁平化。
    
    :param data: 雙層嵌套的表。
    :return: 扁平化後的資料。
    ]==]

    local rules = {}
    for _, group in ipairs(data) do
        for _, subgroup in ipairs(group) do
            for _, rule in ipairs(subgroup) do
                table.insert(rules, rule)
            end
        end
    end
    return rules
end

local function split_interwiki(link)
    --[==[根据首个西文冒号,将跨语言连接拆分为语言代码和页面标题。不含冒号时返回nil。
    
    :param link: 跨语言连接。
    :return: 语言代码和页面名。
    
    示例
    ----
    
    >>> split_interwiki("en:Test")
    en, Test
    
    >>> split_interwiki('Test')
    nil, nil
    ]==]

    link = link or ""
    local pattern = "^([^:]+):(.-)$"
    local _, _, iw_code, iw_title = mw.ustring.find(link, pattern)
    
    return iw_code, iw_title
end

local function get_first_rule(rules, original, lang, dab)
    --[==[根据原文名、语言代码、重复项目标记,从译名规则表中获取符合条件的首条规则。
    若无匹配项,则返回只有原文名(和语言代码)而无译名定义的空规则。

    :param rules: 由译名规则构成的表。
    :param original: 所查询的原文名。
    :param lang: 原文名语言代码。缺省时返回匹配其他条件的首条规则。
    :param dab: 重复原名的「消歧义」标签值,缺省时返回匹配其他条件的首条规则。
    :return: 一个译名定义规则表。

    示例
    ----

    >>> local rules = {
    ...     { 'Arend Jacobs', lang = 'de', cn = '阿伦德·雅各布斯', comment = "德国网站das Computerspiel评论员" },
    ...     { 'Arend Jacobs', lang = 'en', cn = '阿伦·雅各布斯', dab = 'hmf', comment="HFM网站编辑" },
    ...     { 'Arend Jacobs', lang = 'en', cn = '亚伦·雅各布斯' dab = 'movie', comment="电影评论员兼评论游戏" },
    ... }

    >>> get_first_rule(rules, 'Arend Jacobs', 'en', 'movie')
    { 'Arend Jacobs', lang = 'en', cn = '亚伦·雅各布斯' dab = 'movie', comment="电影评论员兼评论游戏" },
    
    >>> get_first_rule(rules, 'Arend Jacobs')  -- 首项出现'Arend Jacobs'的结果
    { 'Arend Jacobs', lang = 'de', cn = '阿伦德·雅各布斯', comment = "德国网站das Computerspiel评论员" },
    
    >>> get_first_rule(rules, 'Arend Jacobs', 'en')  -- 首次出现英文'Arend Jacobs'的结果
    { 'Arend Jacobs', lang = 'en', cn = '阿伦·雅各布斯', dab = 'hmf', comment="HFM网站编辑" },
    
    >>> get_first_rule(rules, 'Arend Jacobs', 'fr')  -- 查无法语名稱的定义,返回空规则
    { 'Arend', lang = 'fr' },
    
    >>> get_first_rule(rules, 'Kamisato Ayaka') -- 查无此原名,返回空规则
    { 'Kamisato Ayaka' },
    ]==]

    for _, rule in ipairs(rules) do
        if (original == rule[1]) and (lang == nil or lang == rule.lang) and (dab == nil or lang == rule.dab) then
            return rule
        end
    end
    return {original, lang = lang, dab = dab}
end

local function get_part_name(name, part, sep)
    --[==[提取姓名的指定部分。主要用于处理地区词转换时,只需要写姓氏的问题。

    :param name: 待提取的姓名全名。
    :param part: 所提取的部分索引(負數則表示逆序),为`nil`或0时返回全名。预设为`nil`。
    :param sep: 分隔符。预设为中文间隔号('·'),但若姓名中不含间隔号时为空格(' ')。
    :return: 所提取的姓名片段。

    示例
    ----

    >>> get_part_name('杰里米·帕里什', 2)
    帕里什
    >>> get_part_name('John D. Carmack', 3, ' ') -- 省略第三参数亦可得出结果
    Carmack
    ]==]

    if name == nil or part == nil or part == 0 then
        return name
    end
    local split_result
    if sep then
        split_result = mw.text.split(name, sep, true)
    elseif mw.ustring.find(name, "·") then
        split_result = mw.text.split(name, "·", true)
    else
        split_result = mw.text.split(name, " ", true)
    end
    if part > #split_result or part < -(#split_result) then
        return name
    elseif part > 0 then
        return split_result[part]
    else
        return split_result[#split_result + part + 1]
    end
end

local function parse_translation(rule, part, sep)
    --[==[将译名定义规则中译名部分解释为维基代码,可根据分隔符取姓名片段。

    若简体(`cn`、`sg`、`my`)和繁体(`tw`、`hk`、`mo`)两类译名均有定义,则返回内联转换代码`-{zh-xx:...;}-`。
    若仅定义其中一种书写体系下的译名,则返回普通文字。
    若未定义任何中文译名,则返回nil。

    :param rule: 译名定义规则。
    :param part: 所提取的姓名片段编号。例如对于译名'约翰·D·卡马克',可输入3仅列出'卡马克'。
    :param sep: 片段的分隔符。预设为中文间隔号('·'),但若姓名中不含间隔号时为空格(' ')。
    :return: 译名规则中译名部分对应的维基代码,以及其中一個中文譯名(用於生成頁面連結)。

    示例
    ----

    >>> rule = { 'Jeremy Parish', cn = '杰里米·帕里什', hk = '謝洛美·巴里殊', tw = '傑瑞米·派瑞許' }
    >>> parse_translation(rule)
    -{zh-cn:杰里米·帕里什; zh-hk:謝洛美·巴里殊; zh-tw:傑瑞米·派瑞許;}-
    >>> parse_translation(rule, 2)
    -{zh-cn:帕里什; zh-hk:巴里殊; zh-tw:派瑞許;}-

    >>> -- 仅定义一种译名,不启用内联字词转换
    >>> local rule = { 'John Carmack', lang='en', cn = '约翰·D·卡马克' }
    >>> parse_translation(rule)
    约翰·D·卡马克
    >>> parse_translation(rule, 3)
    卡马克

    >>> -- 未定义简繁两种体系下的译名,故依然无法地区词转换,只生成了其中一种译名的普通文本。
    >>> local rule = { 'Jeremy Parish', hk = '謝洛美·巴里殊', tw = '傑瑞米·派瑞許' }
    >>> parse_translation(rule)
    傑瑞米·派瑞許

    >>> -- 完全未定义中文译名
    >>> rule = { 'Jeremy Parish' }
    >>> parse_translation(rule)
    nil
    ]==]

    local varieties = {}
    local hant_variety_names, hant_flag = {"tw", "hk", "mo"}, false
    local hans_variety_names, hans_flag = {"cn", "sg", "my"}, false
    for _, v in ipairs(hant_variety_names) do
        if rule[v] then
            table.insert(varieties, {label = "zh-" .. v, part = get_part_name(rule[v], part, sep), full = rule[v]})
            hant_flag = true
        end
    end
    for _, v in ipairs(hans_variety_names) do
        if rule[v] then
            table.insert(varieties, {label = "zh-" .. v, part = get_part_name(rule[v], part, sep), full = rule[v]})
            hans_flag = true
        end
    end

    if #varieties == 0 then
        return nil, nil
    end
    if not (hans_flag and hant_flag) then
        return varieties[1].part, varieties[1].full
    end

    local result_fragments = {}
    -- 内联转换定义按变种代码列出(强迫症)
    table.sort(varieties, function(a, b) return a.label < b.label end)
    for _, variety in ipairs(varieties) do
        table.insert(result_fragments, variety.label .. ":" .. variety.part .. ";")
    end
    local result = table.concat(result_fragments, " ")
    result = "-{" .. result .. "}-"
    return result, varieties[1].full
end

local function parse_original(rule, part, sep)
    --[==[将译名定义规则中的原名部分解释为维基代码,可根据分隔符取姓名片段。
    若定义原名的语言(`lang`),则套用{{lang}}模板生成语义规范的外文名。

    :param rule: 译名定义规则。
    :param part: 所提取的姓名片段编号。例如对于原名'John D. Carmack',输入3可仅提取'Carmack'。
    :param sep: 片段的分隔符。预设为中文间隔号('·'),但若姓名中不含间隔号时为空格(' ')。
    :return: 译名规则中原名部分对应的维基代码,以及不加語文標籤的純文字原名。

    示例
    ----

    >>> local rule = {'John D. Carmack', lang='en', cn='约翰·D·卡马克'}
    >>> parse_original(rule)
    {{lang|en|John D. Carmack}}
    >>> parse_translation(rule, 3)
    {{lang|en|Carmack}}

    >>> -- 未指定语言代码
    >>> rule = {'浜崎あゆみ', tw='濱崎步'}
    >>> parse_translation(rule) 
    浜崎あゆみ
    ]==]

    local original, lang = get_part_name(rule[1], part, sep), rule.lang
    if lang == nil then
        return original, rule[1]
    end
    return expand_template("Lang", {lang, original}), rule[1]
end

local function build_illm_remark_text(target, linked_text, remark, interwiki, wikidata)
    --[==[根据链接目标、链接文字、原文名、跨语言链接、Wikidata数据项构筑{{Interlanguage link multi}}模板并括号加注原文。

    :param target: 本地页面名链接。
    :param linked_text: 管道链接文字。
    :param remark: 需要括号附注的文字。
    :param interwiki: 跨语言连接。
    :param wikidata: Wikidata数据项。
    :return: 生成的维基代码。

    示例
    ----

    >>> build_illm_remark_text("姜聲宇", "姜聲宇", "강성우", "ko:강성우 (성우)", "Q122808535")
    {{Interlanguage link multi|姜聲宇|ko|강성우 (성우)|WD=Q122808535|lt=姜聲宇}}(-{강성우}-)
    ]==]

    local result
    local iw_code, iw_title = split_interwiki(interwiki)
    if iw_title or wikidata then
        local template_args = {target, iw_code, iw_title, WD=wikidata, lt=linked_text, ["vertical-align"]="sup"}
        result = expand_template('Interlanguage link multi', template_args)
    elseif linked_text ~= target then
        result = "[[" .. target .. "|" .. linked_text .. "]]"
    else
        result = "[[" .. target .. "]]"
    end
    result = result .. (remark and ('(' .. remark .. ')') or '')

    return result
end

local function build_redlink_remark_text(target, linked_text, remark)
    --[==[根据链接目标、链接文字、原文名生成链接文字及附注。

    所有参数均为可选值。
    如果指定链接目标,则生成内部链接,且指定链接文字时生成管道链接;
    若未指定链接目标,则以链接文字生成普通文字。
    若指定原文名附注,则在链接后追加此标注。

    :param target: 本地页面名链接。
    :param linked_text: 管道链接文字。
    :param remark: 需要括号附注的文字。
    :return: 生成的维基代码。
    ]==]

    linked_text = linked_text or target

    local result_fragments = {}
    if target then
        table.insert(result_fragments, "[[")
        table.insert(result_fragments, target)
        if linked_text ~= target then
            table.insert(result_fragments, "|")
            table.insert(result_fragments, linked_text)
        end
        table.insert(result_fragments, "]]")
    else
        table.insert(result_fragments, linked_text)
    end
    if remark and remark ~= target then
        table.insert(result_fragments, "(")
        table.insert(result_fragments, remark)
        table.insert(result_fragments, ")")
    end
    return table.concat(result_fragments)
end

local function parse_rule(rule, part, sep, style, link)
    --[==[将译名定义规则解析为维基代码

    :param rule: 译名定义规则。
    :param part: 所提取的姓名片段编号。例如对于译名'约翰·D·卡马克',可输入3仅列出'卡马克'。
    :param sep: 片段的分隔符。预设为中文间隔号('·'),但若姓名中不含间隔号时为空格(' ')。
    :param style: 生成的维基代码格式:
        'link'或'redlink' — 生成普通内部链接;
        'remark' — 生成无链接译文,并括号加注原文;
        'link-remark'或'redlink-remark' — 以普通内部链接语法链接文本,并括号加注原文;
        'iw'、'ilh'、'tsl'或'greenlink' — 当译名规则定义`iw`时,则生成绿色链接,否则为'link-remark'效果;
        'illm'或'ill' — 生成{{illm}}式跨语言连接;维基数据定义(wd)优先于跨语言连接定义(iw),二者均未定义则改用普通连接;
        'illm-remark'或'ill-remark' — 生成{{illm}}式跨语言连接,并括号加注原文;
        其他情况 — 无链接译文(预设值)。
    :param link: 自定本地链接目标。
    :return: 译名规则对应的维基代码。

    示例
    ----

    >>> local rule = {
    ...    'Jeremy Parish', lang='en', 
    ...    cn='杰里米·帕里什', hk='謝洛美·巴里殊', tw='傑瑞米·派瑞許',
    ...    iw='en:Jeremy Parish (writer)'
    ... }
    
    >>> parse_rule(rule)
    -{zh-cn:杰里米·帕里什; zh-hk:謝洛美·巴里殊; zh-tw:傑瑞米·派瑞許;}-
    
    >>> parse_rule(rule, 2)
    -{zh-cn:帕里什; zh-hk:巴里殊; zh-tw:派瑞許;}-
    
    >>> parse_rule(rule, nil, nil, 'link-remark')
    [[杰里米·帕里什|-{zh-cn:杰里米·帕里什; zh-hk:謝洛美·巴里殊; zh-tw:傑瑞米·派瑞許;}-]]({{lang|en|Jeremy Parish}})
    
    >>> parse_rule(rule, -1, nil, 'ilh')
    {{link-en|杰里米·帕里什|Jeremy Parish (writer)|-{zh-cn:帕里什; zh-hk:巴里殊; zh-tw:派瑞許;}-}}
    
    >>> parse_rule(rule, -1, nil, 'ilh', '杰里米·帕里什 (评论员)')
    {{link-en|杰里米·帕里什 (评论员)|Jeremy Parish (writer)|-{zh-cn:帕里什; zh-hk:巴里殊; zh-tw:派瑞許;}-}}
    ]==]

    local translation, local_title = parse_translation(rule, part, sep, link)
    local remark, original = parse_original(rule, part, sep)  -- original即rule[1]
    local_title = link or rule.link or local_title or original -- 優先級:自定連結 > 規則連結 > 譯文 > 原文
    translation = translation or original

    if (style == "illm-remark") or (style == "ill-remark") then
        if translation ~= remark then
            return build_illm_remark_text(local_title, translation, remark, rule.iw, rule.wd)
        end
        return build_illm_remark_text(local_title, translation, nil, rule.iw, rule.wd)
    end

    if (style == "illm") or (style == "ill") then
        return build_illm_remark_text(local_title, translation, nil, rule.iw, rule.wd)
    end

    if (style == "iw") or (style == "ilh") or (style == "tsl") or (style == "greenlink") then
        local iw_code, iw_title = split_interwiki(rule.iw)
        if iw_code then
            return expand_template("Link-" .. iw_code, {local_title, iw_title, translation})
        end
        return build_redlink_remark_text(local_title, translation, original)
    end

    if (style == "link-remark") or (style == "redlink-remark") then
        if translation ~= remark then
            return build_redlink_remark_text(local_title, translation, remark)
        end
        return build_redlink_remark_text(local_title, translation)
    end

    if style == "remark" then
        if translation ~= remark then
            return build_redlink_remark_text(nil, translation, remark)
        end
        return build_redlink_remark_text(nil, translation)
    end

    if (style == "link") or (style == "redlink") then
        return build_redlink_remark_text(local_title, translation)
    end

    return translation
end

local function build_wikitable(data)
    --[==[將定義規則轉換為易讀的維基表格。
    
    :param data: 定義規則,結構為雙層嵌套表。
    :result: 維基代碼,由若干二級目錄、三級目錄和維基表格構成。
    ]==]

    local result_frags = {}

    for _, group in ipairs(data) do
        table.insert(result_frags, "==" .. group.name .. "==")
        for _, subgroup in ipairs(group) do
            table.insert(result_frags, "===" .. subgroup.name .. "===")
            table.insert(result_frags, '{| class="wikitable sortable"')
            table.insert(result_frags, '! scope="col" | 原名')
            table.insert(result_frags, '! scope="col" | 中文譯名')
            table.insert(result_frags, '! scope="col" | 地區詞轉換')
            table.insert(result_frags, '! scope="col" | 備注')
            for _, rule in ipairs(subgroup) do
                local original_lang, original = parse_original(rule)
                local lang_code = ""
                if rule.lang then
                	lang_code = '<code style="margin-left: 0.2em; margin-right: 0.2em; font-size: smaller;">' .. rule.lang .. "</code>"
                	if rule.namu then
                		lang_code = lang_code .. '<code style="margin-left: 0.2em; margin-right: 0.2em; font-size: smaller;">[https://namu.wiki/w/' .. urlencode(rule.namu) .. " " .. rule.namu .. "]</code>"
                	end
                end
                local dab_code =
                    rule.dab and ('<code style="margin-left: 0.2em; margin-right: 0.2em; font-size: smaller;">dab=' .. rule.dab .. "</code>") or ""
                local translation, local_title = parse_translation(rule)
                translation = translation or ""
                local _, _, convert_code = mw.ustring.find(translation, "^%-{(.+)}%-$")
                if rule.hanja then
                	translation = translation .. '<span style="margin-left: 0.2em; font-size: smaller;">' .. rule.hanja .. "</span>"
            	end
                local comment = rule.comment or ""
                local original_link = original_lang  -- 原文帶lang模板
                if rule.iw or rule.link then
                    local_title = rule.link or local_title or original  -- 優先級:規則連結 > 譯文 > 原文
                    original_link = build_redlink_remark_text(local_title, original_lang)  -- 原文帶連結
                    if rule.iw then
                        local iw_code, iw_title = split_interwiki(rule.iw)
                        if iw_code then  -- 跨語言連結
                            original_link = expand_template("Link-" .. iw_code, {local_title, iw_title, original_lang})
                        end
                    end
                end
                table.insert(result_frags, "|-")
                table.insert(result_frags, '! scope="row" style="font-weight: normal; text-align: left;" | ' .. original_link .. lang_code .. dab_code)
                table.insert(result_frags, "| " .. translation)
                if convert_code then
                    table.insert(result_frags, '| <code style="font-size: smaller; letter-spacing: -0.05em;">' .. convert_code .. "</code>")
                else
                    table.insert(result_frags, "| ")
                end
                table.insert(result_frags, "| " .. comment)
            end -- rule
            table.insert(result_frags, "|}")
        end -- subgroup
    end -- group

    return table.concat(result_frags, "\n")
end

function p.main(frame)
    local args = getArgs(frame)
    return p._main(args)
end

function p._main(args)
    local rules = unnest_table(rule_data)
    local rule = get_first_rule(rules, args[1], args.lang, args.dab)
    return parse_rule(rule, tonumber(args.part), args.sep, args.style, args.link)
end

function p.wikitable(frame)
    local args = getArgs(frame)
    return p._wikitable(args)
end

function p._wikitable(args)
    return build_wikitable(rule_data)
end

return p