Search code examples
reactjstypescriptreact-hooksazure-cognitive-services

How do you replace data multiple times with an array of keys using a react hook?


I'm not sure if I worded the question right. Here is my problem. I am trying to translate my webpage dynamically using Cognitive Services Translator from Microsoft. I made a react hook with the translator. This works fine if I just need to translate one value in an object.

const useAzureTranslate = (data: any, lang: string, objKey: string) => {

    const [translatedData, setTranslatedData] = useState<any | null>([]);

    useEffect(() => {
        Promise.all(data.map(function (obj: any) {
            return axios({
                baseURL: endpoint,
                url: '/translate',
                method: 'post',
                headers: {
                    'Ocp-Apim-Subscription-Key': subscriptionKey,
                    'Ocp-Apim-Subscription-Region': location,
                    'Content-type': 'application/json',
                    'X-ClientTraceId': uuidv4().toString()
                },
                params: {
                    'api-version': '3.0',
                    'from': 'en',
                    'to': lang
                },
                data: [{ "text": obj[objKey] }],
                responseType: 'json'
            }).then(response => {
                // console.log(response.data)
                let translatedText = response.data[0].translations[0].text;
                return { ...obj, [objKey]: translatedText };
            }).catch((error) => {
                console.log(error.response.data.error);
                throw error;
            });
        })).then(newDataArray => {
            // console.log(newDataArray);
            setTranslatedData(newDataArray);
        }).catch(err => {
            console.log(err);
            throw err;
        });
        console.log(translatedData);

        // eslint-disable-next-line react-hooks/exhaustive-deps
    }, [lang])

    return { translatedData };
}

I want to be able to have an array of keys instead of having to translate just one key value. I am not sure how to go about this.


Solution

  • The "/translate" API can take up to 100 terms to translate.

    Translate Request Body

    Request body

    The body of the request is a JSON array. Each array element is a JSON object with a string property named Text, which represents the string to translate.

    [
        {"Text":"I would really like to drive your car around the block a few times."}
    ]
    

    The following limitations apply:

    • The array can have at most 100 elements.
    • The entire text included in the request cannot exceed 10,000 characters including spaces.

    If your code remains within these constraints then I suggest the following:

    1. Change objKey to be declared as an array of strings
    2. Map the objKey array to an array of objects with text key
    3. Merge the array of translations into a copy of the mapped obj object

    Example:

    const useAzureTranslate = (data: any, lang: string, objKey: string[]) => {
      const [translatedData, setTranslatedData] = useState<any | null>([]);
    
      useEffect(() => {
        Promise.all(
          data.map(function (obj: any) {
            return axios({
              baseURL: endpoint,
              url: "/translate",
              method: "post",
              headers: {
                "Ocp-Apim-Subscription-Key": subscriptionKey,
                "Ocp-Apim-Subscription-Region": location,
                "Content-type": "application/json",
                "X-ClientTraceId": uuidv4().toString()
              },
              params: {
                "api-version": "3.0",
                from: "en",
                to: lang
              },
              data: objKey.map((key) => ({ text: obj[key] })), // <-- array of "text" objects
              responseType: "json"
            })
              .then((response) => {
                // Reduce array of translations back into current `obj` copy
                return response.data[0].translations.reduce(
                  (newObj, { text }, index) => ({
                    ...newObj,
                    [objKey[index]]: text
                  }),
                  { ...obj }
                );
              })
              .catch((error) => {
                console.log(error.response.data.error);
                throw error;
              });
          })
        )
          .then((newDataArray) => {
            setTranslatedData(newDataArray);
          })
          .catch((err) => {
            console.log(err);
            throw err;
          });
    
        // eslint-disable-next-line react-hooks/exhaustive-deps
      }, [lang]);
    
      return { translatedData };
    };
    

    Edit how-do-you-replace-data-multiple-times-with-an-array-of-keys-using-a-react-hook

    If your code is outside the constraints then you'll need to chunk up your data and/or key translation terms first, then make the requests as above.