From 496ca2bda0c96179f52eaf539141b8ab1822955c Mon Sep 17 00:00:00 2001 From: Ayanaa Rahman Date: Mon, 22 Apr 2024 11:57:16 -0400 Subject: [PATCH] Refactoring by putting LLM translations into a context --- translate/src/context/TranslationContext.tsx | 97 +++++++++++++++++++ .../machinery/components/Machinery.tsx | 29 +++--- .../components/MachineryTranslation.tsx | 39 ++------ .../components/MachineryTranslationSource.tsx | 8 +- .../components/source/GoogleTranslation.tsx | 79 +++------------ 5 files changed, 140 insertions(+), 112 deletions(-) create mode 100644 translate/src/context/TranslationContext.tsx diff --git a/translate/src/context/TranslationContext.tsx b/translate/src/context/TranslationContext.tsx new file mode 100644 index 0000000000..ef627bedc9 --- /dev/null +++ b/translate/src/context/TranslationContext.tsx @@ -0,0 +1,97 @@ +import React, { + createContext, + useContext, + useState, + Dispatch, + SetStateAction, +} from 'react'; +import { fetchGPTTransform } from '~/api/machinery'; + +interface LLMTranslationContextType { + llmTranslation: string; + setLlmTranslation: Dispatch>; + + selectedOption: string; + setSelectedOption: Dispatch>; + transformLLMTranslation: ( + original: string, + current: string, + characteristic: string, + localeName: string, + ) => Promise; +} + +const LLMTranslationContext = createContext({ + llmTranslation: '', + setLlmTranslation: () => {}, + + selectedOption: '', + setSelectedOption: () => {}, + transformLLMTranslation: async () => {}, +}); + +export const LLMTranslationProvider: React.FC = ({ children }) => { + const [llmTranslation, setLlmTranslation] = useState(''); + + const [selectedOption, setSelectedOption] = useState(''); + + const handleSetSelectedOption = (option: string) => { + let displayText = ''; + + switch (option) { + case 'alternative': + displayText = 'REPHRASED'; + break; + case 'formal': + displayText = 'FORMAL'; + break; + case 'informal': + displayText = 'INFORMAL'; + break; + case 'original': + displayText = ''; + break; + } + + setSelectedOption(displayText); + }; + + const transformLLMTranslation = async ( + original: string, + current: string, + characteristic: string, + localeName: string, + ) => { + if (characteristic !== 'original') { + const machineryTranslations = await fetchGPTTransform( + original, + current, + characteristic, + localeName, + ); + if (machineryTranslations.length > 0) { + setLlmTranslation(machineryTranslations[0].translation); + handleSetSelectedOption(characteristic); + } + } else { + setLlmTranslation(''); + handleSetSelectedOption(''); + } + }; + + return ( + + {children} + + ); +}; + +export const useLLMTranslation = () => useContext(LLMTranslationContext); diff --git a/translate/src/modules/machinery/components/Machinery.tsx b/translate/src/modules/machinery/components/Machinery.tsx index 92878a038f..ae94b02bd7 100644 --- a/translate/src/modules/machinery/components/Machinery.tsx +++ b/translate/src/modules/machinery/components/Machinery.tsx @@ -8,6 +8,7 @@ import { SkeletonLoader } from '~/modules/loaders'; import './Machinery.css'; import { MachineryTranslationComponent } from './MachineryTranslation'; +import { LLMTranslationProvider } from '~/context/TranslationContext'; /** * Show translations from machines. @@ -70,22 +71,26 @@ export function Machinery(): React.ReactElement<'section'> {
    {translations.map((translation, index) => ( - + + + ))}
    {results.map((result, index) => ( - + + + ))}
{(fetching || hasMore) && ( diff --git a/translate/src/modules/machinery/components/MachineryTranslation.tsx b/translate/src/modules/machinery/components/MachineryTranslation.tsx index 2e20f73f4d..d071efd73b 100644 --- a/translate/src/modules/machinery/components/MachineryTranslation.tsx +++ b/translate/src/modules/machinery/components/MachineryTranslation.tsx @@ -1,12 +1,6 @@ import { Localized } from '@fluent/react'; import classNames from 'classnames'; -import React, { - useCallback, - useContext, - useEffect, - useRef, - useState, -} from 'react'; +import React, { useCallback, useContext, useEffect, useRef } from 'react'; import type { MachineryTranslation } from '~/api/machinery'; import { logUXAction } from '~/api/uxaction'; @@ -18,6 +12,7 @@ import { useReadonlyEditor } from '~/hooks/useReadonlyEditor'; import { ConcordanceSearch } from './ConcordanceSearch'; import { MachineryTranslationSource } from './MachineryTranslationSource'; +import { useLLMTranslation } from '~/context/TranslationContext'; import './ConcordanceSearch.css'; import './MachineryTranslation.css'; @@ -44,12 +39,7 @@ export function MachineryTranslationComponent({ const { element, setElement } = useContext(HelperSelection); const isSelected = element === index; - const [llmTranslation, setLlmTranslation] = useState(''); - - // Handler to update LLM translation - const handleLLMTranslationChange = useCallback((newTranslation: string) => { - setLlmTranslation(newTranslation); - }, []); + const { llmTranslation } = useLLMTranslation(); const copyRegularTranslationIntoEditor = useCallback(() => { if (window.getSelection()?.isCollapsed !== false) { @@ -90,10 +80,10 @@ export function MachineryTranslationComponent({
  • + onClick={ llmTranslation - ? copyLLMTranslationIntoEditor() - : copyRegularTranslationIntoEditor() + ? copyLLMTranslationIntoEditor + : copyRegularTranslationIntoEditor } ref={translationRef} > @@ -106,9 +96,6 @@ export function MachineryTranslationComponent({ )}
  • @@ -119,17 +106,12 @@ export function MachineryTranslationComponent({ function MachineryTranslationSuggestion({ sourceString, translation, - llmTranslation, - handleLLMTranslationChange, - onLLMClick, }: { sourceString: string; translation: MachineryTranslation; - llmTranslation?: string; - handleLLMTranslationChange: (newTranslation: string) => void; - onLLMClick: () => void; }) { const { code, direction, script } = useContext(Locale); + const { llmTranslation } = useLLMTranslation(); const contentToDisplay = llmTranslation || translation.translation; return ( <> @@ -137,10 +119,8 @@ function MachineryTranslationSuggestion({ {translation.quality && ( {translation.quality + '%'} )} - + +

    diff --git a/translate/src/modules/machinery/components/MachineryTranslationSource.tsx b/translate/src/modules/machinery/components/MachineryTranslationSource.tsx index 174e5b558d..8ffce3c6be 100644 --- a/translate/src/modules/machinery/components/MachineryTranslationSource.tsx +++ b/translate/src/modules/machinery/components/MachineryTranslationSource.tsx @@ -11,7 +11,6 @@ import { TranslationMemory } from './source/TranslationMemory'; type Props = { translation: MachineryTranslation; - handleLLMTranslationChange: (llmTranslation: string) => void; }; /** @@ -19,7 +18,6 @@ type Props = { */ export function MachineryTranslationSource({ translation, - handleLLMTranslationChange, }: Props): React.ReactElement<'ul'> { const sources: React.ReactElement<'li'>[] = []; const seen: string[] = []; @@ -37,11 +35,7 @@ export function MachineryTranslationSource({ break; case 'google-translate': sources.push( - , + , ); break; case 'microsoft-translator': diff --git a/translate/src/modules/machinery/components/source/GoogleTranslation.tsx b/translate/src/modules/machinery/components/source/GoogleTranslation.tsx index 1bc3cf1f46..f6e3de1077 100644 --- a/translate/src/modules/machinery/components/source/GoogleTranslation.tsx +++ b/translate/src/modules/machinery/components/source/GoogleTranslation.tsx @@ -1,13 +1,12 @@ import React, { useState, useRef, useContext } from 'react'; import { Localized } from '@fluent/react'; import type { MachineryTranslation } from '~/api/machinery'; -import { fetchGPTTransform } from '~/api/machinery'; import { Locale } from '~/context/Locale'; import { logUXAction } from '~/api/uxaction'; +import { useLLMTranslation } from '~/context/TranslationContext'; type Props = { translation: MachineryTranslation; - onLLMTranslationChange: (llmTranslation: string) => void; }; /** @@ -16,78 +15,36 @@ type Props = { export function GoogleTranslation({ translation, - onLLMTranslationChange, }: Props): React.ReactElement<'li'> { const [isDropdownOpen, setDropdownOpen] = useState(false); - const [selectedOption, setSelectedOption] = useState(''); - const [showOriginalOption, setShowOriginalOption] = useState(false); - const [currentTranslation, setCurrentTranslation] = useState( - translation.translation, - ); const dropdownRef = useRef(null); const locale = useContext(Locale); + const { transformLLMTranslation, selectedOption } = useLLMTranslation(); + const toggleDropdown = (ev: React.MouseEvent) => { ev.stopPropagation(); setDropdownOpen((isDropdownOpen) => !isDropdownOpen); }; - const handleTransformation = async (characteristic: string) => { - // Only fetch transformation if not reverting to original - if (characteristic !== 'original') { - const machineryTranslations = await fetchGPTTransform( - translation.original, - currentTranslation, - characteristic, - locale.name, - ); - - if (machineryTranslations.length > 0) { - setCurrentTranslation(machineryTranslations[0].translation); - onLLMTranslationChange(machineryTranslations[0].translation); - setShowOriginalOption(true); - } - } else { - setCurrentTranslation(translation.translation); - onLLMTranslationChange(''); - setSelectedOption(''); - setShowOriginalOption(false); - } - }; - - const handleOptionClick = (ev: React.MouseEvent) => { + const handleOptionClick = async (ev: React.MouseEvent) => { ev.stopPropagation(); const target = ev.currentTarget; const characteristic = target.dataset['characteristic']; if (characteristic) { - let displayText = ''; - - switch (characteristic) { - case 'alternative': - displayText = 'REPHRASED'; - break; - case 'formal': - displayText = 'FORMAL'; - break; - case 'informal': - displayText = 'INFORMAL'; - break; - case 'original': - displayText = ''; - break; - default: - break; - } - setSelectedOption(displayText); // TODO: Localize displayText before setting it as selected option. setDropdownOpen(false); + await transformLLMTranslation( + translation.original, + translation.translation, + characteristic, + locale.name, + ); logUXAction('LLM Dropdown Select', 'LLM Feature Adoption', { optionSelected: characteristic, targetLanguage: locale.name, }); - - handleTransformation(characteristic); } }; @@ -128,16 +85,12 @@ export function GoogleTranslation({ MAKE INFORMAL - {showOriginalOption && ( - <> -
  • - -
  • - SHOW ORIGINAL -
  • -
    - - )} +
  • + +
  • + SHOW ORIGINAL +
  • +
    )}