Files
Investidor-V2/resources/js/components/institutional-source/source-text.tsx
T
2026-11-03 15:31:25 +02:00

55 lines
2.3 KiB
TypeScript

import { useEffect, useState } from 'react';
import { useTempoUi } from '@/components/tempo-brand';
import { translateText } from '@/translation/service';
import { protectTranslationTokens, restoreTranslationTokens } from '@/translation/utils';
// Only the fixed public institutional texts using this component are sent to Tempo.
const cache = new Map<string, string>();
const pending = new Map<string, Promise<string>>();
function translateInstitutionalText(text: string, language: string): Promise<string> {
const key = JSON.stringify([language, text]);
const cached = cache.get(key);
if (cached) return Promise.resolve(cached);
const existing = pending.get(key);
if (existing) return existing;
const protectedText = protectTranslationTokens(text);
const request = translateText({
text: protectedText.text,
sourceLanguage: 'pt',
targetLanguage: language,
}).then(({ translatedText }) => {
// Do not accept a response that lost an identifier, URL or protected term.
for (const token of protectedText.replacements.keys()) {
if (!translatedText.includes(token)) throw new Error('Translation lost a protected token');
}
const result = restoreTranslationTokens(translatedText, protectedText.replacements);
// Tempo's API returns the input on upstream failure: do not cache it as success.
if (result !== text) cache.set(key, result);
return result;
}).finally(() => pending.delete(key));
pending.set(key, request);
return request;
}
export function Trans({ children }: { children: string }) {
const { language, t } = useTempoUi();
const localText = t(children);
const [result, setResult] = useState<{ key: string; text: string } | null>(null);
const key = JSON.stringify([language, children]);
useEffect(() => {
if (language === 'pt' || localText !== children || !children.trim()) return;
let active = true;
translateInstitutionalText(children, language).then((text) => {
if (active) setResult({ key, text });
}).catch(() => {
if (active) setResult(null);
});
return () => { active = false; };
}, [children, key, language, localText]);
return <span data-no-translate>{result?.key === key ? result.text : localText}</span>;
}