Struts2 Resource Bundles and Localization Example

Filed Under: Struts 2

Struts2 framework supports internationalization and we can create resource bundle property files to be used by the framework. Struts2 i18n is used a lot in creating labels based on the locale in result pages using UI tags or to show action messages or errors when action classes implement ValidationAware interface or extend ActionSupport class.

Struts2 framework supports i18n through I18nInterceptor interceptor and we can pass locale in request with parameter request_locale. This interceptor is part of defaultStack interceptor stack, so we don’t need to do anything for localization. However we can override locale request parameter with parameterName parameter, we will look into this in our sample project.

When we pass localization key, Struts 2 framework looks for resource bundles at various places in below order:

  • {ActionClassName}.properties and it should be in the same package with Action Class.
  • Interface.properties (every interface and sub-interface)
  • BaseClass.properties (all the way to Object.properties)
  • ModelDriven’s model (if implements ModelDriven)
  • package.properties in the class package and then to the parent packages till the root
  • global resource properties configured in struts property file

It’s good to have options but excessive options can lead to confusion. Also if we have so many property files for localization, it will add IO cost in searching the keys into them. Personally I always create only package and global properties files but sometimes when we need to show message specific to an action class, action class property files come handy.

We can get localized text by following options:


<s:property value="getText('key')" />

Above method can be used when action class is extending ActionSupport class, getText() method is defined in ActionSupport class.


<s:text name="key">Default Text</s:text>

We can use Struts text tag to get the localized string, if the key is not found then “Default Text” will be used.


<s:i18n name="global">
	<s:text name="key"></s:text>
</s:i18n>

We can use i18n tag to specify the location of resource bundle from where the properties will be loaded. In above case, it will try to get key value from global.properties from WEB-INF/classes directory. If we know the location of keys, then it’s good to use i18n tag for better performance.


<s:textfield key="mykey" name="textfieldName"/>

Most of the UI tags have key attribute that we can use to get the localized text from resource bundle.

Let’s look into a simple web application where we are using Struts2 resource bundle and i18n support for generating localized response. Our final project will look like below image.

Struts2-localization-project

For our example, we will have localized texts for fr_FR and de_DE locales and we have ActionClass, package and global resource bundles. As you can see from above image that Action Class property files are in the same package whereas package property files are in the parent package. Global resource bundles are in the classes directory and we will configure it in struts configuration file.

Configuration Files

web.xml


<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
  <display-name>Struts2Localization</display-name>

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

</web-app>

pom.xml


<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>Struts2Localization</groupId>
  <artifactId>Struts2Localization</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>
  <build>
    <plugins>
      <plugin>
        <artifactId>maven-compiler-plugin</artifactId>
        <version>3.1</version>
        <configuration>
          <source>1.6</source>
          <target>1.6</target>
        </configuration>
      </plugin>
      <plugin>
        <artifactId>maven-war-plugin</artifactId>
        <version>2.3</version>
        <configuration>
          <warSourceDirectory>WebContent</warSourceDirectory>
          <failOnMissingWebXml>false</failOnMissingWebXml>
        </configuration>
      </plugin>
    </plugins>
    <finalName>${project.artifactId}</finalName>
  </build>
  <dependencies>
  	<dependency>
  		<groupId>org.apache.struts</groupId>
  		<artifactId>struts2-core</artifactId>
  		<version>2.3.15.1</version>
  	</dependency>
  </dependencies>
</project>

Deployment descriptor and maven pom.xml files are simple and used to configure web application to use Struts2 framework.

struts.xml


<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC
	"-//Apache Software Foundation//DTD Struts Configuration 2.3//EN"
	"http://struts.apache.org/dtds/struts-2.3.dtd">
<struts>
	<!-- constant to define result path locations to project root directory -->
	<constant name="struts.convention.result.path" value="/"></constant>
	
	<!-- constant to define global resource bundle -->
	<constant name="struts.custom.i18n.resources" value="global"></constant>
	
	<package name="user" namespace="/" extends="struts-default">
		<action name="home">
			<result>/home.jsp</result>
		</action>
		<action name="welcome" class="com.journaldev.struts2.actions.WelcomeAction">
			<interceptor-ref name="defaultStack">
				<param name="i18n.parameterName">appLocale</param>
			</interceptor-ref>
			<result name="success">/welcome.jsp</result>
		</action>
	</package>

