简体   繁体   English

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

[英]Resolving spring:messages in javascript for i18n internationalization

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.properties file. 我在JSPX中有一个页面,它使用<spring:message>标签来解析message.properties文件中的字符串。 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页面中的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! 谢谢你的帮助!

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. 在我看来,你想要做的是将JS文件视为JSP文件,并通过spring:message标签解析其内容。
I would not do that. 我不会这样做。

Typically JS i18n is done in one of two ways: 通常JS i18n可以通过以下两种方式之一完成:

  • By writing out Array of translated strings from JSP page 通过从JSP页面写出已翻译的字符串数组
  • By creating translation filter and provide pre-translated JS file to requesting client 通过创建翻译过滤器并向请求客户端提供预翻译的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 lot of 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. 我们提供JS文件的翻译。 Usually, the reason is we want to create some part of UI dynamically. 通常,原因是我们想要动态创建UI的某些部分。 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. 对于我们想要动态创建UI部分的情况,使用模板而不是在JavaScript中连接HTML标记确实很有意义。 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): 因此,不是将翻译传递给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 javaScriptEscape attribute <spring:message />标签默认转义为HTML和JS,我们不需要指定javaScriptEscape属性
  • It make sense to provide text attribute 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). <spring:message />标记提供text属性是有意义的,因为它可以用作后备(如果没有给定语言的翻译)以及注释(此元素代表什么)。 One can even create a tool that would scan files for <spring:message /> tags and automatically generate properties files 甚至可以创建一个工具来扫描<spring:message />标签的文件并自动生成属性文件
  • To prevent Handlebars from escaping HTML contents, I used triple {{{curly braces}}} 为了防止Handlebars转义HTML内容,我使用了三{{{curly braces}}}

That's basically it. 基本上就是这样。 I recommend using templates if that's possible. 如果可能,我建议使用模板。

Thanks for your answer. 感谢您的回答。 Here is a more generic solution. 这是一个更通用的解决方案。

The idea is to provide a dynamic javascript file 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 Controller to 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)实现View “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. 3)在HTML源代码中导入“spring.js”。 Messages array is available and loaded with the right language. Messages数组可用并使用正确的语言加载。

Possible issue : If user change his language, "spring.js" must be reloaded by the navigator, but it will be cached. 可能的问题 :如果用户更改了他的语言,导航器必须重新加载“spring.js”,但它将被缓存。 Clearing the cache is needed when user change his language (or other trick to get the file reloaded). 当用户更改其语言(或其他技巧以重新加载文件)时,需要清除缓存。

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

It's quite simple to transform spring's message.properties to a JavaScript object and then using that object in other JavaScript files. 将spring的message.properties转换为JavaScript对象然后在其他JavaScript文件中使用该对象非常简单。 There's a node module with which we can transform 有一个节点模块 ,我们可以用它来转换

  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. 然后应为每个消息创建messages_ {lang} .js文件。{lang} .properties并在模板中引用。 In a thymeleaf template it would look like this: 在百里香叶模板中,它看起来像这样:

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

I've created a quick grunt plugin for this purpose. 我为此创建了一个快速的grunt插件

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM