Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to change locale in JSF 2.0?

Tags:

jsf

jsf-2

In my app, user should be able to switch the locale (the language used to render text on pages). Tons of tutorials are using FacesContext.getCurrentInstance().getViewRoot().setLocale(). For example: http://www.mkyong.com/jsf2/jsf-2-internationalization-example/. But, that simply doesn't work in JSF 2.0 (it did work in 1.2). The language never switches. No errors or anything. The same code worked fine in JSF 1.2.

What is the correct and definitive approach? I have cobbled together a solution, but not sure if this is the correct one. This works fine. The language switches after user clicks on English or French. Here is code snippet to give you some idea.

@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
}

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

Just to give you more info, here is the config file.

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

Once again, this works. But, I haven't changed the locale of JSF itself by calling any API in any way. This gives me somewhat of a creepy feeling. Is this the correct way to change user's locale?

like image 341
RajV Avatar asked Aug 22 '12 17:08

RajV


2 Answers

OK, at the risk of answering my own question, I will like to summarize all the different approaches that I have found.

The basic approach is what I am already doing. That is, have a managed bean in session scope that returns the Locale of the user. This locale needs to be used in every XHTML using <f:view locale="...">. I learned this technique from a post by BalusC, so thanks are due there.

Now, the concern is the use of the f:view element. This needs to be repeated in every page, a potential source of defect if omitted by mistake. I have found a couple of ways of solving this problem.

Approach #1: Create a Facelet template and add the f:view element there. Individual template user pages don't have to worry about adding this element.

Approach #2 uses a phase listener. @meriton has posted the solution here. Thank you for that.

Approach #3 uses a custom view handler that extends MultiViewHandler and returns user's locale from the calculateLocale() method. This is described in the book Beginning JSF 2 APIs and JBoss Seam By: Kent Ka Iok Tong. Here is a slightly altered example from the book:

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

Then register it in faces config.

<application>
    <view-handler>com.package.MyViewHandler</view-handler>
<!-- Other stuff ... -->
</application>

This is somewhat more elegant than the phase listener. Unfortunately, MultiViewHandler is an internal non-API class from the com.sun.faces.application.view package. That incurs some risk going forward.

With either approach #2 or #3, there is no need for the f:view element in the pages.

like image 192
RajV Avatar answered Oct 24 '22 19:10

RajV


One can use custom view handler that extends javax.faces.application.ViewHandlerWrapper and returns user's locale from the calculateLocale() method.

This is definitely better than extending MultiViewHandler from the proprietary SUN package com.sun.faces.application.view, no matter what is described in the book Beginning JSF 2 APIs mentioned in your suggestion. Apart from that, your original approach is absolutely OK:

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

Then register it in faces config.

    <application>
      <view-handler>com.package.MyViewHandler</view-handler>
      <!-- Other stuff ... -->
    </application>
like image 37
alexeev.net Avatar answered Oct 24 '22 19:10

alexeev.net