Search code examples
jsfsessionjsf-2localizationinternationalization

Localization in JSF, how to remember selected locale per session instead of per request/view


faces-config.xml:

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

In a bean action method, I'm changing the locale in the current view as follows:

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

The problem is that ua Locale is applied, but only per request/view and not for session. Another request/view within the same session resets the locale back to default ru value.

How can I apply the locale for session?


Solution

  • You need to store the selected locale in the session scope and set it in the viewroot in two places: once by UIViewRoot#setLocale() immediately after changing the locale (which changes the locale of the current viewroot and thus get reflected in the postback; this part is not necessary when you perform a redirect afterwards) and once in the locale attribute of the <f:view> (which sets/retains the locale in the subsequent requests/views).

    Here's an example how such a LocaleBean should look like:

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

    And here's an example of the view should look like:

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

    Which assumes that #{text} is already configured in faces-config.xml as below:

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

    Note that <html lang> is not required for functioning of JSF, but it's mandatory how search bots interpret your page. Otherwise it would possibly be marked as duplicate content which is bad for SEO.

    See also: