12

I`m working on a project using nodeJs, handlebars and expressJs framework. I add change language functionality using i18n-express module.This module add query string in the end of url when we are going to change the language. Now the issue is that when i have move one page to another page then query string is removed and lose his state.so how can i maintain state of language?? if user choose french language then all pages are open in french. This is what i want.

Code:

var i18n =  require("i18n-express");

app.use(i18n({
  translationsPath: path.join(__dirname, 'lang'), // <--- use here. Specify translations files path.
  siteLangs: ["ar","en","cn","fr","ge","he","hu","it","ja","ko","es","ru"],
  cookieLangName : 'ulang',
  textsVarName: 'translation'  
}));

Link to change the language

<a href="#!" id="{{icon}}" onclick=" return changeLanguage(this)"></a>

Onclick function to change the language

function changeLanguage(event){
   $('#languages img').attr('src','/images/flag-icons/'+event.id+'.png');
   var url = window.location.href;
   url = url.split("?")[0];
   url += '?clang='+event.id;
   window.location.href = url;
   localStorage.setItem("clang", '?clang='+event.id); //event.id returns locale name such as en, ar, sp, fr etc.
   //console.log(url);
}
Varinder Sohal
  • 1,142
  • 5
  • 24
  • 44
  • Can you create a minimal example of the "double refresh" issue you were experiencing (From the below solution), in a [codesandbox](https://codesandbox.io/)? – Blue Oct 15 '18 at 06:44

4 Answers4

5

I recommend you Lingua for ExpressJS

Basically, Lingua is a middleware for the Express.js and that helps you to internationalise your webapp easily. It determines the language of the user agent and pushes the i18n resources to your views.

$ npm install -s lingua

var express = require('express'),
    lingua  = require('lingua');

// Express app configuration code and lingua init.
app.configure(function() {

app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');

// Lingua configuration
app.use(lingua(app, {
    defaultLocale: 'en',
    path: __dirname + '/i18n'
}));

app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.static(__dirname + '/public'));
app.use(app.router);

Language files

'./i18n/en.json' and './i18n/de-de.json').

// en.json
    {
        "title": "Hello World",
        "content": {
            "description": "A little description."
        }
    }

// de-de.json
    {
        "title": "Hallo Welt",
        "content": {
            "description": "Eine kleine Beschreibung."
        }
    }

And you can easily implement it on your pages with:

<h1><%= lingua.title %></h1> <!-- out: <h1>Hello World</h1> -->
<p><%= lingua.content.description %></h1> <!-- out: <p>A little description.</p> -->
Benjamin RD
  • 11,516
  • 14
  • 87
  • 157
1

On clientside if you are able to set an item on local storage then you are also able to get the same item and use its value to push it to the querystring as well. So you basically need an additional function on your client javascript that will get the item everytime the page opens.

    function getParameterByName(name, url) {
        if (!url) url = window.location.href;
        name = name.replace(/[\[\]]/g, '\\$&');
        var regex = new RegExp('[?&]' + name + '(=([^&#]*)|&|#|$)'),
            results = regex.exec(url);
        if (!results) return null;
        if (!results[2]) return '';
        return decodeURIComponent(results[2].replace(/\+/g, ' '));
    }

    function checkLanguageFromLocalStorage(){
        var clang = getParameterByName('clang');
        if (clang == null) {
            if (localStorage.getItem("clang") != null) {
                var clang = localStorage.getItem("clang");
                var url = window.location.href;
                url = url.split("?")[0];
                url += '?clang='+clang;
                window.location.href = url;
            }
        }
    }

    checkLanguageFromLocalStorage();
Gianpaolo Papa
  • 458
  • 4
  • 9
  • I have try this but it loads page two times. any idea about this? – Varinder Sohal Oct 11 '18 at 08:47
  • Based on this answer [link](https://stackoverflow.com/questions/901115/how-can-i-get-query-string-values-in-javascript) you can add an additional check on your control to get if "clang" is already set and already pushed on the querystring, so to not refresh the page if already set up. I changed the answer with this check. – Gianpaolo Papa Oct 15 '18 at 00:32
1

Why don't you pass a cookie with the language instead of query parameter? i18n-express has an option called cookieLangName which you have allready configured on the server side (cookieLangName: 'ulang'). Setting cookieLangName makes i18n-express read language from the cookie with the name you pass. All you need is to set this cookie in you client side script - inside changeLanguage function - and it will do the trick:

function changeLanguage(event){
   $('#languages img').attr('src','/images/flag-icons/'+event.id+'.png');
   document.cookie = `ulang=${event.id}; path=/`;

   localStorage.setItem("ulang", `ulang=${event.id}; path=/`); // you can still save it to localStorage and synchronize it when cookie is removed
}
Volodymyr
  • 1,360
  • 1
  • 7
  • 12
1

The requirement, I understand boils down to this:

If an end-user had selected a language, the selection should always be transparent in the URL in order to enable proper functioning of the translation module. Meeting this requirement should not cause an unnecessary page reload. However, the scope of this question ignores the fact that on a cold-start where the clang=xx does not exist, a manual redirect would be necessary to load the correct content since previous user selection is only known to the client.

You're almost there, based on the snippet you posted. Function changeLanguage correctly loads the content in the selected language. If I were doing a code review on it though, I would request a few changes to the following outcome:

Patched HTML

<a href="#!" id="{{icon}}" onclick="return loadContentInSelectedLanguage(this.id)"></a>

Patched JS:

function processLanguageSelection(selectedLang) {

  highlightCurrentlySelectedLang(selectedLang);
  saveSelectedLangAcrossSession("clang", selectedLang);

  reloadContentInSelectedLang(selectedLang);
}

function highlightCurrentlySelectedLanguage(selectedLang) {
  $('#languages img').attr('src', '/images/flag-icons/' + selectedLang + '.png');
}

function saveSelectedLangAcrossSession(entryKey, selectedLang) {
  window.localStorage.setItem(entryKey, selectedLang);
}

function reloadContentInSelectedLang(selectedLang) {
  var search = window.location.search || "?";

  if (/clang=\w{2}/.test(search)) {
    search = search.replace(/clang=\w{2}/, "clang=" + selectedLang);
  } else {
    search += ("?" === search ? "clang=" : "&clang=") + selectedLang;
  }

  window.history.replaceState(null, "", search);
  window.location.reload();
}

You're almost there, like I said. So, what's left to get the ultimately? -- The missing chip is this: previous user selection should be applied just when the page starts to load. And any necessary redirection should be done as early as possible before the browser starts to process the document body. Naturally, it means the part of your scripts with this responsibility should be the first thing that is run inside the <head>...</head> section.

<head>
  <script>
    (function() {
      var previouslySelectedLanguage = window.localStorage.getItem("clang");

      if (/clang=\w{2}/.test(window.location.search)) {
        // correct content should have been loaded in this case
        // otherwise the server needs fixing, but that's not
        // in the scope of this question.

        // determine selectedLang from window.location.search, then..
        highlightCurrentlySelectedLanguage(selectedLang);

        if (!previouslySelectedLanguage || "selectedLang" !== previouslySelectedLanguage) {
          saveSelectedLangAcrossSession("clang", selectedLang);
        }

        return;
      }

      if (previouslySelectedLanguage) {
        // redirect
        reloadContentInSelectedLang(previouslySelectedLanguage);
      }
    })();

  </script>
</head>

Kindly note that the examples offered in this answer are not production ready. Do exercise due diligence by ensuring they're robust, resilient and meet other standards required in your organisation. For example, this regex, /clang=\w{2}/ makes an audacious assumption that can't always be true.

Igwe Kalu
  • 14,286
  • 2
  • 29
  • 39