Changing text in several different places on the HTML page

5

I'm thinking of developing a i18n system for an application.

My idea is : there is a text # i18n_label_name somewhere on the page and # i18n_label_contact elsewhere. >

Question : What is the most performative way to scan the whole page for these identifiers and change them to the translation text.

    
asked by anonymous 12.11.2015 / 12:51

5 answers

2

Using replaces , relative to jquery.html vs. innerHTML , the second option was considerably faster by about 29% over the former. This is a fairly considerable difference, regardless of the loading time of Jquery, unlike the InnerHTML which is native to Javascript .

However, in this second test , innerHTML was slower compared to jquery.append and jquery.html .

But how come?

What happens here is as follows, note that in the first example the code is written as follows:

var list = document.getElementById('list');
var html = '';

for (var i = 0; i < 1000; i++) {
    html += '<div>Test ' + i + '</div>';
}

list.innerHTML = html;

Already on the second:

var list = document.getElementById('list');
for (var i = 0; i < len; i++) {
  list.innerHTML = list.innerHTML + '<div>Test ' + i + '</div>';
}

In other words, the two tests have similar purposes, but in performance, you can notice a very different number, this is due to the innerHTML access in the second test being done inside for , it may seem silly, but with every looping the same is accessed and this little slip can generate catastrophic slowness.

Now one more thing regarding security ...

According to MDN : "[...] it is recommended that you do not use innerHTML when inserting plaintext as an alternative, use node.textContent . This does not interpret content passed as HTML , but instead, insert it as plain text. "

This excerpt concerns the execution of <script> and security holes in the use of innerHTML (for more details I suggest you see the link) and alternatively, node.textContent .

The node.textContent represents the contents of the element node and its descendants, and the main difference between it and the innerHTML is that the first one does not parse HTML as a whole, just the textNode making it more performative over the second.

There are several other methods such as: innerText and nodeValue , I will not speak in detail of everyone so that the content is not too long with a brief search you can see what each one does. Well let's go to what matters, this test , includes all the native methods quoted tested on different elements.

But which one to use? The textContent in this last test was more performative compared to the others, totaling an average of approximately 15% in comparison to the other forms. I believe that the best way based on what was described above, in both safety and performance relation is to use the same. Another point to take into consideration and the fact of being Javascript .

Regarding native selectors, the getElementByID selector is faster than selectors like getElementsByClassName and querySelector this test . Id also gains in the Jquery in this test selector.

Which one to use? in first test and in this third you can clearly see that new native features come out ahead of performance.

The first two tests were taken from the StackOverflow .

Here is a list of other interesting topics in relation the performance in javascript, I recommend it as an additional reading.

Note: Values may vary in milliseconds depending on the machine and browser.

    
12.11.2015 / 19:51
5

I think the best way to do this is to use data-custom and json properties with values.

i18n = {};
i18n["pt"] = {
  "name": "Nome",
  "contact": "Contato"
};

i18n["es"] = {
  "name": "Nombre",
  "contact": "Contacto"
};

i18n["fn"] = {
  "name": "Nom",
  "contact": "Contact"
};

i18n["en"] = {
  "name": "Name",
  "contact": "Contact"
};

var language = document.getElementById("language");
var elementos = document.querySelectorAll("[data-i18n]");
var onLanguageChange = function (event) {  
  [].forEach.call(elementos, function (elem, indice) {
    elem.textContent = i18n[language.value][elem.dataset.i18n];
  }); 
};

language.addEventListener("change", onLanguageChange);
onLanguageChange();
<div>
  <select id="language">
    <option value="pt">Português</option>
    <option value="es">Español</option>
    <option value="fn">Français</option>
    <option value="en">English</option>  
  </select>
</div>
<div>
  Name: <label data-i18n="name"></label>
</div>
<div>
  Contact: <label data-i18n="contact"></label>
</div>

Follows an implementation using class instead of data-custom

i18n = {};
i18n["pt"] = {
  "name": "Nome",
  "contact": "Contato"
};

i18n["es"] = {
  "name": "Nombre",
  "contact": "Contacto"
};

i18n["fn"] = {
  "name": "Nom",
  "contact": "Contact"
};

i18n["en"] = {
  "name": "Name",
  "contact": "Contact"
};

var language = document.getElementById("language");
var elementos = document.querySelectorAll("[class*='i18n']");
var onLanguageChange = function (event) {
  [].forEach.call(elementos, function (elem, indice) {
    var propName = [].filter.call(elem.classList, function (classe, indice) {
      return classe.indexOf("i18n") >= 0;
    })[0].split("-")[1];
    elem.textContent = i18n[language.value][propName];
  }); 
};

language.addEventListener("change", onLanguageChange);
onLanguageChange();
<div>
  <select id="language">
    <option value="pt">Português</option>
    <option value="es">Español</option>
    <option value="fn">Français</option>
    <option value="en">English</option>  
  </select>
