DEV Community

Mbonu Blessing
Mbonu Blessing

Posted on

Translate React Public HTML files

I was recently tasked with translating the html files in the public folder in our react app and what I thought was a straightforward task took longer than I expected.

My research lead me to a couple of libraries like google api, translater.js, linguminum etc. But I finally settled for Transalator.js. Here is a link to the original post that introduced me to it.

What I was looking for

I needed a service that

  • Would be hosted online
  • Was easy to setup and lightweight
  • The repository was maintained regularly
  • Would be able to properly render embedded html tags

I only got 2 out of the options and that was option 2 and 3. You had to download the repo locally to use and it didn't render embedded html tags properly. The absence of option one was a good thing as it gave me the option to go through the code, seeing how it works and giving me the opportunity to make the changes I need to get what I wanted. If you are not so concerned with embedded html tags, then you just need to download the library and host it anywhere you chose.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title eo-translator="title"></title>
  </head>
  <body>
    <div class="container">
     <h1 eo-translator="greeting"></h1>
     <h2 eo-translator="question"></h2>
    </div>

    <script src="/path/to/translate.min.js"></script>
    <script type="text/javascript">
        // Construct your dictionary
        const dictionary = {
          en: {
            greeting: 'Hello',
            question: 'How are you doing?',
            title: 'Test title'
          },
          es: { 
            greeting: 'Hola',
            question: '¿Como estas?',
            title: 'Título de la prueba'
          }
        };

        // Get the user's preferred language from the browser
        let language = (window.navigator.userLanguage || window.navigator.language).split('-')[0]
        const displayLanguage = language === 'es' ? 'es' : 'en';
        let translator = new EOTranslator(dictionary, displayLanguage);

        // The translator takes care of everything else
        translator.translateDOM();       
    </script>
  </body>
</html>

Enter fullscreen mode Exit fullscreen mode

My Solution

Since the above didn't solve the display of embedded html tags, I had to kind of extract the part involved in the translator.js file, tweaked it abit and added the javascript to my html file. Here was my finally result.

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title eo-translator="title"></title>
  </head>
  <body>
    <div class="container">
     <h1 eo-translator="greeting"></h1>
     <h2 eo-translator="question"></h2>
    </div>

    <script src="/path/to/translate.min.js"></script>
    <script type="text/javascript">
      (function () {
        // Construct your dictionary
        const dictionary = {
          en: {
            greeting: 'Hello',
            question: 'How are you doing?',
            title: 'Test title'
          },
          es: { 
            greeting: 'Hola',
            question: '¿Como estas?',
            title: 'Título de la prueba'
          }
        };

        // Get the user's preferred language from the browser
        let language = (window.navigator.userLanguage || window.navigator.language).split('-')[0]
        const displayLanguage = language === 'es' ? 'es' : 'en';
        translateDOM();

        function translateDOM(DOMContainer) {
          const language = displayLanguage;
          const container = DOMContainer || typeof document === 'object' ? document : null;

          if (container) {
            const elements = container.querySelectorAll('[eo-translator]');
            elements.forEach((element) => translateElement(element, language));
          }
        }

        function translateElement(DOMElement, lang) {
          if (DOMElement) {
            const input = DOMElement.attributes['eo-translator'].value || DOMElement.textContent || DOMElement.innerText || DOMElement.innerHTML;

            // Here was where i made the change to display the embedded html tags. In the translator.js file, this line was DOMElement.innerText = translate(input);
            DOMElement.innerHTML = translate(input);
          }
        }

        function translate(input = '') {
          const language = displayLanguage;
          const fallback = input;
          const params = {};
          const frags = input.split('.').filter(frag => frag.length > 0);

          let output = dictionary.hasOwnProperty(displayLanguage);

          if (output) {
            if (frags.length > 1) {
              output = extractValue(dictionary, language, frags);
            } else {
              output = dictionary[language][input];
            }
          }

          return output ? assignParams(output, params) : fallback;
        }

        function assignParams(raw, params) {
          Object.keys(params).forEach(key => {
            const pattern = new RegExp(`{${key}}`, 'g');
            raw = raw.replace(pattern, params[key]);
          });
          return raw;
        };

        function extractValue(dictionary, language, frags) {
          let temp = dictionary[language];
          for (const frag of frags) {
            temp = temp[frag] || undefined

            if (!temp) {
              break;
            }
          }
          return temp;
        }
      })((typeof window !== 'undefined') ? window : this);
    </script>
  </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Just before I drop off, Translater.js was another good option if you are not concerned with embedded html tags. Now that I think about it, I could have worked around it. Boom, here is what I could have done. I can't believe I missed that because I was looking for a something straight forward. Here you have it. This below is a better option...

<!DOCTYPE html>
<html lang="en" dir="ltr">
  <head>
    <meta charset="utf-8">
    <title eo-translator="title">
      Test title
      <!--{es} Título de la prueba -->
    </title>
  </head>
  <body>
    <div class="container">
      <h2>
        Hello
        <!--{es}Hola! -->
        <a href="https://www.google.com/chrome/">
          Link to Google Chrome
          <!--{es}Enlace a Google Chrome -->
        </a>
      </h2>
    </div>

    <script src="https://unpkg.com/translater.js/dist/translater.js" type="text/javascript"></script>
    <script type="text/javascript">

    // You can still get the user's language with the example above and pass it to the lang option here. 
      var tran = new Translater({
        lang:"es"
      });
    </script>
 </body>
</html>
Enter fullscreen mode Exit fullscreen mode

If you ever run into issues with no space between words with nested elements, all you have to do it add double spaces after the language definition

   <!--{es}  Enlace a Google Chrome-->
Enter fullscreen mode Exit fullscreen mode

In conclusion, the second option gives you more control as you don't have to worry about whether the package is updated or not. It's quite simple and more bulky but I settled for that.

Sorry If it's disoriented. I am just trying my hand at writing my thinking process solving a problem. Feel free to drop comments

Top comments (0)