Couple of weeks ago I spoke to a friend over at Mozilla about internationalization in JavaScript. This is an area that’s lacking in JavaScript and during this discussion I got the idea of implementing a solution based on the methods used be server side languages. Today I am happy to announce the first release of i18njs to fill this gap.
i18njs is made up of a couple of parts, the main script, json files and a small dependency on the server side. Why this server side dependency? Well basically, because their is no reliable way to get hold of the user’s language and/or locale using client side JavaScript. There are a couple of language properties exposed on the navigator Object such as .language, .userLanguage etc. but none of these are reliable and, for the most part, are not effected by changes made by the user with regards language preferences.
The one place that has this information and is most reliable is the ‘Accept-Language’ HTTP header. Unfortunately we cannot get at this using JavaScript on the client but, we can make a quick call to the server for this information, store it on the client and then we are good to go. I am not going to discuss the implementation of this, there are currently a demos using Java as well as Rails in the repo on Github you can check-out.
Let’s start by looking at the usage of i18njs:
Pretty straight forward. You create the options object and then populate two properties. The first is the URL that will return the sub-string from the Accept-Language header and the second indicates whether you wish to support locales or not. Let’s look at the second property a little more. When a user sets their language in their browser they have, for some languages, more than one option. For example for French, you can select French(France), French(Canada) etc. For these language selections the result returned from the server will include the language as well as the locale as follows: fr-FR or fr-CA
If you need to differentiate between locales in your application, then you want to set the supportLocale property to true. So why does this property exist and why is it important? The best way to explain is to discuss how i18njs works after the call to the server has completed.
In server side languages internationalization is usually handled by loading some form of properties file that contains the strings, formats etc. localized for the current language and/or locale. These normally take the form of a text based properties file or in some cases a ‘standardized’ XML format, i18njs works in the same manner.
After the language has been determined, the appropriate localization file get’s loaded. These files are in the JSON format and the names of the files are named according to the language and, optionally, the locale so, if supportLocale is set to true and the current user language is set to French/Canadia it will try to load a file named fr-CA.json. If the supportLocale is false, it will substring the retunt value and use the first 2 characters only, which means it will now attempt to load a file simply names fr.json.
Your localization files should all reside in a folder called locale, the content of the file is up to you, as long as it is in a valid JSON format or parse error will result. Let’s look at a small sample. Below is thee contents of a en-US.json file:
And the following is then the French localized version:
As you can see from the above, the keys always remain the same irrespective of the language of the values, this is standard practice and ensures that your code does not have to change if the language changes. Say you were doing some form validation, you will use i18njs as follows in this scenario:
So, now that you know why you would want to set the supportLocale property and some of the inner workings of i18njs there is a couple of small details to cover.
This has been tested in IE6+, Firefox, Chrome, Safari and Opera and works everywhere. There is one aspect of i18njs that works slightly differently in IE7/6 and modern browsers and that is the way the localization data is stored on the client. In all modern browsers, and this includes IE8, once the data has been loaded the first time, the result is stored using localStorage part of the WebStorage API and therefore THE Ajax call to the server never happens again.
In IE6/7 on the other hand it will make a call to the server on each page load. Now, I could polyfil this but two things, the size of the localization files are generally not going to be large, generally smaller then the polyfil itself, and the performance hit users with the older versions are going to suffer will not be great. I also wanted to avoid yet another dependency on a polyfil as well as third party plugins.
One last thing to mention is the userSelected function. Even though Accept-Language is going to be accurate in what it returns, we do not want to lock our users down and not give them the option to switch to another language should they wish to, this then is where userSelected is used.
Say we some links at the top of our site or application:
We can hook userSelected to these links as follows:
Now when a user clicks any of those links, the language will be overridden and set to the user’s selected choice. The code and demos are available on Github, a live demo is available on the project page and I would love to hear your feedback. On a side note, you are more than welcome to use http://i18njs.espressive.org/get-lang if you do not feel like implementing the server side or are in a scenario where there is no real back-end code.
Jigar Shah wrote on :
Schalk Neethling wrote on :
Billy Hoffman wrote on :
Simon wrote on :
Billy Hoffman wrote on :
gandalf wrote on :
Schalk Neethling wrote on :
mobile application development wrote on :