Programming Jakarta Struts, 2nd Edition [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Programming Jakarta Struts, 2nd Edition [Electronic resources] - نسخه متنی

Chuck Cavaness

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید








12.2 Support for I18N in Java


Java provides a rich set of I18N
features in the core library. This section briefly discusses a few of
those core features. The I18N support in the Struts framework relies
heavily on these components, and understanding how the Java I18N
components cooperate with each other will help you understand how to
internationalize your Struts applications.

The topic of internationalization is too broad to cover in depth in
this book. A more complete discussion of the topic can be found in
the book Java Internationalization by Andy
Deitsch and David Czarnecki (O'Reilly).


12.2.1 The Locale Class


The java.util.Locale class is undeniably the most important
I18N class in the Java library. Almost all of the support for
internationalization and localization in or around the Java language
relies on this class.

The Locale class provides Java with instances of
the locale concept mentioned earlier. A particular instance of the
Locale represents a unique language and region.
When a class in the Java library modifies its functionality during
runtime based on a Locale object,
it's said to be
locale-sensitive.
For example, the java.text.DateFormat is
locale-sensitive because it will format a date differently depending
on a particular Locale object.

The Locale objects don't do any
of the I18N formatting or parsing work. They are used as identifiers
by the locale-sensitive classes. When you acquire an instance of the
DateFormat class, you can pass in a
Locale object for the United States. The
DateFormat class does all of the locale-sensitive
parsing and formatting; it relies on the Locale
only to identify the proper format.


Be careful when using the java.text.Format class
or any of its descendants, including DateFormat,
NumberFormat, and
SimpleDateFormat, because they are not
thread-safe. The thread-safety problem exists because an instance of
the Calendar class is stored as a member variable
and accessed during the parse() and
format() method invocations. You will need to use
a separate instance for each thread or synchronize access externally.
Don't store a single instance in somewhere like the
application scope and allow multiple client threads to access it. You
can, however, store instances in the users' sessions
and use different instances for each user to help ensure
thread-safety. The thread-safety problem includes all versions of
Java, including 1.4. The API documentation for the
Format classes has been updated to indicate the
known design issue.

When you create a Locale object, you typically
specify the language and country code. The following code fragment
illustrates the creation of two Locale objects,
one for the U.S. and the other for Great Britain:

Locale usLocale = new Locale("en", "US");
Locale gbLocale = new Locale("en", "GB");

The first argument in the constructor is the
language code.
The language code consists of two lowercase letters and must conform
to the ISO-639 specification. You can
find a complete listing of the available language codes at
http://www.unicode.org/unicode/onlinedat/languagesl.

The second argument is the country code. It consists of two uppercase
letters that must conform to the ISO-3166 specification. The list
of available country codes is available at
http://www.unicode.org/unicode/onlinedat/countriesl.

The Locale class provides several static
convenience constants that allow you to acquire an instance of the
most-often-used locales. For example, to get an instance of a
Japanese locale, you could use either of these:

Locale locale1 = Locale.JAPAN;
Locale locale2 = new Locale("ja", "JP");


12.2.1.1 The default locale

The JVM will query the operating system when it's
first started and set a
default locale for the environment. You
can obtain the information for this default locale by calling the
getDefault() method on the
Locale class:

Locale defaultLocale = Locale.getDefault( );

The web container will normally use the default locale for its local
environment, while using the one passed from the client in the
HttpServletRequest to display locale-sensitive
information back to the end user.


12.2.1.2 Determining the user's locale

In the last section, you saw
how to create Locale objects in Java by passing in
the language and country code to the Locale
constructor. Within web applications, including those built using the
Struts framework, you rarely have to create your own locale instances
because the container does it for you. The
ServletRequest interface contains two methods that
can be called to retrieve the locale preferences of a client:

public java.util.Locale getLocale( );
public java.util.Enumeration getLocales( );

Both of these methods use the Accept-Language
header that is part of each client request sent to the servlet
container.

Because the web server doesn't keep a long-term
connection open with a browser, the client locale preference is sent
to the servlet container with each request. Although the
user's locale information may be sent with each
request, Struts will, by default, retrieve the information only once
and store it into the user's session. The
Locale object, if stored into the session, is
stored with a key of Globals.LOCALE_KEY, which
translates to the string
org.apache.struts.action.LOCALE.


You can configure whether Struts stores the user's
locale into the session by setting the locale
attribute in the controller element within the
Struts application configuration file. If you don't
provide a value for the locale attribute, it
defaults to true. See Chapter 4 for more information on configuring the
locale.

Calling the getLocale() method on the
HttpServletRequest object returns the preferred
locale of the client, while the getLocales()
method returns an Enumeration of preferred locales
in decreasing order of preference. If a client
doesn't have a preferred locale configured, the
servlet container will return its default locale. Example 12-1 illustrates how to determine this information
using a servlet.


Example 12-1. Determining the user's locale information in a servlet

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
import java.util.Locale;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* Prints out information about a user's preferred locales
*/
public class LocaleServlet extends HttpServlet {
private static final String CONTENT_TYPE = "text/html";
/**
* Initialize the servlet
*/
public void init(ServletConfig config) throws ServletException {
super.init(config);
}
/**
* Process the HTTP Get request
*/
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType(CONTENT_TYPE);
PrintWriter out = response.getWriter( );
out.println("<html>");
out.println("<head><title>The Example Locale Servlet</title></head>");
out.println("<body>");
// Retrieve and print out the user's preferred locale
Locale preferredLocale = request.getLocale( );
out.println("<p>The user's preffered Locale is " + preferredLocale + "</p>");
// Retrieve all of the supported locales of the user
out.println("<p>A list of preferred Locales in descreasing order</p>");
Enumeration allUserSupportedLocales = request.getLocales( );
out.println("<ul>");
while( allUserSupportedLocales.hasMoreElements( ) ){
Locale supportedLocale = (Locale)allUserSupportedLocales.nextElement( );
StringBuffer buf = new StringBuffer( );
buf.append("<li>");
buf.append("Locale: ");
buf.append( supportedLocale );
buf.append( " - " );
buf.append( supportedLocale.getDisplayName( ) );
buf.append("</li>");
// Print out the line for a single Locale
out.println( buf.toString( ) );
}
out.println("</ul>");
// Get the container's default locale
Locale servletContainerLocale = Locale.getDefault( );
out.println("<p>The container's Locale " + servletContainerLocale + "</p>");
out.println("</body></html>");
}
}

When you execute the servlet in Example 12-1, you
should see output similar to the browser output in Figure 12-1.


Figure 12-1. Browser output from Example 12-1

The output may be different if you have different locales configured
for your system. Most web browsers allow you to configure the locales
you prefer to support. With Microsoft Internet Explorer, for example,
you can edit the languages in the Tools
Internet Options pulldown menu.

etting the user's locale within the Struts framework
is easy. There are, in fact, several ways of getting the stored
Locale for the user, depending on where you are
trying to access it. If you are within an Action
class, for example, you can simply call the getLocale(
)
method defined in the Struts base
Action class. The following fragment shows the
getLocale(
)
method:

protected Locale getLocale(HttpServletRequest request) {
HttpSession session = request.getSession( );
Locale locale = (Locale) session.getAttribute(Globals.LOCALE_KEY);
if (locale == null) {
locale = defaultLocale;
}
return (locale);
}

You will need to pass the request object to this method because it
will need to use the HttpSession to obtain the
locale.

The getLocale() method will always return an
instance of the Locale, even if one
isn't stored in the session for the user. The method
will return the defaultLocale if necessary. The
defaultLocale property is stored as a static
member variable that every Action subclass has
access to:

protected static Locale defaultLocale = Locale.getDefault( );

Obtaining the user's locale from anywhere else is
also straightforward. You can simply get it directly from the session
as the getLocale() method does, using the
Action.LOCALE_KEY:

Locale userLocale = (Locale)session.getAttribute(Globals.LOCALE_KEY);
// With this approach, always check the Locale to see if it's null
if ( userLocale != null ){
// Access the Locale
}

Because it's possible that no
Locale is stored in the user's
session, you should compare the returned Locale to
null before attempting to use it.

If your application allows a user to change locales on the fly, you
may have to call the getLocale() method on each
new request to see if the user has changed locales. An example of
doing this was shown in the CustomRequestProcessor
class in Example 5-4.


12.2.2 Java Resource Bundles


The java.util.ResourceBundle class provides the
ability to group together a set of resources for a given locale. The
resources are usually textual elements such as field and button
labels and status messages, but they can also be items such as image
names, error messages, and page titles.

The Struts framework does not use the
ResourceBundle class
provided by the core language. Instead, it provides similar
functionality with the classes within its framework. The
org.apache.struts.util.MessageResources
class and its only concrete subclass,
org.apache.struts.util.PropertyMessageResources,
are used to perform parallel functionality to that of the
ResourceBundle hierarchy. If you understand the
fundamentals of the ResourceBundle in the code
library, you basically understand how the version within the Struts
framework operates.


In retrospect, the MessageResources class should
at least have been a subclass of the Java
ResourceBundle.

You'll see an example of creating a resource bundle
for a Struts application later in this chapter in the section
"The Struts Resource Bundle."


12.2.3 The MessageFormat Class


The Java
ResourceBundle and the Struts
MessageResources class allow for both static and
dynamic text. Static text is used for elements such as field and
button labels where the localized strings are used exactly as they
are in the bundlein other words, when the text for the message
is known ahead of time. With dynamic text, part of the message may
not be known until runtime. To help make the difference clearer,
let's look at an example.

Suppose you need to display a message to the user informing him that
the name and phone input fields are required in order to save. One
approach would be to add entries like these to the resource bundle:

error.requiredfield.name=The Name field is required to save.
error.requiredfield.phone=The Phone field is required to save.
// other resource messages

This approach works fine, but what if there were hundreds of required
fields? You would need a resource message for each required field,
and the resource bundle would become very large and difficult to
maintain. Note, however, that the only difference between the two
messages is the name of the field that is required.

A much easier and more maintainable approach is to use the
functionality of the java.text.MessageFormat
class. This allows you to do something like this:

error.requiredfield=The {0} field is required to save.
label.phone=Phone
label.name=Name

The values that are not known until runtime are substituted in the
message by a set of braces and an integer value. The integer inside
the braces is used as an index into an Object[]
that is passed in with the format() message of
the MessageFormat class. Example 12-2 provides an example of this.


Example 12-2. Using the MessageFormat class to format messages with variable text

import java.util.ResourceBundle;
import java.util.Locale;
import java.text.MessageFormat;
public class FormatExample {
public static void main(String[] args) {
// Load the resource bundle
ResourceBundle bundle = ResourceBundle.getBundle( "ApplicationResources" );
// Get the message template
String requiredFieldMessage = bundle.getString( "error.requiredfield" );
// Create a String array of size one to hold the arguments
String[] messageArgs = new String[1];
// Get the "Name" field from the bundle and load it in as an argument
messageArgs[0] = bundle.getString( "label.name" );
// Format the message using the message and the arguments
String formattedNameMessage =
MessageFormat.format( requiredFieldMessage, messageArgs );
System.out.println( formattedNameMessage );
// Get the "Phone" field from the bundle and load it in as an argument
messageArgs[0] = bundle.getString( "label.phone" );
// Format the message using the message and the arguments
String formattedPhoneMessage =
MessageFormat.format( requiredFieldMessage, messageArgs );
System.out.println( formattedPhoneMessage );
}
}

Messages that contain variable data are known as
compound messages.
Using compound messages allows you to substitute application-specific
data into messages from the resource bundle at runtime. It can also
reduce the number of messages that your application requires in the
resource bundle, which can decrease the amount of time that it takes
to translate to other locales.


Using compound messages in your resource bundles can make translation
a little harder because the text contains substitution values that
are not known until runtime. Also, human translators must take into
account where the variable text goes in the localized message,
because the substitution values may need to be in different positions
in the message for different languages.

The Struts framework includes the capabilities of the
MessageFormat class but encapsulates the
functionality behind the components within the framework.


12.2.4 Multilingual Support


Most of us cringe at the thought of supporting user groups that are
in one of several possible locales. In many cases, however, once an
application has been installed and localized, it's
like any other single-locale application. The users that access the
application are either all from the same locale or are from locales
similar enough that the language and cultural differences are
insignificant.

Multilingual
applications, on the other hand, take internationalization to the
next level by allowing users from different locales to access the
same application. This means that the application has to be flexible
enough to detect the user's locale and format
everything based on that locale. This is much harder to do than just
localizing an application.

The discussion of building multilingual applications is so large that
it can't be covered satisfactorily in this book. For
the remainder of this chapter, we'll stick with just
the everyday internationalization problems that
you'll face, and not focus on multilingual support.


    / 181