From 8442cc8b8952b2be46342c06c84bfd0c8c79e7bc Mon Sep 17 00:00:00 2001 From: Titan Date: Tue, 26 May 2026 08:31:35 +0800 Subject: [PATCH 1/2] fix: add Qwen3.5 and Qwen3.6 to vision model regex Qwen3.5 and Qwen3.6 series are vision models but their IDs don't contain 'vl' suffix (e.g. Qwen3.6-35B), so they were not matched by the existing qwen3-vl pattern. Co-Authored-by: Claude --- src/config/models/vision.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/config/models/vision.ts b/src/config/models/vision.ts index 8566f6bd7..5ae8237b1 100644 --- a/src/config/models/vision.ts +++ b/src/config/models/vision.ts @@ -26,6 +26,7 @@ const visionAllowedModels = [ 'qwen2-vl', 'qwen2.5-vl', 'qwen3-vl', + 'qwen3\\.[5-9](?:-[\\w-]+)?', 'qwen2.5-omni', 'qwen3-omni(?:-[\\w-]+)?', 'qvq', From 3697934426ca961ca08f5252f2da8019f2a57d65 Mon Sep 17 00:00:00 2001 From: Titan Date: Tue, 26 May 2026 13:11:38 +0800 Subject: [PATCH 2/2] fix: improve speech recognition error messages with i18n Replace generic error messages with detailed, localized messages for each error type (service-not-allowed, no-speech, audio-capture, etc.). On Android devices without a compatible speech service (e.g. MIUI without Google Play Services), users now see a clear explanation instead of the cryptic "Insufficient permissions" error. Co-Authored-by: Claude --- src/hooks/useSpeechRecognition.ts | 49 +++++++++++++++++++++++++++---- src/i18n/locales/en-us.json | 8 ++++- src/i18n/locales/ja-jp.json | 8 ++++- src/i18n/locales/ru-ru.json | 8 ++++- src/i18n/locales/zh-cn.json | 8 ++++- src/i18n/locales/zh-tw.json | 8 ++++- 6 files changed, 79 insertions(+), 10 deletions(-) diff --git a/src/hooks/useSpeechRecognition.ts b/src/hooks/useSpeechRecognition.ts index 4728fa96e..1364cf53e 100644 --- a/src/hooks/useSpeechRecognition.ts +++ b/src/hooks/useSpeechRecognition.ts @@ -2,6 +2,7 @@ import { ExpoSpeechRecognitionModule, useSpeechRecognitionEvent } from 'expo-spe import { useRef, useState } from 'react' import { Platform } from 'react-native' +import { useTranslation } from 'react-i18next' import i18n from '@/i18n' import { loggerService } from '@/services/LoggerService' @@ -45,7 +46,38 @@ const supportsLanguageDetection = (): boolean => { return false } +/** + * Map speech recognition error codes to i18n keys + */ +const getErrorI18nKey = (error: string): string => { + if (error === 'service-not-allowed') { + return 'service_not_available' + } + if (error === 'no-speech') { + return 'no_speech' + } + if (error === 'audio-capture') { + return 'audio_capture' + } + if ( + error === 'network' || + error === 'network-timeout' || + error === 'server' || + error === 'server-disconnected' + ) { + return 'network_error' + } + if (error === 'language-not-supported') { + return 'language_not_supported' + } + if (error === 'not-allowed') { + return 'permission_not_allowed' + } + return 'error' +} + export const useSpeechRecognition = (options: UseSpeechRecognitionOptions = {}) => { + const { t } = useTranslation() const { onTranscript, onError } = options // Use refs for callbacks to prevent stale closures in event listeners @@ -91,10 +123,17 @@ export const useSpeechRecognition = (options: UseSpeechRecognitionOptions = {}) // Listen for errors useSpeechRecognitionEvent('error', event => { - logger.error('Speech recognition error:', new Error(event.message), { code: event.error }) - setError(event.message) + const i18nKey = getErrorI18nKey(event.error) + const detailedMessage = t(`voice.${i18nKey}`) + logger.error('Speech recognition error:', new Error(event.message), { + code: event.error, + message: event.message, + i18nKey, + detailedMessage + }) + setError(detailedMessage) setStatus('idle') - onErrorRef.current?.(event.message) + onErrorRef.current?.(detailedMessage) }) // Start speech recognition @@ -106,7 +145,7 @@ export const useSpeechRecognition = (options: UseSpeechRecognitionOptions = {}) // Check if recognition is available const isAvailable = await ExpoSpeechRecognitionModule.isRecognitionAvailable() if (!isAvailable) { - const errorMsg = 'Speech recognition is not available on this device' + const errorMsg = t('voice.not_available') logger.warn(errorMsg) setError(errorMsg) onErrorRef.current?.(errorMsg) @@ -117,7 +156,7 @@ export const useSpeechRecognition = (options: UseSpeechRecognitionOptions = {}) // Request permissions const permissionResult = await ExpoSpeechRecognitionModule.requestPermissionsAsync() if (!permissionResult.granted) { - const errorMsg = 'Speech recognition permission denied' + const errorMsg = t('voice.permission_denied_message') logger.info(errorMsg) setError(errorMsg) onErrorRef.current?.(errorMsg) diff --git a/src/i18n/locales/en-us.json b/src/i18n/locales/en-us.json index 8449829d7..196e8f6bc 100644 --- a/src/i18n/locales/en-us.json +++ b/src/i18n/locales/en-us.json @@ -1055,7 +1055,13 @@ "permission_denied": "Microphone permission denied", "permission_denied_message": "Please allow Cherry Studio to access your microphone in Settings", "start": "Start voice input", - "stop": "Stop voice input" + "stop": "Stop voice input", + "service_not_available": "Speech recognition requires a system speech service. No compatible service (such as Google Play Services) is installed or available on this device.", + "no_speech": "No speech detected. Please try again.", + "audio_capture": "Microphone access failed. Please check your microphone permissions.", + "network_error": "Network error. Please check your connection and try again.", + "language_not_supported": "Speech recognition is not supported in the current language.", + "permission_not_allowed": "Microphone permission was denied. Please allow microphone access in system settings." } } } diff --git a/src/i18n/locales/ja-jp.json b/src/i18n/locales/ja-jp.json index 0483c83a2..a7f747600 100644 --- a/src/i18n/locales/ja-jp.json +++ b/src/i18n/locales/ja-jp.json @@ -1055,7 +1055,13 @@ "permission_denied": "マイクの権限が拒否されました", "permission_denied_message": "設定でCherry Studioにマイクへのアクセスを許可してください", "start": "音声入力を開始", - "stop": "音声入力を停止" + "stop": "音声入力を停止", + "service_not_available": "音声認識にはシステム音声サービスが必要です。このデバイスに互換性の音声サービス(Google Playサービスなど)がインストールされていないか、利用できません。", + "no_speech": "音声を検知しませんでした。もう一度お試しください。", + "audio_capture": "マイクへのアクセスに失敗しました。マイク権限を確認してください。", + "network_error": "ネットワークエラーです。ネットワーク接続を確認して再度お試しください。", + "language_not_supported": "現在の言語では音声認識がサポートされていません。", + "permission_not_allowed": "マイク権限が拒否されました。システム設定でマイクへのアクセスを許可してください。" } } } diff --git a/src/i18n/locales/ru-ru.json b/src/i18n/locales/ru-ru.json index cf46971e1..94d670249 100644 --- a/src/i18n/locales/ru-ru.json +++ b/src/i18n/locales/ru-ru.json @@ -1055,7 +1055,13 @@ "permission_denied": "Доступ к микрофону запрещён", "permission_denied_message": "Пожалуйста, разрешите Cherry Studio доступ к микрофону в настройках", "start": "Начать голосовой ввод", - "stop": "Остановить голосовой ввод" + "stop": "Остановить голосовой ввод", + "service_not_available": "Для распознавания речи требуется системная речевая служба. Совместимая речевая служба (например, Google Play Services) не установлена или недоступна на этом устройстве.", + "no_speech": "Речь не обнаружена. Пожалуйста, попробуйте ещё раз.", + "audio_capture": "Не удалось получить доступ к микрофону. Проверьте права на использование микрофона.", + "network_error": "Ошибка сети. Проверьте подключение и попробуйте снова.", + "language_not_supported": "Распознавание речи не поддерживается в текущем языке.", + "permission_not_allowed": "Доступ к микрофону запрещён. Разрешите доступ к микрофону в настройках системы." } } } diff --git a/src/i18n/locales/zh-cn.json b/src/i18n/locales/zh-cn.json index 4a3264d7b..91b697954 100644 --- a/src/i18n/locales/zh-cn.json +++ b/src/i18n/locales/zh-cn.json @@ -1055,7 +1055,13 @@ "permission_denied": "麦克风权限被拒绝", "permission_denied_message": "请在设置中允许 Cherry Studio 访问麦克风", "start": "开始语音输入", - "stop": "停止语音输入" + "stop": "停止语音输入", + "service_not_available": "语音识别需要使用系统语音服务,当前系统中未安装或无法使用兼容的语音服务(如 Google 服务框架)。", + "no_speech": "未检测到语音,请重试。", + "audio_capture": "麦克风访问失败,请检查麦克风权限。", + "network_error": "网络错误,请检查网络连接后重试。", + "language_not_supported": "当前语言不支持语音识别。", + "permission_not_allowed": "麦克风权限被拒绝,请在系统设置中允许访问麦克风。" } } } diff --git a/src/i18n/locales/zh-tw.json b/src/i18n/locales/zh-tw.json index e16a4486b..75a7ff954 100644 --- a/src/i18n/locales/zh-tw.json +++ b/src/i18n/locales/zh-tw.json @@ -1055,7 +1055,13 @@ "permission_denied": "麥克風權限被拒絕", "permission_denied_message": "請在設定中允許 Cherry Studio 存取麥克風", "start": "開始語音輸入", - "stop": "停止語音輸入" + "stop": "停止語音輸入", + "service_not_available": "語音識別需要使用系統語音服務,當前系統中未安裝或無法使用相容的語音服務(如 Google 服務框架)。", + "no_speech": "未偵測到語音,請重試。", + "audio_capture": "麥克風存取失敗,請檢查麥克風權限。", + "network_error": "網路錯誤,請檢查網路連線後重試。", + "language_not_supported": "當前語言不支援語音識別。", + "permission_not_allowed": "麥克風權限被拒絕,請在系統設定中允許存取麥克風。" } } }