|
|
| 第16行: |
第16行: |
| local parse = require('模块:署名解析') | | local parse = require('模块:署名解析') |
| local norm = parse.norm | | local norm = parse.norm |
|
| |
| -- ===== 两段式名义表(映射页新结构: 主名义 / 合作名义)=====
| |
| -- 新结构下: 署名整串查表 → 归并到主名义; 查不到则原样。旧结构(平表)保持原逻辑,便于平滑切换。
| |
| local function buildLookup(data)
| |
| if type(data) ~= 'table' or type(data['主名义']) ~= 'table' then return nil end
| |
| local alias, collab, extra = {}, {}, {}
| |
| for _, m in pairs(data['主名义']) do -- 第一趟: 显示名/写法
| |
| if type(m) == 'table' and m['显示名'] then
| |
| local dn = m['显示名']
| |
| extra[parse.norm(dn)] = m
| |
| alias[parse.norm(dn)] = dn
| |
| for _, w in ipairs(m['_写法'] or {}) do alias[parse.norm(w)] = dn end
| |
| end
| |
| end
| |
| for _, m in pairs(data['主名义']) do -- 第二趟: 马甲优先(后写覆盖)
| |
| if type(m) == 'table' and m['显示名'] then
| |
| for _, a in ipairs(m['马甲'] or {}) do alias[parse.norm(a)] = m['显示名'] end
| |
| end
| |
| end
| |
| for _, u in pairs(data['未确认名义'] or {}) do -- 第三段: 主名义未确认的马甲
| |
| if type(u) == 'table' and u['名义'] then
| |
| local dn = u['名义']
| |
| local e = { ['显示名'] = dn, ['马甲'] = {}, ['_未确认'] = true }
| |
| extra[parse.norm(dn)] = e
| |
| alias[parse.norm(dn)] = dn
| |
| end
| |
| end
| |
| for _, c in pairs(data['合作名义']) do
| |
| if type(c) == 'table' and c['名义'] then
| |
| collab[parse.norm(c['名义'])] = c['组合'] or {}
| |
| end
| |
| end
| |
| return { alias = alias, collab = collab, extra = extra }
| |
| end
| |
|
| |
| -- 把一个署名原串解析成归属列表: 合作名义 → 每位成员; 马甲 → 主名义(备注里标出名义); 其余 → 原样
| |
| local function attribute(lk, raw)
| |
| if not lk then return nil end
| |
| local key = parse.norm(raw)
| |
| local c = lk.collab[key]
| |
| if c then
| |
| local out = {}
| |
| for _, m in ipairs(c) do out[#out + 1] = { base = m, ann = '', other = c } end
| |
| return out
| |
| end
| |
| local m = lk.alias[key]
| |
| if m then
| |
| -- 原名与显示名一致时不加「名义」备注
| |
| local ann = (parse.norm(raw) == parse.norm(m)) and '' or ('名义:' .. raw)
| |
| return { { base = m, ann = ann } }
| |
| end
| |
| return { { base = raw, ann = '' } }
| |
| end
| |
|
| |
| -- ===== 工具 =====
| |
| local function esc(s)
| |
| return (tostring(s or ''):gsub('|', '|'))
| |
| end
| |
|
| |
| -- 链接目标: # 是 MediaWiki 的锚点分隔符, 标题里一律换成全角 #
| |
| -- 优先用「链接名」字段; 没有就回退到曲名并自动替换 (与 模块:曲目列表 保持一致)
| |
| local function pageName(song, suffix)
| |
| local n = song['链接名'] or song['曲名']
| |
| if suffix then
| |
| n = n .. '(' .. suffix .. ')'
| |
| end
| |
| return (n:gsub('#', '#'))
| |
| end
| |
|
| |
|
| |
| -- 排序分组: 0=符号/数字 1=拉丁 2=假名 3=汉字 4=其他
| |
| local function groupRank(name)
| |
| local cp = mw.ustring.codepoint(name, 1, 1)
| |
| if not cp then return 9 end
| |
| if (cp >= 0x30 and cp <= 0x39) or cp < 0x30 then return 0 end
| |
| if (cp >= 0x41 and cp <= 0x5A) or (cp >= 0x61 and cp <= 0x7A) then return 1 end
| |
| if (cp >= 0x3040 and cp <= 0x30FF) then return 2 end
| |
| if (cp >= 0x3400 and cp <= 0x9FFF) then return 3 end
| |
| return 4
| |
| end
| |
|
| |
| local function langWrap(song)
| |
| local lang = song['lang']
| |
| if lang == 'ja' then
| |
| return '{{lang|ja|' .. esc(song['曲名']) .. '}}'
| |
| elseif lang == 'zh-Hant' then
| |
| return '{{lang|zh-Hant|' .. esc(song['曲名']) .. '}}'
| |
| end
| |
| return esc(song['曲名'])
| |
| end
| |
|
| |
|
| local function loadJson(title) | | local function loadJson(title) |
| 第157行: |
第67行: |
| if not data then return '<span class="error">数据页错误</span>' end | | if not data then return '<span class="error">数据页错误</span>' end |
| local map = loadJson(MAP_TITLE) or {} | | local map = loadJson(MAP_TITLE) or {} |
| local lk = buildLookup(map) | | local lk = parse.buildLookup(map) |
| local roles = loadJson('模板:曲师参与.json') or {} | | local roles = loadJson('模板:曲师参与.json') or {} |
| local sets, display = {}, {} | | local sets, display = {}, {} |
| 第166行: |
第76行: |
| local raw = song['曲师'] | | local raw = song['曲师'] |
| if type(raw) == 'table' then raw = raw[1] end | | if type(raw) == 'table' then raw = raw[1] end |
| local parts = attribute(lk, raw) or parse.parts(raw) | | local parts = parse.attribute(lk, raw) or parse.parts(raw) |
| local seen = {} | | local seen = {} |
| for _, pt in ipairs(parts) do | | for _, pt in ipairs(parts) do |
| 第232行: |
第142行: |
| local map = loadJson(MAP_TITLE) or {} | | local map = loadJson(MAP_TITLE) or {} |
| local roles = loadJson('模板:曲师参与.json') or {} -- 参与(作曲/作词/人声…):人工知识,并入备注 | | local roles = loadJson('模板:曲师参与.json') or {} -- 参与(作曲/作词/人声…):人工知识,并入备注 |
| local lk = buildLookup(map) | | local lk = parse.buildLookup(map) |
| local intros = loadIntros() | | local intros = loadIntros() |
|
| |
|
| 第240行: |
第150行: |
| local raw = song['曲师'] | | local raw = song['曲师'] |
| if type(raw) == 'table' then raw = raw[1] end | | if type(raw) == 'table' then raw = raw[1] end |
| local parts = attribute(lk, raw) or parse.parts(raw) | | local parts = parse.attribute(lk, raw) or parse.parts(raw) |
| local seen = {} | | local seen = {} |
| for _, pt in ipairs(parts) do | | for _, pt in ipairs(parts) do |