keycloak-aplcache

Merge pull request #1064 from gerbermichi/i18n [KEYCLOAK-301]

3/20/2015 1:34:20 AM

Changes

forms/common-freemarker/src/main/java/org/keycloak/freemarker/beans/TextFormatterBean.java 19(+0 -19)

Details

diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java
index 881606f..8497fc6 100755
--- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java
+++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/FreeMarkerAccountProvider.java
@@ -6,7 +6,7 @@ import org.keycloak.account.AccountProvider;
 import org.keycloak.account.freemarker.model.*;
 import org.keycloak.events.Event;
 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.RealmModel;
 import org.keycloak.models.UserModel;
@@ -18,6 +18,7 @@ import java.io.IOException;
 import java.net.URI;
 import java.text.MessageFormat;
 import java.util.*;
+import org.keycloak.freemarker.beans.LocaleBean;
 
 /**
  * @author <a href="mailto:sthorger@redhat.com">Stian Thorgersen</a>
@@ -86,14 +87,10 @@ public class FreeMarkerAccountProvider implements AccountProvider {
         }
 
         Locale locale = LocaleHelper.getLocale(realm, user, uriInfo, headers);
-        if(locale != null){
-            attributes.put("locale", locale);
-            attributes.put("formatter", new TextFormatterBean(locale));
-        }
         Properties messages;
         try {
             messages = theme.getMessages(locale);
-            attributes.put("rb", messages);
+            attributes.put("msg", new MessageFormatterMethod(locale, messages));
         } catch (IOException e) {
             logger.warn("Failed to load messages", e);
             messages = new Properties();
@@ -113,7 +110,7 @@ public class FreeMarkerAccountProvider implements AccountProvider {
         if (message != null) {
             String formattedMessage;
             if(messages.containsKey(message)){
-                formattedMessage = new MessageFormat(messages.getProperty(message).replace("'","''"),locale).format(parameters);
+                formattedMessage = new MessageFormat(messages.getProperty(message),locale).format(parameters);
             }else{
                 formattedMessage = message;
             }
@@ -130,6 +127,16 @@ public class FreeMarkerAccountProvider implements AccountProvider {
 
         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));
 
         switch (page) {
diff --git a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/UrlBean.java b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/UrlBean.java
index a357aad..de7e432 100755
--- a/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/UrlBean.java
+++ b/forms/account-freemarker/src/main/java/org/keycloak/account/freemarker/model/UrlBean.java
@@ -59,10 +59,6 @@ public class UrlBean {
         return Urls.accountSessionsLogoutPage(baseQueryURI, realm, stateChecker).toString();
     }
 
-    public String getLocaleCookiePath(){
-        return Urls.localeCookiePath(baseURI, realm);
-    }
-
     public String getTotpRemoveUrl() {
         return Urls.accountTotpRemove(baseQueryURI, realm, stateChecker).toString();
     }
diff --git a/forms/common-freemarker/src/main/java/org/keycloak/freemarker/beans/LocaleBean.java b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/beans/LocaleBean.java
new file mode 100644
index 0000000..91ae108
--- /dev/null
+++ b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/beans/LocaleBean.java
@@ -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;
+        }
+
+    }
+
+}
diff --git a/forms/common-freemarker/src/main/java/org/keycloak/freemarker/beans/MessageFormatterMethod.java b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/beans/MessageFormatterMethod.java
new file mode 100644
index 0000000..37bfc56
--- /dev/null
+++ b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/beans/MessageFormatterMethod.java
@@ -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;
+        }
+    }
+}
diff --git a/forms/common-freemarker/src/main/java/org/keycloak/freemarker/LocaleHelper.java b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/LocaleHelper.java
index e153fcf..0029dfd 100644
--- a/forms/common-freemarker/src/main/java/org/keycloak/freemarker/LocaleHelper.java
+++ b/forms/common-freemarker/src/main/java/org/keycloak/freemarker/LocaleHelper.java
@@ -15,7 +15,8 @@ import java.util.*;
  */
 public class LocaleHelper {
     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);
 
