Flutter三方库适配OpenHarmony【flutter_speech】— 语言支持与 Locale 处理
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net语言支持是flutter_speech在OpenHarmony适配中最大的差异点。原插件在Android和iOS上支持6种语言(中文、英文、法文、俄文、意大利文、西班牙文),到了OpenHarmony只能用中文。这不是flutter_speech的问题,而是Core Speech Kit当
前言
欢迎加入开源鸿蒙跨平台社区:https://openharmonycrossplatform.csdn.net
语言支持是flutter_speech在OpenHarmony适配中最大的差异点。原插件在Android和iOS上支持6种语言(中文、英文、法文、俄文、意大利文、西班牙文),到了OpenHarmony只能用中文。
这不是flutter_speech的问题,而是Core Speech Kit当前的能力限制。作为适配者,我们能做的就是优雅地处理这个限制——让用户知道哪些语言不支持,而不是让App崩溃或者返回一堆乱码。
今天我们来看flutter_speech是怎么处理语言相关的逻辑的,包括locale格式转换、语言校验、错误码返回,以及中英混合识别的实际表现。
💡 本文对应源码:
FlutterSpeechPlugin.ets的convertLocale方法(第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 为什么只支持中文
几个可能的原因:
- 市场定位:OpenHarmony目前主要面向中国市场
- 模型训练:多语言模型需要大量的训练数据和算力
- 优先级:先把中文做好,再逐步扩展其他语言
- 生态阶段: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 错误信息的设计
错误信息包含了两个关键信息:
- 哪个语言不支持:
Language "${locale}",明确告诉用户传了什么语言 - 支持什么语言:
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 规律总结
- 常见英文单词/品牌名:能正确识别(WiFi、Email、iPhone、OK、Hello)
- 中英混合短句:能正确识别
- 纯英文短句:无法正确识别
- 英文长句:完全无法识别或产生音译
🎯 结论: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层不需要任何修改。因为:
- Dart层只负责传locale字符串
- 语言校验在原生端完成
- 原生端修改
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处理的完整实现:
- 语言限制:Core Speech Kit目前仅支持中文,这是最大的平台差异
- convertLocale:将下划线格式转为连字符格式(zh_CN → zh-CN)
- isSupportedLocale:宽松校验,只检查是否以"zh"开头
- 错误处理:不支持的语言返回ERROR_LANGUAGE_NOT_SUPPORTED,包含明确的错误信息
- 中英混合:常见英文单词可以识别,纯英文不行
- 扩展性:代码设计灵活,未来添加新语言只需修改isSupportedLocale
下一篇我们进入示例应用开发——看看example/lib/main.dart是怎么使用flutter_speech插件的。
如果这篇文章对你有帮助,欢迎点赞👍、收藏⭐、关注🔔,你的支持是我持续创作的动力!
相关资源:
更多推荐


所有评论(0)