HowTo: Build a multi-language dynamic Flex application

One of the projects I worked upon over the summer was destined for the South-American market and therefore needed to display in both Spanish and Portuguese. It was a microsite built in Flex and needed the capacity to alternate between these two languages at any given time. From the user’s perspective, a single button click should be sufficient to translate the entire site’s text from one language to another instantly. Looking around the web I couldn’t find any answers so I sat down and worked out a solution myself.

Here’s a sample app using English and my second language Swedish:

The solution utilises Flex’s databinding. You can databind using either MXML or ActionScript3, and here are examples of both of those. If you don’t know about databinding go read about it elsewhere first because otherwise this article won’t be much help to you:

<!-- My MXML databinding: -->
<mx:Label id="someLabel" text="{model.path.to.some.variable}" />
// My ActionScript3 databinding:
model.bindProperty(someLabel, "text", "path.to.some.variable");

I’ve had to use both of these databinding methods for different multi-language projects so I’ve developed two slightly different methods for achieving this, which I’ll touch on as this article progresses.

Broadly speaking, step-by-step what you have to do is this:

  1. Get/load/etc your language texts
  2. Parse and store them as Objects
  3. Create a single dynamic Object that will serve as your textual datastore
  4. Use databinding to tie that Object to your textfields/labels/etc
  5. Swap the Objects in and out of your single dynamic Object as required

At this point you have a choice. You can either just download the sample FlexBuilder project and just go through the code yourself or you can read on and download it further down the page.

If you’re reading on, lets go through the 5 steps 1 at a time.

1. Getting your language texts

Use a single XML document for each language you need. Here’s a sample ’swedish.xml’ file:

<language id='swedish'>
	<section id='home'>
		<title>SVENSKA</title>
	</section>
	<section id='game'>
		<title>SPEL</title>
		<copy>
			<text id='header'>RUBRIK</text>
			<text id='mainBody'>STOFF</text>
		</copy>
	</section>
	<section id='footer'>
		<title>LÄNKAR</title>
		<button>BYTA SPRÅK</button>
	</section>
</language>

The reason I recommend storing and loading your data like this is because, yeah, XML is the defacto standard for archiving this kind of information and so it’s simply good practice but mainly - if I’m being honest - because it makes step 2 of this particular process so much easier.

2. Parse and store languages as Objects

Your language data comes replete with its own archivable hierarchy thanks to its XML wrapping. Using the nodenames and the ‘id’ attributes you can parse it into a hierarchy of Objects SIMILAR TO but not EXACTLY THE SAME AS the format of the XML document.

Here’s where we encounter our first MXML/ActionScript3 difference. When we store the XML data for MXML binding we need to do so in a hierarchy of AS3 Objects that mirror the structure of the XML data whereas when we store it for ActionScript3 binding we need to create a single AS3 Object which contains a series of unique dot-notated String-based names.

I’ve already written the code for this and instead of talking you through it I suggest simply waiting until we get to the zip download at the bottom of this page where you can just download and play with it. It’s simple enough, there are two classes called ASParser & MXMLParser that both contain the single static method ‘parseLanguage(XML);’ into which you pass your loaded XML document and have it returned as a parsed Object.

// AS3 parsing (for use with ActionScript binding):
var parsedSwedish:Object = ASParser.parseLanguage(swedishXML);
 
// MXML parsing (for use with MXML binding):
var parsedSwedish:Object = MXMLParser.parseLanguage(swedishXML);

The chief difference between the XML formatted data and the Object stored data is the use of ‘id’ attributes. As an XML document frequently features a whole series of nodes with identical names - such as the ’section’ nodes featured in the XML snippet above - if we attempted to store these in an Object we would constantly overwrite the ‘Object.section’ property with the value of the next <section> node and so we need the ‘id’ attribute to act as a mechanism to guarantee against that. When assigning property names to our Objects if we have an ‘id’ attribute to work with we append it to the nodename, ensuring uniqueness amongst our property names. So when we use the above methods to parse the above ’swedish.xml’ we get an Object with the following two String properties:

