keycloak-uncached

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

3/30/2015 3:25:03 AM

Changes

Details

diff --git a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ImportUtils.java b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ImportUtils.java
index b3b6baf..5c0a60c 100755
--- a/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ImportUtils.java
+++ b/export-import/export-import-api/src/main/java/org/keycloak/exportimport/util/ImportUtils.java
@@ -112,10 +112,12 @@ public class ImportUtils {
 
             RoleModel createRealmRole = realm.addRole(AdminRoles.CREATE_REALM);
             adminRole.addCompositeRole(createRealmRole);
+            createRealmRole.setDescription("${role_"+AdminRoles.CREATE_REALM+"}");
         } else {
             adminRealm = model.getRealmByName(Config.getAdminRealm());
             adminRole = adminRealm.getRole(AdminRoles.ADMIN);
         }
+        adminRole.setDescription("${role_"+AdminRoles.ADMIN+"}");
 
         ApplicationModel realmAdminApp = KeycloakModelUtils.createApplication(adminRealm, KeycloakModelUtils.getMasterRealmAdminApplicationName(realm));
         realmAdminApp.setBearerOnly(true);
@@ -123,6 +125,7 @@ public class ImportUtils {
 
         for (String r : AdminRoles.ALL_REALM_ROLES) {
             RoleModel role = realmAdminApp.addRole(r);
+            role.setDescription("${role_"+r+"}");
             adminRole.addCompositeRole(role);
         }
     }
diff --git a/forms/common-themes/src/main/resources/theme/base/account/template.ftl b/forms/common-themes/src/main/resources/theme/base/account/template.ftl
index 22bade4..d461118 100644
--- a/forms/common-themes/src/main/resources/theme/base/account/template.ftl
+++ b/forms/common-themes/src/main/resources/theme/base/account/template.ftl
@@ -30,8 +30,8 @@
                     <ul class="nav navbar-nav navbar-utility">
                         <#if realm.internationalizationEnabled>
                             <li>
-                                <div class="kc-dropdown">
-                                    <a href="#">${locale.current}</a>
+                                <div class="kc-dropdown" id="kc-locale-dropdown">
+                                    <a href="#" id="kc-current-locale-link">${locale.current}</a>
                                     <ul>
                                         <#list locale.supported as l>
                                             <li class="kc-dropdown-item"><a href="${l.url}">${l.label}</a></li>
diff --git a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_de.properties b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_de.properties
index 1b570c7..06abbb1 100644
--- a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_de.properties
+++ b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_de.properties
@@ -29,8 +29,10 @@ noAccount=Neuer Benutzer?
 username=Benutzername
 usernameOrEmail=Benutzername oder E-Mail
 firstName=Vorname
-fullName=Name
+givenName=Vorname
+fullName=voller Name
 lastName=Nachname
+familyName=Nachname
 email=E-Mail
 password=Passwort
 passwordConfirm=Passwort best\u00E4tigen
@@ -38,11 +40,14 @@ passwordNew=Neues Passwort
 passwordNewConfirm=Neues Passwort best\u00E4tigen
 rememberMe=Angemeldet bleiben
 authenticatorCode=One-time Code
+address=Adresse
 street=Strasse
 region=Staat, Provinz, Region
 postal_code=PLZ
 locality=Stadt oder Ortschaft
 country=Land
+emailVerified=E-Mail verifiziert
+gssDelegationCredential=GSS delegierte Berechtigung
 
 loginTotpStep1=Installieren Sie <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> oder <a href="http://code.google.com/p/google-authenticator/" target="_blank">Google Authenticator</a> auf Ihrem Smartphone.
 loginTotpStep2=\u00D6ffnen Sie die Applikation und scannen Sie den Barcode oder geben sie den Code ein.
@@ -65,6 +70,24 @@ copyCodeInstruction=Bitte kopieren sie den folgenden Code und f\u00FCgen ihn in 
 
 personalInfo=Pers\u00F6nliche Informationen:
 
