Includes in the kcContext missing realm defined translations #582

This commit is contained in:
Joseph Garrone
2024-07-10 22:18:24 +02:00
parent ca7821cfad
commit 24bb4902c2
12 changed files with 157 additions and 95 deletions

View File

@ -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;
};
}

View File

@ -162,8 +162,7 @@ export const kcContextCommonMock: KcContext.Common = {
isAppInitiatedAction: false,
properties: {},
"x-keycloakify": {
realmMessageBundleUserProfile: undefined,
realmMessageBundleTermsText: undefined
messages: {}
}
};

View File

@ -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 ? (

View File

@ -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>";
}

View File

@ -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")}>

View File

@ -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>
))}