简体   繁体   English

JSF 2本地化(托管Bean)

[英]JSF 2 localization (managed bean)

I have a properties file for localization: 我有一个用于本地化的属性文件:

foo=Bar
title=Widget Application

This is tied in as a resource-bundle in the faces-config: 这是在faces-config中作为resource-bundle的:

<resource-bundle>
    <base-name>com.example.messages.messages</base-name>
    <var>msgs</var>
</resource-bundle>

I can access this just fine in the facelets view using EL: 我可以使用EL在facelets视图中访问它:

<title>#{msgs.title}</title>

However, if there are things like SQLExceptions, I need to be able to write messages from the managed bean. 但是,如果有类似SQLExceptions之类的东西,我需要能够从托管bean编写消息。 This is all working also: 这也可以正常工作:

FacesMessage message = new FacesMessage(FacesMessage.SEVERITY_ERROR, "There was an error saving this widget.", null);
FacesContext.getCurrentInstance().addMessage(null, message);

Here is the issue: I want to have those messages come from the properties file so that they, too, can be changed based on the locale. 这是问题所在:我希望这些消息来自属性文件,以便它们也可以基于区域设置进行更改。 Is there an easy way to access the properties file using injection? 有没有一种简单的方法可以使用注入来访问属性文件?

I asked a quite related question on SO: How to inject a non-serializable class (like java.util.ResourceBundle) with Weld 我对SO提出了一个非常相关的问题: 如何使用Weld注入不可序列化的类(例如java.util.ResourceBundle)

And inside the Seam Forum: http://seamframework.org/Community/HowToCreateAnInjectableResourcebundleWithWeld 在Seam论坛内部: http//seamframework.org/Community/HowToCreateAnInjectableResourcebundleWithWeld

To summarize: I realized an injectable ResourceBundle with 3 Producers. 总结一下:我实现了一个由3个生产者组成的可注入ResourceBundle。 First you need a FacesContextProducer. 首先,您需要一个FacesContextProducer。 I took the one from the Seam 3 Alpha sources. 我从Seam 3 Alpha来源中选了一个。

public class FacesContextProducer {
   @Produces @RequestScoped
   public FacesContext getFacesContext() {
      FacesContext ctx = FacesContext.getCurrentInstance();
      if (ctx == null)
         throw new ContextNotActiveException("FacesContext is not active");
      return ctx;
   }
}

Then you need a LocaleProducer, which uses the FacesContextProducer. 然后,您需要一个使用FacesContextProducer的LocaleProducer。 I also took it from Seam 3 Alpha. 我也是从Seam 3 Alpha那里拿来的。

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

Now you have everything to create a ResourceBundleProducer, which can look like this: 现在,您已拥有创建ResourceBundleProducer的所有功能,该外观如下所示:

public class ResourceBundleProducer {
  @Inject       
  public Locale locale;

  @Inject       
  public FacesContext facesContext;

  @Produces
  public ResourceBundle getResourceBundle() {
   return ResourceBundle.getBundle("/messages", facesContext.getViewRoot().getLocale() );
  }
}

Now you can @Inject the ResourceBundle into your beans. 现在您可以@将ResourceBundle注入到bean中。 Pay attention that it has to be injected into a transient attribute, otherwise you'll get an exception complaining that ResourceBundle is not serializable. 请注意,必须将其注入到瞬时属性中,否则您将收到一个异常,抱怨ResourceBundle无法序列化。

@Named
public class MyBean {
  @Inject
  private transient ResourceBundle bundle;

  public void testMethod() {
    bundle.getString("SPECIFIC_BUNDLE_KEY");
  }
}

使用起来更容易,例如MyFaces CODI的消息模块!

You can do this with JSF alone. 您可以单独使用JSF做到这一点。

Start by defining a managed property on your backing bean. 首先在您的支持Bean上定义一个托管属性。 In the JSF configuration, you can set the managed property's value to an EL expression that references your resource bundle. 在JSF配置中,可以将托管属性的值设置为引用资源束的EL表达式。