+role_admin=Admin
+role_realm-admin=Realm Admin
+role_create-realm=Realm erstellen
+role_view-realm=Realm ansehen
+role_view-users=Benutzer ansehen
+role_view-applications=Applicationen ansehen
+role_view-clients=Clients ansehen
+role_view-events=Events ansehen
+role_view-identity-providers=Identity Providers ansehen
+role_manage-realm=Realm verwalten
+role_manage-users=Benutzer verwalten
+role_manage-applications=Applikationen verwalten
+role_manage-identity-providers=Identity Provider verwalten
+role_manage-clients=Clients verwalten
+role_manage-events=Events verwalten
+role_view-profile=Profile ansehen
+role_manage-account=Profile verwalten
+
 invalidUserMessage=Ung\u00FCltiger Benutzername oder Passwort.
 invalidEmailMessage=Ung\u00FCltige E-Mail Adresse.
 accountDisabledMessage=Benutzerkonto ist gesperrt, bitte kontaktieren Sie den Admin.
diff --git a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties
index 84f32c7..4bb3a66 100755
--- a/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties
+++ b/forms/common-themes/src/main/resources/theme/base/login/messages/messages_en.properties
@@ -29,8 +29,10 @@ noAccount=New user?
 username=Username
 usernameOrEmail=Username or email
 firstName=First name
+givenName=Given name
 fullName=Full name
 lastName=Last name
+familyName=Family name
 email=Email
 password=Password
 passwordConfirm=Confirm password
@@ -38,11 +40,14 @@ passwordNew=New Password
 passwordNewConfirm=New Password confirmation
 rememberMe=Remember me
 authenticatorCode=One-time code
+address=Address
 street=Street
 locality=City or Locality
 region=State, Province, or Region
 postal_code=Zip or Postal code
 country=Country
+emailVerified=Email verified
+gssDelegationCredential=gss delegation credential
 
 loginTotpStep1=Install <a href="https://fedorahosted.org/freeotp/" target="_blank">FreeOTP</a> or <a href="http://code.google.com/p/google-authenticator/" target="_blank">Google Authenticator</a> on your mobile
 loginTotpStep2=Open the application and scan the barcode or enter the key
@@ -64,6 +69,24 @@ copyCodeInstruction=Please copy this code and paste it into your application:
 
 personalInfo=Personal Info:
 
+role_admin=Admin
+role_realm-admin=Realm Admin
+role_create-realm=Crate realm
+role_view-realm=View realm
+role_view-users=View users
+role_view-applications=View applications
+role_view-clients=View clients
+role_view-events=View events
+role_view-identity-providers=View identity providers
+role_manage-realm=Manage realm
+role_manage-users=Manage users
+role_manage-applications=Manage applications
+role_manage-identity-providers=Manage identity providers
+role_manage-clients=Manage clients
+role_manage-events=Manage events
+role_view-profile=View profile
+role_manage-account=Manage account
+
 invalidUserMessage=Invalid username or password.
 invalidEmailMessage=Invalid email address.
 accountDisabledMessage=Account is disabled, contact admin.
diff --git a/forms/common-themes/src/main/resources/theme/base/login/template.ftl b/forms/common-themes/src/main/resources/theme/base/login/template.ftl
index 9864214..34b2d4a 100644
--- a/forms/common-themes/src/main/resources/theme/base/login/template.ftl
+++ b/forms/common-themes/src/main/resources/theme/base/login/template.ftl
@@ -48,8 +48,8 @@
             <#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>
+                        <div class="kc-dropdown" id="kc-locale-dropdown">
+                            <a href="#" id="kc-current-locale-link">${locale.current}</a>
                             <ul>
                                 <#list locale.supported as l>
                                     <li class="kc-dropdown-item"><a href="${l.url}">${l.label}</a></li>
