0

I use one javascript to build my html page using templates (PageBuilder) and another to translate the content Translator. Problem is that the page has to be fully built before translating it otherwise some items are not found by Translator when it loads, and that's what happens here.

From reading the logs, I can tell that the problem is in PageBuilder.drawNav(): before it ends, Translator.load() has already started. Can you tell me how to make sure drawNav is fully completed before the code can continue?

Main js

import PageBuilder from "/js/page-builder.js";
import Translator from "/js/translator.js";

var pageBuilder = new PageBuilder();
var translator = new Translator();

pageBuilder.load().then(() => translator.load().then(() => document.getElementById(`languageButton`).onclick = function() {translator.switchLanguage(translator)}));

Translator

/* Courtesy of: https://codeburst.io/translating-your-website-in-pure-javascript-98b9fa4ce427 */
`use strict`

class Translator {
  constructor() {
    this._lang = this.getLanguage();
    this._elements = document.querySelectorAll(`[data-i18n]`);
  }

  getLanguage() {
    var lang = navigator.languages ? navigator.languages[0] : navigator.language;

    return lang.substr(0, 2);
  }

  load(lang = null) {
    this._elements = document.querySelectorAll(`[data-i18n]`);
    console.log("Translator.load() document.querySelectorAll...");
    console.log(this._elements);
    if (lang) {
      this._lang = lang;
    }
    else {
      var re = new RegExp(`lang=([^;]+)`);
      var value = re.exec(document.cookie);
      var cookieLang = (value != null) ? unescape(value[1]) : null;
      if (cookieLang) {
        this._lang = cookieLang;
      }
    }

    return fetch(`/json/lang-${this._lang}.json`)
      .then((res) => res.json())
      .then((translation) => {
        this.translate(translation);
      })
      .then(this.toggleLangTag())
      .then(document.cookie = `lang=${this._lang};path=/`)
      .then(() => {
        console.log("Translator.load() --end document.querySelectorAll...");
        console.log(this._elements);
      })
  }

  translate(translation) {
    console.log("translate(translation)");
    this._elements.forEach((element) => {
      var keys = element.dataset.i18n.split(`.`);
      var text = keys.reduce((obj, i) => obj[i], translation);

      if (text) {
        element.innerHTML = text;
      }
      else {
        element.innerHTML = `key ${keys} not found for ${this._lang}!`
      }
    });
  }

  toggleLangTag() {
    if (document.documentElement.lang !== this._lang) {
      document.documentElement.lang = this._lang;
    }
  }

  switchLanguage(translator) {
    var availableLang = [`en`, `fr`];
    var currentLangIndex = availableLang.indexOf(translator._lang);
    var nextLang = availableLang[(currentLangIndex + 1)%availableLang.length];
    translator.load(nextLang);
  }
}

export default Translator;

PageBuilder

`use strict`

class PageBuilder {
  constructor() {
    this._nav = document.getElementsByTagName(`nav`)[0];
    this._footer = document.getElementsByTagName(`footer`)[0];
    this._url = window.location.href;
  }

  load() {
    return this.drawFooter()
      .then(() => fetch(`/json/menu.json`))
      .then((res) => res.json())
      .then((jsonMenu) => {
        this.drawNav(jsonMenu);
      })
  }

  drawFooter() {
    console.log("drawFooter()");
    return fetch(`/templates/footer.html`)
      .then((res) => res.text())
      .then((resText) => {
        console.log(`this._footer=${this._footer}`);
        console.log(`resText=${resText}`);
        this._footer.innerHTML = resText;
        console.log("drawFooter()--end");
        console.log("PageBuilder.drawFooter() document.querySelectorAll...");
        console.log(document.querySelectorAll(`[data-i18n]`));
      })
  }

  drawNav(jsonMenu) {
    console.log("drawNav(jsonMenu)");
    var htmlMenu = ``;
    return fetch(`/templates/site-title-div.html`)
      .then((res) => res.text())
      .then((resText) => {
        htmlMenu = resText;
        console.log(`htmlMenu=${htmlMenu}`);
      })

      .then(() => {
        htmlMenu += `<ul>`;
        var previousParent = `/`;
        var ulOpen = false;

        for(var i = 0; i < jsonMenu.length; i++) {
          var jsonMenuItem = jsonMenu[i];

          var regexp = /http:\/\/cypher-f\.com((\/[a-z\-]*)?(\/[a-z\-]+)?)/g;
          var match = regexp.exec(this._url);
          var currentPageFullUrl  = match[0];
          var currentPageFullHref = match[1];
          var currentPageParent   = match[2];
          var currentPageLevel2   = match[3];

          var openingUl = ``;
          var closingUl = ``;
          var jsonParent = jsonMenuItem[`parent`];

          if ((jsonParent === `/`) || (jsonParent === currentPageParent)) {
            if (jsonParent != previousParent) {
              if (ulOpen) {
                htmlMenu += `</ul>`;
                ulOpen = false;
              }
              if (jsonParent != `/`) {
                htmlMenu += `<ul>`;
                ulOpen = true;
              }
            }


            var material_icon = jsonMenuItem[`material-icon`];
            var href = jsonMenuItem[`href`];
            var i18n = jsonMenuItem[`data-i18n`];
            var active = (currentPageFullHref === jsonMenuItem.href ? ` class="active"` : ``);
            htmlMenu += `<li${active}><i class="material-icons">${material_icon}</i><a href="${href}" data-i18n="${i18n}"></a></li>`;
            previousParent = jsonParent;
          }
        }

        if (ulOpen) {
          htmlMenu += `</ul>`;
        }
        htmlMenu += `</ul>`;
        this._nav.innerHTML = htmlMenu;
        console.log("drawNav(jsonMenu)--end");
        console.log("PageBuilder.drawNav() document.querySelectorAll...");
        console.log(document.querySelectorAll(`[data-i18n]`));
      })
  }
}

export default PageBuilder;

1 Answer 1

1

You need to add return before this.drawNav() call in

  load() {
    return this.drawFooter()
      .then(() => fetch(`/json/menu.json`))
      .then((res) => res.json())
      .then((jsonMenu) => {
        return this.drawNav(jsonMenu);
      })
  }

I think here you have an extra parentheses .then(this.toggleLangTag()).

Sign up to request clarification or add additional context in comments.

3 Comments

(I don't see what you mean about the extra parenthesis ?)
then accepts a callback function and your this.toggleLangTag() doesn't return a function. Probably, you'd like to have smthng like .then(this.toggleLangTag), am I right?
I'll trust you on that, honestly I'm far from mastering the callback system :D

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.