2010-12-07 44 views
12

我在我的JSF應用程序中遇到i18n枚舉問題。當我開始時,我已經枚舉了裏面定義的文本。但現在,我在枚舉中擁有與消息捆綁綁定的密鑰。本地化資源包中的枚舉值

比如我的枚舉之一:

public enum OrderStatus implements CustomEnum { 
    PENDING("enum.orderstatus.pending"), 
    CANCELED("enum.orderstatus.canceled"); 

    /** 
    * key in message bundle 
    */ 
    private String name; 

    OrderStatus(String name) { 
     this.name = name; 
    } 

    @Override 
    public String getName() { 
     return name; 
    } 

} 

在視圖層,我使用類似:

<!-- input --> 
<h:selectOneMenu value="#{order.status}"> 
    <f:selectItems value="#{flowUtils.orderStatuses}"/> 
</h:selectOneMenu> 

<!-- output --> 
<h:outputText value="#{order.status}"/> 

,並在Java中:

public class FlowUtils { 
    public List<SelectItem> getOrderStatuses() { 
     ArrayList<SelectItem> l = new ArrayList<SelectItem>(); 
     for(OrderStatus c: OrderStatus.values()) { 
      // before i18n 
      // l.add(new SelectItem(c, c.getName())); 

      // after i18n 
      l.add(new SelectItem(c, FacesUtil.getMessageValue(c.getName()))); 
     } 
     return l;    
    } 
} 

public class FacesUtil { 
    public static String getMessageValue(String name) { 
     FacesContext context = FacesContext.getCurrentInstance(); 
     return context.getApplication().getResourceBundle(context, "m").getString(name); 
    } 
} 

它運作良好,但當我需要輸出#{order.status}時,我需要將其轉換。 所以我實現了一個轉換器,但在getAsObject()方法中將String轉換爲Object遇到了麻煩。

的web.xml:

<converter> 
    <converter-for-class>model.helpers.OrderStatus</converter-for-class> 
    <converter-class>model.helpers.EnumTypeConverter</converter-class> 
</converter> 

的Java:

public class EnumTypeConverter implements Converter { 

    @Override 
    public Object getAsObject(FacesContext context, UIComponent comp, 
      String value) throws ConverterException { 
     // value = localized value :(
     Class enumType = comp.getValueBinding("value").getType(context); 
     return Enum.valueOf(enumType, value); 
    } 

    @Override 
    public String getAsString(FacesContext context, UIComponent component, 
      Object object) throws ConverterException { 
     if (object == null) { 
      return null; 
     } 
     CustomEnum type = (CustomEnum) object; 
     ResourceBundle messages = context.getApplication().getResourceBundle(context, "m"); 
     String text = messages.getString(type.getName()); 
     return text; 
    } 

} 

我和現在糾纏。任何人都知道如何有效地國際化多個枚舉?

回答

25

通過轉換器的值不是您期望的選項標籤,而是選項值。最好的做法是不要在模型方面做到這一點,但從視角來看,因爲模型不需要知道。

至於方法,你基本上是不必要的過分複雜的事情。由於JSF 1.2有一個內置的EnumConverter,它將自動引入,並且自JSF 2.0以來,您可以通過新的var屬性迭代通用數組或,f:selectItems,而無需在模型中通過List<SelectItem>複製值。

這裏的bean可以什麼樣子:

public class Bean { 
    private OrderStatus orderStatus; 
    private OrderStatus[] orderStatuses = OrderStatus.values(); 

    // ... 
} 

而這裏的景色可看怎麼樣(假設msg<var>爲你definied在<resource-bundle>faces-config.xml):

<h:selectOneMenu value="#{bean.orderStatus}"> 
    <f:selectItems value="#{bean.orderStatuses}" var="orderStatus" 
     itemValue="#{orderStatus}" itemLabel="#{msg[orderStatus.name]}" /> 
</h:selectOneMenu> 

就是這樣。


無關的問題,你在枚舉名稱和消息密鑰錯別字,應該是:

