javascript Spring - 获取所有可解析的消息键

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6915312/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-25 22:19:20  来源:igfitidea点击:

Spring - get all resolvable message keys

javajavascriptajaxspringspring-mvc

提问by Eero Heikkinen

I have a web application which uses Spring MVC. I would like to have my interface consist of just a single page which retrieves all data dynamically as JSON via AJAX. My problem is with internationalization. When I render content in jsps, I can use JSTL tags to resolve my keys (super-easy with Spring MVC):

我有一个使用 Spring MVC 的 Web 应用程序。我想让我的界面只包含一个页面,该页面通过 AJAX 以 JSON 的形式动态检索所有数据。我的问题是国际化。当我在 jsps 中呈现内容时,我可以使用 JSTL 标签来解析我的键(使用 Spring MVC 非常简单):

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

When properly configured, it renders in finnish locale as

正确配置后,它在芬兰语语言环境中呈现为

Nimi: yay
Otsikko: hoopla
Ryhm?: doo

Now, when I use json, I have only this coming in from the server:

现在,当我使用 json 时,我只有这个来自服务器:

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

There's no keynames! But I have to provide them somehow. I considered changing the keynames to their localized forms or adding the localized keynames to json output (eg. name_localized="Nimi") but both of these options feel like bad practice. I'm using Hymanson json to automatically parse my domain objects into json and I like the encapsulation it provides.

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

The only feasible solution I came up with is this: dynamically create a javascript file with the localized keynames as variables.

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

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

Once I have this loaded, I now have all the information in my javascript to handle the json! But there's a gotcha: my list of field names is dynamic. So actually static rendering in jsp would look like this:

一旦我加载了这个,我现在在我的 javascript 中拥有处理 json 的所有信息!但是有一个问题:我的字段名称列表是动态的。所以实际上jsp中的静态渲染看起来像这样:

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

I need to find all of the messages specified in my messages.properties. Spring MessageSource interface only supports retrieving messages by key. How can I get a list of keynames in my JSP which renders the localized javascript variables?

我需要找到我的messages.properties 中指定的所有消息。Spring MessageSource 接口只支持按键检索消息。如何在我的 JSP 中获取呈现本地化 javascript 变量的键名列表?

回答by Eero Heikkinen

Well, I "solved" my problem by extending ResourceBundleMessageSource.

好吧,我通过扩展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();
    }
}

Now I have access to the keys, but I have to do an ugly cast in my controller, in addition to having to specify the message source basename. Ugh, that's a lot of coupling.

现在我可以访问这些密钥,但除了必须指定消息源基名之外,我还必须在我的控制器中进行难看的转换。呃,这是很多耦合。

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

回答by maziar

I have solve this issue.

我已经解决了这个问题。


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);
    }


}

回答by Hymanysee

My solution:

我的解决方案:

  1. use spring <util:properties />

    <util:properties id="message" location="classpath:messages.properties" />
    
  2. Add an interceptor, with 'message' set to request attributes

    request.setAttribute("msgKeys", RequestContextUtils.getWebApplicationContext(request).getBean("message"));
    
  3. In JSP, use the msgKeysto retrieve message through <fmt:message />tag

    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>};
    
  1. 使用弹簧 <util:properties />

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

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

(Of course you need to further escape the output to match js string.)

(当然你需要进一步转义输出以匹配js字符串。)

So, for properties

所以,对于属性

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

would output

会输出

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

The good thing about that is that you can do some more logic in c:forEachloop.

这样做的好处是你可以在c:forEach循环中做更多的逻辑。

回答by Mahdi

If you do not want to extend the ResourceBundleMessageSourceclass, you can do it like this:

如果你不想扩展这个ResourceBundleMessageSource类,你可以这样做:

@Component
class FrontendMessageLoader {
  import scala.collection.JavaConverters._

  @Resource(name = "messageSource")
  var properties: ResourceBundleMessageSource = _

  @PostConstruct def init(): Unit = {
    val locale = new Locale("en", "US")
    val baseNames: mutable.Set[String] = properties.getBasenameSet.asScala
    val messageMap: Map[String, String] =
      baseNames.flatMap { baseName =>
        readMessages(baseName, locale)
      }.toMap
    }
  }

  private def readMessages(baseName: String, locale: Locale) = {
    val bundle = ResourceBundle.getBundle(baseName, locale)
    bundle.getKeys.asScala.map(key => key -> bundle.getString(key))
  }
}

This is Scala but you get the idea and you can easily convert it to Java if needed. The point is that you get the base-names (which are the property files) and then load those files and then you can get the keys. I am passing also locale, but there are multiple overloaded versions for ResourceBundle.getBundleand you can choose the one that matches your needs.

这是 Scala,但您明白了,如果需要,您可以轻松地将其转换为 Java。关键是您获得基本名称(即属性文件),然后加载这些文件,然后您就可以获得密钥。我也在传递语言环境,但有多个重载版本ResourceBundle.getBundle,您可以选择符合您需求的版本。

回答by Vivin Paliath

I don't think it's bad practice to return the localized values. You could return something like this from your controller (so you just have to resolve the keys there):

我不认为返回本地化值是不好的做法。你可以从你的控制器返回这样的东西(所以你只需要在那里解决密钥):

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

You can create an intermediate object (the easiest would be Map<String, Map<String, String>>) that holds these values and Hymanson will serialize it into JSON. I think it's better than the dynamic Javascript-file option. This solution also ends up doing what you were trying to do with the dynamic Javascript-file anyway, that is, associating the key with its resolved value.

您可以创建一个Map<String, Map<String, String>>包含这些值的中间对象(最简单的是),Hymanson 会将其序列化为 JSON。我认为它比动态 Javascript 文件选项更好。无论如何,此解决方案最终也会执行您尝试对动态 Javascript 文件执行的操作,即将密钥与其解析值相关联。