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.
- package/kcMessages.d.ts +1 -0
- package/kcMessages.js +14 -0
- package/kcMessages.js.map +1 -0
- package/lib/components/Error.d.ts +3 -1
- package/lib/components/Error.js +3 -4
- package/lib/components/Error.js.map +1 -1
- package/lib/components/Info.d.ts +3 -1
- package/lib/components/Info.js +3 -4
- package/lib/components/Info.js.map +1 -1
- package/lib/components/KcApp.d.ts +3 -1
- package/lib/components/KcApp.js +15 -15
- package/lib/components/KcApp.js.map +1 -1
- package/lib/components/Login.d.ts +3 -1
- package/lib/components/Login.js +3 -4
- package/lib/components/Login.js.map +1 -1
- package/lib/components/LoginIdpLinkConfirm.d.ts +3 -1
- package/lib/components/LoginIdpLinkConfirm.js +3 -4
- package/lib/components/LoginIdpLinkConfirm.js.map +1 -1
- package/lib/components/LoginIdpLinkEmail.d.ts +3 -1
- package/lib/components/LoginIdpLinkEmail.js +3 -4
- package/lib/components/LoginIdpLinkEmail.js.map +1 -1
- package/lib/components/LoginOtp.d.ts +3 -1
- package/lib/components/LoginOtp.js +3 -4
- package/lib/components/LoginOtp.js.map +1 -1
- package/lib/components/LoginPageExpired.d.ts +3 -1
- package/lib/components/LoginPageExpired.js +3 -4
- package/lib/components/LoginPageExpired.js.map +1 -1
- package/lib/components/LoginResetPassword.d.ts +3 -1
- package/lib/components/LoginResetPassword.js +3 -4
- package/lib/components/LoginResetPassword.js.map +1 -1
- package/lib/components/LoginUpdatePassword.d.ts +3 -1
- package/lib/components/LoginUpdatePassword.js +3 -4
- package/lib/components/LoginUpdatePassword.js.map +1 -1
- package/lib/components/LoginUpdateProfile.d.ts +3 -1
- package/lib/components/LoginUpdateProfile.js +3 -4
- package/lib/components/LoginUpdateProfile.js.map +1 -1
- package/lib/components/LoginVerifyEmail.d.ts +3 -1
- package/lib/components/LoginVerifyEmail.js +3 -4
- package/lib/components/LoginVerifyEmail.js.map +1 -1
- package/lib/components/Register.d.ts +3 -1
- package/lib/components/Register.js +3 -4
- package/lib/components/Register.js.map +1 -1
- package/lib/components/RegisterUserProfile.d.ts +3 -1
- package/lib/components/RegisterUserProfile.js +6 -6
- package/lib/components/RegisterUserProfile.js.map +1 -1
- package/lib/components/Template.d.ts +2 -0
- package/lib/components/Template.js +2 -2
- package/lib/components/Template.js.map +1 -1
- package/lib/components/Terms.d.ts +4 -2
- package/lib/components/Terms.js +8 -8
- package/lib/components/Terms.js.map +1 -1
- package/lib/i18n/index.d.ts +20 -5249
- package/lib/i18n/index.js +79 -51
- package/lib/i18n/index.js.map +1 -1
- package/lib/i18n/kcMessages.d.ts +5222 -0
- package/lib/i18n/kcMessages.js +19 -0
- package/lib/i18n/kcMessages.js.map +1 -0
- package/lib/useFormValidationSlice.d.ts +3 -0
- package/lib/useFormValidationSlice.js +4 -4
- package/lib/useFormValidationSlice.js.map +1 -1
- package/package.json +9 -2
- package/src/kcMessages.ts +1 -0
- package/src/lib/components/Error.tsx +4 -4
- package/src/lib/components/Info.tsx +5 -5
- package/src/lib/components/KcApp.tsx +16 -15
- package/src/lib/components/Login.tsx +4 -4
- package/src/lib/components/LoginIdpLinkConfirm.tsx +39 -37
- package/src/lib/components/LoginIdpLinkEmail.tsx +27 -25
- package/src/lib/components/LoginOtp.tsx +4 -4
- package/src/lib/components/LoginPageExpired.tsx +31 -29
- package/src/lib/components/LoginResetPassword.tsx +60 -53
- package/src/lib/components/LoginUpdatePassword.tsx +98 -96
- package/src/lib/components/LoginUpdateProfile.tsx +100 -92
- package/src/lib/components/LoginVerifyEmail.tsx +25 -23
- package/src/lib/components/Register.tsx +4 -4
- package/src/lib/components/RegisterUserProfile.tsx +68 -55
- package/src/lib/components/Template.tsx +5 -3
- package/src/lib/components/Terms.tsx +14 -8
- package/src/lib/i18n/index.tsx +81 -49
- package/src/lib/i18n/kcMessages.ts +24 -0
- 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 {
|
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 } =
|
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 {
|
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(
|
12
|
-
|
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
|
-
|
15
|
+
const { msg, msgStr } = useI18n();
|
15
16
|
|
16
|
-
|
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
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
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
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
</
|
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
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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
|
-
</
|
63
|
-
|
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 } =
|
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 {
|
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 } =
|
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 {
|
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
|
-
|
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 } =
|
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")}
|
package/src/lib/i18n/index.tsx
CHANGED
@@ -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
|
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
|
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
|
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
|
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
|
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
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
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 } =
|
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(
|