</struts>

We are providing global resource bundle name as global and overriding the i18n interceptor locale request parameter to appLocale, we will use this in our request form parameters for our use case.

Action Class


package com.journaldev.struts2.actions;

import com.opensymphony.xwork2.ActionSupport;

public class WelcomeAction extends ActionSupport {

	@Override
	public String execute() {
		return SUCCESS;
	}

	private String username;
	private String address;
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	
}

Action class is simple and have only few java bean properties with getter and setter methods.

Resource Bundles

Global Resource Bundles

global.properties


#global labels
global.username=User Name
global.address=Address
global.submit=Submit
global.selectlocale=Select Locale

action.welcome.thankyou=Thank You

global_fr_FR.properties


#global labels
global.username=User Name (FR)
global.address=Address (FR)
global.submit=Submit (FR)
global.selectlocale=Select Locale (FR)

action.welcome.thankyou=Thank You (FR)

global_de_DE.properties


#global labels
global.username=User Name (DE)
global.address=Address (DE)
global.submit=Submit (DE)
global.selectlocale=Select Locale (DE)

action.welcome.thankyou=Thank You (DE)

Package Resource Bundles

package.properties


action.welcome.title=Welcome Page
action.welcome.thankyou=Thank You!!

package_fr_FR.properties


action.welcome.title=Welcome Page (FR)
action.welcome.thankyou=Thank You!! (FR)

package_de_DE.properties


action.welcome.title=Welcome Page (DE)
action.welcome.thankyou=Thank You!! (DE)

Action Class Resource Bundles

WelcomeAction.properties


#action specific labels
action.welcome.username=User Name
action.welcome.address=Address

WelcomeAction_fr_FR.properties


#action specific labels for locale fr_FR
action.welcome.username=User Name (FR)
action.welcome.address=Address (FR)

WelcomeAction_de_DE.properties


#action specific labels for locale de_DE
action.welcome.username=User Name (DE)
action.welcome.address=Address (DE)

JSP Pages

home.jsp


<%@ page language="java" contentType="text/html; charset=US-ASCII"
    pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=US-ASCII">
<title>Struts2 Localization Example</title>
</head>
<body>
<s:form action="welcome">
<s:textfield key="global.username" name="username"></s:textfield>
<s:textfield key="global.address" name="address"></s:textfield>
<s:select list="{'en_US','fr_FR','de_DE'}" name="appLocale" key="global.selectlocale"></s:select>
<s:submit key="global.submit" name="submit"></s:submit>
</s:form>
</body>
</html>

As you can see that we can provide locale from the select box in home.jsp and the variable name is same as configured in struts property file for locale parameter.

welcome.jsp


<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@ taglib uri="/struts-tags" prefix="s" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title><s:property value="getText('action.welcome.title')"/></title>
</head>
<body>
<s:property value="getText('action.welcome.username')"/>: <s:property value="username"/><br>
<s:text name="action.welcome.address"></s:text>: <s:property value="address"/><br><br>

<s:i18n name="global">
	<s:text name="action.welcome.thankyou"></s:text>
</s:i18n>
</body>
</html>

welcome.jsp is showing usage of localization keys through i18n, text and property tag.

Now when we run our application, we get following response pages.

Struts2-localization-request

Struts2-localization-response-de_DE

Struts2-localization-response-fr_FR

That’s all for Struts2 internationalization and resource bundle example, it’s very simple and easy to use. Make sure you have properly designed the application resource bundles otherwise it will become hard to maintain if the number of files are too much.

Download project from below link and play around with it for better understanding.

Comments

  1. SenthilKumar says:

    How about the images will load based on the locale ?

  2. Anish says:

    Localization does not work on page load. I have kept English and French in my page, but when I load the page it does not take user defined key-value pairs. In place of label, key is shown but when I click on English or French than it shows the correct value.

Leave a Reply

Your email address will not be published. Required fields are marked *

close
Generic selectors
Exact matches only
Search in title
Search in content
Search in posts
Search in pages