Wednesday, April 29, 2009

Is all this JSF stuff really worth it? It makes easy things hard!

 

So far, after quite a bit of writing, we’ve managed to put together a simple JSF view page which uses tags from both the JSF core and JSF HTML tag libraries. Just for review, here’s our Java Server Faces code:

<%@taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@taglib uri="
http://java.sun.com/jsf/html" prefix="h"%>
<html>
<head>
<title>JSF Made Easy</title>
</head>
<f:view>
    <body>
    <p>Hello World! Now check out this link:</p>
    <h:outputLink id="link01" value="
http://www.scja.com">
          <h:outputText id="output01" value="Get Java Certified!" />
    </h:outputLink>
    </body>
</f:view>
</html>

And, here’s what the page looks like when it runs in a web browser like Firefox or Chrome:

jsf intro hello 02

And of course, a web browser simply renders HTML, so here’s the HTML that gets send to the web browser:

<html>
<head>
<title>JSF Made Easy</title>
</head>
<body>
<p>Hello World! Now check out this link:</p>
<a id="link01" href="http://www.scja.com">
<span id="output01">Get Java Certified!</span>
</a>
</body>
</html>

Now, one thing you might be thinking right now is ‘that’s a whole heck of a lot of work to send some pretty basic HTML to a web browser!’ I mean, if all we wanted to do was display the web page pictured above, we could have simply written the HTML by hand in a text editor, saved it with an html extension, and published it to a web server like Apache. It would have taken all of fifteen minutes, if even that. So, is all of this stuff with Front Controllers, FacesServlets, JSF custom tags and faces-config.xml files worth it? Right now, it probably seems like it doesn’t.

Of course, we need to bear in mind that right now, one of our main goals is to just get things working. We need to get the JSF framework, and a page that uses JSF custom tags, working. If we can’t write, deploy and test a basic JSF application, we’ll never succeed at the more complicated stuff. But still, one can’t help but wonder if the complexity is really worth it.

Well, let me assure you that the complexity is indeed worth it. Remember, the goal of JSF isn’t just to pump out HTML to a web browser, but instead, the goal is to simplify many of the most common and tedious programming tasks associated with Java based, enterprise web development.

For example, Java Server Faces provides a variety of functions that facilitate state management, provide input validation, implement the standard ‘memento pattern’ and a variety of other important concepts that all good web applications should embrace. One important, yet often difficult to implement function is that of internationalization, or i18n for short.

For example, our application currently hard codes in phrases like “Hello World” and “Get Java Certified.” That’s unacceptable, especially if our application is targeting the US population that primarily speaks Spanish. Yes, all good web based applications should be internationalized, and we should never actually hard-code any language specific text strings in our GUI layer.

Internationalizing your JSF applications is easy. There’s a few steps involved, don’t get me wrong, but with a modicum of time and effort, you’ll find that multi-language support in your JSF view pages is a cinch.

The first thing you need to do as you prepare to internationalize your JSF applications is to decide which languages you are going to support, and if someone views your application with an unsupported language, to which language setting will your application default. This is all then configured in an application entry in the faces-config.xml file, using the XML elements locale-config, default-locale and supported locale. Here’s how our faces-config.xml file would if we chose to support Spanish, French and English by default:

<?xml version="1.0"?>
<!DOCTYPE faces-config PUBLIC
  "-//Sun Microsystems, Inc.//DTD JavaServer Faces Config 1.0//EN"
  "http://java.sun.com/dtd/web-facesconfig_1_0.dtd">

<faces-config>

    <application>
        <locale-config>
            <default-locale>es</default-locale>
            <supported-locale>en</supported-locale>
            <supported-locale>fr</supported-locale>
        </locale-config>
    </application>

</faces-config>

Notice that the entry in the default-locale and supported-locale elements are simply the universally accepted internationalization language codes that all web browsers and application servers understand.

The second thing you need to do in order to internationalize your applications is take a look at all of the places where text strings appear in your application. I’ve bolded all of the hard coded text strings in our current JSP:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<%@taglib uri="
http://java.sun.com/jsf/core" prefix="f"%>
<%@taglib uri="
http://java.sun.com/jsf/html" prefix="h"%>
<html>
<head>
<title>JSF Made Easy</title>
</head>
<f:view >
    <body>
    <p>Hello World! Now check out this link:</p>
    <h:outputLink value=”
http://www.scja.com>
          <h:outputText id="output01" value="Get Java Certified!" />
    </h:outputLink>
    </body>
</f:view>
</html>

Once identified, you need to give a unique identifier to each of these Strings that will act as a key in a properties file or resource bundle. The keys I’ll create will all start with the word ‘index’, as this is the index.jsp page, followed by a ‘dot’ delimiter, and then a description of the text. Next to that goes an equals sign and a language specific translation. Here’s how my English translation, key-value pairs, will look:

###### UIResources_en.properties  English Translations ######
index.title = JSF Made Easy

index.helloworld = Hello World.Check out the link below!

index.link = http://www.google.com
index.linkdescription = It's google dot calm

This then gets saved to a text file named UIResources_en.properties, which I’m placing alongside all of my Java source code, in a subfolder structure of com\mcnz\jsf. This will end up giving the UIResources file a Java based path of com.mcnz.jsf.UIResources_en.properties. Notice the ‘_en’ appended to the name of the file. This indicates that this file should be used for English language users. I’ll also add a UIResources_es.properties file in the same com\mcnz\jsf folder for all of my American viewers, which will look like this:

###### UIResources_es.properties  Spanish Translations ######
index.title = El JSF Made Easy

index.helloworld = Hola mundo! Compruebe hacia fuera este acoplamiento.

index.link = http://www.google.es
index.linkdescription = Google espagnol

And seeing that I’m a Canadian, I’d lose my citizenship if I didn’t include a French, UIResources_fr,  translation file as well:

###### UIResources_fr.properties  Spanish Translations ######
index.title = Le JSF Made Easy

index.helloworld = Bonjour monde! Verifiez ce lien.

index.link = http://www.google.fr
index.linkdescription = Google français

You will notice that each UIResources file ends with an internationalization code such as _fr or _es. It’s always a good idea to include a default translation file with no extension, just for good measure. Here’s my default UIResources file, with a couple of English words added just to allow us to identify it if the Java Server Faces frameworks calls upon it in our application.

###### UIResources.properties  Default Text ######
index.title = Default JSF Made Easy Title

index.helloworld = Default Hello World. Check out the link below!

index.link = http://www.google.com
index.linkdescription = It's google dot calm

So, once your internationalized resource bundles are defined, you need to head over to your JSP page and add a little JSF core tag called loadbundle. In this tag, you provide the package based path to your resource file, along with a variable name that will be used to reference the internationalized resource files later on in the JSP.

<f:loadBundle basename="com.mcnz.jsf.UIResources" var="i18n"/>

Again, notice how the var attribute makes ‘i18n’ a variable reference for this particular resource bundle, as it is possible to have multiple resource bundles used on a page.

The loadbundle tag is best placed immediately after the view tag, like so:

<%@taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<html>
<f:view>
<f:loadBundle basename="com.mcnz.jsf.UIResources" var="i18n"/>

<head>
<title>JSF Made Easy</title>
</head>

    <body>
    <p>Hello World! Now check out this link:</p>
    <h:outputLink value=”
http://www.scja.com>
          <h:outputText id="output01" value="Get Java Certified!" />
    </h:outputLink>
    </body>


</f:view>
</html>

The next step is to remove any hard coded display string with a JSF outputText tag. Furthermore, the value of the outputText tag must point to a key defined in the UIResources file. So, we will replace the “JSF MADE EASY” text in the <title> tags with the following JSF custom tag:

<title>
<h:outputText value="#{i18n['index.title']}"/>
</title>

Now, I’ll be the first to admit that the syntax of the value attribute is a little scary. I mean, what’s with the hash sign and the chicken-lip brackets and the square brackets and all? Well, I’ll try to explain it, but sometimes you just have to accept the syntax as being the required syntax. What’s the old saying? “It is what it is.”

value="#{i18n['index.title']}"

