置顶公告:【置顶】关于临时开启评论区所有功能的公告(2022.10.22) | 【置顶】关于本站Widget恢复使用的公告
  • 你好~!欢迎来到萌娘百科镜像站!如需查看或编辑,请联系本站管理员注册账号。
  • 本镜像站和其他萌娘百科的镜像站无关,请注意分别。

Module:Furigana

猛汉♂百科,万男皆可猛的百科全书!转载请标注来源页面的网页链接,并声明引自猛汉百科。内容不可商用。
跳到导航 跳到搜索
Template-info.svg 模块文档  [查看] [编辑] [历史] [刷新]
Icon-info.png

本模板可以被替换引用(使用{{subst:振假名|…}}

为保持代码整洁,请勿轻易替换引用。

用于标注一堆振假名。

用法

{{Photrans/button}}
{{LyricsKai
|original=
{{振假名|template=Photrans
|きらり空(そら)に響(ひび)く星(ほし)の声(こえ)
ああ海(うみ)を照(て)らす 光(ひかり)を辿(たど)れたら
流(なが)れた星(ほし)はどこへ行(ゆ)く?
きっと何(なに)かがそこで待(ま)ってる
旗(はた)を掲(かか)げ進(すす)もう
いつでも月(つき)は歌(うた)い 旅人(たびびと)を導(みちび)いて
「もう迷(まよ)わなくていいから」
}}
|translated= <!-- 略 -->
}}

与下面这段代码效果相同。

{{Photrans/button}}
{{LyricsKai
|original=
きらり{{Photrans|空|そら}}に{{Photrans|響|ひび}}く{{Photrans|星|ほし}}の{{Photrans|声|こえ}}
ああ{{Photrans|海|うみ}}を{{Photrans|照|て}}らす {{Photrans|光|ひかり}}を{{Photrans|辿|たど}}れたら
{{Photrans|流|なが}}れた{{Photrans|星|ほし}}はどこへ{{Photrans|行|ゆ}}く?
きっと{{Photrans|何|なに}}かがそこで{{Photrans|待|ま}}ってる
{{Photrans|旗|はた}}を{{Photrans|掲|かか}}げ{{Photrans|進|すす}}もう
いつでも{{Photrans|月|つき}}は{{Photrans|歌|うた}}い {{Photrans|旅人|たびびと}}を{{Photrans|導|みちび}}いて
「もう{{Photrans|迷|まよ}}わなくていいから」
|translated= <!-- 略 -->
}}

效果:

本段落中所使用的歌词,其著作权属于原著作权人,仅以介绍为目的引用。
きらりそらひびほしこえ
亮晶晶 天空中回响着 星星的声音
ああうみらす ひかり辿たどれたら
啊 追着照亮海面的光束
ながれたほしはどこへく?
流星将去向何处
きっとなにかがそこでってる
在那儿一定有什么在等着
はたかかすすもう
扬起旗帜前进吧
いつでもつきうたい 旅人たびびとみちびいて
无论何时 月儿在歌唱 为旅人指着路
「もうまよわなくていいから」
所以不会再迷路了

参数

  • 匿名参数:需要转换振假名的内容,可以传入多个参数。形如漢字[1](かんじ)的将被转换为{{template|漢字|かんじ}},注意是全角括号。
  • template:振假名所用模板名,默认值为Ruby
  • compatible:为非空值时启用兼容模式。仅当不启用兼容模式会出问题时才应启用兼容模式。

模板不自带语言属性,请确保本模板外部标注了语言,例如,{{LyricsKai}}左栏默认标注了lang="ja"

连续汉字只标注一部分

用管道符隔开即可:

{{lj|{{振假名|……由崎|星空(ナサ)……}}}}

结果:……由崎星空ナサ……

对比不隔开的效果:

{{lj|{{振假名|……由崎星空(ナサ)……}}}}

结果:……由崎星空ナサ……

汉字后的括号不是注音

要使汉字后的括号出现在正文中,使用管道符将汉字与括号隔开即可:

{{lj|{{振假名|世紀の覚醒|(2度寝ー3度寝ー)}}}}

结果:世紀の覚醒(2度寝ー3度寝ー)

想要注音的文字不是汉字

本模板内可以正常用其他注音模板:

{{lj|{{振假名|{{ruby|1人|ひとり}}でも大丈夫(だいじょうぶ)だからただ前(まえ)を見(み)て}}}}

结果:1人ひとりでも大丈夫だいじょうぶだからただまえ

从其他注音模板转换为本模板

一个工具模块Furigana/utilstransform函数可用于转换源代码,使用时请替换引用(即subst:)。它将自动识别所使用的注音模板(限{{Ruby}}、{{Photrans}}、{{Photrans2}}和{{Photransa}}),并选择出现次数最多的作为template参数[2],未被选中的会保留原样

{{LyricsKai
|original=
{{subst:#invoke:Furigana/utils|transform|
きらり{{Photrans|空|そら}}に{{Photrans|響|ひび}}く{{Photrans|星|ほし}}の{{Photrans|声|こえ}}
ああ{{Photrans|海|うみ}}を{{Photrans|照|て}}らす {{Photrans|光|ひかり}}を{{Photrans|辿|たど}}れたら
{{Photrans|流|なが}}れた{{Photrans|星|ほし}}はどこへ{{ruby|行|ゆ}}く?
きっと{{Photrans|何|なに}}かがそこで{{Photrans|待|ま}}ってる
{{Photrans|旗|はた}}を{{Photrans|掲|かか}}げ{{Photrans|進|すす}}もう
いつでも{{Photrans|月|つき}}は{{Photrans|歌|うた}}い {{Photrans|旅人|たびびと}}を{{Photrans|導|みちび}}いて
「もう{{Photrans|迷|まよ}}わなくていいから」
}}
|translated= <!-- 略 -->
}}

保存后代码将变为:

{{LyricsKai
|original=
{{振假名|template=Photrans
|きらり空(そら)に響(ひび)く星(ほし)の声(こえ)
ああ海(うみ)を照(て)らす 光(ひかり)を辿(たど)れたら
流(なが)れた星(ほし)はどこへ{{ruby|行|ゆ}}く?
きっと何(なに)かがそこで待(ま)ってる
旗(はた)を掲(かか)げ進(すす)もう
いつでも月(つき)は歌(うた)い 旅人(たびびと)を導(みちび)いて
「もう迷(まよ)わなくていいから」
}}
|translated= <!-- 略 -->
}}

该转换模块同样支持通过template手动指定注音模板,不限于上面几个自动识别的模板。

注意,如果原始代码中,某个注音模板符合以下情况之一:

  • 包含超过2个参数;
  • 包含换行;
  • 包含等号;
  • 参数1不全是汉字[1]
  • 嵌套了模板。

则该注音模板将保留原样。例如:

{{subst:#invoke:Furigana/utils|transform|
きらり{{ruby|空|そら}}{{ruby|響|ひび|ja}}{{ruby|星|ほ
し}}{{ruby|声|{{黑幕|こえ}}}}

{{ruby|1人|ひとり}}
}}

保存后代码将变为:

{{振假名
|きらり空(そら){{ruby|響|ひび|ja}}{{ruby|星|ほ
し}}{{ruby|声|{{黑幕|こえ}}}}

{{ruby|1人|ひとり}}
}}

模块测试结果(开发者用)

结果

脚本错误:没有“Furigana/tests”这个模块。

注释

  1. 1.0 1.1 以及“”“”。
  2. 由于Ruby是默认值,所以将会省略。
local p = {}

-- 转化为upvalue,提升速度
local type = type
local ipairs = ipairs
local concat = table.concat
local u_gsub = mw.ustring.gsub
local u_sub = mw.ustring.sub

local KANJI_LIKE_UNICODE_RANGES = {
	{ 0x2E80,  0x2EFF },  -- CJK部首补充
	{ 0x3005,  0x3007 },  -- "々、〆、〇"
	{ 0x31C0,  0x31EF },  -- CJK笔画
	{ 0x3400,  0x4DBF },  -- CJK统一表意文字扩展A
	{ 0x4E00,  0x9FFF },  -- CJK统一表意文字
	{ 0xF900,  0xFAFF },  -- CJK兼容表意文字
	{ 0x20000, 0x2A6DF }, -- CJK统一表意文字扩展B
	{ 0x2A700, 0x2EE5F }, -- CJK统一表意文字扩展C-I
	{ 0x2F800, 0x2FA1F }, -- CJK兼容表意文字补充
	{ 0x30000, 0x323AF }, -- CJK统一表意文字扩展G-H
}
local KANJI_LIKE_PATTERN = (function ()
	local char = mw.ustring.char
	local parts = {}
	for i, range in ipairs(KANJI_LIKE_UNICODE_RANGES) do
		parts[i] = type(range) == 'table' and char(range[1])..'-'..char(range[2]) or char(range)
	end
	return '['..concat(parts)..']'
end)()


--[[
把<tag>、</tag>与其他文本分开。
```lua
divide_tags_and_non_tags('A<span title="(title)">B</span>C')
-->
{
	{ type = 'unknown', content = 'A' },
	{ type = 'tag', content = '<span title="(title)">' },
	{ type = 'unknown', content = 'B' },
	{ type = 'tag', content = '</span>' },
	{ type = 'unknown', content = 'C' },
```
]]
---@param text string
---@return {type: 'tag' | 'unkown', content: string}[]
local function extract_tags(text)
	local result = {}
	local rest = text
		 :gsub(
			 '(.-)(</?[:%a_][:%w_-.]*[^<>]*>)',
			 function (non_tag, tag)
				 if non_tag ~= '' then
					 result[#result+1] = { type = 'unknown', content = non_tag }
				 end
				 result[#result+1] = { type = 'tag', content = tag }
				 return ''
			 end
		 )
	if rest ~= '' then
		result[#result+1] = { type = 'unknown', content = rest }
	end
	return result
end

local EXTRACT_RB_RT_PATTERN = '(.-)('..KANJI_LIKE_PATTERN..'+)(%b())'
---@param text string
---@param into_tokens ({type: 'ruby-part' | 'tag' | 'text', content: string} | {type: 'rb-rt', rb: string, rt: string})[]
local function extract_rb_rt(text, into_tokens)
	for line_with_lf in text:gmatch('[^\n]*\n*') do
		local rest = u_gsub(
			line_with_lf,
			EXTRACT_RB_RT_PATTERN,
			function (before, rb, rt)
				if before ~= '' then
					into_tokens[#into_tokens+1] = { type = 'text', content = before }
				end
				into_tokens[#into_tokens+1] = {
					type = 'rb-rt',
					rb = rb,
					rt = u_sub(rt, 2, -2),  -- 去除括号
				}
				return ''
			end
		)
		if rest ~= '' then
			into_tokens[#into_tokens+1] = { type = 'text', content = rest }
		end
	end
end

---细节见测试 [[Module:Furigana/tests]]。
---@param tokens {type: 'tag' | 'unkown', content: string}[]
---@param into_tokens ({type: 'ruby-part' | 'tag' | 'text', content: string} | {type: 'rb-rt', rb: string, rt: string})[]
local function extract_others(tokens, into_tokens)
	local ruby_start_index = nil

	for i, token in ipairs(tokens) do
		if ruby_start_index then  -- 如果处于<ruby>标签内,则所有token都是ruby-part
			into_tokens[#into_tokens+1] = { type = 'ruby-part', content = token.content }
			if token.type == 'tag' and token.content:match('^</[Rr][Uu][Bb][Yy]') then
				ruby_start_index = nil
			end
		elseif token.type == 'tag' then
			if token.content:match('^<[Rr][Uu][Bb][Yy]') then
				into_tokens[#into_tokens+1] = { type = 'ruby-part', content = token.content }
				ruby_start_index = i
			else
				---@diagnostic disable-next-line: assign-type-mismatch
				into_tokens[#into_tokens+1] = token
			end
		else
			-- assert(token.type == 'unknown')
			extract_rb_rt(token.content, into_tokens)
		end
	end
end

---将`texts`解析为tokens,细节见测试[[Module:Furigana/tests]]。
---@param texts string[]
---@return ({type: 'ruby-part' | 'tag' | 'text', content: string} | {type: 'rb-rt', rb: string, rt: string})[]
local function parse(texts)
	local tokens = {}
	for _, text in ipairs(texts) do
		extract_others(extract_tags(text), tokens)
	end
	return tokens
end
p.parse = parse

---用`render_ruby`渲染`tokens`中的`rb-rt` token,其余token不变,最终连接为一个字符串。
---@param tokens ({type: 'ruby-part' | 'tag' | 'text', content: string} | {type: 'rb-rt', rb: string, rt: string})[]
---@param render_ruby fun(rb: string, rt: string): string
---@return string
local function render(tokens, render_ruby)
	local result = {}
	for i, token in ipairs(tokens) do
		result[i] = token.type == 'rb-rt' and render_ruby(token.rb, token.rt) or token.content
	end
	return concat(result)
end
p.render = render

---将`texts`中用“汉字(注音)”表示的振假名替换为`render_ruby`的返回值。
---@param texts string | string[]
---@param render_ruby fun(rb: string, rt: string): string
---@return string
function p.process(texts, render_ruby)
	if type(texts) == 'string' then
		texts = { texts }
	end
	local tokens = parse(texts)
	return render(tokens, render_ruby)
end

function p.main(frame, args)
	if not args then
		local parent = frame:getParent()
		local templates = { ['Template:振假名'] = true, ['Template:Sandbox'] = true }
		if parent and templates[parent:getTitle()] then
			frame = parent
		end
		args = frame.args
	end
	
	local template = args.template or 'Ruby'

	if mw.isSubsting() then
		local prefix = '{{'..template..'|'
		return p.process(args, function (rb, rt)
			return prefix..rb..'|'..rt..'}}'
		end)
	end

	local is_compatible_mode = args.compatible and args.compatible ~= ''
	if not is_compatible_mode then
		-- 快速模式,通过字符串替换,避免多次调用frame:expandTemplate
		local placeholder_pattern = '!@$r[bt]$@!'
		local rb_placeholder = '!@$rb$@!'
		local rt_placeholder = '!@$rt$@!'
		local template_string = frame:expandTemplate {
			title = template,
			args = { rb_placeholder, rt_placeholder },
		}
		if template_string:find(placeholder_pattern) then
			return p.process(args, function (rb, rt)
				return template_string:gsub(placeholder_pattern, { [rb_placeholder] = rb, [rt_placeholder] = rt })
			end)
		end
	end

	return p.process(args, function (rb, rt)
		return frame:expandTemplate {
			title = template,
			args = { rb, rt },
		}
	end)
end

return p