Локализация JSF 2 (управляемый компонент)
У меня есть файл свойств для локализации:
foo=Bar
title=Widget Application
Это связано как resource-bundle
в лицах-конфигурации:
<resource-bundle>
<base-name>com.example.messages.messages</base-name>
<var>msgs</var>
</resource-bundle>
Я могу получить доступ к этому просто отлично в представлении Facelets с помощью EL:
<title>#{msgs.title}</title>
Однако, если есть такие вещи, как SQLExceptions, мне нужно иметь возможность писать сообщения из управляемого компонента. Это все работает также:
FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "There was an error saving this widget.", null);
FacesContext.getCurrentInstance().addMessage(null, message);
Вот проблема: я хочу, чтобы эти сообщения приходили из файла свойств, чтобы их тоже можно было изменить в зависимости от локали. Есть ли простой способ получить доступ к файлу свойств с помощью инъекции?
5 ответов
Я задал довольно связанный вопрос о SO: Как внедрить несериализуемый класс (например, java.util.ResourceBundle) с помощью Weld
А внутри форума шва: http://seamframework.org/Community/HowToCreateAnInjectableResourcebundleWithWeld
Подводя итог: я реализовал инъекционный ResourceBundle с 3 продюсерами. Сначала вам нужен FacesContextProducer. Я взял тот из источников Шва 3 Альфы.
public class FacesContextProducer {
@Produces @RequestScoped
public FacesContext getFacesContext() {
FacesContext ctx = FacesContext.getCurrentInstance();
if (ctx == null)
throw new ContextNotActiveException("FacesContext is not active");
return ctx;
}
}
Затем вам нужен LocaleProducer, который использует FacesContextProducer. Я также взял это от Шва 3 Альфы.
public class FacesLocaleResolver {
@Inject
FacesContext facesContext;
public boolean isActive() {
return (facesContext != null) && (facesContext.getCurrentPhaseId() != null);
}
@Produces @Faces
public Locale getLocale() {
if (facesContext.getViewRoot() != null)
return facesContext.getViewRoot().getLocale();
else
return facesContext.getApplication().getViewHandler().calculateLocale(facesContext);
}
}
Теперь у вас есть все для создания ResourceBundleProducer, который может выглядеть так:
public class ResourceBundleProducer {
@Inject
public Locale locale;
@Inject
public FacesContext facesContext;
@Produces
public ResourceBundle getResourceBundle() {
return ResourceBundle.getBundle("/messages", facesContext.getViewRoot().getLocale() );
}
}
Теперь вы можете @Inject ResourceBundle в свои бины. Обратите внимание, что он должен быть вставлен во временный атрибут, иначе вы получите исключение, сообщающее, что ResourceBundle не сериализуем.
@Named
public class MyBean {
@Inject
private transient ResourceBundle bundle;
public void testMethod() {
bundle.getString("SPECIFIC_BUNDLE_KEY");
}
}
Вы можете сделать это только с JSF.
Начните с определения управляемого свойства в вашем компоненте поддержки. В конфигурации JSF вы можете установить значение управляемого свойства для выражения EL, которое ссылается на ваш пакет ресурсов.
Я сделал что-то вроде следующего, используя Tomcat 6. Единственное предостережение в том, что вы не можете получить доступ к этому значению из конструктора вашего компонента поддержки, так как JSF еще не инициализировал его. использование @PostConstruct
на метод инициализации, если значение необходимо в начале жизненного цикла бина.
<managed-bean>
...
<managed-property>
<property-name>messages</property-name>
<property-class>java.util.ResourceBundle</property-class>
<value>#{msgs}</value>
</managed-property>
...
</managed-bean>
<application>
...
<resource-bundle>
<base-name>com.example.messages.messages</base-name>
<var>msgs</var>
</resource-bundle>
...
</application>
Это дает преимущество, заключающееся в том, что ваши методы компонента поддержки в меньшей степени зависят от технологии представления, поэтому тестирование должно быть проще. Он также отделяет ваш код от таких деталей, как имя, данное пакету.
Некоторое тестирование с использованием Mojarra 2.0.4-b09 действительно показывает небольшое несоответствие, когда пользователь меняет локаль в середине сеанса. В выражениях EL на странице используется новая локаль, но компоненту поддержки не дается новая ссылка ResourceBundle. Чтобы сделать его согласованным, вы можете использовать значение свойства bean в выражениях EL, например: #{backingBean.messages.greeting}
на месте #{msgs.greeting}
, Тогда страница EL и компонент поддержки всегда будут использовать локаль, которая была активной в начале сеанса. Если пользователям приходилось переключать локали в середине сеанса и получать новые сообщения, вы могли бы попытаться создать bean-объект в области запросов и дать ему ссылки как на bean-компонент сеанса, так и на пакет ресурсов.
Это старый вопрос, но я добавляю другой способ сделать это. Я искал что-то еще и наткнулся на это. Методы здесь все казались запутанными для чего-то, что не кажется мне настолько сложным. Играю с блеском, потому что это мило, если вы спросите меня.
Учитывая файл:
/com/full/package/path/to/messages/errormessages.properties
Внутри файла:
SOME_ERROR_STRING=Your App Just Cratered
Я создаю метод "getBundle()", так как мне нравится ловить среду выполнения и добавлять значимое сообщение, чтобы я понял, откуда оно приходит. Не сложно и может помочь, если вы по какой-то причине получаете прихоть, чтобы поиграть с файлами свойств и не обновлять все правильно. Я иногда делаю это приватным, так как иногда это вспомогательный метод в классе (для меня). Это удерживает пометку try catch от осмысленного кода.
Использование полного пути к файлу позволяет поместить его где-то, кроме каталога / местоположения по умолчанию, если у вас есть другие идеи по организации.
public/private ResourceBundle getMessageResourceBundle(){
String messageBundle = "com.full.package.path.to.messages.errormessages";
ResourceBundle bundle = null;
try{
bundle = ResourceBundle.getBundle(messageBundle);
}catch(MissingResourceException ex){
Logger.getLogger(this.getClass().getName()).log(Level.SEVERE,
"Unable to find message bundle in XYZ Class", ex);
throw ex;
}
}
public void doSomethingWithBundle(){
ResourceBundle bundle = getMessageResourceBundle();
String someString = bundle.getString("SOME_ERROR_STRING");
...
}
Вот пример того, как это сделать: http://www.laliluna.de/articles/javaserver-faces-message-resource-bundle-tutorial.html
Вы хотите взглянуть на ResourceBundle.getBundle()
часть.
Привет, Ларс