this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Experimental Auto Inline Translation (AIT)

For short posts for now and throttled API calls

+96 -32
+18 -3
src/components/status.jsx
··· 57 57 import RelativeTime from './relative-time'; 58 58 import TranslationBlock from './translation-block'; 59 59 60 + const INLINE_TRASNSLATE_LIMIT = 140; 60 61 const throttle = pThrottle({ 61 62 limit: 1, 62 63 interval: 1000, ··· 244 245 ); 245 246 } 246 247 248 + const isSizeLarge = size === 'l'; 249 + 247 250 const [forceTranslate, setForceTranslate] = useState(_forceTranslate); 248 251 const targetLanguage = getTranslateTargetLanguage(true); 249 252 const contentTranslationHideLanguages = 250 253 snapStates.settings.contentTranslationHideLanguages || []; 251 254 if (!snapStates.settings.contentTranslation) enableTranslate = false; 255 + const inlineTranslate = useMemo(() => { 256 + return ( 257 + !isSizeLarge && 258 + !spoilerText && 259 + !poll && 260 + !mediaAttachments?.length && 261 + content?.length > 0 && 262 + content?.length <= INLINE_TRASNSLATE_LIMIT 263 + ); 264 + }, [isSizeLarge, content, spoilerText, poll, mediaAttachments]); 252 265 253 266 const [showEdited, setShowEdited] = useState(false); 254 267 const [showReactions, setShowReactions] = useState(false); ··· 306 319 const createdDateText = niceDateTime(createdAtDate); 307 320 const editedDateText = editedAt && niceDateTime(editedAtDate); 308 321 309 - const isSizeLarge = size === 'l'; 310 322 // Can boost if: 311 323 // - authenticated AND 312 324 // - visibility != direct OR ··· 1091 1103 }} 1092 1104 /> 1093 1105 )} 1094 - {((enableTranslate && !!content.trim() && differentLanguage) || 1106 + {(((enableTranslate || inlineTranslate) && 1107 + !!content.trim() && 1108 + differentLanguage) || 1095 1109 forceTranslate) && ( 1096 1110 <TranslationBlock 1097 - forceTranslate={forceTranslate} 1111 + forceTranslate={forceTranslate || inlineTranslate} 1112 + mini={inlineTranslate} 1098 1113 sourceLanguage={language} 1099 1114 text={ 1100 1115 (spoilerText ? `${spoilerText}\n\n` : '') +
+19
src/components/translation-block.css
··· 105 105 overflow: visible; 106 106 mask-image: none; 107 107 } 108 + 109 + /* MINI */ 110 + 111 + .status-translation-block-mini { 112 + display: flex; 113 + margin: 8px 0 0; 114 + padding: 8px 0 0; 115 + font-size: 90%; 116 + border-top: var(--hairline-width) solid var(--outline-color); 117 + color: var(--text-insignificant-color); 118 + gap: 8px; 119 + transition: color 0.3s ease-in-out; 120 + } 121 + .status-translation-block-mini .icon { 122 + margin-top: 2px; 123 + } 124 + .status:is(:hover, :active) .status-translation-block-mini { 125 + color: var(--text-color); 126 + }
+59 -29
src/components/translation-block.jsx
··· 1 1 import './translation-block.css'; 2 2 3 + import pThrottle from 'p-throttle'; 3 4 import { useEffect, useRef, useState } from 'preact/hooks'; 4 5 5 6 import sourceLanguages from '../data/lingva-source-languages'; ··· 9 10 import Icon from './icon'; 10 11 import Loader from './loader'; 11 12 13 + const throttle = pThrottle({ 14 + limit: 1, 15 + interval: 2000, 16 + }); 17 + 18 + function lingvaTranslate(text, source, target) { 19 + console.log('TRANSLATE', text, source, target); 20 + // Using another API instance instead of lingva.ml because of this bug (slashes don't work): 21 + // https://github.com/thedaviddelta/lingva-translate/issues/68 22 + return fetch( 23 + `https://lingva.garudalinux.org/api/v1/${source}/${target}/${encodeURIComponent( 24 + text, 25 + )}`, 26 + ) 27 + .then((res) => res.json()) 28 + .then((res) => { 29 + return { 30 + provider: 'lingva', 31 + content: res.translation, 32 + detectedSourceLanguage: res.info?.detectedSource, 33 + info: res.info, 34 + }; 35 + }); 36 + // return masto.v1.statuses.translate(id, { 37 + // lang: DEFAULT_LANG, 38 + // }); 39 + } 40 + const throttledLingvaTranslate = throttle(lingvaTranslate); 41 + 12 42 function TranslationBlock({ 13 43 forceTranslate, 14 44 sourceLanguage, 15 45 onTranslate, 16 46 text = '', 47 + mini, 17 48 }) { 18 49 const targetLang = getTranslateTargetLanguage(true); 19 50 const [uiState, setUIState] = useState('default'); ··· 28 59 const targetLangText = localeCode2Text(targetLang); 29 60 const apiSourceLang = useRef('auto'); 30 61 31 - if (!onTranslate) 32 - onTranslate = (source, target) => { 33 - console.log('TRANSLATE', source, target, text); 34 - // Using another API instance instead of lingva.ml because of this bug (slashes don't work): 35 - // https://github.com/thedaviddelta/lingva-translate/issues/68 36 - return fetch( 37 - `https://lingva.garudalinux.org/api/v1/${source}/${target}/${encodeURIComponent( 38 - text, 39 - )}`, 40 - ) 41 - .then((res) => res.json()) 42 - .then((res) => { 43 - return { 44 - provider: 'lingva', 45 - content: res.translation, 46 - detectedSourceLanguage: res.info?.detectedSource, 47 - info: res.info, 48 - }; 49 - }); 50 - // return masto.v1.statuses.translate(id, { 51 - // lang: DEFAULT_LANG, 52 - // }); 53 - }; 62 + if (!onTranslate) { 63 + onTranslate = mini ? throttledLingvaTranslate : lingvaTranslate; 64 + } 54 65 55 66 const translate = async () => { 56 67 setUIState('loading'); 57 68 try { 58 69 const { content, detectedSourceLanguage, provider, ...props } = 59 - await onTranslate(apiSourceLang.current, targetLang); 70 + await onTranslate(text, apiSourceLang.current, targetLang); 60 71 if (content) { 61 72 if (detectedSourceLanguage) { 62 73 const detectedLangText = localeCode2Text(detectedSourceLanguage); ··· 70 81 } 71 82 setTranslatedContent(content); 72 83 setUIState('default'); 73 - detailsRef.current.open = true; 74 - detailsRef.current.scrollIntoView({ 75 - behavior: 'smooth', 76 - block: 'nearest', 77 - }); 84 + if (!mini) { 85 + detailsRef.current.open = true; 86 + detailsRef.current.scrollIntoView({ 87 + behavior: 'smooth', 88 + block: 'nearest', 89 + }); 90 + } 78 91 } else { 79 92 console.error(result); 80 93 setUIState('error'); ··· 90 103 translate(); 91 104 } 92 105 }, [forceTranslate]); 106 + 107 + if (mini) { 108 + if (!!translatedContent && detectedLang !== targetLangText) { 109 + return ( 110 + <div class="status-translation-block-mini"> 111 + <Icon 112 + icon="translate" 113 + alt={`Auto-translated from ${sourceLangText}`} 114 + /> 115 + <output lang={targetLang} dir="auto"> 116 + {translatedContent} 117 + </output> 118 + </div> 119 + ); 120 + } 121 + return null; 122 + } 93 123 94 124 return ( 95 125 <div