001/* 002 * Copyright 2002-2019 the original author or authors. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * https://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016 017package org.springframework.web.servlet.i18n; 018 019import java.util.Locale; 020import javax.servlet.ServletException; 021import javax.servlet.http.HttpServletRequest; 022import javax.servlet.http.HttpServletResponse; 023 024import org.apache.commons.logging.Log; 025import org.apache.commons.logging.LogFactory; 026 027import org.springframework.lang.UsesJava7; 028import org.springframework.util.ObjectUtils; 029import org.springframework.util.StringUtils; 030import org.springframework.web.servlet.LocaleResolver; 031import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; 032import org.springframework.web.servlet.support.RequestContextUtils; 033 034/** 035 * Interceptor that allows for changing the current locale on every request, 036 * via a configurable request parameter (default parameter name: "locale"). 037 * 038 * @author Juergen Hoeller 039 * @author Rossen Stoyanchev 040 * @since 20.06.2003 041 * @see org.springframework.web.servlet.LocaleResolver 042 */ 043public class LocaleChangeInterceptor extends HandlerInterceptorAdapter { 044 045 /** 046 * Default name of the locale specification parameter: "locale". 047 */ 048 public static final String DEFAULT_PARAM_NAME = "locale"; 049 050 051 protected final Log logger = LogFactory.getLog(getClass()); 052 053 private String paramName = DEFAULT_PARAM_NAME; 054 055 private String[] httpMethods; 056 057 private boolean ignoreInvalidLocale = false; 058 059 private boolean languageTagCompliant = false; 060 061 062 /** 063 * Set the name of the parameter that contains a locale specification 064 * in a locale change request. Default is "locale". 065 */ 066 public void setParamName(String paramName) { 067 this.paramName = paramName; 068 } 069 070 /** 071 * Return the name of the parameter that contains a locale specification 072 * in a locale change request. 073 */ 074 public String getParamName() { 075 return this.paramName; 076 } 077 078 /** 079 * Configure the HTTP method(s) over which the locale can be changed. 080 * @param httpMethods the methods 081 * @since 4.2 082 */ 083 public void setHttpMethods(String... httpMethods) { 084 this.httpMethods = httpMethods; 085 } 086 087 /** 088 * Return the configured HTTP methods. 089 * @since 4.2 090 */ 091 public String[] getHttpMethods() { 092 return this.httpMethods; 093 } 094 095 /** 096 * Set whether to ignore an invalid value for the locale parameter. 097 * @since 4.2.2 098 */ 099 public void setIgnoreInvalidLocale(boolean ignoreInvalidLocale) { 100 this.ignoreInvalidLocale = ignoreInvalidLocale; 101 } 102 103 /** 104 * Return whether to ignore an invalid value for the locale parameter. 105 * @since 4.2.2 106 */ 107 public boolean isIgnoreInvalidLocale() { 108 return this.ignoreInvalidLocale; 109 } 110 111 /** 112 * Specify whether to parse request parameter values as BCP 47 language tags 113 * instead of Java's legacy locale specification format. 114 * The default is {@code false}. 115 * <p>Note: This mode requires JDK 7 or higher. Set this flag to {@code true} 116 * for BCP 47 compliance on JDK 7+ only. 117 * @since 4.3 118 * @see Locale#forLanguageTag(String) 119 * @see Locale#toLanguageTag() 120 */ 121 public void setLanguageTagCompliant(boolean languageTagCompliant) { 122 this.languageTagCompliant = languageTagCompliant; 123 } 124 125 /** 126 * Return whether to use BCP 47 language tags instead of Java's legacy 127 * locale specification format. 128 * @since 4.3 129 */ 130 public boolean isLanguageTagCompliant() { 131 return this.languageTagCompliant; 132 } 133 134 135 @Override 136 public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) 137 throws ServletException { 138 139 String newLocale = request.getParameter(getParamName()); 140 if (newLocale != null) { 141 if (checkHttpMethod(request.getMethod())) { 142 LocaleResolver localeResolver = RequestContextUtils.getLocaleResolver(request); 143 if (localeResolver == null) { 144 throw new IllegalStateException( 145 "No LocaleResolver found: not in a DispatcherServlet request?"); 146 } 147 try { 148 localeResolver.setLocale(request, response, parseLocaleValue(newLocale)); 149 } 150 catch (IllegalArgumentException ex) { 151 if (isIgnoreInvalidLocale()) { 152 if (logger.isDebugEnabled()) { 153 logger.debug("Ignoring invalid locale value [" + newLocale + "]: " + ex.getMessage()); 154 } 155 } 156 else { 157 throw ex; 158 } 159 } 160 } 161 } 162 // Proceed in any case. 163 return true; 164 } 165 166 private boolean checkHttpMethod(String currentMethod) { 167 String[] configuredMethods = getHttpMethods(); 168 if (ObjectUtils.isEmpty(configuredMethods)) { 169 return true; 170 } 171 for (String configuredMethod : configuredMethods) { 172 if (configuredMethod.equalsIgnoreCase(currentMethod)) { 173 return true; 174 } 175 } 176 return false; 177 } 178 179 /** 180 * Parse the given locale value as coming from a request parameter. 181 * <p>The default implementation calls {@link StringUtils#parseLocaleString(String)} 182 * or JDK 7's {@link Locale#forLanguageTag(String)}, depending on the 183 * {@link #setLanguageTagCompliant "languageTagCompliant"} configuration property. 184 * @param locale the locale value to parse 185 * @return the corresponding {@code Locale} instance 186 * @since 4.3 187 */ 188 @UsesJava7 189 protected Locale parseLocaleValue(String locale) { 190 return (isLanguageTagCompliant() ? Locale.forLanguageTag(locale) : StringUtils.parseLocaleString(locale)); 191 } 192 193}