Локализация значений enum в пакете ресурса

у меня проблема с перечислениями i18n в моем приложении JSF. Когда я начал, у меня были перечисления с текстом, определенным внутри. Но теперь у меня есть ключи, привязанные к пакетам сообщений в перечислении.

пример одного из моих перечислений:

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 to Object на getAsObject() метод.

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

}

теперь я запутался в этом. Кто-нибудь знает как эффективно интернационализировать несколько перечислений?

5 ответов


значение, которое передается через конвертер, не является меткой опции, как вы, похоже, ожидаете, но значением опции. Лучшая практика-не делать этого на стороне модели, а на стороне представления, потому что модель не должна быть осведомлена.

что касается подхода, вы в основном излишне усложняете вещи. Поскольку JSF 1.2 есть встроенный EnumConverter который будет срабатывать автоматически и с JSF 2.0 вы можете перебирать общий массив или List на f:selectItems новая var атрибут без необходимости дублировать значения через List<SelectItem> в модели.

вот как Боб может выглядеть так:

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

я разместил свое решение здесь:интернационализация нескольких перечислений (перевод значений перечислений) - но все-таки надеясь на дальнейшее улучшение.

EDIT: с помощью @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 к вашему сеть.в XML

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

свойства файлов enum_en.свойства и enum_yourlanguage.свойства такой

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

и-вуаля-теперь вы можете получить доступ к переведенным значениям перечисления в jsf:

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

ну, 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... Это то, что вы ищете?


я вычисляю ключ сообщения в перечислении, как показано ниже; поэтому нет необходимости поддерживать ключи с дополнительными атрибутами в перечислении

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>

С настройки орг.springframework.контекст.поддержка.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>

если кто-то ищет простую служебную библиотеку для обработки интернационализации перечисления, пожалуйста, взгляните на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>

в принципе, вам просто нужно добавить артефакт в свой проект и определить соответствующие ключи перечисления, следуя описанным соглашениям об именах перечислений. Переводы (а также имена классов CSS) могут быть получены с помощью предоставленные функции EL.