Skip to content

Commit

Permalink
ui_locales as specified in:
Browse files Browse the repository at this point in the history
http://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2

replace ' through '' and introduce msg method for i18n in *.ftl files

replace ' through '' and introduce msg method for i18n in *.ftl files

css dorpdown

get current url with locale as query parameter

Introduce LocaleBean

add css dropdown to account page

css styling...

css styling...
  • Loading branch information
gerbermichi committed Mar 19, 2015
1 parent de4c05e commit d8193f0
Show file tree
Hide file tree
Showing 41 changed files with 426 additions and 223 deletions.
Expand Up @@ -6,7 +6,7 @@
import org.keycloak.account.freemarker.model.*; import org.keycloak.account.freemarker.model.*;
import org.keycloak.events.Event; import org.keycloak.events.Event;
import org.keycloak.freemarker.*; import org.keycloak.freemarker.*;
import org.keycloak.freemarker.beans.TextFormatterBean; import org.keycloak.freemarker.beans.MessageFormatterMethod;
import org.keycloak.models.KeycloakSession; import org.keycloak.models.KeycloakSession;
import org.keycloak.models.RealmModel; import org.keycloak.models.RealmModel;
import org.keycloak.models.UserModel; import org.keycloak.models.UserModel;
Expand All @@ -18,6 +18,7 @@
import java.net.URI; import java.net.URI;
import java.text.MessageFormat; import java.text.MessageFormat;
import java.util.*; import java.util.*;
import org.keycloak.freemarker.beans.LocaleBean;


/** /**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a> * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
Expand Down Expand Up @@ -86,14 +87,10 @@ public Response createResponse(AccountPages page) {
} }


Locale locale = LocaleHelper.getLocale(realm, user, uriInfo, headers); Locale locale = LocaleHelper.getLocale(realm, user, uriInfo, headers);
if(locale != null){
attributes.put("locale", locale);
attributes.put("formatter", new TextFormatterBean(locale));
}
Properties messages; Properties messages;
try { try {
messages = theme.getMessages(locale); messages = theme.getMessages(locale);
attributes.put("rb", messages); attributes.put("msg", new MessageFormatterMethod(locale, messages));
} catch (IOException e) { } catch (IOException e) {
logger.warn("Failed to load messages", e); logger.warn("Failed to load messages", e);
messages = new Properties(); messages = new Properties();
Expand All @@ -113,7 +110,7 @@ public Response createResponse(AccountPages page) {
if (message != null) { if (message != null) {
String formattedMessage; String formattedMessage;
if(messages.containsKey(message)){ if(messages.containsKey(message)){
formattedMessage = new MessageFormat(messages.getProperty(message).replace("'","''"),locale).format(parameters); formattedMessage = new MessageFormat(messages.getProperty(message),locale).format(parameters);
}else{ }else{
formattedMessage = message; formattedMessage = message;
} }
Expand All @@ -130,6 +127,16 @@ public Response createResponse(AccountPages page) {


attributes.put("url", new UrlBean(realm, theme, baseUri, baseQueryUri, uriInfo.getRequestUri(), stateChecker)); attributes.put("url", new UrlBean(realm, theme, baseUri, baseQueryUri, uriInfo.getRequestUri(), stateChecker));


if (realm.isInternationalizationEnabled()) {
UriBuilder b;
switch (page) {
default:
b = UriBuilder.fromUri(baseQueryUri).path(uriInfo.getPath());
break;
}
attributes.put("locale", new LocaleBean(realm, locale, b, messages));
}

attributes.put("features", new FeaturesBean(identityProviderEnabled, eventsEnabled, passwordUpdateSupported)); attributes.put("features", new FeaturesBean(identityProviderEnabled, eventsEnabled, passwordUpdateSupported));


switch (page) { switch (page) {
Expand Down
Expand Up @@ -59,10 +59,6 @@ public String getSessionsLogoutUrl() {
return Urls.accountSessionsLogoutPage(baseQueryURI, realm, stateChecker).toString(); return Urls.accountSessionsLogoutPage(baseQueryURI, realm, stateChecker).toString();
} }


public String getLocaleCookiePath(){
return Urls.localeCookiePath(baseURI, realm);
}

public String getTotpRemoveUrl() { public String getTotpRemoveUrl() {
return Urls.accountTotpRemove(baseQueryURI, realm, stateChecker).toString(); return Urls.accountTotpRemove(baseQueryURI, realm, stateChecker).toString();
} }
Expand Down
Expand Up @@ -15,7 +15,8 @@
*/ */
public class LocaleHelper { public class LocaleHelper {
public final static String LOCALE_COOKIE = "KEYCLOAK_LOCALE"; public final static String LOCALE_COOKIE = "KEYCLOAK_LOCALE";
public static final String LOCALE_PARAM = "ui_locale"; public static final String UI_LOCALES_PARAM = "ui_locales";
public static final String KC_LOCALE_PARAM = "kc_locale";


private final static Logger LOGGER = Logger.getLogger(LocaleHelper.class); private final static Logger LOGGER = Logger.getLogger(LocaleHelper.class);


Expand All @@ -28,12 +29,26 @@ public static Locale getLocale(RealmModel realm, UserModel user, UriInfo uriInfo
return Locale.ENGLISH; return Locale.ENGLISH;
} }


//0. 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){
if(user != null){
user.setAttribute(UserModel.LOCALE, locale.toLanguageTag());
}
return locale;
}else{
LOGGER.infof("Locale %s is not supported.", localeString);
}
}

