Friday, October 23, 2009

Recipe 12.4. Changing Locale on the Fly










Recipe 12.4. Changing Locale on the Fly




Problem



You want to allow a user to choose the language and country to be
used by the web application for his session.





Solution



Use my ChangeLocaleAction, based on the Struts
built-in


LocaleAction, as
shown in Example 12-5.




Example 12-5. Struts action for changing the current locale

package com.oreilly.strutsckbk.ch12;

import java.util.Locale;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import javax.servlet.jsp.jstl.core.Config;

import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.struts.Globals;
import org.apache.struts.action.Action;
import org.apache.struts.action.ActionForm;
import org.apache.struts.action.ActionForward;
import org.apache.struts.action.ActionMapping;


/**
* Implementation of <strong>Action</strong> that changes the user's
* @link(java.util.Locale and forwards to a page, based on request level
* parameters that are set (language, country, variant, &amp; page).
* Also changes the JSTL locale.
*/
public final class ChangeLocaleAction extends Action {

private static final String SUCCESS = "success";

/**
* Commons Logging instance.
*/
private Log log = LogFactory.getFactory( ).getInstance(this.getClass( ).
getName( ));

/**
* <p>
* Change the user's @link(java.util.Locale) based on @link(ActionForm)
* properties.
* </p>
* <p>
* This <code>Action</code> looks for <code>language</code> and
* <code>country</code> and <code>variant</code> properties on the given
* form, constructs an appropriate Locale object, and sets it as the Struts
* Locale for this user's session as well as the JSTL locale.
* Any <code>ActionForm, including a @link(DynaActionForm), may be used.
* </p>
* <p>
* If a <code>page</code> property is also provided, then after
* setting the Locale, control is forwarded to that URI path.
* Otherwise, control is forwarded to "success".
* </p>
*
* @param mapping The ActionMapping used to select this instance
* @param form The optional ActionForm bean for this request (if any)
* @param request The HTTP request we are processing
* @param response The HTTP response we are creating
*
* @return Action to forward to
* @exception java.lang.Exception if an input/output error or servlet
* exception occurs
*/
public ActionForward execute(ActionMapping mapping,
ActionForm form,
HttpServletRequest request,
HttpServletResponse response)
throws Exception {

// Extract attributes we will need
HttpSession session = request.getSession( );
Locale locale = getLocale(request);

String language = null;
String country = null;
String variant = null;
String page = null;

try {
language = (String) PropertyUtils.getSimpleProperty(form,
"language");
country = (String) PropertyUtils.getSimpleProperty(form,
"country");
variant = (String) PropertyUtils.getSimpleProperty(form,
"variant");
page = (String) PropertyUtils.getSimpleProperty(form, "page");
} catch (Exception e) {
log.error(e.getMessage( ), e);
}

boolean isLanguage = language != null && language.length( ) > 0;
boolean isCountry = country != null && country.length( ) > 0;
boolean isVariant = variant != null && variant.length( ) > 0;

if ( isLanguage && isCountry && isVariant ) {
locale = new java.util.Locale(language, country, variant);
} else if ( isLanguage && isCountry ) {
locale = new java.util.Locale(language, country);
} else if ( isLanguage ) {
locale = new java.util.Locale(language, "");
}

// reset the Struts locale
session.setAttribute(Globals.LOCALE_KEY, locale);

// reset the JSTL locale
Config.set(session, Config.FMT_LOCALE, locale);

if (null==page || "".equals(page))
return mapping.findForward(SUCCESS);
else
return new ActionForward(page);
}
}







Discussion



The ChangeLocaleAction is based on the Struts 1.2
LocaleAction. This action provides the same
capabilities at the Struts LocaleAction, and it
supports a locale variant. Variants
aren't defined by an ISO standard like language and
country codes but are supported by Java and can give you additional
means of organizing your localized messages. Even more importantly,
the ChangeLocaleAction updates the
Locale used by JSTL as well as the Struts
Locale:



// reset the Struts locale
session.setAttribute(Globals.LOCALE_KEY, locale);

// reset the JSTL locale
Config.set(session, Config.FMT_LOCALE, locale);




The Struts LocaleAction doesn't
do this because it would be dependent on the JSTL APIs. But if
you're using Struts and JSTL's
internationalization capabilities, you will want to update the
Locale for both.



Similar to the LocaleAction, the
ChangeLocaleAction reads the language, country,
and variant codes from properties on an
ActionForm. It creates a Locale
using these values and stores this Locale in the
session. The action returns an ActionForward using
the path specified by the page property or found
under the name "success."



To use the ChangeLocaleAction,
you'll need to create an
ActionForm containing the required properties. You
can extend ActionForm, or you can use a
DynaActionForm specified in your
struts-config.xml file:



<form-bean name="LocaleForm" type="org.apache.struts.action.DynaActionForm">
<form-property name="language" type="java.lang.String"/>
<form-property name="country" type="java.lang.String"/>
<form-property name="variant" type="java.lang.String"/>
<form-property name="page" type="java.lang.String"/>
</form-bean>




Then declare an action in the
struts-config.xml file that uses this
ActionForm and the
ChangeLocaleAction:



<action path="/ChangeLocale"
name="LocaleForm"
scope="request"
type="com.oreilly.strutsckbk.ch12.ChangeLocaleAction"/>




Here's a set of links using this action that allow
the user to change languages between English, Russian, and U.S.
English (Southeastern variant):



<html:link action="/ChangeLocale?language=en">
English
</html:link><br />
<html:link action="/ChangeLocale?language=ru">
Russian
</html:link><br />
<html:link action="/ChangeLocale? language=en&country=US&variant=SE">
Southeastern U.S. English
</html:link><br />




Here's an

HTML form that allows a user
to change the current language. Once changed the request will be
forwarded to the /Welcome.do page.



<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib uri="http://struts.apache.org/tags-bean" prefix="bean" %>
<%@ taglib uri="http://struts.apache.org/tags-html" prefix="html" %>

<html:html lang="true">
<head>
<title>Change language</title>
</head>
<body>
<html:form action="/ChangeLocale">
<html:select property="language">
<html:option value="en">English</html:option>
<html:option value="fr">French</html:option>
<html:option value="ru">Russian</html:option>
</html:select>
<html:hidden property="page" value="/Welcome.do"/>
<html:submit/>
</html:form>
</body>
</html:html>




If you're using Struts 1.2 and
you're not using JSTL or locale variants, then it
doesn't matter if you use the Struts
LocaleAction or my
ChangeLocaleAction. However, if you
aren't using Struts 1.2 or you're
using JSTL, then my ChangeLocaleAction will work
best.





See Also



The API for the LocaleAction can be found at
http://struts.apache.org/api/org/apache/struts/actions/LocaleAction.html.



If you're using Struts and JSTL together, you can
share the resource bundles using the Solution in Recipe 12.2.












    No comments: