import {useCallback, useMemo} from 'react' import {Platform, View} from 'react-native' import {type AppBskyFeedDefs} from '@atproto/api' import {Trans, useLingui} from '@lingui/react/macro' import {HITSLOP_30} from '#/lib/constants' import {useGoogleTranslate} from '#/lib/hooks/useGoogleTranslate' import {useTranslate} from '#/lib/translation' import {type TranslationFunction} from '#/lib/translation' import { codeToLanguageName, isPostInLanguage, languageName, } from '#/locale/helpers' import {LANGUAGES} from '#/locale/languages' import {useLanguagePrefs} from '#/state/preferences' import {atoms as a, native, useTheme, web} from '#/alf' import {Button} from '#/components/Button' import {ArrowRight_Stroke2_Corner0_Rounded as ArrowRightIcon} from '#/components/icons/Arrow' import {TimesLarge_Stroke2_Corner0_Rounded as XIcon} from '#/components/icons/Times' import {Warning_Stroke2_Corner0_Rounded as WarningIcon} from '#/components/icons/Warning' import {createStaticClick, Link} from '#/components/Link' import {Loader} from '#/components/Loader' import * as Select from '#/components/Select' import {Text} from '#/components/Typography' import {useAnalytics} from '#/analytics' import {IS_WEB} from '#/env' export function TranslatedPost({ hideTranslateLink = false, post, postText, }: { hideTranslateLink?: boolean post: AppBskyFeedDefs.PostView postText: string }) { const langPrefs = useLanguagePrefs() const {clearTranslation, translate, translationState} = useTranslate({ key: post.uri, }) const needsTranslation = useMemo(() => { if (hideTranslateLink) return false return !isPostInLanguage(post, [langPrefs.primaryLanguage]) }, [hideTranslateLink, post, langPrefs.primaryLanguage]) switch (translationState.status) { case 'loading': return case 'success': return ( ) case 'error': return ( ) default: return ( needsTranslation && ( ) ) } } function TranslationLoading() { const t = useTheme() return ( Translating… ) } function TranslationLink({ postText, primaryLanguage, translate, }: { postText: string primaryLanguage: string translate: TranslationFunction }) { const t = useTheme() const {t: l} = useLingui() const ax = useAnalytics() const handleTranslate = useCallback(() => { void translate({ text: postText, targetLangCode: primaryLanguage, }) ax.metric('translate', { sourceLanguages: [], // todo: get from post maybe? targetLanguage: primaryLanguage, textLength: postText.length, }) }, [ax, postText, primaryLanguage, translate]) return ( { handleTranslate() })} label={l`Translate`} hoverStyle={[ native({opacity: 0.5}), web([a.underline, {textDecorationColor: t.palette.primary_500}]), ]} hitSlop={HITSLOP_30}> Translate ) } function TranslationError({ clearTranslation, message, postText, primaryLanguage, }: { clearTranslation: () => void message: string postText: string primaryLanguage: string }) { const t = useTheme() const {t: l} = useLingui() const translate = useGoogleTranslate() const handleFallback = () => { void translate(postText, primaryLanguage) } return ( {message} { handleFallback() })} label={l`Try Google Translate`} hoverStyle={[ native({opacity: 0.5}), web([a.underline, {textDecorationColor: t.palette.primary_500}]), ]} hitSlop={HITSLOP_30}> Try Google Translate ) } function TranslationResult({ clearTranslation, translate, postText, sourceLanguage, translatedText, }: { clearTranslation: () => void translate: TranslationFunction postText: string sourceLanguage: string | null translatedText: string }) { const t = useTheme() const langPrefs = useLanguagePrefs() const {i18n, t: l} = useLingui() const langName = sourceLanguage ? codeToLanguageName(sourceLanguage, i18n.locale) : undefined return ( {langName ? ( {langName}{' '} {' '} {codeToLanguageName( langPrefs.primaryLanguage, langPrefs.appLanguage, )} ) : ( Translated )} {sourceLanguage != null && ( <> {' '} ·{' '} )} {translatedText} ) } function TranslationLanguageSelect({ translate, postText, sourceLanguage, }: { translate: TranslationFunction postText: string sourceLanguage: string }) { const t = useTheme() const ax = useAnalytics() const {t: l} = useLingui() const langPrefs = useLanguagePrefs() const items = useMemo( () => LANGUAGES.filter( (lang, index, self) => !langPrefs.primaryLanguage.startsWith(lang.code2) && // Don't show the current language as it would be redundant index === self.findIndex(t => t.code2 === lang.code2), // Remove dupes (which will happen due to multiple code3 values mapping to the same code2) ) .sort((a, b) => { // Prioritize sourceLanguage at the top if (a.code2 === sourceLanguage) return -1 if (b.code2 === sourceLanguage) return 1 // Localized sort return languageName(a, langPrefs.appLanguage).localeCompare( languageName(b, langPrefs.appLanguage), langPrefs.appLanguage, ) }) .map(l => ({ label: languageName(l, langPrefs.appLanguage), // The viewer may not be familiar with the source language, so localize the name value: l.code2, })), [langPrefs, sourceLanguage], ) const handleChangeTranslationLanguage = (sourceLangCode: string) => { ax.metric('translate:override', { os: Platform.OS, sourceLanguage: sourceLangCode, targetLanguage: langPrefs.primaryLanguage, }) void translate({ text: postText, targetLangCode: langPrefs.primaryLanguage, sourceLangCode, }) } return ( {({props}) => { return ( ) }} ( {label} )} items={items} /> ) }