DEV Community

loading...

Translate React Public HTML files

nkemjiks profile image Mbonu Blessing ・4 min read

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>

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>

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>

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-->

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

Discussion

pic
Editor guide