Localizing Sencha Touch Applications for the Cheese Eating Surrender Monkeys

Ever since I implemented Voice of America’s content managed web site (b. 1999, d. 2005) and had to support 44 different languages, I’ve had a rather strange interest in developing multilingual apps. I consider it to be ‘strange’ because no one could have predicted that the guy who once got a D+ in junior high French class would go on to accumulate a ton of experience in successful enterprise application localization. As you can tell from the title of this post, I’m not bitter about the grade that I received 30 years ago …not bitter…at…all. For as I told my teacher, “I already speak three languages: Basic, Pascal, and English. French seems rather superfluous.”

As an adult, I’ve repeatedly told my kids “You can learn any foreign language that you’d like, as long as it’s Java, C++, or Javascript. Just stay the hell away from COBOL – for it’s the modern day equivalent of Latin.” They stare at me blankly, quickly turning back to playing Minecraft.

But.. I digress.

Since my consulting practice is based in Washington, DC, I generally don’t get a lot of opportunity to use my i10N-fu…but when an opportunity comes around, I like to dig deep.

To support latin-based, left-to-right languages (English, Spanish, French, Italian, etc), there are four major concerns:

  1. All text prompts need to be translated into language
  2. Enough space needs to be allocated in order to accomodate variable string lengths in word spelling
  3. Text translations should be encapsulated into separate, easy to edit files so that translations can be easily outsourced to human beings (Google Translate just doesn’t cut it…yet)
  4. Dates and numbers need to be translated into the locale. The French, for example, swap their commas and periods which I’m fairly confident was a central cause of the European banking crisis.

Dynamically Assigning Text Prompts

As the code listing below illustrates, all on-screen prompts are implemented as properties of the View class. Prompts are assigned at runtime in the initialize() method of the view.

Ext.define('MyApp.view.Login', {
	extend: 'Ext.form.Panel',
	alias: 'widget.loginform',
	requires: [
		'Ext.field.Email',
		'Ext.field.Password',
		'Ext.Button'],

	txtEmailPrompt: 'Email:',
	txtPasswordPrompt: 'Password',
	btnSubmitPrompt: 'Submit',
	headerHtml: 'Heading Info',
	footerHtml: 'Footing Html',
	loginErrorMsg: 'Sorry, try again',

	config: {
		scrollable: null
	},

	initialize: function() {

		this.setItems(
		[{
			xtype: 'component',
			itemId: 'header',
			html: this.headerHtml
		}, {
			xtype: 'emailfield',
			name: 'email',
			label: this.txtEmailPrompt,
			labelWidth: '40%'
		}, {
			xtype: 'passwordfield',
			name: 'password',
			label: this.txtPasswordPrompt,
			labelWidth: '40%'
		}, {
			xtype: 'button',
			text: this.btnSubmitPrompt
		}, {
			xtype: 'component',
			itemId: 'footer',
			html: this.footerHtml
		}]);
		this.callParent(arguments);
	}
});

Localizing Text Prompts and Applying Translations to Views

The next step is to encapsulate the translations into a class that can be easily edited. In Ext JS 4, the standard is to place translations in separate files that are read in at runtime. However, since I’m dealing with a mobile app, I wanted to minimize the http overhead and decided to place all the translations into a singleton that is packaged along with the rest of the app.

Note the following:

  • Each language is represented by a two-character language code
  • Translations are grouped by the view in which they’ve been defined
  • The localize() method applies the translations to each of the view classes for which a translation is available.
Ext.define('MyApp.config.Locale', {
 singleton: true,
 config: {
  en: {
   Login: {
    txtEmailPrompt: 'Email:',
    txtPasswordPrompt: 'Password',
    btnSubmitPrompt: 'Submit',
    headerHtml: 'Heading in English',
    footerHtml: 'Footer in English',
    loginErrorMsg: 'Sorry, try again'
   }
  },
  fr: {
   Login: {
    txtEmailPrompt: 'Votre email:',
    txtPasswordPrompt: 'Votre mot de passe',
    btnSubmitPrompt: 'Transmettre',
    headerHtml: 'Heading en Francais',
    footerHtml: 'Footer en Francais',
    loginErrorMsg: 'Merde. Où sont les toilettes?'
   }
  }
},
localize: function(locale) {
 var translations = this.config[locale];
 for (var view in translations) {
   Ext.apply(MyApp.view[view].prototype,translations[view]);
 }
}
});

Using this technique, I’m able to easily apply the translations just prior to launching the main view of the app:

Ext.application({
 name: 'MyApp',
 requires: [
  'Ext.MessageBox',
  'MyApp.config.Locale'
 ],
 views: ['Main'],
 launch: function() {
      MyApp.config.Locale.localize('fr');
      Ext.Viewport.add(Ext.create('MyApp.view.Main'));
 }
});

dateselectorLocalizing Dates

Unfortunately, the aforementioned solution is still a “half-measure” in that we still need to localize the built-in Sencha Touch controls, such as the date selector. In order to do this, you’ll need to override the Ext.Date class (which loads the Ext.DateExtras class as a mixin). Fortunately, the Ext.Date/Ext.DateExtras classes in Sencha Touch closely parallel Ext JS 4, so you can repurpose Ext JS 4 locale files as illustrated below:

Ext.define('MyApp.config.Locale', {
 singleton: true,
 config: {
  en: {
   LoginView: {
    txtEmailPrompt: 'Email:',
    txtPasswordPrompt: 'Password',
    btnSubmitPrompt: 'Submit',
    headerHtml: 'Heading in English',
    footerHtml: 'Footer in English',
    loginErrorMsg: 'Sorry, try again'
   }
  },
  fr: {
   LoginView: {
    txtEmailPrompt: 'Le Email:',
    txtPasswordPrompt: 'Password',
    btnSubmitPrompt: 'Submit',
    headerHtml: 'Heading En Francais',
    footerHtml: 'Footer En Francais',
    loginErrorMsg: 'Sorry, try again'
   }
  }
 },

 fr: function() {
    // date localization borrowed from Ext JS 4 fr locale

   if (Ext.Date) {
    Ext.Date.shortMonthNames = ["Janv", "Févr", "Mars", "Avr", "Mai", "Juin", "Juil", "Août", "Sept", "Oct", "Nov", "Déc"];

    Ext.Date.getShortMonthName = function(month) {
	return Ext.Date.shortMonthNames[month];
    };

    Ext.Date.monthNames = ["Janvier", "Février", "Mars", "Avril", "Mai", "Juin", "Juillet", "Août", "Septembre", "Octobre", "Novembre", "Décembre"];

    Ext.Date.monthNumbers = {
     "Janvier": 0,
     "Janv": 0,
     "Février": 1,
     "Févr": 1,
     "Mars": 2,
     "Mars": 2,
     "Avril": 3,
     "Avr": 3,
     "Mai": 4,
     "Juin": 5,
     "Juillet": 6,
     "Juil": 6,
     "Août": 7,
     "Septembre": 8, 
     "Sept": 8,
     "Octobre": 9,
     "Oct": 9,
     "Novembre": 10,
     "Nov": 10,
     "Décembre": 11,
     "Déc": 11
    };

    Ext.Date.getMonthNumber = function(name) {
	return Ext.Date.monthNumbers[Ext.util.Format.capitalize(name)];
    };

   Ext.Date.dayNames = ["Dimanche", "Lundi", "Mardi", "Mercredi", "Jeudi", "Vendredi", "Samedi"];

   Ext.Date.getShortDayName = function(day) {
    return Ext.Date.dayNames[day].substring(0, 3);
   };

   Ext.Date.parseCodes.S.s = "(?:er)";

   Ext.override(Date, {
    getSuffix: function() {
     return (this.getDate() == 1) ? "er" : "";
    }
   });
 }
},

en: function() {
  // reset all dates to english here
},


localize: function(locale) {
  var translations = this.config[locale];
  for (var view in translations) {
    Ext.apply(MyApp.view[view].prototype, translations[view]);
  }
  this[locale]();
 }
});

So, as my old french teachers would have said…”C’est Bon!”..right after giving my latest effort a C-.

9 thoughts on “Localizing Sencha Touch Applications for the Cheese Eating Surrender Monkeys

  1. your old french teacher

    Need help?

    Email / Votre email / Le Email
    -> “Email” is OK in France (French-speaking inhabitants of Quebec certainly prefer “Courriel”)

    Password / Votre mot de passe / Password
    -> “Mot de passe” is generally used.

    Submit / Transmettre / Submit
    -> “Envoyer” is generally used.

    Headind in English / Heading en Francais
    -> “Entête en Français”

    Footer en Français
    -> “Pied de page en Français” or “Bas de page en Français”

    Sorry, try again / Merde. Où sont les toillettes ?
    -> This one is correct actually 🙂 but you can also use : “Désolé, essayez ecnore”

    My 2 cents: http://lamscommunity.org/i18n/
    Sources: http://edutechwiki.unige.ch/en/Software_localization

    PS (unrelated): Thank you a lot for the post on “jQuery Mobile vs. Sencha Touch vs. Appcelerator Titanium!” + video

    Reply
  2. your old french teacher

    “Désolé, essayez ecnore” -> “Désolé, essayez encore”
    I reversed the C and N in “Encore”

    Reply
  3. gabyokal

    Hi!, I have some problems, I tried to implement the first option of the post, but, like this
    … titleBarHome: ‘Bienvenidos a Sencha Touch 2’,
    config: {
    items: [
    {
    title: this.titleBarHome,
    iconCls: ‘home’…

    But not show the message, I don’t know what happen, help me please 😦

    Reply
  4. Abid

    how to change language on click, for example there are two links in top right, user can switch language anytime from one to other….

    Reply

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s