Home > i18n / struts2 | Japanese



../../images/kobu-big-en.png

../../images/phone.gif



Struts2 Application
Localization Sample



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.

Introduction

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:

Introducing the Sample App

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.

images/simple-entry-en.png

images/simple-view-en.png

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.

images/complex-entry-en.png

Action Mapping

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.

You need an additional plugin JAR in order to use annotation convention (struts2-convention-plugin-x.x.x.jar).

Convention

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

Examples of Conventional Mappings

I tested the following three patterns of conventional mappings in this application:

A view page is directly referenced (no action)

  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

A result page is displayed via an action

  /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

There can be a return to an input page due to validation

  /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:

URL suffix of ".action" can be omitted
You can use /hello instead of /hello.action.
An action class can be omitted
Against a URL of /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.
Class name suffix of "Action" can be omitted
A class named "HelloAction" will be selected against /hello.action. However, you can name the class just "Hello" if it implements Action interface (you can do so by exending ActionSupport).
View page name suffix of "success" can be omitted
If an action class (for example HelloAction.java) returns "success," hello-success.jsp is shown if it exists, otherwise hello.jsp is shown if it exists.

Templates

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

Reference Levels of String Resources

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:

ResourceLocationReferenced from:
action-class.properties and action-class_xx.propertiesWithin a package where the class residesFrom the specific class only
package.properties and package_xx.propertiesWithin a packageAll classes in the package
global.properties and global_xx.propertiesWEB-INF/classesEntire 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.

Examples of String Resource Access

I describe several ways to retrieve a string from a resource file associated with a language currently used.

global_xx.properties

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.

images/global-ja-properties.png

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.

getText

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

images/simple-jsp-en.png

In Japanese

images/simple-jsp-ja.png

Struts tags

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

images/index-jsp-en.png

In Japanese

images/index-jsp-ja.png

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>

Validation of Input Values

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.

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:

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

images/complex-input-en.png

In Japanese

images/complex-input-ja.png

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.

Validation Example

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.

Validation XML

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.

Annotation

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.

Files in the Sample Application

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

JARs Used

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,

web.xml

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>

struts.xml

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.

Tomcat Version Used

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.

Notice

Other localization samples provided in this site are:

Localizing CakePHP 2.x Application
A web application written in the latest CakePHP 2.x tested for localization, naming conventions, Bake generator and authentication component.
Localizing Android Application
Android application with automatic switching between Japanese and English.

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.