Локализация значений перечисления в пакете ресурсов - PullRequest
12 голосов
/ 07 декабря 2010

У меня проблема с перечислениями 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 в 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;
    }

}

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

Ответы [ 5 ]

25 голосов
/ 07 декабря 2010

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

Что касается подхода, то вы, по сути, излишне усложняете вещи.Начиная с 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>

Вот и все.


Не имеет отношения к проблеме, вы опечаткив имени enum и ключах сообщения это должно быть:

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

И, более чистым было бы исключение ключей пакета из enum и использование самого enum как части ключа пакета.Например,

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
2 голосов
/ 26 марта 2013

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

РЕДАКТИРОВАТЬ: с помощью @Joop Eggen мы придумали действительно классное решение:

EDIT снова: полное и готовое к использованию решение:

Сделать класс

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

Добавьте файлы свойств в виде комплектов ресурсов в файлface-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)]}" />
1 голос
/ 26 августа 2012

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

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>
1 голос
/ 07 декабря 2010

Ну, 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 голосов
/ 05 февраля 2015

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...