web-dev-qa-db-fra.com

Résoudre le printemps: messages en javascript pour l'internationalisation i18n

J'essaie d'internationaliser une partie de notre code. J'ai une page dans JSPX qui utilise le <spring:message> tag pour résoudre les chaînes d'un message.properties fichier. Cela fonctionne très bien pour le HTML et le CSS qui se trouvent dans la page JSPX, mais là, un fichier javascript est fourni, et en remplaçant le <spring:message> tag pour la chaîne là-dedans signifie simplement qu'elle est imprimée textuellement.

Mon JSPX source le javascript comme ceci:

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

Le JS où je regarde le remplacer la chaîne est ci-dessous:

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

Et enfin, le message.properties est quelque chose comme:

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

Alors ce que je me demande, est-ce que cela devrait fonctionner? Il me semble que cela devrait, d'après ce que j'ai rassemblé sur divers forums, mais je ne peux pas comprendre où je me trompe. Y a-t-il une meilleure façon de procéder?

Je dois également noter que ces fichiers sont en dehors du dossier WEB-INF, mais en plaçant ReloadableResourceBundleMessageSource dans la racine applicationContext.xml, les balises Spring sont récupérées.

Merci pour toute aide!

25
olan

Il me semble que ce que vous voulez faire est de traiter le fichier JS comme un fichier JSP et de résoudre son contenu via spring: message tag.
Je ne ferais pas ça.

En règle générale, JS i18n se fait de deux manières:

  • En écrivant Tableau de chaînes traduites de la page JSP
  • En créant un filtre de traduction et en fournissant un fichier JS pré-traduit au client demandeur

Les deux fonctionnent mieux si vous créez un emplacement central pour vos chaînes traduisibles côté client.
Dans votre contexte, je recommanderais la première méthode (beaucoup plus facile). C'est à moins que votre projet ne soit assez volumineux et que vous ayez beaucoup de chaînes traduisibles côté client. La modification ressemblerait donc à:

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

Et dans votre fichier JS:

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

Rappelez-vous que j'ai utilisé des guillemets doubles pour écrire des chaînes traduites. C'est à cause de certains mots en français ou en italien qui pourraient contenir une apostrophe.

Modifier: entrée supplémentaire

Nous fournissons des traductions aux fichiers JS pour la raison. Habituellement, la raison est que nous voulons créer dynamiquement une partie de l'interface utilisateur. Il y a aussi des cas où nous devons localiser certains composants tiers, ma réponse ci-dessus les traite assez bien.
Dans les cas où nous voulons créer des parties d'interface utilisateur de manière dynamique, il est vraiment judicieux d'utiliser des modèles plutôt que de concaténer des balises HTML en JavaScript. J'ai décidé d'écrire ceci, car cela rend la solution beaucoup plus propre (et éventuellement réutilisable).
Donc, au lieu de passer des traductions à JavaScript, on peut créer un modèle et le mettre sur la page (mon exemple utilisera Handlebars.js , mais je pense qu'il est possible d'utiliser n'importe quel autre moteur ):

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

Côté client (c'est-à-dire en JavaScript) tout ce que vous avez à faire est d'accéder au modèle (l'exemple ci-dessous utilise évidemment jQuery) et de compiler:

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

Peu de choses à noter ici:

  • <spring:message /> les balises échappent à la fois au HTML et au JS par défaut, nous n'avons pas besoin de spécifier l'attribut javaScriptEscape
  • Il est logique de fournir l'attribut text pour <spring:message /> tag car il pourrait être utilisé comme solution de rechange (s'il n'y a pas de traduction pour une langue donnée) ainsi que comme commentaire (ce que cet élément représente). On peut même créer un outil qui analyserait les fichiers pour <spring:message /> balises et générer automatiquement des fichiers de propriétés
  • Pour empêcher les guidons d'échapper au contenu HTML, j'ai utilisé le triple {{{curly braces}}}

C'est essentiellement ça. Je recommande d'utiliser des modèles si cela est possible.

38
Paweł Dyda

Merci pour votre réponse. Voici une solution plus générique.

L'idée est de fournir un fichier javascript dynamique nommé "string.js", contenant un tableau associatif de messages enregistrés dans le bundle de ressources Java Java, en utilisant le langage utilisateur actuel.

1) Créez une méthode dans Spring Controller pour charger toutes les clés de ressource du bundle de ressources et renvoyez la vue "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) Implémentez 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) Importez "spring.js" dans votre code source HTML. Le tableau des messages est disponible et chargé dans la bonne langue.

Problème possible: Si l'utilisateur change sa langue, "spring.js" doit être rechargé par le navigateur, mais il sera mis en cache. L'effacement du cache est nécessaire lorsque l'utilisateur change de langue (ou autre astuce pour recharger le fichier).

20
Toilal

En plus de répondre à @Toilal, vous pouvez ajouter une fonction d'aide à strings.jsp pour mieux utiliser le tableau de traduction:

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

Ensuite, si nécessaire, utilisez simplement:

alert(t("menu.section.main"));
1
Pedro Pelaez

Il est assez simple de transformer le message.properties de spring en un objet JavaScript, puis d'utiliser cet objet dans d'autres fichiers JavaScript. Il y a un module de noeud avec lequel nous pouvons transformer

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

à

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

Ensuite, un fichier messages_ {lang} .js doit être créé pour chaque message. {Lang} .properties et référencé dans les modèles. Dans un modèle thymeleaf, cela ressemblerait à ceci:

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

J'ai créé un rapide plugin grunt à cet effet.

0
devilcius