I've done something like the following using Tomcat 6. The only caveat is that you can't access this value from your backing bean's constructor, since JSF will not yet have initialized it. 我已经使用Tomcat 6完成了以下操作。唯一的警告是,您无法从后备bean的构造函数访问此值,因为JSF尚未初始化它。 Use @PostConstruct on an initialization method if the value is needed early in the bean's lifecycle. 如果在bean的生命周期的早期需要该值,请在初始化方法上使用@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>

This has the advantage of making your backing bean methods less dependent on the presentation technology, so it should be easier to test. 这具有使您的后备bean方法较少依赖表示技术的优点,因此应该更容易测试。 It also decouples your code from details like the name given to the bundle. 它还使您的代码与详细信息(例如赋予软件包的名称)分离。

Some testing using Mojarra 2.0.4-b09 does show a small inconsistency when a user changes locale mid-session. 当用户更改区域设置中段会话时,使用Mojarra 2.0.4-b09进行的某些测试确实显示出很小的不一致。 In-page EL expressions use the new locale but the backing bean isn't given the new ResourceBundle reference. 页内EL表达式使用新的语言环境,但没有为后备bean提供新的ResourceBundle引用。 To make it consistent you could use the bean property value in EL expressions, such as using #{backingBean.messages.greeting} in place of #{msgs.greeting} . 为了使其一致,可以在EL表达式中使用bean属性值,例如使用#{backingBean.messages.greeting}代替#{msgs.greeting} Then page EL and the backing bean would always use the locale that was active when the session began. 然后,页面EL和支持Bean将始终使用会话开始时处于活动状态的语言环境。 If users had to switch locales mid-session and get the new messages, you could try making a request-scoped bean and give it references to both the session bean and resource bundle. 如果用户必须在会话中切换语言环境并获取新消息,则可以尝试制作请求范围的Bean,并为其提供对会话Bean和资源包的引用。

This is an old question but I'm adding another way to do this. 这是一个古老的问题,但我正在添加另一种方法。 I was looking for something else and ran across this. 我在寻找其他东西,遇到了这个问题。 The methods here all seemed convoluted for something that doesn't strike me as all the that difficult. 这里的方法似乎都让人费解,这让我感到不那么困难。 Playing with glitter because it's pretty if you ask me. 玩闪光,因为如果你问我,这很漂亮。

Given a file: 给定一个文件:

/com/full/package/path/to/messages/errormessages.properties

Inside the file: 在文件内部:

SOME_ERROR_STRING=Your App Just Cratered

I create a "getBundle()" method since I like to catch the runtime and add a meaningful message so I will understand where it is coming from. 我创建了一个“ getBundle()”方法,因为我喜欢赶上运行时并添加一条有意义的消息,以便我了解它的来源。 Not hard and can help if you get the whim to play with the properties files for some reason and don't update everything correctly. 并不难,如果由于某种原因让您一时心血来潮地玩属性文件,并且没有正确更新所有内容,这会有所帮助。 I sometimes make this private as it is sometimes a helper method within a class (for me). 我有时将此私有化,因为有时它是类中的帮助方法(对我而言)。 This keeps the try catch clutter out of the meaningful code. 这将使try catch杂乱无章。

Using the full path to the file allows you to put it somewhere other than the default location/directory if you have other ideas on organization. 如果您对组织有其他想法,则使用文件的完整路径可以将其放置在默认位置/目录之外的其他位置。

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");
    ...
}

Here's an example on how to do this: http://www.laliluna.de/articles/javaserver-faces-message-resource-bundle-tutorial.html 这是有关如何执行此操作的示例: http : //www.laliluna.de/articles/javaserver-faces-message-resource-bundle-tutorial.html

You want to have a look at the ResourceBundle.getBundle() part. 您想看一下ResourceBundle.getBundle()部分。

Greetings, Lars 问候,拉斯

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM