KEYCLOAK-8146: Extract LocaleSelectorSPI to allow custom overrides of locale selection

This commit is contained in:
Johannes Knutsen 2018-08-25 22:56:33 +02:00 committed by Marek Posolda
parent 26f257a6ac
commit d4a5c81034
17 changed files with 430 additions and 232 deletions

View file

@ -0,0 +1,33 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.locale;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.provider.Provider;
import java.util.Locale;
public interface LocaleSelectorProvider extends Provider {
/**
* Resolve the locale which should be used for the request
* @param user
* @return
*/
Locale resolveLocale(RealmModel realm, UserModel user);
}

View file

@ -0,0 +1,22 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.locale;
import org.keycloak.provider.ProviderFactory;
public interface LocaleSelectorProviderFactory extends ProviderFactory<LocaleSelectorProvider> {
}

View file

@ -0,0 +1,45 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.locale;
import org.keycloak.provider.Provider;
import org.keycloak.provider.ProviderFactory;
import org.keycloak.provider.Spi;
public class LocaleSelectorSPI implements Spi {
@Override
public boolean isInternal() {
return false;
}
@Override
public String getName() {
return "localeSelector";
}
@Override
public Class<? extends Provider> getProviderClass() {
return LocaleSelectorProvider.class;
}
@Override
public Class<? extends ProviderFactory> getProviderFactoryClass() {
return LocaleSelectorProviderFactory.class;
}
}

View file

@ -32,6 +32,7 @@
# limitations under the License.
#
org.keycloak.locale.LocaleSelectorSPI
org.keycloak.storage.UserStorageProviderSpi
org.keycloak.theme.ThemeResourceSpi
org.keycloak.theme.ThemeSelectorSpi

View file

