Barracuda Component Model Tutorial - HelloWorld 4

<<|Preface|HelloWorld 1|1a|1b|2|2a|2b|3|4

barracuda.gif (11456 bytes) Hello Localized World - Once more we look to our familiar grocery list, although this time we find that it has been localized to support several different languages!

Special thanks to Richard Kunze (German), Jonas Thurfors (Swedish), Christophe Ney (French), Timo Sillanpää (Finnish) and Roger C. Soares (Portuguese) for providing the various translations!!!


Localization Options

Ok, in order to appreciate how Barracuda helps with localization, we first need to understand the big picture.

To begin, let's review how XMLC works. First we create an HTML template file (in this case HelloWorld4.html); XMLC finds it during the compile process and creates HelloWorld4HTML.java, which gets compiled as a normal class. The developer typically uses XMLCFactory to get an instance of the HelloWorld4HTML.class. Once we have this we can modify the DOM using XMLC generated methods or Barracuda and then render the DOM and return the page to the client.

So let's say we want to localize HelloWorld4? What are our options? Well, we could put id tags on every piece of text inside the template and then substitute them all at runtime. This will work, but it's not very efficient and requires a lot more effort on the part of the developer.

A second option would be to create localized versions of the actual template and then just load the version needed for the target locale. This is much more efficient (since the templates would all get compiled via XMLC) and much less work for the developer (since he'd only have to substitute in dynamic data). Unfortunately, it's a maintenance nightmare for the designer.


XMLC Localization Taskdef

So what's a bloke to do? Barracuda's solution is to automate the template localization process for you. To do this we use a custom Ant taskdef that extends the Xmlc taskdef to support the additional localization behavior. Here's how it works.

  1. First the designer creates HelloWorld4.html

  2. Then the designer creates a standard Java properties file (ie. HelloWorld4.properties) that contains all the static text in HelloWorld4.html. In our case here it looks something like this:

    HelloWorld4.Title = Hello World 4 example (English)
    HelloWorld4.Descr = This example illustrates how ...
    HelloWorld4.Shopping = Shopping, anyone?
    HelloWorld4.ShoppingListTitle = Localized shopping list
    ...

    Notice that we assign unique property keys (like HelloWorld4.Title) to each text item. This will important for the steps that follow.

  3. Next the designer creates additional properties files for all the locales we desire to support. For instance, if I wish to have a Spanish template, I would create a HelloWorld4_es.properties that looks something like this:

    HelloWorld4.Title = Ejémplo "Hello World 4" (Español)
    HelloWorld4.Descr = Este ejémplo ilustra como ...
    HelloWorld4.Shopping = ¿A las tiendas alguien?
    HelloWorld4.ShoppingListTitle = Localizaron lista de cosas...
    ...

    The key point to notice here is that the ids are identical to the original properties file.

    We should also observe that the file is named according to standard resource bundle naming conventions, which makes use of the _[language]_[country]_[variant] naming conventions that apply to Java Locales, so if I wanted to make aproperties file specifically for the dialect of Spanish used in Chile, I'd name my file HelloWorld4_es_CL.properties

  4. Now for the fun part! When I compile my XMLC classes using the xmlc_localization taskdef, it recognizes the presence of the additional localization property files and automatically creates a localized version of the original template using the values found in the properties file. In this case, I end up with an additional HTML template called HelloWorld4_es.html, which gets compiled by XMLC to HelloWorld4HTML_es.java (which in turns gets compiled by javac to HelloWorld4HTML_es.class)

    These localized templates will automatically get recompiled under the following circumstances:

    1. any time the original HelloWorld4.html template changes
    2. any time the HelloWorld4.properties file changes
    3. any time the HelloWorld4_es.properties file changes

NOTE: It is important to understand how the Localize taskdef matches text in the HTML file with the appropriate entry in the .properties file. You do not need to add any id values to your markup. The Localize taskdef uses direct text substitution. For instance, in HelloWorld4.html there is the line:

<h2>Hello World 4 example (English)</h2>

and in the HelloWorld4.properties there is :

HelloWorld4.Title = Hello World 4 example (English)

and similarly in HelloWorld4_de.properties there is :

HelloWorld4.Title = Beispiel "Hello World 4" (Deutsch)

Localize will find the text listed in the HelloWorld4.properties file, give it an id of HelloWorld4.Title and then substitute the original (english) text with the text identified by the identical id in the current language file. What this means is that it is critical for the text in your master HTML template to match the text in your master .properties file.


Loading Templates by Locale

Ok, so we've automatically created all these localized templates simply by building a master HTML template and then providing property files for specific locales. Now how do we actually use these things?

Again, lets consider our options. A programmer could just access each of these individually, just like any other XMLC generated file. Unfortunately, this would be a lot of work because the developer would have to check the locale, figure out the appropriate template name, etc. Barracuda recognizes this is a pain in the neck so it does it for you.

Here's how it works. If we look in the org.enhydra.barracuda.dom package, we will find a class called DefaultDOMLoader.java. This class provides a mechanism to load a DOM object based on Class name and Locale in much the same manner that a Java Resource Bundle works.

For instance, if I ask the DOMLoader to return a DOM for HelloWorld4HTML.class in the German locale, it will actually return an instance of HelloWorld4HTML_de.class. Similarly, if ask for the Mexican locale (ie. _es_MX) and it doesn't exist, it will return the closet matching template, in this case  HelloWorld4HTML_es.class.


Putting it all together

Ok, now that we have a good understanding of all the individual pieces, let's put it all together by looking at the actual HelloWorld4 servlet source.

  1. The first thing we do is figure out our target Locale by looking at the parameters that accompany the HttpRequest. The code looks something like this:

    Locale locale = vc.getViewCapabilities().getClientLocale();

    Fortunately, the system can do this for us. We get the locale by accessing the ViewCapabilities object and asking it to determine the client locale for us. This class is intelligent enough to identify the language, country, and variant codes that get passed in. If these values are missing, it will see if the locale information has been cached in the session, and if not, it will get the information from the HttpRequest object.

    (Note: this means that when you run the example without any extra parameters, you should see the page come up in your default locale, if it's supported. To test this out, reset you machine to German and visit the page...if it doesn't work, it's probably because the lcoale info is already cached in your session...wait 6 minutes and try it again.)

    If the utility function cannot determine the target client locale by looking at HTTPRequest object, it will default to the default locale for the server.

  2. Now that we've got our target locale, we need to get our DOM object. We can usually do this with one line of code:

    XMLObject page = (XMLObject) DefaultDOMLoader.getGlobalInstance() /
                     .getDOM(HelloWorld4HTML.class, locale);

  3. Now we can go ahead and populate our DOM structure as we normally would (in our example here we're again using the BTemplate). The only real difference here from previous examples is that we need to localize our dynamic data as well. In this case we pull it from the same properties file used to create the localization templates in the first place. The code looks something like this:

    templateComp.addModel(new HelloWorldModel());
    ResourceBundle rb = ResourceBundle.getBundle(
              "org.enhydra.barracuda.tutorials.xmlc.HelloWorld4", locale);
    String[] items = new String[] {
        Localize.getString(rb, "Groceries.Cabbage"),
        Localize.getString(rb, "Groceries.Carrots"),
        Localize.getString(rb, "Groceries.Mustard"),
        Localize.getString(rb, "Groceries.Sugar"),
        Localize.getString(rb, "Groceries.Cereal"),
        Localize.getString(rb, "Groceries.Flour"),
        Localize.getString(rb, "Groceries.Potatoes")
    };
    templateComp.addModel(new GroceriesModel(items));

    Note that we simplified this example so as not to deal with quantities (lbs, kgs, etc) as those would have required additional localization/conversion efforts and we wanted to keep the tutorial simple.

That's really all there is too it! Pretty slick, eh?


Final Notes on Localization

There are several other minor points worth noting.

  1. Property file chaining - The localization process is intelligent enough to look to multiple property files, just like resource bundles do. What this means is that when you create a localized property file, it does not need to contain ALL the text of the original template; it only needs to contain the text that is different. When the template is localized, the text will be merged from the various resource bundles.

    As an example, compare the contents of HelloWorld4_es_CL.properties with HelloWorld4_es.properties. You will see that the Chilean properties file only has entries for text that is different from the standard Spanish dialect (in this case the "¡Viva la Chile!"...my wife's native country ;-) All the rest of the text is derived from the standard Spanish properties file.

    Again, this behavior is just like what you find when working with ResourceBundles. Here it's just as convenient, since it allows us to easily support multiple dialects within a language while only having to maintain the data that is different.

  2. Embedded markup in property files - Hey, guess what! We have recently added support so that localized property files can contain markup. The master property file cannot (because remember, the localization process parses the document using the DOM), but localized versions of the properties file can now contain values with embedded markup. Realistically, you probably won't want to do this a lot, but it's always nice to know it's there if you ever need it.

  3. Multiline properties - Property files do support values that take up several lines. All you need is a \ at the end of your lines. Look at the sample property files for an example.

Click here to return to the Barracuda Component Model Tutorial page.

For all the latest information on Barracuda, please refer to http://barracudamvc.org
Questions, comments, feedback? Let us know...