本文说说java web的多语言国际化实现和主题(Theme)的实现,具体到框架是Spring MVC+Freemarker+jQuery/JS的多语言国际化实现和主题(Theme)的实现。如果一个系统会被多个国家使用,则多语言国际化/本地化是必须的。其实多语言只是国际化(i18n)的一部分,国际化(i18n)/本地化(l10n)(Wiki:Internationalization and localization)还包括货币、时区、符号、格式等其它内容。每种语言基本都支持国际化,比如.NET, php都支持,当然java也一样。Java是一种基于Unicode的编程语言,提供了资源绑定(ResourceBundle)、地区(Locale)、时区(Timezone)等支持国际化。本文只说多语言(Multi-language)和主题(Theme)的实现。由于多语言散布在html、jsp、freemarker/*.frl、controller/Spring MVC、和javascript/js中,所以我们需要实现的是一个整体解决方案。本文内容:
Java默认的资源文件为*.properties(例如:messages_en_US.properties),资源文件要放到相应的classPath下面,和java一起编译。资源文件里面的内容是key/value的格式,比如:hello = hello, world. 资源文件的编码是UTF-8,也就是说里面可能不是直接要显示的文字,而是UTF-8编码以后的内容。properties里面的资源必须经过编码,不允许里面出现法文、德文、中文、日文等Unicode字符,而必须是ASCII字符。Unicode转ASCII可以使用JSK自带的native2ascii.exe工具,位于JDK安装目录的bin目录下,双击运行,输入中午或日文或法文,格式:native2ascii -[options] [inputfile [outputfile]],例如:native2ascii -encoding UTF-8 c:\message_de_DE.txt c:\message_de_DE.properties,回车开始转换。
JSP和JSTL标签底层都是通过Java的ResourceBundle类来获取资源并设置参数的。如果弄java不知道这个ResourceBundle类就菜鸟了,比如搞个配置文件还写个FileReader在那搞,折腾半天,最后还不能把配置文件和jar包打在一起发布....例子,读取message_en.properties,里面内容myKey = hello, world.
private static String myName;
static {
try {
ResourceBundle bundle = ResourceBundle
.getBundle("messages", Locale.ENGLISH);
myName = bundle.getString("myKey").trim();
}
catch(Exception ex) {
System.err.println( "[Property]:Can't Load property.properties");
myName = "default name";
System.out.println( "myName will use the default value: " + myName);
}
}
ResourceBundle类有一点注意,给的key注意路径:比如你的文件clasPath路径是/temp/messages_cn.properties ,那么这个key应该是“temp.messages”。
有关ResourceBundle类的更多用法,参考这个网页。
JSP和JSTL标签底层都是通过Java的ResourceBundle类来获取资源并设置参数的,而对于java web程序中Servlet访问资源文件可以通过ServletContext类中getResourceAsStream方法,它是通过Servlet容器来获得资源文件的,它使得Servlet程序可以访问web应用程序内部的任意位置的文件。(非Servlet中用classLoader,jdk中ClassLoader类专门提供了getResource等方法去装载资源文件,他们使用与查找Java类文件同样的方式去查找原文件,即在类 装载器所搜索的目录中查找。为了防止外部使用浏览器访问到资源文件,web应用程序中的资源文件通常应放到Web-INF目录或其子目录中。由于web应 用程序的类装载器会搜索web-inf/classes目录,所有ClassLoader.getResourceAsStream方法也可以访问该目录中的资源文件,但是,该方法不能访问web应用程序内的其他目录中的资源。)
InputStream in=this.getServletContext().getResourceAsStream("/WEB-INF/classes/db.properties");
java.util.Properties properties=new java.util.Properties();
properties.load(in); //得到的是map集合
String url=properties.getProperty("url");
String username=properties.getProperty("username");
String password=properties.getProperty("password");
Servlet访问资源文件要注意相对路径、绝对路径,以及权限问题,具体查看这个页面。
而Spring中的org.springframework.core.io.Resource接口代表着物理存在的任何资源,其继承于org.springframework.core.io.InputStreamSource;其子类有如下几 种:ByteArrayResource, ClassPathResource, DescriptiveResource, FileSystemResource, InputStreamResource, PortletContextResource, ServletContextResource, UrlResource 。常见的有下面四种:
HttpServletRequest支持一些接口来获得请求客户端浏览器的地区、语言和国家(IE-Internet Options设置-General-Language可以设置你的浏览器语言),然后HttpServletResponse通过设置http头的"Content-Language"来动态设置客户端语言。
import java.io.*;
import javax.servlet.*;
import javax.servlet.http.*;
import java.util.Locale;
public class DisplaySpanish extends HttpServlet{
public void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException
{
//Get the client's Locale
Locale locale = request.getLocale();
String language = locale.getLanguage();
String country = locale.getCountry();
// Set response content type
response.setContentType("text/html");
PrintWriter out = response.getWriter();
// Set spanish language code.
response.setHeader("Content-Language", "es");
String title = "En Español";
String docType =
"\n";
out.println(docType +
"\n" +
"" + title + " \n" +
"\n" +
"" + "En Español:" + "
\n" +
"" + "¡Hola Mundo!" + "
\n" +
"");
}
Spring MVC+Freemarker+jQuery/js的多语言实现
上面说了那么多废话,现在进入正题Spring MVC+Freemarker+jQuery/js的多语言实现,最终我们要实现的是:
首先说一下Spring MVC对多语言的支持可以看Spring官方网页,但我们用了Freemarker,这些有些麻烦。另外,Spring MVC提供了下面几种方式来支持多语言:
具体实现:
1. 在Spring-servlet.xml(具体看自己项目中的命名)加入:
resources/messages
2. 添加资源文件messages.properties, messages_en_US.properties, messages_zh_CN.properties,注意路径和上面配置的一致,在classpath的resources目录下:
3. 针对Freemarker的,首先Spring jar包反解出Spring.ftl,然后拷贝到你的ftl目录。这样比较变态,但这样就能在ftl文件中使用宏来获得需要的message了。
4. 在Spring-servlet.xml(具体看自己项目中的命名)加入设置:Freemarker自动导入Spring.ftl宏。不用在每个ftl里面定义这个宏。
localization/spring.ftl as spring
如下图:
5. 在freemarker中使用:<@spring.message "label.menu"/>
6. 如果不能自动获取宏,需要ftl中加入: <#import "/WEB-INF/views/localization/spring.ftl" as spring/>
7. 在Spring-servlet.xml(具体看自己项目中的命名)加入设置:
8. 界面上加个按钮动态切换语言:
EN
|
FR
9. 对于的动态切换语言的js:
function changeLanguage(language)
{
$.ajax({
type: "POST",
url: base + "ajax/changelanguage.do",
data: "new_lang="+language,
dataType:"json",
async: true,
error: function(data, error) {alert("change lang error!");},
success: function(data)
{
window.location.reload();
}
});
}
10. Spring MVC后台对于的controller函数实现动态切换语言:(Spring会自动保存到上面配置的cookie。)
@RequestMapping(value = "ajax/changelanguage.do", method = RequestMethod.POST)
public ModelAndView changeLanguage(@RequestParam String new_lang, HttpServletResponse response)
{
String msg = "";
try
{
LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(this.getRequest());
if (localeResolver == null) {
throw new IllegalStateException("No LocaleResolver found: not in a DispatcherServlet request?");
}
LocaleEditor localeEditor = new LocaleEditor();
localeEditor.setAsText(new_lang);
localeResolver.setLocale(getRequest(), response, (Locale)localeEditor.getValue());
msg = "Change Language Success!";
}
catch(Exception ex)
{
msg = "error";
}
return new ModelAndView("jsonView", "json", msg);
}
11. 在其他controller里面如何获取当前语言并根据语言获取相应的文字? 首先在基类 baseController中加入:
@Autowired
protected MessageSource messageSource;
然后在继承的controller里面加入参数:Locale locale ,如下图:
然后就可以用messageSource 自动根据当前语言获取文字:messageSource.getMessage("myKey", null, locale)
12. 这里说一下,freemarker可能会报错:Template Spring.ftl not found! 但事实上你的Spring.ftl是存在的,为何freemarer会找不到。这是因为freemarker寻找模版是根据配置的位置,templateLoaderPath,所以你配置的Spring.ftl路径必须是相对templateLoaderPath的相对路径。比如:
13. Javascript/jQuery/js的多语言问题:界面输入的validation往往会用js实现,而独立的js文件里面输出的validation message自然也要多语言。这里js的多语言问题有两种实现方法:1. 从后台读取资源,然后存入js数组,而且后台读取是异步的, 这个会有性能问题。有兴趣的朋友可以去实现以下,有一些插件可以实现js读取后台i18n的java资源:参考1(jQuery i18n plugin),参考2(i18next),参考3(jawr). 2. 在js validation message放入单独的多个js文件,比如:validation_message.js和validation_message_fr_FR.js。本文采用的第二种方法。这种方法性能好,清晰,简单,因为validation messages可以拆开,而FCKEditor也是用这个方法。
首先增加validation.strings.js 和 validation.strings.fr.js(一个英语,一个发育),内容:
var messageStrings = {
my_key1: "hello",
my_key2: "world"
}
在js中可以这样用:messageStrings.my_key1.
然后是关键一步:根据cookie的语言,动态加载相应的js文件:
为了性能考虑,在页面最后加载,就是
的前面,加入以下的js:
把这段js放到html的head部分,就是动态根据当前的cookie设置的语言来加载响应的js,会在前面动态引用js,注意是document.head.appendChild (document.getElementsByTagName('head')[0])哦,不是document.body.appendChild。 效果:
注意:动态加载和动态切换jqGrid的语言用上面的代码会出现各个浏览器的兼容性问题,原因是jqGrid/js/i18n/grid.locale-xx.js必须在引用jgGrid之前引用,而动态引用js的执行在各个浏览器(IE、Chrome、Firefox、Opera、Safari)里面顺序不一样,有的是立即同步执行,有的是异步执行,导致jqGrid使用的时候报异常。当然你可以用下面的带OnLoadComple callback的方法来强制同步动态加载引用外部js,保证它们是顺序加载引用和执行的:
function loadScript(head, url, callback){
var script = document.createElement("script")
script.type = "text/javascript";
if (script.readyState){ //IE
script.onreadystatechange = function(){
if (script.readyState == "loaded" ||
script.readyState == "complete"){
script.onreadystatechange = null;
callback();
}
};
} else { //Others
script.onload = function(){
callback();
};
}
script.src = url;
if(head) document.getElementsByTagName("head")[0].appendChild(script);
else document.body.appendChild(script);
}
但这样也会出现其他的问题,比如你在局部的ftl/html里面有内嵌的js:$("#abc").jqGrid.....这些是立即执行的,而上面的动态加载引用的js可能还没有加载完毕....这样的问题在各个浏览器兼容上面有问题。最终实现jqGrid多语言/国际化/本地化/i18n并能实时动态切换语言的解决方案是扩展jqgrid,具体看这个老外的帖子,demo在这儿,需要改造grid.locale-XX.js,注意不是引用的官方的jqGrid。
jQuery.UI有一个DatePicker控件,也是需要多语言和国际化的。因为上面的文字比如月份:
首先去官网下载语言对应的datePicker文件,例如:jquery.ui.datepicker-fr.js就是法语的。然后在html的body后面动态加载响应的js即可,把下面的js调用一下,放在前面就行了,会在前面动态引用js。
Spring MVC对Theme主题的支持可以参考这个Spring官方网页,同上面的多语言类似,Spring MVC也提供了多种方式,比如url的param和拦截器、session和cookie等,这里我们还是用cookie的方式来实现。
1. 在Spring-servlet.xml(具体看自己项目中的命名)加入设置:
2. 在src/resources 添加两个文件: theme-default.properties, theme-blue.properties
theme-default.properties内容: css=themes/default.css
theme-blue.properties内容: css=themes/blue.css
3. 添加themes目录,添加两个文件:default.css, blue.css
default.css内容:
body {
background-color: white;
color: black;
}
blue.css内容:
body {
background-color: #DBF5FF;
color: #007AAB;
}
4. 界面上加一个按钮动态切换主题:
default
|
blue
5. 按钮对应的js:
function changeTheme(theme)
{
$.ajax({
type: "POST",
url: base + "ajax/changetheme.do",
data: "new_theme="+theme,
dataType:"json",
async: true,
error: function(data, error) {alert("change theme error!");},
success: function(data)
{
window.location.reload();
}
});
}
6. Spring MVC对应的后台controller函数实现动态切换主题:
@RequestMapping(value = "ajax/changetheme.do", method = RequestMethod.POST)
public ModelAndView umChangeTheme(@RequestParam String new_theme, HttpServletResponse response)
{
String msg = "";
try
{ ThemeResolver themeResolver = RequestContextUtils.getThemeResolver(this.getRequest());
if (themeResolver == null) {
throw new IllegalStateException("No themeResolver found: not in a DispatcherServlet request?");
}
themeResolver.setThemeName(getRequest(), response, new_theme);
msg = "change Theme Success!";
}
catch(Exception ex)
{
msg = "error";
}
return new ModelAndView("jsonView", "json", msg);
}
7. 在页面中根据cookie动态加载主题相应的css(注意放在html开头来动态加载css) :
你可能感兴趣的:(JAVAEE,前端,JAVA,JS)