{"id":2098,"date":"2011-10-06T02:35:08","date_gmt":"2011-10-06T10:35:08","guid":{"rendered":"http:\/\/blog.mozilla.org\/webdev\/?p=2098"},"modified":"2011-10-06T02:35:08","modified_gmt":"2011-10-06T10:35:08","slug":"i18njs-internationalize-your-javascript-with-a-little-help-from-json-and-the-server","status":"publish","type":"post","link":"https:\/\/blog.mozilla.org\/webdev\/2011\/10\/06\/i18njs-internationalize-your-javascript-with-a-little-help-from-json-and-the-server\/","title":{"rendered":"i18njs : Internationalize Your JavaScript With A Little Help From JSON And The Server"},"content":{"rendered":"<p>Couple of weeks ago I spoke to a friend over at Mozilla about internationalization in JavaScript. This is an area that&#8217;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.<\/p>\n<p>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&#8217;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.<\/p>\n<p>The one place that has this information and is most reliable is the &#8216;<a href=\"http:\/\/www.w3.org\/International\/questions\/qa-accept-lang-locales\">Accept-Language&#8217; <abbr title=\"HyperText Transfer Protocol\">HTTP<\/abbr> header<\/a>. 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 <a href=\"https:\/\/github.com\/ossreleasefeed\/i18njs\/tree\/master\/demos\">Java as well as Rails in the repo on Github<\/a> you can check-out.<\/p>\n<p>Let&#8217;s start by looking at the usage of i18njs:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/1267069.js?file=usage.js\"><\/script><\/p>\n<p>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&#8217;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<\/p>\n<p>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.<\/p>\n<p>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 &#8216;standardized&#8217; XML format, i18njs works in the same manner.<\/p>\n<p>After the language has been determined, the appropriate localization file get&#8217;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.<\/p>\n<p>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&#8217;s look at a small sample. Below is thee contents of a en-US.json file:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/1267069.js?file=sample_lang.json\"><\/script><\/p>\n<p>And the following is then the French localized version:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/1267069.js?file=sample_lang_fr.json\"><\/script><\/p>\n<p>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:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/1267069.js?file=sample_form_val.js\"><\/script><\/p>\n<p>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.<\/p>\n<p>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 <a href=\"http:\/\/dev.w3.org\/html5\/webstorage\/\">localStorage part of the WebStorage API<\/a> and therefore THE Ajax call to the server never happens again.<\/p>\n<p>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.<\/p>\n<p>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.<\/p>\n<p>Say we some links at the top of our site or application:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/1267069.js?file=lang_switch.html\"><\/script><\/p>\n<p>We can hook userSelected to these links as follows:<\/p>\n<p><script src=\"https:\/\/gist.github.com\/1267069.js?file=user_selected.js\"><\/script><\/p>\n<p>Now when a user clicks any of those links, the language will be overridden and set to the user&#8217;s selected choice. The <a href=\"https:\/\/github.com\/ossreleasefeed\/i18njs\">code and demos are available on Github<\/a>, a <a href=\"http:\/\/i18njs.espressive.org\/\">live demo is available on the project page<\/a> and I would love to hear your feedback. On a side note, you are more than welcome to use <a href=\"http:\/\/i18njs.espressive.org\/get-lang\">http:\/\/i18njs.espressive.org\/get-lang<\/a> if you do not feel like implementing the server side or are in a scenario where there is no real back-end code.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Couple of weeks ago I spoke to a friend over at Mozilla about internationalization in JavaScript. This is an area that&#8217;s lacking in JavaScript and during this discussion I got the idea of implementing a solution based on the methods &hellip; <a class=\"go\" href=\"https:\/\/blog.mozilla.org\/webdev\/2011\/10\/06\/i18njs-internationalize-your-javascript-with-a-little-help-from-json-and-the-server\/\">Continue reading<\/a><\/p>\n","protected":false},"author":367,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4713],"tags":[4723,530,4725,669,4727],"coauthors":[],"yoast_head":"<!-- This site is optimized with the Yoast SEO plugin v22.5 - https:\/\/yoast.com\/wordpress\/plugins\/seo\/ -->\n<title>i18njs : Internationalize Your JavaScript With A Little Help From JSON And The Server - Mozilla Web Development<\/title>\n<meta name=\"robots\" content=\"index, follow, max-snippet:-1, max-image-preview:large, max-video-preview:-1\" \/>\n<link rel=\"canonical\" href=\"https:\/\/blog.mozilla.org\/webdev\/2011\/10\/06\/i18njs-internationalize-your-javascript-with-a-little-help-from-json-and-the-server\/\" \/>\n<meta name=\"twitter:label1\" content=\"Written by\" \/>\n\t<meta name=\"twitter:data1\" content=\"Schalk Neethling\" \/>\n\t<meta name=\"twitter:label2\" content=\"Est. reading time\" \/>\n\t<meta name=\"twitter:data2\" content=\"5 minutes\" \/>\n<script type=\"application\/ld+json\" class=\"yoast-schema-graph\">{\"@context\":\"https:\/\/schema.org\",\"@graph\":[{\"@type\":\"WebPage\",\"@id\":\"https:\/\/blog.mozilla.org\/webdev\/2011\/10\/06\/i18njs-internationalize-your-javascript-with-a-little-help-from-json-and-the-server\/\",\"url\":\"https:\/\/blog.mozilla.org\/webdev\/2011\/10\/06\/i18njs-internationalize-your-javascript-with-a-little-help-from-json-and-the-server\/\",\"name\":\"i18njs : Internationalize Your JavaScript With A Little Help From JSON And The Server - Mozilla Web Development\",\"isPartOf\":{\"@id\":\"https:\/\/blog.mozilla.org\/webdev\/#website\"},\"datePublished\":\"2011-10-06T10:35:08+00:00\",\"dateModified\":\"2011-10-06T10:35:08+00:00\",\"author\":{\"@id\":\"https:\/\/blog.mozilla.org\/webdev\/#\/schema\/person\/3d0bd64dc30f1943357f9b0cd6014bc0\"},\"breadcrumb\":{\"@id\":\"https:\/\/blog.mozilla.org\/webdev\/2011\/10\/06\/i18njs-internationalize-your-javascript-with-a-little-help-from-json-and-the-server\/#breadcrumb\"},\"inLanguage\":\"en-US\",\"potentialAction\":[{\"@type\":\"ReadAction\",\"target\":[\"https:\/\/blog.mozilla.org\/webdev\/2011\/10\/06\/i18njs-internationalize-your-javascript-with-a-little-help-from-json-and-the-server\/\"]}]},{\"@type\":\"BreadcrumbList\",\"@id\":\"https:\/\/blog.mozilla.org\/webdev\/2011\/10\/06\/i18njs-internationalize-your-javascript-with-a-little-help-from-json-and-the-server\/#breadcrumb\",\"itemListElement\":[{\"@type\":\"ListItem\",\"position\":1,\"name\":\"Home\",\"item\":\"https:\/\/blog.mozilla.org\/webdev\/\"},{\"@type\":\"ListItem\",\"position\":2,\"name\":\"i18njs : Internationalize Your JavaScript With A Little Help From JSON And The Server\"}]},{\"@type\":\"WebSite\",\"@id\":\"https:\/\/blog.mozilla.org\/webdev\/#website\",\"url\":\"https:\/\/blog.mozilla.org\/webdev\/\",\"name\":\"Mozilla Web Development\",\"description\":\"For make benefit of glorious tubes\",\"potentialAction\":[{\"@type\":\"SearchAction\",\"target\":{\"@type\":\"EntryPoint\",\"urlTemplate\":\"https:\/\/blog.mozilla.org\/webdev\/?s={search_term_string}\"},\"query-input\":\"required name=search_term_string\"}],\"inLanguage\":\"en-US\"},{\"@type\":\"Person\",\"@id\":\"https:\/\/blog.mozilla.org\/webdev\/#\/schema\/person\/3d0bd64dc30f1943357f9b0cd6014bc0\",\"name\":\"Schalk Neethling\",\"image\":{\"@type\":\"ImageObject\",\"inLanguage\":\"en-US\",\"@id\":\"https:\/\/blog.mozilla.org\/webdev\/#\/schema\/person\/image\/e7109a0830ee7cf91572187845b4bfb6\",\"url\":\"https:\/\/secure.gravatar.com\/avatar\/3f0e9b6231df27e285c4589d21c4ae46?s=96&d=mm&r=g\",\"contentUrl\":\"https:\/\/secure.gravatar.com\/avatar\/3f0e9b6231df27e285c4589d21c4ae46?s=96&d=mm&r=g\",\"caption\":\"Schalk Neethling\"},\"description\":\"I am a Mozillian, an evangelist, writer and developer with a passion for open source, web standards and accessibility. I have been so involved with these worlds that I feel they have become a part of me and cannot foresee a future where these topics will not be a part of my daily life.\",\"sameAs\":[\"http:\/\/schalkneethling.com\",\"https:\/\/x.com\/schalkneethling\"],\"url\":\"https:\/\/blog.mozilla.org\/webdev\/author\/sneethlingmozilla-com\/\"}]}<\/script>\n<!-- \/ Yoast SEO plugin. -->","yoast_head_json":{"title":"i18njs : Internationalize Your JavaScript With A Little Help From JSON And The Server - Mozilla Web Development","robots":{"index":"index","follow":"follow","max-snippet":"max-snippet:-1","max-image-preview":"max-image-preview:large","max-video-preview":"max-video-preview:-1"},"canonical":"https:\/\/blog.mozilla.org\/webdev\/2011\/10\/06\/i18njs-internationalize-your-javascript-with-a-little-help-from-json-and-the-server\/","twitter_misc":{"Written by":"Schalk Neethling","Est. reading time":"5 minutes"},"schema":{"@context":"https:\/\/schema.org","@graph":[{"@type":"WebPage","@id":"https:\/\/blog.mozilla.org\/webdev\/2011\/10\/06\/i18njs-internationalize-your-javascript-with-a-little-help-from-json-and-the-server\/","url":"https:\/\/blog.mozilla.org\/webdev\/2011\/10\/06\/i18njs-internationalize-your-javascript-with-a-little-help-from-json-and-the-server\/","name":"i18njs : Internationalize Your JavaScript With A Little Help From JSON And The Server - Mozilla Web Development","isPartOf":{"@id":"https:\/\/blog.mozilla.org\/webdev\/#website"},"datePublished":"2011-10-06T10:35:08+00:00","dateModified":"2011-10-06T10:35:08+00:00","author":{"@id":"https:\/\/blog.mozilla.org\/webdev\/#\/schema\/person\/3d0bd64dc30f1943357f9b0cd6014bc0"},"breadcrumb":{"@id":"https:\/\/blog.mozilla.org\/webdev\/2011\/10\/06\/i18njs-internationalize-your-javascript-with-a-little-help-from-json-and-the-server\/#breadcrumb"},"inLanguage":"en-US","potentialAction":[{"@type":"ReadAction","target":["https:\/\/blog.mozilla.org\/webdev\/2011\/10\/06\/i18njs-internationalize-your-javascript-with-a-little-help-from-json-and-the-server\/"]}]},{"@type":"BreadcrumbList","@id":"https:\/\/blog.mozilla.org\/webdev\/2011\/10\/06\/i18njs-internationalize-your-javascript-with-a-little-help-from-json-and-the-server\/#breadcrumb","itemListElement":[{"@type":"ListItem","position":1,"name":"Home","item":"https:\/\/blog.mozilla.org\/webdev\/"},{"@type":"ListItem","position":2,"name":"i18njs : Internationalize Your JavaScript With A Little Help From JSON And The Server"}]},{"@type":"WebSite","@id":"https:\/\/blog.mozilla.org\/webdev\/#website","url":"https:\/\/blog.mozilla.org\/webdev\/","name":"Mozilla Web Development","description":"For make benefit of glorious tubes","potentialAction":[{"@type":"SearchAction","target":{"@type":"EntryPoint","urlTemplate":"https:\/\/blog.mozilla.org\/webdev\/?s={search_term_string}"},"query-input":"required name=search_term_string"}],"inLanguage":"en-US"},{"@type":"Person","@id":"https:\/\/blog.mozilla.org\/webdev\/#\/schema\/person\/3d0bd64dc30f1943357f9b0cd6014bc0","name":"Schalk Neethling","image":{"@type":"ImageObject","inLanguage":"en-US","@id":"https:\/\/blog.mozilla.org\/webdev\/#\/schema\/person\/image\/e7109a0830ee7cf91572187845b4bfb6","url":"https:\/\/secure.gravatar.com\/avatar\/3f0e9b6231df27e285c4589d21c4ae46?s=96&d=mm&r=g","contentUrl":"https:\/\/secure.gravatar.com\/avatar\/3f0e9b6231df27e285c4589d21c4ae46?s=96&d=mm&r=g","caption":"Schalk Neethling"},"description":"I am a Mozillian, an evangelist, writer and developer with a passion for open source, web standards and accessibility. I have been so involved with these worlds that I feel they have become a part of me and cannot foresee a future where these topics will not be a part of my daily life.","sameAs":["http:\/\/schalkneethling.com","https:\/\/x.com\/schalkneethling"],"url":"https:\/\/blog.mozilla.org\/webdev\/author\/sneethlingmozilla-com\/"}]}},"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/blog.mozilla.org\/webdev\/wp-json\/wp\/v2\/posts\/2098"}],"collection":[{"href":"https:\/\/blog.mozilla.org\/webdev\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/blog.mozilla.org\/webdev\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/webdev\/wp-json\/wp\/v2\/users\/367"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.mozilla.org\/webdev\/wp-json\/wp\/v2\/comments?post=2098"}],"version-history":[{"count":0,"href":"https:\/\/blog.mozilla.org\/webdev\/wp-json\/wp\/v2\/posts\/2098\/revisions"}],"wp:attachment":[{"href":"https:\/\/blog.mozilla.org\/webdev\/wp-json\/wp\/v2\/media?parent=2098"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.mozilla.org\/webdev\/wp-json\/wp\/v2\/categories?post=2098"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.mozilla.org\/webdev\/wp-json\/wp\/v2\/tags?post=2098"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/blog.mozilla.org\/webdev\/wp-json\/wp\/v2\/coauthors?post=2098"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}