keycloakify 10.0.0-rc.36 → 10.0.0-rc.38

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.
Files changed (272) hide show
  1. package/account/Fallback.d.ts +1 -2
  2. package/account/Fallback.js.map +1 -1
  3. package/account/KcContext/KcContext.d.ts +6 -6
  4. package/account/KcContext/getKcContextMock.d.ts +9 -9
  5. package/account/KcContext/getKcContextMock.js +3 -3
  6. package/account/KcContext/getKcContextMock.js.map +1 -1
  7. package/account/Template.d.ts +1 -2
  8. package/account/Template.js +7 -6
  9. package/account/Template.js.map +1 -1
  10. package/account/TemplateProps.d.ts +1 -3
  11. package/account/i18n/i18n.d.ts +9 -4
  12. package/account/i18n/i18n.js +132 -87
  13. package/account/i18n/i18n.js.map +1 -1
  14. package/account/i18n/index.d.ts +9 -2
  15. package/account/i18n/index.js +5 -1
  16. package/account/i18n/index.js.map +1 -1
  17. package/account/index.d.ts +1 -1
  18. package/account/lib/kcClsx.d.ts +9 -0
  19. package/account/lib/{useGetClassName.js → kcClsx.js} +3 -3
  20. package/account/lib/kcClsx.js.map +1 -0
  21. package/account/pages/Account.d.ts +1 -2
  22. package/account/pages/Account.js +9 -7
  23. package/account/pages/Account.js.map +1 -1
  24. package/account/pages/Applications.d.ts +1 -2
  25. package/account/pages/Applications.js +7 -7
  26. package/account/pages/Applications.js.map +1 -1
  27. package/account/pages/FederatedIdentity.d.ts +1 -2
  28. package/account/pages/FederatedIdentity.js +4 -3
  29. package/account/pages/FederatedIdentity.js.map +1 -1
  30. package/account/pages/Log.d.ts +1 -2
  31. package/account/pages/Log.js +6 -5
  32. package/account/pages/Log.js.map +1 -1
  33. package/account/pages/PageProps.d.ts +2 -4
  34. package/account/pages/Password.d.ts +1 -2
  35. package/account/pages/Password.js +10 -9
  36. package/account/pages/Password.js.map +1 -1
  37. package/account/pages/Sessions.d.ts +1 -2
  38. package/account/pages/Sessions.js +6 -6
  39. package/account/pages/Sessions.js.map +1 -1
  40. package/account/pages/Totp.d.ts +1 -2
  41. package/account/pages/Totp.js +6 -5
  42. package/account/pages/Totp.js.map +1 -1
  43. package/bin/{314.index.js → 21.index.js} +162 -4
  44. package/bin/{430.index.js → 214.index.js} +161 -2
  45. package/bin/3.index.js +81 -65
  46. package/bin/526.index.js +3 -2
  47. package/bin/538.index.js +552 -0
  48. package/bin/795.index.js +3 -2
  49. package/bin/{890.index.js → 941.index.js} +2 -159
  50. package/bin/main.js +19 -7
  51. package/lib/getKcClsx.d.ts +11 -0
  52. package/lib/getKcClsx.js +44 -0
  53. package/lib/getKcClsx.js.map +1 -0
  54. package/login/Fallback.d.ts +1 -2
  55. package/login/Fallback.js.map +1 -1
  56. package/login/KcContext/KcContext.d.ts +6 -6
  57. package/login/KcContext/getKcContextMock.d.ts +9 -9
  58. package/login/KcContext/getKcContextMock.js +3 -3
  59. package/login/KcContext/getKcContextMock.js.map +1 -1
  60. package/login/Template.d.ts +1 -2
  61. package/login/Template.js +10 -9
  62. package/login/Template.js.map +1 -1
  63. package/login/TemplateProps.d.ts +1 -3
  64. package/login/UserProfileFormFields.d.ts +5 -5
  65. package/login/UserProfileFormFields.js +35 -34
  66. package/login/UserProfileFormFields.js.map +1 -1
  67. package/login/i18n/i18n.d.ts +9 -4
  68. package/login/i18n/i18n.js +136 -91
  69. package/login/i18n/i18n.js.map +1 -1
  70. package/login/i18n/index.d.ts +9 -2
  71. package/login/i18n/index.js +5 -1
  72. package/login/i18n/index.js.map +1 -1
  73. package/login/index.d.ts +1 -1
  74. package/login/lib/kcClsx.d.ts +9 -0
  75. package/login/lib/{useGetClassName.js → kcClsx.js} +3 -3
  76. package/login/lib/kcClsx.js.map +1 -0
  77. package/login/lib/useUserProfileForm.d.ts +9 -6
  78. package/login/lib/useUserProfileForm.js +7 -5
  79. package/login/lib/useUserProfileForm.js.map +1 -1
  80. package/login/pages/Code.d.ts +1 -2
  81. package/login/pages/Code.js +6 -5
  82. package/login/pages/Code.js.map +1 -1
  83. package/login/pages/DeleteAccountConfirm.d.ts +1 -2
  84. package/login/pages/DeleteAccountConfirm.js +7 -7
  85. package/login/pages/DeleteAccountConfirm.js.map +1 -1
  86. package/login/pages/DeleteCredential.d.ts +1 -2
  87. package/login/pages/DeleteCredential.js +6 -6
  88. package/login/pages/DeleteCredential.js.map +1 -1
  89. package/login/pages/Error.d.ts +1 -2
  90. package/login/pages/Error.js +4 -3
  91. package/login/pages/Error.js.map +1 -1
  92. package/login/pages/FrontchannelLogout.d.ts +1 -2
  93. package/login/pages/FrontchannelLogout.js +4 -3
  94. package/login/pages/FrontchannelLogout.js.map +1 -1
  95. package/login/pages/IdpReviewUserProfile.d.ts +1 -2
  96. package/login/pages/IdpReviewUserProfile.js +6 -6
  97. package/login/pages/IdpReviewUserProfile.js.map +1 -1
  98. package/login/pages/Info.d.ts +1 -2
  99. package/login/pages/Info.js +4 -3
  100. package/login/pages/Info.js.map +1 -1
  101. package/login/pages/Login.d.ts +1 -2
  102. package/login/pages/Login.js +10 -8
  103. package/login/pages/Login.js.map +1 -1
  104. package/login/pages/LoginConfigTotp.d.ts +1 -2
  105. package/login/pages/LoginConfigTotp.js +8 -7
  106. package/login/pages/LoginConfigTotp.js.map +1 -1
  107. package/login/pages/LoginIdpLinkConfirm.d.ts +1 -2
  108. package/login/pages/LoginIdpLinkConfirm.js +6 -6
  109. package/login/pages/LoginIdpLinkConfirm.js.map +1 -1
  110. package/login/pages/LoginIdpLinkEmail.d.ts +1 -2
  111. package/login/pages/LoginIdpLinkEmail.js +4 -3
  112. package/login/pages/LoginIdpLinkEmail.js.map +1 -1
  113. package/login/pages/LoginOauth2DeviceVerifyUserCode.d.ts +1 -2
  114. package/login/pages/LoginOauth2DeviceVerifyUserCode.js +6 -6
  115. package/login/pages/LoginOauth2DeviceVerifyUserCode.js.map +1 -1
  116. package/login/pages/LoginOauthGrant.d.ts +1 -2
  117. package/login/pages/LoginOauthGrant.js +7 -7
  118. package/login/pages/LoginOauthGrant.js.map +1 -1
  119. package/login/pages/LoginOtp.d.ts +1 -2
  120. package/login/pages/LoginOtp.js +6 -6
  121. package/login/pages/LoginOtp.js.map +1 -1
  122. package/login/pages/LoginPageExpired.d.ts +1 -2
  123. package/login/pages/LoginPageExpired.js +4 -3
  124. package/login/pages/LoginPageExpired.js.map +1 -1
  125. package/login/pages/LoginPassword.d.ts +1 -2
  126. package/login/pages/LoginPassword.js +9 -7
  127. package/login/pages/LoginPassword.js.map +1 -1
  128. package/login/pages/LoginRecoveryAuthnCodeConfig.d.ts +1 -2
  129. package/login/pages/LoginRecoveryAuthnCodeConfig.js +9 -7
  130. package/login/pages/LoginRecoveryAuthnCodeConfig.js.map +1 -1
  131. package/login/pages/LoginRecoveryAuthnCodeInput.d.ts +1 -2
  132. package/login/pages/LoginRecoveryAuthnCodeInput.js +6 -6
  133. package/login/pages/LoginRecoveryAuthnCodeInput.js.map +1 -1
  134. package/login/pages/LoginResetOtp.d.ts +1 -2
  135. package/login/pages/LoginResetOtp.js +6 -6
  136. package/login/pages/LoginResetOtp.js.map +1 -1
  137. package/login/pages/LoginResetPassword.d.ts +1 -2
  138. package/login/pages/LoginResetPassword.js +7 -7
  139. package/login/pages/LoginResetPassword.js.map +1 -1
  140. package/login/pages/LoginUpdatePassword.d.ts +1 -2
  141. package/login/pages/LoginUpdatePassword.js +10 -9
  142. package/login/pages/LoginUpdatePassword.js.map +1 -1
  143. package/login/pages/LoginUpdateProfile.d.ts +1 -2
  144. package/login/pages/LoginUpdateProfile.js +7 -12
  145. package/login/pages/LoginUpdateProfile.js.map +1 -1
  146. package/login/pages/LoginUsername.d.ts +1 -2
  147. package/login/pages/LoginUsername.js +8 -7
  148. package/login/pages/LoginUsername.js.map +1 -1
  149. package/login/pages/LoginVerifyEmail.d.ts +1 -2
  150. package/login/pages/LoginVerifyEmail.js +4 -3
  151. package/login/pages/LoginVerifyEmail.js.map +1 -1
  152. package/login/pages/LoginX509Info.d.ts +1 -2
  153. package/login/pages/LoginX509Info.js +6 -6
  154. package/login/pages/LoginX509Info.js.map +1 -1
  155. package/login/pages/LogoutConfirm.d.ts +1 -2
  156. package/login/pages/LogoutConfirm.js +6 -6
  157. package/login/pages/LogoutConfirm.js.map +1 -1
  158. package/login/pages/PageProps.d.ts +2 -4
  159. package/login/pages/Register.d.ts +1 -2
  160. package/login/pages/Register.js +8 -16
  161. package/login/pages/Register.js.map +1 -1
  162. package/login/pages/SamlPostForm.d.ts +1 -2
  163. package/login/pages/SamlPostForm.js +4 -3
  164. package/login/pages/SamlPostForm.js.map +1 -1
  165. package/login/pages/SelectAuthenticator.d.ts +1 -2
  166. package/login/pages/SelectAuthenticator.js +6 -9
  167. package/login/pages/SelectAuthenticator.js.map +1 -1
  168. package/login/pages/Terms.d.ts +1 -2
  169. package/login/pages/Terms.js +7 -7
  170. package/login/pages/Terms.js.map +1 -1
  171. package/login/pages/UpdateEmail.d.ts +1 -2
  172. package/login/pages/UpdateEmail.js +8 -12
  173. package/login/pages/UpdateEmail.js.map +1 -1
  174. package/login/pages/WebauthnAuthenticate.d.ts +1 -2
  175. package/login/pages/WebauthnAuthenticate.js +13 -12
  176. package/login/pages/WebauthnAuthenticate.js.map +1 -1
  177. package/login/pages/WebauthnError.d.ts +1 -2
  178. package/login/pages/WebauthnError.js +7 -7
  179. package/login/pages/WebauthnError.js.map +1 -1
  180. package/login/pages/WebauthnRegister.d.ts +1 -2
  181. package/login/pages/WebauthnRegister.js +9 -8
  182. package/login/pages/WebauthnRegister.js.map +1 -1
  183. package/package.json +24 -16
  184. package/src/account/Fallback.tsx +1 -2
  185. package/src/account/KcContext/KcContext.ts +7 -7
  186. package/src/account/KcContext/getKcContextMock.ts +13 -24
  187. package/src/account/Template.tsx +8 -8
  188. package/src/account/TemplateProps.ts +1 -6
  189. package/src/account/i18n/i18n.tsx +204 -125
  190. package/src/account/i18n/index.ts +10 -2
  191. package/src/account/index.ts +1 -1
  192. package/src/account/lib/{useGetClassName.ts → kcClsx.ts} +6 -2
  193. package/src/account/pages/Account.tsx +15 -21
  194. package/src/account/pages/Applications.tsx +8 -9
  195. package/src/account/pages/FederatedIdentity.tsx +5 -5
  196. package/src/account/pages/Log.tsx +8 -8
  197. package/src/account/pages/PageProps.ts +2 -4
  198. package/src/account/pages/Password.tsx +13 -16
  199. package/src/account/pages/Sessions.tsx +9 -10
  200. package/src/account/pages/Totp.tsx +19 -28
  201. package/src/bin/keycloakify/generateSrcMainResources/generateMessageProperties.ts +2 -66
  202. package/src/bin/keycloakify/generateSrcMainResources/generateSrcMainResourcesForMainTheme.ts +7 -1
  203. package/src/bin/main.ts +15 -0
  204. package/src/bin/shared/buildOptions.ts +5 -2
  205. package/src/bin/shared/generateKcGenTs.ts +61 -0
  206. package/src/bin/tools/escapeStringForPropertiesFile.ts +64 -0
  207. package/src/bin/update-kc-gen.ts +13 -0
  208. package/src/lib/getKcClsx.ts +75 -0
  209. package/src/login/Fallback.tsx +1 -2
  210. package/src/login/KcContext/KcContext.ts +7 -7
  211. package/src/login/KcContext/getKcContextMock.ts +13 -24
  212. package/src/login/Template.tsx +36 -37
  213. package/src/login/TemplateProps.ts +1 -6
  214. package/src/login/UserProfileFormFields.tsx +66 -81
  215. package/src/login/i18n/i18n.tsx +208 -129
  216. package/src/login/i18n/index.ts +10 -2
  217. package/src/login/index.ts +1 -1
  218. package/src/login/lib/{useGetClassName.ts → kcClsx.ts} +6 -2
  219. package/src/login/lib/useUserProfileForm.tsx +29 -21
  220. package/src/login/pages/Code.tsx +10 -8
  221. package/src/login/pages/DeleteAccountConfirm.tsx +9 -10
  222. package/src/login/pages/DeleteCredential.tsx +11 -10
  223. package/src/login/pages/Error.tsx +5 -5
  224. package/src/login/pages/FrontchannelLogout.tsx +7 -5
  225. package/src/login/pages/IdpReviewUserProfile.tsx +16 -25
  226. package/src/login/pages/Info.tsx +7 -5
  227. package/src/login/pages/Login.tsx +34 -53
  228. package/src/login/pages/LoginConfigTotp.tsx +30 -38
  229. package/src/login/pages/LoginIdpLinkConfirm.tsx +10 -21
  230. package/src/login/pages/LoginIdpLinkEmail.tsx +5 -5
  231. package/src/login/pages/LoginOauth2DeviceVerifyUserCode.tsx +19 -24
  232. package/src/login/pages/LoginOauthGrant.tsx +14 -21
  233. package/src/login/pages/LoginOtp.tsx +29 -33
  234. package/src/login/pages/LoginPageExpired.tsx +5 -5
  235. package/src/login/pages/LoginPassword.tsx +23 -33
  236. package/src/login/pages/LoginRecoveryAuthnCodeConfig.tsx +21 -25
  237. package/src/login/pages/LoginRecoveryAuthnCodeInput.tsx +21 -25
  238. package/src/login/pages/LoginResetOtp.tsx +21 -25
  239. package/src/login/pages/LoginResetPassword.tsx +21 -25
  240. package/src/login/pages/LoginUpdatePassword.tsx +42 -52
  241. package/src/login/pages/LoginUpdateProfile.tsx +26 -33
  242. package/src/login/pages/LoginUsername.tsx +23 -35
  243. package/src/login/pages/LoginVerifyEmail.tsx +7 -5
  244. package/src/login/pages/LoginX509Info.tsx +27 -36
  245. package/src/login/pages/LogoutConfirm.tsx +11 -17
  246. package/src/login/pages/PageProps.ts +2 -4
  247. package/src/login/pages/Register.tsx +24 -49
  248. package/src/login/pages/SamlPostForm.tsx +5 -5
  249. package/src/login/pages/SelectAuthenticator.tsx +24 -26
  250. package/src/login/pages/Terms.tsx +11 -18
  251. package/src/login/pages/UpdateEmail.tsx +26 -36
  252. package/src/login/pages/WebauthnAuthenticate.tsx +26 -32
  253. package/src/login/pages/WebauthnError.tsx +11 -22
  254. package/src/login/pages/WebauthnRegister.tsx +20 -28
  255. package/src/tools/clsx.ts +6 -48
  256. package/src/tools/clsx_withTransform.ts +55 -0
  257. package/src/vite-plugin/vite-plugin.ts +14 -6
  258. package/tools/clsx.d.ts +3 -2
  259. package/tools/clsx.js +5 -41
  260. package/tools/clsx.js.map +1 -1
  261. package/tools/clsx_withTransform.d.ts +5 -0
  262. package/tools/clsx_withTransform.js +43 -0
  263. package/tools/clsx_withTransform.js.map +1 -0
  264. package/vite-plugin/index.js +167 -37
  265. package/account/lib/useGetClassName.d.ts +0 -7
  266. package/account/lib/useGetClassName.js.map +0 -1
  267. package/lib/useGetClassName.d.ts +0 -10
  268. package/lib/useGetClassName.js +0 -14
  269. package/lib/useGetClassName.js.map +0 -1
  270. package/login/lib/useGetClassName.d.ts +0 -7
  271. package/login/lib/useGetClassName.js.map +0 -1
  272. package/src/lib/useGetClassName.ts +0 -27
