From 92a4f502a074ef23d71ffe7c85e8a117a5f18e9e Mon Sep 17 00:00:00 2001 From: Lim Chee Aun Date: Tue, 18 Jul 2023 13:31:26 +0800 Subject: [PATCH] Experimental Auto Inline Translation (AIT) For short posts for now and throttled API calls --- src/components/status.jsx | 21 ++++++- src/components/translation-block.css | 19 ++++++ src/components/translation-block.jsx | 88 +++++++++++++++++++--------- 3 files changed, 96 insertions(+), 32 deletions(-) diff --git a/src/components/status.jsx b/src/components/status.jsx index e926fb38..585655bb 100644 --- a/src/components/status.jsx +++ b/src/components/status.jsx @@ -57,6 +57,7 @@ import MenuLink from './menu-link'; import RelativeTime from './relative-time'; import TranslationBlock from './translation-block'; +const INLINE_TRASNSLATE_LIMIT = 140; const throttle = pThrottle({ limit: 1, interval: 1000, @@ -244,11 +245,23 @@ function Status({ ); } + const isSizeLarge = size === 'l'; + const [forceTranslate, setForceTranslate] = useState(_forceTranslate); const targetLanguage = getTranslateTargetLanguage(true); const contentTranslationHideLanguages = snapStates.settings.contentTranslationHideLanguages || []; if (!snapStates.settings.contentTranslation) enableTranslate = false; + const inlineTranslate = useMemo(() => { + return ( + !isSizeLarge && + !spoilerText && + !poll && + !mediaAttachments?.length && + content?.length > 0 && + content?.length <= INLINE_TRASNSLATE_LIMIT + ); + }, [isSizeLarge, content, spoilerText, poll, mediaAttachments]); const [showEdited, setShowEdited] = useState(false); const [showReactions, setShowReactions] = useState(false); @@ -306,7 +319,6 @@ function Status({ const createdDateText = niceDateTime(createdAtDate); const editedDateText = editedAt && niceDateTime(editedAtDate); - const isSizeLarge = size === 'l'; // Can boost if: // - authenticated AND // - visibility != direct OR @@ -1091,10 +1103,13 @@ function Status({ }} /> )} - {((enableTranslate && !!content.trim() && differentLanguage) || + {(((enableTranslate || inlineTranslate) && + !!content.trim() && + differentLanguage) || forceTranslate) && ( res.json()) + .then((res) => { + return { + provider: 'lingva', + content: res.translation, + detectedSourceLanguage: res.info?.detectedSource, + info: res.info, + }; + }); + // return masto.v1.statuses.translate(id, { + // lang: DEFAULT_LANG, + // }); +} +const throttledLingvaTranslate = throttle(lingvaTranslate); + function TranslationBlock({ forceTranslate, sourceLanguage, onTranslate, text = '', + mini, }) { const targetLang = getTranslateTargetLanguage(true); const [uiState, setUIState] = useState('default'); @@ -28,35 +59,15 @@ function TranslationBlock({ const targetLangText = localeCode2Text(targetLang); const apiSourceLang = useRef('auto'); - if (!onTranslate) - onTranslate = (source, target) => { - console.log('TRANSLATE', source, target, text); - // Using another API instance instead of lingva.ml because of this bug (slashes don't work): - // https://github.com/thedaviddelta/lingva-translate/issues/68 - return fetch( - `https://lingva.garudalinux.org/api/v1/${source}/${target}/${encodeURIComponent( - text, - )}`, - ) - .then((res) => res.json()) - .then((res) => { - return { - provider: 'lingva', - content: res.translation, - detectedSourceLanguage: res.info?.detectedSource, - info: res.info, - }; - }); - // return masto.v1.statuses.translate(id, { - // lang: DEFAULT_LANG, - // }); - }; + if (!onTranslate) { + onTranslate = mini ? throttledLingvaTranslate : lingvaTranslate; + } const translate = async () => { setUIState('loading'); try { const { content, detectedSourceLanguage, provider, ...props } = - await onTranslate(apiSourceLang.current, targetLang); + await onTranslate(text, apiSourceLang.current, targetLang); if (content) { if (detectedSourceLanguage) { const detectedLangText = localeCode2Text(detectedSourceLanguage); @@ -70,11 +81,13 @@ function TranslationBlock({ } setTranslatedContent(content); setUIState('default'); - detailsRef.current.open = true; - detailsRef.current.scrollIntoView({ - behavior: 'smooth', - block: 'nearest', - }); + if (!mini) { + detailsRef.current.open = true; + detailsRef.current.scrollIntoView({ + behavior: 'smooth', + block: 'nearest', + }); + } } else { console.error(result); setUIState('error'); @@ -91,6 +104,23 @@ function TranslationBlock({ } }, [forceTranslate]); + if (mini) { + if (!!translatedContent && detectedLang !== targetLangText) { + return ( +
+ + + {translatedContent} + +
+ ); + } + return null; + } + return (