//1. Locale cookie //1. Locale cookie
if(httpHeaders != null && httpHeaders.getCookies().containsKey(LOCALE_COOKIE)){ if(httpHeaders != null && httpHeaders.getCookies().containsKey(LOCALE_COOKIE)){
String localeString = httpHeaders.getCookies().get(LOCALE_COOKIE).getValue(); String localeString = httpHeaders.getCookies().get(LOCALE_COOKIE).getValue();
Locale locale = findLocale(localeString, realm.getSupportedLocales()); Locale locale = findLocale(realm.getSupportedLocales(), localeString);
if(locale != null){ if(locale != null){
if(user != null){ if(user != null && user.getAttribute(UserModel.LOCALE) == null){
user.setAttribute(UserModel.LOCALE, locale.toLanguageTag()); user.setAttribute(UserModel.LOCALE, locale.toLanguageTag());
} }
return locale; return locale;
Expand All @@ -45,7 +60,7 @@ public static Locale getLocale(RealmModel realm, UserModel user, UriInfo uriInfo
//2. User profile //2. User profile
if(user != null && user.getAttributes().containsKey(UserModel.LOCALE)){ if(user != null && user.getAttributes().containsKey(UserModel.LOCALE)){
String localeString = user.getAttribute(UserModel.LOCALE); String localeString = user.getAttribute(UserModel.LOCALE);
Locale locale = findLocale(localeString, realm.getSupportedLocales()); Locale locale = findLocale(realm.getSupportedLocales(), localeString);
if(locale != null){ if(locale != null){


return locale; return locale;
Expand All @@ -55,9 +70,9 @@ public static Locale getLocale(RealmModel realm, UserModel user, UriInfo uriInfo
} }


//3. ui_locales query parameter //3. ui_locales query parameter
if(uriInfo != null && uriInfo.getQueryParameters().containsKey(LOCALE_PARAM)){ if(uriInfo != null && uriInfo.getQueryParameters().containsKey(UI_LOCALES_PARAM)){
String localeString = uriInfo.getQueryParameters().getFirst(LOCALE_PARAM); String localeString = uriInfo.getQueryParameters().getFirst(UI_LOCALES_PARAM);
Locale locale = findLocale(localeString, realm.getSupportedLocales()); Locale locale = findLocale(realm.getSupportedLocales(), localeString.split(" "));
if(locale != null){ if(locale != null){
return locale; return locale;
}else{ }else{
Expand All @@ -69,7 +84,7 @@ public static Locale getLocale(RealmModel realm, UserModel user, UriInfo uriInfo
if(httpHeaders !=null && httpHeaders.getAcceptableLanguages() != null && !httpHeaders.getAcceptableLanguages().isEmpty()){ if(httpHeaders !=null && httpHeaders.getAcceptableLanguages() != null && !httpHeaders.getAcceptableLanguages().isEmpty()){
for(Locale l : httpHeaders.getAcceptableLanguages()){ for(Locale l : httpHeaders.getAcceptableLanguages()){
String localeString = l.toLanguageTag(); String localeString = l.toLanguageTag();
Locale locale = findLocale(localeString, realm.getSupportedLocales()); Locale locale = findLocale(realm.getSupportedLocales(), localeString);
if(locale != null){ if(locale != null){
return locale; return locale;
}else{ }else{
Expand All @@ -94,20 +109,27 @@ public static void updateLocaleCookie(Response.ResponseBuilder builder, Locale l
builder.cookie(new NewCookie(LocaleHelper.LOCALE_COOKIE, locale.toLanguageTag(), path, null, null, 31536000, secure)); builder.cookie(new NewCookie(LocaleHelper.LOCALE_COOKIE, locale.toLanguageTag(), path, null, null, 31536000, secure));
} }


public static Locale findLocale(String localeString, Set<String> supportedLocales) {
Locale result = null;
Locale search = Locale.forLanguageTag(localeString); public static Locale findLocale(Set<String> supportedLocales, String ... localeStrings) {
for(String languageTag : supportedLocales) { for(String localeString : localeStrings){
Locale locale = Locale.forLanguageTag(languageTag); Locale result = null;
if(locale.getLanguage().equals(search.getLanguage())){ Locale search = Locale.forLanguageTag(localeString);
if(locale.getCountry().equals("") && result == null){ for(String languageTag : supportedLocales) {
result = locale; Locale locale = Locale.forLanguageTag(languageTag);
} if(locale.getLanguage().equals(search.getLanguage())){
if(locale.getCountry().equals(search.getCountry())){ if(locale.getCountry().equals("") && result == null){
return locale; result = locale;
}
if(locale.getCountry().equals(search.getCountry())){
return locale;
}
} }
} }
if(result != null){
return result;
}
} }
return result; return null;
} }
} }
@@ -0,0 +1,58 @@
package org.keycloak.freemarker.beans;

import org.keycloak.freemarker.LocaleHelper;
import org.keycloak.models.RealmModel;

import javax.ws.rs.core.UriBuilder;
import java.util.LinkedList;
import java.util.List;
import java.util.Properties;

/**
* @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
*/
public class LocaleBean {

private String current;
private List<Locale> supported;

public LocaleBean(RealmModel realm, java.util.Locale current, UriBuilder uriBuilder, Properties messages) {
this.current = messages.getProperty("locale_" + current.toLanguageTag(), current.toLanguageTag());

supported = new LinkedList<>();
for (String l : realm.getSupportedLocales()) {
String label = messages.getProperty("locale_" + l, l);
String url = uriBuilder.replaceQueryParam(LocaleHelper.KC_LOCALE_PARAM, l).build().toString();
supported.add(new Locale(label, url));
}
}

public String getCurrent() {
return current;
}

public List<Locale> getSupported() {
return supported;
}

public static class Locale {

private String label;
private String url;

public Locale(String label, String url) {
this.label = label;
this.url = url;
}

public String getUrl() {
return url;
}

public String getLabel() {
return label;
}

}

}
@@ -0,0 +1,32 @@
package org.keycloak.freemarker.beans;

import freemarker.template.TemplateMethodModelEx;
import freemarker.template.TemplateModelException;

import java.text.MessageFormat;
import java.util.List;
import java.util.Locale;
import java.util.Properties;

/**
* @author <a href="mailto:gerbermichi@me.com">Michael Gerber</a>
*/
public class MessageFormatterMethod implements TemplateMethodModelEx {
private final Properties messages;
private final Locale locale;

public MessageFormatterMethod(Locale locale, Properties messages) {
this.locale = locale;
this.messages = messages;
}

@Override
public Object exec(List list) throws TemplateModelException {
String key = list.get(0).toString();
if (list.size() >= 1) {
return new MessageFormat(messages.getProperty(key,key),locale).format(list.subList(1, list.size()).toArray());
} else {
return null;
}
}
}

This file was deleted.

Expand Up @@ -13,12 +13,23 @@
public class LocaleHelperTest { public class LocaleHelperTest {
@Test @Test
public void findLocaleTest(){ public void findLocaleTest(){
Assert.assertEquals("de", LocaleHelper.findLocale("de", new HashSet<>(Arrays.asList("de","en"))).toLanguageTag()); Assert.assertEquals("de", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","en")), "de").toLanguageTag());
Assert.assertEquals("en", LocaleHelper.findLocale("en", new HashSet<>(Arrays.asList("de","en"))).toLanguageTag()); Assert.assertEquals("en", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","en")), "en").toLanguageTag());
Assert.assertEquals("de", LocaleHelper.findLocale("de-CH", new HashSet<>(Arrays.asList("de","en"))).toLanguageTag()); Assert.assertEquals("de", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","en")), "de-CH").toLanguageTag());
Assert.assertEquals("de-CH", LocaleHelper.findLocale("de-CH", new HashSet<>(Arrays.asList("de","de-CH","de-DE"))).toLanguageTag()); Assert.assertEquals("de-CH", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","de-CH","de-DE")), "de-CH").toLanguageTag());
Assert.assertEquals("de-DE", LocaleHelper.findLocale("de-DE", new HashSet<>(Arrays.asList("de","de-CH","de-DE"))).toLanguageTag()); Assert.assertEquals("de-DE", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","de-CH","de-DE")), "de-DE").toLanguageTag());
Assert.assertEquals("de", LocaleHelper.findLocale("de", new HashSet<>(Arrays.asList("de","de-CH","de-DE"))).toLanguageTag()); Assert.assertEquals("de", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","de-CH","de-DE")), "de").toLanguageTag());
Assert.assertNull(LocaleHelper.findLocale("de", new HashSet<>(Arrays.asList("de-CH","de-DE")))); Assert.assertNull(LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de-CH","de-DE")), "de"));
}

@Test
public void findLocalesTest(){
Assert.assertEquals("de", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","en")), "de en".split(" ")).toLanguageTag());
Assert.assertEquals("en", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","en")), "en de".split(" ")).toLanguageTag());
Assert.assertEquals("de", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","en")), "de-CH en".split(" ")).toLanguageTag());
Assert.assertEquals("de-CH", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","de-CH","de-DE")), "de-CH de".split(" ")).toLanguageTag());
Assert.assertEquals("de-DE", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","de-CH","de-DE")), "de-DE de-CH de".split(" ")).toLanguageTag());
Assert.assertEquals("de", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","de-CH","de-DE")), "en fr de".split(" ")).toLanguageTag());
Assert.assertNull(LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de-CH","de-DE")), "de en fr".split(" ")));
} }
} }

0 comments on commit d8193f0

Please sign in to comment.