import './translation-block.css'; import pThrottle from 'p-throttle'; import { useEffect, useRef, useState } from 'preact/hooks'; import sourceLanguages from '../data/lingva-source-languages'; import getTranslateTargetLanguage from '../utils/get-translate-target-language'; import localeCode2Text from '../utils/localeCode2Text'; import Icon from './icon'; import Loader from './loader'; const throttle = pThrottle({ limit: 1, interval: 2000, }); function lingvaTranslate(text, source, target) { console.log('TRANSLATE', text, source, target); // 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, // }); } const throttledLingvaTranslate = throttle(lingvaTranslate); function TranslationBlock({ forceTranslate, sourceLanguage, onTranslate, text = '', mini, }) { const targetLang = getTranslateTargetLanguage(true); const [uiState, setUIState] = useState('default'); const [pronunciationContent, setPronunciationContent] = useState(null); const [translatedContent, setTranslatedContent] = useState(null); const [detectedLang, setDetectedLang] = useState(null); const detailsRef = useRef(); const sourceLangText = sourceLanguage ? localeCode2Text(sourceLanguage) : null; const targetLangText = localeCode2Text(targetLang); const apiSourceLang = useRef('auto'); if (!onTranslate) { onTranslate = mini ? throttledLingvaTranslate : lingvaTranslate; } const translate = async () => { setUIState('loading'); try { const { content, detectedSourceLanguage, provider, ...props } = await onTranslate(text, apiSourceLang.current, targetLang); if (content) { if (detectedSourceLanguage) { const detectedLangText = localeCode2Text(detectedSourceLanguage); setDetectedLang(detectedLangText); } if (provider === 'lingva') { const pronunciation = props?.info?.pronunciation?.query; if (pronunciation) { setPronunciationContent(pronunciation); } } setTranslatedContent(content); setUIState('default'); if (!mini && content.trim() !== text.trim()) { detailsRef.current.open = true; detailsRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest', }); } } else { console.error(result); setUIState('error'); } } catch (e) { console.error(e); setUIState('error'); } }; useEffect(() => { if (forceTranslate) { translate(); } }, [forceTranslate]); if (mini) { if ( !!translatedContent && translatedContent.trim() !== text.trim() && detectedLang !== targetLangText ) { return (
{translatedContent}
); } return null; } return (
{ e.preventDefault(); }} >
{' '} → {targetLangText}
{uiState === 'error' ? (

Failed to translate

) : ( !!translatedContent && ( <> {translatedContent} {!!pronunciationContent && ( { e.target.classList.toggle('expand'); }} > {pronunciationContent} )} ) )}
); } export default TranslationBlock;