【中国方言题库|04】HarmonyOS ArkTS 粤语分库实战:处理繁简文本、读音与练习入口

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

一、先从真实入口确认地区身份

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_c1yue_c6

分库入口和练习页传递的是题库 ID;章节模式还会传章节 ID。本地进度也按 bankId + chapterId 查询。任何一层混用 yueb_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 已经落地

BankDetailContentb_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-CN TTS 输出已经过粤语发音验证。

二十、总结

粤语分库的工程难点不在十几行入口代码,而在内容语义不能被技术名词掩盖。YueBankPage 真实完成了固定地区身份和共享详情复用;题库真实保存了粤语字形、释义和文化场景;练习链路真实支持章节、随机和考试。但当前没有运行时简繁转换,历史听音题被重分类为词汇题,audioHint 只是文本,zh-CN TTS 也不能证明粤语读音。

要把"繁简文本与读音"做成可复核能力,应使用结构化粤语原文、普通话解释、粤拼与真实音频资源,建立来源和播放状态,再经过真机字形、发音和多设备验证。保持这条事实边界,才既保护用户体验,也避免上架材料夸大功能。


AI 辅助声明: 本文由 AI 辅助整理,技术结论基于中国方言题库当前 YueBankPage、题库数据转换、TTS 初始化和练习入口真实源码复核;未将 audioHint、普通话 TTS 或已下线听音题描述为现成粤语音频能力。

相关推荐
m0_749690231 小时前
【寻迹校园 HarmonyOS NEXT 实战 06】共享权威词表:让发布表单与首页筛选使用同一套分类
harmonyos·arkts·数据建模·软件架构·arkui
独守一片天3 小时前
鸿蒙新生态服务卡片设计与状态同步
华为·harmonyos
m0_749690233 小时前
【寻迹校园 HarmonyOS NEXT 实战 07】不引入全局状态库:用 dataRevision 实现跨页面刷新
harmonyos·arkts·软件架构·状态管理·arkui
贾伟康4 小时前
【中国方言题库|01】HarmonyOS ArkTS 方言题库首页实战:组织地区入口、推荐内容和学习进度
harmonyos·arkts·arkui·多设备适配·本地数据
OH_TPC6 小时前
HarmonyOS APP开发---"壁纸控"壁纸App,需要用到这个库
harmonyos
独守一片天6 小时前
鸿蒙穿戴设备与健康服务闭环
华为·harmonyos
Magic-ZYJ6 小时前
隐私优先 HarmonyOS 应用怎么设计:无账号、无后台、无统计 SDK
华为·harmonyos·鸿蒙·独立开发者·心晴手记
独守一片天6 小时前
鸿蒙车机手机协同服务生态
华为·智能手机·harmonyos
2501_919749037 小时前
华为鸿蒙测手速APP—小羊手速
华为·harmonyos·鸿蒙