I have developed the idea a bit further.  I think ResourceMessage is a better name (Message could mean too many other things). This is my contribution for you to include in the API if you want to.<br><br>Regards,<br>Rick<br>
<br><br><br><span style="font-family: courier new,monospace;">package ch.qos.cal10n;</span><br style="font-family: courier new,monospace;"><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">import java.io.Serializable;</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">import java.util.Locale;</span><br style="font-family: courier new,monospace;"><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">/**</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;"> * Holds a resource message with the locale lookup deferred until it is needed</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;"> * or until the locale is known. Instances are immutable so are safe to use as</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;"> * Exception data or share between threads. Note that the immutability is</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;"> * actually dependent on the supplied args themselves being immutable. If</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;"> * ResourceMessage instances are created that contain parameters that are</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;"> * mutable, the results could be unpredictable.</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;"> * </span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;"> * @author Rick Beton</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;"> */</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">public final class ResourceMessage implements Serializable {</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">    private static final long serialVersionUID = 6660897864328889682L;</span><br style="font-family: courier new,monospace;"><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">    private final Enum&lt;?&gt; e;</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">    private final Object[] args;</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">    // cache the result of toString</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">    private transient String string = null;</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">    // cache the result of hashCode</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">    private transient int hashcode = 0;</span><br style="font-family: courier new,monospace;">
<br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">    /**<br>     * Constructs an instance.<br>     * <br>     * @param e<br>     *            the key for the corresponding resource.<br>
     * @param args<br>     *            any message parameters, as required.<br>     */<br>    public ResourceMessage(Enum&lt;?&gt; e, Object... args) {</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">        this.e = e;</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">        if (args != null) {</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">            // take defensive copy</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">            this.args = new Object[args.length];</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">            System.arraycopy(args, 0, this.args, 0, args.length);</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">        } else {</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">            this.args = null;</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">        }</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">    }</span><br style="font-family: courier new,monospace;">
<br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">    /**<br>     * Gets the localized message from the resource bundle, using the specified<br>     * locale.<br>     * <br>
     * @return the message for the user<br>     */<br>    public String getLocalizedMessage(Locale locale) {</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">        final IMessageConveyor mc = new MessageConveyor(locale);</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">        return mc.getMessage(e, args);</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">    }</span><br style="font-family: courier new,monospace;">
<br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">    @Override</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">    public String toString() {</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">        if (string == null) {</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">            final StringBuilder b = new StringBuilder(&quot;Message(&quot;);</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">            b.append(<a href="http://e.name">e.name</a>());</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">            b.append(&quot;, [&quot;);</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">            if (args != null) {</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">                String comma = &quot;&quot;;</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">                for (Object o : args) {</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">                    b.append(comma).append(o);</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">                    comma = &quot;, &quot;;</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">                }</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">            }</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">            b.append(&quot;])&quot;);</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">            // there may be a thread race here but the outcome is never</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">            // uncertain</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">            string = b.toString();</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">        }</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">        return string;</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">    }</span><br style="font-family: courier new,monospace;">
<br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">    @Override</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">    public boolean equals(Object obj) {</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">        if (this == obj) {</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">            return true;</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">        }</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">        if (obj.getClass() != ResourceMessage.class) {</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">            return false;</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">        }</span><br style="font-family: courier new,monospace;">
<br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">        final ResourceMessage other = (ResourceMessage) obj;</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">        if (!this.e.equals(other.e)) {</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">            return false;</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">        } else if (this.args == null &amp;&amp; other.args == null) {</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">            return true;</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">        } else if (this.args == null || other.args == null) {</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">            return false;</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">        } else if (this.args.length != other.args.length) {</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">            return false;</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">        }</span><br style="font-family: courier new,monospace;">
<br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">        for (int i = 0; i &lt; args.length; i++) {</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">            if (this.args[i] == null &amp;&amp; other.args[i] == null) {</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">                // ok</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">            } else if (this.args[i] == null &amp;&amp; other.args[i] != null) {</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">                return false;</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">            } else if (!this.args[i].equals(other.args[i])) {</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">                return false;</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">            }</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">        }</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">        return true;</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">    }</span><br style="font-family: courier new,monospace;"><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">    @Override</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">    public int hashCode() {</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">        if (hashcode == 0) {</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">            int newHashCode = 0;</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">            newHashCode = e.hashCode();</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">            if (args != null) {</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">                for (Object o : args) {</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">                    if (o != null) {</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">                        newHashCode ^= o.hashCode();</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">                    }</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">                }</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">            }</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">            // there may be a thread race here but the outcome is never</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">            // uncertain</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">            hashcode = newHashCode;</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">        }</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">        return hashcode;</span><br style="font-family: courier new,monospace;">
<span style="font-family: courier new,monospace;">    }</span><br style="font-family: courier new,monospace;"><span style="font-family: courier new,monospace;">}</span><br style="font-family: courier new,monospace;"><br>