KEYCLOAK-7123: l10n dropdowns (#5170)

* KEYCLOAK-7196: Add kc_locale to keycloak.js

* KEYCLOAK-7123: Localization dropdowns

* Update keycloak-service to latest keycloak.js
This commit is contained in:
Stan Silvert 2018-04-25 15:04:12 -04:00 committed by GitHub
parent fe2ae6ec68
commit 35154db50f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 577 additions and 534 deletions

View file

@ -89,9 +89,7 @@ public class AccountConsole {
map.put("authUrl", session.getContext().getContextPath()); map.put("authUrl", session.getContext().getContextPath());
map.put("baseUrl", session.getContext().getContextPath() + "/realms/" + realm.getName() + "/account"); map.put("baseUrl", session.getContext().getContextPath() + "/realms/" + realm.getName() + "/account");
map.put("realm", realm.getName()); map.put("realm", realm);
map.put("isRegistrationEmailAsUsername", realm.isRegistrationEmailAsUsername());
map.put("isEditUserNameAllowed", realm.isEditUsernameAllowed());
map.put("resourceUrl", Urls.themeRoot(baseUri).getPath() + "/account/" + theme.getName()); map.put("resourceUrl", Urls.themeRoot(baseUri).getPath() + "/account/" + theme.getName());
map.put("resourceVersion", Version.RESOURCES_VERSION); map.put("resourceVersion", Version.RESOURCES_VERSION);
@ -108,7 +106,7 @@ public class AccountConsole {
Properties messages = theme.getMessages(locale); Properties messages = theme.getMessages(locale);
map.put("msg", new MessageFormatterMethod(locale, messages)); map.put("msg", new MessageFormatterMethod(locale, messages));
map.put("msgJSON", messagesToJsonString(messages)); map.put("msgJSON", messagesToJsonString(messages));
map.put("supportedLocales", supportedLocales(messages));
map.put("properties", theme.getProperties()); map.put("properties", theme.getProperties());
FreeMarkerUtil freeMarkerUtil = new FreeMarkerUtil(); FreeMarkerUtil freeMarkerUtil = new FreeMarkerUtil();
@ -119,6 +117,15 @@ public class AccountConsole {
} }
} }
private Map<String, String> supportedLocales(Properties messages) throws IOException {
Map<String, String> supportedLocales = new HashMap<>();
for (String l : realm.getSupportedLocales()) {
String label = messages.getProperty("locale_" + l, l);
supportedLocales.put(l, label);
}
return supportedLocales;
}
private String messagesToJsonString(Properties props) { private String messagesToJsonString(Properties props) {
if (props == null) return ""; if (props == null) return "";

View file

@ -4,6 +4,7 @@ doLogOutAllSessions=Log out all sessions
doRemove=Remove doRemove=Remove
doAdd=Add doAdd=Add
doSignOut=Sign Out doSignOut=Sign Out
doLogIn=Log In
editAccountHtmlTitle=Edit Account editAccountHtmlTitle=Edit Account
personalInfoHtmlTitle=Personal Info personalInfoHtmlTitle=Personal Info
@ -210,18 +211,18 @@ permissionRequestion=Permission Requestion
permission=Permission permission=Permission
shares=share(s) shares=share(s)
locale_ca=Catal\u00E0 locale_ca=Catal\u00e0
locale_de=Deutsch locale_de=Deutsch
locale_en=English locale_en=English
locale_es=Espa\u00F1ol locale_es=Espa\u00f1ol
locale_fr=Fran\u00e7ais locale_fr=Fran\u00e7ais
locale_it=Italian locale_it=Italian
locale_ja=\u65E5\u672C\u8A9E locale_ja=\u65e5\u672c\u8a9e
locale_nl=Nederlands locale_nl=Nederlands
locale_no=Norsk locale_no=Norsk
locale_lt=Lietuvi\u0173 locale_lt=Lietuvi\u0173
locale_pt-BR=Portugu\u00EAs (Brasil) locale_pt-BR=Portugu\u00eas (Brasil)
locale_ru=\u0420\u0443\u0441\u0441\u043A\u0438\u0439 locale_ru=\u0420\u0443\u0441\u0441\u043a\u0438\u0439
locale_sk=Sloven\u010Dina locale_sk=Sloven\u010dina
locale_sv=Svenska locale_sv=Svenska
locale_zh-CN=\u4e2d\u6587\u7b80\u4f53 locale_zh-CN=\u4e2d\u6587\u7b80\u4f53

View file

@ -6,10 +6,16 @@
<script> <script>
var authUrl = '${authUrl}'; var authUrl = '${authUrl}';
var baseUrl = '${baseUrl}'; var baseUrl = '${baseUrl}';
var realm = '${realm}'; var realm = '${realm.name}';
var resourceUrl = '${resourceUrl}'; var resourceUrl = '${resourceUrl}';
var isRegistrationEmailAsUsername = ${isRegistrationEmailAsUsername?c}; var isRegistrationEmailAsUsername = ${realm.registrationEmailAsUsername?c};
var isEditUserNameAllowed = ${isEditUserNameAllowed?c}; var isEditUserNameAllowed = ${realm.editUsernameAllowed?c};
var isInternationalizationEnabled = ${realm.internationalizationEnabled?c};
var availableLocales = [];
<#list supportedLocales as locale, label>
availableLocales.push({locale : '${locale}', label : '${label}'});
</#list>
<#if referrer??> <#if referrer??>
var referrer = '${referrer}'; var referrer = '${referrer}';
@ -67,7 +73,7 @@
<script src="${authUrl}/js/keycloak.js"></script> <script src="${authUrl}/js/keycloak.js"></script>
<script> <script>
var keycloak = Keycloak('${authUrl}/realms/${realm}/account/keycloak.json'); var keycloak = Keycloak('${authUrl}/realms/${realm.name}/account/keycloak.json');
keycloak.init({onLoad: 'check-sso'}).success(function(authenticated) { keycloak.init({onLoad: 'check-sso'}).success(function(authenticated) {
var loadjs = function (url,loadListener) { var loadjs = function (url,loadListener) {
const script = document.createElement("script"); const script = document.createElement("script");
@ -128,27 +134,15 @@
we are unable to localize the button's message. Not sure what to do about that yet. we are unable to localize the button's message. Not sure what to do about that yet.
--> -->
<ul class="nav navbar-nav navbar-right navbar-iconic"> <ul class="nav navbar-nav navbar-right navbar-iconic">
<li><button id="signInButton" style="visibility:hidden" onclick="keycloak.login();" class="btn btn-primary btn-lg btn-sign" type="button">Log In</button></li> <li><button id="signInButton" style="visibility:hidden" onclick="keycloak.login();" class="btn btn-primary btn-lg btn-sign" type="button">${msg("doLogIn")}</button></li>
<li class="dropdown"> <li class="dropdown">
<a href="#0" class="dropdown-toggle nav-item-iconic" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"> <a href="#0" class="dropdown-toggle nav-item-iconic" id="dropdownMenu1" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
${msg("locale_en")} <span class="caret"></span> ${msg("locale_" + locale)} <span class="caret"></span>
</a> </a>
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1"> <ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
<li><a href="#">${msg("locale_ca")}</a></li> <#list supportedLocales as locale, label>
<li><a href="#">${msg("locale_de")}</a></li> <li><a href="${baseUrl}/?kc_locale=${locale}">${label}</a></li>
<li><a href="#">${msg("locale_en")}</a></li> </#list>
<li><a href="#">${msg("locale_es")}</a></li>
<li><a href="#">${msg("locale_fr")}</a></li>
<li><a href="#">${msg("locale_it")}</a></li>
<li><a href="#">${msg("locale_ja")}</a></li>
<li><a href="#">${msg("locale_nl")}</a></li>
<li><a href="#">${msg("locale_no")}</a></li>
<li><a href="#">${msg("locale_lt")}</a></li>
<li><a href="#">${msg("locale_pt-BR")}</a></li>
<li><a href="#">${msg("locale_ru")}</a></li>
<li><a href="#">${msg("locale_sk")}</a></li>
<li><a href="#">${msg("locale_sv")}</a></li>
<li><a href="#">${msg("locale_zh-CN")}</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>

View file

@ -1,7 +1,7 @@
/* /*
* MIT License * MIT License
* *
* Copyright 2017 Andy Hanson * Copyright 2017 Brett Epps <https://github.com/eppsilon>
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
* associated documentation files (the "Software"), to deal in the Software without restriction, including * associated documentation files (the "Software"), to deal in the Software without restriction, including
@ -29,7 +29,7 @@ export = Keycloak;
declare function Keycloak(config?: string|{}): Keycloak.KeycloakInstance; declare function Keycloak(config?: string|{}): Keycloak.KeycloakInstance;
declare namespace Keycloak { declare namespace Keycloak {
type KeycloakAdapterName = 'cordova'|'default'; type KeycloakAdapterName = 'cordova'|'default' | any;
type KeycloakOnLoad = 'login-required'|'check-sso'; type KeycloakOnLoad = 'login-required'|'check-sso';
type KeycloakResponseMode = 'query'|'fragment'; type KeycloakResponseMode = 'query'|'fragment';
type KeycloakResponseType = 'code'|'id_token token'|'code id_token token'; type KeycloakResponseType = 'code'|'id_token token'|'code id_token token';
@ -39,6 +39,15 @@ declare namespace Keycloak {
/** /**
* @private Undocumented. * @private Undocumented.
*/ */
useNonce?: boolean;
/**
* Allows to use different adapter:
*
* - {string} default - using browser api for redirects
* - {string} cordova - using cordova plugins
* - {function} - allows to provide custom function as adapter.
*/
adapter?: KeycloakAdapterName; adapter?: KeycloakAdapterName;
/** /**
@ -78,7 +87,7 @@ declare namespace Keycloak {
* Set the interval to check login state (in seconds). * Set the interval to check login state (in seconds).
* @default 5 * @default 5
*/ */
checkLoginIframeInterval?: boolean; checkLoginIframeInterval?: number;
/** /**
* Set the OpenID Connect response mode to send to Keycloak upon login. * Set the OpenID Connect response mode to send to Keycloak upon login.
@ -141,9 +150,17 @@ declare namespace Keycloak {
idpHint?: string; idpHint?: string;
/** /**
* Specifies the desired locale for the UI. * Sets the 'ui_locales' query param in compliance with section 3.1.2.1
* of the OIDC 1.0 specification.
*/ */
locale?: string; locale?: string;
/**
* Specifies the desired Keycloak locale for the UI. This differs from
* the locale param in that it tells the Keycloak server to set a cookie and update
* the user's profile to a new preferred locale.
*/
kcLocale?: string;
} }
type KeycloakPromiseCallback<T> = (result: T) => void; type KeycloakPromiseCallback<T> = (result: T) => void;

View file

@ -51,6 +51,8 @@
adapter = loadAdapter('cordova'); adapter = loadAdapter('cordova');
} else if (initOptions && initOptions.adapter === 'default') { } else if (initOptions && initOptions.adapter === 'default') {
adapter = loadAdapter(); adapter = loadAdapter();
} else if (initOptions && typeof initOptions.adapter === "object") {
adapter = initOptions.adapter;
} else { } else {
if (window.Cordova || window.cordova) { if (window.Cordova || window.cordova) {
adapter = loadAdapter('cordova'); adapter = loadAdapter('cordova');
@ -281,6 +283,10 @@
url += '&ui_locales=' + encodeURIComponent(options.locale); url += '&ui_locales=' + encodeURIComponent(options.locale);
} }
if (options && options.kcLocale) {
url += '&kc_locale=' + encodeURIComponent(options.kcLocale);
}
return url; return url;
} }
@ -464,6 +470,10 @@
} else { } else {
console.warn('[KEYCLOAK] Failed to refresh token'); console.warn('[KEYCLOAK] Failed to refresh token');
if (req.status == 400) {
kc.clearToken();
}
kc.onAuthRefreshError && kc.onAuthRefreshError(); kc.onAuthRefreshError && kc.onAuthRefreshError();
for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) { for (var p = refreshQueue.pop(); p != null; p = refreshQueue.pop()) {
p.setError(true); p.setError(true);

View file

@ -28,6 +28,12 @@
</a> </a>
<ul class="dropdown-menu" aria-labelledby="dropdownMenu2"> <ul class="dropdown-menu" aria-labelledby="dropdownMenu2">
<li><a href="#" (click)="logout()">{{'doSignOut' | translate}}</a></li> <li><a href="#" (click)="logout()">{{'doSignOut' | translate}}</a></li>
<li class="dropdown-submenu pull-left">
<a class="test" tabindex="-1" href="#">Change language</a>
<ul class="dropdown-menu">
<li *ngFor="let locale of availableLocales" (click)="changeLocale(locale.locale)"><a tabindex="-1" href="#">{{ locale.label }}</a></li>
</ul>
</li>
</ul> </ul>
</li> </li>
</ul> </ul>

View file

@ -24,6 +24,7 @@ declare const resourceUrl: string;
declare const baseUrl: string; declare const baseUrl: string;
declare const referrer: string; declare const referrer: string;
declare const referrer_uri: string; declare const referrer_uri: string;
declare const availableLocales: Array<Object>;
@Component({ @Component({
selector: 'app-top-nav', selector: 'app-top-nav',
@ -34,10 +35,13 @@ export class TopNavComponent implements OnInit {
@Input() showSideNav: String; @Input() showSideNav: String;
public resourceUrl: string = resourceUrl; public resourceUrl: string = resourceUrl;
public availableLocales: Array<Object> = availableLocales;
private referrer: Referrer; private referrer: Referrer;
constructor(private keycloakService: KeycloakService, translateUtil: TranslateUtil, private respSvc: ResponsivenessService) { constructor(private keycloakService: KeycloakService,
translateUtil: TranslateUtil,
private respSvc: ResponsivenessService) {
this.referrer = new Referrer(translateUtil); this.referrer = new Referrer(translateUtil);
} }
@ -52,4 +56,8 @@ export class TopNavComponent implements OnInit {
this.keycloakService.logout(baseUrl); this.keycloakService.logout(baseUrl);
} }
private changeLocale(newLocale: string) {
this.keycloakService.login({kcLocale: newLocale });
}
} }