@ -0,0 +1,160 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.locale;
import org.keycloak.OAuth2Constants;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.util.CookieHelper;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.UriInfo;
import java.util.Locale;
public class DefaultLocaleSelectorProvider implements LocaleSelectorProvider {
protected static final String LOCALE_COOKIE = "KEYCLOAK_LOCALE";
protected static final String KC_LOCALE_PARAM = "kc_locale";
protected final KeycloakSession session;
public DefaultLocaleSelectorProvider(KeycloakSession session) {
this.session = session;
}
@Override
public Locale resolveLocale(RealmModel realm, UserModel user) {
final HttpHeaders requestHeaders = session.getContext().getRequestHeaders();
final UriInfo uri = session.getContext().getUri();
return getLocale(realm, user, requestHeaders, uri);
}
@Override
public void close() {
}
protected Locale getLocale(RealmModel realm, UserModel user, HttpHeaders requestHeaders, UriInfo uriInfo) {
if (!realm.isInternationalizationEnabled()) {
return Locale.ENGLISH;
} else {
Locale locale = getUserLocale(realm, user, requestHeaders, uriInfo);
return locale != null ? locale : Locale.forLanguageTag(realm.getDefaultLocale());
}
}
protected Locale getUserLocale(RealmModel realm, UserModel user, HttpHeaders requestHeaders, UriInfo uriInfo) {
final LocaleSelection kcLocaleQueryParamSelection = getKcLocaleQueryParamSelection(realm, uriInfo);
if (kcLocaleQueryParamSelection != null) {
updateLocaleCookie(realm, kcLocaleQueryParamSelection.getLocaleString(), uriInfo);
if (user != null) {
updateUsersLocale(user, kcLocaleQueryParamSelection.getLocaleString());
}
return kcLocaleQueryParamSelection.getLocale();
}
final LocaleSelection localeCookieSelection = getLocaleCookieSelection(realm, requestHeaders);
if (localeCookieSelection != null) {
if (user != null) {
updateUsersLocale(user, localeCookieSelection.getLocaleString());
}
return localeCookieSelection.getLocale();
}
final LocaleSelection userProfileSelection = getUserProfileSelection(realm, user);
if (userProfileSelection != null) {
updateLocaleCookie(realm, userProfileSelection.getLocaleString(), uriInfo);
return userProfileSelection.getLocale();
}
final LocaleSelection uiLocalesQueryParamSelection = getUiLocalesQueryParamSelection(realm, uriInfo);
if (uiLocalesQueryParamSelection != null) {
return uiLocalesQueryParamSelection.getLocale();
}
final LocaleSelection acceptLanguageHeaderSelection = getAcceptLanguageHeaderLocale(realm, requestHeaders);
if (acceptLanguageHeaderSelection != null) {
return acceptLanguageHeaderSelection.getLocale();
}
return null;
}
protected LocaleSelection getKcLocaleQueryParamSelection(RealmModel realm, UriInfo uriInfo) {
if (uriInfo == null || !uriInfo.getQueryParameters().containsKey(KC_LOCALE_PARAM)) {
return null;
}
String localeString = uriInfo.getQueryParameters().getFirst(KC_LOCALE_PARAM);
return findLocale(realm, localeString);
}
protected LocaleSelection getLocaleCookieSelection(RealmModel realm, HttpHeaders httpHeaders) {
if (httpHeaders == null || !httpHeaders.getCookies().containsKey(LOCALE_COOKIE)) {
return null;
}
String localeString = httpHeaders.getCookies().get(LOCALE_COOKIE).getValue();
return findLocale(realm, localeString);
}
protected LocaleSelection getUserProfileSelection(RealmModel realm, UserModel user) {
if (user == null || !user.getAttributes().containsKey(UserModel.LOCALE)) {
return null;
}
String localeString = user.getFirstAttribute(UserModel.LOCALE);
return findLocale(realm, localeString);
}
protected LocaleSelection getUiLocalesQueryParamSelection(RealmModel realm, UriInfo uriInfo) {
if (uriInfo == null || !uriInfo.getQueryParameters().containsKey(OAuth2Constants.UI_LOCALES_PARAM)) {
return null;
}
String localeString = uriInfo.getQueryParameters().getFirst(OAuth2Constants.UI_LOCALES_PARAM);
return findLocale(realm, localeString.split(" "));
}
protected LocaleSelection getAcceptLanguageHeaderLocale(RealmModel realm, HttpHeaders httpHeaders) {
if (httpHeaders == null || httpHeaders.getAcceptableLanguages() == null || httpHeaders.getAcceptableLanguages().isEmpty()) {
return null;
}
for (Locale l : httpHeaders.getAcceptableLanguages()) {
String localeString = l.toLanguageTag();
LocaleSelection localeSelection = findLocale(realm, localeString);
if (localeSelection != null) {
return localeSelection;
}
}
return null;
}
protected void updateLocaleCookie(RealmModel realm, String locale, UriInfo uriInfo) {
boolean secure = realm.getSslRequired().isRequired(uriInfo.getRequestUri().getHost());
CookieHelper.addCookie(LOCALE_COOKIE, locale, AuthenticationManager.getRealmCookiePath(realm, uriInfo), null, null, -1, secure, true);
}
protected LocaleSelection findLocale(RealmModel realm, String... localeStrings) {
return new LocaleNegotiator(realm.getSupportedLocales()).invoke(localeStrings);
}
protected void updateUsersLocale(UserModel user, String locale) {
if (!locale.equals(user.getFirstAttribute("locale"))) {
user.setSingleAttribute(UserModel.LOCALE, locale);
}
}
}

View file

@ -0,0 +1,45 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.locale;
import org.keycloak.Config;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.KeycloakSessionFactory;
public class DefaultLocaleSelectorProviderFactory implements LocaleSelectorProviderFactory {
@Override
public LocaleSelectorProvider create(KeycloakSession session) {
return new DefaultLocaleSelectorProvider(session);
}
@Override
public void init(Config.Scope config) {
}
@Override
public void postInit(KeycloakSessionFactory factory) {
}
@Override
public void close() {
}
@Override
public String getId() {
return "default";
}
}

View file