diff --git a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java
index 83afe93..2387e1c 100755
--- a/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java
+++ b/saml/saml-protocol/src/main/java/org/keycloak/protocol/saml/SamlProtocolFactory.java
@@ -66,21 +66,21 @@ public class SamlProtocolFactory extends AbstractLoginProtocolFactory {
                 X500SAMLProfileConstants.EMAIL.get(),
                 JBossSAMLURIConstants.ATTRIBUTE_FORMAT_URI.get(),
                 X500SAMLProfileConstants.EMAIL.getFriendlyName(),
-                true, "email");
+                true, "${email}");
         builtins.add(model);
         model = UserPropertyAttributeStatementMapper.createAttributeMapper("X500 givenName",
                 "firstName",
                 X500SAMLProfileConstants.GIVEN_NAME.get(),
                 JBossSAMLURIConstants.ATTRIBUTE_FORMAT_URI.get(),
                 X500SAMLProfileConstants.GIVEN_NAME.getFriendlyName(),
-                true, "given name");
+                true, "${givenName}");
         builtins.add(model);
         model = UserPropertyAttributeStatementMapper.createAttributeMapper("X500 surname",
                 "lastName",
                 X500SAMLProfileConstants.SURNAME.get(),
                 JBossSAMLURIConstants.ATTRIBUTE_FORMAT_URI.get(),
                 X500SAMLProfileConstants.SURNAME.getFriendlyName(),
-                true, "family name");
+                true, "${familyName}");
         builtins.add(model);
         model = RoleListMapper.create("role list", "Role", AttributeStatementHelper.BASIC, null, false);
         builtins.add(model);
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/mappers/AddressMapper.java b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AddressMapper.java
index d91f7ea..fa95be1 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/mappers/AddressMapper.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/mappers/AddressMapper.java
@@ -52,7 +52,7 @@ public class AddressMapper extends AbstractOIDCProtocolMapper implements OIDCAcc
         address.setProtocolMapper(PROVIDER_ID);
         address.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
         address.setConsentRequired(true);
-        address.setConsentText("address");
+        address.setConsentText("${address}");
         config = new HashMap<String, String>();
         config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
         config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
@@ -66,7 +66,7 @@ public class AddressMapper extends AbstractOIDCProtocolMapper implements OIDCAcc
         address.setProtocolMapper(PROVIDER_ID);
         address.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
         address.setConsentRequired(true);
-        address.setConsentText("address");
+        address.setConsentText("${address}");
         config = new HashMap<String, String>();
         config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, Boolean.toString(idToken));
         config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, Boolean.toString(accessToken));
diff --git a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java
index c039929..0c81a3b 100755
--- a/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java
+++ b/services/src/main/java/org/keycloak/protocol/oidc/OIDCLoginProtocolFactory.java
@@ -32,6 +32,13 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
     public static final String GIVEN_NAME = "given name";
     public static final String FAMILY_NAME = "family name";
     public static final String FULL_NAME = "full name";
+    public static final String USERNAME_CONSENT_TEXT = "${username}";
+    public static final String EMAIL_CONSENT_TEXT = "${email}";
+    public static final String EMAIL_VERIFIED_CONSENT_TEXT = "${emailVerified}";
+    public static final String GIVEN_NAME_CONSENT_TEXT = "${givenName}";
+    public static final String FAMILY_NAME_CONSENT_TEXT = "${familyName}";
+    public static final String FULL_NAME_CONSENT_TEXT = "${fullName}";
+
 
     @Override
     public LoginProtocol create(KeycloakSession session) {
@@ -57,35 +64,35 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
         model = UserPropertyMapper.createClaimMapper(USERNAME,
                 "username",
                 "preferred_username", "String",
-                true, USERNAME,
+                true, USERNAME_CONSENT_TEXT,
                 true, true);
         builtins.add(model);
         defaultBuiltins.add(model);
         model = UserPropertyMapper.createClaimMapper(EMAIL,
                 "email",
                 "email", "String",
-                true, EMAIL,
+                true, EMAIL_CONSENT_TEXT,
                 true, true);
         builtins.add(model);
         defaultBuiltins.add(model);
         model = UserPropertyMapper.createClaimMapper(GIVEN_NAME,
                 "firstName",
                 "given_name", "String",
-                true, GIVEN_NAME,
+                true, GIVEN_NAME_CONSENT_TEXT,
                 true, true);
         builtins.add(model);
         defaultBuiltins.add(model);
         model = UserPropertyMapper.createClaimMapper(FAMILY_NAME,
                 "lastName",
                 "family_name", "String",
-                true, FAMILY_NAME,
+                true, FAMILY_NAME_CONSENT_TEXT,
                 true, true);
         builtins.add(model);
         defaultBuiltins.add(model);
         model = UserPropertyMapper.createClaimMapper(EMAIL_VERIFIED,
                 "emailVerified",
                 "email_verified", "boolean",
-                false, null,
+                false, EMAIL_VERIFIED_CONSENT_TEXT,
                 true, true);
         builtins.add(model);
 
@@ -94,7 +101,7 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
         fullName.setProtocolMapper(FullNameMapper.PROVIDER_ID);
         fullName.setProtocol(OIDCLoginProtocol.LOGIN_PROTOCOL);
         fullName.setConsentRequired(true);
-        fullName.setConsentText(FULL_NAME);
+        fullName.setConsentText(FULL_NAME_CONSENT_TEXT);
         Map<String, String> config = new HashMap<String, String>();
         config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ACCESS_TOKEN, "true");
         config.put(OIDCAttributeMapperHelper.INCLUDE_IN_ID_TOKEN, "true");
