diff --git a/application/src/main/java/run/halo/app/infra/webfilter/LocaleChangeWebFilter.java b/application/src/main/java/run/halo/app/infra/webfilter/LocaleChangeWebFilter.java index 3233d4ad5..9f25e98b0 100644 --- a/application/src/main/java/run/halo/app/infra/webfilter/LocaleChangeWebFilter.java +++ b/application/src/main/java/run/halo/app/infra/webfilter/LocaleChangeWebFilter.java @@ -3,6 +3,7 @@ package run.halo.app.infra.webfilter; import static run.halo.app.theme.ThemeLocaleContextResolver.LANGUAGE_COOKIE_NAME; import java.util.Locale; +import java.util.Objects; import java.util.Set; import org.springframework.core.Ordered; import org.springframework.core.annotation.Order; @@ -45,20 +46,24 @@ public class LocaleChangeWebFilter implements WebFilter { @Override @NonNull public Mono filter(ServerWebExchange exchange, @NonNull WebFilterChain chain) { - var request = exchange.getRequest(); return matcher.matches(exchange) .filter(MatchResult::isMatch) .doOnNext(result -> { var localeContext = themeLocaleContextResolver.resolveLocaleContext(exchange); var locale = localeContext.getLocale(); if (locale != null) { - setLanguageCookie(exchange, locale); + setLanguageCookieIfAbsent(exchange, locale); } }) .then(Mono.defer(() -> chain.filter(exchange))); } - void setLanguageCookie(ServerWebExchange exchange, Locale locale) { + void setLanguageCookieIfAbsent(ServerWebExchange exchange, Locale locale) { + var languageCookie = exchange.getRequest().getCookies().getFirst(LANGUAGE_COOKIE_NAME); + if (languageCookie != null + && Objects.equals(languageCookie.getValue(), locale.toLanguageTag())) { + return; + } var cookie = ResponseCookie.from(LANGUAGE_COOKIE_NAME, locale.toLanguageTag()) .path("/") .secure("https".equalsIgnoreCase(exchange.getRequest().getURI().getScheme())) diff --git a/application/src/main/java/run/halo/app/theme/ThemeLocaleContextResolver.java b/application/src/main/java/run/halo/app/theme/ThemeLocaleContextResolver.java index 3d23b2f64..da6b608a0 100644 --- a/application/src/main/java/run/halo/app/theme/ThemeLocaleContextResolver.java +++ b/application/src/main/java/run/halo/app/theme/ThemeLocaleContextResolver.java @@ -4,6 +4,7 @@ import java.util.Locale; import java.util.Optional; import java.util.TimeZone; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.LocaleUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.context.i18n.LocaleContext; import org.springframework.context.i18n.SimpleTimeZoneAwareLocaleContext; @@ -39,6 +40,10 @@ public class ThemeLocaleContextResolver extends AcceptHeaderLocaleContextResolve .or(() -> UserLocaleRequestAttributeWriteFilter.getUserLocale(request)) .orElseGet(() -> super.resolveLocaleContext(exchange).getLocale()); + if (LocaleUtils.isLanguageUndetermined(locale)) { + locale = null; + } + var timeZone = getTimeZoneFromCookie(request) .orElseGet(TimeZone::getDefault); diff --git a/application/src/test/java/run/halo/app/theme/ThemeLocaleContextResolverTest.java b/application/src/test/java/run/halo/app/theme/ThemeLocaleContextResolverTest.java index a1ada782e..2e5cc3985 100644 --- a/application/src/test/java/run/halo/app/theme/ThemeLocaleContextResolverTest.java +++ b/application/src/test/java/run/halo/app/theme/ThemeLocaleContextResolverTest.java @@ -177,6 +177,15 @@ class ThemeLocaleContextResolverTest { assertThat(this.resolver.resolveLocaleContext(exchange).getLocale()).isEqualTo(US); } + @Test + void resolveUnderminedLocale() { + var request = MockServerHttpRequest.get("/") + .header(HttpHeaders.ACCEPT_LANGUAGE, "und") + .build(); + var exchange = MockServerWebExchange.from(request); + + assertThat(this.resolver.resolveLocaleContext(exchange).getLocale()).isNull(); + } private ServerWebExchange exchange(Locale... locales) { return MockServerWebExchange.from( diff --git a/application/src/test/java/run/halo/app/webfilter/LocaleChangeWebFilterTest.java b/application/src/test/java/run/halo/app/webfilter/LocaleChangeWebFilterTest.java index 4d336bc4a..f11db04da 100644 --- a/application/src/test/java/run/halo/app/webfilter/LocaleChangeWebFilterTest.java +++ b/application/src/test/java/run/halo/app/webfilter/LocaleChangeWebFilterTest.java @@ -9,6 +9,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.http.HttpCookie; import org.springframework.http.MediaType; import org.springframework.mock.http.server.reactive.MockServerHttpRequest; import org.springframework.mock.web.server.MockServerWebExchange; @@ -44,11 +45,43 @@ class LocaleChangeWebFilterTest { } @Test - void shouldRespondLanguageCookieWithUndefinedLanguageTag() { + void shouldNotRespondLanguageCookieIfChanged() { WebFilterChain webFilterChain = filterExchange -> { var languageCookie = filterExchange.getResponse().getCookies().getFirst("language"); assertNotNull(languageCookie); - assertEquals("und", languageCookie.getValue()); + assertEquals("zh-CN", languageCookie.getValue()); + return Mono.empty(); + }; + var exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/home") + .accept(MediaType.TEXT_HTML) + .cookie(new HttpCookie("language", "zh-HK")) + .queryParam("language", "zh-CN") + .build() + ); + this.filter.filter(exchange, webFilterChain).block(); + } + + @Test + void shouldNotRespondLanguageCookieIfNotChanged() { + WebFilterChain webFilterChain = filterExchange -> { + var languageCookie = filterExchange.getResponse().getCookies().getFirst("language"); + assertNull(languageCookie); + return Mono.empty(); + }; + var exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/home") + .accept(MediaType.TEXT_HTML) + .cookie(new HttpCookie("language", "zh-CN")) + .queryParam("language", "zh-CN") + .build() + ); + this.filter.filter(exchange, webFilterChain).block(); + } + + @Test + void shouldNotRespondLanguageCookieWithUndeterminedLanguageTag() { + WebFilterChain webFilterChain = filterExchange -> { + var languageCookie = filterExchange.getResponse().getCookies().getFirst("language"); + assertNull(languageCookie); return Mono.empty(); }; var exchange = MockServerWebExchange.from(MockServerHttpRequest.get("/home")