@@ -28,12 +29,26 @@ public class LocaleHelper {
             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
         if(httpHeaders != null && httpHeaders.getCookies().containsKey(LOCALE_COOKIE)){
             String localeString = httpHeaders.getCookies().get(LOCALE_COOKIE).getValue();
-            Locale locale =  findLocale(localeString, realm.getSupportedLocales());
+            Locale locale =  findLocale(realm.getSupportedLocales(), localeString);
             if(locale != null){
-                if(user != null){
+                if(user != null && user.getAttribute(UserModel.LOCALE) == null){
                     user.setAttribute(UserModel.LOCALE, locale.toLanguageTag());
                 }
                 return locale;
@@ -45,7 +60,7 @@ public class LocaleHelper {
         //2. User profile
         if(user != null && user.getAttributes().containsKey(UserModel.LOCALE)){
             String localeString = user.getAttribute(UserModel.LOCALE);
-            Locale locale =  findLocale(localeString, realm.getSupportedLocales());
+            Locale locale =  findLocale(realm.getSupportedLocales(), localeString);
             if(locale != null){
 
                 return locale;
@@ -55,9 +70,9 @@ public class LocaleHelper {
         }
 
         //3. ui_locales query parameter
-        if(uriInfo != null && uriInfo.getQueryParameters().containsKey(LOCALE_PARAM)){
-            String localeString = uriInfo.getQueryParameters().getFirst(LOCALE_PARAM);
-            Locale locale =  findLocale(localeString, realm.getSupportedLocales());
+        if(uriInfo != null && uriInfo.getQueryParameters().containsKey(UI_LOCALES_PARAM)){
+            String localeString = uriInfo.getQueryParameters().getFirst(UI_LOCALES_PARAM);
+            Locale locale =  findLocale(realm.getSupportedLocales(), localeString.split(" "));
             if(locale != null){
                 return locale;
             }else{
@@ -69,7 +84,7 @@ public class LocaleHelper {
         if(httpHeaders !=null && httpHeaders.getAcceptableLanguages() != null && !httpHeaders.getAcceptableLanguages().isEmpty()){
             for(Locale l : httpHeaders.getAcceptableLanguages()){
                 String localeString = l.toLanguageTag();
-                Locale locale =  findLocale(localeString, realm.getSupportedLocales());
+                Locale locale =  findLocale(realm.getSupportedLocales(), localeString);
                 if(locale != null){
                     return locale;
                 }else{
@@ -94,20 +109,27 @@ public class LocaleHelper {
         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);
-        for(String languageTag : supportedLocales) {
-            Locale locale = Locale.forLanguageTag(languageTag);
-            if(locale.getLanguage().equals(search.getLanguage())){
-                if(locale.getCountry().equals("") && result == null){
-                    result = locale;
-                }
-                if(locale.getCountry().equals(search.getCountry())){
-                    return locale;
+
+
+    public static Locale findLocale(Set<String> supportedLocales, String ... localeStrings) {
+        for(String localeString : localeStrings){
+            Locale result = null;
+            Locale search = Locale.forLanguageTag(localeString);
+            for(String languageTag : supportedLocales) {
+                Locale locale = Locale.forLanguageTag(languageTag);
+                if(locale.getLanguage().equals(search.getLanguage())){
+                    if(locale.getCountry().equals("") && result == null){
+                        result = locale;
+                    }
+                    if(locale.getCountry().equals(search.getCountry())){
+                        return locale;
+                    }
                 }
             }
+            if(result != null){
+                return result;
+            }
         }
-        return result;
+        return null;
     }
 }
diff --git a/forms/common-freemarker/src/test/java/org/keycloak/freemarke/LocaleHelperTest.java b/forms/common-freemarker/src/test/java/org/keycloak/freemarke/LocaleHelperTest.java
index 01bb998..47fa9e4 100644
--- a/forms/common-freemarker/src/test/java/org/keycloak/freemarke/LocaleHelperTest.java
+++ b/forms/common-freemarker/src/test/java/org/keycloak/freemarke/LocaleHelperTest.java
@@ -13,12 +13,23 @@ import java.util.HashSet;
 public class LocaleHelperTest {
     @Test
     public void findLocaleTest(){
-        Assert.assertEquals("de", LocaleHelper.findLocale("de", new HashSet<>(Arrays.asList("de","en"))).toLanguageTag());
-        Assert.assertEquals("en", LocaleHelper.findLocale("en", new HashSet<>(Arrays.asList("de","en"))).toLanguageTag());
-        Assert.assertEquals("de", LocaleHelper.findLocale("de-CH", new HashSet<>(Arrays.asList("de","en"))).toLanguageTag());
-        Assert.assertEquals("de-CH", LocaleHelper.findLocale("de-CH", new HashSet<>(Arrays.asList("de","de-CH","de-DE"))).toLanguageTag());
-        Assert.assertEquals("de-DE", LocaleHelper.findLocale("de-DE", new HashSet<>(Arrays.asList("de","de-CH","de-DE"))).toLanguageTag());
-        Assert.assertEquals("de", LocaleHelper.findLocale("de", new HashSet<>(Arrays.asList("de","de-CH","de-DE"))).toLanguageTag());
-        Assert.assertNull(LocaleHelper.findLocale("de", new HashSet<>(Arrays.asList("de-CH","de-DE"))));
+        Assert.assertEquals("de", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","en")), "de").toLanguageTag());
+        Assert.assertEquals("en", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","en")), "en").toLanguageTag());
+        Assert.assertEquals("de", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","en")), "de-CH").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(new HashSet<>(Arrays.asList("de","de-CH","de-DE")), "de-DE").toLanguageTag());
+        Assert.assertEquals("de", LocaleHelper.findLocale(new HashSet<>(Arrays.asList("de","de-CH","de-DE")), "de").toLanguageTag());
+        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(" ")));
     }
 }
diff --git a/forms/common-themes/src/main/resources/theme/account/base/account.ftl b/forms/common-themes/src/main/resources/theme/account/base/account.ftl
index 89e73b8..34487d7 100755
--- a/forms/common-themes/src/main/resources/theme/account/base/account.ftl
+++ b/forms/common-themes/src/main/resources/theme/account/base/account.ftl
@@ -16,7 +16,7 @@
 
         <div class="form-group">
             <div class="col-sm-2 col-md-2">
-                <label for="username" class="control-label">${rb.username}</label>
+                <label for="username" class="control-label">${msg("username")}</label>
             </div>
 
             <div class="col-sm-10 col-md-10">
@@ -26,7 +26,7 @@
 
         <div class="form-group">
             <div class="col-sm-2 col-md-2">
-                <label for="email" class="control-label">${rb.email}</label> <span class="required">*</span>
+            <label for="email" class="control-label">${msg("email")}</label> <span class="required">*</span>
             </div>
 
             <div class="col-sm-10 col-md-10">
@@ -37,7 +37,7 @@
 
         <div class="form-group">
             <div class="col-sm-2 col-md-2">
-                <label for="lastName" class="control-label">${rb.lastName}</label> <span class="required">*</span>
+                <label for="lastName" class="control-label">${msg("lastName")}</label> <span class="required">*</span>
             </div>
 
             <div class="col-sm-10 col-md-10">
@@ -47,7 +47,7 @@
 
         <div class="form-group">
             <div class="col-sm-2 col-md-2">
-                <label for="firstName" class="control-label">${rb.firstName}</label> <span class="required">*</span>
+                <label for="firstName" class="control-label">${msg("firstName")}</label> <span class="required">*</span>
             </div>
 
             <div class="col-sm-10 col-md-10">
@@ -57,7 +57,7 @@
 
         <div class="form-group">
             <div class="col-sm-2 col-md-2">
-                <label for="user.attributes.street" class="control-label">${rb.street}</label>
+                <label for="user.attributes.street" class="control-label">${msg("street")}</label>
             </div>
 
             <div class="col-sm-10 col-md-10">
@@ -66,7 +66,7 @@
         </div>
         <div class="form-group">
             <div class="col-sm-2 col-md-2">
-                <label for="user.attributes.locality" class="control-label">${rb.locality}</label>
+                <label for="user.attributes.locality" class="control-label">${msg("locality")}</label>
             </div>
 
             <div class="col-sm-10 col-md-10">
@@ -75,7 +75,7 @@
         </div>
         <div class="form-group">
             <div class="col-sm-2 col-md-2">
-                <label for="user.attributes.region" class="control-label">${rb.region}</label>
+                <label for="user.attributes.region" class="control-label">${msg("region")}</label>
             </div>
 
             <div class="col-sm-10 col-md-10">
@@ -84,7 +84,7 @@
         </div>
         <div class="form-group">
             <div class="col-sm-2 col-md-2">
-                <label for="user.attributes.postal_code" class="control-label">${rb.postal_code}</label>
+                <label for="user.attributes.postal_code" class="control-label">${msg("postal_code")}</label>
             </div>
 
             <div class="col-sm-10 col-md-10">
@@ -93,7 +93,7 @@
         </div>
         <div class="form-group">
             <div class="col-sm-2 col-md-2">
-                <label for="user.attributes.country" class="control-label">${rb.country}</label>
+                <label for="user.attributes.country" class="control-label">${msg("country")}</label>
             </div>
 
             <div class="col-sm-10 col-md-10">
diff --git a/forms/common-themes/src/main/resources/theme/account/base/messages/messages_en.properties b/forms/common-themes/src/main/resources/theme/account/base/messages/messages_en.properties
index 41072c8..7cf27a4 100755
--- a/forms/common-themes/src/main/resources/theme/account/base/messages/messages_en.properties
+++ b/forms/common-themes/src/main/resources/theme/account/base/messages/messages_en.properties
@@ -17,15 +17,15 @@ invalidEmailMessage=Invalid email address.
 missingLastNameMessage=Please specify last name.
 missingEmailMessage=Please specify email.
 missingPasswordMessage=Please specify password.
-notMatchPasswordMessage=Passwords don't match.
+notMatchPasswordMessage=Passwords don''t match.
 
 missingTotpMessage=Please specify authenticator code
 invalidPasswordExistingMessage=Invalid existing password
-invalidPasswordConfirmMessage=Password confirmation doesn't match
+invalidPasswordConfirmMessage=Password confirmation doesn''t match
 invalidTotpMessage=Invalid authenticator code
 
-readOnlyUserMessage=You can't update your account as it is read only
-readOnlyPasswordMessage=You can't update your password as your account is read only
+readOnlyUserMessage=You can''t update your account as it is read only
+readOnlyPasswordMessage=You can''t update your password as your account is read only
 
 successTotpMessage=Mobile authenticator configured.
 successTotpRemovedMessage=Mobile authenticator removed.
@@ -37,7 +37,7 @@ missingIdentityProviderMessage=Identity provider not specified
 invalidFederatedIdentityActionMessage=Invalid or missing action
 identityProviderNotFoundMessage=Specified identity provider not found
 federatedIdentityLinkNotActiveMessage=This identity is not active anymore
-federatedIdentityRemovingLastProviderMessage=You can't remove last federated identity as you don't have password
+federatedIdentityRemovingLastProviderMessage=You can''t remove last federated identity as you don''t have password
 identityProviderRedirectErrorMessage=Failed to redirect to identity provider
 identityProviderRemovedMessage=Identity provider removed successfully
 
diff --git a/forms/common-themes/src/main/resources/theme/account/base/password.ftl b/forms/common-themes/src/main/resources/theme/account/base/password.ftl
index af68d3b..b0a3813 100755
--- a/forms/common-themes/src/main/resources/theme/account/base/password.ftl
+++ b/forms/common-themes/src/main/resources/theme/account/base/password.ftl
@@ -14,7 +14,7 @@
         <#if password.passwordSet>
             <div class="form-group">
                 <div class="col-sm-2 col-md-2">
-                    <label for="password" class="control-label">${rb.password}</label>
+                    <label for="password" class="control-label">${msg("password")}</label>
                 </div>
 
                 <div class="col-sm-10 col-md-10">
@@ -27,7 +27,7 @@
 
         <div class="form-group">
             <div class="col-sm-2 col-md-2">
-                <label for="password-new" class="control-label">${rb.passwordNew}</label>
+                <label for="password-new" class="control-label">${msg("passwordNew")}</label>
             </div>
 
             <div class="col-sm-10 col-md-10">
@@ -37,7 +37,7 @@
 
         <div class="form-group">
             <div class="col-sm-2 col-md-2">
-                <label for="password-confirm" class="control-label" class="two-lines">${rb.passwordConfirm}</label>
+                <label for="password-confirm" class="control-label" class="two-lines">${msg("passwordConfirm")}</label>
             </div>
 
             <div class="col-sm-10 col-md-10">
diff --git a/forms/common-themes/src/main/resources/theme/account/base/sessions.ftl b/forms/common-themes/src/main/resources/theme/account/base/sessions.ftl
index 435f188..3010743 100755
--- a/forms/common-themes/src/main/resources/theme/account/base/sessions.ftl
+++ b/forms/common-themes/src/main/resources/theme/account/base/sessions.ftl
@@ -42,6 +42,6 @@
 
     </table>
 
-    <a id="logout-all-sessions" href="${url.sessionsLogoutUrl}">${rb.doLogOutAllSessions}</a>
+    <a id="logout-all-sessions" href="${url.sessionsLogoutUrl}">${msg("doLogOutAllSessions")}</a>
 
 </@layout.mainLayout>
diff --git a/forms/common-themes/src/main/resources/theme/account/base/template.ftl b/forms/common-themes/src/main/resources/theme/account/base/template.ftl
index 0d862c3..adfd52c 100644
--- a/forms/common-themes/src/main/resources/theme/account/base/template.ftl
+++ b/forms/common-themes/src/main/resources/theme/account/base/template.ftl
@@ -15,19 +15,6 @@
             <script type="text/javascript" src="${url.resourcesPath}/${script}"></script>
         </#list>
     </#if>
-    <#if realm.internationalizationEnabled>
-        <script type="text/javascript">
-            window.onload = function () {
-                var select = document.querySelector(".kc-locale-select");
-                select.onchange = function (event) {
-                    document.cookie = "KEYCLOAK_LOCALE=" + select.value+"; path=${url.localeCookiePath}";
-                    setTimeout(function () {
-                        window.location.reload();
-                    }, 0);
-                }
-            }
-        </script>
-    </#if>
 </head>
 <body class="admin-console user ${bodyClass}">
 
@@ -43,11 +30,14 @@
                     <ul class="nav navbar-nav navbar-utility">
                         <#if realm.internationalizationEnabled>
                             <li>
-                                <select class="kc-locale-select">
-                                    <#list realm.supportedLocales as l>
-                                        <option value="${l}" <#if locale.toLanguageTag()==l>selected="selected"</#if>>${rb["locale_" + l]}</option>
-                                    </#list>
-                                </select>
+                                <div class="kc-dropdown">
+                                    <a href="#">${locale.current}</a>
+                                    <ul>
+                                        <#list locale.supported as l>
+                                            <li class="kc-dropdown-item"><a href="${l.url}">${l.label}</a></li>
+                                        </#list>
+                                    </ul>
+                                </div>
                             <li>
                         </#if>
                         <#if referrer?has_content && referrer.url?has_content><li><a href="${referrer.url}" id="referrer">Back to ${referrer.name}</a></li></#if>
diff --git a/forms/common-themes/src/main/resources/theme/account/base/totp.ftl b/forms/common-themes/src/main/resources/theme/account/base/totp.ftl
index d480337..45e7829 100755
--- a/forms/common-themes/src/main/resources/theme/account/base/totp.ftl
+++ b/forms/common-themes/src/main/resources/theme/account/base/totp.ftl
@@ -46,7 +46,7 @@
             <input type="hidden" id="stateChecker" name="stateChecker" value="${stateChecker}">
             <div class="form-group">
                 <div class="col-sm-2 col-md-2">
-                    <label for="totp" class="control-label">${rb.authenticatorCode}</label>
+                    <label for="totp" class="control-label">${msg("authenticatorCode")}</label>
                 </div>
 
                 <div class="col-sm-10 col-md-10">
diff --git a/forms/common-themes/src/main/resources/theme/account/patternfly/resources/css/account.css b/forms/common-themes/src/main/resources/theme/account/patternfly/resources/css/account.css
index 07762b0..a18d0f2 100644
--- a/forms/common-themes/src/main/resources/theme/account/patternfly/resources/css/account.css
+++ b/forms/common-themes/src/main/resources/theme/account/patternfly/resources/css/account.css
@@ -220,4 +220,55 @@ ol li span {
 hr + .form-horizontal {
     border: none;
     padding-top: 0;
+}
+
+.kc-dropdown{
+    position: relative;
+}
+.kc-dropdown > a{
+    display:block;
+    padding: 11px 10px 12px;
+    line-height: 12px;
+    font-size: 12px;
+    color: #fff !important;
+    text-decoration: none;
+}
+.kc-dropdown > a::after{
+    content: "\2c5";
+    margin-left: 4px;
+}
+.kc-dropdown:hover > a{
+    background-color: rgba(0,0,0,0.2);
+}
+.kc-dropdown ul li a{
+    padding: 1px 11px;
+    font-size: 12px;
+    color: #000 !important;
+    border: 1px solid #fff;
+    text-decoration: none;
+    display:block;
+    line-height: 20px;
+}
+.kc-dropdown ul li a:hover{
+    color: #4d5258;
+    background-color: #d4edfa;
+    border-color: #b3d3e7;
+}
+.kc-dropdown ul{
+    position: absolute;
+    z-index: 2000;
+    list-style:none;
+    display:none;
+    padding: 5px 0px;
+    margin: 0px;
+    background-color: #fff !important;
+    border: 1px solid #b6b6b6;
+    border-radius: 1px;
+    -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
+    box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
+    background-clip: padding-box;
+    min-width: 100px;
+}
+.kc-dropdown:hover ul{
+    display:block;
 }
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/email/keycloak/email-verification.ftl b/forms/common-themes/src/main/resources/theme/email/keycloak/email-verification.ftl
index 301212f..5f2490b 100644
--- a/forms/common-themes/src/main/resources/theme/email/keycloak/email-verification.ftl
+++ b/forms/common-themes/src/main/resources/theme/email/keycloak/email-verification.ftl
@@ -1 +1 @@
-${formatter.format(rb.emailVerificationBody,link, linkExpiration)}
\ No newline at end of file
+${msg("emailVerificationBody",link, linkExpiration)}
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/email/keycloak/event-login_error.ftl b/forms/common-themes/src/main/resources/theme/email/keycloak/event-login_error.ftl
index cd9d247..7835c91 100644
--- a/forms/common-themes/src/main/resources/theme/email/keycloak/event-login_error.ftl
+++ b/forms/common-themes/src/main/resources/theme/email/keycloak/event-login_error.ftl
@@ -1 +1 @@
-${formatter.format(rb.eventLoginErrorBody,event.date,event.ipAddress)}
\ No newline at end of file
+${msg("eventLoginErrorBody",event.date,event.ipAddress)}
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/email/keycloak/event-remove_totp.ftl b/forms/common-themes/src/main/resources/theme/email/keycloak/event-remove_totp.ftl
index 37ae2f7..8930b64 100644
--- a/forms/common-themes/src/main/resources/theme/email/keycloak/event-remove_totp.ftl
+++ b/forms/common-themes/src/main/resources/theme/email/keycloak/event-remove_totp.ftl
@@ -1 +1 @@
-${formatter.format(rb.eventRemoveTotpBody,event.date, event.ipAddress)}
\ No newline at end of file
+${msg("eventRemoveTotpBody",event.date, event.ipAddress)}
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/email/keycloak/event-update_password.ftl b/forms/common-themes/src/main/resources/theme/email/keycloak/event-update_password.ftl
index 2c88214..754daac 100644
--- a/forms/common-themes/src/main/resources/theme/email/keycloak/event-update_password.ftl
+++ b/forms/common-themes/src/main/resources/theme/email/keycloak/event-update_password.ftl
@@ -1 +1 @@
-${formatter.format(rb.eventUpdatePasswordBody,event.date, event.ipAddress)}
\ No newline at end of file
+${msg("eventUpdatePasswordBody",event.date, event.ipAddress)}
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/email/keycloak/event-update_totp.ftl b/forms/common-themes/src/main/resources/theme/email/keycloak/event-update_totp.ftl
index b34a898..3a7b0f7 100644
--- a/forms/common-themes/src/main/resources/theme/email/keycloak/event-update_totp.ftl
+++ b/forms/common-themes/src/main/resources/theme/email/keycloak/event-update_totp.ftl
@@ -1 +1 @@
-${formatter.format(rb.eventUpdateTotpBody,event.date, event.ipAddress)}
\ No newline at end of file
+${msg("eventUpdateTotpBody",event.date, event.ipAddress)}
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/email/keycloak/messages/messages_en.properties b/forms/common-themes/src/main/resources/theme/email/keycloak/messages/messages_en.properties
index e64a00f..c23daf6 100755
--- a/forms/common-themes/src/main/resources/theme/email/keycloak/messages/messages_en.properties
+++ b/forms/common-themes/src/main/resources/theme/email/keycloak/messages/messages_en.properties
@@ -1,7 +1,7 @@
 emailVerificationSubject=Verify email
-emailVerificationBody=Someone has created a Keycloak account with this email address. If this was you, click the link below to verify your email address\n\n{0}\n\nThis link will expire within {1} minutes.\n\nIf you didn't create this account, just ignore this message.
+emailVerificationBody=Someone has created a Keycloak account with this email address. If this was you, click the link below to verify your email address\n\n{0}\n\nThis link will expire within {1} minutes.\n\nIf you didn''t create this account, just ignore this message.
 passwordResetSubject=Reset password
-passwordResetBody=Someone just requested to change your Keycloak account's password. If this was you, click on the link below to set a new password\n\n{0}\n\nThis link will expire within {1} minutes.\n\nIf you don't want to reset your password, just ignore this message and nothing will be changed.
+passwordResetBody=Someone just requested to change your Keycloak account''s password. If this was you, click on the link below to set a new password\n\n{0}\n\nThis link will expire within {1} minutes.\n\nIf you don''t want to reset your password, just ignore this message and nothing will be changed.
 eventLoginErrorSubject=Login error
 eventLoginErrorBody=A failed login attempt was dettected to your account on {0} from {1}. If this was not you, please contact an admin.
 eventRemoveTotpSubject=Remove TOTP
diff --git a/forms/common-themes/src/main/resources/theme/email/keycloak/password-reset.ftl b/forms/common-themes/src/main/resources/theme/email/keycloak/password-reset.ftl
index 55f0138..d7150d6 100644
--- a/forms/common-themes/src/main/resources/theme/email/keycloak/password-reset.ftl
+++ b/forms/common-themes/src/main/resources/theme/email/keycloak/password-reset.ftl
@@ -1 +1 @@
-${formatter.format(rb.passwordResetBody,link, linkExpiration)}
\ No newline at end of file
+${msg("passwordResetBody",link, linkExpiration)}
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/login/base/error.ftl b/forms/common-themes/src/main/resources/theme/login/base/error.ftl
index 3854ae2..2c8c153 100755
--- a/forms/common-themes/src/main/resources/theme/login/base/error.ftl
+++ b/forms/common-themes/src/main/resources/theme/login/base/error.ftl
@@ -1,9 +1,9 @@
 <#import "template.ftl" as layout>
 <@layout.registrationLayout displayMessage=false; section>
     <#if section = "title">
-        ${rb.errorTitle}
+        ${msg("errorTitle")}
     <#elseif section = "header">
-        ${rb.errorTitleHtml}
+        ${msg("errorTitleHtml")}
     <#elseif section = "form">
         <div id="kc-error-message">
             <p class="instruction">${message.summary}</p>
diff --git a/forms/common-themes/src/main/resources/theme/login/base/info.ftl b/forms/common-themes/src/main/resources/theme/login/base/info.ftl
index d303e5b..f4de855 100755
--- a/forms/common-themes/src/main/resources/theme/login/base/info.ftl
+++ b/forms/common-themes/src/main/resources/theme/login/base/info.ftl
@@ -8,7 +8,7 @@
     <div id="kc-info-message">
         <p class="instruction">${message.summary}</p>
         <#if client.baseUrl??>
-        <p><a href="${client.baseUrl}">${rb.backToApplication}</a></p>
+        <p><a href="${client.baseUrl}">${msg("backToApplication")}</a></p>
         </#if>
     </div>
     </#if>
diff --git a/forms/common-themes/src/main/resources/theme/login/base/login.ftl b/forms/common-themes/src/main/resources/theme/login/base/login.ftl
index 0dea114..e97b35a 100755
--- a/forms/common-themes/src/main/resources/theme/login/base/login.ftl
+++ b/forms/common-themes/src/main/resources/theme/login/base/login.ftl
@@ -2,22 +2,22 @@
 <@layout.registrationLayout displayInfo=social.displayInfo; section>
     <#if section = "title">
         <#if client.application>
-            ${formatter.format(rb.loginTitle,(realm.name!''))}
+            ${msg("loginTitle",(realm.name!''))}
         <#elseif client.oauthClient>
-            ${formatter.format(rb.loginOauthTitle,(realm.name!''))}
+            ${msg("loginOauthTitle",(realm.name!''))}
         </#if>
     <#elseif section = "header">
         <#if client.application>
-            ${formatter.format(rb.loginTitleHtml,(realm.name!''))}
+            ${msg("loginTitleHtml",(realm.name!''))}
         <#elseif client.oauthClient>
-            ${formatter.format(rb.loginOauthTitleHtml,(realm.name!''), (client.clientId!''))}
+            ${msg("loginOauthTitleHtml",(realm.name!''), (client.clientId!''))}
         </#if>
     <#elseif section = "form">
         <#if realm.password>
             <form id="kc-form-login" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
                 <div class="${properties.kcFormGroupClass!}">
                     <div class="${properties.kcLabelWrapperClass!}">
-                        <label for="username" class="${properties.kcLabelClass!}"><#if !realm.registrationEmailAsUsername>${rb.usernameOrEmail}<#else>${rb.email}</#if></label>
+                        <label for="username" class="${properties.kcLabelClass!}"><#if !realm.registrationEmailAsUsername>${msg("usernameOrEmail")}<#else>${msg("email")}</#if></label>
                     </div>
 
                     <div class="${properties.kcInputWrapperClass!}">
@@ -27,7 +27,7 @@
 
                 <div class="${properties.kcFormGroupClass!}">
                     <div class="${properties.kcLabelWrapperClass!}">
-                        <label for="password" class="${properties.kcLabelClass!}">${rb.password}</label>
+                        <label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label>
                     </div>
 
                     <div class="${properties.kcInputWrapperClass!}">
@@ -41,24 +41,24 @@
                             <div class="checkbox">
                                 <label>
                                     <#if login.rememberMe??>
-                                        <input id="rememberMe" name="rememberMe" type="checkbox" tabindex="3" checked> ${rb.rememberMe}
+                                        <input id="rememberMe" name="rememberMe" type="checkbox" tabindex="3" checked> ${msg("rememberMe")}
                                     <#else>
-                                        <input id="rememberMe" name="rememberMe" type="checkbox" tabindex="3"> ${rb.rememberMe}
+                                        <input id="rememberMe" name="rememberMe" type="checkbox" tabindex="3"> ${msg("rememberMe")}
                                     </#if>
                                 </label>
                             </div>
                         </#if>
                         <div class="${properties.kcFormOptionsWrapperClass!}">
                             <#if realm.resetPasswordAllowed>
-                                <span><a href="${url.loginPasswordResetUrl}">${rb.doForgotPassword}</a></span>
+                                <span><a href="${url.loginPasswordResetUrl}">${msg("doForgotPassword")}</a></span>
                             </#if>
                         </div>
                     </div>
 
                     <div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
                         <div class="${properties.kcFormButtonsWrapperClass!}">
-                            <input class="btn btn-primary btn-lg" name="login" id="kc-login" type="submit" value="${rb.doLogIn}"/>
-                            <input class="btn btn-default btn-lg" name="cancel" id="kc-cancel" type="submit" value="${rb.doCancel}"/>
+                            <input class="btn btn-primary btn-lg" name="login" id="kc-login" type="submit" value="${msg("doLogIn")}"/>
+                            <input class="btn btn-default btn-lg" name="cancel" id="kc-cancel" type="submit" value="${msg("doCancel")}"/>
                         </div>
                      </div>
                 </div>
@@ -67,7 +67,7 @@
     <#elseif section = "info" >
         <#if realm.password && realm.registrationAllowed>
             <div id="kc-registration">
-                <span>${rb.noAccount} <a href="${url.registrationUrl}">${rb.doRegister}</a></span>
+                <span>${msg("noAccount")} <a href="${url.registrationUrl}">${msg("doRegister")}</a></span>
             </div>
         </#if>
 
diff --git a/forms/common-themes/src/main/resources/theme/login/base/login-config-totp.ftl b/forms/common-themes/src/main/resources/theme/login/base/login-config-totp.ftl
index c884ad4..94f264a 100755
--- a/forms/common-themes/src/main/resources/theme/login/base/login-config-totp.ftl
+++ b/forms/common-themes/src/main/resources/theme/login/base/login-config-totp.ftl
@@ -1,14 +1,14 @@
 <#import "template.ftl" as layout>
 <@layout.registrationLayout displayInfo=true; section>
     <#if section = "title">
-        ${rb.loginTotpTitle}
+        ${msg("loginTotpTitle")}
     <#elseif section = "header">
-        ${rb.loginTotpTitle}
+        ${msg("loginTotpTitle")}
     <#elseif section = "form">
         <form action="${url.loginUpdateTotpUrl}" class="${properties.kcFormClass!}" id="kc-totp-settings-form" method="post">
             <div class="${properties.kcFormGroupClass!}">
                 <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="otp" class="${properties.kcLabelClass!}">${rb.loginTotpOneTime}</label>
+                    <label for="otp" class="${properties.kcLabelClass!}">${msg("loginTotpOneTime")}</label>
                 </div>
                 <div class="${properties.kcInputWrapperClass!}">
                     <input type="text" id="totp" name="totp" class="${properties.kcInputClass!}" />
@@ -23,22 +23,22 @@
                 </div>
 
                 <div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
-                    <input class="btn btn-primary btn-lg" type="submit" value="${rb.doSubmit}"/>
+                    <input class="btn btn-primary btn-lg" type="submit" value="${msg("doSubmit")}"/>
                 </div>
             </div>
         </form>
     <#elseif section = "info" >
         <ol id="kc-totp-settings">
             <li>
-                <p>${rb.loginTotpStep1}</p>
+                <p>${msg("loginTotpStep1")}</p>
             </li>
             <li>
-                <p>${rb.loginTotpStep2}</p>
+                <p>${msg("loginTotpStep2")}</p>
                 <img src="${totp.totpSecretQrCodeUrl}" alt="Figure: Barcode"><br/>
                 <span class="code">${totp.totpSecretEncoded}</span>
             </li>
             <li>
-                <p>${rb.loginTotpStep3}</p>
+                <p>${msg("loginTotpStep3")}</p>
             </li>
         </ol>
     </#if>
diff --git a/forms/common-themes/src/main/resources/theme/login/base/login-oauth-grant.ftl b/forms/common-themes/src/main/resources/theme/login/base/login-oauth-grant.ftl
index 797b62d..757a715 100755
--- a/forms/common-themes/src/main/resources/theme/login/base/login-oauth-grant.ftl
+++ b/forms/common-themes/src/main/resources/theme/login/base/login-oauth-grant.ftl
@@ -2,12 +2,12 @@
 <#import "template.ftl" as layout>
 <@layout.registrationLayout bodyClass="oauth"; section>
     <#if section = "title">
-        ${rb.oauthGrantTitle}
+        ${msg("oauthGrantTitle")}
     <#elseif section = "header">
-        ${formatter.format(rb.oauthGrantTitleHtml,(realm.name!''), (client.clientId!''))}
+        ${msg("oauthGrantTitleHtml",(realm.name!''), (client.clientId!''))}
     <#elseif section = "form">
         <div id="kc-oauth" class="content-area">
-            <h3>${rb.oauthGrantRequest}</h3>
+            <h3>${msg("oauthGrantRequest")}</h3>
             <ul>
                 <#if oauth.claimsRequested??>
                     <li>
@@ -55,8 +55,8 @@
 
                     <div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
                         <div class="${properties.kcFormButtonsWrapperClass!}">
-                            <input class="btn btn-primary btn-lg" name="accept" id="kc-login" type="submit" value="${rb.doYes}"/>
-                            <input class="btn btn-default btn-lg" name="cancel" id="kc-cancel" type="submit" value="${rb.doNo}"/>
+                            <input class="btn btn-primary btn-lg" name="accept" id="kc-login" type="submit" value="${msg("doYes")}"/>
+                            <input class="btn btn-default btn-lg" name="cancel" id="kc-cancel" type="submit" value="${msg("doNo")}"/>
                         </div>
                     </div>
                 </div>
diff --git a/forms/common-themes/src/main/resources/theme/login/base/login-reset-password.ftl b/forms/common-themes/src/main/resources/theme/login/base/login-reset-password.ftl
index 9fe7a15..ec327c2 100755
--- a/forms/common-themes/src/main/resources/theme/login/base/login-reset-password.ftl
+++ b/forms/common-themes/src/main/resources/theme/login/base/login-reset-password.ftl
@@ -1,14 +1,14 @@
 <#import "template.ftl" as layout>
 <@layout.registrationLayout displayInfo=true; section>
     <#if section = "title">
-        ${rb.emailForgotTitle}
+        ${msg("emailForgotTitle")}
     <#elseif section = "header">
-        ${rb.emailForgotTitle}
+        ${msg("emailForgotTitle")}
     <#elseif section = "form">
         <form id="kc-reset-password-form" class="${properties.kcFormClass!}" action="${url.loginPasswordResetUrl}" method="post">
             <div class="${properties.kcFormGroupClass!}">
                 <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="username" class="${properties.kcLabelClass!}">${rb.usernameOrEmail}</label>
+                    <label for="username" class="${properties.kcLabelClass!}">${msg("usernameOrEmail")}</label>
                 </div>
                 <div class="${properties.kcInputWrapperClass!}">
                     <input type="text" id="username" name="username" class="${properties.kcInputClass!}" />
@@ -18,16 +18,16 @@
             <div class="${properties.kcFormGroupClass!}">
                 <div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
                     <div class="${properties.kcFormOptionsWrapperClass!}">
-                        <span><a href="${url.loginUrl}">${rb.backToLogin}</a></span>
+                        <span><a href="${url.loginUrl}">${msg("backToLogin")}</a></span>
                     </div>
                 </div>
 
                 <div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
-                    <input class="btn btn-primary btn-lg" type="submit" value="${rb.doSubmit}"/>
+                    <input class="btn btn-primary btn-lg" type="submit" value="${msg("doSubmit")}"/>
                 </div>
             </div>
         </form>
     <#elseif section = "info" >
-        ${rb.emailInstruction}
+        ${msg("emailInstruction")}
     </#if>
 </@layout.registrationLayout>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/login/base/login-totp.ftl b/forms/common-themes/src/main/resources/theme/login/base/login-totp.ftl
index 8661fa0..ec07935 100755
--- a/forms/common-themes/src/main/resources/theme/login/base/login-totp.ftl
+++ b/forms/common-themes/src/main/resources/theme/login/base/login-totp.ftl
@@ -1,9 +1,9 @@
 <#import "template.ftl" as layout>
 <@layout.registrationLayout; section>
     <#if section = "title">
-        ${formatter.format(rb.loginTitle,realm.name)}
+        ${msg("loginTitle",realm.name)}
     <#elseif section = "header">
-        ${formatter.format(rb.loginTitleHtml,realm.name)}
+        ${msg("loginTitleHtml",realm.name)}
     <#elseif section = "form">
         <form id="kc-totp-login-form" class="${properties.kcFormClass!}" action="${url.loginAction}" method="post">
             <input id="username" name="username" value="${login.username!''}" type="hidden" />
@@ -11,7 +11,7 @@
 
             <div class="${properties.kcFormGroupClass!}">
                 <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="totp" class="${properties.kcLabelClass!}">${rb.authenticatorCode}</label>
+                    <label for="totp" class="${properties.kcLabelClass!}">${msg("doLogIn")}</label>
                 </div>
 
                 <div class="${properties.kcInputWrapperClass!}">
@@ -27,8 +27,8 @@
 
                 <div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
                     <div class="${properties.kcFormButtonsWrapperClass!}">
-                        <input class="btn btn-primary btn-lg" name="login" id="kc-login" type="submit" value="${rb.doLogIn}"/>
-                        <input class="btn btn-default btn-lg" name="cancel" id="kc-cancel" type="submit" value="${rb.doCancel}"/>
+                        <input class="btn btn-primary btn-lg" name="login" id="kc-login" type="submit" value="${msg("doLogIn")}"/>
+                        <input class="btn btn-default btn-lg" name="cancel" id="kc-cancel" type="submit" value="${msg("doCancel")}"/>
                     </div>
                 </div>
             </div>
diff --git a/forms/common-themes/src/main/resources/theme/login/base/login-update-password.ftl b/forms/common-themes/src/main/resources/theme/login/base/login-update-password.ftl
index bbc69a2..d201616 100755
--- a/forms/common-themes/src/main/resources/theme/login/base/login-update-password.ftl
+++ b/forms/common-themes/src/main/resources/theme/login/base/login-update-password.ftl
@@ -1,14 +1,14 @@
 <#import "template.ftl" as layout>
 <@layout.registrationLayout displayInfo=true; section>
     <#if section = "title">
-        ${rb.updatePasswordTitle}
+        ${msg("updatePasswordTitle")}
     <#elseif section = "header">
-        ${rb.updatePasswordTitle}
+        ${msg("updatePasswordTitle")}
     <#elseif section = "form">
         <form id="kc-passwd-update-form" class="${properties.kcFormClass!}" action="${url.loginUpdatePasswordUrl}" method="post">
             <div class="${properties.kcFormGroupClass!}">
                 <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="password-new" class="${properties.kcLabelClass!}">${rb.passwordNew}</label>
+                    <label for="password-new" class="${properties.kcLabelClass!}">${msg("passwordNew")}</label>
                 </div>
                 <div class="${properties.kcInputWrapperClass!}">
                     <input type="password" id="password-new" name="password-new" class="${properties.kcInputClass!}" autofocus />
@@ -17,7 +17,7 @@
 
             <div class="${properties.kcFormGroupClass!}">
                 <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="password-confirm" class="${properties.kcLabelClass!}">${rb.passwordConfirm}</label>
+                    <label for="password-confirm" class="${properties.kcLabelClass!}">${msg("passwordConfirm")}</label>
                 </div>
                 <div class="${properties.kcInputWrapperClass!}">
                     <input type="password" id="password-confirm" name="password-confirm" class="${properties.kcInputClass!}" />
@@ -31,7 +31,7 @@
                 </div>
 
                 <div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
-                    <input class="btn btn-primary btn-lg" type="submit" value="${rb.doSubmit}"/>
+                    <input class="btn btn-primary btn-lg" type="submit" value="${msg("doSubmit")}"/>
                 </div>
             </div>
         </form>
diff --git a/forms/common-themes/src/main/resources/theme/login/base/login-update-profile.ftl b/forms/common-themes/src/main/resources/theme/login/base/login-update-profile.ftl
index c3e1d9b..56b5cbe 100755
--- a/forms/common-themes/src/main/resources/theme/login/base/login-update-profile.ftl
+++ b/forms/common-themes/src/main/resources/theme/login/base/login-update-profile.ftl
@@ -1,14 +1,14 @@
 <#import "template.ftl" as layout>
 <@layout.registrationLayout; section>
     <#if section = "title">
-        ${rb.loginProfileTitle}
+        ${msg("loginProfileTitle")}
     <#elseif section = "header">
-        ${rb.loginProfileTitle}
+        ${msg("loginProfileTitle")}
     <#elseif section = "form">
         <form id="kc-update-profile-form" class="${properties.kcFormClass!}" action="${url.loginUpdateProfileUrl}" method="post">
             <div class="${properties.kcFormGroupClass!}">
                 <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="email" class="${properties.kcLabelClass!}">${rb.email}</label>
+                    <label for="email" class="${properties.kcLabelClass!}">${msg("email")}</label>
                 </div>
                 <div class="${properties.kcInputWrapperClass!}">
                     <input type="text" id="email" name="email" value="${(user.email!'')?html}" class="${properties.kcInputClass!}" />
@@ -17,7 +17,7 @@
 
             <div class="${properties.kcFormGroupClass!}">
                 <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="firstName" class="${properties.kcLabelClass!}">${rb.firstName}</label>
+                    <label for="firstName" class="${properties.kcLabelClass!}">${msg("firstName")}</label>
                 </div>
                 <div class="${properties.kcInputWrapperClass!}">
                     <input type="text" id="firstName" name="firstName" value="${(user.firstName!'')?html}" class="${properties.kcInputClass!}" />
@@ -26,7 +26,7 @@
 
             <div class="${properties.kcFormGroupClass!}">
                 <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="lastName" class="${properties.kcLabelClass!}">${rb.lastName}</label>
+                    <label for="lastName" class="${properties.kcLabelClass!}">${msg("lastName")}</label>
                 </div>
                 <div class="${properties.kcInputWrapperClass!}">
                     <input type="text" id="lastName" name="lastName" value="${(user.lastName!'')?html}" class="${properties.kcInputClass!}" />
@@ -40,7 +40,7 @@
                 </div>
 
                 <div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
-                    <input class="btn btn-primary btn-lg" type="submit" value="${rb.doSubmit}" />
+                    <input class="btn btn-primary btn-lg" type="submit" value="${msg("doSubmit")}" />
                 </div>
             </div>
         </form>
diff --git a/forms/common-themes/src/main/resources/theme/login/base/login-verify-email.ftl b/forms/common-themes/src/main/resources/theme/login/base/login-verify-email.ftl
index ac48f95..1396351 100755
--- a/forms/common-themes/src/main/resources/theme/login/base/login-verify-email.ftl
+++ b/forms/common-themes/src/main/resources/theme/login/base/login-verify-email.ftl
@@ -1,15 +1,15 @@
 <#import "template.ftl" as layout>
 <@layout.registrationLayout; section>
     <#if section = "title">
-        ${rb.emailVerifyTitle}
+        ${msg("emailVerifyTitle")}
     <#elseif section = "header">
-        ${rb.emailVerifyTitle}
+        ${msg("emailVerifyTitle")}
     <#elseif section = "form">
         <p class="instruction">
-            ${rb.emailVerifyInstruction1}
+            ${msg("emailVerifyInstruction1")}
         </p>
         <p class="instruction">
-            ${rb.emailVerifyInstruction2} <a href="${url.loginEmailVerificationUrl}">${rb.doClickHere}</a> ${rb.emailVerifyInstruction3}
+            ${msg("emailVerifyInstruction2")} <a href="${url.loginEmailVerificationUrl}">${msg("doClickHere")}</a> ${msg("emailVerifyInstruction3")}
         </p>
     </#if>
 </@layout.registrationLayout>
\ No newline at end of file
diff --git a/forms/common-themes/src/main/resources/theme/login/base/messages/messages_en.properties b/forms/common-themes/src/main/resources/theme/login/base/messages/messages_en.properties
index 6f48c59..a33d676 100755
--- a/forms/common-themes/src/main/resources/theme/login/base/messages/messages_en.properties
+++ b/forms/common-themes/src/main/resources/theme/login/base/messages/messages_en.properties
@@ -17,8 +17,8 @@ loginTotpTitle=Mobile Authenticator Setup
 loginProfileTitle=Update Account Information
 oauthGrantTitle=OAuth Grant
 oauthGrantTitleHtml=Temporary access for <strong>{0}</strong> requested by <strong>{1}</strong>.
-errorTitle=We're sorry...
-errorTitleHtml=We're <strong>sorry</strong> ...
+errorTitle=We''re sorry...
+errorTitleHtml=We''re <strong>sorry</strong> ...
 emailVerifyTitle=Email verification
 emailForgotTitle=Forgot Your Password?
 updatePasswordTitle=Update password
@@ -50,7 +50,7 @@ loginTotpOneTime=One-time code
 oauthGrantRequest=Do you grant these access privileges?
 
 emailVerifyInstruction1=An email with instructions to verify your email address has been sent to you.
-emailVerifyInstruction2=Haven't received a verification code in your email?
+emailVerifyInstruction2=Haven''t received a verification code in your email?
 emailVerifyInstruction3=to re-send the email.
 
 backToLogin=&laquo; Back to Login
@@ -69,10 +69,10 @@ missingEmailMessage=Please specify email.
 missingUsernameMessage=Please specify username.
 missingPasswordMessage=Please specify password.
 missingTotpMessage=Please specify authenticator code.
-notMatchPasswordMessage=Passwords don't match.
+notMatchPasswordMessage=Passwords don''t match.
 
 invalidPasswordExistingMessage=Invalid existing password.
-invalidPasswordConfirmMessage=Password confirmation doesn't match.
+invalidPasswordConfirmMessage=Password confirmation doesn''t match.
 invalidTotpMessage=Invalid authenticator code.
 
 usernameExistsMessage=Username already exists.
diff --git a/forms/common-themes/src/main/resources/theme/login/base/register.ftl b/forms/common-themes/src/main/resources/theme/login/base/register.ftl
index 8dcdcea..930a07d 100755
--- a/forms/common-themes/src/main/resources/theme/login/base/register.ftl
+++ b/forms/common-themes/src/main/resources/theme/login/base/register.ftl
@@ -1,15 +1,15 @@
 <#import "template.ftl" as layout>
 <@layout.registrationLayout; section>
     <#if section = "title">
-        ${formatter.format(rb.registerWithTitle,(realm.name!''))}
+        ${msg("registerWithTitle",(realm.name!''))}
     <#elseif section = "header">
-         ${formatter.format(rb.registerWithTitleHtml,(realm.name!''))}
+         ${msg("registerWithTitleHtml",(realm.name!''))}
     <#elseif section = "form">
         <form id="kc-register-form" class="${properties.kcFormClass!}" action="${url.registrationAction}" method="post">
           <#if !realm.registrationEmailAsUsername>
             <div class="${properties.kcFormGroupClass!}">
                 <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="username" class="${properties.kcLabelClass!}">${rb.username}</label>
+                    <label for="username" class="${properties.kcLabelClass!}">${msg("username")}</label>
                 </div>
                 <div class="${properties.kcInputWrapperClass!}">
                     <input type="text" id="username" class="${properties.kcInputClass!}" name="username" value="${(register.formData.username!'')?html}" />
@@ -18,7 +18,7 @@
           </#if>
             <div class="${properties.kcFormGroupClass!}">
                 <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="firstName" class="${properties.kcLabelClass!}">${rb.firstName}</label>
+                    <label for="firstName" class="${properties.kcLabelClass!}">${msg("firstName")}</label>
                 </div>
                 <div class="${properties.kcInputWrapperClass!}">
                     <input type="text" id="firstName" class="${properties.kcInputClass!}" name="firstName" value="${(register.formData.firstName!'')?html}" />
@@ -27,7 +27,7 @@
 
             <div class="${properties.kcFormGroupClass!}">
                 <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="lastName" class="${properties.kcLabelClass!}">${rb.lastName}</label>
+                    <label for="lastName" class="${properties.kcLabelClass!}">${msg("lastName")}</label>
                 </div>
                 <div class="${properties.kcInputWrapperClass!}">
                     <input type="text" id="lastName" class="${properties.kcInputClass!}" name="lastName" value="${(register.formData.lastName!'')?html}" />
@@ -36,7 +36,7 @@
 
             <div class="${properties.kcFormGroupClass!}">
                 <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="email" class="${properties.kcLabelClass!}">${rb.email}</label>
+                    <label for="email" class="${properties.kcLabelClass!}">${msg("email")}</label>
                 </div>
                 <div class="${properties.kcInputWrapperClass!}">
                     <input type="text" id="email" class="${properties.kcInputClass!}" name="email" value="${(register.formData.email!'')?html}" />
@@ -45,7 +45,7 @@
 
             <div class="${properties.kcFormGroupClass!}">
                 <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="password" class="${properties.kcLabelClass!}">${rb.password}</label>
+                    <label for="password" class="${properties.kcLabelClass!}">${msg("password")}</label>
                 </div>
                 <div class="${properties.kcInputWrapperClass!}">
                     <input type="password" id="password" class="${properties.kcInputClass!}" name="password" />
@@ -54,7 +54,7 @@
 
             <div class="${properties.kcFormGroupClass!}">
                 <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="password-confirm" class="${properties.kcLabelClass!}">${rb.passwordConfirm}</label>
+                    <label for="password-confirm" class="${properties.kcLabelClass!}">${msg("passwordConfirm")}</label>
                 </div>
                 <div class="${properties.kcInputWrapperClass!}">
                     <input type="password" id="password-confirm" class="${properties.kcInputClass!}" name="password-confirm" />
@@ -63,7 +63,7 @@
 
             <div class="form-group">
                 <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="user.attributes.street" class="${properties.kcLabelClass!}">${rb.street}</label>
+                    <label for="user.attributes.street" class="${properties.kcLabelClass!}">${msg("street")}</label>
                 </div>
 
                 <div class="${properties.kcInputWrapperClass!}">
@@ -72,7 +72,7 @@
             </div>
             <div class="form-group">
                 <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="user.attributes.locality" class="${properties.kcLabelClass!}">${rb.locality}</label>
+                    <label for="user.attributes.locality" class="${properties.kcLabelClass!}">${msg("locality")}</label>
                 </div>
 
                 <div class="${properties.kcInputWrapperClass!}">
@@ -81,7 +81,7 @@
             </div>
             <div class="form-group">
                 <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="user.attributes.region" class="${properties.kcLabelClass!}">${rb.region}</label>
+                    <label for="user.attributes.region" class="${properties.kcLabelClass!}">${msg("region")}</label>
                 </div>
 
                 <div class="${properties.kcInputWrapperClass!}">
@@ -90,7 +90,7 @@
             </div>
             <div class="form-group">
                 <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="user.attributes.postal_code" class="${properties.kcLabelClass!}">${rb.postal_code}</label>
+                    <label for="user.attributes.postal_code" class="${properties.kcLabelClass!}">${msg("postal_code")}</label>
                 </div>
 
                 <div class="${properties.kcInputWrapperClass!}">
@@ -99,7 +99,7 @@
             </div>
             <div class="form-group">
                 <div class="${properties.kcLabelWrapperClass!}">
-                    <label for="user.attributes.country" class="${properties.kcLabelClass!}">${rb.country}</label>
+                    <label for="user.attributes.country" class="${properties.kcLabelClass!}">${msg("country")}</label>
                 </div>
 
                 <div class="${properties.kcInputWrapperClass!}">
@@ -111,12 +111,12 @@
             <div class="${properties.kcFormGroupClass!}">
                 <div id="kc-form-options" class="${properties.kcFormOptionsClass!}">
                     <div class="${properties.kcFormOptionsWrapperClass!}">
-                        <span><a href="${url.loginUrl}">${rb.backToLogin}</a></span>
+                        <span><a href="${url.loginUrl}">${msg("backToLogin")}</a></span>
                     </div>
                 </div>
 
                 <div id="kc-form-buttons" class="${properties.kcFormButtonsClass!}">
-                    <input class="btn btn-primary btn-lg" type="submit" value="${rb.doRegister}"/>
+                    <input class="btn btn-primary btn-lg" type="submit" value="${msg("doRegister")}"/>
                 </div>
             </div>
         </form>
diff --git a/forms/common-themes/src/main/resources/theme/login/base/template.ftl b/forms/common-themes/src/main/resources/theme/login/base/template.ftl
index c09271d..907cfd5 100644
--- a/forms/common-themes/src/main/resources/theme/login/base/template.ftl
+++ b/forms/common-themes/src/main/resources/theme/login/base/template.ftl
@@ -21,19 +21,6 @@
             <script src="${url.resourcesPath}/${script}" type="text/javascript"></script>
         </#list>
     </#if>
-    <#if realm.internationalizationEnabled>
-        <script type="text/javascript">
-            window.onload = function () {
-                var select = document.querySelector(".kc-locale-select");
-                select.onchange = function (event) {
-                    document.cookie = "KEYCLOAK_LOCALE=" + select.value+"; path=${url.localeCookiePath}";
-                    setTimeout(function () {
-                        window.location.reload();
-                    }, 0);
-                }
-            }
-        </script>
-    </#if>
 </head>
 
 <body class="${properties.kcBodyClass!}">
@@ -44,15 +31,6 @@
 
             <div id="kc-header" class="${properties.kcHeaderClass!}">
                 <div id="kc-header-wrapper" class="${properties.kcHeaderWrapperClass!}"><#nested "header"></div>
-                <#if realm.internationalizationEnabled>
-                    <div id="kc-locale-wrapper" class="${properties.kcLocaleWrapperClass!}">
-                        <select class="kc-locale-select">
-                            <#list realm.supportedLocales as l>
-                                <option value="${l}" <#if locale.toLanguageTag()==l>selected="selected"</#if>>${rb["locale_" + l]}</option>
-                            </#list>
-                        </select>
-                    </div>
-                </#if>
             </div>
 
             <#if displayMessage && message?has_content>
@@ -61,6 +39,25 @@
                         <span class="kc-feedback-text">${message.summary}</span>
                     </div>
                 </div>
+            <#else>
+                <div id="kc-feedback-placeholder" class="${properties.kcFeedBackPlaceholderClass!}">
+                    <div id="kc-feedback-placeholder-wrapper"></div>
+                </div>
+            </#if>
+
+            <#if realm.internationalizationEnabled>
+                <div id="kc-locale" class="${properties.kcLocaleClass!}">
+                    <div id="kc-locale-wrapper" class="${properties.kcLocaleWrapperClass!}">
+                        <div class="kc-dropdown">
+                            <a href="#">${locale.current}</a>
+                            <ul>
+                                <#list locale.supported as l>
+                                    <li class="kc-dropdown-item"><a href="${l.url}">${l.label}</a></li>
+                                </#list>
+                            </ul>
+                        </div>
+                    </div>
+                </div>
             </#if>
 
             <div id="kc-content" class="${properties.kcContentClass!}">
@@ -85,7 +82,7 @@
 
     <!--
         <p class="powered">
-            <a href="http://www.keycloak.org">${rb.poweredByKeycloak}</a>
+            <a href="http://www.keycloak.org">${msg("poweredByKeycloak")}</a>
         </p>
     </div>
     -->
diff --git a/forms/common-themes/src/main/resources/theme/login/patternfly/resources/css/login.css b/forms/common-themes/src/main/resources/theme/login/patternfly/resources/css/login.css
index 32de59f..345f594 100644
--- a/forms/common-themes/src/main/resources/theme/login/patternfly/resources/css/login.css
+++ b/forms/common-themes/src/main/resources/theme/login/patternfly/resources/css/login.css
@@ -1,3 +1,58 @@
+.kc-dropdown{
+    position: relative;
+}
+.kc-dropdown > a{
+    position: absolute;
+    right: 0px;
+    display:block;
+    padding: 11px 10px 12px;
+    line-height: 12px;
+    font-size: 12px;
+    color: #fff !important;
+    text-decoration: none;
+}
+.kc-dropdown > a::after{
+    content: "\2c5";
+    margin-left: 4px;
+}
+.kc-dropdown:hover > a{
+    background-color: rgba(0,0,0,0.2);
+}
+.kc-dropdown ul li a{
+    padding: 1px 11px;
+    font-size: 12px;
+    color: #000 !important;
+    border: 1px solid #fff;
+    text-decoration: none;
+    display:block;
+    line-height: 20px;
+}
+.kc-dropdown ul li a:hover{
+    color: #4d5258;
+    background-color: #d4edfa;
+    border-color: #b3d3e7;
+}
+.kc-dropdown ul{
+    position: absolute;
+    right: 0px;
+    top: 35px;
+    z-index: 2000;
+    list-style:none;
+    display:none;
+    padding: 5px 0px;
+    margin: 0px;
+    background-color: #fff !important;
+    border: 1px solid #b6b6b6;
+    border-radius: 1px;
+    -webkit-box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
+    box-shadow: 0 6px 12px rgba(0, 0, 0, 0.175);
+    background-clip: padding-box;
+    min-width: 100px;
+}
+.kc-dropdown:hover ul{
+    display:block;
+}
+
 .login-pf .container {
     padding-top: 40px;
 }
diff --git a/forms/common-themes/src/main/resources/theme/login/patternfly/theme.properties b/forms/common-themes/src/main/resources/theme/login/patternfly/theme.properties
index 4b85665..25427a7 100644
--- a/forms/common-themes/src/main/resources/theme/login/patternfly/theme.properties
+++ b/forms/common-themes/src/main/resources/theme/login/patternfly/theme.properties
@@ -10,8 +10,9 @@ kcContentClass=col-sm-12 col-md-12 col-lg-12 container
 kcContentWrapperClass=row
 
 kcHeaderClass=col-xs-12 col-sm-7 col-md-6 col-lg-5
-
-kcFeedBackClass=col-xs-12 col-sm-5 col-md-6 col-lg-7
+kcFeedBackClass=col-xs-12 col-sm-4 col-md-5 col-lg-6
+kcFeedBackPlaceholderClass=col-xs-12 col-sm-4 col-md-5 col-lg-6
+kcLocaleClass=col-xs-12 col-sm-1
 
 kcFormAreaClass=col-xs-12 col-sm-8 col-md-8 col-lg-6 login
 
diff --git a/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProvider.java b/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProvider.java
index 21972b2..505a416 100755
--- a/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProvider.java
+++ b/forms/email-freemarker/src/main/java/org/keycloak/email/freemarker/FreeMarkerEmailProvider.java
@@ -10,7 +10,7 @@ import org.keycloak.freemarker.FreeMarkerUtil;
 import org.keycloak.freemarker.LocaleHelper;
 import org.keycloak.freemarker.Theme;
 import org.keycloak.freemarker.ThemeProvider;
-import org.keycloak.freemarker.beans.TextFormatterBean;
+import org.keycloak.freemarker.beans.MessageFormatterMethod;
 import org.keycloak.models.KeycloakSession;
 import org.keycloak.models.RealmModel;
 import org.keycloak.models.UserModel;
@@ -20,6 +20,7 @@ import javax.mail.Session;
 import javax.mail.Transport;
 import javax.mail.internet.InternetAddress;
 import javax.mail.internet.MimeMessage;
+import java.text.MessageFormat;
 import java.util.*;
 
 /**
@@ -84,9 +85,8 @@ public class FreeMarkerEmailProvider implements EmailProvider {
             Locale locale = LocaleHelper.getLocale(realm, user);
             attributes.put("locale", locale);
             Properties rb = theme.getMessages(locale);
-            attributes.put("rb", rb);
-            attributes.put("formatter", new TextFormatterBean(locale));
-            String subject =  rb.getProperty(subjectKey);
+            attributes.put("msg", new MessageFormatterMethod(locale, rb));
+            String subject = new MessageFormat(rb.getProperty(subjectKey,subjectKey),locale).format(new Object[0]);
             String body = freeMarker.processTemplate(attributes, template, theme);
 
             send(subject, body);
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
index 14579b9..44c8bad 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/FreeMarkerLoginFormsProvider.java
@@ -6,11 +6,12 @@ import org.keycloak.OAuth2Constants;
 import org.keycloak.email.EmailException;
 import org.keycloak.email.EmailProvider;
 import org.keycloak.freemarker.*;
-import org.keycloak.freemarker.beans.TextFormatterBean;
+import org.keycloak.freemarker.beans.MessageFormatterMethod;
 import org.keycloak.login.LoginFormsPages;
 import org.keycloak.login.LoginFormsProvider;
 import org.keycloak.login.freemarker.model.ClientBean;
 import org.keycloak.login.freemarker.model.CodeBean;
+import org.keycloak.freemarker.beans.LocaleBean;
 import org.keycloak.login.freemarker.model.LoginBean;
 import org.keycloak.login.freemarker.model.MessageBean;
 import org.keycloak.login.freemarker.model.OAuthGrantBean;
@@ -27,7 +28,6 @@ import org.keycloak.models.RealmModel;
 import org.keycloak.models.RoleModel;
 import org.keycloak.models.UserModel;
 import org.keycloak.services.messages.Messages;
-import org.keycloak.services.resources.LoginActionsService;
 import org.keycloak.services.resources.flows.Urls;
 
 import javax.ws.rs.core.*;
@@ -176,13 +176,9 @@ import java.util.concurrent.TimeUnit;
 
         Properties messages;
         Locale locale = LocaleHelper.getLocale(realm, user, uriInfo, httpHeaders);
-        if(locale != null){
-            attributes.put("locale", locale);
-            attributes.put("formatter", new TextFormatterBean(locale));
-        }
         try {
             messages = theme.getMessages(locale);
-            attributes.put("rb", messages);
+            attributes.put("msg", new MessageFormatterMethod(locale, messages));
         } catch (IOException e) {
             logger.warn("Failed to load messages", e);
             messages = new Properties();
@@ -191,7 +187,7 @@ import java.util.concurrent.TimeUnit;
         if (message != null) {
             String formattedMessage;
             if(messages.containsKey(message)){
-                formattedMessage = new MessageFormat(messages.getProperty(message).replace("'","''"),locale).format(parameters);
+                formattedMessage = new MessageFormat(messages.getProperty(message),locale).format(parameters);
             }else{
                 formattedMessage = message;
             }
@@ -207,6 +203,22 @@ import java.util.concurrent.TimeUnit;
             attributes.put("realm", new RealmBean(realm));
             attributes.put("social", new IdentityProviderBean(realm, baseUri, this.uriInfo));
             attributes.put("url", new UrlBean(realm, theme, baseUri, this.actionUri));
+
+            if (realm.isInternationalizationEnabled()) {
+                UriBuilder b;
+                switch (page) {
+                    case LOGIN:
+                        b = UriBuilder.fromUri(Urls.realmLoginPage(baseUri, realm.getName()));
+                        break;
+                    case REGISTER:
+                        b = UriBuilder.fromUri(Urls.realmRegisterPage(baseUri, realm.getName()));
+                        break;
+                    default:
+                        b = UriBuilder.fromUri(baseUri).path(uriInfo.getPath());
+                        break;
+                }
+                attributes.put("locale", new LocaleBean(realm, locale, b, messages));
+            }
         }
 
         if (client != null) {
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/RealmBean.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/RealmBean.java
index 3e36d08..b161ad2 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/RealmBean.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/RealmBean.java
@@ -66,10 +66,6 @@ public class RealmBean {
         return realm.isInternationalizationEnabled();
     }
 
-    public Set<String> getSupportedLocales(){
-        return realm.getSupportedLocales();
-    }
-
     public boolean isPassword() {
         for (RequiredCredentialModel r : realm.getRequiredCredentials()) {
             if (r.getType().equals(CredentialRepresentation.PASSWORD)) {
diff --git a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/UrlBean.java b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/UrlBean.java
index 259b098..eb9a40b 100755
--- a/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/UrlBean.java
+++ b/forms/login-freemarker/src/main/java/org/keycloak/login/freemarker/model/UrlBean.java
@@ -34,9 +34,7 @@ public class UrlBean {
 
     private final URI actionuri;
     private URI baseURI;
-
     private Theme theme;
-
     private String realm;
 
     public UrlBean(RealmModel realm, Theme theme, URI baseURI, URI actionUri) {
@@ -86,10 +84,6 @@ public class UrlBean {
         return Urls.loginActionEmailVerification(baseURI, realm).toString();
     }
 
-    public String getLocaleCookiePath(){
-        return Urls.localeCookiePath(baseURI, realm);
-    }
-
     public String getOauthAction() {
         if (this.actionuri != null) {
             return this.actionuri.getPath();
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java
index b4b0de5..6bdae4e 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolService.java
@@ -133,7 +133,7 @@ public class OIDCLoginProtocolService {
     public Object registerPage() {
         AuthorizationEndpoint endpoint = new AuthorizationEndpoint(authManager, realm, event);
         ResteasyProviderFactory.getInstance().injectProperties(endpoint);
-        return endpoint.register().init();
+        return endpoint.init().register();
     }
 
     /**