@ -14,19 +14,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.services.util;
package org.keycloak.locale;
import java.util.Locale;
import java.util.Set;
class LocaleNegotiator {
public class LocaleNegotiator {
private Set<String> supportedLocales;
LocaleNegotiator(Set<String> supportedLocales) {
public LocaleNegotiator(Set<String> supportedLocales) {
this.supportedLocales = supportedLocales;
}
Locale invoke(String... localeStrings) {
public LocaleSelection invoke(String... localeStrings) {
for (String localeString : localeStrings) {
if (localeString != null) {
Locale result = null;
@ -38,15 +38,16 @@ class LocaleNegotiator {
result = locale;
}
if (locale.getCountry().equals(search.getCountry())) {
return locale;
return new LocaleSelection(localeString, locale);
}
}
}
if (result != null) {
return result;
return new LocaleSelection(localeString, result);
}
}
}
return null;
}
}

View file

@ -0,0 +1,37 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.locale;
import java.util.Locale;
public class LocaleSelection {
private final String localeString;
private final Locale locale;
public LocaleSelection(String localeString, Locale locale) {
this.localeString = localeString;
this.locale = locale;
}
public String getLocaleString() {
return localeString;
}
public Locale getLocale() {
return locale;
}
}

View file

@ -19,6 +19,7 @@ package org.keycloak.services;
import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.common.ClientConnection;
import org.keycloak.locale.LocaleSelectorProvider;
import org.keycloak.models.ClientModel;
import org.keycloak.models.KeycloakContext;
import org.keycloak.models.KeycloakSession;
@ -26,7 +27,6 @@ import org.keycloak.models.KeycloakUriInfo;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.services.resources.KeycloakApplication;
import org.keycloak.services.util.LocaleHelper;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.UriInfo;
@ -117,6 +117,6 @@ public class DefaultKeycloakContext implements KeycloakContext {
@Override
public Locale resolveLocale(UserModel user) {
return LocaleHelper.getLocale(session, realm, user);
return session.getProvider(LocaleSelectorProvider.class).resolveLocale(realm, user);
}
}

View file

@ -11,7 +11,6 @@ import org.keycloak.models.KeycloakTransaction;
import org.keycloak.models.RealmModel;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.messages.Messages;
import org.keycloak.services.util.LocaleHelper;
import org.keycloak.theme.FreeMarkerUtil;
import org.keycloak.theme.Theme;
import org.keycloak.theme.beans.LocaleBean;
@ -71,7 +70,7 @@ public class KeycloakErrorHandler implements ExceptionMapper<Throwable> {
Theme theme = session.theme().getTheme(Theme.Type.LOGIN);
Locale locale = LocaleHelper.getLocale(session, realm, null);
Locale locale = session.getContext().resolveLocale(null);
FreeMarkerUtil freeMarker = new FreeMarkerUtil();
Map<String, Object> attributes = initAttributes(realm, theme, locale, statusCode);

View file

@ -17,7 +17,6 @@ import org.keycloak.services.managers.Auth;
import org.keycloak.services.managers.AuthenticationManager;
import org.keycloak.services.managers.ClientManager;
import org.keycloak.services.managers.RealmManager;
import org.keycloak.services.util.LocaleHelper;
import org.keycloak.services.util.ResolveRelative;
import org.keycloak.services.validation.Validation;
import org.keycloak.theme.BrowserSecurityHeaderSetup;
@ -100,7 +99,7 @@ public class AccountConsole {
UserModel user = null;
if (auth != null) user = auth.getUser();
Locale locale = LocaleHelper.getLocale(session, realm, user);
Locale locale = session.getContext().resolveLocale(user);
map.put("locale", locale.toLanguageTag());
Properties messages = theme.getMessages(locale);
map.put("msg", new MessageFormatterMethod(locale, messages));

View file

@ -1,146 +0,0 @@
/*
* Copyright 2016 Red Hat, Inc. and/or its affiliates
* and other contributors as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.keycloak.services.util;
import org.keycloak.OAuth2Constants;
import org.keycloak.models.KeycloakContext;
import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel;
import org.keycloak.services.managers.AuthenticationManager;
import javax.ws.rs.core.HttpHeaders;
import javax.ws.rs.core.UriInfo;
import java.util.Locale;
import java.util.Set;
/**
* @author <a href="mailto:gerbermichi@me.com">Michael Gerber</a>
*/
public class LocaleHelper {
private static final String LOCALE_COOKIE = "KEYCLOAK_LOCALE";
private static final String KC_LOCALE_PARAM = "kc_locale";
public static Locale getLocale(KeycloakSession session, RealmModel realm, UserModel user) {
if (!realm.isInternationalizationEnabled()) {
return Locale.ENGLISH;
} else {
Locale locale = getUserLocale(session, realm, user);
return locale != null ? locale : Locale.forLanguageTag(realm.getDefaultLocale());
}
}
public static Locale getLocaleFromCookie(KeycloakSession session) {
KeycloakContext ctx = session.getContext();
if (ctx.getRequestHeaders() != null && ctx.getRequestHeaders().getCookies().containsKey(LOCALE_COOKIE)) {
String localeString = ctx.getRequestHeaders().getCookies().get(LOCALE_COOKIE).getValue();
Locale locale = findLocale(ctx.getRealm().getSupportedLocales(), localeString);
if (locale != null) {
return locale;
}
}
String locale = ctx.getRealm().getDefaultLocale();
if (locale != null) {
return Locale.forLanguageTag(locale);
} else {
return Locale.ENGLISH;
}
}
private static Locale getUserLocale(KeycloakSession session, RealmModel realm, UserModel user) {
UriInfo uriInfo = session.getContext().getUri();
HttpHeaders httpHeaders = session.getContext().getRequestHeaders();
// kc_locale query parameter
if (uriInfo != null && uriInfo.getQueryParameters().containsKey(KC_LOCALE_PARAM)) {
String localeString = uriInfo.getQueryParameters().getFirst(KC_LOCALE_PARAM);
Locale locale = findLocale(realm.getSupportedLocales(), localeString);
if (locale != null) {
updateLocaleCookie(session, realm, localeString);
if (user != null) {
updateUsersLocale(user, localeString);
}
return locale;
}
}
// Locale cookie
if (httpHeaders != null && httpHeaders.getCookies().containsKey(LOCALE_COOKIE)) {
String localeString = httpHeaders.getCookies().get(LOCALE_COOKIE).getValue();
Locale locale = findLocale(realm.getSupportedLocales(), localeString);
if (locale != null) {
if (user != null) {
updateUsersLocale(user, localeString);
}
return locale;
}
}
// User profile
if (user != null && user.getAttributes().containsKey(UserModel.LOCALE)) {
String localeString = user.getFirstAttribute(UserModel.LOCALE);
Locale locale = findLocale(realm.getSupportedLocales(), localeString);
if (locale != null) {
updateLocaleCookie(session, realm, localeString);
return locale;
}
}
// ui_locales query parameter
if (uriInfo != null && uriInfo.getQueryParameters().containsKey(OAuth2Constants.UI_LOCALES_PARAM)) {
String localeString = uriInfo.getQueryParameters().getFirst(OAuth2Constants.UI_LOCALES_PARAM);
Locale locale = findLocale(realm.getSupportedLocales(), localeString.split(" "));
if (locale != null) {
return locale;
}
}
// Accept-Language http header
if (httpHeaders != null && httpHeaders.getAcceptableLanguages() != null && !httpHeaders.getAcceptableLanguages().isEmpty()) {
for (Locale l : httpHeaders.getAcceptableLanguages()) {
String localeString = l.toLanguageTag();
Locale locale = findLocale(realm.getSupportedLocales(), localeString);
if (locale != null) {
return locale;
}
}
}
return null;
}
private static void updateLocaleCookie(KeycloakSession session,
RealmModel realm,
String locale) {
boolean secure = realm.getSslRequired().isRequired(session.getContext().getUri().getRequestUri().getHost());
CookieHelper.addCookie(LOCALE_COOKIE, locale, AuthenticationManager.getRealmCookiePath(realm, session.getContext().getUri()), null, null, -1, secure, true);
}
private static Locale findLocale(Set<String> supportedLocales, String... localeStrings) {
return new LocaleNegotiator(supportedLocales).invoke(localeStrings);
}
private static void updateUsersLocale(UserModel user, String locale) {
if (!locale.equals(user.getFirstAttribute("locale"))) {
user.setSingleAttribute(UserModel.LOCALE, locale);
}
}
}

View file

@ -23,7 +23,6 @@ import org.jboss.resteasy.spi.ResteasyProviderFactory;
import org.keycloak.models.KeycloakSession;
import org.keycloak.services.validation.Validation;
import org.keycloak.theme.Theme;
import org.keycloak.theme.ThemeProvider;
import java.io.IOException;
import java.util.Locale;
@ -41,7 +40,7 @@ public class P3PHelper {
try {
Theme theme = session.theme().getTheme(Theme.Type.LOGIN);
Locale locale = LocaleHelper.getLocaleFromCookie(session);
Locale locale = session.getContext().resolveLocale(null);
String p3pValue = theme.getMessages(locale).getProperty("p3pPolicy");
if (!Validation.isBlank(p3pValue)) {

View file

@ -0,0 +1,18 @@
#
# Copyright 2016 Red Hat, Inc. and/or its affiliates
# and other contributors as indicated by the @author tags.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
org.keycloak.locale.DefaultLocaleSelectorProviderFactory

View file

@ -0,0 +1,57 @@
package org.keycloak.locale;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
public class LocaleNegotiatorTest {
private LocaleNegotiator localeNegotiator;
@Before
public void setUp() {
Set<String> supportedLocales = new HashSet<>();
supportedLocales.add("de");
supportedLocales.add("de-AT");
supportedLocales.add("de-CH");
supportedLocales.add("de-DE");
supportedLocales.add("pt-BR");
localeNegotiator = new LocaleNegotiator(supportedLocales);
}
@Test
public void shouldMatchWithoutCountryCode() {
String expectedLocaleString = "de";
LocaleSelection actualLocale = localeNegotiator.invoke(expectedLocaleString);
Assert.assertEquals(Locale.GERMAN, actualLocale.getLocale());
Assert.assertEquals(expectedLocaleString, actualLocale.getLocaleString());
}
@Test
public void shouldMatchWithPriorityCountryCode() {
String expectedLocaleString = "de-CH";
LocaleSelection actualLocale = localeNegotiator.invoke(expectedLocaleString, "de");
Assert.assertEquals(new Locale("de", "CH"), actualLocale.getLocale());
Assert.assertEquals(expectedLocaleString, actualLocale.getLocaleString());
}
@Test
public void shouldMatchWithPriorityNoCountryCode() {
String expectedLocaleString = "de";
LocaleSelection actualLocale = localeNegotiator.invoke(expectedLocaleString, "de-CH");
Assert.assertEquals(new Locale(expectedLocaleString), actualLocale.getLocale());
Assert.assertEquals(expectedLocaleString, actualLocale.getLocaleString());
}
@Test
public void shouldMatchOmittedCountryCodeWithBestFit() {
String expectedLocaleString = "pt";
LocaleSelection actualLocale = localeNegotiator.invoke(expectedLocaleString, "es-ES");
Assert.assertEquals(new Locale("pt", "BR"), actualLocale.getLocale());
Assert.assertEquals(expectedLocaleString, actualLocale.getLocaleString());
}
}

View file

@ -1,23 +0,0 @@
package org.keycloak.services.util;
import org.junit.Test;
import java.lang.reflect.Method;
import java.util.Locale;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.core.IsNull.nullValue;
public class LocaleHelperTest {
@Test
public void shouldNotExceptionOnNullLocaleAttributeItem() throws Exception {
final Method method = LocaleHelper.class.getDeclaredMethod("findLocale", Set.class, String[].class);
method.setAccessible(true);
Locale foundLocale = (Locale) method.invoke(null, Stream.of("en", "es", "fr").collect(Collectors.toSet()), new String[]{null});
assertThat(foundLocale, nullValue());
}
}

View file

@ -1,49 +0,0 @@
package org.keycloak.services.util;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import java.util.HashSet;
import java.util.Locale;
import java.util.Set;
public class LocaleNegotiatorTest {
private LocaleNegotiator localeNegotiator;
@Before
public void setUp() {
Set<String> supportedLocales = new HashSet<>();
supportedLocales.add("de");
supportedLocales.add("de-AT");
supportedLocales.add("de-CH");
supportedLocales.add("de-DE");
supportedLocales.add("pt-BR");
localeNegotiator = new LocaleNegotiator(supportedLocales);
}
@Test
public void shouldMatchWithoutCountryCode() {
Locale actualLocale = localeNegotiator.invoke("de");
Assert.assertEquals(Locale.GERMAN, actualLocale);
}
@Test
public void shouldMatchWithPriorityCountryCode() {
Locale actualLocale = localeNegotiator.invoke("de-CH", "de");
Assert.assertEquals(new Locale("de", "CH"), actualLocale);
}
@Test
public void shouldMatchWithPriorityNoCountryCode() {
Locale actualLocale = localeNegotiator.invoke("de", "de-CH");
Assert.assertEquals(new Locale("de"), actualLocale);
}
@Test
public void shouldMatchOmittedCountryCodeWithBestFit() {
Locale actualLocale = localeNegotiator.invoke("pt", "es-ES");
Assert.assertEquals(new Locale("pt", "BR"), actualLocale);
}
}