mirror of
https://github.com/cheeaun/phanpy.git
synced 2025-02-02 06:06:41 +01:00
Experimental Auto Inline Translation (AIT)
For short posts for now and throttled API calls
This commit is contained in:
parent
ff41cd3563
commit
92a4f502a0
3 changed files with 96 additions and 32 deletions
|
@ -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) && (
|
||||
<TranslationBlock
|
||||
forceTranslate={forceTranslate}
|
||||
forceTranslate={forceTranslate || inlineTranslate}
|
||||
mini={inlineTranslate}
|
||||
sourceLanguage={language}
|
||||
text={
|
||||
(spoilerText ? `${spoilerText}\n\n` : '') +
|
||||
|
|
|
@ -105,3 +105,22 @@
|
|||
overflow: visible;
|
||||
mask-image: none;
|
||||
}
|
||||
|
||||
/* MINI */
|
||||
|
||||
.status-translation-block-mini {
|
||||
display: flex;
|
||||
margin: 8px 0 0;
|
||||
padding: 8px 0 0;
|
||||
font-size: 90%;
|
||||
border-top: var(--hairline-width) solid var(--outline-color);
|
||||
color: var(--text-insignificant-color);
|
||||
gap: 8px;
|
||||
transition: color 0.3s ease-in-out;
|
||||
}
|
||||
.status-translation-block-mini .icon {
|
||||
margin-top: 2px;
|
||||
}
|
||||
.status:is(:hover, :active) .status-translation-block-mini {
|
||||
color: var(--text-color);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import './translation-block.css';
|
||||
|
||||
import pThrottle from 'p-throttle';
|
||||
import { useEffect, useRef, useState } from 'preact/hooks';
|
||||
|
||||
import sourceLanguages from '../data/lingva-source-languages';
|
||||
|
@ -9,11 +10,41 @@ 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');
|
||||
|
@ -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 (
|
||||
<div class="status-translation-block-mini">
|
||||
<Icon
|
||||
icon="translate"
|
||||
alt={`Auto-translated from ${sourceLangText}`}
|
||||
/>
|
||||
<output lang={targetLang} dir="auto">
|
||||
{translatedContent}
|
||||
</output>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
class="status-translation-block"
|
||||
|
|
Loading…
Reference in a new issue