模块:语音映射/doc
这是模块:语音映射的文档页面
模块导读:语音动态映射系统
本模块(模块:语音映射)是 Wiki 语音系统的核心调度中枢。它负责从分散的数据页面中精准抓取特定角色的语音数据,进行文本清洗与格式化,并最终将数据注入到前端 Widget 中,生成带有打字机动效的交互式语音卡片。
💡 设计初衷:让路人也能参与维护
在传统的 Wiki 架构中,复杂的台词数据往往被写死在 Lua 的 Table 表中。这导致普通玩家发现翻译错误时,面对密密麻麻的代码根本无从下手。
本系统采用了“数据分片存储”架构:将海量语音数据按角色拆分到 数据:语音-xxx 等独立页面,并配合 Page Forms 提供极简的“填表式”编辑。目的是彻底降低维护门槛,让任何路过查阅 Wiki 的玩家,只需点击“编辑”按钮就能直接修正台词翻译,实现真正的社区共建。
基本调用方法
在任何角色页面或相关词条中,使用以下代码即可自动生成该角色的所有语音卡片:
{{#invoke:语音映射|render|角色ID}}
示例: {{#invoke:语音映射|render|930}} 将渲染 ID 为 930 的所有语音。
1. 底层数据结构 (Template)
为了让 Semantic MediaWiki (SMW) 能够识别这些散落的语音数据,我们使用了隐藏模板 Template:语音条目。当玩家通过表单保存数据时,实际上就是将数据包裹在这个模板中。
源码参考:
<noinclude>这是一个后端数据存储模板,请勿直接手动调用。</noinclude>{{#subobject:
|音频文件名={{{音频文件名|}}}
|日文内容={{{日文内容|}}}
|中文翻译={{{中文翻译|}}}
|所属ID={{{所属ID|}}}
|所属数据分片页={{FULLPAGENAME}}
}}
- 原理解析:这里使用了 SMW 的
#subobject(子对象)功能。它使得一个页面可以存储多条独立的语音数据。其中所属数据分片页=模块:语音映射/doc是核心锚点,它告诉系统这条数据保存在哪个具体的页面,从而为前端生成正确的“编辑表单链接”。
2. 系统运行逻辑 (Data Flow)
本系统的运转遵循“底层存储 -> Lua 处理 -> 前端渲染 -> 表单回写”的完整闭环。按执行顺序,逻辑如下:
Step 1: 语义检索 (SMW Query)
模块接收到 角色ID 后,会在内部调用 MediaWiki 解析器函数来执行 SMW 搜索。为了追求最高性能,我们在 Lua 中是这样构建查询的:
local askResult = frame:callParserFunction{
name = '#ask',
args = {
string.format("[[所属ID::%s]]", char_id), -- 查询条件:锁定当前角色
'?音频文件名',
'?日文内容',
'?中文翻译',
'?所属数据分片页',
sort = '音频文件名',
order = 'asc',
format = 'array', -- 核心:阵列格式
sep = '@@@', -- 条目之间的分隔符
propsep = '###', -- 属性之间的分隔符
mainlabel = '-', -- 隐藏主页面名,纯粹取数据
limit = '100'
}
}
- 深度解析:通常
#ask会返回复杂的 HTML 表格。但在 Lua 中,我们强行设置format = 'array',并定义了罕见的分隔符(@@@和###)。这样 SMW 就会返回一个纯文本的超长字符串,Lua 只需要用正则string.gmatch就能极速切分数据,避免了繁重的 DOM 解析,极大降低了服务器负载。
Step 2: 文本清洗与格式化
获取到长字符串后,Lua 进行拆解,并执行关键的文本转义:
- 换行符处理:识别用户在表单填写的字面量换行符(如
\n或/n),并将其转换为真实的换行符。这确保了后续前端的white-space: pre-wrap样式能够正确折行,而不会与 HTML 转义机制冲突。
Step 3: 智能路由构造
对于每一条语音,Lua 会动态计算两个关键 URL:
- 音频源路径:通过
Special:FilePath将文件名转化为真实的.mp3播放地址。 - 编辑跳转链接:这是系统的亮点。Lua 会将当前的页面标题(
returnto)、当前条目的文件名(focus_file)和角色ID(target_id)拼接在 URL 中,生成一个极具针对性的 Special:FormEdit 链接。
Step 4: Widget 分发
将清洗后的参数(audiourl, editurl, jp, cn)逐条传递给 Widget:语音展示,输出最终的 HTML 和 JS 代码。
3. 核心特性与体验优化 (Features)
本系统在设计时重点解决了“大页面性能崩溃”与“移动端/PC端阅读体验”两大痛点,包含以下高级特性:
🎨 前端交互:内联展开与智能打字机
由 Widget:语音展示 实现。
- 极致空间压缩:初始状态仅显示“胶囊形”微型播放按钮,支持横向流式排版(
inline-block),极大节约了长语音列表的垂直空间。 - 精准动效同步:通过设置
<audio preload="metadata"></audio>,浏览器能瞬间获取真实音频时长。JS 会据此精确计算打字速度(保底 40ms~300ms/字),确保文字输出速度与配音时长完美契合。 - 瞬间翻译机制:日文原文逐字打出(带闪烁光标),日文输出完毕的瞬间,中文翻译一次性全部显示,符合玩家阅读习惯。
🛡️ 数据架构:碎片化存储与绕过限制
- 抛弃了将 8000+ 条数据塞入单一页面的传统做法。数据被分散存储在如
数据:语音-930这样的角色专属分片页中。 - 完美避开了 Semantic MediaWiki 单页 100 条子对象(Subobject)的索引上限,保证所有语音都能被全站搜索到。
🤖 傻瓜式表单:防呆与精准定位
由 表单:语音数据表单 和 Widget:语音表单逻辑 协同实现。
当用户点击某条语音的“✏️编辑”按钮时:
- 自动过滤:JS 会读取 URL 中的
target_id,将表单中不属于该角色的其他行瞬间隐藏。 - 高亮与滚动:读取 URL 中的
focus_file,自动将页面滚动到用户刚刚点击的那一条,并加上蓝色高亮边框,实现“指哪打哪”。 - 数据保护:强行隐藏 Page Forms 默认的“增加/删除实例”按钮,并将文件名设为只读。普通用户只能修改中文/日文文本,绝无可能误删核心数据。
- 无缝返回:读取 URL 中的
returnto,用户点击“保存”后,浏览器会自动跳回他们原来所在的 Wiki 词条,而不是停留在枯燥的数据页。
4. 架构依赖清单 (Dependencies)
维护或迁移本系统时,请确保以下组件完整齐备:
| 组件类型 | 页面名称 | 职责说明 |
|---|---|---|
| 数据载体 | 语音-xxx |
存放具体语音条目的页面。必须包含 {{语音条目}} 模板。
|
| 数据模板 | Template:语音条目 |
负责将参数转化为 SMW #set 属性。
|
| 核心模块 | Module:语音映射 |
本模块,负责检索数据并调度展示。 |
| 前端展现 | Widget:语音展示 |
负责渲染胶囊按钮、打字机动画、CSS 样式控制。 |
| 编辑入口 | 表单:语音数据表单 |
提供用户友好的多实例编辑界面。 |
| 表单控制 | Widget:语音表单逻辑 |
注入于表单内,负责高亮行、隐藏按钮、回跳路由等 JS 逻辑。 |
5. 常见问题排查 (Troubleshooting)
- Q: 语音点击播放后,文字出得太快或太慢?
- 检查点:确认
Widget:语音展示中的<audio>标签是否包含preload="metadata"。如果缺失,浏览器无法获取时长,会触发保底的 5 秒逻辑。
- 检查点:确认
- Q: 编辑完语音点击保存,没有跳回角色页面?
- 检查点:检查调用模块的页面 URL 中是否丢失了
returnto参数。或者检查Widget:语音表单逻辑中覆盖form action的 JS 代码是否正常执行。
- 检查点:检查调用模块的页面 URL 中是否丢失了
- Q: 页面上出现了
<br>或者文字没有换行?- 检查点:确保 Lua 模块中转义
\n的代码使用的是jp:gsub("[/\\]n", "\n")替换为真实换行,且 Widget 中展示文本的带有white-space: pre-wrap;样式,并正确使用了 Smarty 过滤器。
- 检查点:确保 Lua 模块中转义

沪公网安备 31011002002714 号