</div>
<div>
  Name: <label class="i18n-name"></label>
</div>
<div>
  Contact: <label class="i18n-contact"></label>
</div>

And finally the solution using string substitution, in which case all HTML is recreated, so all queries to DOM objects need to be redone and re-associated events.

i18n = {};
i18n["pt"] = {
  "name": "Nome",
  "contact": "Contato"
};

i18n["es"] = {
  "name": "Nombre",
  "contact": "Contacto"
};

i18n["fn"] = {
  "name": "Nom",
  "contact": "Contact"
};

i18n["en"] = {
  "name": "Name",
  "contact": "Contact"
};

var template = document.body.innerHTML;
var language = document.getElementById("language");
var onLanguageChange = function (event) {  
  document.body.innerHTML = template.replace(/{{i18n-(\w*)}}/g,function(m,key){
    return i18n[language.value].hasOwnProperty(key)? i18n[language.value][key]: "";
  });
  
  language = document.getElementById("language");
  language.addEventListener("change", onLanguageChange);
};

onLanguageChange();
<div>
  <select id="language">
    <option value="pt">Português</option>
    <option value="es">Español</option>
    <option value="fn">Français</option>
    <option value="en">English</option>  
  </select>
</div>
<div>
  Name: <label>{{i18n-name}}</label>
</div>
<div>
  Contact: <label>{{i18n-contact}}</label>
</div>
    
12.11.2015 / 19:40
2

You can use Jquery

$("#i18n_label_name").html("TEXTO");
$("#i18n_label_contact").html("TEXTO");

With this selector $ ("# i18n_label_name"), it goes straight to the element, without having to scan all HTML.

    
12.11.2015 / 18:52
2

Using only JS with the function [querySelector()][1]

document.querySelector("#i18n_label_name").innerHTML = "Novo Texto";

document.querySelector("#i18n_label_contact").innerHTML = "Novo Texto";
    
12.11.2015 / 18:59
1

For simplicity, I suggest doing a method in jQuery, but if you want to write a little more and stay independent of the library, you can do as the example suggested by @TobyMosque:

HTML content:

<select name="translate" id="tradutor">
    <option value="pt-BR">Portunguês</option>
    <option value="en-US">Inglês</option>
    <option value="es-GT">Espanhol</option>
    <option value="pe-PG">Língua do P</option>

</select>

<div data-translate="pt-BR">
    <p data-i18n="content_A">The Book's on the table</p>
    <p data-i18n="content_B">Hello World</p>
    <p data-i18n="content_C">My name is Ivan</p>
</div>

And the javascript:

 document.onreadystatechange = function () {
    if (document.readyState == "interactive") {
        var el_translate = document.querySelector('[data-translate]');
        var tradutor = document.getElementById('tradutor');

        tradutor.value = el_translate.dataset.translate;        
        tradutor.addEventListener("change", function (event) {
            el_translate.dataset.translate = event.target.value;
            setLang(el_translate);
        });
        setLang(el_translate);
    }
}

var setLang = (function() {
    var i18n = {
        "pt-BR":{
            "content_A":"O livro está sobre a mesa",
            "content_B":"Olá Mundo",
            "content_C":"Meu nome é Ivan",
            "content_PT":"Português",
            "content_EN":"Inglês",
            "content_ES":"Espanhol",
            "content_PE":"Língua do P",

        },
        "en-US":{
            "content_A":"The Book's on the table",
            "content_B":"Hello World",
            "content_C":"My name is Ivan",
            "content_PT":"Portuguese",
            "content_EN":"English",
            "content_ES":"Espanish",
            "content_PE":"Language of P",
        },
        "es-GT":{
            "content_A":"El libro está en la mesa",
            "content_B":"Hola mundo",
            "content_C":"Mi nombre es Iván",
            "content_PT":"Portugués",
            "content_EN":"Inglés",
            "content_ES":"Español",
            "content_PE":"Idioma hacer P",
        },
        "pe-PG":{
            "content_A":"PO PliPvro PesPtá PsoPbre Pa PmePsa",
            "content_B":"POPlá PMunPdo",
            "content_C":"PMeu PnoPme Pé PIPvan",
            "content_PT":"PPorPtuPguês",
            "content_EN":"PInPglês",
            "content_ES":"PEsPpanhol",
            "content_PE":"PLínPgua Pdo PP",
        }
    };

    return function (e) {
        var elementos = document.querySelectorAll("[data-i18n]");
        [].forEach.call(elementos, function (el, index) {
            el.textContent = i18n[e.dataset.translate][el.dataset.i18n];
        }); 
    };
})();

Here the working example and Pure JavaScript

And here the example working with jQuery

  

Edited: As suggested by @TobyMosque, which I found very welcome, an adaptation without using jQuery:

    
12.11.2015 / 21:36