JSF 2 本地化(托管 bean)

发布于 2024-09-14 09:29:11 字数 759 浏览 12 评论 0原文

我有一个用于本地化的属性文件:

foo=Bar
title=Widget Application

这在 faces-config 中作为 resource-bundle 绑定:

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

我可以使用 EL 在 Facelets 视图中很好地访问它:

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

但是,如果有类似的情况SQLExceptions,我需要能够从托管 bean 写入消息。这一切也都有效:

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

问题是:我希望这些消息来自属性文件,以便它们也可以根据区域设置进行更改。有没有一种简单的方法可以使用注入来访问属性文件?

I have a properties file for localization:

foo=Bar
title=Widget Application

This is tied in as a resource-bundle in the faces-config:

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

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

However, if there are things like SQLExceptions, I need to be able to write messages from the managed 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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

幼儿园老大 2024-09-21 09:29:11

我问了一个非常相关的问题:
如何注入非带有 Weld 的可序列化类(如 java.util.ResourceBundle)

以及 Seam 论坛内部:
http://seamframework.org/Community/HowToCreateAnInjectableResourcebundleWithWeld

总结一下:
我实现了一个带有 3 个 Producer 的可注入 ResourceBundle。
首先您需要一个 FacesContextProducer。我从 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;
   }
}

然后您需要一个 LocaleProducer,它使用 FacesContextProducer。我也从 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);
   }
}

现在您已经拥有了创建 ResourceBundleProducer 的一切,它看起来像这样:

public class ResourceBundleProducer {
  @Inject       
  public Locale locale;

  @Inject       
  public FacesContext facesContext;

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

现在您可以将 ResourceBundle @Inject 到您的 bean 中。请注意,它必须被注入到瞬态属性中,否则您将收到一个异常,抱怨 ResourceBundle 不可序列化。

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

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

I asked a quite related question on SO:
How to inject a non-serializable class (like java.util.ResourceBundle) with Weld

And inside the Seam Forum:
http://seamframework.org/Community/HowToCreateAnInjectableResourcebundleWithWeld

To summarize:
I realized an injectable ResourceBundle with 3 Producers.
First you need a FacesContextProducer. I took the one from the Seam 3 Alpha sources.

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. I also took it from 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:

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. Pay attention that it has to be injected into a transient attribute, otherwise you'll get an exception complaining that ResourceBundle is not serializable.

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

  public void testMethod() {
    bundle.getString("SPECIFIC_BUNDLE_KEY");
  }
}
日裸衫吸 2024-09-21 09:29:11

使用MyFaces CODI的消息模块更容易!

It's easier to use e.g. the message module of MyFaces CODI!

初心未许 2024-09-21 09:29:11

您可以单独使用 JSF 来完成此操作。

首先在支持 bean 上定义托管属性。在 JSF 配置中,您可以将托管属性的值设置为引用资源包的 EL 表达式。

我使用 Tomcat 6 完成了如下操作。唯一需要注意的是,您无法从支持 bean 的构造函数访问该值,因为 JSF 尚未初始化它。如果在 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>

这样做的优点是可以减少您的支持 bean 方法对表示技术的依赖,因此应该更容易测试。它还将您的代码与诸如捆绑包名称之类的细节分离。

当用户在会话中更改区域设置时,使用 Mojarra 2.0.4-b09 进行的一些测试确实显示出轻微的不一致。页内 EL 表达式使用新的语言环境,但没有为支持 bean 提供新的 ResourceBundle 引用。为了保持一致,您可以在 EL 表达式中使用 bean 属性值,例如使用 #{backingBean.messages.greeting} 代替 #{msgs.greeting}。然后页面 EL 和支持 bean 将始终使用会话开始时处于活动状态的语言环境。如果用户必须在会话中切换语言环境并获取新消息,您可以尝试创建一个请求范围的 bean 并为其提供对会话 bean 和资源包的引用。

You can do this with JSF alone.

Start by defining a managed property on your backing bean. In the JSF configuration, you can set the managed property's value to an EL expression that references your resource bundle.

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. Use @PostConstruct on an initialization method if the value is needed early in the bean's lifecycle.

<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. 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. In-page EL expressions use the new locale but the backing bean isn't given the new ResourceBundle reference. 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}. Then page EL and the backing bean would always use the locale that was active when the session began. 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.

佼人 2024-09-21 09:29:11

以下是有关如何执行此操作的示例:
http://www.laliluna.de/articles/ javaserver-faces-message-resource-bundle-tutorial.html

您想查看 ResourceBundle.getBundle() 部分。

问候,
拉尔斯

Here's an example on how to do this:
http://www.laliluna.de/articles/javaserver-faces-message-resource-bundle-tutorial.html

You want to have a look at the ResourceBundle.getBundle() part.

Greetings,
Lars

左耳近心 2024-09-21 09:29:11

这是一个老问题,但我添加了另一种方法来做到这一点。我正在寻找其他东西并遇到了这个。这里的方法似乎都很复杂,但对我来说并不那么困难。玩闪光,因为如果你问我的话,它很漂亮。

给定一个文件:

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

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

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");
    ...
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文