Локализация значений 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.