在资源包中本地化枚举值

时间:2010-12-07 10:23:12

标签: jsf-2 enums localization

我的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}时,我需要转换它。 所以我实现了一个转换器,但在String方法中将Object转换为getAsObject()时遇到了麻烦。

的web.xml:

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

爪哇:

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;
    }

}

我现在纠结于此。有谁知道如何有效地使多个Enum国际化?

5 个答案:

答案 0 :(得分:25)

通过转换器传递的值不是您所期望的选项标签,而是选项值。最佳做法是在模型方面不要这样做,但在视图方面,因为模型不需要知道。

至于方法,你基本上不必要地使事情过于复杂。从JSF 1.2开始,内置EnumConverter将自动启动,因为JSF 2.0可以通过新的List属性迭代通用数组或f:selectItems var需要在模型中的List<SelectItem>上复制值。

以下是bean的外观:

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

    // ...
}

以下是视图的外观(假设msg引用了<var> <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;

而且,更干净的是将捆绑密钥保留在枚举中并使用枚举本身作为捆绑密钥的一部分。 E.g。

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

答案 1 :(得分:2)

我已在此处发布了我的解决方案:Internationalization of multiple enums (translation of enum values) - 但仍希望进一步增强。

编辑:在@Joop Eggen的帮助下,我们提出了一个非常酷的解决方案:

再次编辑:完整且随时可用的解决方案:

上课

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

将属性文件作为资源包添加到faces-config.xml

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

将自定义taglib添加到xhtml文件

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

并且 - vo - - 您现在可以在jsf中访问已翻译的枚举值:

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

答案 2 :(得分:1)

嗯,enum只是另一个类。没有什么可以阻止您添加解析和转换字符串转换方法来解析和输出对区域设置敏感的消息。

也许它违反了单一责任原则(是吗?),但我认为让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检查它...这就是你要找的东西吗?

答案 3 :(得分: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>

答案 4 :(得分:0)

如果有人正在寻找一个简单的实用程序库来处理枚举国际化,请查看https://github.com/thiagowolff/litefaces-enum-i18n

该工件也可在Maven Central中使用:

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

基本上,您只需要将工件添加到项目中,并按照所描述的枚举命名约定定义枚举相应的键。可以使用提供的EL函数检索翻译(以及CSS类名称)。