Suggest post language correction (#2486)
* feat: suggested language * fix: wording correction * Factor out SuggestedLanguage into a separate component * Tighten the language-suggestion confidence to avoid false positives * Tweak the copy and UI * Add function fallbacks for safari --------- Co-authored-by: Mary <pineapplecreamcheese@skiff.com>
This commit is contained in:
parent
998ee29986
commit
95d771b3a5
3 changed files with 111 additions and 0 deletions
|
@ -22,6 +22,14 @@ export function code3ToCode2(lang: string): string {
|
||||||
return lang
|
return lang
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function code3ToCode2Strict(lang: string): string | undefined {
|
||||||
|
if (lang.length === 3) {
|
||||||
|
return LANGUAGES_MAP_CODE3[lang]?.code2
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
export function codeToLanguageName(lang: string): string {
|
export function codeToLanguageName(lang: string): string {
|
||||||
const lang2 = code3ToCode2(lang)
|
const lang2 = code3ToCode2(lang)
|
||||||
return LANGUAGES_MAP_CODE2[lang2]?.name || lang
|
return LANGUAGES_MAP_CODE2[lang2]?.name || lang
|
||||||
|
|
|
@ -45,6 +45,7 @@ import {Gallery} from './photos/Gallery'
|
||||||
import {MAX_GRAPHEME_LENGTH} from 'lib/constants'
|
import {MAX_GRAPHEME_LENGTH} from 'lib/constants'
|
||||||
import {LabelsBtn} from './labels/LabelsBtn'
|
import {LabelsBtn} from './labels/LabelsBtn'
|
||||||
import {SelectLangBtn} from './select-language/SelectLangBtn'
|
import {SelectLangBtn} from './select-language/SelectLangBtn'
|
||||||
|
import {SuggestedLanguage} from './select-language/SuggestedLanguage'
|
||||||
import {insertMentionAt} from 'lib/strings/mention-manip'
|
import {insertMentionAt} from 'lib/strings/mention-manip'
|
||||||
import {Trans, msg} from '@lingui/macro'
|
import {Trans, msg} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
|
@ -454,6 +455,7 @@ export const ComposePost = observer(function ComposePost({
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
) : null}
|
) : null}
|
||||||
|
<SuggestedLanguage text={richtext.text} />
|
||||||
<View style={[pal.border, styles.bottomBar]}>
|
<View style={[pal.border, styles.bottomBar]}>
|
||||||
{canSelectImages ? (
|
{canSelectImages ? (
|
||||||
<>
|
<>
|
||||||
|
|
101
src/view/com/composer/select-language/SuggestedLanguage.tsx
Normal file
101
src/view/com/composer/select-language/SuggestedLanguage.tsx
Normal file
|
@ -0,0 +1,101 @@
|
||||||
|
import React, {useEffect, useState} from 'react'
|
||||||
|
import {StyleSheet, View} from 'react-native'
|
||||||
|
import lande from 'lande'
|
||||||
|
import {Trans, msg} from '@lingui/macro'
|
||||||
|
import {useLingui} from '@lingui/react'
|
||||||
|
import {Text} from '../../util/text/Text'
|
||||||
|
import {Button} from '../../util/forms/Button'
|
||||||
|
import {code3ToCode2Strict, codeToLanguageName} from '#/locale/helpers'
|
||||||
|
import {
|
||||||
|
toPostLanguages,
|
||||||
|
useLanguagePrefs,
|
||||||
|
useLanguagePrefsApi,
|
||||||
|
} from '#/state/preferences/languages'
|
||||||
|
import {usePalette} from '#/lib/hooks/usePalette'
|
||||||
|
import {s} from '#/lib/styles'
|
||||||
|
import {
|
||||||
|
FontAwesomeIcon,
|
||||||
|
FontAwesomeIconStyle,
|
||||||
|
} from '@fortawesome/react-native-fontawesome'
|
||||||
|
|
||||||
|
// fallbacks for safari
|
||||||
|
const onIdle = globalThis.requestIdleCallback || (cb => setTimeout(cb, 1))
|
||||||
|
const cancelIdle = globalThis.cancelIdleCallback || clearTimeout
|
||||||
|
|
||||||
|
export function SuggestedLanguage({text}: {text: string}) {
|
||||||
|
const [suggestedLanguage, setSuggestedLanguage] = useState<string>()
|
||||||
|
const langPrefs = useLanguagePrefs()
|
||||||
|
const setLangPrefs = useLanguagePrefsApi()
|
||||||
|
const pal = usePalette('default')
|
||||||
|
const {_} = useLingui()
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const textTrimmed = text.trim()
|
||||||
|
|
||||||
|
// Don't run the language model on small posts, the results are likely
|
||||||
|
// to be inaccurate anyway.
|
||||||
|
if (textTrimmed.length < 40) {
|
||||||
|
setSuggestedLanguage(undefined)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const idle = onIdle(() => {
|
||||||
|
// Only select languages that have a high confidence and convert to code2
|
||||||
|
const result = lande(textTrimmed).filter(
|
||||||
|
([lang, value]) => value >= 0.97 && code3ToCode2Strict(lang),
|
||||||
|
)
|
||||||
|
|
||||||
|
setSuggestedLanguage(
|
||||||
|
result.length > 0 ? code3ToCode2Strict(result[0][0]) : undefined,
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
return () => cancelIdle(idle)
|
||||||
|
}, [text])
|
||||||
|
|
||||||
|
return suggestedLanguage &&
|
||||||
|
!toPostLanguages(langPrefs.postLanguage).includes(suggestedLanguage) ? (
|
||||||
|
<View style={[pal.border, styles.infoBar]}>
|
||||||
|
<FontAwesomeIcon
|
||||||
|
icon="language"
|
||||||
|
style={pal.text as FontAwesomeIconStyle}
|
||||||
|
size={24}
|
||||||
|
/>
|
||||||
|
<Text style={[pal.text, s.flex1]}>
|
||||||
|
<Trans>
|
||||||
|
Are you writing in{' '}
|
||||||
|
<Text type="sm-bold" style={pal.text}>
|
||||||
|
{codeToLanguageName(suggestedLanguage)}
|
||||||
|
</Text>
|
||||||
|
?
|
||||||
|
</Trans>
|
||||||
|
</Text>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
type="default"
|
||||||
|
onPress={() => setLangPrefs.setPostLanguage(suggestedLanguage)}
|
||||||
|
accessibilityLabel={_(
|
||||||
|
msg`Change post language to ${codeToLanguageName(suggestedLanguage)}`,
|
||||||
|
)}
|
||||||
|
accessibilityHint="">
|
||||||
|
<Text type="button" style={[pal.link, s.fw600]}>
|
||||||
|
<Trans>Yes</Trans>
|
||||||
|
</Text>
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
) : null
|
||||||
|
}
|
||||||
|
|
||||||
|
const styles = StyleSheet.create({
|
||||||
|
infoBar: {
|
||||||
|
flexDirection: 'row',
|
||||||
|
alignItems: 'center',
|
||||||
|
gap: 10,
|
||||||
|
borderWidth: 1,
|
||||||
|
borderRadius: 6,
|
||||||
|
paddingHorizontal: 16,
|
||||||
|
paddingVertical: 12,
|
||||||
|
marginHorizontal: 10,
|
||||||
|
marginBottom: 10,
|
||||||
|
},
|
||||||
|
})
|
Loading…
Add table
Add a link
Reference in a new issue