I\'m attempting to internationalize some of our code. I have a page in JSPX which is using the
tag to resolve strings from a messag
In addition to answer of @Toilal you can add a helper function to strings.jsp to do a better use of the translation array:
<%@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. 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. 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.
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.
1) Create a method in Spring Controller to load all resource keys from resource bundle, and return the view "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"
<%@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.
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).
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.
Typically JS i18n is done in one of two ways:
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:
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.
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):
<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:
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
attributetext
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). One can even create a tool that would scan files for <spring:message />
tags and automatically generate properties files{{{curly braces}}}
That's basically it. I recommend using templates if that's possible.