解决 spring:i18n 国际化的 javascript 中的消息

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/6218970/
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-08-23 20:49:29  来源:igfitidea点击:

Resolving spring:messages in javascript for i18n internationalization

javascriptspringspring-mvcinternationalizationdojo

提问by olan

I'm attempting to internationalize some of our code. I have a page in JSPX which is using the <spring:message>tag to resolve strings from a message.propertiesfile. This works fine for the HTML and CSS that is in the JSPX page, however there a javascript file is sourced, and substituting the <spring:message>tag for the string in there just means that it gets printed out verbatim.

我正在尝试国际化我们的一些代码。我在 JSPX 中有一个页面,它使用<spring:message>标记来解析message.properties文件中的字符串。这对于 JSPX 页面中的 HTML 和 CSS 工作正常,但是有一个 javascript 文件来源,并且用<spring:message>标签替换那里的字符串只是意味着它被逐字打印出来。

My JSPX sources the javascript like so:

我的 JSPX 像这样获取 javascript:

<spring:theme code="jsFile" var="js" />
<script type="text/javascript" src="${js}" />

The JS where I'm looking the replace the string is below:

我正在寻找替换字符串的 JS 如下:

buildList('settings', [{
    name: '<spring:message code="proj.settings.toggle" javaScriptEscape="true" />',
    id:"setting1",
    description: '<spring:message code="proj.settings.toggle.description" javaScriptEscape="true" />',
    installed: true
}]);

And finally the message.properties is something like:

最后 message.properties 是这样的:

proj.settings.toggle=Click here to toggle
proj.settings.toggle.description=This toggles between on and off

So what I'm wondering is, should this work? It seems to me like it should, from what I've gathered on various forums, but I can't figure out where I'm going wrong. Is there a better way to go about this?

所以我想知道的是,这应该有效吗?从我在各种论坛上收集的信息来看,在我看来应该是这样,但我无法弄清楚我哪里出错了。有没有更好的方法来解决这个问题?

I should also note that these files are outside the WEB-INF folder, but by placing the ReloadableResourceBundleMessageSource in the root applicationContext.xml the spring tags are picked up.

我还应该注意到,这些文件在 WEB-INF 文件夹之外,但是通过将 ReloadableResourceBundleMessageSource 放在根 applicationContext.xml 中,spring 标签会被选中。

Thanks for any help!

谢谢你的帮助!

回答by Pawe? Dyda

It seems to me that what you want to do is to treat JS file like JSP file and resolve its contents via spring:message tag.
I would not do that.

在我看来,您想要做的是将 JS 文件视为 JSP 文件并通过 spring:message 标签解析其内容。
我不会那样做。

Typically JS i18n is done in one of two ways:

通常 JS i18n 是通过以下两种方式之一完成的:

  • By writing out Array of translated strings from JSP page
  • By creating translation filter and provide pre-translated JS file to requesting client
  • 通过从 JSP 页面写出翻译字符串数组
  • 通过创建翻译过滤器并向请求客户端提供预翻译的 JS 文件

Both works best if you create one central location for your client-side translatable strings.
In your context, I would recommend the first method (much easier). That is unless your project is pretty large and you have a lotof translatable strings on the client side. So the modification would look like:

如果您为客户端可翻译字符串创建一个中心位置,则两者都效果最佳。
在您的上下文中,我会推荐第一种方法(更容易)。除非您的项目非常大并且您在客户端有很多可翻译的字符串。所以修改看起来像:

<script type="text/javascript">
  var strings = new Array();
  strings['settings.toogle'] = "<spring:message code='proj.settings.toggle' javaScriptEscape='true' />";
  strings['settings.toogle.description'] = "<spring:message code='proj.settings.toggle.description' javaScriptEscape='true' />";
</script>
<spring:theme code="jsFile" var="js" />
<script type="text/javascript" src="${js}" />

And in your JS file:

在你的 JS 文件中:

buildList('settings', [{
    name: strings['settings.toggle'],
    id:"setting1",
    description: strings['settings.toggle.description'],
    installed: true
}]);

Mind you that I used double quotes for writing out translated strings. That is because of some words in French or Italian that could contain apostrophe.

请注意,我使用双引号来写出翻译后的字符串。这是因为法语或意大利语中的某些单词可能包含撇号。

Edit: Additional input

编辑:附加输入

We provide translations to JS files for the reason. Usually, the reason is we want to create some part of UI dynamically. There are also cases where we need to localize some 3rd party component, my answer above deals with them pretty well.
For cases where we want to create UI parts dynamically, it really make sense to use templates rather than concatenate HTML tags in JavaScript. I decided to write this, because it makes for much cleaner (and possibly reusable) solution.
So instead of passing translations to JavaScript one may create a template and put it on the page (my example will use Handlebars.js, but I believe it is possible to use any other engine):

出于这个原因,我们提供了 JS 文件的翻译。通常,原因是我们想动态创建 UI 的某些部分。还有一些情况我们需要本地化一些 3rd 方组件,我上面的回答很好地处理了它们。
对于我们想要动态创建 UI 部件的情况,使用模板而不是在 JavaScript 中连接 HTML 标签确实很有意义。我决定写这个,因为它提供了更清洁(并且可能可重用)的解决方案。
因此,与其将翻译传递给 JavaScript,不如创建一个模板并将其放在页面上(我的示例将使用Handlebars.js,但我相信可以使用任何其他引擎):

<script id="article" type="text/x-handlebars-template">
  <div class="head">
    <p>
      <span>
        <spring:message code="article.subject.header" text="Subject: " />
      </span>{{subject}}</p>
  </div>
  <div class="body">
    {{{body}}}
  </div>
