7.5 Use of <fmt:message>The JSTL I18N actions all work together for one purpose: to retrieve localized messages from resource bundles. [7] The <fmt:message> action is responsible for that specific task, so nearly all the other I18N actions ”<fmt:setLocale>, <fmt:setBundle>, <fmt:bundle>, and <fmt:param> ”exist, in one way or another, to support <fmt:message>. The <fmt:message> action extracts localized messages from a resource bundle with this syntax: [8]
<fmt:message key [bundle] [var] [scope]/> The key attribute, which is mandatory in the preceding syntax, specifies a key in a resource bundle. Usually, <fmt:message> actions search for a localization context (that contains a resource bundle) themselves , but you can also specify a localization context with the optional bundle attribute. <fmt:message> actions send their localized message to the current JspWriter or, if you specify the var and scope attributes, to a scoped variable. Here are three examples of how you can use <fmt:message> with the preceding syntax: <fmt:message key='greeting'/> <fmt:message key='greeting' var ='msg' scope='request'/> <fmt:message key='greeting' bundle='${aLocalizationContext}'/> The first line of the preceding code fragment shows the simplest use of <fmt:message>: you only specify the key attribute. This is by far the most popular way to use <fmt:message> ”you don't have to come up with a localization context on your own, and <fmt:message> prints to the current JspWriter . In most cases, that JspWriter prints to the browser, which is usually what you want. The second line of the preceding code fragment shows how you can use <fmt:message> to store a localized message in a scoped variable. In that line of code, the <fmt:message> action stores a localized message in a scoped variable named msg in request scope. If you don't specify the scope attribute, for example, <fmt:message key='greeting' var='msg'/> , <fmt:message> will store the scoped variable in page scope. Later on, you can access that scoped variable with <c:out value='${msg}'/> , which will print the localized message that you stored in the msg scoped variable. The third line of the preceding code fragment shows how you can use the bundle attribute to specify an explicit localization context for <fmt:message>. [9] A localization context ”see "Localization Contexts" on page 263 ”is a simple bean that contains a resource bundle and a locale; if you specify a localization context with the bundle attribute, the corresponding <fmt:message> action will use that localization context's resource bundle to retrieve a localized message. This use of <fmt:message> is not very popular, because it's cumbersome to specify a localization context every time you use <fmt:message>.
You can also localize compound messages with <fmt:message>, with this syntax: <fmt:message key [bundle] [var] [scope]> <fmt:param> actions </fmt:message> A compound message contains parameters; for example, at the bottom of your website you might show the current date with a compound message that looks like this: Today is: {0} , where {0} is a parameter that specifies the current date. You could do that like this: <jsp:useBean id='now' class='java.util.Date'/> <fmt:message key='footer.messages.todaysDate'> <fmt:param value='${now}'/> </fmt:message> The preceding code fragment uses <jsp:useBean> to create a Java bean ”an instance of java.util.Date . A <fmt:param> action specifies that bean as a parameter for its enclosing <fmt:message> action. That <fmt:message> action substitutes the bean's value for {0} in the compound message. In a resource bundle, you would specify that compound message like this: footer.messages.todaysDate=Today is: {0} See "Compound Messages and <fmt:param>" on page 283 for more information about compound messages and message parameters. Also, see Listing 7.16 on page 285 for an alternative to <jsp:useBean> in the preceding code fragment by using the fmt_rt tag library. The <fmt:message> action also lets you specify the message key in the body of the action, with this syntax: <fmt:message [var] [scope] [bundle]> key optional <fmt:param> actions </fmt:message> Long message keys and message keys produced by custom actions are two reasons for the preceding syntax; for example, you might store categories for user preferences as message keys, and you might access those categories with custom actions, so you could do this: <fmt:message> <acme:preference category='SESSION_TIMEOUT'/> </fmt:message> In the preceding code fragment, the <acme:preference> custom action returns the message key associated with a user's session timeout; e.g., preferences.session.timeout . That key is used by <fmt:message> to display a localized message; for example, User Session Timeout , for an English locale. That technique essentially lets you create constant values for message keys, so that you can change the keys without changing your JSP pages. If you specify a null value or an empty string for the <fmt:message> key attribute, that <fmt:message> action will produce this message: ?????? . If a <fmt:message> action cannot locate a localization context or if the specified key is not defined in the resource bundle stored in the localization context, <fmt:message> will produce output that looks like this: ???<key>??? , where <key> represents the value that you specified for the action's key attribute. Localization Context LookupAs we saw in "Use of <fmt:message>" on page 265, <fmt:message> actions use a resource bundle stored in a localization context to turn message keys into localized messages. To locate a localization context, all <fmt:message> actions perform a localization context lookup; once they find a localization context, they use its resource bundle to localize their messages. That lookup works like this:
Let's discuss each of the steps listed above. 1 If the <fmt:message> bundle attribute is specified, use it.First, <fmt:message> checks to see if you specified its bundle attribute, for example: <fmt:message key='footer.messages.title' bundle='${messages}'/> If you specify the bundle attribute, <fmt:message> assumes that the bundle attribute's value ( ${messages} in the preceding code fragment) is a localization context, and it uses that localization context's resource bundle to localize its message. That localization context is most often created by business components , but you can easily create one in a scriptlet, like this: [10]
<%@ page import='javax.servlet.jsp.jstl.fmt.LocalizationContext'%> <%@ page import='java.util.*'%> <% ResourceBundle rb = ResourceBundle.getBundle("messages", Locale.FRENCH); LocalizationContext lc = new LocalizationContext(rb); pageContext.setAttribute("messages", lc); %> <fmt:message key='company.slogan' bundle='${messages}'/> The preceding code fragment retrieves a French resource bundle, which is used to create a localization context. The scriptlet stores the localization context in page scope by using PageContext.setAttribute . Subsequently, a <fmt:message> action accesses that localization context by specifying it with the action's bundle attribute. Specifying a localization context with the <fmt:message> bundle attribute is cumbersome because you have to specify that localization context for every <fmt:message> action. It would be better if you could group <fmt:message> actions that use the same localization context; in fact, you can do just that with the <fmt:bundle> action, which is discussed next . 2 If the <fmt:message> action is nested in a <fmt:bundle> , use the enclosing <fmt:bundle> action's localization context.If you don't specify the bundle attribute ”as is usually the case ”<fmt:message> actions check to see if they are nested in a <fmt:bundle> action. Here's the syntax for <fmt:bundle>: [11]
<fmt:bundle basename [prefix]> body content, presumably with I18N or formatting actions, or both </fmt:bundle> All <fmt:message> actions in the body of a <fmt:bundle> action implicitly access the localization context of their enclosing <fmt:bundle> action; for example, in the following code fragment, the first two <fmt:message> actions use the localization context established by their enclosing <fmt:bundle> action. <fmt:bundle basename='messages' > <%-- The two <fmt:message> actions that follow use the resource bundle associated with the bundle base name "messages" --%> <fmt:message key='login.title'/> <fmt:message key='login.welcome'/> <%-- This <fmt:message> action uses a different localization context --%> <fmt:message key='login.footer' bundle='${footers}' /> </fmt:bundle> The third <fmt:message> action in the preceding code fragment uses a localization context that contains a resource bundle whose base name is footers . Because <fmt:message> actions check their bundle attribute first, that attribute overrides the localization context established by an enclosing <fmt:bundle>; so, in the preceding code fragment, the footers localization context overrides the messages localization context for the third <fmt:message> action. The <fmt:bundle> action has a mandatory basename attribute that specifies a resource bundle base name. That attribute is a string that <fmt:bundle> actions use to search for a resource bundle, which is subsequently stored in the <fmt:bundle> action's localization context. See "The Resource Bundle Lookup Algorithm" on page 276 for more information about that resource bundle search. The <fmt:bundle> action is great for grouping <fmt:message> actions that use the same localization context, so you don't have to specify a localization context for every <fmt:message> action. But <fmt:bundle> cannot span JSP pages; it would be nice if you could specify a localization context for all <fmt:message> actions in a particular scope, say, request or session. You can do that with the <fmt:setBundle> action, which is discussed below. 3 Use the FMT_LOCALIZATION_CONTEXT configuration setting.If a <fmt:message> action is free-standing (meaning it's not enclosed in a <fmt:bundle> action) and its bundle attribute is not specified, then the <fmt:message> action looks for a localization context in the FMT_LOCALIZATION_CONTEXT configuration setting; for example: <%-- The following <fmt:message> action is not nested in <fmt:bundle> and does not specify the bundle attribute, so it uses the FMT_LOCALIZATION_CONTEXT configuration setting --%> <fmt:message key='footer.messages.title'/> The FMT_LOCALIZATION_CONTEXT configuration setting is listed in Table 7.7. Table 7.7. FMT_LOCALIZATION_CONTEXT Configuration Setting
In a nutshell , a JSTL configuration setting is a combination of a context initialization parameter and a scoped variable, where the latter can override the former. Configuration settings are searched for in this order: page scope, request scope, session scope, application scope, context initialization parameter. [12]
Because you can specify a configuration setting as a string, you can specify the FMT_LOCALIZATION_CONTEXT configuration setting as a context initialization parameter in a deployment descriptor, like this: <web-app> ... <context-param> <param-name> javax.servlet.jsp.jstl.fmt.localizationContext </param-name> <param-value> messages </param-value> </context-param> ... </web-app> The preceding fragment of a deployment descriptor ( WEB-INF/web.xml ) declares a context initialization parameter named javax.servlet.jsp.jstl.localizationContext , which is the name of the FMT_LOCALIZATION_CONTEXT configuration setting (see Table 7.7). In a JSP page, you can override that context initialization parameter with the <fmt:setBundle> action ”which, as you can see from Table 7.7, is the only JSTL action that sets the FMT_LOCALIZATION_CONTEXT configuration setting. Here's the <fmt:setBundle> syntax: [13]
<fmt:setBundle basename [var] [scope]/> Like <fmt:bundle>, <fmt:setBundle> has a mandatory basename attribute that specifies a resource bundle basename; for example: <fmt:setBundle basename='messages' scope='request'/> The <fmt:setBundle> action in the preceding line of code locates a resource bundle based on the action's mandatory basename attribute ( messages ), and stores that resource bundle in the localization context stored in the FMT_LOCALIZATION_CONTEXT configuration setting. Instead of storing the localization context in that configuration setting, <fmt:setBundle> will store it in a scoped variable if you specify the var , and optionally the scope , attribute. Whether you specify the FMT_LOCALIZATION_CONTEXT configuration setting in a deployment descriptor or with the <fmt:setBundle> action, you specify a resource bundle base name. That base name, combined with a locale, specifies a resource bundle; for example, an English resource bundle for error messages might be named errors_en.properties , where errors is the resource bundle base name. [14]
You can also specify the FMT_LOCALIZATION_CONTEXT configuration setting in a business component, such as a servlet like the one listed below: [15]
... import javax.servlet.jsp.jstl.core.Config; public class InitializationServlet extends HttpServlet { public void init() throws ServletException { // Create a localization context and store it in // the FMT_LOCALIZATION_CONTEXT configuration setting Config.set (getServletContext(), Config.FMT_LOCALIZATION_CONTEXT, "messages"); // resource bundle base name } } When the servlet in the preceding code fragment starts, it sets the FMT_LOCALIZATION_CONTEXT configuration setting with the string messages , which represents a resource bundle base name. The preceding servlet uses the Config class from the javax.servlet.jsp.jstl.core package to set the FMT_LOCALIZATION_CONTEXT configuration setting. See "The Config Class" on page 239 for more information about how you use the Config class. You can also specify the FMT_LOCALIZATION_CONTEXT configuration setting with an instance of javax.servlet.jsp.jstl.LocalizationContext ; for example, the preceding servlet could be modified as follows : ... import java.util.Locale; import java.util.ResourceBundle; import javax.servlet.jsp.jstl.core.Config; import javax.servlet.jsp.jstl.fmt.LocalizationContext; public class InitializationServlet extends HttpServlet { public void init() throws ServletException { ... Locale l = Locale.US; ResourceBundle rb = ResourceBundle.getBundle("basename", l); Config.set (getServletContext(), Config.FMT_LOCALIZATION_CONTEXT, new LocalizationContext(rb, l) ); ... } } This section discussed the localization context lookup. The next section discusses how a localization context's resource bundle, if not explicitly specified, is determined. Resource Bundle LookupWe've covered some ground since we started discussing <fmt:message> at "Use of <fmt:message>" on page 265; here are the highlights:
<fmt:message> actions perform a localization context lookup to find a localization context they can use to localize messages. Those localization contexts are, for the most part, created by <fmt:bundle> or <fmt:setBundle>, both of which perform a resource bundle lookup, given a resource bundle base name. If you specify a string for the FMT_LOCALIZATION_CONTEXT configuration setting, <fmt:message> actions will also perform a resource bundle lookup, using that string as a resource bundle base name. We've already discussed localization context lookups in "Localization Context Lookup" on page 268, so this section focuses on the resource bundle lookup performed by <fmt:bundle>, <fmt:setBundle>, and <fmt:message>. The resource bundle lookup performed by <fmt:bundle>, <fmt:setBundle>, and <fmt:message> requires two pieces of information: a resource bundle base name and one or more locales. You specify a resource bundle base name with the <fmt:bundle> and <fmt:setBundle> mandatory bundle attributes. The next section shows you how to specify the locale. LocalesYou can specify the locale(s) that the <fmt:bundle>, <fmt:setBundle>, and <fmt:message> actions use to find a resource bundle in one of two ways:
By default, the resource bundle lookup uses your browser's preferred locales, but you can override that default with the FMT_LOCALE configuration setting. There are many ways to set the FMT_LOCALE configuration setting; for example, with a servlet, life-cycle listener, or custom action ”"Configuration Settings" on page 230 shows you how to do all those things. The FMT_LOCALE configuration setting is listed in Table 7.8. Table 7.8. The FMT_LOCALE Configuration Setting
You can also set the FMT_LOCALE configuration setting with the <fmt:setLocale> action. That action has the following syntax: [16]
<fmt:setLocale value [variant] [scope]/> The <fmt:setLocale> value attribute specifies a locale as either a language or a language-country combination, such as fr or fr-CA . The variant attribute specifies a browser- or vendor-specific value, such as WIN for Windows or MAC for Macintosh. See "Locales" on page 258 for more information about locales and variants. In practice, variants are seldom used. The scope attribute, which can be page , request , session , or application , specifies the scope to which the locale applies. For example, if you specify application scope, the locale specified with the value attribute will apply to all JSP pages in your application. The default scope is page . The Resource Bundle Lookup AlgorithmThe resource bundle lookup algorithm performed by <fmt:message>, <fmt:bundle>, and <fmt:setBundle> starts with a resource bundle base name and a list of preferred locales. If you've set the FMT_LOCALE configuration setting, then your list of preferred locales consists of the single locale. If you haven't set the FMT_LOCALE configuration setting, then your browser's list of preferred locales will be used. For each preferred locale in order of preference, JSTL searches for resource bundles in WEB-INF/classes ”or a subdirectory of WEB-INF/classes ”with the following names: basename + "_" + language + "_" + country + "_" + variant + "." + class basename + "_" + language + "_" + country + "_" + variant + "." + properties basename + "_" + language + "_" + country + "." + class basename + "_" + language + "_" + country + "." + properties basename + "_" + language + "." + class basename + "_" + language + "." + properties For the preceding names, basename represents a bundle base name, and language, country, and variant are specified by a locale. As soon as a match is found, the algorithm selects that resource bundle and stops. If the set of available resource bundles changes during the execution of a page, the resulting behavior is undefined; leaving that behavior undefined allows implementations to cache resource bundles to improve performance. If no resource bundle is found for any of the preferred locales, the resource bundle lookup is applied to the fallback locale, which you can set with the FMT_FALLBACK_LOCALE configuration variable. [17]
If <fmt:bundle>, <fmt:setBundle>, or <fmt:message> cannot find a resource bundle with the preferred locales or the fallback locale, they try to find a resource bundle with only the base name. To help clarify the resource bundle search algorithm, here are some examples that illustrate how the algorithm works: Example 1:
The algorithm matches the en_GB locale with this Java class: /WEB-INF/classes/Resources_en.class . Example 2:
The algorithm does not find a match for the de or fr locales, but it does find a match for the fallback locale; therefore, the selected resource bundle is specified with this properties file: /WEB-INF/classes/Resources_en.properties . Example 3:
The algorithm does not find a match for the fr locale, because that locale specifies French, not Canadian French. The algorithm does find a match for the sv locale (Swedish), and therefore selects the resource bundle specified with the properties file /WEB-INF/classes/Resources_sv.properties . In this case, even though the user prefers French over Swedish and has a French resource bundle, that resource bundle will not be selected, because it is Canadian French. This example illustrates that resource bundles for a specific language and country should be backed up with a resource bundle covering just the language. If the country-specific differences for a language are too significant for a language-only bundle, users should specify two locales with both language and country in their list of preferred locales. An Example of Dynamically Switching LocalesThis section discusses a Web application, shown in Figure 7-4, that lets users change their locale by clicking on an image of a flag. Changing the locale initiates the resource bundle search algorithm discussed in "The Resource Bundle Lookup Algorithm" on page 276, which changes the current resource bundle. The Login button does not initiate a login; for the sake of illustration, it simply reloads the login page. Figure 7-4. Using <fmt:setLocale> ”Clicking on a Flag Switches Locales
The application shown in Figure 7-4 consists of three JSP pages: index.jsp , flags.jsp , and set_locale.jsp . Figure 7-4 only shows index.jsp , localized for English, German, and Chinese. The application also has three properties files in the WEB-INF/classes directory that represent resource bundles for English, German, and Chinese: app_en.properties , app_de.properties , and app_zh.properties , respectively. As an aside, notice that the window title for the Chinese version of the application is not properly displayed. That's because the window uses the system charset and the application was run under Windows English. Because that charset does not support Chinese, the window itself cannot properly display characters in Chinese. The window title would be properly displayed if the application were run on an operating system that used the Chinese charset. The JSP page shown in Figure 7-4 is listed in Listing 7.9, and the associated properties files are shown in Listings 7.10, 7.11 and 7.12. The preceding JSP page sets its charset to UTF-8 to support multiple locales; see "Unicode and Charsets" on page 260 for more information about charsets and UTF-8 . The preceding JSP page uses <fmt:setBundle> to specify a bundle base name for the JSP page. The application's properties files are listed below. The Chinese properties file ” app_zh.properties ”specifies messages with Unicode escape sequences; see "Unicode and Charsets" on page 260 for more information about Unicode. The JSP page listed in Listing 7.9 also imports flags.jsp and set_locale.jsp . The former displays the flags, and the latter sets the locale, depending on which flag was last selected. When the application is started, no flags have been selected, so the locale is determined from the javax.servlet.jsp.jstl.fmt.locale context initialization parameter, which is specified in WEB-INF/web.xml . Listing 7.13 lists an excerpt from that deployment descriptor. In the preceding listing, the FMT_LOCALE configuration variable (whose value is javax.servlet.jsp.jstl.fmt.locale ) is set to de , so the application shown in Figure 7-4 is initially displayed in German. flags.jsp is listed in Listing 7.14. The preceding JSP page displays the flags shown in Figure 7-4 on page 278. When you click on one of those flags, the current page in which flags.jsp resides is reloaded with the corresponding locale request parameter. Note that the preceding JSP page uses the rather unusual HTML construct <a href='?XXX'> . That construct causes the current HTML (or JSP) page to be reloaded with XXX appended to the page's URL as a request parameter. That construct comes in handy for developing reusable HTML or JSP components, such as the JSP page listed in Listing 7.14. You can read about that construct in section 4.0 (Resolving Relative URLs) of RFC 1808; that RFC is available online at this URL: http://www.ietf.org/rfc/rfc1808.txt. The JSP page set_locale.jsp , which sets the locale according to the last flag selected, is listed in Listing 7.15. The preceding JSP page sets the locale for the user's session with <fmt:setLocale>, depending on the value of the locale request parameter. If that request parameter has not been set, the preceding JSP page does nothing. Listing 7.9 index.jsp<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <%-- Set the charset to UTF-8 for multiple language support --%> <%@ page contentType='text/html; charset=UTF-8' %> <%@ taglib uri='http://java.sun.com/jstl/fmt' prefix='fmt'%> <%@ taglib uri='http://java.sun.com/jstl/core' prefix='c'%> <%-- set_locale.jsp sets the locale according to the locale request parameter that is set by flags.jsp, which is imported below. set_locale.jsp uses <fmt:setLocale>--%> <c:import url='set_locale.jsp'/> <%-- Establish a localization context (based on the locale set by set_locale.jsp and the bundle base name specified for <fmt:setBundle>) used by subsequent <fmt:message> actions. Because the locale set by set_locale.jsp is used to establish that localization context, set_locale.jsp must be imported before <fmt:setBundle> is used. --%> <fmt:setBundle basename='app'/> <%-- Use <fmt:message> to show the page title --%> <title><fmt:message key='login.page.title'/></title> </head> <body> <%-- flags.jsp shows the flags --%> <c:import url='flags.jsp'/> <br> <%-- All of the <fmt:message> actions that follow will use the resource bundle determined by the 'app' bundle base name and the locale that was last selected by a user clicking on a flag. Initially, that locale is set by the javax.servlet.jsp.jstl.fmt.locale context init param in WEB-INF/web.xml. --%> <fmt:message key='login.form.title'/><hr/> <form method='post'> <table> <tr><td><fmt:message key='login.textfield.name'/></td> <td><input type='text' name='userName'/></td> </tr> <tr> <td><fmt:message key='login.textfield.pwd'/></td> <td><input type='password' name='password'/></td> </tr> </table><br> <input type='submit' value='<fmt:message key="login.button.submit"/>'/> </form> </body> </html> Listing 7.10 WEB-INF/classes/app_en.propertieslogin.page.title=Switching Locales login.form.title=Please Log In login.textfield.name=Name login.textfield.pwd=Password login.button.submit=Login Listing 7.11 WEB-INF/classes/app_de.propertieslogin.page.title=Schaltend Locales login.form.title=Bitte Ausstellung(Einrichtung) der Verbindung login.textfield.name=Name: login.textfield.pwd=Password: login.button.submit=Ausstellung(Einrichtung) der Verbindung Listing 7.12 WEB-INF/classes/app_zh.propertieslogin.page.title=\u8bf7\u767b\u5f55 login.form.title=\u8bf7\u767b\u5f55 login.textfield.name=\u7528\u6237\u540d login.textfield.pwd=\u5bc6\u7801 login.button.submit=\u767b\u5f55 Listing 7.13 WEB-INF/web.xml (Excerpt)<?xml version="1.0" encoding="ISO-8859-1"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/j2ee/dtds/web-app_2.3.dtd"> <web-app> ... <context-param> <param-name> javax.servlet.jsp.jstl.fmt.locale </param-name> <param-value> de </param-value> </context-param> ... </web-app> Listing 7.14 flags.jsp<table> <tr><td> <a href='?locale=en'> <img src='graphics/flags/britain_flag.gif'/></a> <a href='?locale=de'> <img src='graphics/flags/german_flag.gif'/></a> <a href='?locale=zh'> <img src='graphics/flags/chinese_flag.gif'/></a> </td></tr> </table> The <fmt:setLocale> action calls ServletResponse.setLocale() , which is required by the servlet specification, to set the response charset to match the specified locale. Because of that requirement, index.jsp , listed in Listing 7.9 on page 280, should not have to specify the UTF-8 charset; however, some servlet containers do not properly implement that requirement, so the contentType page directive in Listing 7.9 on page 280 is necessary to ensure that the example works with all servlet containers. Listing 7.15 set_locale.jsp<%@ taglib uri='http://java.sun.com/jstl/fmt' prefix='fmt'%> <%@ taglib uri='http://java.sun.com/jstl/core' prefix='c'%> <c:choose> <c:when test='${param.locale == "de"}'> <fmt:setLocale value='de' scope='session'/> </c:when> <c:when test='${param.locale == "zh"}'> <fmt:setLocale value='zh' scope='session'/> </c:when> <c:when test='${param.locale == "en"}'> <fmt:setLocale value='en' scope='session'/> </c:when> </c:choose> Also, notice that flags.jsp and set_locale.jsp can be used in any JSP page to switch locales. In fact, those JSP pages, modified to display different flags, are used in the Web applications discussed below and in "Validation with JSP Pages" on page 296. Compound Messages and <fmt:param>When you go to the store, you might say, "I'm going to {0}," where {0} represents the name of the store; after all, there are only so many ways to say that you're going to the store. Sometimes you might say, "I'm going to {0} to get {1}." This phenomenon , known as parameterized text, is commonplace; in fact, you can find many occurrences of "The JSP page shown in {0} is listed in {1}" scattered throughout this book. You can parameterize text by specifying compound messages in your resource bundles. Compound messages specify parameters with {0} to {n-1} , where n represents the number of parameters. <fmt:param> actions, contained within the body of <fmt:message> actions, specify values for those parameters; each <fmt:param> action specifies one parameter for the message displayed by its enclosing <fmt:message> action. The first <fmt:param> action contained within a <fmt:message> action specifies a value for the first parameter, the second <fmt:param> action specifies the second parameter, and so on. You can use <fmt:param> by specifying a parameter with the value attribute, with this syntax: [18]
<fmt:param value/> You can also specify a parameter within the body of a <fmt:param> action, with this syntax: <fmt:param> value </fmt:param> Figure 7-5 shows a Web application that uses both <fmt:param> syntaxes. Figure 7-5. Localizing Compound Messages with <fmt:param> Actions
The Web application shown in Figure 7-5 provides parameters for a disk number and date. The top picture in Figure 7-5 shows the English version of the message, and the bottom picture shows the French version. The JSP page shown in Figure 7-5 is listed in Listing 7.16. Like the JSP page shown in Figure 7-4 on page 278, the preceding JSP page imports set_locale.jsp and flags.jsp so the user can switch locales and sets the bundle base name to app for page scope. The preceding JSP page uses both syntaxes of <fmt:param> to specify parameters for a message specified with the message.diskFull key. The <fmt:param> action specifies its value in its body, whereas the <fmt_rt:param> action specifies its value with the value attribute. Notice that the second param tag uses the runtime expression library version of the param action (<fmt_rt:param>) because it creates a Java object for its value, which is something you cannot do with the expression language equivalent action (<fmt:param>). [19]
Listing 7.16 index.jsp<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <%@ taglib uri='http://java.sun.com/jstl/core' prefix='c' %> <%@ taglib uri='http://java.sun.com/jstl/fmt' prefix='fmt'%> <%@ taglib uri='http://java.sun.com/jstl/fmt_rt' prefix='fmt_rt' %> <%-- set_locale.jsp sets the locale according to the locale request parameter that is set by flags.jsp, which is imported below. set_locale.jsp uses <fmt:setLocale> --%> <c:import url='set_locale.jsp'/> <%-- Establish a localization context (based on the locale set by set_locale.jsp and the bundle base name specified for <fmt:setBundle>) used by subsequent <fmt:message> actions. Because the locale set by set_locale.jsp is used to establish that localization context, set_locale.jsp must be imported before <fmt:setBundle> is used. --%> <fmt:setBundle basename='app'/> <title><fmt:message key='page.title'/></title> </head> <body> <c:import url='flags.jsp'/><br/> <%-- All of the <fmt:message> actions that follow will use the resource bundle determined by the 'app' bundle base name and the locale that was last selected by the user clicking on a flag. --%> <fmt:message key='message.diskFull'> <%-- Specify the parameter for the disk number in the body of the <fmt:param> action --%> <fmt:param> 5 </fmt:param> <%-- Specify the parameter for the date with the <fmt_rt:param> action's value attribute --%> <fmt_rt:param value='<%= new java.util.Date() %>'/> </fmt:message> </body> </html> The English and French properties files used by the application shown in Figure 7-5 are listed below. Listing 7.17 WEB-INF/classes/app_en.properties# application properties -- English Version page.title=Using Compound Messages message.diskFull=Disk number <b>{0}</b> filled up at \ <b>{1, time}</b> on <b>{1, date, full}</b>. Listing 7.18 WEB-INF/classes/app_fr.properties# application properties -- French Version page.title=Utiliser les Messages Compose message.diskFull=Le disque nombre <b>{0}</b> est rempli sur \ <b>{1, time}</b> <b>{1, date, full}</b> The messages in the preceding properties files identify parameters with {0} and {1}. Those parameters are supplied by the <fmt:param> and <fmt_rt:param> actions, respectively. The preceding properties files also specify styles to format the date supplied by <fmt:param>; for example, {1, time } specifies the time portion of the date, and {1, date, full } specifies the full date without the time. You can use other styles, such as {n, percent}, to specify a number as a percent. See the Java documentation for the java.text.MessageFormat class for more information about those styles. The flags.jsp and set_locale.jsp JSP pages imported by the preceding JSP page are nearly identical to the JSP pages of the same name discussed in "An Example of Dynamically Switching Locales" on page 278, except for the flags and locales they support. Those files are listed in Listing 7.19 and Listing 7.20 for completeness. Listing 7.19 flags.jsp<table> <tr><td> <a href='?locale=en-GB'> <img src='graphics/flags/united-kingdom-flag.gif'/></a> <a href='?locale=fr-FR'> <img src='graphics/flags/french-flag.gif'/></a> </td></tr> </table> Listing 7.20 set_locale.jsp<%@ taglib uri='http://java.sun.com/jstl/core'prefix='c' %> <%@ taglib uri='http://java.sun.com/jstl/fmt' prefix='fmt'%> <c:choose> <c:when test='${param.locale == "fr-FR"}'> <fmt:setLocale value='fr-FR' scope='request'/> </c:when> <c:when test='${param.locale == "en-GB"}'> <fmt:setLocale value='en-GB' scope='request'/> </c:when> </c:choose> |