1
votes

I have a web application, which uses JSF with PrimeFaces, and there is a JSF which allows users to change the locale dynamically. But the locale changes back to the default after the user goes to another JSF. I looked at the codes here, and here, but still couldn't get it to work.

In my resource directory, I have 3 property files.

Project
    - src/main/resources
        - <default package>
            - messages_en_US.properties
            - messages_zh_CN.properties
            - messages_zh_TW.properties

In my faces-config.xml I have the locales defined

<application>
        <locale-config>
            <default-locale>en_US</default-locale>
            <supported-locale>zh_TW</supported-locale>    <!-- generic traditional chinese -->
            <supported-locale>zh_CN</supported-locale>    <!-- generic simplified chinese -->
        </locale-config>
        <message-bundle>
            messages
        </message-bundle>
        <resource-bundle>
            <base-name>messages</base-name>
            <var>msg</var>
        </resource-bundle>
    </application>

In the JSF which allows users to change the locale, it is actually a user profile page. The <p:selectOneMenu /> items are read from an Ennum and the locale is changed upon clicking on the "Save" button.

<p:selectOneMenu id="defaultLanguage" value="#{userController.userLanguage}">
                            <f:selectItems value="#{userController.langcode}" var="lang" itemValue="#{lang.locale}"
                                           itemLabel="#{lang.locale} - #{lang.desc}" />
                        </p:selectOneMenu>
...
<p:commandButton id="save" value="#{msg.save}" title="#{msg.save}" icon="ui-icon-disk"
                             styleClass="action-buttons" actionListener="#{userController.doSave}" update="@form" />

In the UserController ManagedBean, the code are as follows:

@ManagedBean
@ViewScoped
public class UserController extends BaseController implements Serializable {

    public void doSave(ActionEvent e) {
            String lang;
            String country = null;
            String[] selectedLanguage = userLanguage.split("_");

            lang = selectedLanguage[0];
            if (selectedLanguage.length > 1) {
                country = selectedLanguage[1];
                setUserLocale(new Locale(lang, country));
            }
            else {
                setUserLocale(new Locale(lang));
            }

            LOG.debug("userLanguage: {}; lang: {}", userLanguage, lang);

            FacesContext.getCurrentInstance().getViewRoot().setLocale(userLocale);      // sets the locale from the selected user language

            Messages.addGlobalInfo(getMessage(msgInfoUpdated));
        }

    private LangCode userLangCode;              // getter + setter
    private LangCode[] langcode = LangCode.values();    // getter + setter
    @ManagedProperty(value = "#{loginController.userLocale}")
        private Locale userLocale;              // getter + setter

}

In the JSF I tried to add the <f:view locale="#{loginController.locale}" />. But still the same. In debug mode, when going to a new JSF page, the value of the userLocale is always the default locale and not the one which the user changed.

The LoginController code is as below. At the doLogin() method, I set the userLocale object with the locale from the Faces Context.

@ManagedBean
@SessionScoped
public class LoginController extends BaseController implements Serializable {

    public String doLogin() {
            String returnValue;

            try {
                currentUser = aduserFacade.validateUserLogin(username, password);
                LOG.debug("Successful login: {}", currentUser.getUsrId());

                // Set currentUser object into request session attribute
                FacesContext context = FacesContext.getCurrentInstance();
                HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest();

                setUserSession();
                userLocale = Faces.getLocale();
                request.getSession().setAttribute("userSession", userSession);

                returnValue = "main";
            }
            catch (Exception e) {
                Messages.addGlobalError(getMessage(e.getMessage()));
                LOG.error(e.toString(), e);
                returnValue = null;
            }

            return returnValue;
    }

    private Locale userLocale;  // getter + setter

}
1
Make UserController session scoped to remember language choice and set view locale to change view accordingly.skuntsel
but shouldn't it work when in the UserController I am setting the value to a property from the LoginController (@SessionScoped) using the ManagedProperty annotation?Alvin Sim

1 Answers

2
votes

The reason your locale is failing to hold is that the locale property you're injecting has not yet been updated with the new value as at the time you're injecting it. See, when you inject a managed bean property, as against the managed bean itself as in

@ManagedProperty(value = "#{loginController.userLocale}")

instead of

 @ManagedProperty(value = "#{loginController}") 

You're going to get a static value injection. Even after the value in the injected bean has been updated, you'll still only get a stale value that was set as soon as the injected bean was initialized. So what you need to do is inject the entire LoginController and then pull the value yourself, so you'll get the most current values.

See also


Unrelated to your question, you really shouldn't put member variables and injection at the bottom of your class. Makes your code inconvenient to read