StrutsのメッセージファイルをXMLにする
Strutsではメッセージをpropertiesファイルに記述しますが、これを変更してXMLで書けるようにする方法を紹介します。
仕様
今回のサンプルでは、メッセージファイルはクラスパス直下に置くことにします。
多言語化を考慮して、メッセージファイルの末尾にはロケールを付与するようにします。
(ファイル名の例)
message_ja.xml
XMLの形式はこんな感じで。
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <messages> <message key="k0"> <text>メッセージ0</text> </message> <message key="k1"> <text>メッセージ1</text> </message> <message key="k2"> <text>メッセージ2</text> </message> </messages>
見たまんま。
クラスを作る
MessageResourcesとMessageResourcesFactoryを拡張したクラスを作ります。
まずはMessageResourcesの拡張。長いですが省略なしで。なおXMLのパースにはJAXBを使っています。
/** * */ package aaa.bbb; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import javax.xml.bind.JAXB; import javax.xml.bind.annotation.XmlAttribute; import javax.xml.bind.annotation.XmlElement; import javax.xml.bind.annotation.XmlRootElement; import org.apache.struts.util.MessageResources; import org.apache.struts.util.MessageResourcesFactory; /** * @author jabaraster */ public class XmlMessageResources extends MessageResources { private static final long serialVersionUID = -1390664385723178825L; private final Map<Locale, Map<String, Message>> messages = new ConcurrentHashMap<Locale, Map<String, Message>>(); /** * @param pFactory * @param pConfig */ public XmlMessageResources(final MessageResourcesFactory pFactory, final String pConfig) { super(pFactory, pConfig); } /** * @see org.apache.struts.util.MessageResources#getMessage(java.util.Locale, java.lang.String) */ @Override public String getMessage(final Locale pLocale, final String pMessageKey) { Map<String, Message> m = this.messages.get(pLocale); if (m == null) { m = load(pLocale, this.config); this.messages.put(pLocale, m); } final Message message = m.get(pMessageKey); if (message == null) { return "*** no message found for '" + pMessageKey + "' ***"; } return message.getText(); } private static void appendIfNotNull(final StringBuilder pBuffer, final String pToken) { if (pToken != null && pToken.trim().length() > 0) { pBuffer.append("_" + pToken); } } private static Map<String, Message> load(final Locale pLocale, final String pConfig) { try { final String path = resolvePath(pLocale, pConfig); final URL resourceLocation = XmlMessageResources.class.getResource(path); if (resourceLocation == null) { throw new IllegalStateException("メッセージリソース " + path + " が見付かりませんでした."); } final InputStream in = new BufferedInputStream(resourceLocation.openStream()); final Messages messages = JAXB.unmarshal(in, Messages.class); final Map<String, Message> ret = new ConcurrentHashMap<String, XmlMessageResources.Message>(); for (final Message msg : messages.getList()) { ret.put(msg.getKey(), msg); } return ret; } catch (final IOException e) { throw new IllegalStateException(e); } } private static String resolvePath(final Locale pLocale, final String pKey) { final StringBuilder buffer = new StringBuilder(); buffer.append("/"); buffer.append(pKey); appendIfNotNull(buffer, pLocale.getLanguage()); appendIfNotNull(buffer, pLocale.getCountry()); appendIfNotNull(buffer, pLocale.getVariant()); buffer.append(".xml"); return new String(buffer); } /** * @author jabaraster */ @XmlRootElement private static class Message { private String key; private String text; /** * */ @SuppressWarnings("unused") public Message() { // } /** * @return the key */ @XmlAttribute public String getKey() { return this.key; } /** * @return the text */ public String getText() { return this.text; } /** * @param pKey the key to set */ @SuppressWarnings("unused") public void setKey(final String pKey) { this.key = pKey; } /** * @param pText the text to set */ @SuppressWarnings("unused") public void setText(final String pText) { this.text = pText; } } /** * @author jabaraster */ @XmlRootElement private static class Messages { private List<Message> list = new ArrayList<XmlMessageResources.Message>(); /** * @return the list */ @XmlElement(name = "message") public List<Message> getList() { return this.list; } /** * @param pList the list to set */ @SuppressWarnings("unused") public void setList(final List<Message> pList) { this.list = pList; } } }
次にMessageResourceFactoryの拡張。さっき作ったXmlMessageResourcesを返すようにするだけです。
import org.apache.struts.util.MessageResources; import org.apache.struts.util.MessageResourcesFactory; /** * @author jabaraster */ public class XmlMessageResourcesFactory extends MessageResourcesFactory { private static final long serialVersionUID = -5520240862924828575L; /** * @see org.apache.struts.util.MessageResourcesFactory#createResources(java.lang.String) */ @Override public MessageResources createResources(final String pConfig) { return new XmlMessageResources(this, pConfig); } }
sturts-config.xmlを編集
XmlMessageResourcesFactoryをstruts-config.xmlに設定します。
今回は2つメッセージファイルを設定する例を挙げます。
(修正前)
<message-resources parameter="application" factory="org.seasar.struts.util.S2PropertyMessageResourcesFactory"/> <message-resources parameter="label" key="label" factory="org.seasar.struts.util.S2PropertyMessageResourcesFactory"/>
(修正後)
<message-resources parameter="application" factory="aaa.bbb.XmlMessageResourcesFactory"/> <message-resources parameter="label" key="label" factory="aaa.bbb.XmlMessageResourcesFactory"/>
メッセージファイルを作る
application_ja.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <messages> <message key="k0"> <text>メッセージ0</text> </message> <message key="k1"> <text>メッセージ1</text> </message> <message key="k2"> <text>メッセージ2</text> </message> </messages>
label_ja.xml
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <messages> <message key="k3"> <text>メッセージ3</text> </message> <message key="k4"> <text>メッセージ4</text> </message> <message key="k5"> <text>メッセージ5</text> </message> </messages>
使う
準備は完了です。
JSPでメッセージを表示してみます。
<%@ page pageEncoding="UTF-8" contentType="text/html; charset=UTF-8" %> <!DOCTYPE html> <html> <head> <title>XMLでメッセージ</title> <meta charset="UTF-8"> </head> <body style="line-height: 1.5em;"> <bean:message key="k0"/> <br/> <bean:message key="k3" bundle="label"/> </body> </html>