How to change the locale in JSF 2.0?

In my application, the user should be able to switch the language (the language used to render text on pages). Tons of tutorials use FacesContext.getCurrentInstance (). GetViewRoot (). SetLocale (). For example: http://www.mkyong.com/jsf2/jsf-2-internationalization-example/ . But it just doesn't work in JSF 2.0 (it worked in 1.2). The language never switches. There are no errors. The same code worked fine in JSF 1.2.

What is the correct and final approach? I combined the solution, but not sure if this is correct. It works great. The language switches after the user clicks in English or French. Here is a 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 } 

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> 

To provide you more information, here is the configuration 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> 

It works again. But I did not change the locale of the JSF itself by invoking any API in any way. It gives me a somewhat eerie feeling. Is it correct to change the user language?

+6
source share
3 answers

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

The basic approach is what I'm already doing. That is, to have a managed bean in the session area that returns the user's Locale. This language should be used in every XHTML using <f:view locale="..."> . I learned this technique from a BalusC post, so thanks there.

Now the problem is using the f: view element. This must be repeated on each page, the potential source of the defect, if mistakenly mistaken. I have found several ways to solve this problem.

Approach No. 1: create a Facelet template and add the f: view element there. Separate pages of custom templates should not worry about adding this element.

Approach No. 2 uses a phase listener. @meriton posted the solution here. Thanks for this.

Approach No. 3 uses a specialized view handler that extends MultiViewHandler and returns a custom language from the calculateLocale () method. This is described in The Beginning of the JSF 2 and JBoss Seam APIs: Kent Ka Yok Tong. Here is a slightly modified 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 the face configurations.

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

It is somewhat more elegant than a phase listener. Unfortunately, MultiViewHandler is an internal non-API class from com.sun.faces.application.view package. This leads to some risk in the future.

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

+6
source

You can use your own view handler, which extends javax.faces.application.ViewHandlerWrapper and returns a custom language from the calculateLocale () method.

This is certainly better than the MultiViewHandler extension from the patented SUN package com.sun.faces.application.view , regardless of what is described in the JSF 2 Beginner APIs mentioned in your proposal. In addition, your original approach is absolutely fine:

 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 the face configurations.

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

I recently had a problem with this. In my case, the JSF implementation forgot the view locale set by UIViewRoot.setLocale() after moving to another view. I rather consider this a mistake in the JSF impl, but I did not have time to make sure.

I especially did not like the <f:view> approach, since this tag was deprecated by facelets - except for saving the locale. This made my subordinate include it in the Facelets template. Therefore, I wrote the following 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(); } } 

However, I cannot offer any convincing evidence that this is really better than just using <f:view> .

But I did not change the JSF locale in any way.

Are you sure: the <f:view> reads the locale from the value expression and passes it to UIViewRoot.setLocale() .

0
source

Source: https://habr.com/ru/post/923502/


All Articles