Maze Runner: JSTL Syntax

May 27, 2019
Senior Developer

At Bounteous, we often encounter projects that require translation work. These types of projects allow us to flex our technical skills and provide us an opportunity to discover simple solutions for otherwise complex tasks. With this in mind, we wanted to share some useful, low-level technical help, instead of general principles, to assist folks who may also be working with translation-related projects.

While Java Server Pages (JSPs) are not the rage they were at the turn of the century, JSP websites are still common. Despite their longevity, however, many still do not support multiple languages. The good news: the JavaServer Pages Tag Library (JSTL) provides ample support for localization in JSPs. The bad news: it is easy to get lost in the syntactical labyrinth of languages and frameworks when trying to add JSTL. Fear not! This article is your maze runner to get you through safely.

The Basics

This article assumes you have some basic understanding of JSTL, its features, and how to get started. This is not meant to be a tutorial on JSTL, rather a guide to integrating it into a code base of JavaScript, Java, and HTML. 

Setup

For our examples, we will assume a starting point of setting the locale and default resource bundle containing our translated strings as follows:

<fmt:setLocale value="fr_CA" />
<fmt:setBundle basename="directory.resourceFile" />

Note: in this case, we are hard-coding the locale, which is typically not done. We will update this example to be more flexible, once we have introduced some additional syntax to integrate libraries.

Accessing Localized Text

The standard example for getting a resource string using JSTL is:

<fmt:message key=”button.reply"/>

In this case, the source of the data to be displayed is hardcoded to the string keyed by “button.reply” in the resource file “directory/resourceFile_fr_CA.properties”. 

With these basic examples in place, let’s take a look at some of the twists and turns typically encountered when accessing and displaying data from various layers and frameworks.

JSP-Only Examples

HTML

The following shows how to display that same “button.reply” string as the text of a button in HTML:

<input type="button" name="send" value='<fmt:message key="button.reply"/>' onClick="reply();" /> 

Note how the original example is embedded in the button declaration in single quotes.

HTML With Variable

This scenario shows HTML displaying a message generated by JSTL using a concatenation of a localized string and a variable. Note: the variable numberUnread can be set in a number of ways. For simplicity, we show it hard coded here:

<c:set var="numberUnread" value="2" />
...
<span>
<fmt:message key='message.unreadMessages'/> (<c:out value="${numberUnread}" />)
</span>

The resulting message might be something like, “Unread Messages (3)”.

Java Using JSTL

One of the key features of JSPs is embedding Java code right in the file, using the tag “<% %>”. This allows you to mix HTML, JavaScript, and Java in the same file. Therefore, it is reasonable to expect that at some point we will want to localize a string from within the Java code. Since JSTL and Java do not play in the same context, there is no way to call JSTL from Java code, so we need to pass data between the layers.

