142 lines
4.2 KiB
TypeScript
142 lines
4.2 KiB
TypeScript
import { useState, useCallback, useEffect } from 'react';
|
|
import { useAuth } from '@/context/AuthContext';
|
|
import { getTranslator, getTranslators } from '@/services/translators';
|
|
import { getFromCache, storeInCache, polish, UseTranslatorOptions } from '@/services/translators';
|
|
|
|
export function useTranslator({
|
|
provider = 'deepl',
|
|
sourceLang = 'AUTO',
|
|
targetLang = 'EN',
|
|
enablePolishing = true,
|
|
}: UseTranslatorOptions = {}) {
|
|
const { token } = useAuth();
|
|
const [loading, setLoading] = useState(false);
|
|
const [translator, setTransltor] = useState(() => getTranslator(provider));
|
|
const [translators] = useState(() => getTranslators());
|
|
|
|
useEffect(() => {
|
|
setLoading(false);
|
|
}, [provider, sourceLang, targetLang]);
|
|
|
|
useEffect(() => {
|
|
setTransltor(getTranslator(provider));
|
|
}, [provider]);
|
|
|
|
const translate = useCallback(
|
|
async (
|
|
input: string[],
|
|
options?: { source?: string; target?: string; useCache?: boolean },
|
|
): Promise<string[]> => {
|
|
const sourceLanguage = options?.source || sourceLang;
|
|
const targetLanguage = options?.target || targetLang;
|
|
const useCache = options?.useCache ?? false;
|
|
const textsToTranslate = input;
|
|
|
|
if (textsToTranslate.length === 0 || textsToTranslate.every((t) => !t?.trim())) {
|
|
return textsToTranslate;
|
|
}
|
|
|
|
const textsNeedingTranslation: string[] = [];
|
|
const indicesNeedingTranslation: number[] = [];
|
|
|
|
await Promise.all(
|
|
textsToTranslate.map(async (text, index) => {
|
|
if (!text?.trim()) return;
|
|
|
|
const cachedTranslation = await getFromCache(
|
|
text,
|
|
sourceLanguage,
|
|
targetLanguage,
|
|
provider,
|
|
);
|
|
if (cachedTranslation) return;
|
|
|
|
textsNeedingTranslation.push(text);
|
|
indicesNeedingTranslation.push(index);
|
|
}),
|
|
);
|
|
|
|
if (textsNeedingTranslation.length === 0) {
|
|
const results = await Promise.all(
|
|
textsToTranslate.map((text) =>
|
|
getFromCache(text, sourceLanguage, targetLanguage, provider).then(
|
|
(cached) => cached || text,
|
|
),
|
|
),
|
|
);
|
|
|
|
return enablePolishing ? polish(results, targetLanguage) : results;
|
|
}
|
|
|
|
setLoading(true);
|
|
|
|
try {
|
|
const translator = translators.find((t) => t.name === provider);
|
|
if (!translator) {
|
|
throw new Error(`No translator found for provider: ${provider}`);
|
|
}
|
|
const translatedTexts = await translator.translate(
|
|
textsNeedingTranslation,
|
|
sourceLanguage,
|
|
targetLanguage,
|
|
token,
|
|
useCache,
|
|
);
|
|
|
|
await Promise.all(
|
|
textsNeedingTranslation.map(async (text, index) => {
|
|
return storeInCache(
|
|
text,
|
|
translatedTexts[index] || '',
|
|
sourceLanguage,
|
|
targetLanguage,
|
|
provider,
|
|
);
|
|
}),
|
|
);
|
|
|
|
const results = [...textsToTranslate];
|
|
indicesNeedingTranslation.forEach((originalIndex, translationIndex) => {
|
|
results[originalIndex] = translatedTexts[translationIndex] || '';
|
|
});
|
|
|
|
await Promise.all(
|
|
results.map(async (_, index) => {
|
|
if (!indicesNeedingTranslation.includes(index)) {
|
|
const originalText = textsToTranslate[index];
|
|
if (!originalText?.trim()) return;
|
|
|
|
const cachedTranslation = await getFromCache(
|
|
originalText,
|
|
sourceLanguage,
|
|
targetLanguage,
|
|
provider,
|
|
);
|
|
|
|
if (cachedTranslation) {
|
|
results[index] = cachedTranslation;
|
|
}
|
|
}
|
|
}),
|
|
);
|
|
|
|
setLoading(false);
|
|
return enablePolishing ? polish(results, targetLanguage) : results;
|
|
} catch (err) {
|
|
console.error('Translation error:', err);
|
|
setLoading(false);
|
|
throw err instanceof Error ? err : new Error(String(err));
|
|
}
|
|
},
|
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
[provider, sourceLang, targetLang, translator, token],
|
|
);
|
|
|
|
return {
|
|
translate,
|
|
translator,
|
|
translators,
|
|
loading,
|
|
};
|
|
}
|