Best practice javascript and multilanguage

JavascriptInternationalization

Javascript Problem Overview


what is the best practice for multilanguage website using DOM Manipulating with javascript? I build some dynamic parts of the website using javascript. My first thought was using an array with the text strings and the language code as index. Is this a good idea?

Javascript Solutions


Solution 1 - Javascript

When I've built multi-lingual sites before (not very large ones, so this might not scale too well), I keep a series of "language" files:

  • lang.en.js
  • lang.it.js
  • lang.fr.js

Each of the files declares an object which is basically just a map from key word to language phrase:

// lang.en.js
lang = {
    greeting : "Hello"
};

// lang.fr.js
lang = {
    greeting : "Bonjour"
};

Dynamically load one of those files and then all you need to do is reference the key from your map:

document.onload = function() {
    alert(lang.greeting);
};

There are, of course, many other ways to do this, and many ways to do this style but better: encapsulating it all into a function so that a missing phrase from your "dictionary" can be handled gracefully, or even do the whole thing using OOP, and let it manage the dynamic including of the files, it could perhaps even draw language selectors for you, etc.

var l = new Language('en');
l.get('greeting');

Solution 2 - Javascript

There are a few things you need to keep in mind when designing multilanguage support:

1 - Separate code from data (i.e. don't hard-code strings right into your functions)

2 - create a formatting hook function to deal with localization differences. Allowing formattable strings ("{0}") is better than concatenating ("Welcome to" + value), for a lot of reasons:

  • in some languages, a number is formatted like 1.234.678,00 instead of 1,234,567.00
  • pluralization is often not as simple as appending an "s" at the end of the singular
  • grammar rules are different and can affect the order of things so you should allow dynamic data to be appended after the translation hook: for example, "Welcome to {0}" turns into "{0} he youkoso" in japanese (this happens in pretty much every language, mind you).

3 - Make sure that you can actually format strings after the translation hook runs, so you can reuse keys.

4 - Do not, under any circunstance, hook database outputs to the translator utility. If you have multilingual data, create separate tables / rows in your database. I've seen people get this no-brainer wrong fairly often (usually for countries and states/provinces in forms).

5 - Create explicit coding practices rules for creating keys. The formatter utility function (which will look something like translate("hello world") will take a key as a parameter, and keys with slight variations make maintainance very annoying. For instance, you might end up with three keys in the following example: "enter you name", "enter your name:", "enter your name: ". Choose one format (e.g. no colon, trimmed) and catch discrepancies in code reviews. Don't do this filtering programmatically, as it can trigger false positives.

6 - Be mindful that HTML markup could potentially be needed in the translation table (e.g. if you need to bold a word in a sentence, or have footnote medical references). Test for this extensively.

7 - There are several ways of importing language strings. Ideally, you should have multiple versions of a language.lang.js file, switch between them with server side code, and reference the file from the bottom of the HTML file. Pulling the file via AJAX is also an alternative, but could introduce delays. Merging language.js into your main code file is not advisable, since you lose the benefits of file caching.

8 - Test with your target languages. This sounds silly, but I've seen a serious bug once because the programmer didn't bother to check for the existence of "é" in the key.

Solution 3 - Javascript

function Language(lang)
{
	var __construct = function() {
		if (eval('typeof ' + lang) == 'undefined')
		{
			lang = "en";
		}
		return;
	}()

	this.getStr = function(str, defaultStr) {
		var retStr = eval('eval(lang).' + str);
		if (typeof retStr != 'undefined')
		{
			return retStr;
		} else {
			if (typeof defaultStr != 'undefined')
			{
				return defaultStr;
			} else {
				return eval('en.' + str);
			}
		}
	}
}

After adding this to your page, you can work with it like this:

var en = {
	SelPlace:"Select this place?",
	Save:"Saved."
};

var tr = {
	SelPlace:"Burayı seçmek istiyor musunuz?"
};

var translator = new Language("en");
alert(translator.getStr("SelPlace")); // result: Select this place?
alert(translator.getStr("Save")); // result: Saved.
alert(translator.getStr("DFKASFASDFJK", "Default string for non-existent string")); // result: Default string for non-existent string

var translator = new Language("tr");
alert(translator.getStr("SelPlace")); // result: Burayı seçmek istiyor musunuz?
alert(translator.getStr("Save")); // result: Saved. (because it doesn't exist in this language, borrowed from english as default)
alert(translator.getStr("DFKASFASDFJK", "Default string for non-existent string")); // result: Default string for non-existent string

If you call the class with a language that you haven't defined, English(en) will be selected.

Solution 4 - Javascript

Just found a nice article about i18n in javascript:
http://24ways.org/2007/javascript-internationalisation

Although a simple google search with i18n + javascript reveals plenty of alternatives.

In the end, it depends on how deep you want it to be. For a couple of languages, a single file is enough.

You could use a framework like Jquery, use a span to identify the text (with a class) and then use the id of each span to find the corresponding text in the chosen language.
1 Line of Jquery, done.

Solution 5 - Javascript

After reading the great answers by nickf and Leo, I created the following CommonJS style language.js to manage all my strings (and optionally, Mustache to format them):

var Mustache = require('mustache');

var LANGUAGE = {
	general: {
        welcome: "Welcome {{name}}!"
    }
};

function _get_string(key) {
	var parts = key.split('.');
	var result = LANGUAGE, i;
	for (i = 0; i < parts.length; ++i) {
		result = result[parts[i]];
	}
	return result;
}

module.exports = function(key, params) {
	var str = _get_string(key);
	if (!params || _.isEmpty(params)) {
		return str;
	}
	return Mustache.render(str, params);
};

And this is how I get a string:

var L = require('language');
var the_string = L('general.welcome', {name='Joe'});

Solution 6 - Javascript

This way you can use one js code for multi language by multi word :

var strings = new Object();

if(navigator.browserLanguage){
  lang = navigator.browserLanguage;
}else{
  lang = navigator.language;
}

lang = lang.substr(0,2).toLowerCase();



if(lang=='fa'){/////////////////////////////Persian////////////////////////////////////////////////////


        strings["Contents"]                              = "فهرست";
        strings["Index"]                                 = "شاخص";
        strings["Search"]                                = "جستجو";
        strings["Bookmark"]                              = "ذخیره";
        
        strings["Loading the data for search..."]        = "در حال جسنجوی متن...";
        strings["Type in the word(s) to search for:"]    = "لغت مد نظر خود را اینجا تایپ کنید:";
        strings["Search title only"]                     = "جستجو بر اساس عنوان";
        strings["Search previous results"]               = "جستجو در نتایج قبلی";
        strings["Display"]                               = "نمایش";
        strings["No topics found!"]                      = "موردی یافت نشد!";
        
        strings["Type in the keyword to find:"]          = "کلیدواژه برای یافتن تایپ کنید";
        
        strings["Show all"]                              = "نمایش همه";
        strings["Hide all"]                              = "پنهان کردن";
        strings["Previous"]                              = "قبلی";
        strings["Next"]                                  = "بعدی";
        
        strings["Loading table of contents..."]          = "در حال بارگزاری جدول فهرست...";
        
        strings["Topics:"]                               = "عنوان ها";
        strings["Current topic:"]                        = "عنوان جاری:";
        strings["Remove"]                                = "پاک کردن";
        strings["Add"]                                   = "افزودن";

}else{//////////////////////////////////////English///////////////////////////////////////////////////

strings["Contents"]                              = "Contents";
strings["Index"]                                 = "Index";
strings["Search"]                                = "Search";
strings["Bookmark"]                              = "Bookmark";

strings["Loading the data for search..."]        = "Loading the data for search...";
strings["Type in the word(s) to search for:"]    = "Type in the word(s) to search for:";
strings["Search title only"]                     = "Search title only";
strings["Search previous results"]               = "Search previous results";
strings["Display"]                               = "Display";
strings["No topics found!"]                      = "No topics found!";

strings["Type in the keyword to find:"]          = "Type in the keyword to find:";

strings["Show all"]                              = "Show all";
strings["Hide all"]                              = "Hide all";
strings["Previous"]                              = "Previous";
strings["Next"]                                  = "Next";

strings["Loading table of contents..."]          = "Loading table of contents...";

strings["Topics:"]                               = "Topics:";
strings["Current topic:"]                        = "Current topic:";
strings["Remove"]                                = "Remove";
strings["Add"]                                   = "Add";

}

you can add another lang in this code and set objects on your html code. I used Persian For Farsi language and English, you can use any type language just create copy of this part of code by If-Else statement.

Solution 7 - Javascript

You should look into what has been done in classic JS components - take things like Dojo, Ext, FCKEditor, TinyMCE, etc. You'll find lots of good ideas.

Usually it ends up being some kind of attributes you set on tags, and then you replace the content of the tag with the translation found in your translation file, based on the value of the attribute.

One thing to keep in mind, is the evolution of the language set (when your code evolves, will you need to retranslate the whole thing or not). We keep the translations in PO Files (Gnu Gettext), and we have a script that transforms the PO File into ready to use JS Files.

In addition:

  • Always use UTF-8 - this sounds silly, but if you are not in utf-8 from start (HTML head + JS encoding), you'll be bust quickly.
  • Use the english string as a key to your translations - this way you won't end up with things like: lang.Greeting = 'Hello world' - but lang['Hello world'] = 'Hello world';

Solution 8 - Javascript

You can use a google translator:

<div id="google_translate_element" style = "float: left; margin-left: 10px;"></div>

<script type="text/javascript">
function googleTranslateElementInit() {
  new google.translate.TranslateElement({pageLanguage: 'en', layout: google.translate.TranslateElement.InlineLayout.HORIZONTAL}, 'google_translate_element');
}
</script>

<script type="text/javascript" src="//translate.google.com/translate_a/element.js?cb=googleTranslateElementInit"></script>

</div><input type = "text" style = "display: inline; margin-left: 8%;" class = "sear" placeholder = "Search people..."><button class = "bar">&#128270;</button>

Solution 9 - Javascript

class Language {
	constructor(lang) {
		var __construct = function (){
			if (eval('typeof ' + lang) == 'undefined'){
				lang = "en";
			}
			return;
		};
		this.getStr = function (str){
			var retStr = eval('eval(lang).' + str);
			if (typeof retStr != 'undefined'){
				return retStr;
			} else {
				return str;
			}
		};
	}
}

var en = {
    Save:"Saved."
};

var fa = {
    Save:"ذخیره"
};

var translator = new Language("fa");
console.log(translator.getStr("Save"));

Solution 10 - Javascript

For Spring bundles and JavaScript there are simple solution: generate i18n array in template (e.g. JSP) and use it in JavaScript:

JSP:

<html>
<script type="text/javascript">
    var i18n = [];
    <c:forEach var='key' items='<%=new String[]{"common.deleted","common.saved","common.enabled","common.disabled","...}%>'>
        i18n['${key}'] = '<spring:message code="${key}"/>';
    </c:forEach>
</script>
</html>

And in JS:

alert(i18n["common.deleted"]);

See also https://stackoverflow.com/questions/6218970/resolving-springmessages-in-javascript-for-i18n-internationalization

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionmarquiesView Question on Stackoverflow
Solution 1 - JavascriptnickfView Answer on Stackoverflow
Solution 2 - JavascriptLeoView Answer on Stackoverflow
Solution 3 - JavascriptNailView Answer on Stackoverflow
Solution 4 - JavascriptBerzemusView Answer on Stackoverflow
Solution 5 - JavascriptTzachView Answer on Stackoverflow
Solution 6 - JavascriptAmirhosseinView Answer on Stackoverflow
Solution 7 - JavascriptBertrand GorgeView Answer on Stackoverflow
Solution 8 - JavascriptBenjamin SloutskyView Answer on Stackoverflow
Solution 9 - JavascriptamirrezasalariView Answer on Stackoverflow
Solution 10 - JavascriptGrigory KislinView Answer on Stackoverflow