Most of enterprise applications should support internationalization and themes. I mean they should prepare users to change the application language and also select a themplate or even customize it.Spring has a powerful features to support them. Here I am going to take a sample of spring internationalization and themes.
Application Specification
The sample application is an extension of spring security sample. A user can login to it then a list of users will be displayed and if he or she has administrator or power user role, can change selected user's current theme and language in edit mode. Take a look to the following picture.
Image 1 |
System has 3 themes:
- Default(Gray) Theme: Gray background color
- Green Theme: Green background color
- Blue Theme: Blue background color
- Default(United States)
- Farsi
Image 2: after selecting blue theme and Farsi language |
I added two more extera fields in User Entity (refer to spring security post). theme and locale:
@Entity
@Table(name = "tb_user")
public class User implements UserDetails {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
@Column(name = "id")
Long id;
@Column(name = "name")
String name;
@Column(name = "username")
String username;
@Column(name = "password", updatable = false)
String password;
@Column(name = "theme")
String theme;
@Column(name = "locale")
String locale;
@ManyToMany(targetEntity = Role.class, cascade = {CascadeType.PERSIST, CascadeType.MERGE}, fetch = FetchType.EAGER)
@JoinTable(name = "tb_user_role", joinColumns = {@JoinColumn(name = "user_id")}, inverseJoinColumns = {@JoinColumn(name = "role_id")})
@Fetch(org.hibernate.annotations.FetchMode.SUBSELECT)
List<Role> roleList;
@Column(name = "account_non_expired")
boolean accountNonExpired;
@Column(name = "account_non_locked")
boolean accountNonLocked;
@Column(name = "credentials_non_expired")
boolean credentialsNonExpired;
@Column(name = "enabled")
boolean enabled;
...
}
Now it's the time to explain about spring internationalization and themes.
Spring Internationalization
First we should prepare some different languages property files with the same master name with language brief name in continue. For example if the master name is messages, for United States: messages_en and for Farsi: messages_fa. Then we should place the keys and their values in them. Take a look this:
messages_en:
user.name=name
user.username=username
user.password=password
user.theme=user custom theme
user.locale=user language
user.accountNonExpired=account non expired
user.accountNonLocked=account non locked
user.credentialsNonExpired=credentials non expired
user.enabled=enabled
user.authorities=user authorities
welcome=welcome
save=save
back=back
edit=edit
view=view details
add=insert
delete=delete
search=search
login=login
logout=logout
messages_fa:
user.name=نام کاربر
user.username=نام کاربري
user.password=کلمه عبور
user.theme=قالب جاري کاربر
user.locale=زبان جاري کاربر
user.accountNonExpired=حساب کاربری باطل نشده
user.accountNonLocked=حساب کاربری قفل نشده
user.credentialsNonExpired=اعتبار نامه کابر باطل نشده
user.enabled=فعال
user.authorities=اجازه های دسترسی کاربر
welcome=خوش آمدید
save=ذخیره
back=بازگشت
edit=ویرایش
view=نمایش جزئیات
add=افزودن
delete=حذف
search=جستجو
login=ورود
logout=خروج
The next step is spring configuration. I make an xml file called internationalization-config.xml and import it in major spring configuration(spring-servlet.xml). Here is the configuration:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<beans:bean id="messageSource"
class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
<beans:property name="basename" value="classpath:messages"/>
<beans:property name="defaultEncoding" value="UTF-8"/>
</beans:bean>
<beans:bean id="localeResolver"
class="com.internationalization.CustomLocaleResolver">
<beans:property name="defaultLocale" value="en"/>
</beans:bean>
</beans:beans>
Then I should define the localeResolver to determine current locale so I prepare CustomLocaleResolver for that. This class will find the current user and returns its current locale otherwise returns the default locale(United States). Every localeResolver classes should be instance of org.springframework.web.servlet.LocaleResolver interface. But there is a useful abstract class called "AbstractLocaleResolver" is an instance of LocaleResolver interface. It has good properties like "defaultLocale", so I extend CustomLocaleResolver class from AbstractLocaleResolver class. Here is the code:
public class CustomLocaleResolver extends AbstractLocaleResolver {
public Locale resolveLocale(HttpServletRequest httpServletRequest) {
User user = SecurityUtils.getCurrentUser();
if (user != null && user.getLocale() != null && !user.getLocale().equals("")) {
return new Locale(user.getLocale());
} else {
return Locale.US;
}
}
public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) {
}
}
public class SecurityUtils {
public static User getCurrentUser(){
Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
User user = null;
if (principal instanceof User) {
user = (User) principal;
}
return user;
}
}
<spring:message code="key"/>
For example in user-edit.jsp I use this tag:
<form:form id="userForm">
<form:hidden path="user.id"/>
<table>
<tr>
<td><spring:message code="user.name"/> </td>
<td><form:input path="user.name"/></td>
</tr>
<tr>
<td><spring:message code="user.username"/> </td>
<td><form:input path="user.username" autocomplete="false"/></td>
</tr>
<tr>
<td><spring:message code="user.password"/> </td>
<td><form:password showPassword="false" path="user.password" autocomplete="false"/></td>
</tr>
<tr>
<td><spring:message code="user.theme"/> </td>
<td><form:select path="user.theme">
<form:option value="default" label="default"></form:option>
<form:option value="green" label="green"></form:option>
<form:option value="blue" label="blue"></form:option>
</form:select></td>
</tr>
...
</form:form>
<security:authentication property="principal" var="principal"/>
<c:set var="anonymousUser" value="${false}"/>
<security:authorize access="isAnonymous()">
<c:set var="anonymousUser" value="${true}"/>
</security:authorize>
<c:choose>
<c:when test="${!anonymousUser and principal.locale=='fa'}">
<div class="mainDiv" dir="rtl"></c:when>
<c:otherwise>
<div class="mainDiv"></c:otherwise>
</c:choose>
<security:authorize access="isAuthenticated()">
<a href="/j_spring_security_logout"><spring:message code="logout"/> </a><br/> <spring:message code="welcome"/> <security:authentication
property="principal.name"/>
</security:authorize>
<tiles:insertAttribute name="body"/>
</div>
spring:message looks like fmt:message jstl tag. we can set it to variable. Take a look to this:
<spring:message key="user.name" var="name"/>
${name}
Spring Theme
Spring Theme looks like Spring Internationalization both in configuration and usage.
First I should create an xml file called theme-config.xml for configuration and import it in main configuration file(spring-servlet.xml).
theme-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd">
<beans:bean id="themeSource" class="org.springframework.ui.context.support.ResourceBundleThemeSource">
<beans:property name="basenamePrefix" value="theme-" />
</beans:bean>
<beans:bean id="themeResolver" class="com.theme.CustomThemeResolver">
<beans:property name="defaultThemeName" value="default"/>
</beans:bean>
</beans:beans>
Then I should define the themeResolver to determine current theme so I prepare CustomThemeResolver for that. This class will find the current user and returns its current theme otherwise returns the default theme(default). Every localeResolver classes should be instance of org.springframework.web.servlet.ThemeResolver interface. But there is a useful abstract class called "AbstractThemeResolver" is an instance of ThemeResolver interface. It has good properties like "defaultThemeName", so I extend CustomThemeResolver class from AbstractThemeResolver class. Here is the code:
public class CustomThemeResolver extends AbstractThemeResolver {
public static final String DEFAULT_THEME = "default";
public static final String DEFAULT_GREEN = "green";
public static final String DEFAULT_BLUE = "blue";
public String resolveThemeName(HttpServletRequest request) {
User user = SecurityUtils.getCurrentUser();
if (user != null && user.getTheme() != null && !user.getTheme().equals("")) {
return user.getTheme();
} else {
return DEFAULT_THEME;
}
}
public void setThemeName(HttpServletRequest request, HttpServletResponse response, String s) {
}
}
theme-default.properties
css=/themes/default/main.css
theme-green.properties
css=/themes/green/main.css
theme-blue.properties
css=/themes/blue/main.css
As is shown in above there should be 3 folders in theme folder including: default, green and blue. Each folder should contains a main.css file.
/theme/default/main.css
.mainDiv{
background-color:lightgray;
padding:2px;
margin:2px;
}
/theme/green/main.css
.mainDiv{
background-color:lightgreen;
padding:2px;
margin:2px;
}
/theme/blue/main.css
.mainDiv{
background-color:lightblue;
padding:2px;
margin:2px;
}
Now it's the time to use it in pages. Spring has a tag which return the value of the current theme key
<spring:theme code='key'/>
For example in /WEB-INF/layout/view.jsp file:
<html>
<head>
<link rel="stylesheet" type="text/css" href="<spring:theme code='css'/>"/>
</head>
<body>
<security:authentication property="principal" var="principal"/>
<c:set var="anonymousUser" value="${false}"/>
<security:authorize access="isAnonymous()">
<c:set var="anonymousUser" value="${true}"/>
</security:authorize>
<c:choose>
<c:when test="${!anonymousUser and principal.locale=='fa'}">
<div class="mainDiv" dir="rtl"></c:when>
<c:otherwise>
<div class="mainDiv"></c:otherwise>
</c:choose>
<security:authorize access="isAuthenticated()">
<a href="/j_spring_security_logout"><spring:message code="logout"/> </a><br/> <spring:message code="welcome"/> <security:authentication
property="principal.name"/>
</security:authorize>
<tiles:insertAttribute name="body"/>
</div>
</body>
</html>
OK. I think it is the time that you yourselves should take tour in the application. You can download source code from here. The needed jar files are not available in it. But the jar files are the same as older project. You can use Spring WebFlow sample jar files. there is a mysql database script available in [Project Root]/db/intermsg.sql. You can use it to restore the database in your mysql server. after running the application the admin user specification is:
username: administrator
password: 123456
The datasouce properties is available in [Project Root]/src/database.properties.
all rights reserved by Mostafa Rastgar and Programmer Assistant weblog
2 comments:
Very nice and Informative....
Sloty Casino Hotel, New York - MapyRO
Overview. Sloty 광주광역 출장마사지 Casino Hotel, New York. 고양 출장마사지 MapyRO location. 전주 출장샵 Location. 3131 강릉 출장샵 South Main Street. Mapyro. (212) 김제 출장마사지 751-4280
Post a Comment