前言

欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net

语言支持是flutter_speech在OpenHarmony适配中最大的差异点。原插件在Android和iOS上支持6种语言(中文、英文、法文、俄文、意大利文、西班牙文),到了OpenHarmony只能用中文。

这不是flutter_speech的问题,而是Core Speech Kit当前的能力限制。作为适配者,我们能做的就是优雅地处理这个限制——让用户知道哪些语言不支持,而不是让App崩溃或者返回一堆乱码。

今天我们来看flutter_speech是怎么处理语言相关的逻辑的,包括locale格式转换、语言校验、错误码返回,以及中英混合识别的实际表现。

💡 本文对应源码FlutterSpeechPlugin.etsconvertLocale方法(第133-135行)和isSupportedLocale方法(第137-139行)。

一、OpenHarmony Core Speech Kit 语言限制分析

请添加图片描述

1.1 当前支持的语言

截至API 20版本,Core Speech Kit的语音识别仅支持中文(普通话)

语言 BCP 47代码 支持状态 备注
中文(普通话) zh-CN ✅ 支持 唯一支持的语言
英文(美式) en-US ❌ 不支持 未来可能支持
法文 fr-FR ❌ 不支持 暂无计划
俄文 ru-RU ❌ 不支持 暂无计划
意大利文 it-IT ❌ 不支持 暂无计划
西班牙文 es-ES ❌ 不支持 暂无计划

1.2 与其他平台的对比

平台 支持语言数 主要语言
Android 100+ 几乎所有主流语言
iOS 60+ 主流语言
OpenHarmony 1 仅中文

差距确实很大。但从另一个角度看,OpenHarmony的中文识别准确率还是不错的,对于国内用户来说,中文是最常用的语言。

1.3 为什么只支持中文

几个可能的原因:

  1. 市场定位:OpenHarmony目前主要面向中国市场
  2. 模型训练:多语言模型需要大量的训练数据和算力
  3. 优先级:先把中文做好,再逐步扩展其他语言
  4. 生态阶段:OpenHarmony还在快速发展期,功能会逐步完善

🤔 个人看法:语言限制是暂时的。随着OpenHarmony生态的发展和AI能力的增强,多语言支持只是时间问题。我们现在要做的是把适配框架搭好,等新语言支持了,改动量会很小。

二、convertLocale 方法:下划线到连字符的转换

2.1 问题背景

Dart层和Core Speech Kit使用不同的locale格式:

Dart层格式:    zh_CN   en_US   fr_FR   (下划线分隔)
Core Kit格式:  zh-CN   en-US   fr-FR   (连字符分隔)

2.2 实现代码

private convertLocale(locale: string): string {
  return locale.replace('_', '-');
}

一行代码,简洁明了。

2.3 转换示例

输入(Dart) 输出(Core Kit) 说明
“zh_CN” “zh-CN” 中文(中国)
“en_US” “en-US” 英文(美国)
“fr_FR” “fr-FR” 法文(法国)
“zh” “zh” 只有语言码,无需转换
“zh_Hans_CN” “zh-Hans_CN” ⚠️ 只替换第一个下划线

2.4 潜在问题

replace('_', '-')只替换第一个下划线。如果locale是三段式的(如zh_Hans_CN),第二个下划线不会被替换:

"zh_Hans_CN".replace('_', '-')  // 结果:"zh-Hans_CN"(不完全正确)
// 正确应该是:"zh-Hans-CN"

不过flutter_speech的Dart层只传两段式locale(如zh_CN),所以这个问题在实际使用中不会出现。

如果要做得更健壮:

// 全局替换版本
private convertLocale(locale: string): string {
  return locale.replace(/_/g, '-');  // 替换所有下划线
}

2.5 为什么不在Dart层做转换

你可能会想:为什么不在Dart层直接传zh-CN格式?

原因是保持Dart API的平台无关性。Dart层使用的是Flutter标准的locale格式(下划线分隔),不应该因为某个平台的要求而改变。格式转换是平台适配层的职责。