@@ -108,7 +115,7 @@ public class OIDCLoginProtocolFactory extends AbstractLoginProtocolFactory {
         model = UserSessionNoteMapper.createClaimMapper(KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
                 KerberosConstants.GSS_DELEGATION_CREDENTIAL,
                 KerberosConstants.GSS_DELEGATION_CREDENTIAL, "String",
-                true, KerberosConstants.GSS_DELEGATION_CREDENTIAL_DISPLAY_NAME,
+                true, "${gssDelegationCredential}",
                 true, false);
         builtins.add(model);
     }
diff --git a/services/src/main/java/org/keycloak/services/managers/RealmManager.java b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
index 240d5cb..4da8e12 100755
--- a/services/src/main/java/org/keycloak/services/managers/RealmManager.java
+++ b/services/src/main/java/org/keycloak/services/managers/RealmManager.java
@@ -179,11 +179,13 @@ public class RealmManager {
             realmAdminApp = applicationManager.createApplication(realm, realmAdminApplicationName);
         }
         RoleModel adminRole = realmAdminApp.addRole(AdminRoles.REALM_ADMIN);
+        adminRole.setDescription("${role_"+AdminRoles.REALM_ADMIN+"}");
         realmAdminApp.setBearerOnly(true);
         realmAdminApp.setFullScopeAllowed(false);
 
         for (String r : AdminRoles.ALL_REALM_ROLES) {
             RoleModel role = realmAdminApp.addRole(r);
+            role.setDescription("${role_"+r+"}");
             adminRole.addCompositeRole(role);
         }
     }
@@ -202,6 +204,7 @@ public class RealmManager {
 
             for (String role : AccountRoles.ALL) {
                 application.addDefaultRole(role);
+                application.getRole(role).setDescription("${role_"+role+"}");
             }
         }
     }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/ProfileTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/ProfileTest.java
index 18ca838..97e2095 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/account/ProfileTest.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/account/ProfileTest.java
@@ -111,7 +111,6 @@ public class ProfileTest {
         assertEquals("Last", profile.getString("lastName"));
 
         JSONObject attributes = profile.getJSONObject("attributes");
-        assertEquals(2, attributes.length());
         assertEquals("value1", attributes.getString("key1"));
         assertEquals("value2", attributes.getString("key2"));
     }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/i18n/AccountPageTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/i18n/AccountPageTest.java
