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!!!
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.
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.
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.
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
These localized templates will automatically get recompiled under the following circumstances:
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.
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.barracudamvc.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.
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.
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.
XMLObject page = (XMLObject) DefaultDOMLoader.getGlobalInstance().getDOM(HelloWorld4HTML.class, locale);
templateComp.addModel(new
HelloWorldModel());
ResourceBundle rb = ResourceBundle.getBundle(
"org.barracudamvc.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?
There are several other minor points worth noting.
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.