keycloakify 10.0.0-rc.37 → 10.0.0-rc.39
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/account/Fallback.d.ts +1 -2
- package/account/Fallback.js.map +1 -1
- package/account/KcContext/KcContext.d.ts +6 -6
- package/account/KcContext/getKcContextMock.d.ts +9 -9
- package/account/KcContext/getKcContextMock.js +3 -3
- package/account/KcContext/getKcContextMock.js.map +1 -1
- package/account/Template.d.ts +1 -2
- package/account/Template.js +7 -6
- package/account/Template.js.map +1 -1
- package/account/TemplateProps.d.ts +1 -3
- package/account/i18n/i18n.d.ts +9 -4
- package/account/i18n/i18n.js +132 -87
- package/account/i18n/i18n.js.map +1 -1
- package/account/i18n/index.d.ts +9 -2
- package/account/i18n/index.js +5 -1
- package/account/i18n/index.js.map +1 -1
- package/account/lib/kcClsx.d.ts +9 -0
- package/account/lib/{useGetClassName.js → kcClsx.js} +3 -3
- package/account/lib/kcClsx.js.map +1 -0
- package/account/pages/Account.d.ts +1 -2
- package/account/pages/Account.js +9 -7
- package/account/pages/Account.js.map +1 -1
- package/account/pages/Applications.d.ts +1 -2
- package/account/pages/Applications.js +7 -7
- package/account/pages/Applications.js.map +1 -1
- package/account/pages/FederatedIdentity.d.ts +1 -2
- package/account/pages/FederatedIdentity.js +4 -3
- package/account/pages/FederatedIdentity.js.map +1 -1
- package/account/pages/Log.d.ts +1 -2
- package/account/pages/Log.js +6 -5
- package/account/pages/Log.js.map +1 -1
- package/account/pages/PageProps.d.ts +2 -4
- package/account/pages/Password.d.ts +1 -2
- package/account/pages/Password.js +10 -9
- package/account/pages/Password.js.map +1 -1
- package/account/pages/Sessions.d.ts +1 -2
- package/account/pages/Sessions.js +6 -6
- package/account/pages/Sessions.js.map +1 -1
- package/account/pages/Totp.d.ts +1 -2
- package/account/pages/Totp.js +6 -5
- package/account/pages/Totp.js.map +1 -1
- package/bin/193.index.js +13 -13
- package/bin/{430.index.js → 214.index.js} +161 -2
- package/bin/3.index.js +165 -149
- package/bin/322.index.js +595 -0
- package/bin/453.index.js +3 -3
- package/bin/526.index.js +42 -632
- package/bin/538.index.js +563 -0
- package/bin/932.index.js +7 -7
- package/bin/{890.index.js → 941.index.js} +2 -159
- package/bin/944.index.js +621 -0
- package/bin/961.index.js +5 -5
- package/bin/97.index.js +3 -3
- package/bin/{314.index.js → 98.index.js} +217 -48
- package/bin/main.js +20 -8
- package/lib/getKcClsx.d.ts +11 -0
- package/lib/getKcClsx.js +55 -0
- package/lib/getKcClsx.js.map +1 -0
- package/login/Fallback.d.ts +2 -2
- package/login/Fallback.js.map +1 -1
- package/login/KcContext/KcContext.d.ts +6 -6
- package/login/KcContext/getKcContextMock.d.ts +9 -9
- package/login/KcContext/getKcContextMock.js +3 -3
- package/login/KcContext/getKcContextMock.js.map +1 -1
- package/login/Template.d.ts +1 -2
- package/login/Template.js +10 -9
- package/login/Template.js.map +1 -1
- package/login/TemplateProps.d.ts +1 -3
- package/login/UserProfileFormFields.d.ts +6 -5
- package/login/UserProfileFormFields.js +35 -36
- package/login/UserProfileFormFields.js.map +1 -1
- package/login/i18n/i18n.d.ts +9 -4
- package/login/i18n/i18n.js +136 -91
- package/login/i18n/i18n.js.map +1 -1
- package/login/i18n/index.d.ts +9 -2
- package/login/i18n/index.js +5 -1
- package/login/i18n/index.js.map +1 -1
- package/login/lib/kcClsx.d.ts +9 -0
- package/login/lib/{useGetClassName.js → kcClsx.js} +3 -3
- package/login/lib/kcClsx.js.map +1 -0
- package/login/lib/useUserProfileForm.d.ts +9 -6
- package/login/lib/useUserProfileForm.js +7 -5
- package/login/lib/useUserProfileForm.js.map +1 -1
- package/login/pages/Code.d.ts +1 -2
- package/login/pages/Code.js +6 -5
- package/login/pages/Code.js.map +1 -1
- package/login/pages/DeleteAccountConfirm.d.ts +1 -2
- package/login/pages/DeleteAccountConfirm.js +7 -7
- package/login/pages/DeleteAccountConfirm.js.map +1 -1
- package/login/pages/DeleteCredential.d.ts +1 -2
- package/login/pages/DeleteCredential.js +6 -6
- package/login/pages/DeleteCredential.js.map +1 -1
- package/login/pages/Error.d.ts +1 -2
- package/login/pages/Error.js +4 -3
- package/login/pages/Error.js.map +1 -1
- package/login/pages/FrontchannelLogout.d.ts +1 -2
- package/login/pages/FrontchannelLogout.js +4 -3
- package/login/pages/FrontchannelLogout.js.map +1 -1
- package/login/pages/IdpReviewUserProfile.d.ts +2 -2
- package/login/pages/IdpReviewUserProfile.js +6 -6
- package/login/pages/IdpReviewUserProfile.js.map +1 -1
- package/login/pages/Info.d.ts +1 -2
- package/login/pages/Info.js +4 -3
- package/login/pages/Info.js.map +1 -1
- package/login/pages/Login.d.ts +1 -2
- package/login/pages/Login.js +10 -8
- package/login/pages/Login.js.map +1 -1
- package/login/pages/LoginConfigTotp.d.ts +1 -2
- package/login/pages/LoginConfigTotp.js +8 -7
- package/login/pages/LoginConfigTotp.js.map +1 -1
- package/login/pages/LoginIdpLinkConfirm.d.ts +1 -2
- package/login/pages/LoginIdpLinkConfirm.js +6 -6
- package/login/pages/LoginIdpLinkConfirm.js.map +1 -1
- package/login/pages/LoginIdpLinkEmail.d.ts +1 -2
- package/login/pages/LoginIdpLinkEmail.js +4 -3
- package/login/pages/LoginIdpLinkEmail.js.map +1 -1
- package/login/pages/LoginOauth2DeviceVerifyUserCode.d.ts +1 -2
- package/login/pages/LoginOauth2DeviceVerifyUserCode.js +6 -6
- package/login/pages/LoginOauth2DeviceVerifyUserCode.js.map +1 -1
- package/login/pages/LoginOauthGrant.d.ts +1 -2
- package/login/pages/LoginOauthGrant.js +7 -7
- package/login/pages/LoginOauthGrant.js.map +1 -1
- package/login/pages/LoginOtp.d.ts +1 -2
- package/login/pages/LoginOtp.js +6 -6
- package/login/pages/LoginOtp.js.map +1 -1
- package/login/pages/LoginPageExpired.d.ts +1 -2
- package/login/pages/LoginPageExpired.js +4 -3
- package/login/pages/LoginPageExpired.js.map +1 -1
- package/login/pages/LoginPassword.d.ts +1 -2
- package/login/pages/LoginPassword.js +9 -7
- package/login/pages/LoginPassword.js.map +1 -1
- package/login/pages/LoginRecoveryAuthnCodeConfig.d.ts +1 -2
- package/login/pages/LoginRecoveryAuthnCodeConfig.js +9 -7
- package/login/pages/LoginRecoveryAuthnCodeConfig.js.map +1 -1
- package/login/pages/LoginRecoveryAuthnCodeInput.d.ts +1 -2
- package/login/pages/LoginRecoveryAuthnCodeInput.js +6 -6
- package/login/pages/LoginRecoveryAuthnCodeInput.js.map +1 -1
- package/login/pages/LoginResetOtp.d.ts +1 -2
- package/login/pages/LoginResetOtp.js +6 -6
- package/login/pages/LoginResetOtp.js.map +1 -1
- package/login/pages/LoginResetPassword.d.ts +1 -2
- package/login/pages/LoginResetPassword.js +7 -7
- package/login/pages/LoginResetPassword.js.map +1 -1
- package/login/pages/LoginUpdatePassword.d.ts +1 -2
- package/login/pages/LoginUpdatePassword.js +10 -9
- package/login/pages/LoginUpdatePassword.js.map +1 -1
- package/login/pages/LoginUpdateProfile.d.ts +2 -2
- package/login/pages/LoginUpdateProfile.js +7 -12
- package/login/pages/LoginUpdateProfile.js.map +1 -1
- package/login/pages/LoginUsername.d.ts +1 -2
- package/login/pages/LoginUsername.js +8 -7
- package/login/pages/LoginUsername.js.map +1 -1
- package/login/pages/LoginVerifyEmail.d.ts +1 -2
- package/login/pages/LoginVerifyEmail.js +4 -3
- package/login/pages/LoginVerifyEmail.js.map +1 -1
- package/login/pages/LoginX509Info.d.ts +1 -2
- package/login/pages/LoginX509Info.js +6 -6
- package/login/pages/LoginX509Info.js.map +1 -1
- package/login/pages/LogoutConfirm.d.ts +1 -2
- package/login/pages/LogoutConfirm.js +6 -6
- package/login/pages/LogoutConfirm.js.map +1 -1
- package/login/pages/PageProps.d.ts +2 -4
- package/login/pages/Register.d.ts +2 -2
- package/login/pages/Register.js +8 -16
- package/login/pages/Register.js.map +1 -1
- package/login/pages/SamlPostForm.d.ts +1 -2
- package/login/pages/SamlPostForm.js +4 -3
- package/login/pages/SamlPostForm.js.map +1 -1
- package/login/pages/SelectAuthenticator.d.ts +1 -2
- package/login/pages/SelectAuthenticator.js +6 -9
- package/login/pages/SelectAuthenticator.js.map +1 -1
- package/login/pages/Terms.d.ts +1 -2
- package/login/pages/Terms.js +6 -6
- package/login/pages/Terms.js.map +1 -1
- package/login/pages/UpdateEmail.d.ts +2 -2
- package/login/pages/UpdateEmail.js +8 -12
- package/login/pages/UpdateEmail.js.map +1 -1
- package/login/pages/WebauthnAuthenticate.d.ts +1 -2
- package/login/pages/WebauthnAuthenticate.js +13 -12
- package/login/pages/WebauthnAuthenticate.js.map +1 -1
- package/login/pages/WebauthnError.d.ts +1 -2
- package/login/pages/WebauthnError.js +7 -7
- package/login/pages/WebauthnError.js.map +1 -1
- package/login/pages/WebauthnRegister.d.ts +1 -2
- package/login/pages/WebauthnRegister.js +9 -8
- package/login/pages/WebauthnRegister.js.map +1 -1
- package/package.json +27 -22
- package/src/account/Fallback.tsx +1 -2
- package/src/account/KcContext/KcContext.ts +7 -7
- package/src/account/KcContext/getKcContextMock.ts +13 -24
- package/src/account/Template.tsx +8 -8
- package/src/account/TemplateProps.ts +1 -6
- package/src/account/i18n/i18n.tsx +204 -125
- package/src/account/i18n/index.ts +10 -2
- package/src/account/lib/{useGetClassName.ts → kcClsx.ts} +6 -2
- package/src/account/pages/Account.tsx +15 -21
- package/src/account/pages/Applications.tsx +8 -9
- package/src/account/pages/FederatedIdentity.tsx +5 -5
- package/src/account/pages/Log.tsx +8 -8
- package/src/account/pages/PageProps.ts +2 -4
- package/src/account/pages/Password.tsx +13 -16
- package/src/account/pages/Sessions.tsx +9 -10
- package/src/account/pages/Totp.tsx +19 -28
- package/src/bin/add-story.ts +3 -3
- package/src/bin/copy-keycloak-resources-to-public.ts +3 -3
- package/src/bin/download-keycloak-default-theme.ts +5 -5
- package/src/bin/eject-page.ts +3 -3
- package/src/bin/initialize-email-theme.ts +5 -5
- package/src/bin/keycloakify/buildJars/buildJar.ts +14 -14
- package/src/bin/keycloakify/buildJars/buildJars.ts +8 -8
- package/src/bin/keycloakify/buildJars/generatePom.ts +9 -9
- package/src/bin/keycloakify/generateFtl/generateFtl.ts +12 -12
- package/src/bin/keycloakify/generateSrcMainResources/bringInAccountV1.ts +7 -7
- package/src/bin/keycloakify/generateSrcMainResources/generateMessageProperties.ts +2 -66
- package/src/bin/keycloakify/generateSrcMainResources/generateSrcMainResources.ts +9 -9
- package/src/bin/keycloakify/generateSrcMainResources/generateSrcMainResourcesForMainTheme.ts +30 -24
- package/src/bin/keycloakify/generateSrcMainResources/generateSrcMainResourcesForThemeVariant.ts +8 -8
- package/src/bin/keycloakify/generateStartKeycloakTestingContainer.ts +9 -9
- package/src/bin/keycloakify/keycloakify.ts +11 -11
- package/src/bin/keycloakify/replacers/replaceImportsInCssCode.ts +6 -6
- package/src/bin/keycloakify/replacers/replaceImportsInInlineCssCode.ts +7 -7
- package/src/bin/keycloakify/replacers/replaceImportsInJsCode/replaceImportsInJsCode.ts +10 -10
- package/src/bin/keycloakify/replacers/replaceImportsInJsCode/vite.ts +13 -13
- package/src/bin/keycloakify/replacers/replaceImportsInJsCode/webpack.ts +11 -11
- package/src/bin/main.ts +17 -2
- package/src/bin/shared/{buildOptions.ts → buildContext.ts} +65 -51
- package/src/bin/shared/copyKeycloakResourcesToPublic.ts +12 -12
- package/src/bin/shared/downloadKeycloakDefaultTheme.ts +7 -7
- package/src/bin/shared/downloadKeycloakStaticResources.ts +7 -7
- package/src/bin/shared/generateKcGenTs.ts +61 -0
- package/src/bin/shared/getThemeSrcDirPath.ts +3 -3
- package/src/bin/start-keycloak/appBuild.ts +11 -11
- package/src/bin/start-keycloak/keycloakifyBuild.ts +7 -7
- package/src/bin/start-keycloak/start-keycloak.ts +34 -22
- package/src/bin/tools/escapeStringForPropertiesFile.ts +64 -0
- package/src/bin/tools/getNpmWorkspaceRootDirPath.ts +3 -3
- package/src/bin/update-kc-gen.ts +13 -0
- package/src/lib/getKcClsx.ts +89 -0
- package/src/login/Fallback.tsx +2 -2
- package/src/login/KcContext/KcContext.ts +7 -7
- package/src/login/KcContext/getKcContextMock.ts +13 -24
- package/src/login/Template.tsx +36 -37
- package/src/login/TemplateProps.ts +1 -6
- package/src/login/UserProfileFormFields.tsx +67 -84
- package/src/login/i18n/i18n.tsx +208 -129
- package/src/login/i18n/index.ts +10 -2
- package/src/login/lib/{useGetClassName.ts → kcClsx.ts} +6 -2
- package/src/login/lib/useUserProfileForm.tsx +29 -21
- package/src/login/pages/Code.tsx +10 -8
- package/src/login/pages/DeleteAccountConfirm.tsx +9 -10
- package/src/login/pages/DeleteCredential.tsx +11 -10
- package/src/login/pages/Error.tsx +5 -5
- package/src/login/pages/FrontchannelLogout.tsx +7 -5
- package/src/login/pages/IdpReviewUserProfile.tsx +18 -21
- package/src/login/pages/Info.tsx +7 -5
- package/src/login/pages/Login.tsx +34 -53
- package/src/login/pages/LoginConfigTotp.tsx +30 -38
- package/src/login/pages/LoginIdpLinkConfirm.tsx +10 -21
- package/src/login/pages/LoginIdpLinkEmail.tsx +5 -5
- package/src/login/pages/LoginOauth2DeviceVerifyUserCode.tsx +19 -24
- package/src/login/pages/LoginOauthGrant.tsx +14 -21
- package/src/login/pages/LoginOtp.tsx +29 -33
- package/src/login/pages/LoginPageExpired.tsx +5 -5
- package/src/login/pages/LoginPassword.tsx +23 -33
- package/src/login/pages/LoginRecoveryAuthnCodeConfig.tsx +21 -25
- package/src/login/pages/LoginRecoveryAuthnCodeInput.tsx +21 -25
- package/src/login/pages/LoginResetOtp.tsx +21 -25
- package/src/login/pages/LoginResetPassword.tsx +21 -25
- package/src/login/pages/LoginUpdatePassword.tsx +42 -52
- package/src/login/pages/LoginUpdateProfile.tsx +29 -30
- package/src/login/pages/LoginUsername.tsx +23 -35
- package/src/login/pages/LoginVerifyEmail.tsx +7 -5
- package/src/login/pages/LoginX509Info.tsx +27 -36
- package/src/login/pages/LogoutConfirm.tsx +11 -17
- package/src/login/pages/PageProps.ts +2 -4
- package/src/login/pages/Register.tsx +27 -46
- package/src/login/pages/SamlPostForm.tsx +5 -5
- package/src/login/pages/SelectAuthenticator.tsx +24 -26
- package/src/login/pages/Terms.tsx +9 -16
- package/src/login/pages/UpdateEmail.tsx +29 -33
- package/src/login/pages/WebauthnAuthenticate.tsx +26 -32
- package/src/login/pages/WebauthnError.tsx +11 -22
- package/src/login/pages/WebauthnRegister.tsx +20 -28
- package/src/tools/clsx.ts +6 -48
- package/src/tools/clsx_withTransform.ts +55 -0
- package/src/vite-plugin/vite-plugin.ts +29 -21
- package/tools/clsx.d.ts +3 -2
- package/tools/clsx.js +5 -41
- package/tools/clsx.js.map +1 -1
- package/tools/clsx_withTransform.d.ts +5 -0
- package/tools/clsx_withTransform.js +43 -0
- package/tools/clsx_withTransform.js.map +1 -0
- package/vite-plugin/index.js +243 -102
- package/vite-plugin/vite-plugin.d.ts +3 -3
- package/account/lib/useGetClassName.d.ts +0 -7
- package/account/lib/useGetClassName.js.map +0 -1
- package/bin/795.index.js +0 -1197
- package/lib/isStorybook.d.ts +0 -1
- package/lib/isStorybook.js +0 -3
- package/lib/isStorybook.js.map +0 -1
- package/lib/useGetClassName.d.ts +0 -10
- package/lib/useGetClassName.js +0 -14
- package/lib/useGetClassName.js.map +0 -1
- package/login/lib/useGetClassName.d.ts +0 -7
- package/login/lib/useGetClassName.js.map +0 -1
- package/src/lib/isStorybook.ts +0 -3
- package/src/lib/useGetClassName.ts +0 -27
package/src/login/i18n/i18n.tsx
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
import "keycloakify/tools/Object.fromEntries";
|
2
|
-
import { useEffect, useState
|
2
|
+
import { useEffect, useState } from "react";
|
3
3
|
import { assert } from "tsafe/assert";
|
4
|
-
import
|
4
|
+
import messages_fallbackLanguage from "./baseMessages/en";
|
5
5
|
import { getMessages } from "./baseMessages";
|
6
6
|
import type { KcContext } from "../KcContext";
|
7
7
|
import { Reflect } from "tsafe/Reflect";
|
@@ -18,7 +18,7 @@ export type KcContextLike = {
|
|
18
18
|
|
19
19
|
assert<KcContext extends KcContextLike ? true : false>();
|
20
20
|
|
21
|
-
export type MessageKey = keyof typeof
|
21
|
+
export type MessageKey = keyof typeof messages_fallbackLanguage;
|
22
22
|
|
23
23
|
export type GenericI18n<MessageKey extends string> = {
|
24
24
|
/**
|
@@ -80,183 +80,262 @@ export type GenericI18n<MessageKey extends string> = {
|
|
80
80
|
* See advancedMsg() but instead of returning a JSX.Element it returns a string.
|
81
81
|
*/
|
82
82
|
advancedMsgStr: (key: string, ...args: (string | undefined)[]) => string;
|
83
|
+
|
84
|
+
/**
|
85
|
+
* Initially the messages are in english (fallback language).
|
86
|
+
* The translations in the current language are being fetched dynamically.
|
87
|
+
* This property is true while the translations are being fetched.
|
88
|
+
*/
|
89
|
+
isFetchingTranslations: boolean;
|
83
90
|
};
|
84
91
|
|
85
|
-
|
92
|
+
function createGetI18n<ExtraMessageKey extends string = never>(extraMessages: { [languageTag: string]: { [key in ExtraMessageKey]: string } }) {
|
93
|
+
type I18n = GenericI18n<MessageKey | ExtraMessageKey>;
|
94
|
+
|
95
|
+
type Result = { i18n: I18n; prI18n_currentLanguage: Promise<I18n> | undefined };
|
96
|
+
|
97
|
+
const cachedResultByKcContext = new WeakMap<KcContextLike, Result>();
|
98
|
+
|
99
|
+
function getI18n(params: { kcContext: KcContextLike }): Result {
|
100
|
+
const { kcContext } = params;
|
101
|
+
|
102
|
+
use_cache: {
|
103
|
+
const cachedResult = cachedResultByKcContext.get(kcContext);
|
104
|
+
|
105
|
+
if (cachedResult === undefined) {
|
106
|
+
break use_cache;
|
107
|
+
}
|
108
|
+
|
109
|
+
return cachedResult;
|
110
|
+
}
|
111
|
+
|
112
|
+
const partialI18n: Pick<I18n, "currentLanguageTag" | "getChangeLocalUrl" | "labelBySupportedLanguageTag"> = {
|
113
|
+
currentLanguageTag: kcContext.locale?.currentLanguageTag ?? fallbackLanguageTag,
|
114
|
+
getChangeLocalUrl: newLanguageTag => {
|
115
|
+
const { locale } = kcContext;
|
116
|
+
|
117
|
+
assert(locale !== undefined, "Internationalization not enabled");
|
118
|
+
|
119
|
+
const targetSupportedLocale = locale.supported.find(({ languageTag }) => languageTag === newLanguageTag);
|
120
|
+
|
121
|
+
assert(targetSupportedLocale !== undefined, `${newLanguageTag} need to be enabled in Keycloak admin`);
|
122
|
+
|
123
|
+
return targetSupportedLocale.url;
|
124
|
+
},
|
125
|
+
labelBySupportedLanguageTag: Object.fromEntries((kcContext.locale?.supported ?? []).map(({ languageTag, label }) => [languageTag, label]))
|
126
|
+
};
|
127
|
+
|
128
|
+
const { createI18nTranslationFunctions } = createI18nTranslationFunctionsFactory<MessageKey, ExtraMessageKey>({
|
129
|
+
messages_fallbackLanguage,
|
130
|
+
extraMessages_fallbackLanguage: extraMessages[fallbackLanguageTag],
|
131
|
+
extraMessages: extraMessages[partialI18n.currentLanguageTag],
|
132
|
+
__localizationRealmOverridesUserProfile: kcContext.__localizationRealmOverridesUserProfile
|
133
|
+
});
|
134
|
+
|
135
|
+
const isCurrentLanguageFallbackLanguage = partialI18n.currentLanguageTag === fallbackLanguageTag;
|
136
|
+
|
137
|
+
const result: Result = {
|
138
|
+
i18n: {
|
139
|
+
...partialI18n,
|
140
|
+
...createI18nTranslationFunctions({ messages: undefined }),
|
141
|
+
isFetchingTranslations: !isCurrentLanguageFallbackLanguage
|
142
|
+
},
|
143
|
+
prI18n_currentLanguage: isCurrentLanguageFallbackLanguage
|
144
|
+
? undefined
|
145
|
+
: (async () => {
|
146
|
+
const messages = await getMessages(partialI18n.currentLanguageTag);
|
147
|
+
|
148
|
+
const i18n_currentLanguage: I18n = {
|
149
|
+
...partialI18n,
|
150
|
+
...createI18nTranslationFunctions({ messages }),
|
151
|
+
isFetchingTranslations: false
|
152
|
+
};
|
153
|
+
|
154
|
+
// NOTE: This promise.resolve is just because without it we TypeScript
|
155
|
+
// gives a Variable 'result' is used before being assigned. error
|
156
|
+
await Promise.resolve().then(() => {
|
157
|
+
result.i18n = i18n_currentLanguage;
|
158
|
+
result.prI18n_currentLanguage = undefined;
|
159
|
+
});
|
160
|
+
|
161
|
+
return i18n_currentLanguage;
|
162
|
+
})()
|
163
|
+
};
|
164
|
+
|
165
|
+
cachedResultByKcContext.set(kcContext, result);
|
166
|
+
|
167
|
+
return result;
|
168
|
+
}
|
169
|
+
|
170
|
+
return { getI18n };
|
171
|
+
}
|
86
172
|
|
87
173
|
export function createUseI18n<ExtraMessageKey extends string = never>(extraMessages: {
|
88
174
|
[languageTag: string]: { [key in ExtraMessageKey]: string };
|
89
175
|
}) {
|
90
|
-
|
176
|
+
type I18n = GenericI18n<MessageKey | ExtraMessageKey>;
|
177
|
+
|
178
|
+
const { getI18n } = createGetI18n(extraMessages);
|
179
|
+
|
180
|
+
function useI18n(params: { kcContext: KcContextLike }): I18n {
|
91
181
|
const { kcContext } = params;
|
92
182
|
|
93
|
-
const
|
183
|
+
const { i18n, prI18n_currentLanguage } = getI18n({ kcContext });
|
94
184
|
|
95
|
-
const
|
185
|
+
const [i18n_toReturn, setI18n_toReturn] = useState<I18n>(i18n);
|
96
186
|
|
97
187
|
useEffect(() => {
|
98
|
-
|
99
|
-
return;
|
100
|
-
}
|
188
|
+
let isActive = true;
|
101
189
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
messages: {
|
114
|
-
...(await getMessages(currentLanguageTag)),
|
115
|
-
...(extraMessages[currentLanguageTag] ?? {})
|
116
|
-
} as any,
|
117
|
-
__localizationRealmOverridesUserProfile: kcContext.__localizationRealmOverridesUserProfile
|
118
|
-
}),
|
119
|
-
currentLanguageTag,
|
120
|
-
getChangeLocalUrl: newLanguageTag => {
|
121
|
-
const { locale } = kcContext;
|
122
|
-
|
123
|
-
assert(locale !== undefined, "Internationalization not enabled");
|
124
|
-
|
125
|
-
const targetSupportedLocale = locale.supported.find(({ languageTag }) => languageTag === newLanguageTag);
|
126
|
-
|
127
|
-
assert(targetSupportedLocale !== undefined, `${newLanguageTag} need to be enabled in Keycloak admin`);
|
128
|
-
|
129
|
-
return targetSupportedLocale.url;
|
130
|
-
},
|
131
|
-
labelBySupportedLanguageTag: Object.fromEntries(
|
132
|
-
(kcContext.locale?.supported ?? []).map(({ languageTag, label }) => [languageTag, label])
|
133
|
-
)
|
134
|
-
});
|
135
|
-
})();
|
190
|
+
prI18n_currentLanguage?.then(i18n => {
|
191
|
+
if (!isActive) {
|
192
|
+
return;
|
193
|
+
}
|
194
|
+
|
195
|
+
setI18n_toReturn(i18n);
|
196
|
+
});
|
197
|
+
|
198
|
+
return () => {
|
199
|
+
isActive = false;
|
200
|
+
};
|
136
201
|
}, []);
|
137
202
|
|
138
|
-
return
|
203
|
+
return i18n_toReturn;
|
139
204
|
}
|
140
205
|
|
141
|
-
return {
|
142
|
-
useI18n,
|
143
|
-
ofTypeI18n: Reflect<GenericI18n<MessageKey | ExtraMessageKey>>()
|
144
|
-
};
|
206
|
+
return { useI18n, ofTypeI18n: Reflect<I18n>() };
|
145
207
|
}
|
146
208
|
|
147
|
-
function
|
148
|
-
|
149
|
-
|
209
|
+
function createI18nTranslationFunctionsFactory<MessageKey extends string, ExtraMessageKey extends string>(params: {
|
210
|
+
messages_fallbackLanguage: Record<MessageKey, string>;
|
211
|
+
extraMessages_fallbackLanguage: Record<ExtraMessageKey, string> | undefined;
|
212
|
+
extraMessages: Partial<Record<ExtraMessageKey, string>> | undefined;
|
150
213
|
__localizationRealmOverridesUserProfile: Record<string, string> | undefined;
|
151
|
-
})
|
152
|
-
const {
|
153
|
-
|
154
|
-
function resolveMsg(props: { key: string; args: (string | undefined)[]; doRenderAsHtml: boolean }): string | JSX.Element | undefined {
|
155
|
-
const { key, args, doRenderAsHtml } = props;
|
214
|
+
}) {
|
215
|
+
const { __localizationRealmOverridesUserProfile, extraMessages } = params;
|
156
216
|
|
157
|
-
|
217
|
+
const messages_fallbackLanguage = {
|
218
|
+
...params.messages_fallbackLanguage,
|
219
|
+
...params.extraMessages_fallbackLanguage
|
220
|
+
};
|
158
221
|
|
159
|
-
|
160
|
-
|
161
|
-
|
222
|
+
function createI18nTranslationFunctions(params: {
|
223
|
+
messages: Partial<Record<MessageKey, string>> | undefined;
|
224
|
+
}): Pick<GenericI18n<MessageKey | ExtraMessageKey>, "msg" | "msgStr" | "advancedMsg" | "advancedMsgStr"> {
|
225
|
+
const messages = {
|
226
|
+
...params.messages,
|
227
|
+
...extraMessages
|
228
|
+
};
|
162
229
|
|
163
|
-
|
230
|
+
function resolveMsg(props: { key: string; args: (string | undefined)[]; doRenderAsHtml: boolean }): string | JSX.Element | undefined {
|
231
|
+
const { key, args, doRenderAsHtml } = props;
|
164
232
|
|
165
|
-
|
166
|
-
const startIndex = message
|
167
|
-
.match(/{[0-9]+}/g)
|
168
|
-
?.map(g => g.match(/{([0-9]+)}/)![1])
|
169
|
-
.map(indexStr => parseInt(indexStr))
|
170
|
-
.sort((a, b) => a - b)[0];
|
233
|
+
const messageOrUndefined: string | undefined = (messages as any)[key] ?? (messages_fallbackLanguage as any)[key];
|
171
234
|
|
172
|
-
if (
|
173
|
-
|
174
|
-
return message;
|
235
|
+
if (messageOrUndefined === undefined) {
|
236
|
+
return undefined;
|
175
237
|
}
|
176
238
|
|
177
|
-
|
239
|
+
const message = messageOrUndefined;
|
178
240
|
|
179
|
-
|
180
|
-
|
181
|
-
|
241
|
+
const messageWithArgsInjectedIfAny = (() => {
|
242
|
+
const startIndex = message
|
243
|
+
.match(/{[0-9]+}/g)
|
244
|
+
?.map(g => g.match(/{([0-9]+)}/)![1])
|
245
|
+
.map(indexStr => parseInt(indexStr))
|
246
|
+
.sort((a, b) => a - b)[0];
|
247
|
+
|
248
|
+
if (startIndex === undefined) {
|
249
|
+
// No {0} in message (no arguments expected)
|
250
|
+
return message;
|
182
251
|
}
|
183
252
|
|
184
|
-
messageWithArgsInjected =
|
185
|
-
new RegExp(`\\{${i + startIndex}\\}`, "g"),
|
186
|
-
arg.replace(/</g, "<").replace(/>/g, ">")
|
187
|
-
);
|
188
|
-
});
|
253
|
+
let messageWithArgsInjected = message;
|
189
254
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
<span
|
195
|
-
// NOTE: The message is trusted. The arguments are not but are escaped.
|
196
|
-
dangerouslySetInnerHTML={{
|
197
|
-
__html: messageWithArgsInjectedIfAny
|
198
|
-
}}
|
199
|
-
/>
|
200
|
-
) : (
|
201
|
-
messageWithArgsInjectedIfAny
|
202
|
-
);
|
203
|
-
}
|
255
|
+
args.forEach((arg, i) => {
|
256
|
+
if (arg === undefined) {
|
257
|
+
return;
|
258
|
+
}
|
204
259
|
|
205
|
-
|
206
|
-
|
260
|
+
messageWithArgsInjected = messageWithArgsInjected.replace(
|
261
|
+
new RegExp(`\\{${i + startIndex}\\}`, "g"),
|
262
|
+
arg.replace(/</g, "<").replace(/>/g, ">")
|
263
|
+
);
|
264
|
+
});
|
207
265
|
|
208
|
-
|
209
|
-
|
266
|
+
return messageWithArgsInjected;
|
267
|
+
})();
|
210
268
|
|
211
269
|
return doRenderAsHtml ? (
|
212
270
|
<span
|
213
271
|
// NOTE: The message is trusted. The arguments are not but are escaped.
|
214
272
|
dangerouslySetInnerHTML={{
|
215
|
-
__html:
|
273
|
+
__html: messageWithArgsInjectedIfAny
|
216
274
|
}}
|
217
275
|
/>
|
218
276
|
) : (
|
219
|
-
|
277
|
+
messageWithArgsInjectedIfAny
|
220
278
|
);
|
221
279
|
}
|
222
280
|
|
223
|
-
|
224
|
-
const
|
281
|
+
function resolveMsgAdvanced(props: { key: string; args: (string | undefined)[]; doRenderAsHtml: boolean }): JSX.Element | string {
|
282
|
+
const { key, args, doRenderAsHtml } = props;
|
283
|
+
|
284
|
+
if (__localizationRealmOverridesUserProfile !== undefined && key in __localizationRealmOverridesUserProfile) {
|
285
|
+
const resolvedMessage = __localizationRealmOverridesUserProfile[key];
|
286
|
+
|
287
|
+
return doRenderAsHtml ? (
|
288
|
+
<span
|
289
|
+
// NOTE: The message is trusted. The arguments are not but are escaped.
|
290
|
+
dangerouslySetInnerHTML={{
|
291
|
+
__html: resolvedMessage
|
292
|
+
}}
|
293
|
+
/>
|
294
|
+
) : (
|
295
|
+
resolvedMessage
|
296
|
+
);
|
297
|
+
}
|
298
|
+
|
299
|
+
if (!/\$\{[^}]+\}/.test(key)) {
|
300
|
+
const resolvedMessage = resolveMsg({ key, args, doRenderAsHtml });
|
225
301
|
|
226
|
-
|
227
|
-
|
302
|
+
if (resolvedMessage === undefined) {
|
303
|
+
return doRenderAsHtml ? <span dangerouslySetInnerHTML={{ __html: key }} /> : key;
|
304
|
+
}
|
305
|
+
|
306
|
+
return resolvedMessage;
|
228
307
|
}
|
229
308
|
|
230
|
-
|
231
|
-
}
|
309
|
+
let isFirstMatch = true;
|
232
310
|
|
233
|
-
|
311
|
+
const resolvedComplexMessage = key.replace(/\$\{([^}]+)\}/g, (...[, key_i]) => {
|
312
|
+
const replaceBy = resolveMsg({ key: key_i, args: isFirstMatch ? args : [], doRenderAsHtml: false }) ?? key_i;
|
234
313
|
|
235
|
-
|
236
|
-
const replaceBy = resolveMsg({ key: key_i, args: isFirstMatch ? args : [], doRenderAsHtml: false }) ?? key_i;
|
314
|
+
isFirstMatch = false;
|
237
315
|
|
238
|
-
|
316
|
+
return replaceBy;
|
317
|
+
});
|
239
318
|
|
240
|
-
return
|
241
|
-
}
|
319
|
+
return doRenderAsHtml ? <span dangerouslySetInnerHTML={{ __html: resolvedComplexMessage }} /> : resolvedComplexMessage;
|
320
|
+
}
|
242
321
|
|
243
|
-
return
|
322
|
+
return {
|
323
|
+
msgStr: (key, ...args) => resolveMsg({ key, args, doRenderAsHtml: false }) as string,
|
324
|
+
msg: (key, ...args) => resolveMsg({ key, args, doRenderAsHtml: true }) as JSX.Element,
|
325
|
+
advancedMsg: (key, ...args) =>
|
326
|
+
resolveMsgAdvanced({
|
327
|
+
key,
|
328
|
+
args,
|
329
|
+
doRenderAsHtml: true
|
330
|
+
}) as JSX.Element,
|
331
|
+
advancedMsgStr: (key, ...args) =>
|
332
|
+
resolveMsgAdvanced({
|
333
|
+
key,
|
334
|
+
args,
|
335
|
+
doRenderAsHtml: false
|
336
|
+
}) as string
|
337
|
+
};
|
244
338
|
}
|
245
339
|
|
246
|
-
return {
|
247
|
-
msgStr: (key, ...args) => resolveMsg({ key, args, doRenderAsHtml: false }) as string,
|
248
|
-
msg: (key, ...args) => resolveMsg({ key, args, doRenderAsHtml: true }) as JSX.Element,
|
249
|
-
advancedMsg: (key, ...args) =>
|
250
|
-
resolveMsgAdvanced({
|
251
|
-
key,
|
252
|
-
args,
|
253
|
-
doRenderAsHtml: true
|
254
|
-
}) as JSX.Element,
|
255
|
-
advancedMsgStr: (key, ...args) =>
|
256
|
-
resolveMsgAdvanced({
|
257
|
-
key,
|
258
|
-
args,
|
259
|
-
doRenderAsHtml: false
|
260
|
-
}) as string
|
261
|
-
};
|
340
|
+
return { createI18nTranslationFunctions };
|
262
341
|
}
|
package/src/login/i18n/index.ts
CHANGED
@@ -1,2 +1,10 @@
|
|
1
|
-
export type {
|
2
|
-
|
1
|
+
export type { MessageKey, KcContextLike } from "./i18n";
|
2
|
+
import { createUseI18n } from "./i18n";
|
3
|
+
export { createUseI18n };
|
4
|
+
export { fallbackLanguageTag } from "./i18n";
|
5
|
+
|
6
|
+
const { useI18n, ofTypeI18n } = createUseI18n({});
|
7
|
+
|
8
|
+
export type I18n = typeof ofTypeI18n;
|
9
|
+
|
10
|
+
export { useI18n };
|
@@ -1,7 +1,7 @@
|
|
1
|
-
import {
|
1
|
+
import { createGetKcClsx } from "keycloakify/lib/getKcClsx";
|
2
2
|
import type { ClassKey } from "keycloakify/login/TemplateProps";
|
3
3
|
|
4
|
-
export const {
|
4
|
+
export const { getKcClsx } = createGetKcClsx<ClassKey>({
|
5
5
|
defaultClasses: {
|
6
6
|
kcHtmlClass: "login-pf",
|
7
7
|
kcBodyClass: undefined,
|
@@ -137,3 +137,7 @@ export const { useGetClassName } = createUseClassName<ClassKey>({
|
|
137
137
|
kcLabelClass: "pf-c-form__label pf-c-form__label-text"
|
138
138
|
}
|
139
139
|
});
|
140
|
+
|
141
|
+
export type { ClassKey };
|
142
|
+
|
143
|
+
export type KcClsx = ReturnType<typeof getKcClsx>["kcClsx"];
|
@@ -7,9 +7,11 @@ import { useConstCallback } from "keycloakify/tools/useConstCallback";
|
|
7
7
|
import { emailRegexp } from "keycloakify/tools/emailRegExp";
|
8
8
|
import { formatNumber } from "keycloakify/tools/formatNumber";
|
9
9
|
import { useInsertScriptTags } from "keycloakify/tools/useInsertScriptTags";
|
10
|
-
import type {
|
10
|
+
import type { PasswordPolicies, Attribute, Validators } from "keycloakify/login/KcContext";
|
11
|
+
import type { KcContext } from "../KcContext";
|
11
12
|
import type { MessageKey } from "keycloakify/login/i18n";
|
12
|
-
import
|
13
|
+
import { KcContextLike as KcContextLike_i18n } from "keycloakify/login/i18n";
|
14
|
+
import { useI18n } from "../i18n";
|
13
15
|
|
14
16
|
export type FormFieldError = {
|
15
17
|
errorMessage: JSX.Element;
|
@@ -64,23 +66,23 @@ export type FormAction =
|
|
64
66
|
fieldIndex: number | undefined;
|
65
67
|
};
|
66
68
|
|
67
|
-
export type KcContextLike =
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
69
|
+
export type KcContextLike = KcContextLike_i18n &
|
70
|
+
KcContextLike_useGetErrors & {
|
71
|
+
profile: {
|
72
|
+
attributesByName: Record<string, Attribute>;
|
73
|
+
html5DataAnnotations?: Record<string, string>;
|
74
|
+
};
|
75
|
+
passwordRequired?: boolean;
|
76
|
+
realm: { registrationEmailAsUsername: boolean };
|
77
|
+
url: {
|
78
|
+
resourcesPath: string;
|
79
|
+
};
|
78
80
|
};
|
79
|
-
|
81
|
+
|
82
|
+
assert<Extract<KcContext.Register, { pageId: "register.ftl" }> extends KcContextLike ? true : false>();
|
80
83
|
|
81
84
|
export type ParamsOfUseUserProfileForm = {
|
82
85
|
kcContext: KcContextLike;
|
83
|
-
i18n: I18n;
|
84
86
|
doMakeUserConfirmPassword: boolean;
|
85
87
|
};
|
86
88
|
|
@@ -103,7 +105,7 @@ namespace internal {
|
|
103
105
|
}
|
104
106
|
|
105
107
|
export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTypeOfUseUserProfileForm {
|
106
|
-
const { kcContext,
|
108
|
+
const { kcContext, doMakeUserConfirmPassword } = params;
|
107
109
|
|
108
110
|
const { insertScriptTags } = useInsertScriptTags({
|
109
111
|
componentOrHookName: "useUserProfileForm",
|
@@ -120,8 +122,7 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|
120
122
|
}, []);
|
121
123
|
|
122
124
|
const { getErrors } = useGetErrors({
|
123
|
-
kcContext
|
124
|
-
i18n
|
125
|
+
kcContext
|
125
126
|
});
|
126
127
|
|
127
128
|
const initialState = useMemo((): internal.State => {
|
@@ -515,12 +516,19 @@ export function useUserProfileForm(params: ParamsOfUseUserProfileForm): ReturnTy
|
|
515
516
|
};
|
516
517
|
}
|
517
518
|
|
518
|
-
|
519
|
-
|
519
|
+
type KcContextLike_useGetErrors = KcContextLike_i18n & {
|
520
|
+
messagesPerField: Pick<KcContext["messagesPerField"], "existsError" | "get">;
|
521
|
+
passwordPolicies?: PasswordPolicies;
|
522
|
+
};
|
523
|
+
|
524
|
+
assert<KcContextLike extends KcContextLike_useGetErrors ? true : false>();
|
525
|
+
|
526
|
+
function useGetErrors(params: { kcContext: KcContextLike_useGetErrors }) {
|
527
|
+
const { kcContext } = params;
|
520
528
|
|
521
529
|
const { messagesPerField, passwordPolicies } = kcContext;
|
522
530
|
|
523
|
-
const { msg, msgStr, advancedMsg, advancedMsgStr } =
|
531
|
+
const { msg, msgStr, advancedMsg, advancedMsgStr } = useI18n({ kcContext });
|
524
532
|
|
525
533
|
const getErrors = useConstCallback(
|
526
534
|
(params: {
|
package/src/login/pages/Code.tsx
CHANGED
@@ -1,30 +1,32 @@
|
|
1
|
-
import {
|
1
|
+
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
|
2
2
|
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
3
3
|
import type { KcContext } from "../KcContext";
|
4
|
-
import
|
4
|
+
import { useI18n } from "../i18n";
|
5
5
|
|
6
|
-
export default function Code(props: PageProps<Extract<KcContext, { pageId: "code.ftl" }
|
7
|
-
const { kcContext,
|
6
|
+
export default function Code(props: PageProps<Extract<KcContext, { pageId: "code.ftl" }>>) {
|
7
|
+
const { kcContext, doUseDefaultCss, Template, classes } = props;
|
8
8
|
|
9
|
-
const {
|
9
|
+
const { kcClsx } = getKcClsx({
|
10
10
|
doUseDefaultCss,
|
11
11
|
classes
|
12
12
|
});
|
13
13
|
|
14
14
|
const { code } = kcContext;
|
15
15
|
|
16
|
-
const { msg } =
|
16
|
+
const { msg } = useI18n({ kcContext });
|
17
17
|
|
18
18
|
return (
|
19
19
|
<Template
|
20
|
-
{
|
20
|
+
kcContext={kcContext}
|
21
|
+
doUseDefaultCss={doUseDefaultCss}
|
22
|
+
classes={classes}
|
21
23
|
headerNode={code.success ? msg("codeSuccessTitle") : msg("codeErrorTitle", code.error)}
|
22
24
|
>
|
23
25
|
<div id="kc-code">
|
24
26
|
{code.success ? (
|
25
27
|
<>
|
26
28
|
<p>{msg("copyCodeInstruction")}</p>
|
27
|
-
<input id="code" className={
|
29
|
+
<input id="code" className={kcClsx("kcTextareaClass")} defaultValue={code.code} />
|
28
30
|
</>
|
29
31
|
) : (
|
30
32
|
<p id="error">{code.error}</p>
|
@@ -1,23 +1,22 @@
|
|
1
|
-
import {
|
2
|
-
import { useGetClassName } from "keycloakify/login/lib/useGetClassName";
|
1
|
+
import { getKcClsx } from "keycloakify/login/lib/kcClsx";
|
3
2
|
import type { PageProps } from "keycloakify/login/pages/PageProps";
|
4
3
|
import type { KcContext } from "../KcContext";
|
5
|
-
import
|
4
|
+
import { useI18n } from "../i18n";
|
6
5
|
|
7
|
-
export default function DeleteAccountConfirm(props: PageProps<Extract<KcContext, { pageId: "delete-account-confirm.ftl" }
|
8
|
-
const { kcContext,
|
6
|
+
export default function DeleteAccountConfirm(props: PageProps<Extract<KcContext, { pageId: "delete-account-confirm.ftl" }>>) {
|
7
|
+
const { kcContext, doUseDefaultCss, Template, classes } = props;
|
9
8
|
|
10
|
-
const {
|
9
|
+
const { kcClsx } = getKcClsx({
|
11
10
|
doUseDefaultCss,
|
12
11
|
classes
|
13
12
|
});
|
14
13
|
|
15
14
|
const { url, triggered_from_aia } = kcContext;
|
16
15
|
|
17
|
-
const { msg, msgStr } =
|
16
|
+
const { msg, msgStr } = useI18n({ kcContext });
|
18
17
|
|
19
18
|
return (
|
20
|
-
<Template {
|
19
|
+
<Template kcContext={kcContext} doUseDefaultCss={doUseDefaultCss} classes={classes} headerNode={msg("deleteAccountConfirm")}>
|
21
20
|
<form action={url.loginAction} className="form-vertical" method="post">
|
22
21
|
<div className="alert alert-warning" style={{ marginTop: "0", marginBottom: "30px" }}>
|
23
22
|
<span className="pficon pficon-warning-triangle-o"></span>
|
@@ -37,13 +36,13 @@ export default function DeleteAccountConfirm(props: PageProps<Extract<KcContext,
|
|
37
36
|
<p className="delete-account-text">{msg("finalDeletionConfirmation")}</p>
|
38
37
|
<div id="kc-form-buttons">
|
39
38
|
<input
|
40
|
-
className={
|
39
|
+
className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonLargeClass")}
|
41
40
|
type="submit"
|
42
41
|
value={msgStr("doConfirmDelete")}
|
43
42
|
/>
|
44
43
|
{triggered_from_aia && (
|
45
44
|
<button
|
46
|
-
className={
|
45
|
+
className={kcClsx("kcButtonClass", "kcButtonDefaultClass", "kcButtonLargeClass")}
|
47
46
|
style={{ marginLeft: "calc(100% - 220px)" }}
|
48
47
|
type="submit"
|
49
48
|
name="cancel-aia"
|