1

I am trying to add a google translate element to my Gatsby website. In essence, I am trying to get the following code to work in Gatsby:

<div id="google_translate_element"></div>

<script type="text/javascript">
function googleTranslateElementInit() {
  new google.translate.TranslateElement({pageLanguage: 'en'}, 'google_translate_element');
}
</script>

<script type="text/javascript" src="//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script>

I am relatively new to Gatsby and would like to know the proper (or at least a working) way of achieving this.

What I have tried

  1. Calling the Javascript code on mount
const Footer = () => {

    useEffect(() => {
        const script = document.createElement("script")
        script.src = "//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"
        script.async = true
        document.body.appendChild(script)

        googleTranslateElementInit(() => {
            new google.translate.TranslateElement({pageLanguage: 'en'}, 'google_translate_element')
        })

        return () => {
            document.body.removeChild(script)
        }
    }, [])

    return <div id="google_translate_element"></div>
}

This did not work, due to googleTranslateElementInit and google being undefined.

  1. Moving the <script to the <Helmet> component
<Helmet>
    <script type="text/javascript" src="//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script>
    <script type="text/javascript">
        {googleTranslateElementInit(() => {
             new google.translate.TranslateElement({pageLanguage: 'en'}, 'google_translate_element')
        })}
    </script>
</Helmet>

Which produced the same error as (1)

  1. Adding the Javascript to the beginning of the <body> tag of html.js
 <body {...props.bodyAttributes}>
     <script 
          dangerouslySetInnerHTML={{
            __html: `
            function googleTranslateElementInit() {
                new google.translate.TranslateElement({pageLanguage: 'en'}, 'google_translate_element');
            }
            `
          }}
    />
    <script type="text/javascript" src="//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script>

This worked the first time I loaded the website. After refreshing it, the button was gone. It only appeared the first time I loaded the page after starting the development server. I also tried to make a production build with same results.

I have been stuck with this for a long time and I would greatly appreciate any advice.

Jordan
  • 38
  • 1
  • 4

2 Answers2

2

The following worked for me:

const GoogleTranslate = () => {

const googleTranslateElementInit = () => {
    new window.google.translate.TranslateElement({ pageLanguage: 'en', layout: window.google.translate.TranslateElement.FloatPosition.TOP_LEFT }, 'google_translate_element')
   }

   useEffect(() => {
    var addScript = document.createElement('script');
    addScript.setAttribute('src', '//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit');
    document.body.appendChild(addScript);
    window.googleTranslateElementInit = googleTranslateElementInit;
  }, []);

return (
  <>
   <div id="google_translate_element"></div>
  </>
);

} 
export default GoogleTranslate
J. Gunderson
  • 57
  • 1
  • 1
  • 12
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Dec 29 '21 at 19:38
1

It looks like you are not using a React-based approach, so your translate script will always lack scope (it will load asynchronously or it will never load-on-demand). My suggestion is to use a react dependency, such as react-google-translate, in addition the clean will be much cleaner.

Once you set your environment (environment variables and so on), you just need to:

import React, { useState, useEffect } from 'react'
 
import { useLazyTranslate } from 'react-google-translate'
 
const Example = () => {
 
  const [text] = useState('test');
  const [language] = useState('zh-CN');
 
  const [translate, { data, loading }] = useLazyTranslate({
    language 
  })
 
  useEffect(() => {
    if (text) {
      translate(text, language);
    }
  }, [translate, text])
 
  render() {
    return (
      <div>{loading ? 'Loading...' : data}</div>
    )
  }
}

As you can see, there are some custom hooks exposed (useLazyTranslate) that allow you to use the translation on-demand, ensuring that the library is properly loaded.

Ferran Buireu
  • 28,630
  • 6
  • 39
  • 67
  • I am looking to translate the whole website, which is quite large. Is there a way of achieving that without wrapping every piece of text in `translate`? I have searched for a React-based package on npm and could not find any. – Jordan Feb 23 '21 at 16:08
  • 1
    Yes, you can as soon as the content remains in the state. You can parse even HTML tags – Ferran Buireu Feb 24 '21 at 09:37