|
Articles Index
By Govind Seshadri; Reprinted from JavaWorld
Merchants can no longer afford to have English-only versions of their
ecommerce sites. Every Web surfer turned away by
a site's
English-centric nature is a potential customer
lost. Consequently, the
issue of internationalizing Websites has become a
fairly hot topic in
recent months. In this article, Govind Seshadri
discusses the
development of global JavaServer Pages
(JSP) pages. By making use of certain classes
provided by the Java
2 Software Development Kit (SDK), you can develop
Web applications that can dynamically change
their display, thus
adapting themselves to different languages and
countries where they may
be viewed. This article assumes the reader is
familiar with the
fundamentals of JSP programming.
Typically, most websites you come across are customized for a specific locale,
with the locale defined as a geographical region or political entity that shares
a common language, culture, and customs. While this may not be a significant
issue to most people, it may pose some interesting challenges to those
conducting business on the Web. By its very nature, ecommerce is all about
facilitating commerce across national, linguistic, and cultural boundaries.
While the Web may have opened up businesses to a truly international clientele,
Web-based firms have to contend with the all-too-probable scenario of
non-English-speaking users struggling to understand their sites' content. As
many businesses have come to realize, every web surfer who turns away because of
the site's English-centric nature is a potential customer lost.
So, is there an easy, manageable solution to this problem that keeps you from
having to run multiple versions of the site? Well, if the dynamic content for
the website is generated through Java
technology, there sure is! That's because the Java platform intrinsically
supports the development of locale-independent applications through various
classes supporting internationalization found within the
java.util, java.io, and java.text
packages. Internationalization itself is the name given to the design process
wherein an application's country, language, and culture-specific information is
isolated and typically encapsulated within some external files or objects.
Consequently, making your application compatible with an entirely new language
or country does not have to involve a major rewrite of your presentation logic;
rather, it now merely involves creating an additional version, specific to the
new locale, of the external files. The process of creating these locale-specific
entities (be they files or objects), along with the associated translation of
text, is called localization.
Websites based on JavaServer Pages lend themselves more easily to
internationalization than those developed using servlets. This is because the
technology facilitates the separation of presentation from application logic.
Now, let us examine this premise in detail by developing a global version of the
Music Without Borders online store that was demonstrated in my earlier article
on JSP architecture (see the
Resources section below for a link to this story). The online store is
based on the Model-View-Controller (MVC) architecture to maximize the separation
of presentation from content.
Although internationalizing a JSP-based Web application is not particularly
difficult, it is a task best initiated at the design stage. Toward this end, all
locale-specific textual data that constitute an application's user interface,
such as page headers and trailers, user messages, form button labels, dates,
currencies, and so forth, are first identified and isolated from the application
within external files. These entities, which hold different values under
different locales, are also known as resources; a grouping of resources
within an external class or property file is known as a resource bundle
or, simply, a bundle.
The Java 2 SDK provides the abstract base class
java.util.ResourceBundle for handling resources, along with its
subclasses ListResourceBundle and
PropertyResourceBundle. The bundle for a supported locale can be
defined as either a classfile (by using ListResourceBundle) or as a
property file (by using PropertyResourceBundle). Both these
mechanisms then allow your application to access the values for resources by
name, just as you would with a Hashtable object. Although you can
store objects of any type as values for resources using classfiles, most web
applications use property files, since they almost always customize only string
types. Consequently, I will focus my discussion on the use of property files.
Developing Global Web Applications
In this example, the Music Without Borders online store has been localized to
support users not only in the US, but also in Germany, Sweden, and France.
Listings 1 and 2 show the property files supporting the locales for the US and
France. (You can obtain the property files for Germany and Sweden when you
download the source code for the example in Resources.) As you can see, the property files essentially isolate all
of the GUI elements from the JSP pages. The property files for bundles must
follow some standard naming conventions. The suffix must always be
properties. Also, since a locale is typically defined in Java via a
country code and/or a language code and an optional variant, the name of the
bundle has to correlate with the locale it serves. For instance, all of the
following bundle prefixes are valid:
BundleName + "_" + localeLanguage + "_" + localeCountry + "_" +
localeVariant
BundleName + "_" + localeLanguage + "_" + localeCountry
BundleName + "_" + localeLanguage
BundleName
Observe that the property file serving as the bundle for France (shown in
Listing 2) was named Message_fr_FR.properties. Bundles pertaining
to a specific country need to have both the language code and country code
appear within the prefix. (I could also have used the optional
application-specific variant and further specialized the file as
Message_fr_FR_MAC.properties -- the bundle for all French-speaking
Mac users from France. But I shall resist the temptation!) Similarly, the bundle
containing the German labels is named Message_de_DE.properties, and
so on. Resources contains links for obtaining valid country and language codes.
For the overly curious, the following code shows the source you can use to
create a classfile, serving the same purpose as the property file shown in
Listing 2:
public class Message_fr_FR extends ListResourceBundle {
public Object[][] getContents() {
return contents;
}
static final Object[][] contents = {
{"main.title", "Musique sans fronti?res"},
{"main.subhead", "Sons du village global"},
{"main.addLabel","Ajouter"},
. . .
{"cd.quantityLabel","Quantit?"}
}
}
You might wonder why the property file containing the English labels (shown in
Listing 1) does not contain a country or language code in its prefix. Although I
could have named the bundle Message_en_US.properties, I chose to
simply call it Message.properties. Any bundle that contains neither
country nor language code is treated as the default bundle and serves a specific
purpose: it is picked up as a last resort if there is no other matching bundle
for the requested locale. Observe that the literals used to identify resources
remain the same within the bundles for all locales; only the values change for
the corresponding locale. Now that I have isolated the GUI elements and
localized them within the property files, I'll show how you can use them within
a JSP page.
Listing 1: Message.properties
main.title=Music Without Borders
main.subhead=Sounds from the Global Village
main.addLabel=Add
main.qtyLabel=Quantity
main.bgcolor=#33CCFF
cart.bgcolor=#FFFFFF
cart.delLabel=Delete
cart.checkoutLabel=Checkout
checkout.bgcolor=#33CCFF
checkout.title=Music Without Borders Checkout
checkout.subhead=Thanks for your order!
checkout.totalLabel=Total
checkout.returnLabel=Shop some more!
dollar.exchRate=1.00
cd.albumLabel=Album
cd.artistLabel=Artist
cd.countryLabel=Country
cd.priceLabel=Price
cd.quantityLabel=Quantity
Listing 2: Message_fr_FR.properties
main.title=Musique sans fronti?res
main.subhead=Sons du village global
main.addLabel=Ajouter
main.qtyLabel=Quantit?
main.bgcolor=#33CCFF
cart.bgcolor=#FFFFFF
cart.delLabel=Supprimer
cart.checkoutLabel=Passez ^ la caisse
checkout.bgcolor=#33CCFF
checkout.title= Caisse pour Musique sans fronti?res
checkout.subhead=Merci pour votre commande!
checkout.totalLabel=Total
checkout.returnLabel=Faites d'autres commandes!
dollar.exchRate=6.48
cd.albumLabel=Album
cd.artistLabel=Artiste
cd.countryLabel=Pays
cd.priceLabel=Prix
cd.quantityLabel=Quantit?
The JSP page i18nDemo.jsp (shown in Listing 3) acts as the gateway
to the online store. Its main task is to enable the user to select an
appropriate language for viewing the site, as shown in Figure 1.
Figure 1. Gateway to the internationalized Web application
In Java, the focal point of all internationalization activity is the
java.util.Locale class, which serves to encapsulate the country
code, language code, and the optional variant code. The Locale
class also provides a number of convenient constants representing the most
commonly used locales. Consider the following snippet, which establishes the
locale for the Web application:
Locale locale=null;
if (lang.equals("German")) {
locale=Locale.GERMANY;
} else if (lang.equals("French")) {
locale=Locale.FRANCE;
} else if (lang.equals("Swedish")) {
locale=new Locale("sv","SE");
} else {
locale=Locale.US;
}
While the Locale instance is typically obtained from a predefined
constant within the Locale class, it can also be instantiated using
the class's constructor. You can do this by specifying the language code and
country code (there is also another version of the Locale
constructor that additionally accepts an application-specific variant code). The
language code is two lowercase letters, and the country code is always two
uppercase letters. (As mentioned earlier, you can find a link to lists of valid
country and language code in Resources.)
Since I do not have a predefined constant for Sweden, observe that its
Locale instance is instantiated by explicitly passing the language
and country code as:
locale=new Locale("sv","SE");
Once the locale has been established, you can retrieve its corresponding bundle:
ResourceBundle bundle =
ResourceBundle.getBundle("Message", locale);
You can store the bundles anywhere in the classpath, as understood by your
application server or JSP engine. The getBundle() invocation
initiates the search for the bundle after extracting the language code
(xx, say) and the country code (YY, say) from the
locale object passed as a parameter. (You can also invoke
getBundle() without passing an instance of Locale, in
which case the system default locale is used.) You conduct the search first by
looking for the presence of Message_xx_YY.class within the
classpath. If this is absent, the search tries to locate
Message_xx.class and then Message.class. If there is
no matching classfile, the search proceeds to locate a matching property file
instead, starting with Message_xx_YY.properties, moving to
Message_xx.properties, and finally,
Message.properties. If the lookup fails without a match,
getBundle() throws a MissingResourceException.
Listing 3: i18nDemo.jsp
<%@ page import="java.util.*" %>
<%
String lang = request.getParameter("lang");
if (lang == null) {
%>
<html>
<head>
<title>
Music Without Borders
</title>
</head>
<body bgcolor="#33CCFF">
<font face="Times New Roman,Times" size=+3>
Music Without Borders
</font>
<hr>
<p>
Please select a language:
<form action="/developer/technicalArticles/Programming/jspwebsites/i18nDemo.jsp" method="post">
English <input type="radio" name="lang" value="English" checked>
Deutsch <input type="radio" name="lang" value="German">
Fran?ais <input type="radio" name="lang" value="French">
<p>
<input type="submit" value="Continue">
</form>
</body>
</html>
<%
} else {
Locale locale=null;
if (lang.equals("German")) {
locale=Locale.GERMANY;
} else if (lang.equals("French")) {
locale=Locale.FRANCE;
} else if (lang.equals("Swedish")) {
locale=new Locale("sv","SE");
} else {
locale=Locale.US;
}
session.putValue("myLocale",locale);
ResourceBundle bundle =
ResourceBundle.getBundle("Message",locale);
for (Enumeration e = bundle.getKeys();e.hasMoreElements();) {
String key = (String)e.nextElement();
String s = bundle.getString(key);
session.putValue(key,s);
}
%>
<jsp:forward page="eshop.jsp" />
<%
}
%>
After retrieving the bundle, you obtain the resources for the locale along with
their associated values and place them into the session as:
for (Enumeration e = bundle.getKeys();e.hasMoreElements();) {
String key = (String)e.nextElement();
String s = bundle.getString(key);
session.putValue(key,s);
}
Once you have initialized the session with all the necessary localized
information, the request is forwarded to eshop.jsp (shown in
Listing 4), which facilitates the main view for the online store.
As every good JSP page should, eshop.jsp deals almost exclusively
with presenting the application's UI to the client. In fact, the only processing
it performs is when it formats the price field according to the selected locale.
Take a look at the method computePrice() declared within the page,
which helps handle the display of currencies and formats them in a manner
appropriate to the locale. The real work is done by the
java.text.NumberFormat object for the specified locale obtained
from the getCurrencyInstance() invocation. When the
format() method is invoked on this instance, it returns a
String that includes the correctly formatted number, along with the
appropriate currency sign. Observe that I also make use of the resource
dollar.exchRate to convert the input dollar amount to the
equivalent currency of the locale. Otherwise, the CDs would be a major bargain
in Sweden, for instance, where a dollar is worth about 8.40 Swedish Krona!
You will see that most GUI elements, including header messages, button labels,
and so forth, are locale sensitive. They are set at runtime by interrogating the
session for the appropriate resource:
<title>
<%=session.getValue("main.title")%>
</title>
Listing 4: eshop.jsp
lt;%@ page import="java.util.*, shopping.i18n.CD, java.text.*" %>
<%!
public String computePrice(double price, HttpSession session) {
String exchangeRate = (String)session.getValue("dollar.exchRate");
price*= new Double(exchangeRate).doubleValue();
NumberFormat form = NumberFormat.getCurrencyInstance((Locale)
session.getValue("myLocale"));
return form.format(price);
}
%>
<html>
<head>
<title>
<%=session.getValue("main.title")%>
</title>
</head>
<body bgcolor='<%=session.getValue("main.bgcolor")%>'>
<font face="Times New Roman,Times" size=+3>
<%=session.getValue("main.title")%>
</font>
<br>
<em>
<%=session.getValue("main.subhead")%>
</em>
<hr>
<p>
<center>
<form name="shoppingForm" action="/developer/technicalArticles/Programming/jspwebsites/main.jsp" method="post"
CD:
<select name=CD>
<option>A Toda Cuba le Gusta| Afro-Cuban All Stars | Cuba |
<%=computePrice(16.95,session)%></option>
<option>Vujicsics | Vujicsics | Croatia |
<%=computePrice(14.95,session)%></option>
<option>Kaira | Tounami Diabate| Mali |
<%=computePrice(16.95,session)%></option>
<option>The Lion is Loose | Eliades Ochoa | Cuba |
<%=computePrice(13.95,session)%></option>
<option>Dance the Devil Away | Outback | Australia |
<%=computePrice(14.95,session)%></option>
<option>Record of Changes | Samulnori | Korea |
<%=computePrice(12.95,session)%></option>
<option>Djelika | Tounami Diabate | Mali |
<%=computePrice(14.95,session)%></option>
<option>Wimme | Wimme | Finland |
<%=computePrice(12.95,session)%></option>
<option>Cesaria Evora | Cesaria Evora | Cape Verde |
<%=computePrice(16.95,session)%></option>
<option>Ibuki | Kodo | Japan |
<%=computePrice(13.95,session)%></option>
</select>
<%=session.getValue("main.qtyLabel")%>:
<input type="text" name="qty" size="3" value="1">
<input type="hidden" name="action" value="ADD">
<input type="submit" name="Submit"
value='<%=session.getValue("main.addLabel")%>'>
</form>
</center>
<p>
<jsp:include page="cart.jsp" flush="true" />
</body>
</html>
Also, notice that another JSP page, cart.jsp (shown in Listing 5),
is included within eshop.jsp via the following directive:
<jsp:include page="cart.jsp" flush="true" />
Here, cart.jsp handles the presentation of the session-based
shopping cart, which is implemented by a Vector object. Observe the
scriptlet at the beginning of cart.jsp:
<%
Vector buylist = (Vector) session.getValue("shopping.shoppingcart");
if (buylist != null && (buylist.size() > 0)) {
%>
Basically, the scriptlet extracts the shopping cart from the session. If the
cart is empty or not yet created, it displays nothing; thus, the first time
users access the application, they are presented with the view shown in Figure
2, assuming the users had opted to localize the site for French.
Figure 2. Main view localized for French
Listing 5: cart.jsp
<%@ page import="java.util.*, shopping.i18n.CD" %>
<%
Vector buylist = (Vector) session.getValue("shopping.shoppingcart");
if (buylist != null && (buylist.size() >0)) {
%>
<center>
<table border=0 cellpadding=0 width=100%
bgcolor='<%=session.getValue("cart.bgcolor")%>'>
<tr>
<td><%=session.getValue("cd.albumLabel")%></td>
<td><%=session.getValue("cd.artistLabel")%></td>
<td><%=session.getValue("cd.countryLabel")%></td>
<td><%=session.getValue("cd.priceLabel")%></td>
<td><%=session.getValue("cd.quantityLabel")%></td>
<td></td>
</tr>
<%
for (int index=0; index< buylist.size();index++) {
CD anOrder = (CD) buylist.elementAt(index);
%>
<tr>
<td><%=anOrder.getAlbum()%></td>
<td><%=anOrder.getArtist()%></td>
<td><%=anOrder.getCountry()%></td>
<td><%=anOrder.getPrice()%></td>
<td><%=anOrder.getQuantity()%></td>
<td>
<form name="deleteForm" action="/developer/technicalArticles/Programming/jspwebsites/main.jsp"
method="post">
<input type="submit"
value='<%=session.getValue("cart.delLabel")%>'>
<input type="hidden" name=delindex
value='<%=index%>'>
<input type="hidden" name="action" value="DELETE">
</form>
</td>
</tr>
<% } %>
</table>
<p>
<form name="checkoutForm" action="/developer/technicalArticles/Programming/jspwebsites/main.jsp" method="post">
<input type="hidden" name="action" value="CHECKOUT">
<input type="submit" name="Checkout"
value='<%=session.getValue("cart.checkoutLabel")%>'>
</form>
</center>
<% } %>
If the shopping cart is not empty, the selected CDs are extracted from the cart
one at a time, as demonstrated by the following scriptlet:
<%
for (int index=0; index < buylist.size(); index++) {
CD anOrder = (CD) buylist.elementAt(index);
%>
Once the variable describing an item has been created, it is simply inserted
into the static HTML template using JSP expressions. You can avoid having an
excessive amount of script code when iterating beans with indexed properties by
implementing the looping via custom tags. Figure 3 shows the Swedish view after
the user has placed some items into the shopping cart. Contrast it with the
English view for the same procedure, as shown in Figure 4. Observe how
seamlessly the localization is performed while using the same underlying
application logic.
Figure 3. Shopping cart view localized for Swedish
Figure 4. Shopping cart view localized for English
The important thing to observe here is that the processing for all actions
carried out within either eshop.jsp or cart.jsp is
handled by the controller JSP page, main.jsp, shown in
Listing 6.
Listing 6: main.jsp
<%@ page import="java.util.*, java.text.*, shopping.i18n.CD" %>
<%
String action = request.getParameter("action");
if (action == null) {
%>
<jsp:forward page="i18nDemo.jsp" />
<%
} else {
Vector buylist=
(Vector)session.getValue("shopping.shoppingcart");
if (!action.equals("CHECKOUT")) {
if (action.equals("DELETE")) {
String del = request.getParameter("delindex");
int delIndex = (new Integer(del)).intValue();
buylist.removeElementAt(delIndex);
} else if (action.equals("ADD")) {
//any previous buys of same cd?
boolean match=false;
%>
<jsp:useBean id="aCD" class="shopping.i18n.CD" scope="page">
<% aCD.setCDProperties(request); %>
</jsp:useBean>
<%
if (buylist==null) {
//add first cd to the cart
buylist = new Vector(); //first order
buylist.addElement(aCD);
} else { // not first buy
for (int i=0; i< buylist.size();i++) {
CD cd = (CD) buylist.elementAt(i);
if (cd.getAlbum().equals(aCD.getAlbum())) {
int tmpQty =
(new Integer(cd.getQuantity())).intValue() +
(new Integer(aCD.getQuantity())).intValue();
cd.setQuantity(new Integer(tmpQty).toString());
buylist.setElementAt(cd,i);
match = true;
} //end of if name matches
} // end of for
if (!match) buylist.addElement(aCD);
} //end of not first buy
} //end of action==ADD
session.putValue("shopping.shoppingcart", buylist);
%>
<jsp:forward page="eshop.jsp" />
<%
} else { // if checkout
NumberFormat nFormat = NumberFormat.getCurrencyInstance(
(Locale)session.getValue("myLocale"));
float total =0;
for (int i=0; i< buylist.size();i++) {
CD anOrder = (CD) buylist.elementAt(i);
Number n = nFormat.parse(anOrder.getPrice().trim());
float price= n.floatValue();
int qty = (new Integer(anOrder.getQuantity())).intValue();
total += (price * qty);
}
String amountDue=nFormat.format(total);
request.setAttribute("amountDue",amountDue);
%>
<jsp:forward page="checkout.jsp" />
<%
}
}
%>
If the user tries to add or delete an item, or checks out, the request is posted
to the controller, main.jsp. This controller page handles addition
requests initiated from eshop.jsp, as well as deletion and checkout
requests triggered from cart.jsp. If the request is an addition, for instance,
the controller processes the request parameters for the item to be added, and
then instantiates a new CD bean (shown in Listing 7) representing the selection.
The updated shopping-cart object is then placed back within the session. The
controller is also enabled with enough smarts to understand that if a previously
added CD is reselected, the controller should simply increase the count for that
CD bean within the shopping cart. Changes affecting the state of the shopping
cart, such as an addition or deletion, cause the controller to forward the
request after processing to eshop.jsp. This, in turn, redisplays
the main view, along with the updated contents of the shopping cart. If the user
decides to check out, the request is forwarded after processing to
checkout.jsp, shown in Listing 8.
A big advantage of having a separate controller page such as this one is that
you can always exercise complete control over the resources that should be
invoked in response to specific actions. In fact, you can further isolate this
by having the controller initialize itself via a special property file
containing target resources for specific user actions. That way, you can
completely externalize the maintenance of your Web application and gain maximum
flexibility.
Listing 7: CD.java
package shopping.i18n;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.*;
public class CD {
String album;
String artist;
String country;
String price;
String quantity;
public CD() {
album="";
artist="";
country="";
price="";
quantity="";
}
public void setCDProperties(HttpServletRequest req) {
String myCd = req.getParameter("CD");
StringTokenizer t = new StringTokenizer(myCd,"|");
this.album= t.nextToken();
this.artist = t.nextToken();
this.country = t.nextToken();
this.price = t.nextToken().trim();
this.quantity = req.getParameter("qty");
}
public void setAlbum(String title) {
album=title;
}
public String getAlbum() {
return album;
}
public void setArtist(String group) {
artist=group;
}
public String getArtist() {
return artist;
}
public void setCountry(String cty) {
country=cty;
}
public String getCountry() {
return country;
}
public void setPrice(String p) {
price=p;
}
public String getPrice() {
return price;
}
public void setQuantity(String q) {
quantity=q;
}
public String getQuantity() {
return quantity;
}
}
The checkout.jsp page simply extracts the shopping cart from the
session and the total amount for the request, and then displays the selected
items and their total cost. Figure 5 shows the client view upon checkout,
localized for German.
Figure 5. Checkout view localized for German
Once the user goes to the checkout counter, it is equally important to get rid
of the session object. That is taken care of by having a
session.invalidate() invocation at the end of the page. This
process is necessary for two reasons. First, if the session is not invalidated,
the user's shopping cart is not reinitialized; if the user then attempts to
commence another round of shopping upon checkout, his shopping cart will
continue to hold items that he has already purchased. The second reason is that
if the user simply left the site upon checkout, the session object will not be
garbage collected and will continue to take up valuable system resources until
its lease period expires. Since the default session-lease period is about 30
minutes, in a high-volume system this can quickly lead to the system's running
out of memory.
Listing 8: checkout.jsp
<%@ page import="java.util.*, shopping.i18n.CD" %>
<html>
<head>
<title>
<%=session.getValue("checkout.title")%>
</title>
</head>
<body bgcolor='<%=session.getValue("checkout.bgcolor")%>'>
<font face="Times New Roman,Times" size=+3>
<%=session.getValue("checkout.title")%>
</font>
<p> <hr>
<em><%=session.getValue("checkout.subhead")%></em>
<p>
<center>
<table border="0" cellpadding="0" width="100%"
bgcolor='<%=session.getValue("cart.bgcolor")%>'>
<tr>
<td><%=session.getValue("cd.albumLabel")%></td>
<td><%=session.getValue("cd.artistLabel")%></td>
<td><%=session.getValue("cd.countryLabel")%></td>
<td><%=session.getValue("cd.priceLabel")%></td>
<td><%=session.getValue("cd.quantityLabel")%></td>
<td></td>
</tr>
<%
Vector buylist=(Vector)session.getValue("shopping.shoppingcart");
for (int i=0; i< buylist.size();i++) {
CD anOrder = (CD) buylist.elementAt(i);
%>
<tr>
<td><%=anOrder.getAlbum()%></td>
<td><%=anOrder.getArtist()%></td>
<td><%=anOrder.getCountry()%></td>
<td><%=anOrder.getPrice()%></td>
<td><%=anOrder.getQuantity()%></td>
</tr>
<% } %>
<tr>
<td> </td>
<td> </td>
<td><%=session.getValue("checkout.totalLabel")%></td>
<td><%=request.getAttribute("amountDue")%></td>
<td> </td>
</tr>
</table>
<p>
<a
href=i18nDemo.jsp><%=session.getValue("checkout.returnLabel")%<>/a&g
t;
</center>
<%
session.invalidate();
%>
</body>
</html>
Deploying the Internationalized Web Application
I will assume you are using the latest version of JavaServer Web Development Kit
(JSWDK) from Sun for running the example. If not, see Resources to find out where to get it. Assuming the server is installed
in \jswdk-1.0.1, its default location in Microsoft Windows, deploy
the Music Without Borders application files as follows:
- Create a
i18n directory under
\jswdk-1.0.1\examples\jsp
- Copy
i18nDemo.jsp to
\jswdk-1.0.1\examples\jsp\i18n
- Copy
eshop.jsp to \jswdk-1.0.1\examples\jsp\i18n
- Copy
cart.jsp to \jswdk-1.0.1\examples\jsp\i18n
- Copy
main.jsp to \jswdk-1.0.1\examples\jsp\i18n
- Copy
checkout.jsp to
\jswdk-1.0.1\examples\jsp\i18n
- Compile
CD.java by typing javac CD.java
- Create a
shopping\i18n directory under
\jswdk-1.0.1\examples\Web-Inf\jsp\beans
- Copy
CD.class to
\jswdk-1.0.1\examples\Web-Inf\jsp\beans\shopping\i18n
Once your server has been started, you should be able to access the application
using http://localhost:8080/examples/jsp/i18n/i18nDemo.jsp as the URL.
Speaking of characters
Throughout this example, I made use of the default 8-bit ISO Latin-1 character
set (aka ISO-8859-1), since it supports the 200 or so characters common to most
Western European languages. But to develop truly international applications, you
would need to use a character set capable of supporting languages such as
Chinese, Arabic, Japanese, and so forth, whose range of characters far exceeds
the 256-character limit of ISO Latin-1. You can obtain a list of character
encodings supported by Java from Resources. If you are using something other than the ISO-8859-1
character set, you must communicate this to the browser using the
contentType attribute of the page tag, as:
<%@ page contentType="text/html; charset=charset_name" %>
For example, if you generated your dynamic content in Cyrillic (for a page in
Russian or Bulgarian, for example), your JSP would have to communicate that fact
to the browser as:
<%@ page contentType="text/html; charset=ISO-8859-5" %>
Of course, in this case, your users will also need a browser that supports the
new character set. Toward this end, you must ensure that the browsers are HTML
4.0 compliant, since HTML 4.0 supports Basic Multilingual Plane, a standardized
16-bit character set that supports most of the world's languages. Additionally,
it is important that the browser is also enabled with the requisite font to
display the characters of your target language.
Conclusion
In this article, I have examined the development of multilocale JSP pages.
Websites based on JSP technology lend themselves more naturally to
internationalization because of the ease with which the presentation view can be
separated from application logic. Developing a truly global site is, however, a
challenging task, due to the complexities introduced by diverse character sets
and erratic browser support.
The authors of this month's server-side Java computing articles will be
holding a free online seminar on March 9 at 10:00 a.m. PST. Register to join
at http://seminars.jguru.com.
Reprinted with permission from the March 2000 edition of JavaWorld magazine. Copyright Web
Publishing Inc., an IDG Communications company. Regis
ter for editorial e-mailalerts
About the Author
Govind Seshadri is an enterprise Java guru for jGuru.com,
and the author of Enterprise Java Computing--Applications and Architecture
from Cambridge University Press (1999). Learn more about Govind at jGuru.com.
|