Object.sectionHome.title
Object.sectionGame.title

Using the MXML method ‘Object.sectionHome.title’ would refer to an Object called ‘Object’ containing a second Object called ’sectionHome’ which would have String property ‘title’ whilst in the ActionScript3 method ‘Object.sectionHome.title’ would refer to a single Object called ‘Object’ containing the String property ’sectionHome.title’.

Your parsed language Object is then stored away in an Array/Dictionary/etc of similar language Objects and we move onto step 3.

3. Create a single dynamic Object that will serve as your textual datastore

This is the magic voodoo bit that had me banging my head against the wall for a few hours. Flash puts its awesome Events model at your disposal Again, like databinding, if you don’t know about AS3’s Event model go find out about it NOW before continuing. I expect you do know so I’ll just carry on.

The trickiness here lies in the paradox inherent in the fact that in order for databinding to work you need to bind to an object that fires events - ie: one that extends EventDispatcher - so that it can tell you about any changes it might undergo. But in order to store dynamic properties on an object - as we did in step 2 - it needs to be a dynamic object and only the Object class in AS3 allows this. EventDispatcher inherits from Object so if we databind to an instance of Object it’s essentially useless because said Object will never be able to tell us that its value changed.

So how can we get around this?

The answer is: with Composition instead of Inheritance. Instead of binding directly to an Object incapable of firing events we bind to an instance of a DispatchingObject class that has an Object as its only property that can act as a mediator and do the job for us. This is the entire code for the DispatchingObject class:

package uk.co.richtextformat.multiLanguage
{
	import flash.events.EventDispatcher;
 
	public class DispatchingObject extends EventDispatcher
	{
		private var _object:Object;
 
		public function DispatchingObject ():void
		{
			_object = new Object();
		}
 
		[Bindable]
		public function get object():Object { return _object; }
		public function set object(object:Object):void
		{
			_object = object;
			dispatchEvent( new Event(Event.CHANGE) );
		}
	}
}

Having imported EventDispatcher all we need do here is make our Object getter [Bindable] and we’re good to go! Thereafter, whenever we replace the DispatchingObject’s ‘object’ property via the setter method an event will be fired and anything binding to the DispatchingObject will be informed.

4. Use databinding to tie that Object to your textual DisplayObjects

That’s the hard work basically all done. All we need do now is bind any textual DisplayObjects to our instance of DispatchingObject. Again, we have to choose between the MXML method and the ActionScript3 method:

<!-- My MXML databinding: -->
<mx:Label id="someLabel" text="{model.language.sectionHome.title}" />
// ActionScript3 databinding:
model.bindProperty(someLabel, "text", "sectionHome.title");

Here an MXML label is binding its ‘text’ property to a property on the the application’s Model called ‘language’. This is the name of our instance of DispatchingObject, or more specifically the following getter method, that returns the DispatchingObject’s ‘object’ property:

private var _language:DispatchingObject;
 
[Bindable]
public function get language ():Object
{
	return _language.object;
}
 
public function set language (language:Object):void
{
	_language.object = language;
}

One thing to know at this point concerns warnings that Flex pumps out to your Output window IF you’re using the MXML method. Whilst the binding to the DispatchingObject clearly DOES work - something easily verifiable because you can actually see the text changing onscreen - somewhere down in the murky depths of the Flex framework some check takes place which determines that in fact your bindings AREN’T working and kicks out a warning like this for each and every binding you have made:

warning: unable to bind to property 'title' on class 'Object'
(class is not an IEventDispatcher)

