This is an example of an internationalized (I18N) and localizable (L10N) Struts2 Application.
Download: Struts2 Application in Eclipse Project - struts2-i18n.zip
Demonstration: This sample application is running here -> http://www.ssjava.net/struts2-i18n/ (for some time)
Highlights of the sample Struts2 application are:
I describe an overview of the sample, explanation of topics shown above and finally some notes about constituent files.
Apache Struts is a Web application framework donated to Apache Foundation in 2000. Its version 2 (Struts2) appeared in 2007 and has greatly changed from the previous version (Struts1). As the official site states "Apache Struts 2 was originally known as WebWork 2," the core is now a different product.
The initial purposes of creating this sample app are:
In that process, I could exmamine several methods for input value validation mechanisms too.
Conclusion is that most of the tests I tried worked as expected without complicated settings. I was impressed by Struts2 as opposed to Struts1 which I didn't feel very synpathetic.
I used the following tutorial sample as a starting point of this sample application:
Basic Struts 2 Application That Uses Ant and Struts 2 Version 2.3.1.2
(2012 Jan version of this)
I mainly used the following site for information:
This sample app contains actions of two major types.
The first one is a simple example.
It receives a message (a single string), adds another string to it and displays it.
This type of actions are contained in sample.action.simple package.
The other is a little bit complex example.
This also recieves and displays a message.
It receives more than one input field like a submitter of the message.
The input fields are created as a separate model class.
These actions are contained in sample.action.complex package.
An action mapping specifies relationship between the following three things:
Struts 2 provides the following three ways for specifying an action mapping. You can mix these methods.
struts.xml in Struts2); a traditional way.
You need an additional plugin JAR in order to use annotation convention (struts2-convention-plugin-x.x.x.jar).
This sample exclusively uses the third method, convention, as a method of action mapping.
No struts.xml nor annotations are used for action mappings in this sample application.
Briefly stated, a URL associated with an action is determined in conventional action mapping:
See the following for detailed rules of conventional mapping:
Apache Struts 2 Documentation - Convention Plugin (2.3.3)
Config Browser Plugin allows you to check the current action mappings -> Browse mappings of this sample
I tested the following three patterns of conventional mappings in this application:
http://www.example.com/struts-i18n/index -> /index.jsp
/hello -> /hello.jsp
/hello-in-free-marker -> /hello-in-free-marker.ftl
/hello-in-velocity -> /hello-in-velocity.vm
/simple/simple -> sample.action.simple.Simple.class -> /simple/simple.jsp
/simple/simple -> sample.action.simple.Simple.class -> /simple/simple.jsp
/simple/simple-in-free-marker -> sample.action.simple.SimpleInFreeMarker.class
-> /simple/simple-in-free-marker.jsp
/simple/simple-in-free-marker -> sample.action.simple.SimpleInVelocity.class
-> /simple/simple-in-velocity.jsp
/complex/object-backed-input.jsp
--> sample.action.complex.ObjectBacked.class
<--
/complex/object-backed-input.jsp
--> sample.action.complex.ObjectBacked.class
--> /complex/object-backed-sucess.jsp
/complex/object-backed-input.jsp
--> sample.action.complex.ModelBased.class
<--
/complex/object-backed-input.jsp
--> sample.action.complex.ModelBased.class
--> /complex/object-backed-sucess.jsp
Here are some notes and tips:
/hello instead of /hello.action./hello.action, an action class of sample.action.Hello or sample.action.HelloAction is executed if it exists. Otherwise, hello.jsp is executed if it exists. I placed hello-success.jsp but Struts2 did not find it./hello.action. However, you can name the class just "Hello" if it implements Action interface (you can do so by exending ActionSupport).HelloAction.java) returns "success," hello-success.jsp is shown if it exists, otherwise hello.jsp is shown if it exists.In Struts2, in addition to JSP, FreeMarker and Velocity templates can be used as a view page. You can mix these in a single application. FreeMarker is supported without extra setup because it is used by Struts2 internally to convert Struts tags to HTML code. You can use Velocity by adding the required JARs (listed later).
In conventional action mapping, a view page or template file with a certain extention is selected based on a name of a determined result page.
In a test with the default setting, Struts2 chose in order of Velocity, JSP, FreeMarker when I put these files with the same result page name.
/hello.jsp + /hello.ftl + /hello.vm
/hello.jsp + /hello.vm
/hello.ftl + /hello.vm
/hello.vm takes precedence in all three cases
/hello.jsp + /hello.ftl
/hello.jsp selected
if there is only a sigle page having /hello.???,
that type is selected
Next, I describe the main subject, Internationalization (I18N) and Localzation (L10N) of a Struts2 application.
Java already provides a mechanism for preparing string resources for each language in property files grouped as a resource bundle and automatically selecting a string resource of a currently used language runtime based on a locale. A Struts2 application can use the mechanism in the same way as ordinary Java applications. Struts tags are provided for referencing string resources from a view page. Struts tags can be used in a FreemMarker or Velocity template as well as a JSP page.
In Struts2, string resources (Java property files) can be prepared in a multi-level hierarchy. Major levels are shown below in order of reference priority:
| Resource | Location | Referenced from: |
| action-class.properties and action-class_xx.properties | Within a package where the class resides | From the specific class only |
| package.properties and package_xx.properties | Within a package | All classes in the package |
| global.properties and global_xx.properties | WEB-INF/classes | Entire application |
In order to use global(_xx).properties, you must specify its base name of the files in struts.xml as:
<constant name="struts.custom.i18n.resources" value="global" />
See the following for details of reference level to a string resource:
Apache Struts 2 Documentation - Localization
I tested and made sure that a string is taken from a resource file at an appropriate level.
Assume action.sample.Simple action case.
If I referenced a string within a code of the action class or in the result view page, Simple.properties (or Simple_xx.properties) is checked first, followed by action.sample.package.properties (or package_xx.properties) and finally global.properties or (global_xx.properties) to extract a string of a specified key.
However, I experienced a problem with an input page.
If the input page is displayed as a result of validation that detected an error in an input field after the action class is executed, a string is extracted from a resource file at an appropriate level.
But in case the input page is first called, that page is not associted with any action class, thus only strings in the global(_xx).properties can be referenced.
I wish there is a solution to this problem but I am not sure there is.
I describe several ways to retrieve a string from a resource file associated with a language currently used.
Let's take global.properties as an example.
The strings written here are default and used when global_xx.properties for a language specified in a request from a user does not exist.
In this sample, english strings are written in this file.
title=I18N Sample with Struts2 message=Message submit=Submit is_what_you_typed=is what you typed. back=Go back
The Japanese string resource file name should be global_ja.properties.
You have to convert this file to a format that uses unicode escapes such as "\u306E" with native2ascii.
Not very useful, but you can use an Eclipse property editor to enter Japanese directly.
If you commit kana-kanji conversion, the characters changed to escaped codes.
You can first enter text in a text editor then paste it into the Eclipse property editor.
You can get a string in a Java source code by specifying a key with getText():
public String execute()
{
message = "'" + message + "' " + getText("is_what_you_typed");
}
The resulted views are:
In English
In Japanese
Struts tags can be used to reference these strings in a JSP page:
<h2><s:text name="title"/></h2> <s:form action="simple" method="post"> <s:textfield key="message" /> <s:submit key="submit" /> </s:form>
There are differences in Struts tag syntax in FreeMarker and Velocity:
FreeMarker
<h2><@s.text name="title"/></h2> <@s.form action="simple" method="post"> <@s.textfield key="message" /> <@s.submit key="submit" /> </@s.form>
Velociy
<h2>#stext ("name=title")♂</h2> #sform ("action=simple method=post") #stextfield ("key=message") #ssubmit ("key=submit") #end
Here are display examples where the language setting of the web browser is english or japanese:
In English
In Japanese
As you can see in this example, you can force a language to use regardless of the browser setting:
<a href="<s:url action='/index.action?request_locale=en'/>">English</a><br/>
<a href="<s:url action='/index.action?request_locale=ja'/>">Japanese</a>
Input value check is closely related to localization. We need to display a label of an erroneous input value and an error message.
Struts2 provides three methods for validation of form input values. The three methods can be used together.
validate method in an action class
In Struts2, you can place in the action class itself a set method for receiving a form input value and a get method for passing a result value to a view page. You also have a choice to write an independent model class and place get/set methods in it.
I tested the above three validation methods in the following three variations of form value placement:
ModelDriven.getModel method of an action class (sample.action.complex.ModelBased.java and sample.model.Message.java)
Among these, I removed validation code for Simple.java for simplicity but validation code for the remaining two classes are not deleted.
The next display is object-backed-input.jsp shown after I clicked Submit button when I typed nothing in every field.
Validation errors are shown for the first three fields:
In English
In Japanese
The test result is that all combinations but one worked as expected.
The exception is an annotated validation for a field in a model class with the last getModel case.
I show some typical validation specification examples:
validate method in Action class
If you write validate method in an Action class, Struts2 calls it before calling execute method.
You can check data here and calls addFieldError() when you find some error, Struts2 treats as if the action class returns the result string "input."
As a result, the corresponding input page is displayed with one or more error messages.
public void validate()
{
String submitter = message.getSubmitter();
if (submitter == null || submitter.length() == 0)
{
addFieldError("message.submitter", getText("message_submitter_missing"));
}
}
Here, message.submitter is a field label while message_submitter_missing is an error message.
Both are keys to a string resource and Struts will get strings from the current language resource file.
Prepare an XML file associated with an action class containing validation information (ActionClassName-validation.xml).
Example: ObjectBacked-validation.xml (validation XML for ObjectBacked action class)
<validators>
<validator type="requiredstring">
<param name="fieldname">message.text</param><!-- validates 'text' -->
<message key="message_text_missing"/>
</validator>
</validators>
message.text is a resource key for a field label while message_text_missing is a key to an error message.
Validation instruction can be written immediately before set method of an input value.
private String message;
@RequiredStringValidator(key = "message_missing")
public void setMessage(String message)
{
this.message = message;
}
This is the end of description of validation.
Here is a list of files that comprise the sample application.
+ src
+ sample
+ action
+ simple
package.properties
package_ja.properties
Simple.java
SimpleInFreeMarker.java
SimpleInVelocity.java
+ complex
package.properties
package_ja.properties
ObjectBacked.java
ObjectBacked-validation.xml
ObjectBacked.properties
ObjectBacked_ja.properties
ModelBased.class
ModelBased-validation.xml
+ model
Message.java
Message.properties
Message_ja.properties
+ WebContent
+ WEB-INF
web.xml
+ content
index.jsp
hello.jsp
hello-in-free-marker.ftl
hello-in-velocity.vm
+ simple
simple.jsp
simple-in-free-marker.ftl
simple-in-velocity.vm
+ complex
object-backed-input.jsp
object-backed-success.jsp
model-based-input.jsp
model-based-success.jsp
+ classes
global.properties
global_ja.properties
struts.xml
+ lib
asm-3.3.jar
asm-commons-3.3.jar
asm-tree-3.3.jar
commons-collections-3.2.jar
commons-fileupload-1.2.2.jar
commons-io-2.0.1.jar
commons-lang-2.6.jar
commons-lang3-3.1.jar
freemarker-2.3.19.jar
javassist-3.11.0.GA.jar
ognl-3.0.5.jar
struts2-config-browser-plugin-2.3.3.jar
struts2-convention-plugin-2.3.3.jar
struts2-core-2.3.3.jar
velocity-1.7.jar
velocity-tools-view-2.0.jar
xwork-core-2.3.3.jar
The project file of this sample application does not include the necessary JARs such as Struts2.
If you like to try this sample by compiling and running the project, not just looking at source files, please add the necessary JARs to the project.
In my case,
struts-2.3.3-all.zip (latest at this time) from Apache Struts,
WEB-INF/lib in apps/struts2-blank.war, and
lib:
You only have to add these lines to web.xml in order to use Struts2.
This makes every request to reach the Struts2 filter.
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
I put the Struts2 configuration file, struts.xml, in WEB-INF/classes.
It is so simple because I don't have to write any action mappings.
<struts>
<constant name="struts.devMode" value="true" />
<constant name="struts.custom.i18n.resources" value="global" />
</struts>
It specifies debug mode and use of global(_xx).properties.
According to the Struts2 document, Struts2 requires Servlet API 2.4 or higher, JSP 2.0 or higher, and Java 5 or higher. This means you can use Tomcat 5.5.x or higher. I used 5.5.23 with some reason. I didn't tested this sample with 6.x or 7.x. I used Java 6.0.
Other localization samples provided in this site are:
Kobu.Com has a lot of experiences in authoring software and documentation usable world-wide.
Please feel free to contact us.
Copyright © 2012 Kobu.Com. All rights reserved.
Presented by: Kobu.Com
Author: ARAI Bunkichi
Written: 2012 May 29
The published sample code is a prototype and is not complete.
Please refrain from duplicating the sample code and its document in another place.
This page is link-free. We welcome your questions and comments.