<% // start Java scriptlet
...  
// try to save files or other such Java activity. 
// “fileSaved” = number of files successfully saved
If (filesSaved == 0){
  	// If not file saved, raise a error. Get the
// localized error from the appropriate resource file
%>

<fmt:message var="exception" key="error.nofilesaved"/>

<%
// No files saved so throw exception using the variable set 
// via JSTL when we escaped Java
throw new RuntimeException( 
(String)pageContext.getAttribute("exception"));
...
%>

To pass data between Java and JSTL, we set a variable in JSTL (“exception”) which is available to Java in the pageContext as an attribute (pageContext.getAttribute("exception")). By taking a break in the middle of the Java scriptlet we can set data in JSTL for Java to use, though this variable could have been set just as easily prior to the scriptlet code.

Java Using JSTL Alternative

It should be mentioned that there is a seemingly more direct path through this part of the maze, but like many shortcuts, it might lead to a dead end:

// String exception = “error msg”; //where “error msg” comes from JSTL
<%String exception = "%><fmt:message key="error.nofilesaved"/><%";%>

The JSP: 

  • Sets a variable “exception” in a Java scriptlet.
  • Exits the scriptlet to inject the localized error message.
  • Restarts the Java code to close out the appropriate syntax. 

While this can be effective, you can run into issues if the localized string has quotes. The quotes could inadvertently close the Java quotes resulting in a syntax error. Trying to resolve the syntax issues may take you to a part of the maze better left unexplored. Therefore, passing data through the pageContext is generally cleaner and safer.

JSTL Using Java

If Java needs JSTL values, we can imagine the need for the reverse, where JSTL needs a value set in Java code. The pageContext comes in handy again:

<%
// Java code here, including setting variable “uploadFileName”.
// Prepare to pass uploadFilename to JSTL by setting it 
// in the pagecontext
pageContext.setAttribute("uploadFilename", uploadFilename);
%>
<fmt:message key="messages.fileNotUploaded" /> <c:out value="${uploadFilename}" />

Setting the attribute “uploadFilename” in the pagecontext makes it directly accessible to JSTL as a variable, “${uploadFilename}”.

Using Java Data As A JSTL Parameter

Since appending the file name to the localized message might not make sense in all languages, it would be better to take advantage of JSTL param option to pass a parameter to the localization process. So the above example becomes:

<%
pageContext.setAttribute("uploadFilename", uploadFilename);
%>
<fmt:message key="messages.fileNotUploaded" />
<fmt:param value='${uploadFilename}'/>
</fmt:message>

The code uses the JSTL “fmt:param” option to pass a parameter to the “fmt” command which will replace “{0}” in the resource string with the parameter value. The resource string might look something like this:

messages.fileNotUploaded=The file {0} could not be uploaded.

Therefore, for parameter “myFile.txt”, the resulting message will be:

The file myFile.txt could not be uploaded.

Java/JSTL/Java

Combining these examples we can see how to navigate the maze of Java and JSTL, weaving in and out of each framework:

  • Java contains a file name. 
  • Java wants to throw an exception/generate a message that uses that file name but needs the message localized.
  • JSTL can access the localized message, but needs the specific file name to generate the full message.

In brief, the data flow is:

visualization of the data flow between java and jstl

Here is some sample code for using a Java variable to create a JSTL localized string that is used in the Java code:

<%
pageContext.setAttribute("uploadFilename", uploadFilename);
%>
<fmt:message var=”exception” key="messages.fileNotUploaded">
<fmt:param value='${uploadFilename}'/>
</fmt:message>
<%
throw new RuntimeException(
(String)pageContext.getAttribute("exception"));
%>

The need to pass data back and forth between layers is very common, and deceptively tricky. Again, we see that passing information via the pageContext makes this possible, this time from Java to JSTL, and then from JSTL back to Java.

JavaScript

Using JSTL to display a string in JavaScript, in this case as a message in an alert, is fairly straightforward:

alert ("<fmt:message key='message.warning'/>");

Note how the original example is basically embedded in the alert, with careful use of nested quotes, making sure to match double quotes around the JSTL command and single quotes around the resource key.

So far we have shown the interaction of only two libraries. Let’s mix things up by adding a third. 

JavaScript/JSTL/HTML

In this example, we have HTML that wants to include a call to a JavaScript function, but that function call can vary based on certain conditions, so it is provided in a JSTL variable:

<fmt:message var="alertMessage" key=message.warning/>
<c:set var="jsfunc" value='alert("${alertMessage}");' />
<img src='/images/myimg.gif' onclick='<c:out value="${jsfunc}" />' />

Notice that the parameter to the JavaScript function is also provided via JSTL, because the message to be displayed by the function must be localized. So, dissecting this bit of code we see that:

  • alertMessage is the localized message to display to the user.
  • jsfunc is the variable containing the JavaScript function call.
  • Both variables, alertMessage and jsfunc are referenced using the same syntax, “${variable name}”.
  • As always, pay careful attention to the use of quotes and other syntax sugar.

Java Delegates

Date From Delegate, Second Resource Bundle

It is common that the data to be displayed comes from an external source, like a database or API call. JSPs can make use of a delegate class/bean, a backend class to provide this data. The details of passing data from delegates is beyond the scope of this article, but this is included as a general example. 

In this use case we want to display a formatted date where the actual date comes from just such an external source. Fortunately, JSTL provides for date localization with the “formatDate” tag, supporting different date formats, i.e. MM/DD/YYYY vs. YYYY/MM/DD format. In this case, the date format is the resource stored in the bundle, not the content being displayed.

To add another layer of realistic flavor, our developers had the good wisdom to put the date format into a centralized resource bundle, so that all code would use a single configuration. This means the localized resource we need, the date format, is stored in a separate bundle from our default bundle for this page. Take a look:

<jsp:useBean id=msg class="MessageDetails" scope=”session” />
<%
pageContext.setAttribute("email", msg);
%>
<fmt:message var="dateFormat" key="dateFormat"
bundle="${commonBundle}"/>
<fmt:formatDate value="${email.originationdate}" pattern="${dateFormat}"/>

You can see that the code is getting the date format from a second, non-default, bundle, “commonBundle”, that has been set up earlier in the code. So the code flow is:

  • Delegate gets an email object whose date we care about and makes it available in a MessageDetails class (code not shown).
  • JSP scriptlet gets the MessageDetails object in a variable “msg” set by the delegate and sets it in the pageContext as attribute “email”.
  • “fmt:message” gets the localized date format from the bundle “commonBundle” and saves in a JSTL variable “dateFormat”.
  • The formatted date is now constructed by passing the “formatDate” tag/command the localized date format, “dateFormat”, and the date from MessageDetails property “originationDate”, accessed via “email.originationDate”

Wrapping Up

Setting Locale

Now, we can go full circle and complete our first example, and realistically set the locale, not using a hard-coded value as in our very first example, but using the technique in the previous example:

<jsp:useBean id=userDetails class="UserDetails" scope=”session” />
<%
pageContext.setAttribute("user", userDetails);
%>
<fmt:setLocale value="$user.locale" />

In this example, the delegate is setting a variable of type UserDetails, to represent the logged in user. This class contains the user’s preferred locale, so we use that to set the default locale for this JSP.

Summary

JSPs provide the flexibility to integrate multiple technologies, but it comes at the cost of increased syntactical and data flow complexity. These code snippets provide some basic examples of how to navigate between the common JSP layers and get them to play well together when introducing localization via the JavaServer Pages Tag Library. Hopefully these will help guide you through the maze as you encounter other coding surprises. And who knows, by adding multiple language support to your site, your JSPs may be around for another decade.