This doesn’t present any code checking problems (via the ‘Problems’ dialog in FlexBuilder) or compile-time errors, it’s just a bit irritating to see your Output window get filled up with so much rubbish all the time. Perhaps there is a way of disabling this via a project property somewhere (someone do leave a comment to that effect if you know of one) but otherwise the best way I found of dealing with it is simply not to change languages whilst developing the rest of your site’s functionality. That way you’ll get all these traces just once at the start when your textual components first populate with content and never again. Of course, if you’re working on the multi-language stuff you’ll have to put up with it, but… yeah, I guess that’s just an unfortunate side effect.

5. Swap the Objects in and out of your single dynamic Object as required

Once all that is in place to change the language of your ENTIRE Flex app on the fly all you need to do is to grab one of the other language Objects out of your collection and pass it to the ‘language’ setter method documented above. How simple!

The last thing you need to do here is to download the sample FlexBuilder project, run it and go through the code yourself.

Please let me know if this post has been helpful!

Please feel free to use the code i provided both on this page and in the zipped download for free. Just to know where it’s managed to get to though, it would be good if you could just leave a comment or email me to let me know where you’re using it. I don’t wish to spy or ask for any rights or money or anything, it’s just nice to know where this system is being used. :)

Tags: , , , , , ,

7 Responses to “HowTo: Build a multi-language dynamic Flex application”

  1. Alvaro Raminelli Says:

    hi
    I would like to know if you can to send me this app.
    I dont get to do to function this..
    I need urgent to make some like with this..

    Thanks
    wait

  2. rtf Says:

    Hey Alvaro

    The apps i've made using this method are kind of massive and belong (IP-wise) to other organisations anyway, so i'm not really able to email to anyone and everyone. If there's any part of this tutorial that's not too clear just let me know and i'll try to rewrite that bit to make it clearer.

  3. Wubinator Says:

    In the XML method of processing the XML it seems a part of the code is missing:

    if (childID.length() == 1){
    childName = _camelCase( [childName, childID.toString()] );
    }

    what is _camelCase?

  4. rich Says:

    hey wubinator

    you’re right, some of the code had become lost, so i replaced the large section of code in the middle with a zipped download, much safer. thanks for the heads up

  5. Maik Schulze Says:

    Hello Richard,

    thank you very much for your explanations and the code. I’m new to FLEX and have one question / problem though.

    I set up custom validators for my textinputs. The ValidationResults do not update correctly using the AS binding as following:

    var valid:ValidationResult = new ValidationResult(true, null);
    dictionaryAS.bindProperty(valid, “errorMessage”, “register.secretanswer”);

    A workaround would be to validate all respective textinputs on a language change, but I feel this is a dirty way. Do you have any suggestions why this problem occurs and how to fix it?

    Thanks again, for your effort and openness.

    Regards
    Maik

  6. Maik Schulze Says:

    Hello Richard,

    I’ve discovered a solution in the meantime. Instead of using validators,e.g. extending my CustomValidator from Validator, I’m now manually setting up the Tooltips.

    Check out this website:
    http://blog.flexmonkeypatches.com/2007/09/17/using-the-flex-tooltip-manager-to-create-error-tooltips-and-position-them/

    In case, the website goes offline, here’s a short sample code:

    public class ValidatedTextInput extends TextInput
    {
    protected var errorTip:ToolTip;
    public setToolTip():void
    {
    errorTip = ToolTipManager.createToolTip(”",0,0) as ToolTip;
    model.bindProperty(errorTip,”text”,”register.secretanswer”);
    }

    }

    That way, the tooltip text will update on language change

    The downside of this approach is having to manually deal with the events and such. Since, the default behavior of FLEX’ validators isn’t that nice, you might end up altering this anyway.

    Regards
    Maik
    }

  7. Thales Violakis Says:

    Men, can you help me with one little thing? I have a website, with 7 FLA’s externals (7 links) , i need put 2 languages in the website, i am searching in the google and i find your site, its interesting, but think in the flash its different isnt ?
    You know or have a example semelhant?
    Dont know working with XML in AS3 very well, only with as2, and the website i writen in as3, please can you help?

    Thanks

Leave a Reply