keycloakify 5.4.4 → 6.0.0-beta.3

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 (81) hide show
  1. package/kcMessages.d.ts +1 -0
  2. package/kcMessages.js +14 -0
  3. package/kcMessages.js.map +1 -0
  4. package/lib/components/Error.d.ts +3 -1
  5. package/lib/components/Error.js +3 -4
  6. package/lib/components/Error.js.map +1 -1
  7. package/lib/components/Info.d.ts +3 -1
  8. package/lib/components/Info.js +3 -4
  9. package/lib/components/Info.js.map +1 -1
  10. package/lib/components/KcApp.d.ts +3 -1
  11. package/lib/components/KcApp.js +15 -15
  12. package/lib/components/KcApp.js.map +1 -1
  13. package/lib/components/Login.d.ts +3 -1
  14. package/lib/components/Login.js +3 -4
  15. package/lib/components/Login.js.map +1 -1
  16. package/lib/components/LoginIdpLinkConfirm.d.ts +3 -1
  17. package/lib/components/LoginIdpLinkConfirm.js +3 -4
  18. package/lib/components/LoginIdpLinkConfirm.js.map +1 -1
  19. package/lib/components/LoginIdpLinkEmail.d.ts +3 -1
  20. package/lib/components/LoginIdpLinkEmail.js +3 -4
  21. package/lib/components/LoginIdpLinkEmail.js.map +1 -1
  22. package/lib/components/LoginOtp.d.ts +3 -1
  23. package/lib/components/LoginOtp.js +3 -4
  24. package/lib/components/LoginOtp.js.map +1 -1
  25. package/lib/components/LoginPageExpired.d.ts +3 -1
  26. package/lib/components/LoginPageExpired.js +3 -4
  27. package/lib/components/LoginPageExpired.js.map +1 -1
  28. package/lib/components/LoginResetPassword.d.ts +3 -1
  29. package/lib/components/LoginResetPassword.js +3 -4
  30. package/lib/components/LoginResetPassword.js.map +1 -1
  31. package/lib/components/LoginUpdatePassword.d.ts +3 -1
  32. package/lib/components/LoginUpdatePassword.js +3 -4
  33. package/lib/components/LoginUpdatePassword.js.map +1 -1
  34. package/lib/components/LoginUpdateProfile.d.ts +3 -1
  35. package/lib/components/LoginUpdateProfile.js +3 -4
  36. package/lib/components/LoginUpdateProfile.js.map +1 -1
  37. package/lib/components/LoginVerifyEmail.d.ts +3 -1
  38. package/lib/components/LoginVerifyEmail.js +3 -4
  39. package/lib/components/LoginVerifyEmail.js.map +1 -1
  40. package/lib/components/Register.d.ts +3 -1
  41. package/lib/components/Register.js +3 -4
  42. package/lib/components/Register.js.map +1 -1
  43. package/lib/components/RegisterUserProfile.d.ts +3 -1
  44. package/lib/components/RegisterUserProfile.js +6 -6
  45. package/lib/components/RegisterUserProfile.js.map +1 -1
  46. package/lib/components/Template.d.ts +2 -0
  47. package/lib/components/Template.js +2 -2
  48. package/lib/components/Template.js.map +1 -1
  49. package/lib/components/Terms.d.ts +4 -2
  50. package/lib/components/Terms.js +8 -8
  51. package/lib/components/Terms.js.map +1 -1
  52. package/lib/i18n/index.d.ts +20 -5249
  53. package/lib/i18n/index.js +79 -51
  54. package/lib/i18n/index.js.map +1 -1
  55. package/lib/i18n/kcMessages.d.ts +5222 -0
  56. package/lib/i18n/kcMessages.js +19 -0
  57. package/lib/i18n/kcMessages.js.map +1 -0
  58. package/lib/useFormValidationSlice.d.ts +3 -0
  59. package/lib/useFormValidationSlice.js +4 -4
  60. package/lib/useFormValidationSlice.js.map +1 -1
  61. package/package.json +9 -2
  62. package/src/kcMessages.ts +1 -0
  63. package/src/lib/components/Error.tsx +4 -4
  64. package/src/lib/components/Info.tsx +5 -5
  65. package/src/lib/components/KcApp.tsx +16 -15
  66. package/src/lib/components/Login.tsx +4 -4
  67. package/src/lib/components/LoginIdpLinkConfirm.tsx +39 -37
  68. package/src/lib/components/LoginIdpLinkEmail.tsx +27 -25
  69. package/src/lib/components/LoginOtp.tsx +4 -4
  70. package/src/lib/components/LoginPageExpired.tsx +31 -29
  71. package/src/lib/components/LoginResetPassword.tsx +60 -53
  72. package/src/lib/components/LoginUpdatePassword.tsx +98 -96
  73. package/src/lib/components/LoginUpdateProfile.tsx +100 -92
  74. package/src/lib/components/LoginVerifyEmail.tsx +25 -23
  75. package/src/lib/components/Register.tsx +4 -4
  76. package/src/lib/components/RegisterUserProfile.tsx +68 -55
  77. package/src/lib/components/Template.tsx +5 -3
  78. package/src/lib/components/Terms.tsx +14 -8
  79. package/src/lib/i18n/index.tsx +81 -49
  80. package/src/lib/i18n/kcMessages.ts +24 -0
  81. package/src/lib/useFormValidationSlice.tsx +7 -4