PENDING("enum.orderstatus.pending"), 
CANCELLED("enum.orderstatus.cancelled"); 

而且,更清潔是保持捆鍵調出枚舉和使用枚舉本身作爲捆綁鍵的一部分。例如。

PENDING, 
CANCELLED; 
<h:selectOneMenu value="#{bean.orderStatus}"> 
    <f:selectItems value="#{bean.orderStatuses}" var="orderStatus" 
     itemValue="#{orderStatus}" itemLabel="#{msg['enum.orderstatus.' += orderStatus]}" /> 
</h:selectOneMenu> 
enum.orderstatus.PENDING = Pending 
enum.orderstatus.CANCELLED = Cancelled 
1

恩,enum只是另一個類。沒有任何東西阻止您添加解析和字符串轉換方法,這些方法將解析和輸出與區域相關的消息。

也許它違反了單責任原則(是否?),但我相信讓枚舉負責解析和返回區域感知值是正確的。

只需添加這兩種方法是這樣的:

public String toString(FacesContext context) { 
    // need to modify the method 
    FacesUtil.getMessageValue(context, name); 
} 

public OrderStatus parse(FacesContext context, String theName) { 
    for (OrderStatus value : values()) { 
    if (value.toString(context).equals(theName) { 
     return value; 
    } 
    } 
    // think of something better 
    return null; 
} 

我希望我得到了正確的代碼,因爲我不與現在IDE檢查它......難道這就是你要找的人?

+0

代碼是正確的,但這不適用於OP的情況,因爲它不是傳遞給轉換器的選項標籤。 – BalusC 2010-12-07 13:54:26

1

我計算像如下所示的枚舉消息密鑰;所以不需要維持在枚舉與附加屬性的鍵

public String getMessageKey() { 
    return String.format("enum_%s_%s", this.getClass().getSimpleName(), 
      this.name()); 
} 

然後,我使用這樣

 <p:selectOneMenu id="type" 
     value="#{xyzBean.type}" required="true"> 
      <f:selectItems 
       value="#{xyzBean.possibleTypes}" 
       var="type" itemLabel="#{msg[type.messageKey]}"> 
      </f:selectItems> 
    </p:selectOneMenu> 

與應用中上下文,其具有配置了org.springframework.context.support.ReloadableResourceBundleMessageSource

<bean id="msg" 
    class="org.springframework.context.support.ReloadableResourceBundleMessageSource"> 
    <property name="basename" value="/resources/locale/messages" /> 
    <property name="useCodeAsDefaultMessage" value="true" /> 
    <property name="cacheSeconds" value="1" /> 
</bean> 
2

我在這裏發佈了我的解決方案:Internationalization of multiple enums (translation of enum values) - 但仍希望進一步提高。

編輯:與@Joop埃根的幫助下,我們已經提出了一個非常酷的解決方案:

再次編輯:完成並準備使用的解決方案:

使類

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

使它成爲一個自定義EL函數

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

的taglib添加到你的web.xml

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

有屬性文件enum_en.properties和enum_yourlanguage.properties這樣

TransferStatus.NOT_TRANSFERRED = Not transferred 
TransferStatus.TRANSFERRED = Transferred 

屬性文件作爲資源包添加到您的面孔,配置。XML

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

自定義標籤庫添加到您的XHTML文件

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

而且 - 瞧 - 你現在可以在JSF訪問翻譯枚舉值:

<h:outputText value="#{enum[l:xlate(order.transferStatus)]}" /> 
0

如果任何人正在尋找一個簡單的實用程序庫來處理枚舉國際化,請看看https://github.com/thiagowolff/litefaces-enum-i18n

神器也可以在Maven的中央:

<dependency> 
    <groupId>br.com.litecode</groupId> 
    <artifactId>litefaces-enum-i18n</artifactId> 
    <version>1.0.1</version> 
</dependency> 

基本上,你只需要對工件添加到您的項目,並定義枚舉各個按鍵下面所描述的枚舉命名約定。可以使用提供的EL函數來檢索翻譯(以及CSS類名稱)。