Plusieurs Fichiers de Ressource
Plusieurs Fichiers de Ressource
Beaucoup de projets requissent plus qu'un seul ResourceBundle
(fichier de traductions au format "Java Properties") pour mieux organiser des messages localisés. Cet exemple montrera comment customiser Stripes afin de définir n'importe quel nombre de ResourceBundle
. Par ex :
application.properties
(messages générals et libellés des champs de formulaire)images.properties
(attributssrc
etalt
pourstripes:images
)erreurs.properties
(Validation Stripes et messages générals)
Pour informer Stripes de nos noms de ResourceBundle
nous allons créer une classe de configuration customisée pour qu'on puisse ajouter un nouveau paramètre (pour les noms des nos ResourceBundle
) puis spécifier un LocalizationBundleFactory
customisé qui retournera notre ResourceBundle
customisé qui cherchera dans tous nos ResourceBundle
spécifiés tel que l'on a spécifié.
Configuration
<filter> <display-name>Stripes Filter</display-name> <filter-name>StripesFilter</filter-name> <filter-class> net.sourceforge.stripes.controller.StripesFilter </filter-class> <init-param> <param-name>ActionResolver.Packages</param-name> <param-value>action</param-value> </init-param> <init-param> <param-name>Configuration.Class</param-name> <param-value>ext.CustomRuntimeConfiguration</param-value> </init-param> <init-param> <param-name>LocalizationBundleFactory.Class</param-name> <param-value>ext.CustomLocalizationBundleFactory</param-value> </init-param> <init-param> <param-name>ResourceBundles.BaseNames</param-name> <param-value>application,images,erreurs</param-value> </init-param> </filter>
Les 3 premiers init-param
appartiennent à Stripes, et le dernier : ResourceBundles.BaseNames
est notre init-param
customisé.
Référence de Configuration
Pour plus d'info sur la configuration de Stripes jetez un coup d'œil au Référence de Configuration.
Factory de Localisation de ResourceBundle Customisé
package ext; import java.util.*; import net.sourceforge.stripes.config.Configuration; import net.sourceforge.stripes.localization.LocalizationBundleFactory; import net.sourceforge.stripes.util.StringUtil; /** * Permits cycling through multiple named ResourceBundles instead of * just StripesResources.properties using the init-param * ResourceBundles.BaseNames ( * {@link #RESOURCE_BUNDLES_BASE_NAMES}) * * @author DJDaveMark */ public class CustomLocalizationBundleFactory implements LocalizationBundleFactory { /** * The Configuration Key which specifies * multiple resource bundles. */ public static final String RESOURCE_BUNDLES_BASE_NAMES = "ResourceBundles.BaseNames"; private String[] bundles; public ResourceBundle getFormFieldBundle(Locale locale) { return new MultipleResourceBundle(locale, getBundleNames()); } public ResourceBundle getErrorMessageBundle(Locale locale) { return new MultipleResourceBundle(locale, getBundleNames()); } public void init(Configuration config) { String bundleNames = config.getBootstrapPropertyResolver() .getProperty(RESOURCE_BUNDLES_BASE_NAMES); bundles = StringUtil.standardSplit(bundleNames); } public List<String> getBundleNames() { return Arrays.asList(bundles); } }
Ici nous informons Stripes d'utiliser la même classe-multi-bundle-customisée tout le temps. Pour plus d'info voir le JavaDoc pour LocalizationBundleFactory et DefaultLocalizationBundleFactory. Le BootstrapPropertyResolver
permet de récupérer le param-value
associé avec notre param-name
customisé : ResourceBundles.BaseNames
. Puis la méthode StringUtil#standardSplit(String)
de Stripes permet de séparer tous le noms de ResourceBundle
.
ResourceBundle Customisé
package ext; import java.util.*; import net.sourceforge.stripes.controller.StripesFilter; import net.sourceforge.stripes.localization.DefaultLocalizationBundleFactory; /** * With thanks to Freddy's Stripes Book http://www.pragprog.com/titles/fdstr * * @author DJDaveMark * @author Fred Daoud */ public class MultipleResourceBundle extends ResourceBundle { private Locale locale; private List<String> bundleNames; public MultipleResourceBundle(Locale locale, List<String> bundleNames) { this.locale = locale; this.bundleNames = bundleNames; } @Override public Enumeration<String> getKeys() { return null; } @Override protected Object handleGetObject(String key) { Object result = null; if (bundleNames != null) { // Look in each configured bundle for (String bundleName : bundleNames) { if (bundleName != null) { result = getFromBundle(locale, bundleName, key); if (result != null) { break; } } } } if (result == null) { // Try the application's default bundle String bundleName = DefaultLocalizationBundleFactory.BUNDLE_NAME; result = getFromBundle(locale, bundleName, key); } return result; } /** * Returns null if the bundle or key is not found. No exceptions thrown. */ private String getFromBundle(Locale loc, String bundleName, String key) { String result = null; ResourceBundle bundle = ResourceBundle.getBundle(bundleName, loc); if (bundle != null) { try { result = bundle.getString(key); } catch (MissingResourceException exc) { } } return result; } }
Nous pouvons désormais réorganiser nos ResourceBundle
comme ceci :
app.title=Application Multi-ResourceBundle my.label=Mon libellé # Chaine de caractères utilises par la balise <stripes:messages /> stripes.messages.header=<ul class="messages"> stripes.messages.beforeMessage=<li> stripes.messages.afterMessage=</li> stripes.messages.footer=</ul> ...
image.logo.src=images/logo_fr.jpg image.logo.alt=Logo de l'Appli M.R.B. ...
# Chaine de caractères utilises par la balise <stripes:errors /> stripes.erreurs.header=<div style="color:#b72222; font-weight: bold">\ Veuillez corriger les erreurs suivantes :</div><ol> stripes.erreurs.beforeError=<li style="color: #b72222;"> stripes.erreurs.afterError=</li> stripes.erreurs.footer=</ol> # Messages d'erreurs utilisés par les # annotations de validation de Stripes. validation.required.valueNotPresent={0} est un champ requis validation.minlength.valueTooShort={0} doit contenir au \ moins {2} caractères validation.maxlength.valueTooLong={0} ne doit contenir plus \ que {2} caractères ...
JSTL
Tout ce qu'il manque est une façon d'informer les balises JSTL fmt (JSTL taglibs) lesquels de nos ResourceBundle
qu'il faut utiliser. Vu que les balises JSTL ne permettent pas vraiment de spécifier plusieurs ResourceBundle
nous pouvons au moins configurer notre ResourceBundle
principal dans le fichier web.xml
:
<context-param> <param-name> javax.servlet.jsp.jstl.fmt.localizationContext </param-name> <param-value>application</param-value> </context-param>
puis, au cas où nous avons besoin d'utiliser les autres ResourceBundle
hors des balises Stripes, nous pouvons spécifier ce qui suit une fois dans une JSP qui sera inclus dans toutes nos JSP :
<fmt:setBundle var="images" basename="images" scope="application" /> <fmt:setBundle var="erreurs" basename="erreurs" scope="application" />
JSTL fmt
Pour plus d'info sur comment les balises JSTL fmt gèrent des ResourceBundle
consulter les Références de Balises :
Rassemblons le tout
<!-- trouvé dans application.properties --> <fmt:message key="app.title" /> <br /> <!-- trouvé dans erreurs.properties --> <s:errors /> <s:form beanclass="action.TestAction"> <!-- trouvé dans application.properties --> <s:label for="my.label" /> <br /> <!-- trouvé dans images.properties --> <!-- .src et .alt sont ajoutés automatiquement pour générer --> <!-- <input alt="Logo de l'Appli M.R.B." name="image.logo" src="images/logo_fr.jpg" type="image" /> --> <s:image name="image.logo" /> <br /> <!-- outrepasser le ResourceBundle de defaut de JSTL --> <!-- trouvé dans images.properties --> Le source de l'image est : <fmt:message bundle="${images}" key="image.logo.src" /> </s:form>
Bien sûr nous aurions pu tricher et au lieu d'utiliser <fmt:message key="" bundle="${}" />
nous aurions pu très bien utiliser <s:label for="" />
qui se dirige vers notre classe MultipleResourceBundle
, mais hélas, cela serait de la triche! ;o)