@@ -2,19 +2,19 @@ import { memo } from "react";
2
2
  import { Template } from "./Template";
3
3
  import type { KcProps } from "./KcProps";
4
4
  import type { KcContextBase } from "../getKcContext/KcContextBase";
5
- import { getMsg } from "../i18n";
5
+ import type { I18n } from "../i18n";
6
6
  import { useCssAndCx } from "tss-react";
7
7
 
8
- export const Register = memo(({ kcContext, ...props }: { kcContext: KcContextBase.Register } & KcProps) => {
8
+ export const Register = memo(({ kcContext, useI18n, ...props }: { kcContext: KcContextBase.Register; useI18n: () => I18n } & KcProps) => {
9
9
  const { url, messagesPerField, register, realm, passwordRequired, recaptchaRequired, recaptchaSiteKey } = kcContext;
10
10
 
11
- const { msg, msgStr } = getMsg(kcContext);
11
+ const { msg, msgStr } = useI18n();
12
12
 
13
13
  const { cx } = useCssAndCx();
14
14
 
15
15
  return (
16
16
  <Template
17
- {...{ kcContext, ...props }}
17
+ {...{ kcContext, useI18n, ...props }}
18
18
  doFetchDefaultThemeResources={true}
19
19
  headerNode={msg("registerTitle")}
20
20
  formNode={
@@ -2,79 +2,91 @@ import { useMemo, memo, useEffect, useState, Fragment } from "react";
2
2
  import { Template } from "./Template";
3
3
  import type { KcProps } from "./KcProps";
4
4
  import type { KcContextBase, Attribute } from "../getKcContext/KcContextBase";
5
- import { getMsg } from "../i18n";
5
+ import type { I18n } from "../i18n";
6
6
  import { useCssAndCx } from "tss-react";
7
7
  import type { ReactComponent } from "../tools/ReactComponent";
8
8
  import { useCallbackFactory } from "powerhooks/useCallbackFactory";
9
9
  import { useFormValidationSlice } from "../useFormValidationSlice";
10
10
 
11
- export const RegisterUserProfile = memo(({ kcContext, ...props_ }: { kcContext: KcContextBase.RegisterUserProfile } & KcProps) => {
12
- const { url, messagesPerField, recaptchaRequired, recaptchaSiteKey } = kcContext;
11
+ export const RegisterUserProfile = memo(
12
+ ({ kcContext, useI18n, ...props_ }: { kcContext: KcContextBase.RegisterUserProfile; useI18n: () => I18n } & KcProps) => {
13
+ const { url, messagesPerField, recaptchaRequired, recaptchaSiteKey } = kcContext;
13
14
 
14
- const { msg, msgStr } = getMsg(kcContext);
15
+ const { msg, msgStr } = useI18n();
15
16
 
16
- const { cx, css } = useCssAndCx();
17
-
18
- const props = useMemo(
19
- () => ({
20
- ...props_,
21
- "kcFormGroupClass": cx(props_.kcFormGroupClass, css({ "marginBottom": 20 })),
22
- }),
23
- [cx, css],
24
- );
25
-
26
- const [isFomSubmittable, setIsFomSubmittable] = useState(false);
17
+ const { cx, css } = useCssAndCx();
27
18
 
28
- return (
29
- <Template
30
- {...{ kcContext, ...props }}
31
- displayMessage={messagesPerField.exists("global")}
32
- displayRequiredFields={true}
33
- doFetchDefaultThemeResources={true}
34
- headerNode={msg("registerTitle")}
35
- formNode={
36
- <form id="kc-register-form" className={cx(props.kcFormClass)} action={url.registrationAction} method="post">
37
- <UserProfileFormFields kcContext={kcContext} onIsFormSubmittableValueChange={setIsFomSubmittable} {...props} />
38
- {recaptchaRequired && (
39
- <div className="form-group">
40
- <div className={cx(props.kcInputWrapperClass)}>
41
- <div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey} />
19
+ const props = useMemo(
20
+ () => ({
21
+ ...props_,
22
+ "kcFormGroupClass": cx(props_.kcFormGroupClass, css({ "marginBottom": 20 })),
23
+ }),
24
+ [cx, css],
25
+ );
26
+
27
+ const [isFomSubmittable, setIsFomSubmittable] = useState(false);
28
+
29
+ return (
30
+ <Template
31
+ {...{ kcContext, useI18n, ...props }}
32
+ displayMessage={messagesPerField.exists("global")}
33
+ displayRequiredFields={true}
34
+ doFetchDefaultThemeResources={true}
35
+ headerNode={msg("registerTitle")}
36
+ formNode={
37
+ <form id="kc-register-form" className={cx(props.kcFormClass)} action={url.registrationAction} method="post">
38
+ <UserProfileFormFields
39
+ kcContext={kcContext}
40
+ onIsFormSubmittableValueChange={setIsFomSubmittable}
41
+ useI18n={useI18n}
42
+ {...props}
43
+ />
44
+ {recaptchaRequired && (
45
+ <div className="form-group">
46
+ <div className={cx(props.kcInputWrapperClass)}>
47
+ <div className="g-recaptcha" data-size="compact" data-sitekey={recaptchaSiteKey} />
48
+ </div>
42
49
  </div>
43
- </div>
44
- )}
45
- <div className={cx(props.kcFormGroupClass)}>
46
- <div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
47
- <div className={cx(props.kcFormOptionsWrapperClass)}>
48
- <span>
49
- <a href={url.loginUrl}>{msg("backToLogin")}</a>
50
- </span>
50
+ )}
51
+ <div className={cx(props.kcFormGroupClass)}>
52
+ <div id="kc-form-options" className={cx(props.kcFormOptionsClass)}>
53
+ <div className={cx(props.kcFormOptionsWrapperClass)}>
54
+ <span>
55
+ <a href={url.loginUrl}>{msg("backToLogin")}</a>
56
+ </span>
57
+ </div>
51
58
  </div>
52
- </div>
53
59
 
54
- <div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
55
- <input
56
- className={cx(props.kcButtonClass, props.kcButtonPrimaryClass, props.kcButtonBlockClass, props.kcButtonLargeClass)}
57
- type="submit"
58
- value={msgStr("doRegister")}
59
- disabled={!isFomSubmittable}
60
- />
60
+ <div id="kc-form-buttons" className={cx(props.kcFormButtonsClass)}>
61
+ <input
62
+ className={cx(
63
+ props.kcButtonClass,
64
+ props.kcButtonPrimaryClass,
65
+ props.kcButtonBlockClass,
66
+ props.kcButtonLargeClass,
67
+ )}
68
+ type="submit"
69
+ value={msgStr("doRegister")}
70
+ disabled={!isFomSubmittable}
71
+ />
72
+ </div>
61
73
  </div>
62
- </div>
63
- </form>
64
- }
65
- />
66
- );
67
- });
68
-
69
- type UserProfileFormFieldsProps = { kcContext: KcContextBase.RegisterUserProfile } & KcProps &
74
+ </form>
75
+ }
76
+ />
77
+ );
78
+ },
79
+ );
80
+
81
+ type UserProfileFormFieldsProps = { kcContext: KcContextBase.RegisterUserProfile; useI18n: () => I18n } & KcProps &
70
82
  Partial<Record<"BeforeField" | "AfterField", ReactComponent<{ attribute: Attribute }>>> & {
71
83
  onIsFormSubmittableValueChange: (isFormSubmittable: boolean) => void;
72
84
  };
73
85
 
74
- const UserProfileFormFields = memo(({ kcContext, onIsFormSubmittableValueChange, ...props }: UserProfileFormFieldsProps) => {
86
+ const UserProfileFormFields = memo(({ kcContext, useI18n, onIsFormSubmittableValueChange, ...props }: UserProfileFormFieldsProps) => {
75
87
  const { cx, css } = useCssAndCx();
76
88
 
77
- const { advancedMsg } = getMsg(kcContext);
89
+ const { advancedMsg } = useI18n();
78
90
 
79
91
  const {
80
92
  formValidationState: { fieldStateByAttributeName, isFormSubmittable },
@@ -82,6 +94,7 @@ const UserProfileFormFields = memo(({ kcContext, onIsFormSubmittableValueChange,
82
94
  attributesWithPassword,
83
95
  } = useFormValidationSlice({
84
96
  kcContext,
97
+ useI18n,
85
98
  });
86
99
 
87
100
  useEffect(() => {
@@ -1,6 +1,7 @@
1
1
  import { useReducer, useEffect, memo } from "react";
2
2
  import type { ReactNode } from "react";
3
- import { getMsg, getCurrentKcLanguageTag, changeLocale, getTagLabel } from "../i18n";
3
+ import { getCurrentKcLanguageTag, changeLocale, getTagLabel } from "../i18n";
4
+ import type { I18n } from "../i18n";
4
5
  import type { KcLanguageTag } from "../i18n";
5
6
  import type { KcContextBase } from "../getKcContext/KcContextBase";
6
7
  import { assert } from "../tools/assert";
@@ -25,7 +26,7 @@ export type TemplateProps = {
25
26
  * to avoid pulling the default theme assets.
26
27
  */
27
28
  doFetchDefaultThemeResources: boolean;
28
- } & { kcContext: KcContextBase } & KcTemplateProps;
29
+ } & { kcContext: KcContextBase; useI18n: () => I18n } & KcTemplateProps;
29
30
 
30
31
  export const Template = memo((props: TemplateProps) => {
31
32
  const {
@@ -40,6 +41,7 @@ export const Template = memo((props: TemplateProps) => {
40
41
  infoNode = null,
41
42
  kcContext,
42
43
  doFetchDefaultThemeResources,
44
+ useI18n,
43
45
  } = props;
44
46
 
45
47
  const { cx } = useCssAndCx();
@@ -48,7 +50,7 @@ export const Template = memo((props: TemplateProps) => {
48
50
  console.log("Rendering this page with react using keycloakify");
49
51
  }, []);
50
52
 
51
- const { msg } = getMsg(kcContext);
53
+ const { msg } = useI18n();
52
54
 
53
55
  const onChangeLanguageClickFactory = useCallbackFactory(([kcLanguageTag]: [KcLanguageTag]) =>
54
56
  changeLocale({
@@ -2,32 +2,38 @@ import { useReducer, useEffect, memo } from "react";
2
2
  import { Template } from "./Template";
3
3
  import type { KcProps } from "./KcProps";
4
4
  import type { KcContextBase } from "../getKcContext/KcContextBase";
5
- import { getMsg } from "../i18n";
6
5
  import { useCssAndCx } from "tss-react";
7
- import { kcMessages, getCurrentKcLanguageTag } from "../i18n";
8
- import type { KcLanguageTag } from "../i18n";
6
+ import { getCurrentKcLanguageTag } from "../i18n";
7
+ import type { KcLanguageTag, I18n } from "../i18n";
9
8
 
10
9
  /** Allow to avoid bundling the terms and download it on demand*/
11
10
  export function useDownloadTerms(params: {
12
11
  kcContext: KcContextBase;
13
12
  downloadTermMarkdown: (params: { currentKcLanguageTag: KcLanguageTag }) => Promise<string>;
13
+ useI18n: () => I18n;
14
14
  }) {
15
- const { kcContext, downloadTermMarkdown } = params;
15
+ const { kcContext, downloadTermMarkdown, useI18n } = params;
16
16
 
17
17
  const [, forceUpdate] = useReducer(x => x + 1, 0);
18
18
 
19
+ const { evtKcMessages } = useI18n();
20
+
19
21
  useEffect(() => {
20
22
  const currentKcLanguageTag = getCurrentKcLanguageTag(kcContext);
21
23
 
22
24
  downloadTermMarkdown({ currentKcLanguageTag }).then(thermMarkdown => {
23
- kcMessages[currentKcLanguageTag].termsText = thermMarkdown;
25
+ evtKcMessages.$attachOnce(
26
+ kcMessages => (kcMessages !== undefined ? [kcMessages] : null),
27
+ kcMessages => (kcMessages[currentKcLanguageTag].termsText = thermMarkdown),
28
+ );
29
+
24
30
  forceUpdate();
25
31
  });
26
32
  }, []);
27
33
  }
28
34
 
29
- export const Terms = memo(({ kcContext, ...props }: { kcContext: KcContextBase.Terms } & KcProps) => {
30
- const { msg, msgStr } = getMsg(kcContext);
35
+ export const Terms = memo(({ kcContext, useI18n, ...props }: { kcContext: KcContextBase.Terms; useI18n: () => I18n } & KcProps) => {
36
+ const { msg, msgStr } = useI18n();
31
37
 
32
38
  const { cx } = useCssAndCx();
33
39
 
@@ -35,7 +41,7 @@ export const Terms = memo(({ kcContext, ...props }: { kcContext: KcContextBase.T
35
41
 
36
42
  return (
37
43
  <Template
38
- {...{ kcContext, ...props }}
44
+ {...{ kcContext, useI18n, ...props }}
39
45
  doFetchDefaultThemeResources={true}
40
46
  displayMessage={false}
41
47
  headerNode={msg("termsTitle")}
@@ -1,35 +1,17 @@
1
1
  import "minimal-polyfills/Object.fromEntries";
2
2
  //NOTE for later: https://github.com/remarkjs/react-markdown/blob/236182ecf30bd89c1e5a7652acaf8d0bf81e6170/src/renderers.js#L7-L35
3
3
  import ReactMarkdown from "react-markdown";
4
- import memoize from "memoizee";
5
- import { kcMessages as kcMessagesBase } from "./generated_kcMessages/15.0.2/login";
4
+ import type { kcMessages as t_kcMessages } from "./kcMessages";
6
5
  import { assert } from "tsafe/assert";
7
6
  import type { Equals } from "tsafe";
7
+ import { Evt } from "evt";
8
+ import { useRerenderOnStateChange } from "evt/hooks";
9
+ import { useMemo } from "react";
10
+ import type { StatefulReadonlyEvt } from "evt";
8
11
 
9
- export const kcMessages = {
10
- ...kcMessagesBase,
11
- "en": {
12
- ...kcMessagesBase["en"],
13
- "termsText": "⏳",
14
- "shouldBeEqual": "{0} should be equal to {1}",
15
- "shouldBeDifferent": "{0} should be different to {1}",
16
- "shouldMatchPattern": "Pattern should match: `/{0}/`",
17
- "mustBeAnInteger": "Must be an integer",
18
- "notAValidOption": "Not a valid option",
19
- },
20
- "fr": {
21
- ...kcMessagesBase["fr"],
22
- /* spell-checker: disable */
23
- "shouldBeEqual": "{0} doit être egale à {1}",
24
- "shouldBeDifferent": "{0} doit être différent de {1}",
25
- "shouldMatchPattern": "Dois respecter le schéma: `/{0}/`",
26
- "mustBeAnInteger": "Doit être un nombre entiers",
27
- "notAValidOption": "N'est pas une option valide",
28
- /* spell-checker: enable */
29
- },
30
- };
12
+ export type KcMessages = typeof t_kcMessages;
31
13
 
32
- export type KcLanguageTag = keyof typeof kcMessages;
14
+ export type KcLanguageTag = keyof KcMessages;
33
15
 
34
16
  export const kcLanguageTags = [
35
17
  "en",
@@ -106,15 +88,16 @@ export function changeLocale(params: {
106
88
  assert(false, "never");
107
89
  }
108
90
 
109
- export type MessageKey = keyof typeof kcMessages["en"];
91
+ export type MessageKey = keyof KcMessages["en"];
110
92
 
111
93
  function resolveMsg<Key extends string, DoRenderMarkdown extends boolean>(props: {
112
94
  key: Key;
113
95
  args: (string | undefined)[];
114
96
  kcLanguageTag: string;
115
97
  doRenderMarkdown: DoRenderMarkdown;
98
+ kcMessages: KcMessages;
116
99
  }): Key extends MessageKey ? (DoRenderMarkdown extends true ? JSX.Element : string) : undefined {
117
- const { key, args, kcLanguageTag, doRenderMarkdown } = props;
100
+ const { key, args, kcLanguageTag, doRenderMarkdown, kcMessages } = props;
118
101
 
119
102
  let str = kcMessages[kcLanguageTag as any as "en"][key as MessageKey] ?? kcMessages["en"][key as MessageKey];
120
103
 
@@ -160,8 +143,9 @@ function resolveMsgAdvanced<Key extends string, DoRenderMarkdown extends boolean
160
143
  args: (string | undefined)[];
161
144
  kcLanguageTag: string;
162
145
  doRenderMarkdown: DoRenderMarkdown;
146
+ kcMessages: KcMessages;
163
147
  }): DoRenderMarkdown extends true ? JSX.Element : string {
164
- const { key, args, kcLanguageTag, doRenderMarkdown } = props;
148
+ const { key, args, kcLanguageTag, doRenderMarkdown, kcMessages } = props;
165
149
 
166
150
  const match = key.match(/^\$\{([^{]+)\}$/);
167
151
 
@@ -172,35 +156,83 @@ function resolveMsgAdvanced<Key extends string, DoRenderMarkdown extends boolean
172
156
  args,
173
157
  kcLanguageTag,
174
158
  doRenderMarkdown,
159
+ kcMessages,
175
160
  });
176
161
 
177
162
  return (out !== undefined ? out : doRenderMarkdown ? <span>{keyUnwrappedFromCurlyBraces}</span> : keyUnwrappedFromCurlyBraces) as any;
178
163
  }
179
164
 
165
+ export type I18n = {
166
+ msg: (key: MessageKey, ...args: (string | undefined)[]) => JSX.Element;
167
+ msgStr: (key: MessageKey, ...args: (string | undefined)[]) => string;
168
+ advancedMsgStr: (key: string, ...args: (string | undefined)[]) => string;
169
+ advancedMsg: (key: string, ...args: (string | undefined)[]) => JSX.Element;
170
+ evtKcMessages: StatefulReadonlyEvt<KcMessages | undefined>;
171
+ };
172
+
173
+ export { StatefulReadonlyEvt };
174
+
180
175
  /**
181
- * When the language is switched the page is reloaded, this may appear
182
- * as a bug as you might notice that the language successfully switch before
183
- * reload.
184
- * However we need to tell Keycloak that the user have changed the language
185
- * during login so we can retrieve the "local" field of the JWT encoded accessToken.
186
- * https://user-images.githubusercontent.com/6702424/138096682-351bb61f-f24e-4caf-91b7-cca8cfa2cb58.mov
187
- *
188
176
  * advancedMsg("${access-denied}") === advancedMsg("access-denied") === msg("access-denied")
189
177
  * advancedMsg("${not-a-message-key}") === advancedMsg(not-a-message-key") === "not-a-message-key"
190
- *
191
- *
192
- * NOTE: This function is memoized, it always returns the same object for a given kcContext)
193
- *
194
178
  */
195
- export const getMsg = memoize((kcContext: KcContextLike) => {
179
+ export function createUseI18n(props: { kcMessages: KcMessages | (() => Promise<KcMessages>); kcContext: KcContextLike | undefined }) {
180
+ const { kcContext, kcMessages: kcMessagesOrFetchKcMessages } = props;
181
+
182
+ if (kcContext === undefined) {
183
+ return {
184
+ "useI18n": (): I18n => {
185
+ throw new Error("Can't use Keycloakify i18n outside of keycloak");
186
+ },
187
+ };
188
+ }
189
+
190
+ const { evtKcMessages } = (() => {
191
+ const evtKcMessages = Evt.create<KcMessages | undefined>(undefined);
192
+
193
+ if (typeof kcMessagesOrFetchKcMessages === "function") {
194
+ kcMessagesOrFetchKcMessages().then(kcMessages => (evtKcMessages.state = kcMessages));
195
+ } else {
196
+ evtKcMessages.state = kcMessagesOrFetchKcMessages;
197
+ }
198
+
199
+ return { evtKcMessages };
200
+ })();
201
+ Evt.factorize(
202
+ typeof kcMessagesOrFetchKcMessages === "function"
203
+ ? Evt.from(kcMessagesOrFetchKcMessages()).toStateful()
204
+ : Evt.create(kcMessagesOrFetchKcMessages),
205
+ );
206
+
196
207
  const kcLanguageTag = getCurrentKcLanguageTag(kcContext);
197
208
 
198
- return {
199
- "msgStr": (key: MessageKey, ...args: (string | undefined)[]): string => resolveMsg({ key, args, kcLanguageTag, "doRenderMarkdown": false }),
200
- "msg": (key: MessageKey, ...args: (string | undefined)[]): JSX.Element => resolveMsg({ key, args, kcLanguageTag, "doRenderMarkdown": true }),
201
- "advancedMsg": <Key extends string>(key: Key, ...args: (string | undefined)[]): JSX.Element =>
202
- resolveMsgAdvanced({ key, args, kcLanguageTag, "doRenderMarkdown": true }),
203
- "advancedMsgStr": <Key extends string>(key: Key, ...args: (string | undefined)[]): string =>
204
- resolveMsgAdvanced({ key, args, kcLanguageTag, "doRenderMarkdown": false }),
205
- };
206
- });
209
+ function useI18n() {
210
+ useRerenderOnStateChange(evtKcMessages);
211
+
212
+ const i18n = useMemo((): I18n => {
213
+ const kcMessages = evtKcMessages.state;
214
+
215
+ if (kcMessages === undefined) {
216
+ return {
217
+ "msgStr": () => "",
218
+ "msg": () => <></>,
219
+ "advancedMsg": () => <></>,
220
+ "advancedMsgStr": () => "",
221
+ evtKcMessages,
222
+ };
223
+ }
224
+
225
+ return {
226
+ "msgStr": (key, ...args) => resolveMsg({ key, args, kcLanguageTag, "doRenderMarkdown": false, kcMessages }),
227
+ "msg": (key, ...args): JSX.Element => resolveMsg({ key, args, kcLanguageTag, "doRenderMarkdown": true, kcMessages }),
228
+ "advancedMsg": (key, ...args) => resolveMsgAdvanced({ key, args, kcLanguageTag, "doRenderMarkdown": true, kcMessages }),
229
+ "advancedMsgStr": (key, ...args) => resolveMsgAdvanced({ key, args, kcLanguageTag, "doRenderMarkdown": false, kcMessages }),
230
+ evtKcMessages,
231
+ };
232
+ }, [evtKcMessages.state]);
233
+
234
+ return i18n;
235
+ }
236
+
237
+ return { useI18n };
238
+ }
@@ -0,0 +1,24 @@
1
+ import { kcMessages as kcMessagesBase } from "./generated_kcMessages/15.0.2/login";
2
+
3
+ export const kcMessages = {
4
+ ...kcMessagesBase,
5
+ "en": {
6
+ ...kcMessagesBase["en"],
7
+ "termsText": "⏳",
8
+ "shouldBeEqual": "{0} should be equal to {1}",
9
+ "shouldBeDifferent": "{0} should be different to {1}",
10
+ "shouldMatchPattern": "Pattern should match: `/{0}/`",
11
+ "mustBeAnInteger": "Must be an integer",
12
+ "notAValidOption": "Not a valid option",
13
+ },
14
+ "fr": {
15
+ ...kcMessagesBase["fr"],
16
+ /* spell-checker: disable */
17
+ "shouldBeEqual": "{0} doit être egale à {1}",
18
+ "shouldBeDifferent": "{0} doit être différent de {1}",
19
+ "shouldMatchPattern": "Dois respecter le schéma: `/{0}/`",
20
+ "mustBeAnInteger": "Doit être un nombre entiers",
21
+ "notAValidOption": "N'est pas une option valide",
22
+ /* spell-checker: enable */
23
+ },
24
+ };
@@ -1,11 +1,10 @@
1
1
  import "./tools/Array.prototype.every";
2
2
  import { useMemo, useReducer, Fragment } from "react";
3
3
  import type { KcContextBase, Validators, Attribute } from "./getKcContext/KcContextBase";
4
- import { getMsg } from "./i18n";
5
4
  import type { KcLanguageTag } from "./i18n";
6
5
  import { useConstCallback } from "powerhooks/useConstCallback";
7
6
  import { id } from "tsafe/id";
8
- import type { MessageKey } from "./i18n";
7
+ import type { I18n, MessageKey } from "./i18n";
9
8
  import { emailRegexp } from "./tools/emailRegExp";
10
9
 
11
10
  export function useGetErrors(params: {
@@ -16,15 +15,16 @@ export function useGetErrors(params: {
16
15
  };
17
16
  locale?: { currentLanguageTag: KcLanguageTag };
18
17
  };
18
+ useI18n: () => I18n;
19
19
  }) {
20
- const { kcContext } = params;
20
+ const { kcContext, useI18n } = params;
21
21
 
22
22
  const {
23
23
  messagesPerField,
24
24
  profile: { attributes },
25
25
  } = kcContext;
26
26
 
27
- const { msg, msgStr, advancedMsg, advancedMsgStr } = getMsg(kcContext);
27
+ const { msg, msgStr, advancedMsg, advancedMsgStr } = useI18n();
28
28
 
29
29
  const getErrors = useConstCallback((params: { name: string; fieldValueByAttributeName: Record<string, { value: string }> }) => {
30
30
  const { name, fieldValueByAttributeName } = params;
@@ -314,11 +314,13 @@ export function useFormValidationSlice(params: {
314
314
  passwordRequired: boolean;
315
315
  realm: { registrationEmailAsUsername: boolean };
316
316
  };
317
+ useI18n: () => I18n;
317
318
  /** NOTE: Try to avoid passing a new ref every render for better performances. */
318
319
  passwordValidators?: Validators;
319
320
  }) {
320
321
  const {
321
322
  kcContext,
323
+ useI18n,
322
324
  passwordValidators = {
323
325
  "length": {
324
326
  "ignore.empty.value": true,
@@ -383,6 +385,7 @@ export function useFormValidationSlice(params: {
383
385
  "attributes": attributesWithPassword,
384
386
  },
385
387
  },
388
+ useI18n,
386
389
  });
387
390
 
388
391
  const initialInternalState = useMemo(