Tuesday, March 5, 2013

Localization is important


One of the larger obstacles in using material (books, software, etc.) from outside Latin America in Latin America is that it is usually only provided in English.  Unfortunately, many young students don't speak, read or write anything but Spanish, so it's important to think localization into solutions for this region from the very beginning.

With the technologies I have settled on for the Education Kit, this becomes an easy task for some parts and require a more creative solution for others.

The Client

Because the custom web client is written in pure Qt5 (C++ and QML), localization becomes quite easy here:

  • Wrap all strings to be translated in qsTr("...") (some extra hacks needed to get QML to swap language on the fly)
  • Modify the .pro file to inform which languages we want to translate to
  • Run lupdate to create/update translation files for each selected language
  • Give some nice red wine, chocolate and QtLinguist to the translators - and wait for the result
  • Run lrelease to produce compact binary versions
  • Enjoy! :)

The Server

As we would like the server to keep the settings and generally be able to dispatch any global configuration to remotely connected browsers fetching exercises, the server needs to support a few things:

  • Getting the chosen setting information from the QML client
  • Some way of storing the settings
  • Provide the settings to connected web clients (both the custom client's WebView as well as remote clients, e.g. tablets, phones and more)
As a first shot, to get the information from the QML client to the node.js server, I chose to do a simple XHR from QML:

    function selectLanguage(value) {
        language.selectedLanguage = value;
        var req = new XMLHttpRequest();
        req.open("POST", "http://127.0.0.1:3000/settings/language/"+value, true);
        req.send();
    }


NOTE:  This will probably change to some cleaner direct socket call.

There is a corresponding handler on the server side:

    app.post('/settings/language/:value', edukitapps.setLanguage);


And now store and dispatch the information to all connected clients (using socket.io):

    exports.setLanguage = function(req, res) {
        var language = req.params.value;
        console.log("Setting language to: " + language);
        selectedLanguage = language;
        res.send("Setting language to: " + language);
        if (_io !== 0) {
            _io.sockets.emit("language", {language: selectedLanguage});
        }
    };


The Exercises (WebApps)

All visible texts in the exercises are surrounded by span tags with unique IDs, e.g.:

    <h1><span id="txt_color_mixer"></span></h1> 

When the application loads, it will include a settings.js javascript file that is auto-generated on the server side to include all kinds of user settings (for now - just the language):

    exports.getSettingsJS = function(req, res) {
        var str = "// This is the auto-generated settings from the server\n";
        str += "var _LANGUAGE = '" + selectedLanguage + "';\n";
        res.type("application/javascript");
        res.send(str);
    };


Inside the exercises, the language information can then be used to set the text strings, e.g.:

    function setStrings(language)
    {
        if (language === "es") {
            $("txt_color_mixer").innerHTML = "Mezclador de colores";
        } else {
            $("txt_color_mixer").innerHTML = "Color Mixer";
        }
    }


NOTE:  A more elegant solution is in the works - and it will most probably be based on pure socket.io .

Even if the WebApps are running in a WebView (WebKit2) in the custom built client, there is currently no easy way of for the QML to communicate directly with the content when changes happen as there was in the case of QtWebView (WebKit1) - or at least I haven't found any ;).

Therefore, the sequence of events happening from the user pushing a language selection button to the exercise visually changing the text strings is the following:

  1. The user selects a language in the QML client
  2. QML sends an XHR call to the server
  3. The server notifies all connected WebApps of the change using socket.io 
  4. All connected WebApps, including the one running inside the custom client, gets the notification and can act upon it

It may sound a bit complicated, but the result is instant and the code to handle it is simple on both server and client side.  Here is the required handler code in the WebApp:

    socket.on('language', function (data) {
        setStrings(data.language);
    });

What about the source code?

 This gets a bit more complicated to keep localized as the code should remain readable and functional.  However, it should also be OK to require for future software developers to learn a bit of the technical English they will encounter anyway if they choose to become proper developers.

The comments should be kept in a very clear and simple English - with the possibility for small extra translations in complex places.

NOTE:  This may change though - based on the feedback from future pilot tests in Latin America.

Saturday, March 2, 2013

Working on the UI

Trying to keep the UI as simple and uncluttered as possible,  I have chosen to go with a design, where the running exercise (WebApp) is using the whole screen without any border, etc.

The only visible additions are the 2 (currently) tabs in the bottom of the screen.  One brings up the exercise overview and the other the settings panel.



Exercises are blueprints


The design chosen for the exercises themselves is done as if everything is an animated blueprint.  This was done to 
  1. Make sure that the information in each exercise is always easy to find (e.g. no fancy colors distracting everywhere - unless the exercise is about distracting fancy colors ;)) - and
  2. To keep the design simple for the students to be able to - fairly quickly - come up with their own applications to share, without the need to be experts in Gimp or Photoshop.
The application is made with Qt5/QML with some C++ mixed in, where needed.  This results in a smooth UI experience - even on RaspberryPi*

The panels




When clicking one of the tabs in the bottom, the respective panel appears to reveal a semi-transparent overlay with very simple functionality.

NOTE:  This is NOT the final design - I've left that for my graphics design buddies to help me with.

The exercise panel has three sections:


  1. An exercise class filter (symbols representing chemistry, history, mathematics, etc.) to be used for fast filtering.  E.g. when a chemistry teacher wants to select among the exercises that link to chemistry, a simple click on the filter will remove all other apps from the list.
  2. The actual list of exercises.  This is done as a scrollable (QML ListView) list of exercise applications, where each one has a title, an icon and up to 4 category markers (the same as the filters above)
  3. Deeper information about the app that is selected or hovered with the mouse.

The settings panel currently has a few buttons - but will be extended with more juicy stuff.

One of the more relevant things already working though, is the ability to change language on the fly inside the client:

Selecting "English"

Selecting "EspaƱol"

What's missing?


I'd love to hear your input! - but please be nice... IANAGD!**

Some things I know I want to include:
  • A way for students to do a simple login before performing an exercise (for feedback to the teacher)
  • A way for students to see their results
  • An exercise cloning tool and a simple exercise creator, allowing students to make their own exercises and share them with others, when the device is online.



* NOTE:  Sometimes, WebKit brings the RaspberryPi to it's knees, which is not the fault of QML.  I am working on ironing out the performance issues in the QtWebKit source and the exercises (WebApps) themselves so the final product will run like a charm (hopefully).

** I am not a graphics designer ;)