2011-08-02 16 views
14

我有一个使用Spring MVC的Web应用程序。我想让我的界面只包含一个页面,它通过AJAX将所有数据动态地检索为JSON。我的问题是国际化。当我呈现在JSP中的内容,我可以使用JSTL标记来解决我的钥匙(超级容易与Spring MVC):Spring - 获取所有可解析的消息密钥

<fmt:message key="name"/>: ${name} 
<fmt:message key="title"/>: ${title} 
<fmt:message key="group"/>: ${group} 

如果配置正确,它在芬兰的语言环境呈现为

Nimi: yay 
Otsikko: hoopla 
Ryhmä: doo 

现在,当我使用json时,我只能从服务器进入:

{ 
name: "yay", 
title: "hoopla", 
group: "doo" 
} 

没有键名!但我必须以某种方式提供它们。我考虑将键名更改为本地化表单或将本地化键名添加到json输出中(例如name_localized =“Nimi”),但这两个选项都是不好的做法。我使用jackson json自动将我的域对象解析为json,我喜欢它提供的封装。

我想出的唯一可行的解​​决方案是:动态创建一个JavaScript文件,其中包含本地化的键名作为变量。

<script type="text/javascript"> 
var name="Nimi"; 
var title="Otsikko"; 
var group="Ryhmä"; 
</script> 

一旦我有这个加载,我现在有我的JavaScript中的所有信息来处理json!但有一个问题:我的字段名称列表是动态的。因此,在JSP实际静态渲染应该是这样的:

<c:forEach var="field" values="${fields}"> 
<fmt:message key="${field.key}"/>: ${field.value} 
</c:forEach> 

我需要找到所有在我messages.properties指定的消息。 Spring MessageSource接口只支持按键检索消息。我怎样才能得到我的JSP中的关键字列表,呈现本地化的JavaScript变量?

回答

0

我不认为这是不好的做法返回本地化的价值观。你可以从你的控制器返回是这样的(所以你必须要解决那里的键):

{ 
    name: {label: "Nimi", value: "yay"}, 
    title: {label: "Ostikko", value: "hoopla"}, 
    group: {label: "Rhymä", value: "doo"}, 
} 

您可以创建一个中间对象(最简单的将是Map<String, Map<String, String>>)持有这些价值观和杰克逊将序列化转换成JSON。我认为它比动态的Javascript文件选项更好。这个解决方案最终还是要做你正在试图用动态JavaScript文件做的事情,也就是将密钥与解析后的值相关联。

12

那么,我通过扩展ResourceBundleMessageSource来“解决”了我的问题。

package org.springframework.context.support; 

import java.util.Locale; 
import java.util.ResourceBundle; 
import java.util.Set; 

public class ExposedResourceBundleMessageSource extends 
     ResourceBundleMessageSource { 
    public Set<String> getKeys(String basename, Locale locale) { 
     ResourceBundle bundle = getResourceBundle(basename, locale); 
     return bundle.keySet(); 
    } 
} 

现在我已经获得了钥匙,但我必须做一个丑陋投在我的控制器,除了具有指定消息源基名。呃,这是很多的耦合。

Set<String> keys = 
    ((ExposedResourceBundleMessageSource) 
    messageSource).getKeys("messages", locale); 
+2

那么,通过将自定义消息源设置为项目的默认值,您可以清除一些丑陋的内容。很难将它全部放在评论中,但是在你的配置文件中,然后在你的类中使用''然后'@Autowired private ExposedResourceBundleMessageSourcemessageSource;'。 – Patrick

+0

对于那些想要如何获得'Locale'的人,你可能想使用'LocaleContextHolder.getLocale()'。这将获得绑定到线程的语言环境,这通常是您在servlet/spring mvc环境中所需的。 –

4

我已经解决了这个问题。


public class ExposedResourceBundleMessageSource extends 
     ResourceBundleMessageSource { 
    public static final String WHOLE = "whole"; 
    private Set baseNames; 
    private Map> cachedData = new HashMap>(); 

    public Set getKeys(String baseName, Locale locale) { 
     ResourceBundle bundle = getResourceBundle(baseName, locale); 
     return bundle.keySet(); 
    } 

    public Map getKeyValues(String basename, Locale locale) { 
     String cacheKey = basename + locale.getCountry(); 
     if (cachedData.containsKey(cacheKey)) { 
      return cachedData.get(cacheKey); 
     } 
     ResourceBundle bundle = getResourceBundle(basename, locale); 
     TreeMap treeMap = new TreeMap(); 
     for (String key : bundle.keySet()) { 
      treeMap.put(key, getMessage(key, null, locale)); 
     } 
     cachedData.put(cacheKey, treeMap); 
     return treeMap; 
    } 

    public Map getKeyValues(Locale locale) { 
     String cacheKey = WHOLE + locale.getCountry(); 
     if (cachedData.containsKey(cacheKey)) { 
      return cachedData.get(cacheKey); 
     } 
     TreeMap treeMap = new TreeMap(); 
     for (String baseName : baseNames) { 
      treeMap.putAll(getKeyValues(baseName, locale)); 
     } 
     cachedData.put(cacheKey, treeMap); 
     return treeMap; 
    } 

    public void setBasenames(String[] basenames) { 
     baseNames = CollectionUtils.arrayAsSet(basenames); 
     super.setBasenames(basenames); 
    } 


} 
+1

这是一个很好的解决方案,因为它自动添加了缓存并捕获了基名。 – JBCP

+1

但是,如果您在同一个国家/地区内支持多种语言,则使用locale.getCountry()可能会出现问题。只使用locale本身可能更安全。 – JBCP

1

我的解决办法:

  1. 使用弹簧<util:properties />

    <util:properties id="message" location="classpath:messages.properties" /> 
    
  2. 添加拦截器,设置为请求 '消息' 属性

    request.setAttribute("msgKeys", RequestContextUtils.getWebApplicationContext(request).getBean("message")); 
    
  3. 在JSP中,使用msgKeys通过<fmt:message />标签

    var msg = {<c:forEach items="${msgKeys}" var="m" varStatus="s"> 
        <c:set var="key" value="${fn:substringBefore(m,'=')}"/> 
        "${fn:substringAfter(key)}":"<fmt:message key="${key}"/>", 
    </c:forEach>}; 
    

检索信息(当然,你需要进一步逃避输出相匹配的js的字符串)。

因此,对于性能

app.name=Application Name 
app.title=Some title 

会输出

var msg = { "name":"Application Name", "title":"Some title" }; 

好处在于,您可以在c:forEach循环中执行更多逻辑。

相关问题