001/* 002 * Copyright 2002-2018 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.ArrayList; 020import java.util.Enumeration; 021import java.util.List; 022import java.util.Locale; 023import javax.servlet.http.HttpServletRequest; 024import javax.servlet.http.HttpServletResponse; 025 026import org.springframework.util.StringUtils; 027import org.springframework.web.servlet.LocaleResolver; 028 029/** 030 * {@link LocaleResolver} implementation that simply uses the primary locale 031 * specified in the "accept-language" header of the HTTP request (that is, 032 * the locale sent by the client browser, normally that of the client's OS). 033 * 034 * <p>Note: Does not support {@code setLocale}, since the accept header 035 * can only be changed through changing the client's locale settings. 036 * 037 * @author Juergen Hoeller 038 * @author Rossen Stoyanchev 039 * @since 27.02.2003 040 * @see javax.servlet.http.HttpServletRequest#getLocale() 041 */ 042public class AcceptHeaderLocaleResolver implements LocaleResolver { 043 044 private final List<Locale> supportedLocales = new ArrayList<Locale>(4); 045 046 private Locale defaultLocale; 047 048 049 /** 050 * Configure supported locales to check against the requested locales 051 * determined via {@link HttpServletRequest#getLocales()}. If this is not 052 * configured then {@link HttpServletRequest#getLocale()} is used instead. 053 * @param locales the supported locales 054 * @since 4.3 055 */ 056 public void setSupportedLocales(List<Locale> locales) { 057 this.supportedLocales.clear(); 058 if (locales != null) { 059 this.supportedLocales.addAll(locales); 060 } 061 } 062 063 /** 064 * Return the configured list of supported locales. 065 * @since 4.3 066 */ 067 public List<Locale> getSupportedLocales() { 068 return this.supportedLocales; 069 } 070 071 /** 072 * Configure a fixed default locale to fall back on if the request does not 073 * have an "Accept-Language" header. 074 * <p>By default this is not set in which case when there is "Accept-Language" 075 * header, the default locale for the server is used as defined in 076 * {@link HttpServletRequest#getLocale()}. 077 * @param defaultLocale the default locale to use 078 * @since 4.3 079 */ 080 public void setDefaultLocale(Locale defaultLocale) { 081 this.defaultLocale = defaultLocale; 082 } 083 084 /** 085 * The configured default locale, if any. 086 * @since 4.3 087 */ 088 public Locale getDefaultLocale() { 089 return this.defaultLocale; 090 } 091 092 093 @Override 094 public Locale resolveLocale(HttpServletRequest request) { 095 Locale defaultLocale = getDefaultLocale(); 096 if (defaultLocale != null && request.getHeader("Accept-Language") == null) { 097 return defaultLocale; 098 } 099 Locale requestLocale = request.getLocale(); 100 List<Locale> supportedLocales = getSupportedLocales(); 101 if (supportedLocales.isEmpty() || supportedLocales.contains(requestLocale)) { 102 return requestLocale; 103 } 104 Locale supportedLocale = findSupportedLocale(request, supportedLocales); 105 if (supportedLocale != null) { 106 return supportedLocale; 107 } 108 return (defaultLocale != null ? defaultLocale : requestLocale); 109 } 110 111 private Locale findSupportedLocale(HttpServletRequest request, List<Locale> supportedLocales) { 112 Enumeration<Locale> requestLocales = request.getLocales(); 113 Locale languageMatch = null; 114 while (requestLocales.hasMoreElements()) { 115 Locale locale = requestLocales.nextElement(); 116 if (supportedLocales.contains(locale)) { 117 if (languageMatch == null || languageMatch.getLanguage().equals(locale.getLanguage())) { 118 // Full match: language + country, possibly narrowed from earlier language-only match 119 return locale; 120 } 121 } 122 else if (languageMatch == null) { 123 // Let's try to find a language-only match as a fallback 124 for (Locale candidate : supportedLocales) { 125 if (!StringUtils.hasLength(candidate.getCountry()) && 126 candidate.getLanguage().equals(locale.getLanguage())) { 127 languageMatch = candidate; 128 break; 129 } 130 } 131 } 132 } 133 return languageMatch; 134 } 135 136 @Override 137 public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) { 138 throw new UnsupportedOperationException( 139 "Cannot change HTTP accept header - use a different locale resolution strategy"); 140 } 141 142}