0

I understand that document.write is evil and should not be used to output text on a website.

However, I did not find a better solution yet for my usage, where I output translated text via javascript on a page. Consider this:

My template code looks like this:

<h1><script>_e("title");</script></h1>

In a javascript file I have similar code like this to translate the strigs:

// Initialize the dictionaries and set current language.
window.lang = {};
window.lang.en = { "title": "Sample Page" };
window.lang.de = { "title": "Beispiel Seite" };

window.curLang = "en";

// Translation function that should output translated text.
function _e(text) {
    var dictionary = window.lang[window.curLang];
    var translation = dictionary[text];

    // BELOW LINE IS EVIL.
    // But what's the alternative without having to change the template code above?
    document.write( translation );
}

Question:

Can I change the javascript function to not use document.write to work without changing the template code? If not: What would be the best solution for translation here?

Philipp
  • 10,240
  • 8
  • 59
  • 71
  • `document.write` isn't evil, there is nothing with it when used correctly (*ex: in this case*). Although if you insist [have a look at this question](http://stackoverflow.com/questions/1197575/can-scripts-be-inserted-with-innerhtml). – Spencer Wieczorek Jun 13 '15 at 17:43

3 Answers3

1

One way of doing the translations without using document.write - and with the added consequence (benefit?) of having text when you turn off Javascript or don't have a translation - would be to annotate elements that require translation and perform the translation after the document is loaded by changing the innerHTML or textContent (if you want to escape entities).

Your translate functions could be something like this:

function translateAll() {
    var dictionary = window.lang[window.curLang];
    var elements = document.querySelectorAll("[data-i18n-id]");

    for (var i = 0; i < elements.length; i++) {
        var el = elements[i];

        var translation = dictionary[el.dataset.i18nId]
        if (translation) {
            el.innerHTML = translation;
        }
    }
}

And you could execute it, for example, onload:

<body onload="translateAll();">
  <h1 data-i18n-id="title">My Title</h1>
</body>
m4ktub
  • 3,061
  • 1
  • 13
  • 17
  • A nice solution, though I'd need to change the existing markup of all templates... I'll consider it, since it allows me to change the language without reloading the page ;-) – Philipp Jun 13 '15 at 18:51
1

This is my first time using document.currentScript but i've tried this code and it should work correctly:

    <!DOCTYPE html>
<html>
<head>
    <title></title>
    <script  type="text/javascript" charset="utf-8">
        // Initialize the dictionaries and set current language.
        window.lang = {};
        window.lang.en = { "title": "Sample Page" };
        window.lang.de = { "title": "Beispiel Seite" };

        window.curLang = "en";

        // Translation function that should output translated text.
        function _e(text) {
            var dictionary = window.lang[window.curLang];
            var translation = dictionary[text];

            var container = document.currentScript;
            container.parentNode.innerHTML = translation;
        }
    </script>
</head>
<body>
    <h1><script>_e("title");</script></h1>
</body>
</html> 

Another why is to use special tags or attribute to replace with something like jQuery, but this change your template. Something like

<span class='i18n-text'>title</span>

    var dictionary = window.lang[window.curLang];
    jQuery('.i18n-text').each(function(){
        var text = $(this).text();
        var translation = dictionary[text];
        $(this).html(translation);
    });

(i haven't tried the second solution but it should work :D)

wezzy
  • 5,897
  • 3
  • 31
  • 42
  • That's two great options! Just a note: I found that `document.currentScript` is not supported by IE yet - but for me this is okay. – Philipp Jun 13 '15 at 18:48
0

Using , the way to go would be to store your translation keys in computed variables, then write a translation service function that looks the translations up in a hash table. The thing that would make this trigger would then be an observable which you would do an attribute binding on the HTML tag with, setting the lang attribute. Changing this observable would trigger the depending computed variables to automatically re-evaluate and update your document. If using knockout is an option here, let me know and I will setup an example.

connexo
  • 53,704
  • 14
  • 91
  • 128