Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/config/models/vision.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ const visionAllowedModels = [
'qwen2-vl',
'qwen2.5-vl',
'qwen3-vl',
'qwen3\\.[5-9](?:-[\\w-]+)?',
'qwen2.5-omni',
'qwen3-omni(?:-[\\w-]+)?',
'qvq',
Expand Down
49 changes: 44 additions & 5 deletions src/hooks/useSpeechRecognition.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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'

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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)
Expand All @@ -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)
Expand Down
8 changes: 7 additions & 1 deletion src/i18n/locales/en-us.json
Original file line number Diff line number Diff line change
Expand Up @@ -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."
}
}
}
8 changes: 7 additions & 1 deletion src/i18n/locales/ja-jp.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "マイク権限が拒否されました。システム設定でマイクへのアクセスを許可してください。"
}
}
}
8 changes: 7 additions & 1 deletion src/i18n/locales/ru-ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "Доступ к микрофону запрещён. Разрешите доступ к микрофону в настройках системы."
}
}
}
8 changes: 7 additions & 1 deletion src/i18n/locales/zh-cn.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "麦克风权限被拒绝,请在系统设置中允许访问麦克风。"
}
}
}
8 changes: 7 additions & 1 deletion src/i18n/locales/zh-tw.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": "麥克風權限被拒絕,請在系統設定中允許存取麥克風。"
}
}
}