在我的应用中,用户应该能够切换语言环境(用于在页面上呈现文本的语言)。大量的教程正在使用FacesContext.getCurrentInstance()。getViewRoot()。setLocale()。例如:http://www.mkyong.com/jsf2/jsf-2-internationalization-example/。但是,这根本不适用于JSF 2.0(它确实在1.2中工作)。语言永远不会切换。没有错误或任何东西。相同的代码在JSF 1.2中运行良好。
什么是正确和明确的方法?我拼凑了一个解决方案,但不确定这是否正确。这很好用。用户点击英语或法语后语言切换。这是代码片段,可以给你一些想法。
@ManagedBean(name = "switcher")
@SessionScoped
public class LanguageSwitcher {
Locale locale = FacesContext.getCurrentInstance().getViewRoot().getLocale();
public String switchLocale(String lang) {
locale = new Locale(lang);
return FacesContext.getCurrentInstance().getViewRoot().getViewId() +
"?faces-redirect=true";
}
//getLocale() etc. omitted for brevity
}
XHTML:
<f:view locale="#{switcher.locale}">
<h:outputText value="#{msg.greeting}" />
<h:commandLink value="English" action="#{switcher.switchLocale('en')}" />
<h:commandLink value="French" action="#{switcher.switchLocale('fr')}" />
</f:view>
只是为了提供更多信息,这是配置文件。
<application>
<locale-config>
<supported-locale>en</supported-locale>
<supported-locale>fr</supported-locale>
</locale-config>
<resource-bundle>
<base-name>com.resources.Messages</base-name>
<var>msg</var>
</resource-bundle>
</application>
再次,这是有效的。但是,我没有以任何方式调用任何API来改变JSF本身的语言环境。这给了我一些令人毛骨悚然的感觉。这是更改用户区域设置的正确方法吗?
答案 0 :(得分:8)
好的,冒着回答我自己的问题的风险,我想总结一下我找到的所有不同方法。
基本方法就是我已经在做的事情。也就是说,在会话范围内有一个托管bean,它返回用户的Locale。需要使用<f:view locale="...">
在每个XHTML中使用此区域设置。我从BalusC的一篇文章中学到了这个技术,所以谢谢你的到来。
现在,关注的是使用f:view元素。这需要在每一页中重复,如果错误地省略,则可能是缺陷的潜在来源。我找到了几种解决这个问题的方法。
方法#1:创建一个Facelet模板并在那里添加f:view元素。单个模板用户页面不必担心添加此元素。
方法#2使用阶段监听器。 @meriton已经在这里发布了解决方案。谢谢你。
方法#3使用自定义视图处理程序,该处理程序扩展MultiViewHandler并从calculateLocale()方法返回用户的语言环境。这在Beginning JSF 2 API和JBoss Seam By:Kent Ka Iok Tong一书中有所描述。以下是本书中略有改动的例子:
public class MyViewHandler extends MultiViewHandler {
public Locale calculateLocale(FacesContext context) {
HttpSession session = (HttpSession) context.getExternalContext()
.getSession(false);
if (session != null) {
//Return the locale saved by the managed bean earlier
Locale locale = (Locale) session.getAttribute("locale");
if (locale != null) {
return locale;
}
}
return super.calculateLocale(context);
}
}
然后在faces config中注册。
<application>
<view-handler>com.package.MyViewHandler</view-handler>
<!-- Other stuff ... -->
</application>
这比阶段监听器更优雅。不幸的是,MultiViewHandler是com.sun.faces.application.view包中的内部非API类。这会带来一些风险。
使用方法#2或#3,页面中不需要f:view元素。
答案 1 :(得分:2)
可以使用扩展javax.faces.application.ViewHandlerWrapper
的自定义视图处理程序,并从calculateLocale()方法返回用户的语言环境。
这绝对比从专有SUN软件包MultiViewHandler
扩展com.sun.faces.application.view
更好,无论您的建议中提到的 Beginning JSF 2 API 一书中有哪些内容。除此之外,你原来的方法是绝对可以的:
public class MyViewHandler extends ViewHandlerWrapper {
public Locale calculateLocale(FacesContext context) {
HttpSession session = (HttpSession) context.getExternalContext()
.getSession(false);
if (session != null) {
//Return the locale saved by the managed bean earlier
Locale locale = (Locale) session.getAttribute("locale");
if (locale != null) {
return locale;
}
}
return super.calculateLocale(context);
}
}
然后在faces config中注册。
<application>
<view-handler>com.package.MyViewHandler</view-handler>
<!-- Other stuff ... -->
</application>
答案 2 :(得分:0)
我最近遇到了相关问题。在我的例子中,JSF实现在导航到不同视图后忘记了UIViewRoot.setLocale()
设置的视图区域设置。我认为这是JSF impl中的一个错误,但我没有时间确定。
我并不特别喜欢<f:view>
方法,因为该标签已被facelets淘汰 - 除了保留语言环境之外,似乎。这让我把它包含在Facelets模板中。因此,我编写了以下PhaseListener:
/**
* PhaseListener that keeps the current view locale in the session while no request is being processed, to work around
* bugs where JSF forgets the changed locale.
*/
public class SaveViewLocaleToSessionPhaseListener implements PhaseListener {
private static final String key = "locale";
@Override
public PhaseId getPhaseId() {
return PhaseId.ANY_PHASE;
}
@Override
public void beforePhase(PhaseEvent event) {
// do nothing
}
@Override
public void afterPhase(PhaseEvent event) {
PhaseId currentPhase = event.getPhaseId();
if (currentPhase == PhaseId.RESTORE_VIEW) {
viewRoot().setLocale((Locale) sessionMap().get(key));
} else if (currentPhase == PhaseId.RENDER_RESPONSE) {
sessionMap().put(key, viewRoot().getLocale());
}
}
private Map<String, Object> sessionMap() {
return FacesContext.getCurrentInstance().getExternalContext().getSessionMap();
}
private UIViewRoot viewRoot() {
return FacesContext.getCurrentInstance().getViewRoot();
}
}
但是,我无法提供任何可靠证据表明这比仅使用<f:view>
更好。
但是,我没有以任何方式改变JSF本身的语言环境。
当然可以:<f:view>
标记从值表达式中读取区域设置,并将其传递给UIViewRoot.setLocale()
。