为 i18n 国际化解析 spring:messages in javascript

Posted

技术标签:

【中文标题】为 i18n 国际化解析 spring:messages in javascript【英文标题】:Resolving spring:messages in javascript for i18n internationalization 【发布时间】:2011-09-07 07:39:15 【问题描述】:

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

我的 JSPX 像这样获取 javascript:

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

我正在寻找替换字符串的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
]);

最后 message.properties 类似于:

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

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

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

感谢您的帮助!

【问题讨论】:

【参考方案1】:

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

通常 JS i18n 以两种方式之一完成:

通过从 JSP 页面写出已翻译字符串数组 通过创建翻译过滤器并将预翻译的JS文件提供给请求客户端

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

<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" />

在你的 JS 文件中:

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

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

编辑:附加输入

出于这个原因,我们提供了对 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>

在客户端(即在 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);

这里有几点需要注意:

&lt;spring:message /&gt; 标签默认同时转义 HTML 和 JS,我们不需要指定 javaScriptEscape 属性 为&lt;spring:message /&gt; 标签提供text 属性是有意义的,因为它可以用作后备(如果没有给定语言的翻译)以及注释(此元素代表什么)。甚至可以创建一个工具来扫描文件中的&lt;spring:message /&gt; 标签并自动生成属性文件 为了防止 Handlebars 转义 HTML 内容,我使用了三重 curly braces

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

【讨论】:

很好的答案,但 spring:message 标签应该正确终止以避免错误。我试图编辑,但由于它只添加了 2 个字符,所以告诉我离开,因为显然它不够有建设性,除非有 6 个或更多字符:) @GreenDay:谢谢,我自己没有注意到这个问题。现在应该没问题了。 @PawełDyda 请注意,我使用双引号来写出翻译后的字符串。这是因为法语或意大利语中的某些单词可能包含撇号。 这不就是 javaScriptEscape='true' 应该注意的吗? 标签默认同时转义HTML和JS,我们不需要指定javaScriptEscape属性 那你上面为什么指定呢? @domenicop:基本上,我从问题中重写了代码,没有考虑太多。没错,spring:message 标签默认转义文本(暗示javaScriptEscape='true')。问题是,有时您实际上不想转义这个东西(即您想保留 HTML 标记),然后您需要指定 javaScriptEscape='false' 并使用双引号。这就是为什么我在重新审视这个问题后决定保留它。【参考方案2】:

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

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

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) 实现 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) 在您的 HTML 源代码中导入“spring.js”。 Messages 数组可用并加载了正确的语言。

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

【讨论】:

【参考方案3】:

除了@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;

然后在需要时使用:

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

【讨论】:

【参考方案4】:

将 spring 的 message.properties 转换为 JavaScript 对象,然后在其他 JavaScript 文件中使用该对象非常简单。我们可以使用node module 进行转换

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

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

然后应该为每个 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>

为此,我创建了一个快速grunt plugin。

【讨论】:

以上是关于为 i18n 国际化解析 spring:messages in javascript的主要内容,如果未能解决你的问题,请参考以下文章

Cocos2d-x支持 i18n 国际化——i18n XML 解析生成头文件

Springboot国际化信息(i18n)解析

SpringBoot 快速支持国际化i18n

vue中的js文件如何使用i18n 国际化

vue中的js文件如何使用i18n 国际化

为什么国际化缩短为“i18n”?