Dart层:统一使用 zh_CN 格式(平台无关)
    │
    ├── Android端:直接使用 zh_CN(Android也接受这种格式)
    ├── iOS端:转换为 zh-CN(iOS的NSLocale使用连字符)
    └── OpenHarmony端:转换为 zh-CN(Core Speech Kit使用连字符)

三、isSupportedLocale 语言校验逻辑

3.1 实现代码

private isSupportedLocale(locale: string): boolean {
  return locale.startsWith('zh');
}

3.2 校验逻辑

只要locale以zh开头,就认为是支持的。这涵盖了:

locale startsWith(‘zh’) 是否支持
“zh-CN” ✅ true ✅ 支持
“zh-TW” ✅ true ⚠️ 可能支持(繁体)
“zh-HK” ✅ true ⚠️ 可能支持(粤语?)
“zh” ✅ true ✅ 支持
“en-US” ❌ false ❌ 不支持
“fr-FR” ❌ false ❌ 不支持

3.3 校验的宽松度

这个校验比较宽松——只检查语言码是否为中文,不检查地区码。这意味着zh-TW(台湾繁体)和zh-HK(香港粤语)也会通过校验。

实际上Core Speech Kit主要支持的是普通话,对于粤语和台湾腔的识别效果可能不理想。但让它们通过校验也没什么坏处——最多识别准确率低一些,不会崩溃。

3.4 更严格的校验方案

如果需要更精确的校验:

// 严格版本(flutter_speech未采用)
private isSupportedLocale(locale: string): boolean {
  const supportedLocales = ['zh-CN', 'zh'];
  return supportedLocales.includes(locale);
}
// 中等严格版本
private isSupportedLocale(locale: string): boolean {
  return locale === 'zh-CN' || locale === 'zh' || locale.startsWith('zh-');
}

flutter_speech选择了最宽松的方案,这是一个合理的决策——宁可让用户试试(可能识别效果不好),也不要直接拒绝。

3.5 校验在activate流程中的位置

// activate方法中的调用顺序
const language = this.convertLocale(locale);  // 1. 先转换格式
if (!this.isSupportedLocale(language)) {       // 2. 再校验
  result.error('ERROR_LANGUAGE_NOT_SUPPORTED',
    `Language "${locale}" is not supported on HarmonyOS. Only Chinese (zh_CN) is currently supported.`,
    null);
  return;
}

注意校验的是转换后的locale(连字符格式),不是原始的Dart格式。

四、不支持语言的错误码返回:ERROR_LANGUAGE_NOT_SUPPORTED

4.1 错误码设计

result.error(
  'ERROR_LANGUAGE_NOT_SUPPORTED',                    // 错误码
  `Language "${locale}" is not supported on HarmonyOS. Only Chinese (zh_CN) is currently supported.`,  // 错误信息
  null                                                // 错误详情
);
字段 说明
code ERROR_LANGUAGE_NOT_SUPPORTED 标准化错误码
message Language “en_US” is not supported… 人类可读的错误描述
details null 无额外详情

4.2 错误信息的设计

错误信息包含了两个关键信息:

  1. 哪个语言不支持Language "${locale}",明确告诉用户传了什么语言
  2. 支持什么语言Only Chinese (zh_CN) is currently supported,告诉用户应该用什么

💡 好的错误信息应该回答两个问题:“出了什么问题"和"怎么解决”。这条错误信息两个都回答了。

4.3 Dart层的错误处理

_speech.activate(selectedLocale).then((res) {
  setState(() => _speechRecognitionAvailable = res);
}).catchError((e) {
  if (e is PlatformException) {
    if (e.code == 'ERROR_LANGUAGE_NOT_SUPPORTED') {
      // 显示语言不支持的提示
      ScaffoldMessenger.of(context).showSnackBar(
        SnackBar(content: Text('当前语言不支持,请选择中文')),
      );
    }
  }
  setState(() => _speechRecognitionAvailable = false);
});

4.4 示例App中的语言选择

flutter_speech的示例App提供了语言选择功能:

const languages = [
  Language("中文", "zh_CN"),
  Language("English", "en_US"),
  Language("Français", "fr_FR"),
  Language("Pусский", "ru_RU"),
  Language("Italiano", "it_IT"),
  Language("Español", "es_ES"),
];

在OpenHarmony上,只有中文能正常工作。选择其他语言会收到ERROR_LANGUAGE_NOT_SUPPORTED错误。

4.5 平台感知的语言列表

示例App中有一个平台判断逻辑,在OpenHarmony上只显示中文选项:

// example/lib/main.dart中的平台判断
if (Platform.isOhos) {
  // OpenHarmony只显示中文
  languages = [Language("中文", "zh_CN")];
}

这是一种更好的用户体验——直接不显示不支持的选项,而不是让用户选了再报错。

五、中英混合识别能力与纯英语识别的限制

5.1 实际测试结果

虽然Core Speech Kit官方只支持中文,但我做了一些中英混合的测试:

输入语音 识别结果 准确度
“打开WiFi” “打开WiFi” ✅ 准确
“发送Email” “发送Email” ✅ 准确
“搜索iPhone 15” “搜索iPhone 15” ✅ 准确
“Hello你好” “Hello你好” ✅ 准确
“OK没问题” “OK没问题” ✅ 准确
“Please open the door” 识别失败或乱码 ❌ 不准确
“What’s the weather” 识别失败 ❌ 不准确
“Thank you very much” “三克油外瑞马奇” ❌ 音译

5.2 规律总结

  1. 常见英文单词/品牌名:能正确识别(WiFi、Email、iPhone、OK、Hello)
  2. 中英混合短句:能正确识别
  3. 纯英文短句:无法正确识别
  4. 英文长句:完全无法识别或产生音译

🎯 结论:Core Speech Kit的中文模型内置了常见英文词汇的识别能力,但不支持纯英文输入。对于日常使用场景(中文为主,偶尔夹杂英文单词),识别效果是可以接受的。

5.3 对用户的建议

如果你的App面向中国用户,可以在UI中提示:

支持语言:中文(普通话)
提示:支持中英混合输入,如"打开WiFi设置"
注意:不支持纯英文输入

六、未来多语言支持的扩展设计

6.1 当前代码的扩展性

flutter_speech的语言处理代码设计得比较灵活,未来添加新语言支持很容易:

// 当前实现
private isSupportedLocale(locale: string): boolean {
  return locale.startsWith('zh');
}

// 未来扩展(假设支持了英文)
private isSupportedLocale(locale: string): boolean {
  const supportedPrefixes = ['zh', 'en'];  // 添加新语言前缀
  return supportedPrefixes.some(prefix => locale.startsWith(prefix));
}

// 更远的未来(支持多种语言)
private isSupportedLocale(locale: string): boolean {
  const supportedLocales = ['zh-CN', 'en-US', 'ja-JP', 'ko-KR'];
  return supportedLocales.some(supported =>
    locale.startsWith(supported.split('-')[0])
  );
}

6.2 动态语言检测

更好的方案是运行时动态检测支持的语言,而不是硬编码:

// 理想方案(Core Speech Kit未提供此API)
private async getSupportedLanguages(): Promise<string[]> {
  return await speechRecognizer.getSupportedLanguages();
}

// 然后在校验时使用
private async isSupportedLocale(locale: string): Promise<boolean> {
  const supported = await this.getSupportedLanguages();
  return supported.includes(locale);
}

遗憾的是Core Speech Kit目前没有提供getSupportedLanguages这样的API。如果未来提供了,flutter_speech可以很容易地切换到动态检测。

6.3 Dart层的适配

如果OpenHarmony未来支持了新语言,Dart层不需要任何修改。因为:

  1. Dart层只负责传locale字符串
  2. 语言校验在原生端完成
  3. 原生端修改isSupportedLocale即可
Dart层:activate("en_US")  ← 不需要改
    │
    └── Native端:isSupportedLocale("en-US")
        ├── 当前:return false → 报错
        └── 未来:return true → 正常创建引擎