new file mode 100644
index 0000000..23b7398
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/i18n/AccountPageTest.java
@@ -0,0 +1,84 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite.i18n;
+
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.pages.AccountUpdateProfilePage;
+import org.keycloak.testsuite.pages.LoginPage;
+import org.keycloak.testsuite.rule.KeycloakRule;
+import org.keycloak.testsuite.rule.WebResource;
+import org.keycloak.testsuite.rule.WebRule;
+
+/**
+ * @author <a href="mailto:gerbermichi@me.com">Michael Gerber</a>
+ */
+public class AccountPageTest {
+
+    @ClassRule
+    public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
+        @Override
+        public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+            UserModel user = manager.getSession().users().addUser(appRealm, "login-test");
+            user.setEmail("login@test.com");
+            user.setEnabled(true);
+
+            UserCredentialModel creds = new UserCredentialModel();
+            creds.setType(CredentialRepresentation.PASSWORD);
+            creds.setValue("password");
+
+            user.updateCredential(creds);
+        }
+    });
+
+    @Rule
+    public WebRule webRule = new WebRule(this);
+
+    @WebResource
+    protected AccountUpdateProfilePage accountUpdateProfilePage;
+
+    @WebResource
+    protected LoginPage loginPage;
+
+    @Test
+    public void languageDropdown() {
+        accountUpdateProfilePage.open();
+        loginPage.login("login@test.com", "password");
+        Assert.assertTrue(accountUpdateProfilePage.isCurrent());
+
+        Assert.assertEquals("English", accountUpdateProfilePage.getLanguageDropdownText());
+
+        accountUpdateProfilePage.openLanguage("German");
+        Assert.assertEquals("Deutsch", accountUpdateProfilePage.getLanguageDropdownText());
+
+        accountUpdateProfilePage.openLanguage("Englisch");
+        Assert.assertEquals("English", accountUpdateProfilePage.getLanguageDropdownText());
+        accountUpdateProfilePage.logout();
+    }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/i18n/EmailTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/i18n/EmailTest.java
new file mode 100644
index 0000000..b8e2453
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/i18n/EmailTest.java
@@ -0,0 +1,106 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite.i18n;
+
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.keycloak.models.RealmModel;
+import org.keycloak.models.UserCredentialModel;
+import org.keycloak.models.UserModel;
+import org.keycloak.representations.idm.CredentialRepresentation;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.pages.LoginPage;
+import org.keycloak.testsuite.pages.LoginPasswordResetPage;
+import org.keycloak.testsuite.rule.GreenMailRule;
+import org.keycloak.testsuite.rule.KeycloakRule;
+import org.keycloak.testsuite.rule.WebResource;
+import org.keycloak.testsuite.rule.WebRule;
+
+import javax.mail.MessagingException;
+import javax.mail.internet.MimeMessage;
+import java.io.IOException;
+
+import static org.junit.Assert.assertEquals;
+
+/**
+ * @author <a href="mailto:gerbermichi@me.com">Michael Gerber</a>
+ */
+public class EmailTest {
+    @ClassRule
+    public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
+        @Override
+        public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+            UserModel user = manager.getSession().users().addUser(appRealm, "login-test");
+            user.setEmail("login@test.com");
+            user.setEnabled(true);
+            user.setAttribute(UserModel.LOCALE, "de");
+
+            UserCredentialModel creds = new UserCredentialModel();
+            creds.setType(CredentialRepresentation.PASSWORD);
+            creds.setValue("password");
+
+            user.updateCredential(creds);
+        }
+    });
+
+    @Rule
+    public WebRule webRule = new WebRule(this);
+
+    @Rule
+    public GreenMailRule greenMail = new GreenMailRule();
+
+    @WebResource
+    protected LoginPage loginPage;
+
+    @WebResource
+    protected LoginPasswordResetPage resetPasswordPage;
+
+    @Test
+    public void restPasswordEmail() throws IOException, MessagingException {
+        loginPage.open();
+        loginPage.resetPassword();
+        resetPasswordPage.changePassword("login-test");
+
+        assertEquals(1, greenMail.getReceivedMessages().length);
+
+        MimeMessage message = greenMail.getReceivedMessages()[0];
+
+        Assert.assertEquals("Passwort zurückzusetzen", message.getSubject());
+
+        keycloakRule.update(new KeycloakRule.KeycloakSetup() {
+            @Override
+            public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+                manager.getSession().users().getUserByUsername("login-test", appRealm).setAttribute(UserModel.LOCALE, "en");
+            }
+        });
+
+        resetPasswordPage.changePassword("login-test");
+
+        assertEquals(2, greenMail.getReceivedMessages().length);
+
+        message = greenMail.getReceivedMessages()[1];
+
+        Assert.assertEquals("Reset password", message.getSubject());
+    }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/i18n/LoginPageTest.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/i18n/LoginPageTest.java
new file mode 100755
index 0000000..39bfd99
--- /dev/null
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/i18n/LoginPageTest.java
@@ -0,0 +1,122 @@
+/*
+ * JBoss, Home of Professional Open Source.
+ * Copyright 2012, Red Hat, Inc., and individual contributors
+ * as indicated by the @author tags. See the copyright.txt file in the
+ * distribution for a full listing of individual contributors.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.keycloak.testsuite.i18n;
+
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.jboss.resteasy.client.jaxrs.ResteasyClient;
+import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder;
+import org.jboss.resteasy.client.jaxrs.engines.ApacheHttpClient4Engine;
+import org.junit.Assert;
+import org.junit.ClassRule;
+import org.junit.Rule;
+import org.junit.Test;
+import org.keycloak.adapters.HttpClientBuilder;
+import org.keycloak.freemarker.LocaleHelper;
+import org.keycloak.models.RealmModel;
+import org.keycloak.services.managers.RealmManager;
+import org.keycloak.testsuite.OAuthClient;
+import org.keycloak.testsuite.pages.LoginPage;
+import org.keycloak.testsuite.rule.KeycloakRule;
+import org.keycloak.testsuite.rule.WebResource;
+import org.keycloak.testsuite.rule.WebRule;
+import org.openqa.selenium.WebDriver;
+
+import javax.ws.rs.core.Response;
+
+/**
+ * @author <a href="mailto:gerbermichi@me.com">Michael Gerber</a>
+ */
+public class LoginPageTest {
+
+    @ClassRule
+    public static KeycloakRule keycloakRule = new KeycloakRule(new KeycloakRule.KeycloakSetup() {
+        @Override
+        public void config(RealmManager manager, RealmModel adminstrationRealm, RealmModel appRealm) {
+
+        }
+    });
+
+    @Rule
+    public WebRule webRule = new WebRule(this);
+
+    @WebResource
+    protected OAuthClient oauth;
+
+    @WebResource
+    protected WebDriver driver;
+
+    @WebResource
+    protected LoginPage loginPage;
+
+    @Test
+    public void languageDropdown() {
+        loginPage.open();
+        Assert.assertEquals("English", loginPage.getLanguageDropdownText());
+
+        loginPage.openLanguage("German");
+        Assert.assertEquals("Deutsch", loginPage.getLanguageDropdownText());
+
+        loginPage.openLanguage("Englisch");
+        Assert.assertEquals("English", loginPage.getLanguageDropdownText());
+    }
+
+    @Test
+    public void uiLocalesParameter() {
+        loginPage.open();
+        Assert.assertEquals("English", loginPage.getLanguageDropdownText());
+
+        //test if cookie works
+        oauth.uiLocales("de");
+        loginPage.open();
+        Assert.assertEquals("English", loginPage.getLanguageDropdownText());
+
+        driver.manage().deleteAllCookies();
+        loginPage.open();
+        Assert.assertEquals("Deutsch", loginPage.getLanguageDropdownText());
+
+        oauth.uiLocales("en de");
+        driver.manage().deleteAllCookies();
+        loginPage.open();
+        Assert.assertEquals("English", loginPage.getLanguageDropdownText());
+
+        oauth.uiLocales("fr de");
+        driver.manage().deleteAllCookies();
+        loginPage.open();
+        Assert.assertEquals("Deutsch", loginPage.getLanguageDropdownText());
+    }
+
+    @Test
+    public void acceptLanguageHeader() {
+        DefaultHttpClient httpClient = (DefaultHttpClient) new HttpClientBuilder().build();
+        ApacheHttpClient4Engine engine = new ApacheHttpClient4Engine(httpClient);
+        ResteasyClient client = new ResteasyClientBuilder().httpEngine(engine).build();
+
+        loginPage.open();
+        Response response = client.target(driver.getCurrentUrl()).request().acceptLanguage("de").get();
+        Assert.assertTrue(response.readEntity(String.class).contains("Anmeldung bei test"));
+        Assert.assertEquals("de", response.getCookies().get(LocaleHelper.LOCALE_COOKIE).getValue());
+
+        response = client.target(driver.getCurrentUrl()).request().acceptLanguage("en").get();
+        Assert.assertTrue(response.readEntity(String.class).contains("Log in to test"));
+        Assert.assertEquals("en", response.getCookies().get(LocaleHelper.LOCALE_COOKIE).getValue());
+    }
+}
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
index 80d0310..3c81b8a 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/OAuthClient.java
@@ -35,6 +35,7 @@ import org.junit.Assert;
 import org.keycloak.OAuth2Constants;
 import org.keycloak.RSATokenVerifier;
 import org.keycloak.VerificationException;
