【问题标题】:How to change locale in JSF 2.0?如何在 JSF 2.0 中更改语言环境?
【发布时间】:2012-08-18 04:41:00
【问题描述】:

在我的应用中,用户应该能够切换区域设置(用于在页面上呈现文本的语言)。大量教程使用 FacesContext.getCurrentInstance().getViewRoot().setLocale()。例如:http://www.mkyong.com/jsf2/jsf-2-internationalization-example/。但是,这在 JSF 2.0 中根本不起作用(它在 1.2 中确实起作用)。语言从不切换。没有错误或任何东西。相同的代码在 JSF 1.2 中运行良好。

什么是正确和确定的方法?我拼凑了一个解决方案,但不确定这是否正确。这工作正常。用户点击英语或法语后,语言切换。这是代码 sn-p 给你一些想法。

@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 本身的语言环境。这让我有些毛骨悚然的感觉。这是更改用户语言环境的正确方法吗?

【问题讨论】:

  • 如果你想被理解,也许比“根本行不通”和“这行得通”更具体。你的测试用例到底是什么? “更改语言环境”到底是什么意思? (更改哪个语言环境?)
  • @meriton,为问题添加了更多详细信息。基本上,语言永远不会切换。测试用例就在那里。用户点击英语或法语链接, 会显示该语言的文本。
  • 看看 BalusC(JSF 专家)答案:JSF locale is set per request,not for session.
  • @Luiggi,这几乎就是我在解决方案中所做的。除了,我不明白为什么他们都调用 FacesContext.getCurrentInstance().getViewRoot().setLocale(locale);当它似乎什么都不做的时候。
  • @RajV IMO Bauke Scholtz(a.k.a. BalusC)在他的回答和它提供的用于进一步解释的链接(也由他制作)中都非常清楚。

标签: jsf jsf-2


【解决方案1】:

可以使用扩展javax.faces.application.ViewHandlerWrapper 的自定义视图处理程序,并从calculateLocale() 方法返回用户的语言环境。

这绝对比从专有的 SUN 包 com.sun.faces.application.view 扩展 MultiViewHandler 更好,无论您的建议中提到的 Beginning JSF 2 APIs 一书中描述了什么。除此之外,您的原始方法绝对可以:

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>

【讨论】:

  • 这种情况下如何实现getWrapped()方法?
【解决方案2】:

好的,冒着回答我自己问题的风险,我想总结一下我发现的所有不同方法。

基本方法是我已经在做的。也就是说,在会话范围内有一个托管 bean,它返回用户的 Locale。这个语言环境需要在每个使用&lt;f:view locale="..."&gt; 的XHTML 中使用。我从 BalusC 的一篇文章中学到了这项技术,因此在此感谢。

现在,问题在于 f:view 元素的使用。这需要在每一页中重复,如果被错误地省略,这可能是缺陷的来源。我找到了几种解决这个问题的方法。

方法 #1:创建一个 Facelet 模板并在其中添加 f:view 元素。单个模板用户页面不必担心添加此元素。

方法 #2 使用相位侦听器。 @meriton 已在此处发布解决方案。谢谢你。

方法 #3 使用自定义视图处理程序,它扩展了 MultiViewHandler 并从 calculateLocale() 方法返回用户的区域设置。这在《Beginning JSF 2 APIs and JBoss Seam》一书中有所描述,作者: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 元素。

【讨论】:

  • 现在,问题在于 f:view 元素的使用。这需要在每个页面中重复,如果错误省略,可能会导致缺陷。 只使用 Facelets 模板?
【解决方案3】:

我最近遇到了一个相关问题。在我的例子中,JSF 实现在导航到不同的视图后忘记了由UIViewRoot.setLocale() 设置的视图区域设置。我宁愿认为这是 JSF impl 中的错误,但我没有时间确定。

我不是特别喜欢&lt;f:view&gt; 方法,因为该标签已被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();
    }
}

但是,我无法提供任何确凿的证据证明这确实比简单地使用 &lt;f:view&gt; 更好。

但是,我没有以任何方式更改 JSF 本身的语言环境。

你确定:&lt;f:view&gt; 标记从值表达式中读取语言环境,并将其传递给UIViewRoot.setLocale()

【讨论】:

  • 我同意 的使用有点奇怪。其他人使用了相位监听器,这对于框架应该有一个简单解决方案的东西来说似乎有点过头了。让我去读几本关于 Safari 的书,看看他们有什么建议。我会尽快发布。
猜你喜欢
  • 1970-01-01
  • 2018-07-09
  • 1970-01-01
  • 2012-05-15
  • 1970-01-01
  • 2013-11-15
  • 1970-01-01
  • 2021-03-18
  • 2013-04-27
相关资源
最近更新 更多