Document last updated 18 April 2001. We are very interested in feedback that would make these materials better. Feel free to write the author, Jonathan Revusky.

Niggle Internationalization

This directory contains a minimal example of internationalization using Niggle. As was pointed out in the "Hello, Niggle" example, Niggle leverages the open source Freemarker library for its template capability. Though Freemarker is a capable template engine, it was not written with any special dispositions for dealing with the issues involved in web site localization -- e.g. maintaining different versions of a page template for different audiences. Niggle remedies this by providing a higher-level API that wraps the Freemarker and also provides for localization via the canonical java naming scheme.

As usual, we start with the mechanical steps that allow you to get the application running and then we explain the various parts.

The Mechanical Steps

We assume that, at the very least, you have successfully built and run the "Hello, Niggle" example.

I'll get straight to the point. Here are the steps involved in building and running this example:

  1. Compile the java code.
  2. Copy the .class files to $TOMCAT_HOME/webapps/niggletut/WEB-INF/classes
  3. Copy the .properties files and the .nhtml files to the same location above.
  4. Copy the web.xml file to $TOMCAT_HOME/webapps/niggletut/WEB-INF
  5. Start (or restart) your servlet server.
  6. Open the URL that maps to our page template: http://localhost:8080/niggletut/welcome.nhtml

Overview

You have probably already noticed that there are 2 new elements in this example. The first is that we use a web.xml configuration file to set up a mapping between the *.nhtml extension and the I18N servlet. This was not absolutely necessary to demonstrate the functionality, but it does show that things can be set up in such a way as to make this a fairly elegant, transparent solution. The other novel element is that, rather than just having a single welcome.nhtml file, we have localized versions for English, Spanish and French, that follow the canonical Java localization naming scheme. We have also externalized certain strings for those languages in separate .properties files.

The key thing to understand is that the getPage() routine that vends a page template is locale-aware. Every ServletInteraction instance has a Locale associated with it. The template vending mechanism takes this into account, so that if the locale is "es" (Spanish language, no country specified) or "en_GB" (English language, Great Britain) or whatever, the system will try to find the resource that best matches the locale. In the latter case of "en_GB", let's say you invoke:

page = getPage("welcome.nhtml");

Then the system will look for the resource "welcome_en_GB.nhtml" and failing that, will try "welcome_en.nhtml", and if that resource is not found, will go to the most generally specified template: "welcome.nhtml". This pretty much mirrors the canonical Java internationalization scheme.

The deduceLocale() hook

Now, let's take a look at the java code in this example. The deduceLocale() method is a hook that can be overridden by the application programmer to provide the logic by which the application decides what the preferred locale is. If you do not override this method, the locale will simply be taken to be whatever the base servlet API's request.getLocale() method returns.

In order to demonstrate internationalization, our implementation of deduceLocale() simply sets the locale as being France. (Of course, you can play around and experiment with this by changing that line.) Now, obviously, a real-world application would not be so simple-minded. It would set the locale based on user-preferences, perhaps remembered via a cookie. Or it might take into account what the referring site was. If the referring site was yahoo.fr, it would make sense to set a French language locale, whereas if it was lycos.de, it might make more sense to set a German language locale. It might also make sense to try reverse DNS lookup to try to deduce a user's locale. Of course, the user should be given a chance at later points to explicitly change her preferred language.

Note that the servlet 2.2 API introduced the request.getLocale() call. This method gives you the locale based on the Accept-Language HTTP header. Note that the client-side user can adjust what is in this header in either Internet Explorer or Netscape via user preferences. I would venture to say that few users ever adjust this, so that what is most likely is that someone using the Spanish-language version of Netscape, say, has "es-ES" as their Accept-Language header.

In passing, I would discourage you from relying solely on this mechanism. On the one hand, in the absence of other indications, it is a perfectly good starting point, since a Spanish speaker will likely be using the Spanish version of a browser. However, many Spanish speakers may be using the English version of Netscape or IE and might still prefer to see localized Spanish-language content, if it is available. This is a non-trivial issue that requires some thought and that is why I preferred to simply provide a deduceLocale() hook for the application programmer to override. You can decide what heuristics you want to use to decide what the preferred locale is.

In execDefault() we assume that the user has invoked the servlet via the *.nhtml mapping defined in web.xml. If the .nhtml template specified in the URL does not exist, it uses "error.html". Note that the error page is not localized, though it could perfectly well be. We could perfectly well have an error_fr.nhtml and an error_es.html for example.