+import org.keycloak.freemarker.LocaleHelper;
 import org.keycloak.jose.jws.JWSInput;
 import org.keycloak.jose.jws.crypto.RSAProvider;
 import org.keycloak.protocol.oidc.OIDCLoginProtocolService;
@@ -73,6 +74,8 @@ public class OAuthClient {
 
     private String state = "mystate";
 
+    private String uiLocales = null;
+
     private PublicKey realmPublicKey;
 
     public OAuthClient(WebDriver driver) {
@@ -296,6 +299,9 @@ public class OAuthClient {
         if (state != null) {
             b.queryParam(OAuth2Constants.STATE, state);
         }
+        if(uiLocales != null){
+            b.queryParam(LocaleHelper.UI_LOCALES_PARAM, uiLocales);
+        }
         return b.build(realm).toString();
     }
 
@@ -349,6 +355,11 @@ public class OAuthClient {
         return this;
     }
 
+    public OAuthClient uiLocales(String uiLocales){
+        this.uiLocales = uiLocales;
+        return this;
+    }
+
     public String getRealm() {
         return realm;
     }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AbstractAccountPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AbstractAccountPage.java
index 3eee185..684eedf 100755
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AbstractAccountPage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/AbstractAccountPage.java
@@ -21,6 +21,7 @@
  */
 package org.keycloak.testsuite.pages;
 
+import org.openqa.selenium.By;
 import org.openqa.selenium.WebElement;
 import org.openqa.selenium.support.FindBy;
 
@@ -32,8 +33,21 @@ public abstract class AbstractAccountPage extends  AbstractPage {
     @FindBy(linkText = "Sign Out")
     private WebElement logoutLink;
 
+    @FindBy(id = "kc-current-locale-link")
+    private WebElement languageText;
+
+    @FindBy(id = "kc-locale-dropdown")
+    private WebElement localeDropdown;
+
     public void logout() {
         logoutLink.click();
     }
 
+    public String getLanguageDropdownText() {
+        return languageText.getText();
+    }
+
+    public void openLanguage(String language){
+        localeDropdown.findElement(By.linkText(language)).click();
+    }
 }
diff --git a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java
index 7d83b0c..1f0b7a2 100644
--- a/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java
+++ b/testsuite/integration/src/test/java/org/keycloak/testsuite/pages/LoginPage.java
@@ -68,6 +68,12 @@ public class LoginPage extends AbstractPage {
     @FindBy(className = "feedback-warning")
     private WebElement loginWarningMessage;
 
+    @FindBy(id = "kc-current-locale-link")
+    private WebElement languageText;
+
+    @FindBy(id = "kc-locale-dropdown")
+    private WebElement localeDropdown;
+
     public void login(String username, String password) {
         usernameInput.clear();
         usernameInput.sendKeys(username);
@@ -103,7 +109,7 @@ public class LoginPage extends AbstractPage {
 
 
     public boolean isCurrent() {
-        return driver.getTitle().equals("Log in to test");
+        return driver.getTitle().equals("Log in to test") || driver.getTitle().equals("Anmeldung bei test");
     }
 
     public void clickRegister() {
@@ -145,4 +151,12 @@ public class LoginPage extends AbstractPage {
         assertCurrent();
     }
 
+    public String getLanguageDropdownText() {
+        return languageText.getText();
+    }
+
+    public void openLanguage(String language){
+        localeDropdown.findElement(By.linkText(language)).click();
+    }
+
 }
diff --git a/testsuite/integration/src/test/resources/testrealm.json b/testsuite/integration/src/test/resources/testrealm.json
index 17c54dc..cedc22d 100755
--- a/testsuite/integration/src/test/resources/testrealm.json
+++ b/testsuite/integration/src/test/resources/testrealm.json
@@ -112,5 +112,9 @@
                 "roles": ["customer-user"]
             }
         ]
-    }
+    },
+
+    "internationalizationEnabled": true,
+    "supportedLocales": ["en", "de"],
+    "defaultLocale": "en"
 }