Announcement Announcement Module
Collapse
No announcement yet.
Home page not i18n until navigating away then returning Page Title Module
Move Remove Collapse
X
Conversation Detail Module
Collapse
  • Filter
  • Time
  • Show
Clear All
new posts

  • Home page not i18n until navigating away then returning

    Hey all:

    My web application stores a users chosen Locale preferences in the database. When the user logs in the next page (home page) should reflect their preference. What is strange is the home page doesn't show as French for example, until the user navigates away and returns. So, upon initial log in of a user who has chosen French as their preference the home page is in English if you click away and then return it's French.

    My code snips are below which show how the SessionLocaleResolver and LocaleChangeInterceptor are configured as well as how the HomeController updates the session with the appropriate Locale.

    Code:
     <bean id="localeResolver" class="org.springframework.web.servlet.i18n.SessionLocaleResolver" > 
    	 	<property name="defaultLocale" value="en" />
    	 </bean>
    	 	
    	<mvc:interceptors>
    		 <bean id="localeChangeInterceptor"
    			class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor">
    			<property name="paramName" value="langId" />
    		</bean>
    	</mvc:interceptors>
    Here is the code for updating the Locale. I have printed to the console the results of getting the SessionLocaleResolver out of session and it is indeed set to French, but the page just doesn't update or change.


    Code:
    			
    	final LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
    
    	if (localeResolver != null) {
    
    	   final String newLocaleName = userProfile.getDefaultLocaleId();
    
    	   if (newLocaleName != null) {
    		final LocaleEditor localeEditor = new LocaleEditor();
    		localeEditor.setAsText(newLocaleName);
    
    	
    		localeResolver.setLocale(request, response,
    	      (Locale) localeEditor.getValue());
    	   }
    	}

    Any help and/or guidance is greatly appreciated!

  • #2
    It depends on WHERE/WHEN you set/change the locale. Instead of only changing the Locale in the LocaleResolver also reset the Locale in the LocalContextHolder. this is set once and reused if you change the Locale after it has been set in the LocaleContextHolder it isn't going to be used until you rerender the page.

    Comment


    • #3
      I set/change the Locale as soon as the user logs in and before the user is taken to the home page.
      Once a user logs in spring security then forwards the /home/home.html as show below. This url goes through the HomeController which
      among other things sets the Locale for a user as show in my previous post above.

      I attempted to set the LocaleContextHolder.setLocale () with the new Locale, but this still is not changing the page to French.



      <security:form-login login-page="/logon.html"
      login-processing-url="/j_edoption_spring_security_check.chk"
      default-target-url="/home/home.html"
      always-use-default-target="true" /

      Comment


      • #4
        If you use SpringSecurity then why not implement your own LocaleResolver which uses the user from the SecurityContext to determine the context... That way you don't have to mess around yourself with changing the locale in a controller to make it work.

        Psuedo code...
        Code:
        public class SecurityContextLocaleResolver extends SessionLocaleResolver {
        
          public Locale resolveLocale(HttpServletRequest request) {
            //determine if user is logged in and retrieve from SecurityContext and return Locale
            } else {
              return super.resolveLocale(request);
            }
          }
        }
        Make sure that you have only a single LocaleResolver and that that is the one used by the DispatcherServlet... Could be that you have a duplicate somewhere...

        Comment


        • #5
          I implemented the changes you mentioned, but the issue still remains. When the user first logs in and is forwarded to /home/home.html the page is English. If I click on the link to the same page (home) or just a refresh then the page changes to French.

          When you said

          Make sure that you have only a single LocaleResolver and that that is the one used by the DispatcherServlet... Could be that you have a duplicate somewhere...
          Does this mean just make sure another is not configured in any of the context files spring reads? A search on the code base on turns up one localeResolver - the one I implemented.

          <bean id="localeResolver" class="com.follett.fheg.ecommerce.onlineadoptions. web.controller.SecurityContextLocaleResolver">
          <property name="defaultLocale" value="en"/>
          </bean>

          here is the class I implemented. Not perfect of course (hard coded values etc..).

          public class SecurityContextLocaleResolver extends SessionLocaleResolver{


          public Locale resolveLocale( HttpServletRequest request){
          final UsernamePasswordAuthenticationToken upat = (UsernamePasswordAuthenticationToken) SecurityContextHolder.getContext().getAuthenticati on();
          final LocaleEditor localeEditor = new LocaleEditor();
          if(upat != null){

          final UserCredential userCredential = (UserCredential) upat.getPrincipal();

          if(userCredential != null){
          final LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);

          if (localeResolver != null) {
          final String newLocaleName = "fr_FR";
          if (newLocaleName != null) {
          localeEditor.setAsText(newLocaleName);
          LocaleContextHolder.setLocale((Locale)localeEditor .getValue());
          }
          }
          }
          }else{
          return super.resolveLocale(request);
          }

          return (Locale)localeEditor.getValue();
          }

          }



          Again, thanks for your help.

          Comment


          • #6
            Please use [ code][/code ] tags when posting code..

            I wasn't mentioning the single implementation I was mentioning duplicate instances at runtime. Maybe your configuration is loaded twice. Also your code is flawed as you are now a LocaleResolver yourself you shouldn't mess around with setting it in an other localeresolver (in theory you should get a stackoverflow as it should resolve to itself, if it doesn't there is something else wrong in your setup).

            Also your code is dangerous as the Authentication doesn't have to be a UsernamePasswordAuthenticationToken.

            Code:
            public class SecurityContextLocaleResolver extends SessionLocaleResolver{
            
            	public Locale resolveLocale( HttpServletRequest request){
            		
            		final Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
            		final LocaleEditor localeEditor = new LocaleEditor();
            		if (principal instanceof UserCredential) {
            			String locale = ((UserCredential) principal).getDefaultLocaleId();
            			localeEditor.setAsText(newLocaleName);
            			return (Locale) localeEditor.getValue());
            		} else {
            			return super.resolveLocale(request);
            		}
            	}
            }
            This should do it...

            Again check your setup and you might want to check in your own code what type of LocaleResolver is returned from RequestContextUtils.getLocaleResolver, that should be the same as your current one.

            Comment


            • #7
              Please use [ code][/code ] tags when posting code..
              Will do!

              I went over my web.xml (provided below) and I do not see anything out of the ordinary that would cause it to load the configuration twice. Is there somewhere else I should be checking which could cause this behavior?

              Again check your setup and you might want to check in your own code what type of LocaleResolver is returned from RequestContextUtils.getLocaleResolver, that should be the same as your current one.
              I checked this and I am indeed getting back a SessionLocaleResolver from RequestContextUtils.getLocaleResolver
              Also, the first time a user logs in and is directed to the home page (the one which should be in French) I noticed the Locale object set by Spring SesssionLocaleResolver does exist and it is the appropriate locale (fr_FR). The page just doesn't change until a second time around.

              Code:
              <?xml version="1.0" encoding="UTF-8"?>
              <web-app id="oa-web-app" version="2.5" 
              	xmlns="http://java.sun.com/xml/ns/javaee" 
              	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
              	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 
              	http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
              	>
              	
              	<context-param>
              		<param-name>contextConfigLocation</param-name>
              		<param-value>
              		classpath:/config/spring/wiring/web-application-config.xml,
              		classpath:/config/spring/wiring/datasource-config.xml,
              		classpath:/config/spring/wiring/context-config.xml,
              		classpath:/config/spring/wiring/weaving-config.xml,
              		classpath:/config/spring/wiring/cache-config.xml,
              		classpath:/config/spring/wiring/integration-management-config.xml,
              		classpath:/config/spring/wiring/service-config.xml,
              		classpath:/config/spring/wiring/dao-config.xml,
              		classpath:/config/spring/wiring/controller-config.xml,
              		classpath:/config/spring/wiring/security-config.xml,
              		classpath:/config/spring/wiring/mail-config.xml,
              		classpath:/config/spring/wiring/timer-config.xml,
              		classpath:/config/spring/wiring/webmvc-config.xml,
              		classpath:/config/spring/wiring/webflow-config.xml
              		</param-value>
              	</context-param>
              	
              	<listener>
              		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
              	</listener>	
              	
              	<filter>
              		<filter-name>springSecurityFilterChain</filter-name>
              		<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
              	</filter>
              	<filter-mapping>
              		<filter-name>springSecurityFilterChain</filter-name>
              		<url-pattern>/*</url-pattern>
              	</filter-mapping>
              	
              	
              	<servlet>
              		<servlet-name>Spring-MVC-for-OA</servlet-name>
              		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
              		<init-param>
              			<param-name>contextConfigLocation</param-name>
              			<param-value></param-value>
              		</init-param>
              		<!--<load-on-startup>1</load-on-startup>-->
              	</servlet>
              		
              	<servlet-mapping>
              		<servlet-name>Spring-MVC-for-OA</servlet-name>
              		<url-pattern>*.html</url-pattern>
              		<url-pattern>/spring/*</url-pattern>
              	</servlet-mapping>
              
              	<welcome-file-list>
              		<welcome-file>index.jsp</welcome-file>
              	</welcome-file-list>	
              </web-app>

              Comment


              • #8
                Something a bit odd I find is that if I pull a French property from the messageSource object in the HomeController right after the user logs in. The page is then French as it should be!

                So, adding this in the original code I had causes the page to update to French right after log in. But, if I remove it then the page does not change to French.

                Code:
                  Object[] a = new Object[0];
                  System.out.println(messageSource.
                        getMessage("passwordChangeForm.newPassword.required", a, (Locale)localeEditor.getValue()));

                Code:
                	final LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request);
                	if (localeResolver != null) {
                	
                	   final String newLocaleName = userProfile.getDefaultLocaleId();
                
                		if (newLocaleName != null) {
                	   	   final LocaleEditor localeEditor = new LocaleEditor();
                		   localeEditor.setAsText(newLocaleName);
                
                			localeResolver.setLocale(request, response,
                    		       (Locale) localeEditor.getValue());
                                        Object[] a = new Object[0];
                
                                        System.out.println(messageSource.
                                                 getMessage("passwordChangeForm.newPassword.required", a, (Locale)localeEditor.getValue()));
                		}

                Comment


                • #9
                  I checked this and I am indeed getting back a SessionLocaleResolver from RequestContextUtils.getLocaleResolver
                  Now this is strange as it should be the one you are currently in assuming you have implemented the LocaleResolver as mentioned before.

                  Can you enable debug logging on org.springframework.web this should give you information on which LocaleResolver is used. You could also enable debug (or even trace) logging for Spring to see what is going on.

                  Also not sure if you should extend the SessionLocaleResolver or just implement your own extension to the AbstractLocaleResolver which checks the current locale of the logged in user.

                  Code:
                  public class SecurityContextLocaleResolver extends AbstractLocaleResolver  {
                  
                  	public Locale resolveLocale( HttpServletRequest request){
                  		
                  		final Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
                  		final LocaleEditor localeEditor = new LocaleEditor();
                  		if (principal instanceof UserCredential) {
                  			String locale = ((UserCredential) principal).getDefaultLocaleId();
                  			localeEditor.setAsText(newLocaleName);
                  			return (Locale) localeEditor.getValue());
                  		} else {
                  			return getDefaultLocale();
                  		}		
                  	}
                  	
                  	protected Locale determineDefaultLocale(HttpServletRequest request) {
                  		Locale defaultLocale = getDefaultLocale();
                  		if (defaultLocale == null) {
                  			defaultLocale = request.getLocale();
                  		}
                  		return defaultLocale;
                  	}
                  
                  	public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
                  		throw new UnsupportedOperationException("Cannot change locale, change user settings instead.");
                  	}
                  }
                  You might want to add some debugging information to it to see what is called when...

                  Comment


                  • #10
                    We provided an implementation of the ResourceBundleMessageSource in the application. Once I removed that and just configured the ResourceBundleMessageSource everything works great! I am not sure why this issue cropped up though. When I run the application with extended version of ResourceBundleMessageSource the code is exercised and the bundles are loaded. But, for whatever reason in order to get the page to display French I have to do as shown and access the messaegSource first.

                    Code:
                      Object[] a = new Object[0];
                      System.out.println(messageSource.
                            getMessage("passwordChangeForm.newPassword.required", a, (Locale)localeEditor.getValue()));
                    Off hand I do not see any reason why I can't just use the ResourceBundleMessageSource - not sure why someone created a home grown implementation. Hopefully, that's the case and I can just stick with this way.

                    I really appreciate all of your help and effort. I learned a lot along the way!

                    Comment

                    Working...
                    X