2

Again something that has been discussed before and where I wanted to share "my" solution and ask for enhancements, other approaches or best practices.

I have several enums where I need internationalization (I need the enum values translated into some languages in order to display them in a jsf page). Examle enum:

public enum TransferStatus {
  NOT_TRANSFERRED,
  TRANSFERRED
}

Translation would be for example Not yet transferred / Transferred, all good

The translation should be stored in a MessageBundle (properties files). I was searching for an easy, generic solution (best would be without the need of writing extra code in all the enums) that does not need much on the jsf side. Just to mention it, of course it it possible that two different enums shae the same enum value (e.g. values like COMPLETED that have a different meaning in different enums).

The solution I came up with:

(1) Store translations in the properties file like this:

TransferStatus.NOT_TRANSFERRED = Not yet transferred
TransferStatus.TRANSFERRED = Transferred, all good

(2) Make a helper class that takes an enum and generates the lookup key:

public class EnumTranslator {
  public static String getMessageKey(Enum<?> e) {
    return e.getClass().getSimpleName() + '.' + e.name();
  }
}

(3) Add this code to every enum:

public String getKey() {
  return EnumTranslator.getMessageKey(this);
}

(4) Now, I can access the translated values of my enums like this:

<h:outputText value="#{enum[order.transferStatus.key]}" />

Which is okay, but what I just don't like is adding the same getKey() method to every enum. There should be something better that that! Now it's your turn, SO :-)

Community
  • 1
  • 1
Manuel M
  • 809
  • 1
  • 10
  • 25

4 Answers4

3

Ok, now this is the complete and ready-to-use solution: (thanks to @Joop Eggen)

Make a class

public final class EnumTranslator {
  public static String getMessageKey(Enum<?> e) {
    return e.getClass().getSimpleName() + '.' + e.name();
  }
}

Make it a custom EL function

<?xml version="1.0" encoding="UTF-8"?>
<facelet-taglib 
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibrary_2_0.xsd"
version="2.0">
<namespace>http://example.com/enumi18n</namespace>
<function>
    <function-name>xlate</function-name>
    <function-class>your.package.EnumTranslator</function-class>
    <function-signature>String getMessageKey(java.lang.Enum)</function-signature>
</function>
</facelet-taglib>

Add the taglib to your web.xml

<context-param>
    <param-name>javax.faces.FACELETS_LIBRARIES</param-name>
    <param-value>/WEB-INF/enumi18n.taglib.xml</param-value>
</context-param>

Have properties files enum_en.properties and enum_yourlanguage.properties like this

TransferStatus.NOT_TRANSFERRED = Not transferred
TransferStatus.TRANSFERRED = Transferred

Add the properties files as resource bundles to your faces-config.xml

    <resource-bundle>
        <base-name>kk.os.obj.jsf.i18n.enum</base-name>
        <var>enum</var>
    </resource-bundle>

Add the custom taglib to your xhtml files

<html ... xmlns:l="http://example.com/enumi18n">

And - voilà - you can now access the translated enum values in jsf:

<h:outputText value="#{enum[l:xlate(order.transferStatus)]}" />
Manuel M
  • 809
  • 1
  • 10
  • 25
1

I would make an EL function, leaving the enum classes as they are:

#{l:xlate(order.transferStatus)}

Okay, xlate has an Object parameter then (or Enum<?>). But the enum classes stay as they are.

Joop Eggen
  • 107,315
  • 7
  • 83
  • 138
  • This is just AWESOME. Had to change nothing but adding the custom function to call my `EnumTranslator.getMessageKey()` (see [here](http://stackoverflow.com/questions/7079978/how-to-create-a-custom-el-function) how to do it) and now I can access the translated enum values like this: ``. Brilliant! Thanks a lot! – Manuel M Mar 26 '13 at 09:53
1

The way I'd be doing this takes a bit more code (but far less than the other answers I've seen here), but I feel is more reliable:

@ManagedBean
@ApplicationScoped
public class EnumTranslator {

    private <E extends Enum<E>> Map<E, String> getPresentableNames(Class<E> enumClass) {
        ResourceBundle resources = ResourceBundle.getBundle(
            "com.example.app.MyMessageBundle",
            FacesContext.getCurrentInstance().getViewRoot().getLocale());

        String prefix = enumClass.getSimpleName() + ".";

        Map<E, String> map = new EnumMap<E, String>(enumClass);
        for (E value : enumClass.getEnumConstants()) {
            map.put(value, resources.getString(prefix + value));
        }

        return map;
    }

    // Bean method, accessible via EL
    public Map<?, ?> getTransferStatuses() {
        return getPresentableNames(TransferStatus.class);
    }
}

Then your page can do:

<h:outputText value="#{enumTranslator.transferStatuses[order.transferStatus]}" />
VGR
  • 40,506
  • 4
  • 48
  • 63
1

What about:

public interface InternationalizationOfEnum {
default String getKey() {
      return EnumTranslator.getMessageKey(this);
    }
String name();}

and

public enum anyEnum implements InternationalizationOfEnum{
szcs
  • 11
  • 1