Introduction
This article presents an example to localize applications with Java property files and the "ResourceBundle" class.
Background
Localization becomes a more and more important feature in modern applications. When the localization task is not very heavy weight, I found that the property files are very handy and easy to use. I will try to achieve two goals in this example.
- I will show you how to translate the texts into different languages with property files and the "ResourceBundle" class;
- If a translation entry is not available in the best matched property file, I will discuss the fallback rules of the translation loop up mechanism.
The example is a Maven project. If you are not familiar with Maven, you can take a look at my early articles from here and here on how to import it into Eclipse and how to run it in Eclipse. The following is the project shown in the Eclipse project explorer.
The Localizer and Property Files
To make it simple, let us take a look at the "Localizer.java" file directly.
package com.song.localization;
import java.util.Locale;
import java.util.ResourceBundle;
import org.springframework.context.i18n.LocaleContextHolder;
public class Localizer {
private final static String RESOURCE_BUNDLE = "com.song.localization.dictionary";
private Locale locale = null;
public Localizer() { this(Locale.getDefault()); }
public Localizer(Locale locale) { this.locale = locale; }
public String getLocalizedText(String key)
{
try
{
ResourceBundle bundle = ResourceBundle.getBundle(RESOURCE_BUNDLE,
locale, this.getClass().getClassLoader());
if (bundle.keySet().contains(key)) {
return bundle.getString(key);
}
else {
return key + "(No localization entry found)";
}
}
catch (Exception e)
{
return "LOCALIZATION FAILED: " + e.toString();
}
}
public Locale getLocale() { return this.locale; }
public static Localizer getBrowserLocalizer() {
return new Localizer(LocaleContextHolder.getLocale());
}
}
- The "getLocalizedText()" translates the input key into the string of the desired language. It looks up the translated texts from the resource files;
- When the browser sends an HTTP request to the server, it will send the desired response language and the culture in the request header. If you use Spring MVC, you can use the "getBrowserLocalizer()" factory method to get the "Localizer" object. It will automatically set the "Locale" of the "Localizer" object to what the browser requested.
The property files are implemented as the following.
If you take a look at the names of the property files in my example, you will notice that it has three parts.
- "dictionary" is the package bundle name;
- "zh" is the language code, which is Chinese;
- "TW" is the region code, which is Taiwan. In Taiwan, people use a slightly different form of written Chinese, which is also sometimes called "traditional Chinese".
According to the documentation, the language code should always be lower case, and the region code should always be upper case. In order to show the fallback rules of the "Localizer" class, I purposefully skipped some translation entries in the "dictionary_zh.properties" and "dictionary_zh_TW.properties" files.
The Sping MVC Controller and the View
In order to show you how the "Localizer" class works, I created a Spring MVC web application. The MVC controller is implemented in the "LocalizationExampleController.java" file.
package com.song.web.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;
import com.song.localization.Localizer;
@Controller
public class LocalizationExampleController {
@RequestMapping(value ={"/example"}, method = RequestMethod.GET)
public ModelAndView Example() {
ModelAndView mv = new ModelAndView();
mv.setViewName("example.jsp");
Localizer localizer = Localizer.getBrowserLocalizer();
mv.addObject("welcome_text", localizer.getLocalizedText("welcome_text"));
mv.addObject("to_text", localizer.getLocalizedText("to_text"));
mv.addObject("mywebsite_text", localizer.getLocalizedText("mywebsite_text"));
return mv;
}
}
The corresponding MVC view is implemented in the "example.jsp" file.
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<%String baseUrl = getServletContext().getInitParameter("BaseUrl");%>
<%
String welcome_text = (String)request.getAttribute("welcome_text");
String to_text = (String)request.getAttribute("to_text");
String mywebsite_text = (String)request.getAttribute("mywebsite_text");
%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Localization Example with Property Files</title>
<link rel="stylesheet" type="text/css"
href="<%out.print(baseUrl); %>styles/site.css">
</head>
<body>
<div class="container">
<div class="center"><%=welcome_text %></div>
<div class="center"><%=to_text %></div>
<div class="center"><%=mywebsite_text %></div>
</div>
</body>
</html>
- The controller uses the "getBrowserLocalizer()" factory method to create a "Localizer" object and adds the translated text to the "ModelAndView" object;
- The view then writes the translated text into the HTML content returned to the browser.
Run the Application
If you want to run the application, you can import it into Eclipse and run it in the development environment. You can also issue an "mvn clean install" command to get the "war" file and deploy it to a true J2EE server. To test how the localization works, let us first configure the browser's language preference. If you use Google Chrome, you can go to "Settings" -> "Show advanced settings..." -> "Languages" and click on the "Language and input setttings..." button. For the first test, Let us drag Italian to the top and set it as the browser preferred language.
Since we do not have a property file for Italian, the localization will go to the default property file "dictionary.properties" (by chance, it is English) to look for the translation entries. If we load our web page, we can see the default translation text.
Now let us now drag "Chinese (Traditional)" to the top and set it as the browser preferred language. We can then refresh our web page to see what text is displayed.
Discussion on the Fallback Rules
- When we set the browser preferred language to "Chinese(Traditional)", the browser will send "zh_TW" in the request header to tell the server to send "traditional Chinese" text if possible;
- The localizer will first try the best matched property file "dictionary_zh_TW.properties", which matches both the language and the region. In this file, it found the translation entry "welcome_text" and the translated text "歡迎";
- The localizer cannot find the translation entry "to_text" in the "dictionary_zh_TW.properties" file, it then tries the second best matched property file "dictionary_zh.properties", which matches the language, but no region specified. In this file, the localizer found the translation entry "to_text" and the translated text "来到";
- The localizer cannot find the translation entry "mywebsite_text" in either the best or the second matched property files, so it goes to the default property file "dictionary.properties" to get the translated text "My Web Site";
- If the localizer cannot event find the translation entry in the default property file, it will return the lookup key appended with "(No localization entry found)".
Points of Interest
- This article presented an example to localize applications with Java property files and the "ResourceBundle" class;
- It showed you how to use property files to add localization to your applications;
- It also discussed the fallback rules implemented by Java ResourceBundle;
- The biggest advantage of using property files for localization is simplicity, but it may not fit the applications that need to translate a large number of texts. If you have a large number of texts to translate, I will recommend you to implement your own localization mechanism that may fit your needs the best;
- If you want to implement your own localization mechanism, I think that you may want to borrow the fallback rules, which I feel very elegant and reasonable.
History
First Revision - 6/10/2015