JSF 中的本地化,如何记住每个会话而不是每个请求/视图选择的语言环境

Posted

技术标签:

【中文标题】JSF 中的本地化,如何记住每个会话而不是每个请求/视图选择的语言环境【英文标题】:Localization in JSF, how to remember selected locale per session instead of per request/view 【发布时间】:2011-06-17 08:46:47 【问题描述】:

faces-config.xml:

<application>
    <locale-config>
        <default-locale>ru</default-locale>
        <supported-locale>ua</supported-locale>
    </locale-config>
</application> 

在 bean 操作方法中,我正在更改当前视图中的语言环境,如下所示:

FacesContext.getCurrentInstance().getViewRoot().setLocale(new Locale("ua"));

问题是应用了ua 区域设置,但仅适用于每个请求/视图,而不适用于会话。同一会话中的另一个请求/视图会将语言环境重置为默认的 ru 值。

如何为会话应用语言环境?

【问题讨论】:

我很好奇,这种设置语言的技术多久使用一次?我所有的浏览器都是英文的,这作为 i18n 技术可靠吗? 【参考方案1】:

您需要将选定的语言环境存储在会话范围内,并将其设置在 viewroot 的两个位置:一次由 UIViewRoot#setLocale() 在更改语言环境后立即进行(这会更改当前视图根的语言环境,从而反映在回发中; 这部分在您之后执行重定向时不是必需的)和一次在&lt;f:view&gt;locale 属性中(在后续请求/视图中设置/保留区域设置)。

下面是一个例子,LocaleBean 应该是这样的:

package com.example.faces;

import java.util.Locale;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;
import javax.faces.context.FacesContext;

@ManagedBean
@SessionScoped
public class LocaleBean 

    private Locale locale;

    @PostConstruct
    public void init() 
        locale = FacesContext.getCurrentInstance().getExternalContext().getRequestLocale();
    

    public Locale getLocale() 
        return locale;
    

    public String getLanguage() 
        return locale.getLanguage();
    

    public void setLanguage(String language) 
        locale = new Locale(language);
        FacesContext.getCurrentInstance().getViewRoot().setLocale(locale);
    


下面是一个视图示例:

<!DOCTYPE html>
<html lang="#localeBean.language"
    xmlns:f="http://java.sun.com/jsf/core"
    xmlns:h="http://java.sun.com/jsf/html">
<f:view locale="#localeBean.locale">
    <h:head>
        <title>JSF/Facelets i18n example</title>
    </h:head>
    <h:body>
        <h:form>
            <h:selectOneMenu value="#localeBean.language" onchange="submit()">
                <f:selectItem itemValue="en" itemLabel="English" />
                <f:selectItem itemValue="nl" itemLabel="Nederlands" />
                <f:selectItem itemValue="es" itemLabel="Español" />
            </h:selectOneMenu>
        </h:form>
        <p><h:outputText value="#text['some.text']" /></p>
    </h:body>
</f:view>
</html>

假设#text 已在faces-config.xml 中配置如下:

<application>
    <resource-bundle>
        <base-name>com.example.i18n.text</base-name>
        <var>text</var>
    </resource-bundle>
</application>

请注意,&lt;html lang&gt; 不是 JSF 运行所必需的,但搜索机器人如何解释您的页面是强制性的。否则可能会被标记为不利于 SEO 的重复内容。

另见:

Maven and JSF webapp structure, where exactly to put JSF resources Internationalization in JSF, when to use message-bundle and resource-bundle? i18n with UTF-8 encoded properties files in JSF 2.0 application

【讨论】:

f:view 不应该进入 body 标签内吗? @Kemoda:您的选择。在幕后,&lt;f:view&gt; 的属性进入了作为根组件的UIViewRoot @BalusC,你能修复你主要文章中的代码吗?人们一次又一次地“踩到耙子”,尤其是没有经验的程序员。谢谢。 @BalusC 还有一个问题。是否必须以这种方式编辑 faces-config.xml pastebin.com/uEm9eYVg @MestreLion:见答案的第一段。这取决于您是重用还是重新创建视图。如果重用视图,则需要在视图上手动设置。如果你重新创建视图,那么它会自动从 bean 中获取它。【参考方案2】:

对@BalusC 伟大解决方案的一点评论。如果我们有 &lt;f:viewAction&gt; 在支持 bean 中执行一些方法。在该方法内部调用FacesContext.getCurrentInstance().getViewRoot().getLocale() 时可用的语言环境将是由用户浏览器或默认应用程序语言环境设置的语言环境,而不是由用户选择在会话 bean 上设置的语言环境(当然,如果浏览器语言环境等于该语言环境,它们可以匹配用户选择)。

我可以接受纠正,因为在实施@BalusC 提供的解决方案时我可能做错了什么。

编辑。 使用JSF lifecycle 后,这种区域设置行为与&lt;f:viewAction&gt; 无关,因为@PostContruct 也存在类似行为。请求中的&lt;f:view locale="#localeBean.locale"&gt;(在用户选择区域设置后)在渲染响应阶段执行。 &lt;f:viewAction&gt;@PostContruct 方法在调用应用程序阶段执行。这就是为什么在此方法中执行的逻辑无权访问用户选择的语言环境。

当我们需要正确的语言环境时,我们使用的解决方案是在包含&lt;f:viewAction&gt;@PostContruct 方法的其他支持bean 中注入(CDI)localeBean,然后在开头使用localeBean 中的UIViewRoot#setLocale() 设置语言环境这些方法。

【讨论】:

这是评论而不是答案!请获得足够的声誉并对答案发表评论。 @Kukeltje 可能你是对的,但是当我获得足够的声誉时,我可能会忘记这个问题。从另一面和你对 Jsf 的经验来看,你可以验证我在这里提到的f:viewAction 是否有任何问题。【参考方案3】:

这个组件 f:view 不存在你的 JSF 页面,它不会工作,它只会显示默认的英语语言。提供这个 f:view 组件的 localae 值然后它会正常工作。我现在遇到了同样的问题,它工作正常。

【讨论】:

【参考方案4】:

如果您可以在您的环境中使用 CDI 和 deltaspike (JSF module),您可以将以下内容添加到您的 LocaleBean 以自动重置当前视图的区域设置:

@javax.enterprise.context.SessionScoped
public class LocaleBean implements Serializable 

    ...

    public void resetLocale(@Observes @BeforePhase(JsfPhaseId.RENDER_RESPONSE) PhaseEvent event) 
        event.getFacesContext().getViewRoot().setLocale(this.locale);
    

【讨论】:

【参考方案5】:

我发现问题也与 .properties 文件名有关。 Java Locale us 代码(小写),例如:en_gb 但自动创建的语言环境(由 Netbeans)是 lowercase_uppercase 即:messages_en_GB.properties 将名称更改为:messages_en_gb.properties 它应该可以工作 - 如果你尝试了一切

【讨论】:

以上是关于JSF 中的本地化,如何记住每个会话而不是每个请求/视图选择的语言环境的主要内容,如果未能解决你的问题,请参考以下文章

JSF 请求范围的 bean 不断在每个请求上重新创建新的有状态会话 bean?

在 JSF 中生成自己的会话 ID

如何在 JSF 中正确使用组件绑定? (会话范围 bean 中的请求范围组件)

“每个请求的会话”模式是不是利用了缓存? (“每个会话的会话”或“每个请求的会话”)

会话超时是不是在每个请求上重置

Laravel 会话 ID 随每个请求而变化