模組:書名
呢個模組已經被評為beta版,可以廣泛用。因為佢新近完成,請慎用,以確保輸出結果符合預期。 |
概要
[編輯]{{#invoke:書名 |Syu1meng2 [ |mode=chapter ] }}
{{#invoke:書名 |Zyun1ming4 [ |mode=dotted ] }}
{{#invoke:書名 |Zoek6zung6}}
p .auto_build_citable( 書名, 篇名 );
p .cvs( 值 );
p .ref( 值 );
p .cjk_p( 字串 );
p .array_p( 值 );
用途
[編輯]呢個模組係幾個模嘅真身,用來排一啲比較難處理嘅中文標點,目前處理嘅有書名號、專名號同着重號。模組嘅輸出儘量保持同文字式網頁瀏覽器同螢幕閱讀器嘅兼容。
模組嘅正常輸出係以印刷嘅常規為準,所以就算係排乙式書名號都嘗試會做 kerning,做出來嘅效果係好似書名號有半形版咁。呢種做法係類似標點(例如引號)嘅傳統排法[1]。模組本身並唔會做任何嘅格式化,但係會將輸入加結構同足夠嘅 class,等樣式表可以將輸出格式化。
模組會直接喺 frame 個阿媽(parent)度攞模嘅參數。例外嘅係一個入口(entry point)被兩個或以上模用嘅時候用來認邊個模用緊佢嘅參數會喺 frame 攞,目前呢種參數有書名號嘅 mode=chapter(篇名號模式)同專名號嘅 mode=dotted(翻譯人名模式)。
直接叫用
[編輯]其他模組載入呢個模組之後可以直接叫用 auto_build_citable 函數,呢個函數輸入一至兩個字串(可以係 nil),輸出字串,相等於 {{#invoke:書名|Syu1meng2}},但係輸出唔會
輔助函數
[編輯]其他模組可以載入呢個模組之後用幾個輔助函數,呢幾個係
- cvs:輸入任何值,輸出字串,係輸入值比較合理嘅字串版本(類似 PostScript 嘅 cvs 算子)
- cjk_p:輸入字串,輸出布林值,係輸入嘅字串似唔似全部係全形中日韓字
- array_p:輸入仼何值,輸出布林值,係輸入嘅值似唔似係陣列
另外仲有一個 ref 函數可以直接叫用,輸入仼何值,輸出字串,類似 type 算子嘅輸出,但係如果輸入睇落似一個陣列,輸出會係 「array」;如果似 parse_title(一個內部函數)嘅輸出,輸出會係 「parsed-title」。但係因為係唔係 parsed-title 其實係靠估,呢個函數可能唔係太有用。
本地化
[編輯]呢個模組係粵維嘅原創(原作者用開嘅 Javascript+PHP 譯做 Lua,再改到喺維基用到),唔係喺第個維基抄返來再本地化。
模組嘅編輯說明
[編輯]呢個模組整合咗落引模嘅模組(用來排中文書名)同官網模嘅模組度(用來檢查使唔使喺篇名後便加空格),所以呢個模死咗會整死埋引模同官網模,所以一定唔可以出大錯。發佈之前一定要用 「睇下改咗呢個模之後,用到個模嘅版嘅效果」 嘅功能試清楚冇問題先可以撳發佈掣。
個模組用來排版用嘅樣式表喺模組:書名/styles.css。改完樣式表之後要改埋引模嘅樣式表嘅對應部分。
睇埋
[編輯]- 模:書名
- 模:篇名
- 模:專名
- 模:着重
- 模組:Citation/CS1/Utilities,持別係 wrap_style 函數
- 模組:Citation/CS1/styles.css
- 模組:Official website,_main 函數
攷
[編輯]- ↑ 參見 。安屏翻譯。〈倫敦伯特拉印刷研究所概況〉。《藝文印刷月刊》。1 (6): 40–47。1931年6月1號。喺2022年10月11號搵到。
上面嘅解係穿透包含咗自模組:書名/doc。 (改 | 史) 編者可以響呢個模組嘅沙盤 (開 | 鏡)同埋試例 (開)版度試驗佢。 呢個模組嘅細版。 |
-- vi:set sw=4 ts=4 ai sm:
---- This module implements the backend logic for displaying the two types
---- of ˈsyˌmiŋˍhou (punctuation mark for indicating the title of a citable
---- work in Chinese languages)
---- It has also been modified to deal with ˍdzœkˍdzuŋˍhou (emphasis marks,
---- which unfortunately needs to be dealt with despite being a standard part
---- of CSS) and also some experimental support for ˈdzynˌmiŋˍhou (punctuation
---- mark for indicating personal and geographical names)
require ('strict');
local p = {};
local TYPE_1 = '甲';
local TYPE_2 = '乙';
--- Auxiliaries ---------------------------------------------------------------
-- Manually count elements, in case # doesn't work for some reason
local function count_elements( thing )
local it;
if type(thing) == 'table' then
it = 0;
for _, v in pairs(thing) do
it = it + 1;
end
end
return it;
end
-- Figure out if a thing is an array
local function array_p( thing )
local it = (type(thing) == 'table');
if it then
local i_min, i_max;
local n = 0;
for i, v in pairs(thing) do
if type(i) ~= 'number' then
it = false;
else
if i_min == nil or i < i_min then
i_min = i;
end
if i_max == nil or i > i_max then
i_max = i;
end
end
n = n + 1;
end
-- in PostScript array indexes start at 0, but in Lua they start at 1
-- in addition, Lua "sequences" cannot contain nils (ignoring for now)
-- We don't use #thing bc sometimes (e.g., frame.args) it doesn't work
if it and not (i_min and i_min == 1 and i_max == n) then
it = false; -- index has holes, not a real array
end
end
return it;
end
-- Stringify something into a form suitable for debugging and error messages
-- (cvs is the name of the Postscript operator that does this)
local function cvs( s )
if s == nil or s == false or s == true then
s= tostring(s);
elseif type(s) == 'string' then
s = '(' .. mw.ustring.gsub(s, '([()])', '\\%1') .. ')';
elseif type(s) == 'function' then
s = 'FUNCTION'; -- sigh
elseif type(s) == 'table' then
local s2 = '';
if array_p(s) then
local n = count_elements(s);
for i = 1, n, 1 do
if #s2 > 0 then
s2 = s2 .. ' ';
end
s2 = s2 .. cvs(s[i]);
end
s2 = '[' .. s2 .. ']';
else
for i, v in pairs(s) do
if #s2 > 1 then
s2 = s2 .. ' ';
end
if type(i) == 'string' and i:match('^[-%w_]+$') then
s2 = s2 .. '/' .. i;
else
s2 = s2 .. cvs(i) .. ' cvn';
end
s2 = s2 .. ' ' .. cvs(v);
end
s2 = '<<' .. s2 .. '>>';
end
s = s2;
end
return s;
end
-- Return a sanitized version of an argument value
local function sanitize( arg )
if arg ~= nil and arg:match('^{{{%w-}}}$') then
arg = nil
end
return arg;
end
-- If the passed string is a null string, turn it into an actual null
local function nullify( s )
if s == '' then
s = nil
end
return s;
end
-- Check if the given string is (believed to be) CJK
local function cjk_p( s )
return mw.ustring.match(s, '^['
.. '—' -- 2014 (em dash)
.. '…' -- 2026
.. '─' -- 2500 (line drawing)
.. '○' -- 25CB (circle [not zero])
.. '⺀-䶿' -- 2E80-4DBF
.. '一-鿿' -- 4E00-9FFF
.. '가-' -- AC00-D7AF
.. '豈-' -- F900-FAFF
.. '︰-﹏' -- FE30-FE4F
.. '!-⦆' -- FF01-FF60
.. '𠀀-' -- 20000-2FA1F
.. ']+$');
end
local function space_p( c )
return mw.ustring.match(c, '[ ]'); -- sp, CJK sp
end
local function kernable_left_punctuation_p( c )
return c and mw.ustring.match(c, '[《〈(【「『]');
end
local function kernable_right_punctuation_p( c )
return c and mw.ustring.match(c, '[》〉)】」』]');
end
local function left_kernable_narrow_punctuation_p( c )
return c and mw.ustring.match(c, '[,;:。!]');
end
local function right_kernable_narrow_punctuation_p( c )
return c and mw.ustring.match(c, '[、,;:。!]');
end
-- Canonicalize the type parameter
local function canonicalize_type_value( type )
if type == '1' or type == '甲' then
type = '甲'
elseif type == '2' or type == '乙' then
type = '乙'
end
return type;
end
-- Try to guess what REALLY is the thing, in the context of this module.
-- Like Lisp or Perl, Lua has no real concept of classes, but unlike Perl
-- there's no way to "bless" an array (table in Lua) into a class.
-- So everything is just a generic table - a most non-ideal situation.
-- (ref is the name of the Perl operator that does this)
local function ref( thing )
local it = type(thing);
if thing == nil then
it = nil
elseif it == 'table' then
-- Are we looking at a parsed title?
if thing.label and type(thing.label) == 'string' then
it = 'parsed-title';
-- Are we looking at an array?
elseif array_p(thing) then
it = 'array'
end
end
return it;
end
-- Analyze the title and see if it's a link of some kind
local function parse_title( s )
local it = s;
local t = ref(s);
if t == 'string' then
local label, link, url;
local bold, italic, tmp;
bold, tmp = mw.ustring.match(s, "^(''')(.*)%1$");
if bold then
s = tmp;
end
italic, tmp = mw.ustring.match(s, "^('')(.*)%1$");
if italic then
s = tmp;
end
link, label = mw.ustring.match(s, '^%[%[%s*([^%[][^%[]*)%s*|%s*([^%[%]]*)%s*%]%]$');
if link then
if label and #label == 0 then
label = link;
end
else
link = mw.ustring.match(s, '^%[%[([^%[]+)%]%]$');
if link then
label = link;
else
url, label = mw.ustring.match(s, '^%[(%S+)%s([^%]]+)%]$');
if not url then
label = s;
end
end
end
-- Silent change a few things to make it easier to typeset
label = mw.ustring.gsub(label, '[─]', '—'); -- U+2500 line drawing
label = mw.ustring.gsub(label, '[○]', '〇'); -- U+25CB circle
label = mw.ustring.gsub(label, '⸺', '——'); -- 2-em dash -0> 2x em dash'
it = {
['label'] = label;
['link'] = link;
['url'] = url;
['bold'] = bold;
['italic'] = italic;
};
elseif t and t ~= 'parsed-title' then
error('parse-title got unexpected argument '..cvs(s) .. ' (t=' .. cvs(t) .. ')', 2);
end
return it;
end
-- Inverse of parse_title: Try to reconstruct a wikified link given its parse
-- This does not seem to actually work
local function linkify_title( label, link, url )
local it;
local bold, italic;
if type(label) == 'table' then
link = label.link;
url = label.url;
bold = label.bold;
italic = label.italic;
label = label.label;
end
if label == nil then
-- all is lost
elseif link ~= nil then
it = '[[' .. link .. '|' .. label .. ']]';
elseif url ~= nil then
it = '[' .. url .. ' ' .. label .. ']';
else
it = label;
end
if bold then
it = "'''" .. it .. "'''";
end
if italic then
it = "''" .. it .. "''";
end
return it;
end
-- Return a value that should be suitable for use as an alt or aria-label
-- and safe to use for double-quoting
local function build_aria_label_from( s )
if s == nil then
return s;
end
if type(s) ~= 'string' then
error('ERROR: build_aria_label got a non-string '..cvs(s), 2);
end
return mw.ustring.gsub(
mw.ustring.gsub(
mw.ustring.gsub(
mw.ustring.gsub(
mw.ustring.gsub(
mw.ustring.gsub(s,
'<[^<>]*>', ''), -- try to nuke tags
"'''''", ''), -- Wikitext bold italic
"'''", ''), -- Wikitext bold
"'", ''), -- Wikitext italic
'"', '”'),
"'", '’')
end
local function get_token( t )
local t_next;
local it = {['pre'] = ''; ['c'] = ''; ['post'] = '';};
for stage = 1, 3, 1 do
local formatting = '';
while true do
local t1, t2;
local done_p = false;
if stage == 1 then
t1, t2 = mw.ustring.match(t, '^(<[^/][^<>]*>)(.*)$');
elseif stage == 2 then
t1 = nil;
elseif stage == 3 then
t1, t2 = mw.ustring.match(t, '^(</[^<>]*>)(.*)$');
else
error('Internal error 1, stage='..cvs(stage), 1)
end
if t1 then
formatting = formatting .. t1;
t_next = t2;
elseif stage == 1 or stage == 3 then
t1, t2 = mw.ustring.match(t, "^(''''')(.*)$");
if not t1 then
t1, t2 = mw.ustring.match(t, "^(''')(.*)$")
if not t1 then
t1, t2 = mw.ustring.match(t, "^('')(.*)$");
end
end
if t1 then
formatting = formatting .. t1;
t_next = t2;
else
done_p = true;
end
elseif stage == 2 then
t1, t2 = mw.ustring.match(t, '^([^<])(.*)$');
if t1 then
it.c = it.c .. t1;
t_next = t2;
end
done_p = true;
else
error('Internal error 2, stage='..cvs(stage), 1)
end
if done_p then break end
t = t_next;
end
if formatting then
if stage == 1 then
it.pre = it.pre .. formatting;
else
it.post = it.post .. formatting;
end
end
end
return it, t_next or '';
end
-- Build a type 1 part (not necessarily the entire title)
-- Try to construct the final HTML so that it line wrap correctly
-- while also following kinsokushori rules
local function build_type_1_part( s )
local it;
local stage1 = {};
local opening_p = false;
s = parse_title(s);
assert(ref(s) == 'parsed-title')
it = '';
local t = s.label;
while #t > 0 do
local c, t_next = get_token(t);
local closing_p = mw.ustring.match('[】》〉)」」』]', c.c);
if opening_p or closing_p then
table.insert(stage1[#stage1], c);
else
table.insert(stage1, {c});
end
opening_p = mw.ustring.match('[【《〈(「「『]', c.c);
t = t_next;
end
for i = 1, #stage1, 1 do
local stage2 = stage1[i];
local class = 'zit3';
if i == #stage1 then
class = 'hai2zeoi3mei1ge3 '..class;
end
if i == 1 then
class = 'hai2tau4ge3 '..class;
end
it = it .. '<span class="'..class..'">' .. '<span class=zit3>';
for j = 1, #stage2, 1 do
local class = 'zi6';
if j == #stage2 then
class = 'hai2zeoi3mei1ge3 '..class;
end
if j == 1 then
class = 'hai2tau4ge3 '..class;
end
local s = stage2[j];
local c = table.concat({s.pre or '', s.c, s.post or ''});
it = it .. '<span class="'..class..'">' .. c .. '</span>';
end
it = it .. '</span>' .. '</span>';
end
--it = 'DEBUG: stage1 = ' .. cvs(stage1) .. '<br>→ it = ' .. cvs(it);
s.label = it;
it = linkify_title(s);
return it;
end
-- Try to kern the given string
-- The logic is a deterministic finite state machine. The DFA diagram is
-- currently on a piece of paper and will be added here later.
local function kern( s0 )
local it;
local s = parse_title(s0);
assert(ref(s) == 'parsed-title');
local t = s.label;
local segment;
local STATE = { ['INITIAL'] = 'INITIAL';
['OPENING'] = 'OPENING';
['CLOSING'] = 'CLOSING';
};
local state = STATE.INITIAL;
local function tag_p( s )
return s and s:sub(1, 1) == '<';
end
local function remember_kerned( c, class, segment, state )
if segment == nil then
segment = {};
end
table.insert(segment, { ['c'] = c;
['class'] = class;
['state'] = state; });
return segment;
end
local function remember_unkerned(c, segment, state)
if segment == nil then
segment = {};
end
if #segment == 0
or segment[#segment].class
or segment[#segment].post
or c.pre then
table.insert(segment, { ['c'] = c;
['state'] = state; } );
else
segment[#segment].c.c = table.concat({segment[#segment].c.c, c.c});
segment[#segment].c.post = c.post;
end
return segment;
end
local function change_class_of_last_remembered( segment, class, state )
if segment ~= nil and #segment > 0 then
segment[#segment].class = class;
end
return segment;
end
local function remove_last_character_from_memory( segment )
local it = {};
if segment and #segment > 0
and segment[#segment].c.post then -- last segment has a tag. sigh.
it.c = '';
elseif segment and #segment > 0
and #(segment[#segment].c.c) > 0 then
local s1, s2 = mw.ustring.match(segment[#segment].c.c, '^(.*)(.)$');
it.c = s2;
it.post = segment[#segment].c.post;
segment[#segment].c.c = s1;
segment[#segment].c.post = nil;
if #s1 == 0 then
it.pre = segment[#segment].c.pre;
segment[#segment].c.pre = nil;
end
else
it.c = '';
end
return it, segment;
end
local function flush_segment( segment, it, state )
local s = '';
if segment then
for i, v in pairs(segment) do
s = s .. (v.c.pre or '');
if v.class then
s = s .. '<span class=' .. v.class .. '>' .. v.c.c .. '</span>'
else
s = s .. v.c.c;
end
s = s .. (v.c.post or '');
end
if #s > 0 then
s = '<span class=zit3 ' .. '>' .. s .. '</span>';
--s=mw.ustring.gsub(s,'<','<');--DEBUG
it = it .. s;
end
end
segment = {};
return segment, it;
end
it = '';
while #t > 0 do
-- We might encounter a tag. try to not break it
-- XXX Actually, if we see a tag maybe we should stop and give up
local c, t_next = get_token(t);
if state == STATE.INITIAL then
if kernable_left_punctuation_p(c.c) then
state = STATE.OPENING;
if #it == 0 then
segment = remember_kerned(c, 'koen1zo2', segment, state);
else
segment = remember_kerned(c, 'koen1zo2siu2siu2', segment, state);
end
elseif kernable_right_punctuation_p(c.c) then
state = STATE.CLOSING;
local c_last;
c_last, segment = remove_last_character_from_memory(segment);
segment, it = flush_segment(segment, it, state);
segment = remember_unkerned(c_last, segment, state);
segment = remember_kerned(c, 'koen1jau6', segment, state);
else
segment = remember_unkerned(c, segment, state);
end
elseif state == STATE.OPENING then
if kernable_left_punctuation_p(c.c) then
segment = remember_kerned(c, 'koen1zo2', segment, state);
elseif kernable_right_punctuation_p(c.c) then
state = STATE.CLOSING;
segment = remember_kerned(c, 'koen1jau6', segment, state);
else
state = STATE.INITIAL;
segment = remember_unkerned(c, segment, state);
segment, it = flush_segment(segment, it, state);
end
elseif state == STATE.CLOSING then
if kernable_left_punctuation_p(c.c) then
state = STATE.OPENING;
segment, it = flush_segment(segment, it, state);
segment = remember_unkerned(c, segment, state);
elseif kernable_right_punctuation_p(c.c) then
segment = remember_kerned(c, 'koen1jau6', segment, state);
else
state = STATE.INITIAL;
if not space_p(c.c) and not left_kernable_narrow_punctuation_p(c.c) then
segment = change_class_of_last_remembered(segment, 'koen1jau6siu2siu2', state)
end
segment = remember_unkerned(c, segment, state);
segment, it = flush_segment(segment, it, state);
end
else
error('kern() encountered unexpeected state '.. cvs(state));
end
t = t_next;
end
segment, it = flush_segment(segment, it, state);
if ref(s0) == 'parsed-title' then -- try to return a result that's the same type
s.label = it;
it = s;
end
return it;
end
-- Build a type 1 citable with type 2 as a fallback
local function build_type_1_citable( work, part, lang )
local it;
local part1, part2;
local class1, class2;
local prefix, suffix;
local infix = '';
work = parse_title(work);
part = parse_title(part);
if part ~= nil then
prefix = '〈';
suffix = '〉';
if work ~= nil then
infix = '・'; -- U+30FB
part1 = build_type_1_part(work);
part2 = build_type_1_part(part);
class1 = 'syu1ming4';
class2 = 'pin1ming4';
else
part1 = build_type_1_part(part);
class1 = 'pin1ming4';
end
elseif work ~= nil then
part1 = build_type_1_part(work);
class1 = 'syu1ming4';
prefix = '《';
suffix = '》';
end
local alt;
-- build HTML tag with fallback and aria-label
it = '';
if part1 ~= nil then
it = it .. '<span class=' .. class1 .. '>'
.. '<span class=hoi1>' .. prefix .. '</span>'
.. linkify_title(part1)
.. '</span>';
end
if part2 ~= nil then
it = it .. '<span class=' .. class2 .. '>'
.. '<span class=fan1gaak3>' .. infix .. '</span>'
.. linkify_title(part2)
.. '</span>';
if part1 ~= nil then
alt = prefix .. build_aria_label_from(part1
..infix
..part2) .. suffix;
else
alt = prefix .. build_aria_label_from(part2) .. suffix;
end
elseif part1 ~= nil then
alt = prefix .. build_aria_label_from(part1) .. suffix;
end
if it ~= nil then
local attrs = {'<span'};
it = mw.ustring.gsub(it,
'(</span>)$',
'<span class=saan1>' ..suffix .. '</span>%1');
if alt:match('"') then
error('alt 參數 「' .. alt .. '」 無效,暫時唔支援 ASCII 雙引號');
end
table.insert(attrs, 'aria-label="' .. alt .. '"');
if lang then
if lang:match('[ "]') then
error('語言參數 「' .. lang .. '」 無效');
end
table.insert(attrs, 'lang="' .. lang .. '"');
end
it = table.concat(attrs, ' ') .. '>' .. it .. '</span>';
end
return it;
end
-- Build a type 2 citable using the correct quotation marks
-- and, if needed, inner separator
local function build_type_2_citable( work, part, lang )
local it;
local class;
local prefix;
local suffix;
local root;
local alt;
work = parse_title(work);
part = parse_title(part);
if part ~= nil then
class = 'pin1ming4';
if work ~= nil then
prefix = '《';
suffix = '》';
work.label = prefix .. work.label;
part.label = part.label .. suffix;
root = linkify_title(kern(work))
.. '・' -- dot = U+30FB
.. linkify_title(kern(part));
alt = work.label .. '・' .. part.label;
else
prefix = '〈';
suffix = '〉';
part.label = prefix .. part.label .. suffix;
root = linkify_title(kern(part));
alt = part.label;
end
elseif work ~= nil then
class = 'syu1ming4';
prefix = '《';
suffix = '》';
work.label = prefix .. work.label .. suffix;
root = linkify_title(kern(work));
alt = work.label;
end
if root ~= nil then
alt = build_aria_label_from(alt);
--prefix = '<span class=hoi1-adj>' .. prefix .. '</span>';
--suffix = '<span class=saan1-adj>'.. suffix .. '</span>';
it = '<span class = "' .. class .. '-b" aria-label="' .. alt .. '">'
.. root
.. '</span>';
end
return it;
end
-- Build a non-citable proper noun using CSS underlining or borders
local function build_noncitable_proper_simple( parts, use_dot_p )
local it;
local class = 'zyun1ming4';
local zwsp = ''; -- U+200B
local zwnj = ''; -- U+200C
local infix = '・' -- dot = U+30FB
local root;
local alt;
if not use_dot_p then
infix = zwnj;
end
if parts then
for k, v in pairs(parts) do
local part = parse_title(parts[k]);
if not root then
root = '';
alt = '';
else
root = root .. zwnj;
alt = alt .. infix;
end
local segment = linkify_title(part);
alt = build_aria_label_from(part.label);
segment = '<span class = "' .. class .. '-b" aria-label="' .. alt .. '">'
.. segment
.. '</span>';
root = root .. segment;
end
end
it = '<span class=zyun1ming4-b>'
.. root
.. '</span>';
return it;
end
-- Attempt to build a non-citable proper noun using Unicode
local function build_noncitable_proper_alternate( parts, use_dot_p )
local it;
local class = 'zyun1ming4';
local zwsp = ''; -- U+200B
local zwnj = ''; -- U+200C
local infix = '・' -- dot = U+30FB
local root;
local alt;
if not use_dot_p then
infix = zwnj;
end
if parts then
for k, v in pairs(parts) do
local part = parse_title(parts[k]);
if not root then
root = '';
alt = '';
else
root = root .. zwnj;
alt = alt .. infix;
end
local segment = build_type_1_part(part);
alt = build_aria_label_from(part.label);
segment = '<span class = "' .. class .. '" aria-label="' .. alt .. '">'
.. segment
.. '</span>';
root = root .. segment;
end
end
it = '<span class=zyun1ming4>'
.. root
.. '</span>';
return it;
end
-- Analyze the given title(s) and decide if type 1 is safe to use
local function determine_which_type_to_use( work, part )
local it;
local det;
local t = ref(work);
if t == 'parsed-title' then
work = work.label;
elseif t == 'array' then
work = table.concat(work);
end
if type(part) == 'table' then
part = part.label;
end
if work ~= nil and part ~= nil then
det = work .. part;
elseif work ~= nil then
det = work;
elseif part ~= nil then
det = part;
else
det = '';
end
if cjk_p(det) then
it = TYPE_1;
else
it = TYPE_2;
end
return it;
end
-- Automatically select whether to build either a type 1 or type 2 citable
local function auto_build_citable( work, part, lang )
local type = determine_which_type_to_use(work, part);
local it;
if type == TYPE_1 then
it = build_type_1_citable(work, part, lang);
else
it = build_type_2_citable(work, part, lang);
end
return it;
end
-- Automatically select whether to build noncitable with Unicode or underlining
local function auto_build_noncitable( parts, use_dots_p )
local type = determine_which_type_to_use(parts);
local it;
if type == TYPE_1 then
it = build_noncitable_proper_alternate(parts, use_dots_p);
else
it = build_noncitable_proper_simple(parts, use_dots_p);
end
return it;
end
--- Exported, invocable functions ---------------------------------------------
-- Entry point for Template:書名 and Template:篇名
p.Syu1meng2 = function( frame )
local parent = frame:getParent();
local chapter_mode_p = (frame.args.mode and frame.args.mode == 'chapter') == true;
local it;
local alt = '';
local styles = 'Module:書名/styles.css';
local work, part;
local lang;
local type;
-- figure out what is actually being marked up as a citable
local parts = {};
local name = parent:getTitle();
if chapter_mode_p then
table.insert(parts, '');
name = '篇名模';
end
for k, v in pairs(parent.args) do
local ps = ' (參數明細:' .. cvs(parent.args) .. ')';
v = sanitize(v);
if ref(k) == 'number' then
assert(v ~= nil);
table.insert(parts, v);
elseif k == 'type' or k == '式' then
if type then
error(name..'遇到 '..k..' 參數,但係已經指咗 「'..type..'」 式'..ps);
else
type = canonicalize_type_value(v);
end
elseif (not chapter_mode_p and k == 'title')
or (chapter_mode_p and k == 'work') then
if nullify(parts[1]) then
error(name..'遇到 '..k..' 參數,但係已經有 「'..parts[1]..'」'..ps);
else
parts[1] = v;
end
elseif k == 'chapter'
or (chapter_mode_p and k == 'title') then
if nullify(parts[2]) then
error(name..'遇到 '..k..' 參數,但係已經有 「'..parts[2]..'」'..ps);
else
if not parts[1] then
parts[1] = '';
end
parts[2] = v;
end
elseif k == 'lang' or k == '話' or k == '語言' then
lang = v;
else
error(name..'遇到不明參數 「' .. k .. '」'..ps);
end
end
if #parts == 0 then
error('冇指定書名或者篇名');
elseif #parts > 2 then
error('指定咗太多章名,暫時處理唔到(parts='..cvs(parts)..')');
end
work = parse_title(nullify(parts[1]));
part = parse_title(nullify(parts[2]));
-- fixup default type
if type == nil then
type = 'auto';
end
-- build it
if type == 'auto' then
it = auto_build_citable(work, part)
elseif type == TYPE_1 then -- 甲式 (type 1)
it = build_type_1_citable(work, part)
elseif type == TYPE_2 then -- 乙式 (type 2)
it = build_type_2_citable(work, part)
else
error('唔明'..cvs(type)..'式書名號係乜');
end
-- request our style sheet
it = table.concat ({
frame:extensionTag ('templatestyles', '', {src=styles}),
it
});
return it;
end
-- Entry point for Template:專名
p.Zyun1ming4 = function( frame )
local parent = frame:getParent();
local use_dots_p = (frame.args.mode and frame.args.mode == 'dotted') == true;
local it;
local alt = '';
local styles = 'Module:書名/styles.css';
local error;
local parts = {};
for k, v in pairs(parent.args) do
if type(k) == 'number' then
table.insert(parts, v);
elseif not error then
error = '專名模遇到不明參數 「' .. k .. '」';
else
error = error .. '、「' .. k .. '」';
end
end
if #parts == 0 then
parts = nil;
end
-- build it
if parts ~= nil then
it = auto_build_noncitable(parts, use_dots_p);
else
if error == nil then
error = '專名模出錯,搵唔到有乜嘢名';
end
end
if it == nil and error ~= nil then
it = '<span class=error>' .. error .. '</span>'
.. '(parts=' .. cvs(parts)
.. ')'
end
-- request our style sheet
it = table.concat ({
frame:extensionTag ('templatestyles', '', {src=styles}),
it
});
return it;
end
-- Entry point for Template:着重
p.Zoek6zung6 = function( frame )
local parent = frame:getParent();
local it;
local alt = '';
local styles = 'Module:書名/styles.css';
local parts = {};
for k, v in pairs(parent.args) do
if type(k) == 'number' then
v = parse_title(v);
v.alt = build_aria_label_from(v.label);
table.insert(parts, v);
else
error('着重模遇到不明參數 「' .. k .. '」');
end
end
if #parts == 0 then
error('着重模出錯,搵唔到着重乜嘢');
end
-- build it
it = '';
for i, v in pairs(parts) do
v = parse_title(v);
local segment = build_type_1_part(v.label);
if it ~= nil then
it = it
.. '<span class=zoek6zung6 aria-label="' .. v.alt .. '">'
.. segment
.. '</span>';
end
end
-- request our style sheet
it = table.concat ({
frame:extensionTag ('templatestyles', '', {src=styles}),
it
});
return it;
end
-- Entry point for Template:Kern
p.Kern = function( frame )
local parent = frame:getParent();
local it;
local alt = '';
local styles = 'Module:書名/styles.css';
local parts = {};
for k, v in pairs(parent.args) do
if type(k) == 'number' then
v = parse_title(v);
v.alt = build_aria_label_from(v.label);
table.insert(parts, v);
else
error('Kern 模遇到不明參數 「' .. k .. '」');
end
end
if #parts == 0 then
error('Kern 模出錯,搵唔到 kern 乜嘢');
end
-- build it
it = '';
for i, v in pairs(parts) do
v = parse_title(v);
local segment = kern(v.label);
if it ~= nil then
it = it
.. '<span class=koen1 aria-label="' .. v.alt .. '">'
.. segment
.. '</span>';
end
end
-- request our style sheet
it = table.concat ({
frame:extensionTag ('templatestyles', '', {src=styles}),
it
});
return it;
end
--- Non-invocable internal functions exported for other modules to use --------
p.cvs = cvs;
p.ref = ref;
p.cjk_p = cjk_p;
p.kernable_left_punctuation_p = kernable_left_punctuation_p;
p.kernable_right_punctuation_p = kernable_right_punctuation_p;
p.array_p = array_p;
p.determine_which_type_to_use = determine_which_type_to_use;
p.build_type_1_citable = build_type_1_citable;
p.build_type_2_citable = build_type_2_citable;
p.auto_build_citable = auto_build_citable;
return p;