Translated Strings and Foreign Language Support in JavaScript Web Apps

Translated Strings and Foreign Language Support in JavaScript

Translated Strings and Foreign Language Support

Foreign language support in JavaScript single-page web apps is hard. JavaScript has no built-in support for translated string tables. What is so important about string tables?

String tables are important because they are the primary way of abstracting text string resources away from programming logic. Decoupling that connection is crucial when crafting a web app that knows how to speak in many languages.

Translation strings and foreign language support seems to be one of those gritty boring subjects that no one cares to write about and plenty of us are stuck implementing. Searching through Google a few months back earned me no useful hits on this topic. Rolling my own scheme made me think I had to have a go at writing this article sharing my success. I hope this helps you when you need a practical reference.

GitHub Repo Demo Project

No need for me to hold back on you. Here’s a link to the GitHub repo I use to demo the techniques discussed in this article. Please feel free to pull it down, read it, run it, and remix it as you like.

https://github.com/KDawg/TStringJS

Live Demo Site

One more thing. As a convenience to you I’ve put up a live demo site of the above repo web app. Toy with it playing the technique written about in this article. Nothing like a hand’s on example I figure.

http://www.tstringjs.com

Architectural Plan

Most apps of a significant popularity, or even modest ones providing value in the enterprise, need to implement translated text. Why? Given that capability they support the largest possible audience in today’s connected world. I think it’s always great when coders create an opportunity for expanding the business.

Not only that, but having a one-stop-shop for all the text strings makes maintenance a dream. Scattering text-string literals all over your codebase is a nightmare for updates and consistency.

Considering translated text as a feature from the start of building our apps makes a positive productivity difference. Retrofitting is always possible, but never easy.

A few simple and reliable tenets for web apps supporting foreign languages:

  • Never hard-code text strings
  • String-tables must be easily accessed throughout the codebase
  • Language definition files must be demand-loadable resources

Making the Case

How do we justify taking the time to do something professional and proper from the start? Wow, did I really just write that sentence? Of course having a string table defining all text displayed to the user has lots of cost saving benefits. Can you justify this even if you are in the tough spot retrofitting a current project? Yes you can so do it already.

Retrofitting, by the way, took this programmer a week on his recent project, and the entire team is happy that I suffered through it. Here is my take-away when I did the retrofit:

  • We can support many foreign languages and even a single region has many (e.g. North America: US English, Spanish, and French-Canadian speakers)
  • Duplicated strings – collapsing many into one shaves bytes off a download and that always helps on mobile
  • Almost duplicated strings – two programmers implemented the exact same response to our user in two different features because one asked his business-analyst and one asked the project manager a few months between implementations
  • Wordsmithing – a single data file with all text makes it easy for a technical writer to perform a final pass for polish and consistent messaging before pushing to prod
  • Translation service convenience – a single data file means our business unit actually has something an external company can concentrate on totally decoupled from code
  • Date/time formatting strings – don’t forget these are regional

String Table Appearance

Keep it simple and clean. No need to get clever in this case since JavaScript provides a perfectly reasonable data structure for defining a string table.

  • Fundamentally a plain old object that’s well-known
  • Key-name properties consistently shared across all language files
  • Name the properties with dot CamelCase attribute style (.selectedTitle)
  • Don’t use bracket/string-literal property style (“SelectedTitle”) even if it seems useful at first
  • Dot CamelCase property identifiers are a productivity win from a proper IDE (love me some PHPStorm) offering “find usages” or “click to definition” from code references

Example of the en-US.js language file:

TS.tstring = {
   appTitle: 'TString.JS',
   appSubTitle: 'Example of translated text strings and foreign language support',
   blogLink: 'Read the technical article on my blog',
   programListing: 'Program Listing',
   english: 'English',
   french: 'French',
   spanish: 'Spanish',
   italian: 'Italian',
   german: 'German',
   select: 'Select',
   title: 'Title',
   date: 'Date',
   channel: 'Channel',
   synopsis: 'Synopsis',
   selectedProgram: 'You selected the program titled',
   programDate: 'MMM Do YYYY, h:mm a'
};

Global String Table

In the accompanying demo project you’ll see all the code is tucked away into a  namespace pattern. One of the namespace elements is called TS.tstring and that’s assigned to the currently loaded string-table. Remember that’s just a plain old JavaScript object. There’s no magic there.

You’ll see my demo loads the English data file by default. An ambitious reader will change her production web app to preload the language file appropriate for the current user based on individual persistent settings.

Typical Code References

When coding up a dynamic, and non-templated, response to the user simply reference an attribute of the TS.tstring data object. Something like this:

alert(TS.tstring.selectedProgram + ': ' + userModel.getDisplayTitle());

Template Code References

You’re web app is probably displaying lists of well-formed data retrieved from a service. Additionally you’re probably using a template library such as Handlebars. In the accompanying demo project I leverage Underscore templates since it’s solid and already a dependency for Backbone upon which the app is based.

Here’s an example of a view’s template using the translated string table:

template: '<h1><%= TS.tstring.appTitle %></h1><h2><%= TS.tstring.appSubTitle %></h2>' ,

This works because the Underscore template compiles to JavaScript code executing within the app’s global memory context. From there it can access the translated strings table. It’s beautiful and simple!

Building this in Handlerbars I would have written and registered a helper function that refers to the TS.tstring data. That too is simple.

Retrieving the Language File

One of the architectural goals/requirements listed above is an ability to demand-load a translated string-table during run-time. Why? Of course the user’s browser will report a preferred language through the “Accept-Language” attribute for potential service initialization when the web app page loads.

In general I believe flexibility on the client-side is best since we’re going to the trouble of making a single-page interactive experience. Allowing our users to change their language on the fly is an extra valuable option in my opinion.

Here’s a simple UX flow that the demo app puts to action:

  • The App offers a language setting somewhere
  • Key off the selected option with a jQuery handler deciding which string table .js file to load
  • Perform a resource load with $.ajax() taking advantage of its datatype = ‘script’ for auto evaluation
  • On .success() the string table .js file is parsed and it’s expected that the global TS.tstring data object is updated
  • On .success() trigger a re-render of whatever part of the app is presenting
  • Subsequent app rendering will naturally reference the TS.tstring object never suspecting it was changed beneath it – hurrah for decoupling!

Here’s an example strings file retrieval from the demo app:

$.ajax({
   url: 'code/strings/en-US.js' ,
   dataType: 'script',
   success: function(data, textStatus, jqXHR) {
      TS.app.onRender();
   }
});

Demo App Tech Stack

Quickly reviewing the demo’s tech stack shows its key support libraries. Not only that, this is a decent skeleton app for your R&D when building single-page web apps.

  • Backbone – offers a structured model-view-controller architecture for the app
  • UnderScore – required by Backbone, but its template feature is used displaying the response listing, and its collection iterators are terrific
  • JQuery – Supports Backbone for collection service requests, view events, ad various DOM handlers/manipulation
  • Moment – Data and time parsing and rendering
  • BBC API – using their open and live API as a concrete example for displaying service information

Apologies

Apologies if any of my language files contains crazy translations. If they do please let me know as I simply passed the English through Google Translate to quickly gain realistic-looking test data.

Have a Think on This

Now, with this fantastic technique in your professional toolbox, when the business team comes up to you with a week before launch saying, “Hey, this North American release needs French for Quebec”, simply email the English language file over to them. Otherwise you’d be up all night cutting-and-pasting hundreds of text-strings from dozens of source code files quickly going insane.

Instead of that grim scenario, as your business team’s translation service of choice churns through the strings table, sit back, have a coffee, and enjoy life.

Let’s do something awesome today!