Handle more translation errors (#10160)

This commit is contained in:
DS Boyce
2026-04-01 13:39:05 -07:00
committed by GitHub
parent 69164c640f
commit 5a2135733f
3 changed files with 40 additions and 10 deletions
+8
View File
@@ -10,6 +10,10 @@ const iOSMajorVersion =
Platform.OS === 'ios' && typeof Platform.Version === 'string'
? parseInt(Platform.Version.split('.')[0], 10)
: 0
const androidPlatformVersion =
Platform.OS === 'android' && typeof Platform.Version === 'number'
? Platform.Version
: 0
/**
* The semver version of the app, specified in our `package.json`.file. On
@@ -49,3 +53,7 @@ export const IS_WEB_FIREFOX: boolean = false
export const IS_HIGH_DPI: boolean = true
// ideally we'd use isLiquidGlassAvailable() from expo-glass-effect but checking iOS version is good enough for now
export const IS_LIQUID_GLASS: boolean = iOSMajorVersion >= 26
// So we can avoid attempting on-device translation when we know it's unsupported.
export const IS_TRANSLATION_SUPPORTED: boolean =
(IS_IOS && iOSMajorVersion >= 18) ||
(IS_ANDROID && androidPlatformVersion > 22)
+1
View File
@@ -48,3 +48,4 @@ export const IS_HIGH_DPI: boolean = window.matchMedia(
'(min-resolution: 2dppx)',
).matches
export const IS_LIQUID_GLASS: boolean = false
export const IS_TRANSLATION_SUPPORTED: boolean = false
+31 -10
View File
@@ -1,18 +1,17 @@
import {useCallback, useContext, useEffect, useMemo, useState} from 'react'
import {LayoutAnimation, Platform} from 'react-native'
import {getLocales} from 'expo-localization'
import {
isTranslationSupported,
onTranslateTask,
} from '@bsky.app/expo-translate-text'
import {onTranslateTask} from '@bsky.app/expo-translate-text'
import {type TranslationTaskResult} from '@bsky.app/expo-translate-text/build/ExpoTranslateText.types'
import {useLingui} from '@lingui/react/macro'
import {useFocusEffect} from '@react-navigation/native'
import {useGoogleTranslate} from '#/lib/hooks/useGoogleTranslate'
import {codeToLanguageName} from '#/locale/helpers'
import {logger} from '#/logger'
import {useLanguagePrefs} from '#/state/preferences'
import {useAnalytics} from '#/analytics'
import {IS_ANDROID, IS_IOS} from '#/env'
import {IS_ANDROID, IS_IOS, IS_TRANSLATION_SUPPORTED} from '#/env'
import {Context} from './context'
import {
type ContextType,
@@ -25,6 +24,11 @@ import {guessLanguage} from './utils'
export * from './types'
export * from './utils'
const E_SAME_AS_SOURCE_LANGUAGE =
'Translation result is the same as the source text.'
const E_EMPTY_RESULT = 'Translation result is empty.'
const E_INVALID_SOURCE_LANGUAGE = 'Invalid source language'
/**
* Attempts on-device translation via @bsky.app/expo-translate-text.
* Uses a lazy import to avoid crashing if the native module isn't linked into
@@ -80,11 +84,11 @@ async function attemptTranslation(
typeof result.translatedTexts === 'string' ? result.translatedTexts : ''
if (translatedText === input) {
throw new Error('Translation result is the same as the source text.')
throw new Error(E_SAME_AS_SOURCE_LANGUAGE)
}
if (translatedText === '') {
throw new Error('Translation result is empty.')
throw new Error(E_EMPTY_RESULT)
}
return {
@@ -159,6 +163,7 @@ export function Provider({children}: React.PropsWithChildren<unknown>) {
>({})
const [refCounts, setRefCounts] = useState<Record<string, number>>({})
const ax = useAnalytics()
const langPrefs = useLanguagePrefs()
const {t: l} = useLingui()
const googleTranslate = useGoogleTranslate()
@@ -235,7 +240,7 @@ export function Provider({children}: React.PropsWithChildren<unknown>) {
googleTranslate: shouldForceGoogleTranslate,
})
if (shouldForceGoogleTranslate || !isTranslationSupported()) {
if (shouldForceGoogleTranslate || !IS_TRANSLATION_SUPPORTED) {
await googleTranslate(
text,
expectedTargetLanguage,
@@ -280,7 +285,8 @@ export function Provider({children}: React.PropsWithChildren<unknown>) {
postLanguages: possibleSourceLanguages,
},
}))
} catch (e) {
} catch (err) {
const e = err as Error
logger.error('Failed to translate text on device', {safeMessage: e})
// On-device translation failed (language pack missing or user
// dismissed the download prompt).
@@ -295,6 +301,21 @@ export function Provider({children}: React.PropsWithChildren<unknown>) {
textLength: text.length,
})
let errorMessage = l`Device failed to translate :(`
if (e.message === E_SAME_AS_SOURCE_LANGUAGE) {
errorMessage = l`Translation to the same language is unavailable on your device.`
}
if (e.message === E_EMPTY_RESULT) {
errorMessage = l`No translation received from your device.`
}
if (
expectedSourceLanguage &&
e.message.includes(E_INVALID_SOURCE_LANGUAGE)
) {
errorMessage = l`${codeToLanguageName(
expectedSourceLanguage,
langPrefs.appLanguage,
)} is not supported by your device.`
}
if (!IS_ANDROID) {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut)
}
@@ -304,7 +325,7 @@ export function Provider({children}: React.PropsWithChildren<unknown>) {
}))
}
},
[ax, googleTranslate, l],
[ax, googleTranslate, l, langPrefs.appLanguage],
)
const ctx = useMemo(