这就是关注点分离的好处——语言支持的变化只影响原生端,不影响Dart层。

七、locale处理的完整流程

7.1 从用户选择到引擎创建

用户在UI中选择"中文"
    │
    └── Dart: _speech.activate("zh_CN")
         │
         └── Native: onMethodCall("speech.activate", "zh_CN")
              │
              ├── convertLocale("zh_CN") → "zh-CN"
              │
              ├── isSupportedLocale("zh-CN") → true
              │
              └── createEngine({ language: "zh-CN", online: 1 })
                   │
                   └── 引擎创建成功 ✅

7.2 不支持语言的流程

用户在UI中选择"English"
    │
    └── Dart: _speech.activate("en_US")
         │
         └── Native: onMethodCall("speech.activate", "en_US")
              │
              ├── convertLocale("en_US") → "en-US"
              │
              ├── isSupportedLocale("en-US") → false ❌
              │
              └── result.error("ERROR_LANGUAGE_NOT_SUPPORTED", ...)
                   │
                   └── Dart: catchError → 显示错误提示

7.3 所有locale相关的代码汇总

flutter_speech中与locale相关的代码一共只有4处

// 1. activate方法中接收locale参数
private async activate(locale: string, result: MethodResult): Promise<void> {

// 2. 格式转换
const language = this.convertLocale(locale);

// 3. 语言校验
if (!this.isSupportedLocale(language)) {

// 4. 传给createEngine
this.asrEngine = await speechRecognizer.createEngine({
  language: language,

代码量很少,但每一处都有明确的职责。

八、与Android/iOS语言处理的对比

8.1 Android的语言处理

// Android不做语言校验,直接传给系统
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE, locale);
speechRecognizer.startListening(intent);
// 如果语言不支持,系统会通过onError回调通知

Android的策略是不校验,让系统处理。如果语言不支持,RecognitionListener.onError会收到错误。

8.2 iOS的语言处理

// iOS在创建识别器时指定语言
SFSpeechRecognizer *recognizer = [[SFSpeechRecognizer alloc]
    initWithLocale:[NSLocale localeWithLocaleIdentifier:locale]];
// 如果语言不支持,recognizer可能为nil
if (!recognizer) {
    result([FlutterError errorWithCode:@"LANGUAGE_NOT_SUPPORTED" ...]);
}

iOS的策略是创建时检测——如果语言不支持,识别器创建会返回nil。

8.3 OpenHarmony的语言处理

// OpenHarmony在创建引擎前主动校验
if (!this.isSupportedLocale(language)) {
    result.error('ERROR_LANGUAGE_NOT_SUPPORTED', ...);
    return;
}
this.asrEngine = await speechRecognizer.createEngine({ language: language, ... });

OpenHarmony的策略是主动校验——在调用createEngine之前就检查语言是否支持。

8.4 三种策略的对比

策略 平台 优点 缺点
不校验 Android 代码简单 错误信息不明确
创建时检测 iOS 利用系统能力 需要处理nil
主动校验 OpenHarmony 错误信息明确 需要维护支持列表

flutter_speech选择了主动校验,这样可以给用户最明确的错误提示——直接告诉用户"这个语言不支持,请用中文"。

总结

本文详细讲解了flutter_speech中语言支持与Locale处理的完整实现:

  1. 语言限制:Core Speech Kit目前仅支持中文,这是最大的平台差异
  2. convertLocale:将下划线格式转为连字符格式(zh_CN → zh-CN)
  3. isSupportedLocale:宽松校验,只检查是否以"zh"开头
  4. 错误处理:不支持的语言返回ERROR_LANGUAGE_NOT_SUPPORTED,包含明确的错误信息
  5. 中英混合:常见英文单词可以识别,纯英文不行
  6. 扩展性:代码设计灵活,未来添加新语言只需修改isSupportedLocale

下一篇我们进入示例应用开发——看看example/lib/main.dart是怎么使用flutter_speech插件的。

如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!


相关资源:

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