Includes in the kcContext missing realm defined translations #582
This commit is contained in:
parent
ca7821cfad
commit
24bb4902c2
@ -42,37 +42,84 @@ if( kcContext.resourceUrl && !kcContext.url ){
|
||||
enumerable: false
|
||||
});
|
||||
}
|
||||
kcContext["x-keycloakify"] = {};
|
||||
kcContext["x-keycloakify"] = {
|
||||
messages: {}
|
||||
};
|
||||
<#if profile?? && profile.attributes??>
|
||||
kcContext["x-keycloakify"].realmMessageBundleUserProfile = {
|
||||
<#list profile.attributes as attribute>
|
||||
<#if attribute.annotations?? && attribute.displayName??>
|
||||
"${attribute.displayName}": decodeHtmlEntities("${advancedMsg(attribute.displayName)?js_string}"),
|
||||
</#if>
|
||||
<#if attribute.annotations.inputHelperTextBefore??>
|
||||
"${attribute.annotations.inputHelperTextBefore}": decodeHtmlEntities("${advancedMsg(attribute.annotations.inputHelperTextBefore)?js_string}"),
|
||||
</#if>
|
||||
<#if attribute.annotations.inputHelperTextAfter??>
|
||||
"${attribute.annotations.inputHelperTextAfter}": decodeHtmlEntities("${advancedMsg(attribute.annotations.inputHelperTextAfter)?js_string}"),
|
||||
</#if>
|
||||
<#if attribute.annotations.inputTypePlaceholder??>
|
||||
"${attribute.annotations.inputTypePlaceholder}": decodeHtmlEntities("${advancedMsg(attribute.annotations.inputTypePlaceholder)?js_string}"),
|
||||
</#if>
|
||||
<!-- Loop through the options that are in attribute.validators.options.options -->
|
||||
<#if (
|
||||
attribute.annotations.inputOptionLabelsI18nPrefix?? &&
|
||||
attribute.validators?? &&
|
||||
attribute.validators.options??
|
||||
)>
|
||||
<#list attribute.validators.options.options as option>
|
||||
"${attribute.annotations.inputOptionLabelsI18nPrefix}.${option}": decodeHtmlEntities("${msg(attribute.annotations.inputOptionLabelsI18nPrefix + "." + option)?js_string}"),
|
||||
</#list>
|
||||
</#if>
|
||||
</#list>
|
||||
};
|
||||
{
|
||||
var messages = {
|
||||
<#list profile.attributes as attribute>
|
||||
<#if attribute.displayName??>
|
||||
"${attribute.displayName}": decodeHtmlEntities("${advancedMsg(attribute.displayName)?js_string}"),
|
||||
</#if>
|
||||
<#if attribute.annotations??>
|
||||
<#if attribute.annotations.inputHelperTextBefore??>
|
||||
"${attribute.annotations.inputHelperTextBefore}": decodeHtmlEntities("${advancedMsg(attribute.annotations.inputHelperTextBefore)?js_string}"),
|
||||
</#if>
|
||||
<#if attribute.annotations.inputHelperTextAfter??>
|
||||
"${attribute.annotations.inputHelperTextAfter}": decodeHtmlEntities("${advancedMsg(attribute.annotations.inputHelperTextAfter)?js_string}"),
|
||||
</#if>
|
||||
<#if attribute.annotations.inputTypePlaceholder??>
|
||||
"${attribute.annotations.inputTypePlaceholder}": decodeHtmlEntities("${advancedMsg(attribute.annotations.inputTypePlaceholder)?js_string}"),
|
||||
</#if>
|
||||
<!-- Loop through the options that are in attribute.validators.options.options -->
|
||||
<#if (
|
||||
attribute.annotations.inputOptionLabelsI18nPrefix?? &&
|
||||
attribute.validators?? &&
|
||||
attribute.validators.options??
|
||||
)>
|
||||
<#list attribute.validators.options.options as option>
|
||||
"${attribute.annotations.inputOptionLabelsI18nPrefix}.${option}": decodeHtmlEntities("${msg(attribute.annotations.inputOptionLabelsI18nPrefix + "." + option)?js_string}"),
|
||||
</#list>
|
||||
</#if>
|
||||
</#if>
|
||||
</#list>
|
||||
};
|
||||
Object.assign(kcContext["x-keycloakify"].messages, messages);
|
||||
}
|
||||
</#if>
|
||||
<#if pageId == "terms.ftl" || termsAcceptanceRequired?? && termsAcceptanceRequired>
|
||||
kcContext["x-keycloakify"].realmMessageBundleTermsText= decodeHtmlEntities("${msg("termsText")?js_string}");
|
||||
kcContext["x-keycloakify"].messages["termsText"]= decodeHtmlEntities("${msg("termsText")?js_string}");
|
||||
</#if>
|
||||
<#if auth?? && auth.authenticationSelections??>
|
||||
{
|
||||
var messages = {
|
||||
<#list auth.authenticationSelections as authenticationSelection>
|
||||
<#if authenticationSelection.displayName??>
|
||||
"${authenticationSelection.displayName}": decodeHtmlEntities("${advancedMsg(authenticationSelection.displayName)?js_string}"),
|
||||
</#if>
|
||||
<#if authenticationSelection.helpText??>
|
||||
"${authenticationSelection.helpText}": decodeHtmlEntities("${advancedMsg(authenticationSelection.helpText)?js_string}"),
|
||||
</#if>
|
||||
</#list>
|
||||
};
|
||||
Object.assign(kcContext["x-keycloakify"].messages, messages);
|
||||
}
|
||||
</#if>
|
||||
<#if themeType == "login" && pageId == "info.ftl" && requiredActions??>
|
||||
{
|
||||
var messages = {
|
||||
<#list requiredActions as requiredAction>
|
||||
"requiredAction.${requiredAction}": decodeHtmlEntities("${advancedMsg("requiredAction." + requiredAction)?js_string}"),
|
||||
</#list>
|
||||
};
|
||||
Object.assign(kcContext["x-keycloakify"].messages, messages);
|
||||
}
|
||||
</#if>
|
||||
<#if authenticators?? && authenticators.authenticators??>
|
||||
{
|
||||
var messages = {
|
||||
<#list authenticators.authenticators as authenticator>
|
||||
"${authenticator.label}": decodeHtmlEntities("${advancedMsg(authenticator.label)?js_string}"),
|
||||
<#if authenticator.transports?? && authenticator.transports.displayNameProperties??>
|
||||
<#list authenticator.transports.displayNameProperties as displayNameProperty>
|
||||
"${displayNameProperty}": decodeHtmlEntities("${advancedMsg(displayNameProperty)?js_string}"),
|
||||
</#list>
|
||||
</#if>
|
||||
</#list>
|
||||
};
|
||||
Object.assign(kcContext["x-keycloakify"].messages, messages);
|
||||
}
|
||||
</#if>
|
||||
attributes_to_attributesByName: {
|
||||
if( !kcContext.profile ){
|
||||
|
@ -1,9 +1,8 @@
|
||||
import type { ThemeType, LoginThemePageId } from "keycloakify/bin/shared/constants";
|
||||
import type { ExtractAfterStartingWith } from "keycloakify/tools/ExtractAfterStartingWith";
|
||||
import type { ValueOf } from "keycloakify/tools/ValueOf";
|
||||
import { assert } from "tsafe/assert";
|
||||
import type { Equals } from "tsafe";
|
||||
import type { MessageKey } from "../i18n/i18n";
|
||||
import type { ClassKey } from "keycloakify/login/TemplateProps";
|
||||
|
||||
export type ExtendKcContext<
|
||||
KcContextExtension extends { properties?: Record<string, string | undefined> },
|
||||
@ -155,8 +154,7 @@ export declare namespace KcContext {
|
||||
};
|
||||
properties: {};
|
||||
"x-keycloakify": {
|
||||
realmMessageBundleUserProfile: Record<string, string> | undefined;
|
||||
realmMessageBundleTermsText: string | undefined;
|
||||
messages: Record<string, string>;
|
||||
};
|
||||
};
|
||||
|
||||
@ -221,7 +219,7 @@ export declare namespace KcContext {
|
||||
export type Info = Common & {
|
||||
pageId: "info.ftl";
|
||||
messageHeader?: string;
|
||||
requiredActions?: ExtractAfterStartingWith<"requiredAction.", MessageKey>[];
|
||||
requiredActions?: string[];
|
||||
skipLink: boolean;
|
||||
pageRedirectUri?: string;
|
||||
actionUri?: string;
|
||||
@ -384,7 +382,7 @@ export declare namespace KcContext {
|
||||
credentialId: string;
|
||||
transports: {
|
||||
iconClass: string;
|
||||
displayNameProperties?: MessageKey[];
|
||||
displayNameProperties?: string[];
|
||||
};
|
||||
label: string;
|
||||
createdAt: string;
|
||||
@ -501,26 +499,9 @@ export declare namespace KcContext {
|
||||
export namespace SelectAuthenticator {
|
||||
export type AuthenticationSelection = {
|
||||
authExecId: string;
|
||||
displayName:
|
||||
| "otp-display-name"
|
||||
| "password-display-name"
|
||||
| "auth-username-form-display-name"
|
||||
| "auth-username-password-form-display-name"
|
||||
| "webauthn-display-name"
|
||||
| "webauthn-passwordless-display-name";
|
||||
helpText:
|
||||
| "otp-help-text"
|
||||
| "password-help-text"
|
||||
| "auth-username-form-help-text"
|
||||
| "auth-username-password-form-help-text"
|
||||
| "webauthn-help-text"
|
||||
| "webauthn-passwordless-help-text";
|
||||
iconCssClass?:
|
||||
| "kcAuthenticatorDefaultClass"
|
||||
| "kcAuthenticatorPasswordClass"
|
||||
| "kcAuthenticatorOTPClass"
|
||||
| "kcAuthenticatorWebAuthnClass"
|
||||
| "kcAuthenticatorWebAuthnPasswordlessClass";
|
||||
displayName: string;
|
||||
helpText: string;
|
||||
iconCssClass?: ClassKey;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -162,8 +162,7 @@ export const kcContextCommonMock: KcContext.Common = {
|
||||
isAppInitiatedAction: false,
|
||||
properties: {},
|
||||
"x-keycloakify": {
|
||||
realmMessageBundleUserProfile: undefined,
|
||||
realmMessageBundleTermsText: undefined
|
||||
messages: {}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -11,8 +11,7 @@ export type KcContextLike = {
|
||||
supported: { languageTag: string; url: string; label: string }[];
|
||||
};
|
||||
"x-keycloakify": {
|
||||
realmMessageBundleUserProfile: Record<string, string> | undefined;
|
||||
realmMessageBundleTermsText: string | undefined;
|
||||
messages: Record<string, string>;
|
||||
};
|
||||
};
|
||||
|
||||
@ -131,8 +130,7 @@ export function createGetI18n<ExtraMessageKey extends string = never>(messageBun
|
||||
messages_fallbackLanguage,
|
||||
messageBundle_fallbackLanguage: messageBundle[fallbackLanguageTag],
|
||||
messageBundle_currentLanguage: messageBundle[partialI18n.currentLanguageTag],
|
||||
realmMessageBundleUserProfile: kcContext["x-keycloakify"].realmMessageBundleUserProfile,
|
||||
realmMessageBundleTermsText: kcContext["x-keycloakify"].realmMessageBundleTermsText
|
||||
messageBundle_realm: kcContext["x-keycloakify"].messages
|
||||
});
|
||||
|
||||
const isCurrentLanguageFallbackLanguage = partialI18n.currentLanguageTag === fallbackLanguageTag;
|
||||
@ -179,10 +177,9 @@ function createI18nTranslationFunctionsFactory<MessageKey extends string, ExtraM
|
||||
messages_fallbackLanguage: Record<MessageKey, string>;
|
||||
messageBundle_fallbackLanguage: Record<ExtraMessageKey, string> | undefined;
|
||||
messageBundle_currentLanguage: Partial<Record<ExtraMessageKey, string>> | undefined;
|
||||
realmMessageBundleUserProfile: Record<string, string> | undefined;
|
||||
realmMessageBundleTermsText: string | undefined;
|
||||
messageBundle_realm: Record<string, string>;
|
||||
}) {
|
||||
const { messageBundle_currentLanguage, realmMessageBundleUserProfile, realmMessageBundleTermsText } = params;
|
||||
const { messageBundle_currentLanguage, messageBundle_realm } = params;
|
||||
|
||||
const messages_fallbackLanguage = {
|
||||
...params.messages_fallbackLanguage,
|
||||
@ -201,12 +198,21 @@ function createI18nTranslationFunctionsFactory<MessageKey extends string, ExtraM
|
||||
const { key, args, doRenderAsHtml } = props;
|
||||
|
||||
const messageOrUndefined: string | undefined = (() => {
|
||||
const messageOrUndefined = (messages_currentLanguage as any)[key] ?? (messages_fallbackLanguage as any)[key];
|
||||
terms_text: {
|
||||
if (key !== "termsText") {
|
||||
break terms_text;
|
||||
}
|
||||
const termsTextMessage = messageBundle_realm[key];
|
||||
|
||||
if (key === "termsText" && realmMessageBundleTermsText !== undefined) {
|
||||
return realmMessageBundleTermsText;
|
||||
if (termsTextMessage === undefined) {
|
||||
break terms_text;
|
||||
}
|
||||
|
||||
return termsTextMessage;
|
||||
}
|
||||
|
||||
const messageOrUndefined = (messages_currentLanguage as any)[key] ?? (messages_fallbackLanguage as any)[key];
|
||||
|
||||
return messageOrUndefined;
|
||||
})();
|
||||
|
||||
@ -259,15 +265,11 @@ function createI18nTranslationFunctionsFactory<MessageKey extends string, ExtraM
|
||||
function resolveMsgAdvanced(props: { key: string; args: (string | undefined)[]; doRenderAsHtml: boolean }): JSX.Element | string {
|
||||
const { key, args, doRenderAsHtml } = props;
|
||||
|
||||
user_profile: {
|
||||
if (realmMessageBundleUserProfile === undefined) {
|
||||
break user_profile;
|
||||
}
|
||||
|
||||
const resolvedMessage = realmMessageBundleUserProfile[key] ?? realmMessageBundleUserProfile["${" + key + "}"];
|
||||
realm_messages: {
|
||||
const resolvedMessage = messageBundle_realm[key] ?? messageBundle_realm["${" + key + "}"];
|
||||
|
||||
if (resolvedMessage === undefined) {
|
||||
break user_profile;
|
||||
break realm_messages;
|
||||
}
|
||||
|
||||
return doRenderAsHtml ? (
|
||||
|
@ -5,7 +5,7 @@ import type { I18n } from "../i18n";
|
||||
export default function Info(props: PageProps<Extract<KcContext, { pageId: "info.ftl" }>, I18n>) {
|
||||
const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
|
||||
|
||||
const { msgStr, msg } = i18n;
|
||||
const { advancedMsgStr, msg } = i18n;
|
||||
|
||||
const { messageHeader, message, requiredActions, skipLink, pageRedirectUri, actionUri, client } = kcContext;
|
||||
|
||||
@ -34,7 +34,7 @@ export default function Info(props: PageProps<Extract<KcContext, { pageId: "info
|
||||
if (requiredActions) {
|
||||
html += "<b>";
|
||||
|
||||
html += requiredActions.map(requiredAction => msgStr(`requiredAction.${requiredAction}` as const)).join(",");
|
||||
html += requiredActions.map(requiredAction => advancedMsgStr(`requiredAction.${requiredAction}`)).join(",");
|
||||
|
||||
html += "</b>";
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ export default function SelectAuthenticator(props: PageProps<Extract<KcContext,
|
||||
const { url, auth } = kcContext;
|
||||
|
||||
const { kcClsx } = getKcClsx({ doUseDefaultCss, classes });
|
||||
const { msg } = i18n;
|
||||
const { msg, advancedMsg } = i18n;
|
||||
|
||||
return (
|
||||
<Template
|
||||
@ -30,11 +30,11 @@ export default function SelectAuthenticator(props: PageProps<Extract<KcContext,
|
||||
value={authenticationSelection.authExecId}
|
||||
>
|
||||
<div className={kcClsx("kcSelectAuthListItemIconClass")}>
|
||||
<i className={kcClsx(authenticationSelection.iconCssClass, "kcSelectAuthListItemIconPropertyClass")} />
|
||||
<i className={kcClsx("kcSelectAuthListItemIconPropertyClass", authenticationSelection.iconCssClass)} />
|
||||
</div>
|
||||
<div className={kcClsx("kcSelectAuthListItemBodyClass")}>
|
||||
<div className={kcClsx("kcSelectAuthListItemHeadingClass")}>{msg(authenticationSelection.displayName)}</div>
|
||||
<div className={kcClsx("kcSelectAuthListItemDescriptionClass")}>{msg(authenticationSelection.helpText)}</div>
|
||||
<div className={kcClsx("kcSelectAuthListItemHeadingClass")}>{advancedMsg(authenticationSelection.displayName)}</div>
|
||||
<div className={kcClsx("kcSelectAuthListItemDescriptionClass")}>{advancedMsg(authenticationSelection.helpText)}</div>
|
||||
</div>
|
||||
<div className={kcClsx("kcSelectAuthListItemFillClass")} />
|
||||
<div className={kcClsx("kcSelectAuthListItemArrowClass")}>
|
||||
|
@ -204,13 +204,13 @@ export default function WebauthnAuthenticate(props: PageProps<Extract<KcContext,
|
||||
className={kcClsx("kcSelectAuthListItemDescriptionClass")}
|
||||
>
|
||||
{authenticator.transports.displayNameProperties
|
||||
.map((nameProperty, i, arr) => ({
|
||||
nameProperty,
|
||||
.map((displayNameProperty, i, arr) => ({
|
||||
displayNameProperty,
|
||||
hasNext: i !== arr.length - 1
|
||||
}))
|
||||
.map(({ nameProperty, hasNext }) => (
|
||||
<Fragment key={nameProperty}>
|
||||
<span>{msg(nameProperty)}</span>
|
||||
.map(({ displayNameProperty, hasNext }) => (
|
||||
<Fragment key={displayNameProperty}>
|
||||
{advancedMsg(displayNameProperty)}
|
||||
{hasNext && <span>, </span>}
|
||||
</Fragment>
|
||||
))}
|
||||
|
@ -1,4 +0,0 @@
|
||||
export type ExtractAfterStartingWith<
|
||||
Prefix extends string,
|
||||
StrEnum
|
||||
> = StrEnum extends `${Prefix}${infer U}` ? U : never;
|
@ -43,9 +43,14 @@ export const WithRequiredActions: Story = {
|
||||
<KcPageStory
|
||||
kcContext={{
|
||||
message: {
|
||||
summary: "Server message"
|
||||
summary: "Required actions: "
|
||||
},
|
||||
requiredActions: ["CONFIGURE_TOTP", "UPDATE_PROFILE", "VERIFY_EMAIL"]
|
||||
requiredActions: ["CONFIGURE_TOTP", "UPDATE_PROFILE", "VERIFY_EMAIL", "CUSTOM_ACTION"],
|
||||
"x-keycloakify": {
|
||||
messages: {
|
||||
"requiredAction.CUSTOM_ACTION": "Custom action"
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
|
@ -69,7 +69,7 @@ export const WithRestrictedToMITStudents: Story = {
|
||||
}
|
||||
},
|
||||
"x-keycloakify": {
|
||||
realmMessageBundleUserProfile: {
|
||||
messages: {
|
||||
"${profile.attributes.email.inputHelperTextBefore}": "Please use your MIT or Berkeley email.",
|
||||
"${profile.attributes.email.pattern.error}":
|
||||
"This is not an MIT (<strong>@mit.edu</strong>) nor a Berkeley (<strong>@berkeley.edu</strong>) email."
|
||||
@ -103,7 +103,7 @@ export const WithFavoritePet: Story = {
|
||||
}
|
||||
},
|
||||
"x-keycloakify": {
|
||||
realmMessageBundleUserProfile: {
|
||||
messages: {
|
||||
"${profile.attributes.favoritePet}": "Favorite Pet",
|
||||
"${profile.attributes.favoritePet.options.cat}": "Fluffy Cat",
|
||||
"${profile.attributes.favoritePet.options.dog}": "Loyal Dog",
|
||||
@ -177,7 +177,9 @@ export const WithTermsAcceptance: Story = {
|
||||
kcContext={{
|
||||
termsAcceptanceRequired: true,
|
||||
"x-keycloakify": {
|
||||
realmMessageBundleTermsText: "<a href='https://example.com/terms'>Service Terms of Use</a>"
|
||||
messages: {
|
||||
termsText: "<a href='https://example.com/terms'>Service Terms of Use</a>"
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
@ -41,3 +41,29 @@ export const WithDifferentAuthenticationMethods: Story = {
|
||||
/>
|
||||
)
|
||||
};
|
||||
|
||||
export const WithRealmTranslations: Story = {
|
||||
render: () => (
|
||||
<KcPageStory
|
||||
kcContext={{
|
||||
auth: {
|
||||
authenticationSelections: [
|
||||
{
|
||||
authExecId: "f0c22855-eda7-4092-8565-0c22f77d2ffb",
|
||||
displayName: "home-idp-discovery-display-name",
|
||||
helpText: "home-idp-discovery-help-text",
|
||||
iconCssClass: "kcAuthenticatorDefaultClass"
|
||||
}
|
||||
]
|
||||
},
|
||||
["x-keycloakify"]: {
|
||||
messages: {
|
||||
"${home-idp-discovery-display-name}": "Home identity provider",
|
||||
"${home-idp-discovery-help-text}":
|
||||
"Sign in via your home identity provider which will be automatically determined based on your provided email address."
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)
|
||||
};
|
||||
|
@ -18,7 +18,9 @@ export const Default: Story = {
|
||||
<KcPageStory
|
||||
kcContext={{
|
||||
"x-keycloakify": {
|
||||
realmMessageBundleTermsText: "<p>My terms in <strong>English</strong></p>"
|
||||
messages: {
|
||||
termsText: "<p>My terms in <strong>English</strong></p>"
|
||||
}
|
||||
}
|
||||
}}
|
||||
/>
|
||||
@ -34,7 +36,9 @@ export const French: Story = {
|
||||
},
|
||||
"x-keycloakify": {
|
||||
// cSpell: disable
|
||||
realmMessageBundleTermsText: "<p>Mes terme en <strong>Français</strong></p>"
|
||||
messages: {
|
||||
termsText: "<p>Mes terme en <strong>Français</strong></p>"
|
||||
}
|
||||
// cSpell: enable
|
||||
}
|
||||
}}
|
||||
|
Loading…
x
Reference in New Issue
Block a user