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.