摘要:粤语分库不仅是把
bankId换成b_yue。粤字形、繁简展示、读音来源和练习参数都需要明确的数据契约。本文基于"中国方言题库"当前YueBankPage、MockBanks、BankDetailContent与PracticePage真实源码,说明固定入口如何复用统一详情组件,题库如何保存"唔、冇、咗、攰"等粤语文本,以及为什么当前audioHint和zh-CNTTS 不能被宣传为真人或标准粤语读音。文章同时给出结构化文本与真实音频的安全扩展路径。

一、先从真实入口确认地区身份
YueBankPage.ets 是一个薄入口:
ts
import { BankDetailContent } from './BankDetailPage'
@Entry
@Component
struct YueBankPage {
build() {
Column() {
BankDetailContent({ fixedBankId: 'b_yue' })
}
.width('100%')
.height('100%')
}
}
它只负责把 b_yue 注入共享详情组件。题库简介、文化提示、进度、章节列表、随机练习和模拟考试均由 BankDetailContent 实现。
本文的唯一标记是:粤语读音必须有可核验的音频来源 。代码中出现 audioHint、播放按钮或 TTS API,都不能自动证明输出的是标准粤语,更不能证明是真人录音。
二、b_yue 是整条链路的统一主键
粤语题库目录如下:
ts
{
id: 'b_yue',
regionId: 'yue',
name: '粤语题库',
cover: $r('app.media.img_bank_cover_yue'),
totalCount: 0,
accuracy: 0,
hot: 95,
chapters: YUE_CHAPTERS
}
章节包括:
ts
const YUE_CHAPTERS: Chapter[] = makeChapters('yue', [
'基础发音',
'九声六调',
'日常对话',
'茶餐厅词汇',
'老广俗语',
'香港流行语'
])
这里要区分:
- 地区 ID:
yue - 题库 ID:
b_yue - 章节 ID 前缀:
yue_c1到yue_c6
分库入口和练习页传递的是题库 ID;章节模式还会传章节 ID。本地进度也按 bankId + chapterId 查询。任何一层混用 yue 和 b_yue,都会出现详情为空或进度不更新。
三、粤语文字不是简单的"繁体中文"
题库包含:
text
唔该
冇见
食咗饭未
好攰
边个
做乜
返屋企
钟意
这些内容既包含传统字形,也包含粤语书写中的常用方言字。它们不能被普通简繁转换库无差别处理。例如"冇""咗""乜"不是把普通话简体文本机械转为繁体就能得到的。
当前源码没有发现自动繁简转换服务。题干、选项、解析和 audioHint 都按本地数据原样保存、原样渲染。这一事实可以准确描述为:
应用保留题库中录入的粤语字形,不在运行时自动改写。
不能描述为:
应用已实现完整的简繁双向转换。
如果产品需要简体解释与粤语原文并存,应把两者建成不同字段,而不是运行时替换字符。
四、为粤语文本建立类型化模型
当前 RawQuestion 使用:
ts
interface RawQuestion {
type: string
stem: string
options: string[]
answer: number
analysis: string
example?: string
audioHint?: string
}
它可以承载题目,但无法明确区分"粤语原文""普通话释义""粤拼"和"显示字形策略"。更适合粤语内容的模型可以是:
ts
interface YueExpression {
id: string
traditionalText: string
simplifiedExplanation: string
jyutping?: string
meaning: string
exampleTraditional?: string
exampleExplanation?: string
audioResource?: Resource
audioSourceNote?: string
}
这里不把粤语原文命名为泛化的 traditional 后就自动推导简体。simplifiedExplanation 是编辑审核后的普通话解释;jyutping 是独立读音标注;音频资源与来源说明也是独立字段。
五、字体与 Unicode 显示要在真机验证
ArkUI Text 能显示 Unicode 字符,但最终可见效果还取决于系统字体是否包含对应字形。测试应覆盖:
- "唔、冇、咗、攰、啲、嚟"等字符是否缺字。
- 粤拼数字或声调标记是否被截断。
- 大字体下原文和释义能否换行。
- 粗体是否仍有可用字形。
- phone、tablet、2in1 是否使用一致字体回退。
不要为了一个缺字字符把整套大型字体打进包。应先确认系统字体覆盖,再评估是否需要小范围字体资源及其授权。
ts
Text(expression.traditionalText)
.fontSize(Sizes.TITLE_FONT)
.fontWeight(FontWeight.Bold)
.maxLines(2)
.textOverflow({ overflow: TextOverflow.Ellipsis })
若原文承担核心学习信息,最好不要强制单行省略。可以允许两行,并让卡片高度随内容增长。
六、当前题库中的 audioHint 只是文本
大量原始题目包含:
ts
{
type: 'audio',
stem: '听音理解:"得唔得呀?"是在问?',
audioHint: '得唔得呀',
options: ['行不行', '去哪里', '吃什么', '几点钟'],
answer: 0,
analysis: '"得唔得"就是可不可以、行不行。'
}
audioHint 的类型是 string,内容是待朗读文本,不是音频文件、URI 或媒体资源。仅凭这个字段无法证明发音者、口音、地区或录制质量。
更关键的是,当前目录构建会把所有 audio 题重分类为 vocab:
ts
const reclassified: RawQuestion[] = source.map((r: RawQuestion) => {
if (r.type === 'audio') {
return {
type: 'vocab',
stem: r.stem,
options: r.options,
answer: r.answer,
analysis: r.analysis,
example: r.example,
audioHint: r.audioHint
} as RawQuestion
}
return r
})
注释写明"听音题题型已下线"。因此,这些题在当前缓存中是词汇题,不能因为仍保留 audioHint 就说"粤语听音题已上线"。