</script>

On the client side (that is in JavaScript) all you have to do is to access the template (example below obviously uses jQuery) and compile:

在客户端(即在 JavaScript 中),您所要做的就是访问模板(下面的示例显然使用 jQuery)并编译:

var template = Handlebars.compile($("#article").html());
var html = template({subject: "It is really clean",
  body: "<p>Don't you agree?</p><p>It looks much better than usual spaghetti with JavaScript variables.</p>"
});
$("#someDOMReference").html(html);

Few things to note here:

这里有几点需要注意:

  • <spring:message />tags escape both HTML and JS by default, we do not need to specify the javaScriptEscapeattribute
  • It make sense to provide textattribute for <spring:message />tag as it could be used as a fall-back (if there is no translation for given language) as well as a comment (what this element stands for). One can even create a tool that would scan files for <spring:message />tags and automatically generate properties files
  • To prevent Handlebars from escaping HTML contents, I used triple {{{curly braces}}}
  • <spring:message />标签默认同时转义 HTML 和 JS,我们不需要指定javaScriptEscape属性
  • text<spring:message />标签提供属性是有意义的,因为它可以用作后备(如果给定语言没有翻译)以及注释(该元素代表什么)。甚至可以创建一种工具来扫描文件中的<spring:message />标签并自动生成属性文件
  • 为了防止 Handlebars 转义 HTML 内容,我使用了三重 {{{curly braces}}}

That's basically it. I recommend using templates if that's possible.

基本上就是这样。如果可能,我建议使用模板。

回答by Toilal

Thanks for your answer. Here is a more generic solution.

感谢您的回答。这是一个更通用的解决方案。

The idea is to provide a dynamic javascriptfile named "string.js", containing an associative array of messages registered in the java resource bundle, using current user language.

这个想法是提供一个名为“string.js”的动态 javascript文件,其中包含使用当前用户语言在 java 资源包中注册的关联消息数组。

1) Create a method in Spring Controllerto load all resource keys from resource bundle, and return the view "spring.jsp"

1) 在 Spring Controller 中创建一个方法,从资源包中加载所有资源键,并返回视图“spring.jsp”

@RequestMapping(value="strings.js")
public ModelAndView strings(HttpServletRequest request) {
    // Retrieve the locale of the User
    Locale locale = RequestContextUtils.getLocale(request);
    // Use the path to your bundle
    ResourceBundle bundle = ResourceBundle.getBundle("WEB-INF.i18n.messages", locale);  
    // Call the string.jsp view
    return new ModelAndView("strings.jsp", "keys", bundle.getKeys());
}

2) Implement the View"strings.jsp"

2)实现视图“strings.jsp”

<%@page contentType="text/javascript" pageEncoding="UTF-8"
%><%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"
%><%@taglib prefix="spring" uri="http://www.springframework.org/tags"
%>var messages = new Array();

<c:forEach var="key" items="${keys}">messages["<spring:message text='${key}' javaScriptEscape='true'/>"] = "<spring:message code='${key}' javaScriptEscape='true' />";
</c:forEach>

3) Import "spring.js" in your HTML source code. Messages array is available and loaded with the right language.

3) 在您的 HTML 源代码中导入“spring.js”。消息数组可用并加载了正确的语言。

Possible issue: If user change his language, "spring.js" must be reloaded by the navigator, but it will be cached. Clearing the cache is needed when user change his language (or other trick to get the file reloaded).

可能的问题:如果用户更改他的语言,导航器必须重新加载“spring.js”,但它会被缓存。当用户更改语言(或其他重新加载文件的技巧)时,需要清除缓存。

回答by Pedro Pelaez

In addition to answer of @Toilal you can add a helper function to strings.jsp to do a better use of the translation array:

除了@Toilal 的回答之外,您还可以向strings.jsp 添加一个辅助函数以更好地利用翻译数组:

<%@page contentType="text/javascript" pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
<%@taglib prefix="spring" uri="http://www.springframework.org/tags"%>
var messages = new Array();

<c:forEach var="key" items="${keys}">messages["<spring:message text='${key}' javaScriptEscape='true'/>"] = "<spring:message code='${key}' javaScriptEscape='true' />";
</c:forEach>

/**
 * Tranlate a String by key, if key is not defined return the key.
 *  
 * @author Pedro Peláez <aaaaa976 at gmail dot com>, Drupal/Wordpress authors, and others
 * @param {String} key
 * @returns {String}
 */
function t(key) {
    if (messages[key]) {
        return messages[key];
    }
    return key;
}

Then when needed just use:

然后在需要时使用:

alert(t("menu.section.main"));

回答by devilcius

It's quite simple to transform spring's message.properties to a JavaScript object and then using that object in other JavaScript files. There's a node modulewith which we can transform

将 spring 的 message.properties 转换为 JavaScript 对象,然后在其他 JavaScript 文件中使用该对象非常简单。有一个节点模块,我们可以用它来转换

  app.name=Application name
  app.description=Application description

to

const messages = { app: { name: 'Application name', description: 'Application description' } };

Then a messages_{lang}.js file should be created for each messages.{lang}.properties and be referenced in the templates. In a thymeleaf template it would look like this:

然后应该为每个 messages.{lang}.properties 创建一个 messages_{lang}.js 文件并在模板中引用。在 thymeleaf 模板中,它看起来像这样:

  <script th:src="@{'/js/messages_' + ${#locale}  + '.js'}"></script> 
  <script>
    console.log(messages.app.name, messages.app.description);
  </script>

I've created a quick grunt pluginfor this purpose.

为此,我创建了一个快速的grunt 插件