For the value attribute, we must instruct the JSF framework to process the value programatically, and not just print out the text as it appears in the tag. After all, I don’t think any user wants to see all of the textual junk inside the quotes there displaying on their page. So, the hash sign indicates that the attribute should be processed by the JSF framework. Essentially, the has sign, #, begins what we call a “JSF expression” language statement, which in this case, will ask the JSF framework to look for the locally defined variable named i18n (which is the name of our resource bundle as defined in the loadBundle tag), and ask an internationalized version of the property file that i18n variable represents, for a text string that represents a translation for the key ‘index.title’. So, when an English user hits this tag, JSF will spit out the value “JSF Made Easy”, and when a Spanish user hits this tag, JSF will spit out the string “El JSF Made Easy.” (Please excuse me for my poor Spanish translations.)

Of course, we need to replace all of the language-dependent text with a reference to the corresponding key in the associated, translated, resource bundle. When we’ve fully internationalized our index.jsp, here’s how the page source will look:

<%@taglib uri="http://java.sun.com/jsf/core" prefix="f"%>
<%@taglib uri="http://java.sun.com/jsf/html" prefix="h"%>
<html>
<f:view>
<head>
<title><h:outputText value="#{i18n['index.title']}"/></title>
</head>

<f:loadBundle basename="com.mcnz.jsf.UIResources" var="i18n"/>
    <body>
    <h:outputText value="#{i18n['index.helloworld']}"/>
    <h:outputLink id ="link01" value="#{i18n['index.link']}">
          <h:outputText value="#{i18n['index.linkdescription']}"/>
    </h:outputLink>
    </body>
</f:view>
</html>

When I’m browsing the internet, the language setting on my computer is Canadian-English, so when I test my application, I end up getting text pulled from the UIResources_en message bundle. If the language setting of my browser was changed to Spanish or French, I would get the corresponding Spanish or French translations.

jsf intro hello 03

By default, JSF will look at the language setting configured by the web browser, and subsequently sent to the server throught the corresponding Http request header. However, we can override this default behavior by adding a locale attribute to the h:view JSF tag. Here’s how we could edit the h:view tag to force the web page to display in spanish:

<f:view locale="es">

jsf intro hello 04

And of course, setting our page’s locale to French, fr, would trigger a French rendering of the page:

jsf intro hello 05

And of course, setting the language code to swahili, well, it just diverts back to the English translation, as English was configured as the default language in the faces-config.xml file:

 

Now, one thing that I should point out is that not only have all of the text strings been internationalized, but so has the associated link!

When we talk about internationalization, we usually limit the conversation to text strings, but there are many different parts of a web page that might require internationalization. You’ll notice that with the outputLink tag, we used the same JSF expression to look up an appropriately internationalized URL as we did for the outputText tag. So now, French users will be sent to the google.fr page, and Spanish users will be sent to the google.es webpage.

And of course, this same convention can be used in other JSF tags as well. For example, images displayed on a webpage might be different for different languages, so we can have images internationalized the same way when we use the graphicImage tag provided by JSF. And of course, this is really the idea behind using a framework like JSF – sure, some very easy tasks, such as just spitting out plain old HTML might be a little more work with JSF, but complex tasks, like internationalization and localization, are greatly simplified and standardized when using Java Server Faces. This is why we love JSF, because it helps to simplify and standardize how we approach some of the most difficult, challenging, and dare I say it – tedious – aspects of modern Java development.

 

By the way, in JSF 1.2, you don’t need to add a loadBundle tag to every JSP page. You can simply set up the i18n resource bundle and its name once in the faces-config.xml file, and unless instructed otherwise, JSF pages will look for translations in these named files.

<application> <resource-bundle> <base-name>bundles.messages</base-name> <var>bundle</var> </resource-bundle> </application>

1 comment:

  1. Developed through the Java Community Process under JSR - 314, JavaServer Faces technology establishes the standard for building server-side user interfaces. With the contributions of the expert group, the JavaServer Faces APIs are being designed so that they can be leveraged by tools that will make web application development even easier.

    vitamin b12

    ReplyDelete

Followers