七、PracticePage 的 TTS 也不能证明粤语读音
PracticePage 创建文本转语音引擎时使用:
ts
this.ttsEngine = await textToSpeech.createEngine({
language: 'zh-CN',
person: 0,
online: 0
})
失败后回退到:
ts
this.ttsEngine = await textToSpeech.createEngine({
language: 'zh-CN',
person: 0,
online: 1
})
这说明:
- 语言参数是
zh-CN。 - 优先本地引擎。
- 失败后可能使用在线引擎。
- 模块声明
ohos.permission.INTERNET与在线回退一致。
但它没有指定或验证粤语音色。即便引擎能朗读"唔该",输出也可能按普通话字音处理。商店文案不应写"标准粤语发音""真人粤语朗读"。
此外,PracticePage 的播放 UI只在 question.type === 'audio' 时出现,而目录已经把 audio 改成 vocab。因此当前普通题链路中,播放分支可能根本不会触发。
八、真正的粤语读音需要独立资源契约
若要上线粤语读音,建议使用明确资源:
ts
interface PronunciationAsset {
id: string
expressionId: string
locale: 'yue-Hant'
region?: 'HK' | 'GZ'
speakerType: 'human' | 'licensed-engine'
resource: Resource
durationMs: number
sourceNote: string
reviewedAt: string
}
播放前按表达 ID 查找资源:
ts
private canPlay(entry: YueExpression): boolean {
return entry.audioResource !== undefined
}
没有资源时隐藏播放按钮,只显示粤拼或文本读音;不使用普通话 TTS 伪装成粤语。若采用系统支持的粤语 TTS,也要根据官方 API 的实际 locale 与设备支持矩阵实现,并在不可用时给出降级状态。
九、繁简展示应是内容策略,不是全局替换
一个清晰的卡片可以这样组织:
text
粤语原文:你食咗饭未?
粤拼:nei5 sik6 zo2 faan6 mei6
普通话释义:你吃饭了吗?
例句语境:日常问候
这不是"把整页切换为繁体",而是保留学习对象并提供解释。若增加繁简偏好,可以只控制解释文本的资源版本,不能修改题库答案所依赖的粤语原文。
错误做法包括:
- 将"咗"替换成普通话"了"后仍称为粤语原文。
- 对题干和答案分别转换,导致匹配失效。
- 用字符替换表生成粤拼。
- 把香港与广州语音差异压成唯一"标准答案"。
十、分库详情的文化 Profile 已经落地
BankDetailContent 为 b_yue 提供:
ts
{
subtitle: '从茶餐厅到地铁口,练最常见的粤语表达',
intro: '粤语题库覆盖基础发音、日常对话和城市常见短句,适合想先听懂、再敢开口的人。',
cultureNote: '内容会把早茶、通勤、港式流行语和礼貌表达放进同一套练习里,实用度很高。',
focusTags: ['听辨节奏', '礼貌表达', '生活口语'],
sceneTags: ['早茶点单', '通勤借过', '街头问路']
}
这些内容真实展示在详情页,但"基础发音""听辨节奏"是学习主题,不等于当前已有可验证音频。技术文章要把"题库覆盖的知识点"和"应用提供的播放能力"分开。
十一、章节、随机与考试入口保持同一 bankId
粤语分库可进入:
ts
{ bankId: 'b_yue', chapterId, mode: 'chapter' }
{ bankId: 'b_yue', mode: 'random' }
{ bankId: 'b_yue', mode: 'exam' }
PracticePage 应以 b_yue 查找题库题目,不依赖上一页标题或当前 Tab。参数校验包括:
- chapter 模式必须有属于粤语题库的章节 ID。
- random 模式只能从粤语题目抽取。
- exam 模式按粤语真实可用题量出卷。
- 无效题库或空题集不直接进入结果页。
如果用户从收藏或搜索进入粤语题目,也应携带同一题库 ID,使进度写回正确分区。
十二、本地进度不能被文本变体拆成两份
学习记录应使用稳定题目 ID,而不是题干文本作为 key。否则编辑人员把"你食咗饭未?"改成另一字形后,旧进度会丢失或重复。
当前题目 ID由:
ts
id: `${bankId}_q${i + 1}`
生成。它比题干稳定,但依赖数组顺序;在中间插入题目可能让后续 ID整体变化。长期维护可为每道题提供人工稳定 ID,例如:
ts
id: 'yue_daily_have_you_eaten_001'
文本、粤拼、释义和例句可以更新,学习记录仍归到同一道题。
十三、去重不能只依赖题干 trim()
当前目录按题干去重:
ts
const key = r.stem.trim()
if (seen.has(key)) continue
seen.add(key)
粤语文本中,直角引号、弯引号、全半角标点、空格和繁简差异都可能让语义相同题目绕过去重。可以建立内容规范化,仅用于去重 key,不修改展示文本:
ts
function normalizeForDedup(text: string): string {
return text
.normalize('NFKC')
.replace(/["""]/g, '')
.replace(/\s+/g, '')
.trim()
}
但不要自动做简繁转换去重,因为不同字形可能对应有意的教学差异。疑似重复应交给内容审核确认。
十四、Unicode 安全不等于语义安全
ArkTS 字符串能够保存粤语字符,但以下操作仍需谨慎:
- 不用
substring()按 UTF-16 单元截断用户可见文本。 - 搜索时明确是否区分繁简与异体字。
- 高亮匹配要保持原始索引。
- 导出和日志使用 UTF-8。
- JSON 与资源文件保持一致编码。
如果实现搜索别名,可以为每个表达维护 searchAliases:
ts
searchAliases: [
'食咗饭未',
'吃饭了吗',
'nei5 sik6 zo2 faan6 mei6'
]
显示仍使用原文,搜索可以匹配解释或粤拼。
十五、音频播放的状态机必须完整
真实音频能力至少包含:
text
idle
-> preparing
-> playing
-> completed
-> error
还要处理:
- 连续点击同一个按钮。
- 播放一个词时切换另一个词。
- 页面退到后台。
- 音频焦点被其他应用占用。
- 资源不存在或损坏。
- 在线引擎不可用。
按钮不能只显示一个永远不变的播放图标。加载、播放和错误状态要让用户看得懂;离开页面时停止并释放资源。
十六、多设备布局复用共享详情能力
YueBankPage 根容器满宽满高,BankDetailContent 根据断点和实际宽度切换:
- 常规窗口:单列滚动。
lg且宽度不小于 700vp:双栏。
底部随机练习与模拟考试按钮使用系统导航避让高度。若后续新增粤语词条音频区,应避免在左右两栏同时创建播放器;可以把播放器服务保持为页面级单实例。
2in1 还应支持鼠标悬停、键盘焦点与空格播放。音频按钮的触控/点击区域不能只有 20vp 图标本身。

十七、隐私与权限说明要对应在线回退
模块声明 ohos.permission.INTERNET。如果语音功能使用在线 TTS 回退,隐私与审核备注应准确说明:
- 核心题库与学习记录保存在本地。
- 若本地语音引擎不可用,系统语音能力可能尝试在线服务。
- 当前是否实际启用粤语播放,要以题型和 locale 支持为准。
- 应用不需要麦克风权限,因为这是语音输出而非录音。
不能同时写"完全离线"和保留在线引擎回退。如果产品决定使用纯本地真实音频资源,可以删除不必要的在线回退与网络权限,再重新构建验证。
十八、粤语分库测试矩阵
入口与参数
- YueBankPage 固定展示
b_yue。 - 外部传入其他 bankId 不覆盖固定 ID。
- 路由清单包含
pages/YueBankPage。 - 三种练习模式均保持
b_yue。
文本
- 粤语原文不被自动改写。
- "唔、冇、咗、攰、啲、嚟"真机无缺字。
- 普通话释义与粤语原文分字段。
- 大字体和窄屏不截断核心文本。
- 搜索别名不改变原文。
音频
audioHint不被当作音频资源。- 重分类后的 vocab 题不显示虚假听音按钮。
- 只有真实资源或已验证粤语 locale 才允许播放。
- 无资源时隐藏按钮。
- 播放失败有状态反馈。
数据
- 粤语进度不串到其他题库。
- 文本编辑后稳定题目 ID不变。
- 重复题目经规范化检查。
- 题量来自最终去重、重分类后的集合。
十九、可以宣传与不能宣传的边界
可以准确描述:
- 粤语分库拥有独立入口并固定
b_yue。 - 本地题库包含粤语字形、普通话解释和文化语境。
- 页面展示学习进度和六个章节。
- 支持章节、随机和考试入口。
- 共享详情适配 phone、tablet、2in1。
当前不能直接描述:
- 已实现简繁双向转换。
- 已提供标准粤语或真人粤语读音。
- 所有"听音题"当前都可播放。
audioHint是录音文件。zh-CNTTS 输出已经过粤语发音验证。
二十、总结
粤语分库的工程难点不在十几行入口代码,而在内容语义不能被技术名词掩盖。YueBankPage 真实完成了固定地区身份和共享详情复用;题库真实保存了粤语字形、释义和文化场景;练习链路真实支持章节、随机和考试。但当前没有运行时简繁转换,历史听音题被重分类为词汇题,audioHint 只是文本,zh-CN TTS 也不能证明粤语读音。
要把"繁简文本与读音"做成可复核能力,应使用结构化粤语原文、普通话解释、粤拼与真实音频资源,建立来源和播放状态,再经过真机字形、发音和多设备验证。保持这条事实边界,才既保护用户体验,也避免上架材料夸大功能。
AI 辅助声明: 本文由 AI 辅助整理,技术结论基于中国方言题库当前 YueBankPage、题库数据转换、TTS 初始化和练习入口真实源码复核;未将 audioHint、普通话 TTS 或已下线听音题描述为现成粤语音频能力。