@@ -1,8 +1,8 @@
1
1
  import "keycloakify/tools/Object.fromEntries";
2
- import { useEffect, useState, useRef } from "react";
3
- import fallbackMessages from "./baseMessages/en";
4
- import { getMessages } from "./baseMessages";
2
+ import { useEffect, useState } from "react";
5
3
  import { assert } from "tsafe/assert";
4
+ import messages_fallbackLanguage from "./baseMessages/en";
5
+ import { getMessages } from "./baseMessages";
6
6
  import type { KcContext } from "../KcContext";
7
7
  import { Reflect } from "tsafe/Reflect";
8
8
 
@@ -17,7 +17,7 @@ export type KcContextLike = {
17
17
 
18
18
  assert<KcContext extends KcContextLike ? true : false>();
19
19
 
20
- export type MessageKey = keyof typeof fallbackMessages;
20
+ export type MessageKey = keyof typeof messages_fallbackLanguage;
21
21
 
22
22
  export type GenericI18n<MessageKey extends string> = {
23
23
  /**
@@ -79,166 +79,245 @@ export type GenericI18n<MessageKey extends string> = {
79
79
  * See advancedMsg() but instead of returning a JSX.Element it returns a string.
80
80
  */
81
81
  advancedMsgStr: (key: string, ...args: (string | undefined)[]) => string;
82
+
83
+ /**
84
+ * Initially the messages are in english (fallback language).
85
+ * The translations in the current language are being fetched dynamically.
86
+ * This property is true while the translations are being fetched.
87
+ */
88
+ isFetchingTranslations: boolean;
82
89
  };
83
90
 
84
- export type I18n = GenericI18n<MessageKey>;
91
+ function createGetI18n<ExtraMessageKey extends string = never>(extraMessages: { [languageTag: string]: { [key in ExtraMessageKey]: string } }) {
92
+ type I18n = GenericI18n<MessageKey | ExtraMessageKey>;
93
+
94
+ type Result = { i18n: I18n; prI18n_currentLanguage: Promise<I18n> | undefined };
95
+
96
+ const cachedResultByKcContext = new WeakMap<KcContextLike, Result>();
97
+
98
+ function getI18n(params: { kcContext: KcContextLike }): Result {
99
+ const { kcContext } = params;
100
+
101
+ use_cache: {
102
+ const cachedResult = cachedResultByKcContext.get(kcContext);
103
+
104
+ if (cachedResult === undefined) {
105
+ break use_cache;
106
+ }
107
+
108
+ return cachedResult;
109
+ }
110
+
111
+ const partialI18n: Pick<I18n, "currentLanguageTag" | "getChangeLocalUrl" | "labelBySupportedLanguageTag"> = {
112
+ currentLanguageTag: kcContext.locale?.currentLanguageTag ?? fallbackLanguageTag,
113
+ getChangeLocalUrl: newLanguageTag => {
114
+ const { locale } = kcContext;
115
+
116
+ assert(locale !== undefined, "Internationalization not enabled");
117
+
118
+ const targetSupportedLocale = locale.supported.find(({ languageTag }) => languageTag === newLanguageTag);
119
+
120
+ assert(targetSupportedLocale !== undefined, `${newLanguageTag} need to be enabled in Keycloak admin`);
121
+
122
+ return targetSupportedLocale.url;
123
+ },
124
+ labelBySupportedLanguageTag: Object.fromEntries((kcContext.locale?.supported ?? []).map(({ languageTag, label }) => [languageTag, label]))
125
+ };
126
+
127
+ const { createI18nTranslationFunctions } = createI18nTranslationFunctionsFactory<MessageKey, ExtraMessageKey>({
128
+ messages_fallbackLanguage,
129
+ extraMessages_fallbackLanguage: extraMessages[fallbackLanguageTag],
130
+ extraMessages: extraMessages[partialI18n.currentLanguageTag]
131
+ });
132
+
133
+ const isCurrentLanguageFallbackLanguage = partialI18n.currentLanguageTag !== fallbackLanguageTag;
134
+
135
+ const result: Result = {
136
+ i18n: {
137
+ ...partialI18n,
138
+ ...createI18nTranslationFunctions({ messages: undefined }),
139
+ isFetchingTranslations: !isCurrentLanguageFallbackLanguage
140
+ },
141
+ prI18n_currentLanguage: isCurrentLanguageFallbackLanguage
142
+ ? undefined
143
+ : (async () => {
144
+ const messages = await getMessages(partialI18n.currentLanguageTag);
145
+
146
+ const i18n_currentLanguage: I18n = {
147
+ ...partialI18n,
148
+ ...createI18nTranslationFunctions({ messages }),
149
+ isFetchingTranslations: false
150
+ };
151
+
152
+ // NOTE: This promise.resolve is just because without it we TypeScript
153
+ // gives a Variable 'result' is used before being assigned. error
154
+ await Promise.resolve().then(() => {
155
+ result.i18n = i18n_currentLanguage;
156
+ result.prI18n_currentLanguage = undefined;
157
+ });
158
+
159
+ return i18n_currentLanguage;
160
+ })()
161
+ };
162
+
163
+ cachedResultByKcContext.set(kcContext, result);
164
+
165
+ return result;
166
+ }
167
+
168
+ return { getI18n };
169
+ }
85
170
 
86
171
  export function createUseI18n<ExtraMessageKey extends string = never>(extraMessages: {
87
172
  [languageTag: string]: { [key in ExtraMessageKey]: string };
88
173
  }) {
89
- function useI18n(params: { kcContext: KcContextLike }): GenericI18n<MessageKey | ExtraMessageKey> | null {
174
+ type I18n = GenericI18n<MessageKey | ExtraMessageKey>;
175
+
176
+ const { getI18n } = createGetI18n(extraMessages);
177
+
178
+ function useI18n(params: { kcContext: KcContextLike }): I18n {
90
179
  const { kcContext } = params;
91
180
 
92
- const [i18n, setI18n] = useState<GenericI18n<ExtraMessageKey | MessageKey> | undefined>(undefined);
181
+ const { i18n, prI18n_currentLanguage } = getI18n({ kcContext });
93
182
 
94
- const refHasStartedFetching = useRef(false);
183
+ const [i18n_toReturn, setI18n_toReturn] = useState<I18n>(i18n);
95
184
 
96
185
  useEffect(() => {
97
- if (refHasStartedFetching.current) {
98
- return;
99
- }
186
+ let isActive = true;
100
187
 
101
- refHasStartedFetching.current = true;
102
-
103
- (async () => {
104
- const { currentLanguageTag = fallbackLanguageTag } = kcContext.locale ?? {};
105
-
106
- setI18n({
107
- ...createI18nTranslationFunctions({
108
- fallbackMessages: {
109
- ...fallbackMessages,
110
- ...(extraMessages[fallbackLanguageTag] ?? {})
111
- } as any,
112
- messages: {
113
- ...(await getMessages(currentLanguageTag)),
114
- ...(extraMessages[currentLanguageTag] ?? {})
115
- } as any
116
- }),
117
- currentLanguageTag,
118
- getChangeLocalUrl: newLanguageTag => {
119
- const { locale } = kcContext;
120
-
121
- assert(locale !== undefined, "Internationalization not enabled");
122
-
123
- const targetSupportedLocale = locale.supported.find(({ languageTag }) => languageTag === newLanguageTag);
124
-
125
- assert(targetSupportedLocale !== undefined, `${newLanguageTag} need to be enabled in Keycloak admin`);
126
-
127
- return targetSupportedLocale.url;
128
- },
129
- labelBySupportedLanguageTag: Object.fromEntries(
130
- (kcContext.locale?.supported ?? []).map(({ languageTag, label }) => [languageTag, label])
131
- )
132
- });
133
- })();
188
+ prI18n_currentLanguage?.then(i18n => {
189
+ if (!isActive) {
190
+ return;
191
+ }
192
+
193
+ setI18n_toReturn(i18n);
194
+ });
195
+
196
+ return () => {
197
+ isActive = false;
198
+ };
134
199
  }, []);
135
200
 
136
- return i18n ?? null;
201
+ return i18n_toReturn;
137
202
  }
138
203
 
139
- return {
140
- useI18n,
141
- ofTypeI18n: Reflect<GenericI18n<MessageKey | ExtraMessageKey>>()
142
- };
204
+ return { useI18n, ofTypeI18n: Reflect<I18n>() };
143
205
  }
144
206
 
145
- function createI18nTranslationFunctions<MessageKey extends string>(params: {
146
- fallbackMessages: Record<MessageKey, string>;
147
- messages: Record<MessageKey, string>;
148
- }): Pick<GenericI18n<MessageKey>, "msg" | "msgStr" | "advancedMsg" | "advancedMsgStr"> {
149
- const { fallbackMessages, messages } = params;
150
-
151
- function resolveMsg(props: { key: string; args: (string | undefined)[]; doRenderAsHtml: boolean }): string | JSX.Element | undefined {
152
- const { key, args, doRenderAsHtml } = props;
207
+ function createI18nTranslationFunctionsFactory<MessageKey extends string, ExtraMessageKey extends string>(params: {
208
+ messages_fallbackLanguage: Record<MessageKey, string>;
209
+ extraMessages_fallbackLanguage: Record<ExtraMessageKey, string> | undefined;
210
+ extraMessages: Partial<Record<ExtraMessageKey, string>> | undefined;
211
+ }) {
212
+ const { extraMessages } = params;
153
213
 
154
- const messageOrUndefined: string | undefined = (messages as any)[key] ?? (fallbackMessages as any)[key];
214
+ const messages_fallbackLanguage = {
215
+ ...params.messages_fallbackLanguage,
216
+ ...params.extraMessages_fallbackLanguage
217
+ };
155
218
 
156
- if (messageOrUndefined === undefined) {
157
- return undefined;
158
- }
219
+ function createI18nTranslationFunctions(params: {
220
+ messages: Partial<Record<MessageKey, string>> | undefined;
221
+ }): Pick<GenericI18n<MessageKey | ExtraMessageKey>, "msg" | "msgStr" | "advancedMsg" | "advancedMsgStr"> {
222
+ const messages = {
223
+ ...params.messages,
224
+ ...extraMessages
225
+ };
159
226
 
160
- const message = messageOrUndefined;
227
+ function resolveMsg(props: { key: string; args: (string | undefined)[]; doRenderAsHtml: boolean }): string | JSX.Element | undefined {
228
+ const { key, args, doRenderAsHtml } = props;
161
229
 
162
- const messageWithArgsInjectedIfAny = (() => {
163
- const startIndex = message
164
- .match(/{[0-9]+}/g)
165
- ?.map(g => g.match(/{([0-9]+)}/)![1])
166
- .map(indexStr => parseInt(indexStr))
167
- .sort((a, b) => a - b)[0];
230
+ const messageOrUndefined: string | undefined = (messages as any)[key] ?? (messages_fallbackLanguage as any)[key];
168
231
 
169
- if (startIndex === undefined) {
170
- // No {0} in message (no arguments expected)
171
- return message;
232
+ if (messageOrUndefined === undefined) {
233
+ return undefined;
172
234
  }
173
235
 
174
- let messageWithArgsInjected = message;
236
+ const message = messageOrUndefined;
175
237
 
176
- args.forEach((arg, i) => {
177
- if (arg === undefined) {
178
- return;
238
+ const messageWithArgsInjectedIfAny = (() => {
239
+ const startIndex = message
240
+ .match(/{[0-9]+}/g)
241
+ ?.map(g => g.match(/{([0-9]+)}/)![1])
242
+ .map(indexStr => parseInt(indexStr))
243
+ .sort((a, b) => a - b)[0];
244
+
245
+ if (startIndex === undefined) {
246
+ // No {0} in message (no arguments expected)
247
+ return message;
179
248
  }
180
249
 
181
- messageWithArgsInjected = messageWithArgsInjected.replace(
182
- new RegExp(`\\{${i + startIndex}\\}`, "g"),
183
- arg.replace(/</g, "&lt;").replace(/>/g, "&gt;")
184
- );
185
- });
250
+ let messageWithArgsInjected = message;
186
251
 
187
- return messageWithArgsInjected;
188
- })();
189
-
190
- return doRenderAsHtml ? (
191
- <span
192
- // NOTE: The message is trusted. The arguments are not but are escaped.
193
- dangerouslySetInnerHTML={{
194
- __html: messageWithArgsInjectedIfAny
195
- }}
196
- />
197
- ) : (
198
- messageWithArgsInjectedIfAny
199
- );
200
- }
252
+ args.forEach((arg, i) => {
253
+ if (arg === undefined) {
254
+ return;
255
+ }
256
+
257
+ messageWithArgsInjected = messageWithArgsInjected.replace(
258
+ new RegExp(`\\{${i + startIndex}\\}`, "g"),
259
+ arg.replace(/</g, "&lt;").replace(/>/g, "&gt;")
260
+ );
261
+ });
262
+
263
+ return messageWithArgsInjected;
264
+ })();
265
+
266
+ return doRenderAsHtml ? (
267
+ <span
268
+ // NOTE: The message is trusted. The arguments are not but are escaped.
269
+ dangerouslySetInnerHTML={{
270
+ __html: messageWithArgsInjectedIfAny
271
+ }}
272
+ />
273
+ ) : (
274
+ messageWithArgsInjectedIfAny
275
+ );
276
+ }
201
277
 
202
- function resolveMsgAdvanced(props: { key: string; args: (string | undefined)[]; doRenderAsHtml: boolean }): JSX.Element | string {
203
- const { key, args, doRenderAsHtml } = props;
278
+ function resolveMsgAdvanced(props: { key: string; args: (string | undefined)[]; doRenderAsHtml: boolean }): JSX.Element | string {
279
+ const { key, args, doRenderAsHtml } = props;
204
280
 
205
- if (!/\$\{[^}]+\}/.test(key)) {
206
- const resolvedMessage = resolveMsg({ key, args, doRenderAsHtml });
281
+ if (!/\$\{[^}]+\}/.test(key)) {
282
+ const resolvedMessage = resolveMsg({ key, args, doRenderAsHtml });
207
283
 
208
- if (resolvedMessage === undefined) {
209
- return doRenderAsHtml ? <span dangerouslySetInnerHTML={{ __html: key }} /> : key;
284
+ if (resolvedMessage === undefined) {
285
+ return doRenderAsHtml ? <span dangerouslySetInnerHTML={{ __html: key }} /> : key;
286
+ }
287
+
288
+ return resolvedMessage;
210
289
  }
211
290
 
212
- return resolvedMessage;
213
- }
291
+ let isFirstMatch = true;
214
292
 
215
- let isFirstMatch = true;
293
+ const resolvedComplexMessage = key.replace(/\$\{([^}]+)\}/g, (...[, key_i]) => {
294
+ const replaceBy = resolveMsg({ key: key_i, args: isFirstMatch ? args : [], doRenderAsHtml: false }) ?? key_i;
216
295
 
217
- const resolvedComplexMessage = key.replace(/\$\{([^}]+)\}/g, (...[, key_i]) => {
218
- const replaceBy = resolveMsg({ key: key_i, args: isFirstMatch ? args : [], doRenderAsHtml: false }) ?? key_i;
296
+ isFirstMatch = false;
219
297
 
220
- isFirstMatch = false;
298
+ return replaceBy;
299
+ });
221
300
 
222
- return replaceBy;
223
- });
301
+ return doRenderAsHtml ? <span dangerouslySetInnerHTML={{ __html: resolvedComplexMessage }} /> : resolvedComplexMessage;
302
+ }
224
303
 
225
- return doRenderAsHtml ? <span dangerouslySetInnerHTML={{ __html: resolvedComplexMessage }} /> : resolvedComplexMessage;
304
+ return {
305
+ msgStr: (key, ...args) => resolveMsg({ key, args, doRenderAsHtml: false }) as string,
306
+ msg: (key, ...args) => resolveMsg({ key, args, doRenderAsHtml: true }) as JSX.Element,
307
+ advancedMsg: (key, ...args) =>
308
+ resolveMsgAdvanced({
309
+ key,
310
+ args,
311
+ doRenderAsHtml: true
312
+ }) as JSX.Element,
313
+ advancedMsgStr: (key, ...args) =>
314
+ resolveMsgAdvanced({
315
+ key,
316
+ args,
317
+ doRenderAsHtml: false
318
+ }) as string
319
+ };
226
320
  }
227
321
 
228
- return {
229
- msgStr: (key, ...args) => resolveMsg({ key, args, doRenderAsHtml: false }) as string,
230
- msg: (key, ...args) => resolveMsg({ key, args, doRenderAsHtml: true }) as JSX.Element,
231
- advancedMsg: (key, ...args) =>
232
- resolveMsgAdvanced({
233
- key,
234
- args,
235
- doRenderAsHtml: true
236
- }) as JSX.Element,
237
- advancedMsgStr: (key, ...args) =>
238
- resolveMsgAdvanced({
239
- key,
240
- args,
241
- doRenderAsHtml: false
242
- }) as string
243
- };
322
+ return { createI18nTranslationFunctions };
244
323
  }
@@ -1,2 +1,10 @@
1
- export type { I18n } from "./i18n";
2
- export { createUseI18n } from "./i18n";
1
+ export type { MessageKey } 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,3 +1,3 @@
1
1
  export type { ExtendKcContext } from "keycloakify/account/KcContext";
2
- export type { PageProps } from "keycloakify/account/pages/PageProps";
2
+ export type { ClassKey } from "keycloakify/account/TemplateProps";
3
3
  export { createUseI18n } from "keycloakify/account/i18n";
@@ -1,7 +1,7 @@
1
- import { createUseClassName } from "keycloakify/lib/useGetClassName";
1
+ import { createGetKcClsx } from "keycloakify/lib/getKcClsx";
2
2
  import type { ClassKey } from "keycloakify/account/TemplateProps";
3
3
 
4
- export const { useGetClassName } = createUseClassName<ClassKey>({
4
+ export const { getKcClsx } = createGetKcClsx<ClassKey>({
5
5
  defaultClasses: {
6
6
  kcHtmlClass: undefined,
7
7
  kcBodyClass: undefined,
@@ -19,3 +19,7 @@ export const { useGetClassName } = createUseClassName<ClassKey>({
19
19
  "pf-c-form__helper-text pf-m-error required kc-feedback-text"
20
20
  }
21
21
  });
22
+
23
+ export type { ClassKey };
24
+
25
+ export type KcClsx = ReturnType<typeof getKcClsx>["kcClsx"];
@@ -1,26 +1,28 @@
1
1
  import { clsx } from "keycloakify/tools/clsx";
2
2
  import type { PageProps } from "keycloakify/account/pages/PageProps";
3
- import { useGetClassName } from "keycloakify/account/lib/useGetClassName";
3
+ import { getKcClsx } from "keycloakify/account/lib/kcClsx";
4
4
  import type { KcContext } from "../KcContext";
5
- import type { I18n } from "../i18n";
5
+ import { useI18n } from "../i18n";
6
6
 
7
- export default function Account(props: PageProps<Extract<KcContext, { pageId: "account.ftl" }>, I18n>) {
8
- const { kcContext, i18n, doUseDefaultCss, Template, classes } = props;
7
+ export default function Account(props: PageProps<Extract<KcContext, { pageId: "account.ftl" }>>) {
8
+ const { kcContext, doUseDefaultCss, Template } = props;
9
9
 
10
- const { getClassName } = useGetClassName({
10
+ const classes = {
11
+ ...props.classes,
12
+ kcBodyClass: clsx(props.classes?.kcBodyClass, "user")
13
+ };
14
+
15
+ const { kcClsx } = getKcClsx({
11
16
  doUseDefaultCss,
12
- classes: {
13
- ...classes,
14
- kcBodyClass: clsx(classes?.kcBodyClass, "user")
15
- }
17
+ classes
16
18
  });
17
19
 
18
20
  const { url, realm, messagesPerField, stateChecker, account, referrer } = kcContext;
19
21
 
20
- const { msg } = i18n;
22
+ const { msg } = useI18n({ kcContext });
21
23
 
22
24
  return (
23
- <Template {...{ kcContext, i18n, doUseDefaultCss, classes }} active="account">
25
+ <Template {...{ kcContext, doUseDefaultCss, classes }} active="account">
24
26
  <div className="row">
25
27
  <div className="col-md-10">
26
28
  <h2>{msg("editAccountHtmlTitle")}</h2>
@@ -102,11 +104,7 @@ export default function Account(props: PageProps<Extract<KcContext, { pageId: "a
102
104
  {referrer !== undefined && <a href={referrer?.url}>{msg("backToApplication")}</a>}
103
105
  <button
104
106
  type="submit"
105
- className={clsx(
106
- getClassName("kcButtonClass"),
107
- getClassName("kcButtonPrimaryClass"),
108
- getClassName("kcButtonLargeClass")
109
- )}
107
+ className={kcClsx("kcButtonClass", "kcButtonPrimaryClass", "kcButtonLargeClass")}
110
108
  name="submitAction"
111
109
  value="Save"
112
110
  >
@@ -114,11 +112,7 @@ export default function Account(props: PageProps<Extract<KcContext, { pageId: "a
114
112
  </button>
115
113
  <button
116
114
  type="submit"
117
- className={clsx(
118
- getClassName("kcButtonClass"),
119
- getClassName("kcButtonDefaultClass"),
120
- getClassName("kcButtonLargeClass")
121
- )}
115
+ className={kcClsx("kcButtonClass", "kcButtonDefaultClass", "kcButtonLargeClass")}
122
116
  name="submitAction"
123
117
  value="Cancel"
124
118
  >
@@ -1,13 +1,12 @@
1
- import { clsx } from "keycloakify/tools/clsx";
2
- import { useGetClassName } from "keycloakify/account/lib/useGetClassName";
1
+ import { getKcClsx } from "keycloakify/account/lib/kcClsx";
3
2
  import type { PageProps } from "keycloakify/account/pages/PageProps";
4
3
  import type { KcContext } from "../KcContext";
5
- import type { I18n } from "../i18n";
4
+ import { useI18n } from "../i18n";
6
5
 
7
- export default function Applications(props: PageProps<Extract<KcContext, { pageId: "applications.ftl" }>, I18n>) {
8
- const { kcContext, i18n, doUseDefaultCss, classes, Template } = props;
6
+ export default function Applications(props: PageProps<Extract<KcContext, { pageId: "applications.ftl" }>>) {
7
+ const { kcContext, doUseDefaultCss, classes, Template } = props;
9
8
 
10
- const { getClassName } = useGetClassName({
9
+ const { kcClsx } = getKcClsx({
11
10
  doUseDefaultCss,
12
11
  classes
13
12
  });
@@ -18,10 +17,10 @@ export default function Applications(props: PageProps<Extract<KcContext, { pageI
18
17
  stateChecker
19
18
  } = kcContext;
20
19
 
21
- const { msg, advancedMsg } = i18n;
20
+ const { msg, advancedMsg } = useI18n({ kcContext });
22
21
 
23
22
  return (
24
- <Template {...{ kcContext, i18n, doUseDefaultCss, classes }} active="applications">
23
+ <Template {...{ kcContext, doUseDefaultCss, classes }} active="applications">
25
24
  <div className="row">
26
25
  <div className="col-md-10">
27
26
  <h2>{msg("applicationsHtmlTitle")}</h2>
@@ -114,7 +113,7 @@ export default function Applications(props: PageProps<Extract<KcContext, { pageI
114
113
  application.additionalGrants.length > 0 ? (
115
114
  <button
116
115
  type="submit"
117
- className={clsx(getClassName("kcButtonPrimaryClass"), getClassName("kcButtonClass"))}
116
+ className={kcClsx("kcButtonPrimaryClass", "kcButtonClass")}
118
117
  id={`revoke-${application.client.clientId}`}
119
118
  name="clientId"
120
119
  value={application.client.id}
@@ -1,14 +1,14 @@
1
1
  import type { PageProps } from "keycloakify/account/pages/PageProps";
2
2
  import type { KcContext } from "../KcContext";
3
- import type { I18n } from "../i18n";
3
+ import { useI18n } from "../i18n";
4
4
 
5
- export default function FederatedIdentity(props: PageProps<Extract<KcContext, { pageId: "federatedIdentity.ftl" }>, I18n>) {
6
- const { kcContext, i18n, doUseDefaultCss, classes, Template } = props;
5
+ export default function FederatedIdentity(props: PageProps<Extract<KcContext, { pageId: "federatedIdentity.ftl" }>>) {
6
+ const { kcContext, doUseDefaultCss, classes, Template } = props;
7
7
 
8
8
  const { url, federatedIdentity, stateChecker } = kcContext;
9
- const { msg } = i18n;
9
+ const { msg } = useI18n({ kcContext });
10
10
  return (
11
- <Template {...{ kcContext, i18n, doUseDefaultCss, classes }} active="federatedIdentity">
11
+ <Template {...{ kcContext, doUseDefaultCss, classes }} active="federatedIdentity">
12
12
  <div className="main-layout social">
13
13
  <div className="row">
14
14
  <div className="col-md-10">
@@ -1,24 +1,24 @@
1
1
  import type { Key } from "react";
2
- import { useGetClassName } from "keycloakify/account/lib/useGetClassName";
2
+ import { getKcClsx } from "keycloakify/account/lib/kcClsx";
3
3
  import type { PageProps } from "keycloakify/account/pages/PageProps";
4
- import type { I18n } from "../i18n";
5
4
  import type { KcContext } from "../KcContext";
5
+ import { useI18n } from "../i18n";
6
6
 
7
- export default function Log(props: PageProps<Extract<KcContext, { pageId: "log.ftl" }>, I18n>) {
8
- const { kcContext, i18n, doUseDefaultCss, classes, Template } = props;
7
+ export default function Log(props: PageProps<Extract<KcContext, { pageId: "log.ftl" }>>) {
8
+ const { kcContext, doUseDefaultCss, classes, Template } = props;
9
9
 
10
- const { getClassName } = useGetClassName({
10
+ const { kcClsx } = getKcClsx({
11
11
  doUseDefaultCss,
12
12
  classes
13
13
  });
14
14
 
15
15
  const { log } = kcContext;
16
16
 
17
- const { msg } = i18n;
17
+ const { msg } = useI18n({ kcContext });
18
18
 
19
19
  return (
20
- <Template {...{ kcContext, i18n, doUseDefaultCss, classes }} active="log">
21
- <div className={getClassName("kcContentWrapperClass")}>
20
+ <Template {...{ kcContext, doUseDefaultCss, classes }} active="log">
21
+ <div className={kcClsx("kcContentWrapperClass")}>
22
22
  <div className="col-md-10">
23
23
  <h2>{msg("accountLogHtmlTitle")}</h2>
24
24
  </div>
@@ -1,12 +1,10 @@
1
1
  import type { TemplateProps, ClassKey } from "keycloakify/account/TemplateProps";
2
2
  import type { LazyOrNot } from "keycloakify/tools/LazyOrNot";
3
- import type { I18n } from "../i18n";
4
3
  import type { KcContext } from "../KcContext";
5
4
 
6
- export type PageProps<NarrowedKcContext = KcContext, I18nExtended extends I18n = I18n> = {
7
- Template: LazyOrNot<(props: TemplateProps<any, any>) => JSX.Element | null>;
5
+ export type PageProps<NarrowedKcContext = KcContext> = {
6
+ Template: LazyOrNot<(props: TemplateProps<any>) => JSX.Element | null>;
8
7
  kcContext: NarrowedKcContext;
9
- i18n: I18nExtended;
10
8
  doUseDefaultCss: boolean;
11
9
  classes?: Partial<Record<ClassKey, string>>;
12
10
  };