Refactor: Use hook instead of Context for i18n
This commit is contained in:
@ -2,16 +2,16 @@ import React, { memo } from "react";
|
|||||||
import Template from "./Template";
|
import Template from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useI18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const Error = memo(({ kcContext, ...props }: { kcContext: KcContextBase.Error } & KcProps) => {
|
|
||||||
const { msg } = useI18n();
|
|
||||||
|
|
||||||
|
const Error = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.Error; i18n: I18n } & KcProps) => {
|
||||||
const { message, client } = kcContext;
|
const { message, client } = kcContext;
|
||||||
|
|
||||||
|
const { msg } = i18n;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, i18n, ...props }}
|
||||||
doFetchDefaultThemeResources={true}
|
doFetchDefaultThemeResources={true}
|
||||||
displayMessage={false}
|
displayMessage={false}
|
||||||
headerNode={msg("errorTitle")}
|
headerNode={msg("errorTitle")}
|
||||||
|
@ -3,10 +3,10 @@ import Template from "./Template";
|
|||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import { assert } from "../tools/assert";
|
import { assert } from "../tools/assert";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useI18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const Info = memo(({ kcContext, ...props }: { kcContext: KcContextBase.Info } & KcProps) => {
|
const Info = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.Info; i18n: I18n } & KcProps) => {
|
||||||
const { msg, msgStr } = useI18n();
|
const { msgStr, msg } = i18n;
|
||||||
|
|
||||||
assert(kcContext.message !== undefined);
|
assert(kcContext.message !== undefined);
|
||||||
|
|
||||||
@ -14,7 +14,7 @@ const Info = memo(({ kcContext, ...props }: { kcContext: KcContextBase.Info } &
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, i18n, ...props }}
|
||||||
doFetchDefaultThemeResources={true}
|
doFetchDefaultThemeResources={true}
|
||||||
displayMessage={false}
|
displayMessage={false}
|
||||||
headerNode={messageHeader !== undefined ? <>{messageHeader}</> : <>{message.summary}</>}
|
headerNode={messageHeader !== undefined ? <>{messageHeader}</> : <>{message.summary}</>}
|
||||||
|
@ -1,7 +1,8 @@
|
|||||||
import React, { lazy, memo, Suspense } from "react";
|
import React, { lazy, memo, Suspense } from "react";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import { I18nProvider } from "../i18n";
|
import { __unsafe_useI18n as useI18n } from "../i18n";
|
||||||
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const Login = lazy(() => import("./Login"));
|
const Login = lazy(() => import("./Login"));
|
||||||
const Register = lazy(() => import("./Register"));
|
const Register = lazy(() => import("./Register"));
|
||||||
@ -20,48 +21,60 @@ const LoginIdpLinkEmail = lazy(() => import("./LoginIdpLinkEmail"));
|
|||||||
const LoginConfigTotp = lazy(() => import("./LoginConfigTotp"));
|
const LoginConfigTotp = lazy(() => import("./LoginConfigTotp"));
|
||||||
const LogoutConfirm = lazy(() => import("./LogoutConfirm"));
|
const LogoutConfirm = lazy(() => import("./LogoutConfirm"));
|
||||||
|
|
||||||
const KcApp = memo(({ kcContext, ...props }: { kcContext: KcContextBase } & KcProps) => {
|
const KcApp = memo(({ kcContext, i18n: userProvidedI18n, ...props }: { kcContext: KcContextBase; i18n?: I18n } & KcProps) => {
|
||||||
|
const i18n = (function useClosure() {
|
||||||
|
const i18n = useI18n({
|
||||||
|
kcContext,
|
||||||
|
"extraMessages": {},
|
||||||
|
"doSkip": userProvidedI18n !== undefined,
|
||||||
|
});
|
||||||
|
|
||||||
|
return userProvidedI18n ?? i18n;
|
||||||
|
})();
|
||||||
|
|
||||||
|
if (i18n === undefined) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<I18nProvider kcContext={kcContext}>
|
<Suspense>
|
||||||
<Suspense>
|
{(() => {
|
||||||
{(() => {
|
switch (kcContext.pageId) {
|
||||||
switch (kcContext.pageId) {
|
case "login.ftl":
|
||||||
case "login.ftl":
|
return <Login {...{ kcContext, i18n, ...props }} />;
|
||||||
return <Login {...{ kcContext, ...props }} />;
|
case "register.ftl":
|
||||||
case "register.ftl":
|
return <Register {...{ kcContext, i18n, ...props }} />;
|
||||||
return <Register {...{ kcContext, ...props }} />;
|
case "register-user-profile.ftl":
|
||||||
case "register-user-profile.ftl":
|
return <RegisterUserProfile {...{ kcContext, i18n, ...props }} />;
|
||||||
return <RegisterUserProfile {...{ kcContext, ...props }} />;
|
case "info.ftl":
|
||||||
case "info.ftl":
|
return <Info {...{ kcContext, i18n, ...props }} />;
|
||||||
return <Info {...{ kcContext, ...props }} />;
|
case "error.ftl":
|
||||||
case "error.ftl":
|
return <Error {...{ kcContext, i18n, ...props }} />;
|
||||||
return <Error {...{ kcContext, ...props }} />;
|
case "login-reset-password.ftl":
|
||||||
case "login-reset-password.ftl":
|
return <LoginResetPassword {...{ kcContext, i18n, ...props }} />;
|
||||||
return <LoginResetPassword {...{ kcContext, ...props }} />;
|
case "login-verify-email.ftl":
|
||||||
case "login-verify-email.ftl":
|
return <LoginVerifyEmail {...{ kcContext, i18n, ...props }} />;
|
||||||
return <LoginVerifyEmail {...{ kcContext, ...props }} />;
|
case "terms.ftl":
|
||||||
case "terms.ftl":
|
return <Terms {...{ kcContext, i18n, ...props }} />;
|
||||||
return <Terms {...{ kcContext, ...props }} />;
|
case "login-otp.ftl":
|
||||||
case "login-otp.ftl":
|
return <LoginOtp {...{ kcContext, i18n, ...props }} />;
|
||||||
return <LoginOtp {...{ kcContext, ...props }} />;
|
case "login-update-password.ftl":
|
||||||
case "login-update-password.ftl":
|
return <LoginUpdatePassword {...{ kcContext, i18n, ...props }} />;
|
||||||
return <LoginUpdatePassword {...{ kcContext, ...props }} />;
|
case "login-update-profile.ftl":
|
||||||
case "login-update-profile.ftl":
|
return <LoginUpdateProfile {...{ kcContext, i18n, ...props }} />;
|
||||||
return <LoginUpdateProfile {...{ kcContext, ...props }} />;
|
case "login-idp-link-confirm.ftl":
|
||||||
case "login-idp-link-confirm.ftl":
|
return <LoginIdpLinkConfirm {...{ kcContext, i18n, ...props }} />;
|
||||||
return <LoginIdpLinkConfirm {...{ kcContext, ...props }} />;
|
case "login-idp-link-email.ftl":
|
||||||
case "login-idp-link-email.ftl":
|
return <LoginIdpLinkEmail {...{ kcContext, i18n, ...props }} />;
|
||||||
return <LoginIdpLinkEmail {...{ kcContext, ...props }} />;
|
case "login-page-expired.ftl":
|
||||||
case "login-page-expired.ftl":
|
return <LoginPageExpired {...{ kcContext, i18n, ...props }} />;
|
||||||
return <LoginPageExpired {...{ kcContext, ...props }} />;
|
case "login-config-totp.ftl":
|
||||||
case "login-config-totp.ftl":
|
return <LoginConfigTotp {...{ kcContext, i18n, ...props }} />;
|
||||||
return <LoginConfigTotp {...{ kcContext, ...props }} />;
|
case "logout-confirm.ftl":
|
||||||
case "logout-confirm.ftl":
|
return <LogoutConfirm {...{ kcContext, i18n, ...props }} />;
|
||||||
return <LogoutConfirm {...{ kcContext, ...props }} />;
|
}
|
||||||
}
|
})()}
|
||||||
})()}
|
</Suspense>
|
||||||
</Suspense>
|
|
||||||
</I18nProvider>
|
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -5,12 +5,12 @@ import type { KcContextBase } from "../getKcContext/KcContextBase";
|
|||||||
import { useCssAndCx } from "tss-react";
|
import { useCssAndCx } from "tss-react";
|
||||||
import { useConstCallback } from "powerhooks/useConstCallback";
|
import { useConstCallback } from "powerhooks/useConstCallback";
|
||||||
import type { FormEventHandler } from "react";
|
import type { FormEventHandler } from "react";
|
||||||
import { useI18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const Login = memo(({ kcContext, ...props }: { kcContext: KcContextBase.Login } & KcProps) => {
|
const Login = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.Login; i18n: I18n } & KcProps) => {
|
||||||
const { social, realm, url, usernameEditDisabled, login, auth, registrationDisabled } = kcContext;
|
const { social, realm, url, usernameEditDisabled, login, auth, registrationDisabled } = kcContext;
|
||||||
|
|
||||||
const { msg, msgStr } = useI18n();
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
const { cx } = useCssAndCx();
|
const { cx } = useCssAndCx();
|
||||||
|
|
||||||
@ -32,7 +32,7 @@ const Login = memo(({ kcContext, ...props }: { kcContext: KcContextBase.Login }
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, i18n, ...props }}
|
||||||
doFetchDefaultThemeResources={true}
|
doFetchDefaultThemeResources={true}
|
||||||
displayInfo={social.displayInfo}
|
displayInfo={social.displayInfo}
|
||||||
displayWide={realm.password && social.providers !== undefined}
|
displayWide={realm.password && social.providers !== undefined}
|
||||||
|
@ -2,15 +2,16 @@ import React, { memo } from "react";
|
|||||||
import Template from "./Template";
|
import Template from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useI18n } from "../i18n";
|
|
||||||
import { useCssAndCx } from "tss-react";
|
import { useCssAndCx } from "tss-react";
|
||||||
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginConfigTotp = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginConfigTotp } & KcProps) => {
|
const LoginConfigTotp = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginConfigTotp; i18n: I18n } & KcProps) => {
|
||||||
const { url, isAppInitiatedAction, totp, mode, messagesPerField } = kcContext;
|
const { url, isAppInitiatedAction, totp, mode, messagesPerField } = kcContext;
|
||||||
|
|
||||||
const { cx } = useCssAndCx();
|
const { cx } = useCssAndCx();
|
||||||
|
|
||||||
const { msg, msgStr } = useI18n();
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
const algToKeyUriAlg: Record<KcContextBase.LoginConfigTotp["totp"]["policy"]["algorithm"], string> = {
|
const algToKeyUriAlg: Record<KcContextBase.LoginConfigTotp["totp"]["policy"]["algorithm"], string> = {
|
||||||
HmacSHA1: "SHA1",
|
HmacSHA1: "SHA1",
|
||||||
HmacSHA256: "SHA256",
|
HmacSHA256: "SHA256",
|
||||||
@ -19,7 +20,7 @@ const LoginConfigTotp = memo(({ kcContext, ...props }: { kcContext: KcContextBas
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, i18n, ...props }}
|
||||||
doFetchDefaultThemeResources={true}
|
doFetchDefaultThemeResources={true}
|
||||||
headerNode={msg("loginTotpTitle")}
|
headerNode={msg("loginTotpTitle")}
|
||||||
formNode={
|
formNode={
|
||||||
|
@ -2,19 +2,19 @@ import React, { memo } from "react";
|
|||||||
import Template from "./Template";
|
import Template from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useI18n } from "../i18n";
|
|
||||||
import { useCssAndCx } from "tss-react";
|
import { useCssAndCx } from "tss-react";
|
||||||
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginIdpLinkConfirm = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginIdpLinkConfirm } & KcProps) => {
|
const LoginIdpLinkConfirm = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginIdpLinkConfirm; i18n: I18n } & KcProps) => {
|
||||||
const { url, idpAlias } = kcContext;
|
const { url, idpAlias } = kcContext;
|
||||||
|
|
||||||
const { msg } = useI18n();
|
const { msg } = i18n;
|
||||||
|
|
||||||
const { cx } = useCssAndCx();
|
const { cx } = useCssAndCx();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, i18n, ...props }}
|
||||||
doFetchDefaultThemeResources={true}
|
doFetchDefaultThemeResources={true}
|
||||||
headerNode={msg("confirmLinkIdpTitle")}
|
headerNode={msg("confirmLinkIdpTitle")}
|
||||||
formNode={
|
formNode={
|
||||||
|
@ -2,16 +2,16 @@ import React, { memo } from "react";
|
|||||||
import Template from "./Template";
|
import Template from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useI18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginIdpLinkEmail = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginIdpLinkEmail } & KcProps) => {
|
const LoginIdpLinkEmail = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginIdpLinkEmail; i18n: I18n } & KcProps) => {
|
||||||
const { url, realm, brokerContext, idpAlias } = kcContext;
|
const { url, realm, brokerContext, idpAlias } = kcContext;
|
||||||
|
|
||||||
const { msg } = useI18n();
|
const { msg } = i18n;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, i18n, ...props }}
|
||||||
doFetchDefaultThemeResources={true}
|
doFetchDefaultThemeResources={true}
|
||||||
headerNode={msg("emailLinkIdpTitle", idpAlias)}
|
headerNode={msg("emailLinkIdpTitle", idpAlias)}
|
||||||
formNode={
|
formNode={
|
||||||
|
@ -2,17 +2,17 @@ import React, { useEffect, memo } from "react";
|
|||||||
import Template from "./Template";
|
import Template from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useI18n } from "../i18n";
|
|
||||||
import { headInsert } from "../tools/headInsert";
|
import { headInsert } from "../tools/headInsert";
|
||||||
import { pathJoin } from "../../bin/tools/pathJoin";
|
import { pathJoin } from "../../bin/tools/pathJoin";
|
||||||
import { useCssAndCx } from "tss-react";
|
import { useCssAndCx } from "tss-react";
|
||||||
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginOtp = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginOtp } & KcProps) => {
|
const LoginOtp = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginOtp; i18n: I18n } & KcProps) => {
|
||||||
const { otpLogin, url } = kcContext;
|
const { otpLogin, url } = kcContext;
|
||||||
|
|
||||||
const { cx } = useCssAndCx();
|
const { cx } = useCssAndCx();
|
||||||
|
|
||||||
const { msg, msgStr } = useI18n();
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let isCleanedUp = false;
|
let isCleanedUp = false;
|
||||||
@ -33,7 +33,7 @@ const LoginOtp = memo(({ kcContext, ...props }: { kcContext: KcContextBase.Login
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, i18n, ...props }}
|
||||||
doFetchDefaultThemeResources={true}
|
doFetchDefaultThemeResources={true}
|
||||||
headerNode={msg("doLogIn")}
|
headerNode={msg("doLogIn")}
|
||||||
formNode={
|
formNode={
|
||||||
|
@ -2,16 +2,16 @@ import React, { memo } from "react";
|
|||||||
import Template from "./Template";
|
import Template from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useI18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginPageExpired = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginPageExpired } & KcProps) => {
|
const LoginPageExpired = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginPageExpired; i18n: I18n } & KcProps) => {
|
||||||
const { url } = kcContext;
|
const { url } = kcContext;
|
||||||
|
|
||||||
const { msg } = useI18n();
|
const { msg } = i18n;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, i18n, ...props }}
|
||||||
doFetchDefaultThemeResources={true}
|
doFetchDefaultThemeResources={true}
|
||||||
displayMessage={false}
|
displayMessage={false}
|
||||||
headerNode={msg("pageExpiredTitle")}
|
headerNode={msg("pageExpiredTitle")}
|
||||||
|
@ -2,19 +2,19 @@ import React, { memo } from "react";
|
|||||||
import Template from "./Template";
|
import Template from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useI18n } from "../i18n";
|
|
||||||
import { useCssAndCx } from "tss-react";
|
import { useCssAndCx } from "tss-react";
|
||||||
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginResetPassword = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginResetPassword } & KcProps) => {
|
const LoginResetPassword = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginResetPassword; i18n: I18n } & KcProps) => {
|
||||||
const { url, realm, auth } = kcContext;
|
const { url, realm, auth } = kcContext;
|
||||||
|
|
||||||
const { msg, msgStr } = useI18n();
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
const { cx } = useCssAndCx();
|
const { cx } = useCssAndCx();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, i18n, ...props }}
|
||||||
doFetchDefaultThemeResources={true}
|
doFetchDefaultThemeResources={true}
|
||||||
displayMessage={false}
|
displayMessage={false}
|
||||||
headerNode={msg("emailForgotTitle")}
|
headerNode={msg("emailForgotTitle")}
|
||||||
|
@ -2,19 +2,19 @@ import React, { memo } from "react";
|
|||||||
import Template from "./Template";
|
import Template from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useI18n } from "../i18n";
|
|
||||||
import { useCssAndCx } from "tss-react";
|
import { useCssAndCx } from "tss-react";
|
||||||
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginUpdatePassword = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginUpdatePassword } & KcProps) => {
|
const LoginUpdatePassword = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginUpdatePassword; i18n: I18n } & KcProps) => {
|
||||||
const { cx } = useCssAndCx();
|
const { cx } = useCssAndCx();
|
||||||
|
|
||||||
const { msg, msgStr } = useI18n();
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
const { url, messagesPerField, isAppInitiatedAction, username } = kcContext;
|
const { url, messagesPerField, isAppInitiatedAction, username } = kcContext;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, i18n, ...props }}
|
||||||
doFetchDefaultThemeResources={true}
|
doFetchDefaultThemeResources={true}
|
||||||
headerNode={msg("updatePasswordTitle")}
|
headerNode={msg("updatePasswordTitle")}
|
||||||
formNode={
|
formNode={
|
||||||
|
@ -2,19 +2,19 @@ import React, { memo } from "react";
|
|||||||
import Template from "./Template";
|
import Template from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useI18n } from "../i18n";
|
|
||||||
import { useCssAndCx } from "tss-react";
|
import { useCssAndCx } from "tss-react";
|
||||||
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginUpdateProfile = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginUpdateProfile } & KcProps) => {
|
const LoginUpdateProfile = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginUpdateProfile; i18n: I18n } & KcProps) => {
|
||||||
const { cx } = useCssAndCx();
|
const { cx } = useCssAndCx();
|
||||||
|
|
||||||
const { msg, msgStr } = useI18n();
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
const { url, user, messagesPerField, isAppInitiatedAction } = kcContext;
|
const { url, user, messagesPerField, isAppInitiatedAction } = kcContext;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, i18n, ...props }}
|
||||||
doFetchDefaultThemeResources={true}
|
doFetchDefaultThemeResources={true}
|
||||||
headerNode={msg("loginProfileTitle")}
|
headerNode={msg("loginProfileTitle")}
|
||||||
formNode={
|
formNode={
|
||||||
|
@ -2,16 +2,16 @@ import React, { memo } from "react";
|
|||||||
import Template from "./Template";
|
import Template from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useI18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LoginVerifyEmail = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginVerifyEmail } & KcProps) => {
|
const LoginVerifyEmail = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LoginVerifyEmail; i18n: I18n } & KcProps) => {
|
||||||
const { msg } = useI18n();
|
const { msg } = i18n;
|
||||||
|
|
||||||
const { url, user } = kcContext;
|
const { url, user } = kcContext;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, i18n, ...props }}
|
||||||
doFetchDefaultThemeResources={true}
|
doFetchDefaultThemeResources={true}
|
||||||
displayMessage={false}
|
displayMessage={false}
|
||||||
headerNode={msg("emailVerifyTitle")}
|
headerNode={msg("emailVerifyTitle")}
|
||||||
|
@ -1,21 +1,20 @@
|
|||||||
import React, { memo } from "react";
|
import React, { memo } from "react";
|
||||||
import { useCssAndCx } from "tss-react";
|
import { useCssAndCx } from "tss-react";
|
||||||
|
|
||||||
import Template from "./Template";
|
import Template from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useI18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const LogoutConfirm = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LogoutConfirm } & KcProps) => {
|
const LogoutConfirm = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.LogoutConfirm; i18n: I18n } & KcProps) => {
|
||||||
const { url, client, logoutConfirm } = kcContext;
|
const { url, client, logoutConfirm } = kcContext;
|
||||||
|
|
||||||
const { cx } = useCssAndCx();
|
const { cx } = useCssAndCx();
|
||||||
|
|
||||||
const { msg, msgStr } = useI18n();
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, i18n, ...props }}
|
||||||
doFetchDefaultThemeResources={true}
|
doFetchDefaultThemeResources={true}
|
||||||
displayMessage={false}
|
displayMessage={false}
|
||||||
headerNode={msg("logoutConfirmTitle")}
|
headerNode={msg("logoutConfirmTitle")}
|
||||||
|
@ -2,19 +2,19 @@ import React, { memo } from "react";
|
|||||||
import Template from "./Template";
|
import Template from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useI18n } from "../i18n";
|
|
||||||
import { useCssAndCx } from "tss-react";
|
import { useCssAndCx } from "tss-react";
|
||||||
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const Register = memo(({ kcContext, ...props }: { kcContext: KcContextBase.Register } & KcProps) => {
|
const Register = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.Register; i18n: I18n } & KcProps) => {
|
||||||
const { url, messagesPerField, register, realm, passwordRequired, recaptchaRequired, recaptchaSiteKey } = kcContext;
|
const { url, messagesPerField, register, realm, passwordRequired, recaptchaRequired, recaptchaSiteKey } = kcContext;
|
||||||
|
|
||||||
const { msg, msgStr } = useI18n();
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
const { cx } = useCssAndCx();
|
const { cx } = useCssAndCx();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, i18n, ...props }}
|
||||||
doFetchDefaultThemeResources={true}
|
doFetchDefaultThemeResources={true}
|
||||||
headerNode={msg("registerTitle")}
|
headerNode={msg("registerTitle")}
|
||||||
formNode={
|
formNode={
|
||||||
|
@ -2,16 +2,16 @@ import React, { useMemo, memo, useEffect, useState, Fragment } from "react";
|
|||||||
import Template from "./Template";
|
import Template from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase, Attribute } from "../getKcContext/KcContextBase";
|
import type { KcContextBase, Attribute } from "../getKcContext/KcContextBase";
|
||||||
import { useI18n } from "../i18n";
|
|
||||||
import { useCssAndCx } from "tss-react";
|
import { useCssAndCx } from "tss-react";
|
||||||
import type { ReactComponent } from "../tools/ReactComponent";
|
import type { ReactComponent } from "../tools/ReactComponent";
|
||||||
import { useCallbackFactory } from "powerhooks/useCallbackFactory";
|
import { useCallbackFactory } from "powerhooks/useCallbackFactory";
|
||||||
import { useFormValidationSlice } from "../useFormValidationSlice";
|
import { useFormValidationSlice } from "../useFormValidationSlice";
|
||||||
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
const RegisterUserProfile = memo(({ kcContext, ...props_ }: { kcContext: KcContextBase.RegisterUserProfile } & KcProps) => {
|
const RegisterUserProfile = memo(({ kcContext, i18n, ...props_ }: { kcContext: KcContextBase.RegisterUserProfile; i18n: I18n } & KcProps) => {
|
||||||
const { url, messagesPerField, recaptchaRequired, recaptchaSiteKey } = kcContext;
|
const { url, messagesPerField, recaptchaRequired, recaptchaSiteKey } = kcContext;
|
||||||
|
|
||||||
const { msg, msgStr } = useI18n();
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
const { cx, css } = useCssAndCx();
|
const { cx, css } = useCssAndCx();
|
||||||
|
|
||||||
@ -27,14 +27,14 @@ const RegisterUserProfile = memo(({ kcContext, ...props_ }: { kcContext: KcConte
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, i18n, ...props }}
|
||||||
displayMessage={messagesPerField.exists("global")}
|
displayMessage={messagesPerField.exists("global")}
|
||||||
displayRequiredFields={true}
|
displayRequiredFields={true}
|
||||||
doFetchDefaultThemeResources={true}
|
doFetchDefaultThemeResources={true}
|
||||||
headerNode={msg("registerTitle")}
|
headerNode={msg("registerTitle")}
|
||||||
formNode={
|
formNode={
|
||||||
<form id="kc-register-form" className={cx(props.kcFormClass)} action={url.registrationAction} method="post">
|
<form id="kc-register-form" className={cx(props.kcFormClass)} action={url.registrationAction} method="post">
|
||||||
<UserProfileFormFields kcContext={kcContext} onIsFormSubmittableValueChange={setIsFomSubmittable} {...props} />
|
<UserProfileFormFields kcContext={kcContext} onIsFormSubmittableValueChange={setIsFomSubmittable} i18n={i18n} {...props} />
|
||||||
{recaptchaRequired && (
|
{recaptchaRequired && (
|
||||||
<div className="form-group">
|
<div className="form-group">
|
||||||
<div className={cx(props.kcInputWrapperClass)}>
|
<div className={cx(props.kcInputWrapperClass)}>
|
||||||
@ -66,15 +66,15 @@ const RegisterUserProfile = memo(({ kcContext, ...props_ }: { kcContext: KcConte
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
type UserProfileFormFieldsProps = { kcContext: KcContextBase.RegisterUserProfile } & KcProps &
|
type UserProfileFormFieldsProps = { kcContext: KcContextBase.RegisterUserProfile; i18n: I18n } & KcProps &
|
||||||
Partial<Record<"BeforeField" | "AfterField", ReactComponent<{ attribute: Attribute }>>> & {
|
Partial<Record<"BeforeField" | "AfterField", ReactComponent<{ attribute: Attribute }>>> & {
|
||||||
onIsFormSubmittableValueChange: (isFormSubmittable: boolean) => void;
|
onIsFormSubmittableValueChange: (isFormSubmittable: boolean) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const UserProfileFormFields = memo(({ kcContext, onIsFormSubmittableValueChange, ...props }: UserProfileFormFieldsProps) => {
|
const UserProfileFormFields = memo(({ kcContext, onIsFormSubmittableValueChange, i18n, ...props }: UserProfileFormFieldsProps) => {
|
||||||
const { cx, css } = useCssAndCx();
|
const { cx, css } = useCssAndCx();
|
||||||
|
|
||||||
const { advancedMsg } = useI18n();
|
const { advancedMsg } = i18n;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
formValidationState: { fieldStateByAttributeName, isFormSubmittable },
|
formValidationState: { fieldStateByAttributeName, isFormSubmittable },
|
||||||
@ -82,6 +82,7 @@ const UserProfileFormFields = memo(({ kcContext, onIsFormSubmittableValueChange,
|
|||||||
attributesWithPassword,
|
attributesWithPassword,
|
||||||
} = useFormValidationSlice({
|
} = useFormValidationSlice({
|
||||||
kcContext,
|
kcContext,
|
||||||
|
i18n,
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -8,7 +8,7 @@ import { pathJoin } from "../../bin/tools/pathJoin";
|
|||||||
import { useConstCallback } from "powerhooks/useConstCallback";
|
import { useConstCallback } from "powerhooks/useConstCallback";
|
||||||
import type { KcTemplateProps } from "./KcProps";
|
import type { KcTemplateProps } from "./KcProps";
|
||||||
import { useCssAndCx } from "tss-react";
|
import { useCssAndCx } from "tss-react";
|
||||||
import { useI18n } from "../i18n";
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
export type TemplateProps = {
|
export type TemplateProps = {
|
||||||
displayInfo?: boolean;
|
displayInfo?: boolean;
|
||||||
@ -24,7 +24,7 @@ export type TemplateProps = {
|
|||||||
* to avoid pulling the default theme assets.
|
* to avoid pulling the default theme assets.
|
||||||
*/
|
*/
|
||||||
doFetchDefaultThemeResources: boolean;
|
doFetchDefaultThemeResources: boolean;
|
||||||
} & { kcContext: KcContextBase } & KcTemplateProps;
|
} & { kcContext: KcContextBase; i18n: I18n } & KcTemplateProps;
|
||||||
|
|
||||||
const Template = memo((props: TemplateProps) => {
|
const Template = memo((props: TemplateProps) => {
|
||||||
const {
|
const {
|
||||||
@ -38,6 +38,7 @@ const Template = memo((props: TemplateProps) => {
|
|||||||
formNode,
|
formNode,
|
||||||
infoNode = null,
|
infoNode = null,
|
||||||
kcContext,
|
kcContext,
|
||||||
|
i18n,
|
||||||
doFetchDefaultThemeResources,
|
doFetchDefaultThemeResources,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
@ -47,7 +48,7 @@ const Template = memo((props: TemplateProps) => {
|
|||||||
console.log("Rendering this page with react using keycloakify");
|
console.log("Rendering this page with react using keycloakify");
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const { msg, changeLocale, labelBySupportedLanguageTag, currentLanguageTag } = useI18n();
|
const { msg, changeLocale, labelBySupportedLanguageTag, currentLanguageTag } = i18n;
|
||||||
|
|
||||||
const onChangeLanguageClickFactory = useCallbackFactory(([kcLanguageTag]: [string]) => changeLocale(kcLanguageTag));
|
const onChangeLanguageClickFactory = useCallbackFactory(([kcLanguageTag]: [string]) => changeLocale(kcLanguageTag));
|
||||||
|
|
||||||
|
@ -2,23 +2,36 @@ import React, { useEffect, memo } from "react";
|
|||||||
import Template from "./Template";
|
import Template from "./Template";
|
||||||
import type { KcProps } from "./KcProps";
|
import type { KcProps } from "./KcProps";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
import { useI18n } from "../i18n";
|
|
||||||
import { useCssAndCx } from "tss-react";
|
import { useCssAndCx } from "tss-react";
|
||||||
import { Evt } from "evt";
|
import { Evt } from "evt";
|
||||||
import { useRerenderOnStateChange } from "evt/hooks";
|
import { useRerenderOnStateChange } from "evt/hooks";
|
||||||
|
import { assert } from "tsafe/assert";
|
||||||
|
import { fallbackLanguageTag } from "../i18n";
|
||||||
|
import type { I18n } from "../i18n";
|
||||||
|
|
||||||
export const evtTermMarkdown = Evt.create<string | undefined>(undefined);
|
export const evtTermMarkdown = Evt.create<string | undefined>(undefined);
|
||||||
|
|
||||||
/** Allow to avoid bundling the terms and download it on demand*/
|
export type KcContextLike = {
|
||||||
export function useDownloadTerms(params: { downloadTermMarkdown: (params: { currentLanguageTag: string }) => Promise<string> }) {
|
locale?: {
|
||||||
const { downloadTermMarkdown } = params;
|
currentLanguageTag: string;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
const { currentLanguageTag } = useI18n();
|
assert<KcContextBase extends KcContextLike ? true : false>();
|
||||||
|
|
||||||
|
/** Allow to avoid bundling the terms and download it on demand*/
|
||||||
|
export function useDownloadTerms(params: {
|
||||||
|
kcContext: KcContextLike;
|
||||||
|
downloadTermMarkdown: (params: { currentLanguageTag: string }) => Promise<string>;
|
||||||
|
}) {
|
||||||
|
const { kcContext, downloadTermMarkdown } = params;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
let isMounted = true;
|
let isMounted = true;
|
||||||
|
|
||||||
downloadTermMarkdown({ currentLanguageTag }).then(thermMarkdown => {
|
downloadTermMarkdown({
|
||||||
|
"currentLanguageTag": kcContext.locale?.currentLanguageTag ?? fallbackLanguageTag,
|
||||||
|
}).then(thermMarkdown => {
|
||||||
if (!isMounted) {
|
if (!isMounted) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -32,8 +45,8 @@ export function useDownloadTerms(params: { downloadTermMarkdown: (params: { curr
|
|||||||
}, []);
|
}, []);
|
||||||
}
|
}
|
||||||
|
|
||||||
const Terms = memo(({ kcContext, ...props }: { kcContext: KcContextBase.Terms } & KcProps) => {
|
const Terms = memo(({ kcContext, i18n, ...props }: { kcContext: KcContextBase.Terms; i18n: I18n } & KcProps) => {
|
||||||
const { msg, msgStr } = useI18n();
|
const { msg, msgStr } = i18n;
|
||||||
|
|
||||||
useRerenderOnStateChange(evtTermMarkdown);
|
useRerenderOnStateChange(evtTermMarkdown);
|
||||||
|
|
||||||
@ -47,7 +60,7 @@ const Terms = memo(({ kcContext, ...props }: { kcContext: KcContextBase.Terms }
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Template
|
<Template
|
||||||
{...{ kcContext, ...props }}
|
{...{ kcContext, i18n, ...props }}
|
||||||
doFetchDefaultThemeResources={true}
|
doFetchDefaultThemeResources={true}
|
||||||
displayMessage={false}
|
displayMessage={false}
|
||||||
headerNode={msg("termsTitle")}
|
headerNode={msg("termsTitle")}
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import type { PageId } from "../../bin/build-keycloak-theme/generateFtl";
|
import type { PageId } from "../../bin/build-keycloak-theme/generateFtl";
|
||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
import type { Equals } from "tsafe";
|
import type { Equals } from "tsafe";
|
||||||
import type { BaseMessageKey } from "../i18n/createI18nApi";
|
import type { MessageKeyBase } from "../i18n";
|
||||||
|
|
||||||
type ExtractAfterStartingWith<Prefix extends string, StrEnum> = StrEnum extends `${Prefix}${infer U}` ? U : never;
|
type ExtractAfterStartingWith<Prefix extends string, StrEnum> = StrEnum extends `${Prefix}${infer U}` ? U : never;
|
||||||
|
|
||||||
@ -153,7 +153,7 @@ export declare namespace KcContextBase {
|
|||||||
export type Info = Common & {
|
export type Info = Common & {
|
||||||
pageId: "info.ftl";
|
pageId: "info.ftl";
|
||||||
messageHeader?: string;
|
messageHeader?: string;
|
||||||
requiredActions?: ExtractAfterStartingWith<"requiredAction.", BaseMessageKey>[];
|
requiredActions?: ExtractAfterStartingWith<"requiredAction.", MessageKeyBase>[];
|
||||||
skipLink: boolean;
|
skipLink: boolean;
|
||||||
pageRedirectUri?: string;
|
pageRedirectUri?: string;
|
||||||
actionUri?: string;
|
actionUri?: string;
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
import { createI18nApi } from "./createI18nApi";
|
|
||||||
import type { I18n } from "./createI18nApi";
|
|
||||||
|
|
||||||
export const { I18nProvider, useI18n } = createI18nApi({
|
|
||||||
"extraMessages": {},
|
|
||||||
});
|
|
||||||
|
|
||||||
export type MessageKey = ReturnType<typeof useI18n> extends I18n<infer U> ? U : never;
|
|
@ -1,15 +1,25 @@
|
|||||||
import "minimal-polyfills/Object.fromEntries";
|
import "minimal-polyfills/Object.fromEntries";
|
||||||
//NOTE for later: https://github.com/remarkjs/react-markdown/blob/236182ecf30bd89c1e5a7652acaf8d0bf81e6170/src/renderers.js#L7-L35
|
//NOTE for later: https://github.com/remarkjs/react-markdown/blob/236182ecf30bd89c1e5a7652acaf8d0bf81e6170/src/renderers.js#L7-L35
|
||||||
import React, { createContext, useContext, useEffect, useState, memo } from "react";
|
import React, { useEffect, useState } from "react";
|
||||||
import type { ReactNode } from "react";
|
|
||||||
import ReactMarkdown from "react-markdown";
|
import ReactMarkdown from "react-markdown";
|
||||||
import type baseMessages from "./generated_messages/18.0.1/login/en";
|
import type baseMessages from "./generated_messages/18.0.1/login/en";
|
||||||
import { assert } from "tsafe/assert";
|
import { assert } from "tsafe/assert";
|
||||||
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
import type { KcContextBase } from "../getKcContext/KcContextBase";
|
||||||
|
|
||||||
const fallbackLanguageTag = "en";
|
export const fallbackLanguageTag = "en";
|
||||||
|
|
||||||
export type I18n<MessageKey extends string> = {
|
export type KcContextLike = {
|
||||||
|
locale?: {
|
||||||
|
currentLanguageTag: string;
|
||||||
|
supported: { languageTag: string; url: string; label: string }[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
assert<KcContextBase extends KcContextLike ? true : false>();
|
||||||
|
|
||||||
|
export type MessageKeyBase = keyof typeof baseMessages | keyof typeof keycloakifyExtraMessages[typeof fallbackLanguageTag];
|
||||||
|
|
||||||
|
export type I18n<MessageKey extends string = MessageKeyBase> = {
|
||||||
msgStr: (key: MessageKey, ...args: (string | undefined)[]) => string;
|
msgStr: (key: MessageKey, ...args: (string | undefined)[]) => string;
|
||||||
msg: (key: MessageKey, ...args: (string | undefined)[]) => JSX.Element;
|
msg: (key: MessageKey, ...args: (string | undefined)[]) => JSX.Element;
|
||||||
/** advancedMsg("${access-denied}") === advancedMsg("access-denied") === msg("access-denied") */
|
/** advancedMsg("${access-denied}") === advancedMsg("access-denied") === msg("access-denied") */
|
||||||
@ -22,102 +32,85 @@ export type I18n<MessageKey extends string> = {
|
|||||||
labelBySupportedLanguageTag: Record<string, string>;
|
labelBySupportedLanguageTag: Record<string, string>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type KcContextLike = {
|
export function __unsafe_useI18n<ExtraMessageKey extends string = never>(params: {
|
||||||
locale?: {
|
|
||||||
currentLanguageTag: string;
|
|
||||||
supported: { languageTag: string; url: string; label: string }[];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
assert<KcContextBase extends KcContextLike ? true : false>();
|
|
||||||
|
|
||||||
export type I18nProviderProps = {
|
|
||||||
children: ReactNode;
|
|
||||||
fallback?: ReactNode;
|
|
||||||
kcContext: KcContextLike;
|
kcContext: KcContextLike;
|
||||||
};
|
|
||||||
|
|
||||||
const allExtraMessages: { [languageTag: string]: { [key: string]: string } } = {};
|
|
||||||
|
|
||||||
export function createI18nApi<ExtraMessageKey extends string = never>(params: {
|
|
||||||
extraMessages: { [languageTag: string]: { [key in ExtraMessageKey]: string } };
|
extraMessages: { [languageTag: string]: { [key in ExtraMessageKey]: string } };
|
||||||
}) {
|
doSkip: boolean;
|
||||||
Object.assign(allExtraMessages, params.extraMessages);
|
}): I18n<MessageKeyBase | ExtraMessageKey> | undefined {
|
||||||
|
const { kcContext, extraMessages, doSkip } = params;
|
||||||
|
|
||||||
type MessageKey = ExtraMessageKey | keyof typeof baseMessages | keyof typeof keycloakifyExtraMessages[typeof fallbackLanguageTag];
|
const [i18n, setI18n] = useState<I18n<ExtraMessageKey | MessageKeyBase> | undefined>(undefined);
|
||||||
|
|
||||||
const context = createContext<I18n<MessageKey> | undefined>(undefined);
|
useEffect(() => {
|
||||||
|
if (doSkip) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
function useI18n(): I18n<MessageKey> {
|
let isMounted = true;
|
||||||
const i18n = useContext(context);
|
|
||||||
|
|
||||||
assert(i18n !== undefined, "Now Wrapped in <I18nProvider>");
|
(async () => {
|
||||||
|
const { currentLanguageTag = fallbackLanguageTag } = kcContext.locale ?? {};
|
||||||
|
|
||||||
return i18n;
|
const [fallbackMessages, messages] = await Promise.all([
|
||||||
}
|
import("./generated_messages/18.0.1/login/en"),
|
||||||
|
import(`./generated_kcMessages/18.0.1/login/${currentLanguageTag}`),
|
||||||
|
]);
|
||||||
|
|
||||||
const I18nProvider = memo((props: I18nProviderProps) => {
|
if (!isMounted) {
|
||||||
const { children, fallback, kcContext } = props;
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
const [i18n, setI18n] = useState<I18n<MessageKey> | undefined>(undefined);
|
setI18n({
|
||||||
|
...createI18nTranslationFunctions({
|
||||||
|
"fallbackMessages": {
|
||||||
|
...fallbackMessages,
|
||||||
|
...(keycloakifyExtraMessages[fallbackLanguageTag] ?? {}),
|
||||||
|
...(extraMessages[fallbackLanguageTag] ?? {}),
|
||||||
|
} as any,
|
||||||
|
"messages": {
|
||||||
|
...messages,
|
||||||
|
...((keycloakifyExtraMessages as any)[currentLanguageTag] ?? {}),
|
||||||
|
...(extraMessages[currentLanguageTag] ?? {}),
|
||||||
|
} as any,
|
||||||
|
}),
|
||||||
|
currentLanguageTag,
|
||||||
|
"changeLocale": newLanguageTag => {
|
||||||
|
const { locale } = kcContext;
|
||||||
|
|
||||||
useEffect(() => {
|
assert(locale !== undefined, "Internationalization not enabled");
|
||||||
let isMounted = true;
|
|
||||||
|
|
||||||
(async () => {
|
const targetSupportedLocale = locale.supported.find(({ languageTag }) => languageTag === newLanguageTag);
|
||||||
const { currentLanguageTag = fallbackLanguageTag } = kcContext.locale ?? {};
|
|
||||||
|
|
||||||
const [fallbackMessages, messages] = await Promise.all([
|
assert(targetSupportedLocale !== undefined, `${newLanguageTag} need to be enabled in Keycloak admin`);
|
||||||
import("./generated_messages/18.0.1/login/en"),
|
|
||||||
import(`./generated_kcMessages/18.0.1/login/${currentLanguageTag}`),
|
|
||||||
]);
|
|
||||||
|
|
||||||
if (!isMounted) {
|
window.location.href = targetSupportedLocale.url;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
setI18n({
|
assert(false, "never");
|
||||||
...createI18nTranslationFunctions({
|
},
|
||||||
"fallbackMessages": {
|
"labelBySupportedLanguageTag": Object.fromEntries(
|
||||||
...fallbackMessages,
|
(kcContext.locale?.supported ?? []).map(({ languageTag, label }) => [languageTag, label]),
|
||||||
...(keycloakifyExtraMessages[fallbackLanguageTag] ?? {}),
|
),
|
||||||
...(allExtraMessages[fallbackLanguageTag] ?? {}),
|
});
|
||||||
} as any,
|
})();
|
||||||
"messages": {
|
|
||||||
...messages,
|
|
||||||
...((keycloakifyExtraMessages as any)[currentLanguageTag] ?? {}),
|
|
||||||
...(allExtraMessages[currentLanguageTag] ?? {}),
|
|
||||||
} as any,
|
|
||||||
}),
|
|
||||||
currentLanguageTag,
|
|
||||||
"changeLocale": newLanguageTag => {
|
|
||||||
const { locale } = kcContext;
|
|
||||||
|
|
||||||
assert(locale !== undefined, "Internationalization not enabled");
|
return () => {
|
||||||
|
isMounted = false;
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
const targetSupportedLocale = locale.supported.find(({ languageTag }) => languageTag === newLanguageTag);
|
return i18n;
|
||||||
|
}
|
||||||
|
|
||||||
assert(targetSupportedLocale !== undefined, `${newLanguageTag} need to be enabled in Keycloak admin`);
|
const useI18n_private = __unsafe_useI18n;
|
||||||
|
|
||||||
window.location.href = targetSupportedLocale.url;
|
export function useI18n<ExtraMessageKey extends string = never>(params: {
|
||||||
|
kcContext: KcContextLike;
|
||||||
assert(false, "never");
|
extraMessages: { [languageTag: string]: { [key in ExtraMessageKey]: string } };
|
||||||
},
|
}): I18n<MessageKeyBase | ExtraMessageKey> | undefined {
|
||||||
"labelBySupportedLanguageTag": Object.fromEntries(
|
return useI18n_private({
|
||||||
(kcContext.locale?.supported ?? []).map(({ languageTag, label }) => [languageTag, label]),
|
...params,
|
||||||
),
|
"doSkip": false,
|
||||||
});
|
|
||||||
})();
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
isMounted = false;
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
return <context.Provider value={i18n}>{i18n === undefined ? fallback ?? null : children}</context.Provider>;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return { useI18n, I18nProvider };
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function createI18nTranslationFunctions<MessageKey extends string>(params: {
|
function createI18nTranslationFunctions<MessageKey extends string>(params: {
|
@ -1,8 +1,7 @@
|
|||||||
import "./tools/Array.prototype.every";
|
import "./tools/Array.prototype.every";
|
||||||
import React, { useMemo, useReducer, Fragment } from "react";
|
import React, { useMemo, useReducer, Fragment } from "react";
|
||||||
import type { KcContextBase, Validators, Attribute } from "./getKcContext/KcContextBase";
|
import type { KcContextBase, Validators, Attribute } from "./getKcContext/KcContextBase";
|
||||||
import { useI18n } from "./i18n";
|
import type { I18n, MessageKeyBase } from "./i18n";
|
||||||
import type { MessageKey } from "./i18n";
|
|
||||||
import { useConstCallback } from "powerhooks/useConstCallback";
|
import { useConstCallback } from "powerhooks/useConstCallback";
|
||||||
import { id } from "tsafe/id";
|
import { id } from "tsafe/id";
|
||||||
import { emailRegexp } from "./tools/emailRegExp";
|
import { emailRegexp } from "./tools/emailRegExp";
|
||||||
@ -15,15 +14,16 @@ export function useGetErrors(params: {
|
|||||||
attributes: { name: string; value?: string; validators: Validators }[];
|
attributes: { name: string; value?: string; validators: Validators }[];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
i18n: I18n;
|
||||||
}) {
|
}) {
|
||||||
const { kcContext } = params;
|
const { kcContext, i18n } = params;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
messagesPerField,
|
messagesPerField,
|
||||||
profile: { attributes },
|
profile: { attributes },
|
||||||
} = kcContext;
|
} = kcContext;
|
||||||
|
|
||||||
const { msg, msgStr, advancedMsg, advancedMsgStr } = useI18n();
|
const { msg, msgStr, advancedMsg, advancedMsgStr } = i18n;
|
||||||
|
|
||||||
const getErrors = useConstCallback((params: { name: string; fieldValueByAttributeName: Record<string, { value: string }> }) => {
|
const getErrors = useConstCallback((params: { name: string; fieldValueByAttributeName: Record<string, { value: string }> }) => {
|
||||||
const { name, fieldValueByAttributeName } = params;
|
const { name, fieldValueByAttributeName } = params;
|
||||||
@ -134,7 +134,7 @@ export function useGetErrors(params: {
|
|||||||
|
|
||||||
const msgArg = [
|
const msgArg = [
|
||||||
errorMessageKey ??
|
errorMessageKey ??
|
||||||
id<MessageKey>(
|
id<MessageKeyBase>(
|
||||||
(() => {
|
(() => {
|
||||||
switch (shouldBe) {
|
switch (shouldBe) {
|
||||||
case "equal":
|
case "equal":
|
||||||
@ -175,7 +175,7 @@ export function useGetErrors(params: {
|
|||||||
break scope;
|
break scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
const msgArgs = [errorMessageKey ?? id<MessageKey>("shouldMatchPattern"), pattern] as const;
|
const msgArgs = [errorMessageKey ?? id<MessageKeyBase>("shouldMatchPattern"), pattern] as const;
|
||||||
|
|
||||||
errors.push({
|
errors.push({
|
||||||
validatorName,
|
validatorName,
|
||||||
@ -207,7 +207,7 @@ export function useGetErrors(params: {
|
|||||||
break scope;
|
break scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
const msgArgs = [id<MessageKey>("invalidEmailMessage")] as const;
|
const msgArgs = [id<MessageKeyBase>("invalidEmailMessage")] as const;
|
||||||
|
|
||||||
errors.push({
|
errors.push({
|
||||||
validatorName,
|
validatorName,
|
||||||
@ -287,7 +287,7 @@ export function useGetErrors(params: {
|
|||||||
break scope;
|
break scope;
|
||||||
}
|
}
|
||||||
|
|
||||||
const msgArgs = [id<MessageKey>("notAValidOption")] as const;
|
const msgArgs = [id<MessageKeyBase>("notAValidOption")] as const;
|
||||||
|
|
||||||
errors.push({
|
errors.push({
|
||||||
validatorName,
|
validatorName,
|
||||||
@ -315,6 +315,7 @@ export function useFormValidationSlice(params: {
|
|||||||
};
|
};
|
||||||
/** NOTE: Try to avoid passing a new ref every render for better performances. */
|
/** NOTE: Try to avoid passing a new ref every render for better performances. */
|
||||||
passwordValidators?: Validators;
|
passwordValidators?: Validators;
|
||||||
|
i18n: I18n;
|
||||||
}) {
|
}) {
|
||||||
const {
|
const {
|
||||||
kcContext,
|
kcContext,
|
||||||
@ -324,6 +325,7 @@ export function useFormValidationSlice(params: {
|
|||||||
"min": "4",
|
"min": "4",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
i18n,
|
||||||
} = params;
|
} = params;
|
||||||
|
|
||||||
const attributesWithPassword = useMemo(
|
const attributesWithPassword = useMemo(
|
||||||
@ -342,7 +344,7 @@ export function useFormValidationSlice(params: {
|
|||||||
curr,
|
curr,
|
||||||
id<Attribute>({
|
id<Attribute>({
|
||||||
"name": "password",
|
"name": "password",
|
||||||
"displayName": id<`\${${MessageKey}}`>("${password}"),
|
"displayName": id<`\${${MessageKeyBase}}`>("${password}"),
|
||||||
"required": true,
|
"required": true,
|
||||||
"readOnly": false,
|
"readOnly": false,
|
||||||
"validators": passwordValidators,
|
"validators": passwordValidators,
|
||||||
@ -352,7 +354,7 @@ export function useFormValidationSlice(params: {
|
|||||||
}),
|
}),
|
||||||
id<Attribute>({
|
id<Attribute>({
|
||||||
"name": "password-confirm",
|
"name": "password-confirm",
|
||||||
"displayName": id<`\${${MessageKey}}`>("${passwordConfirm}"),
|
"displayName": id<`\${${MessageKeyBase}}`>("${passwordConfirm}"),
|
||||||
"required": true,
|
"required": true,
|
||||||
"readOnly": false,
|
"readOnly": false,
|
||||||
"validators": {
|
"validators": {
|
||||||
@ -360,7 +362,7 @@ export function useFormValidationSlice(params: {
|
|||||||
"name": "password",
|
"name": "password",
|
||||||
"ignore.empty.value": true,
|
"ignore.empty.value": true,
|
||||||
"shouldBe": "equal",
|
"shouldBe": "equal",
|
||||||
"error-message": id<`\${${MessageKey}}`>("${invalidPasswordConfirmMessage}"),
|
"error-message": id<`\${${MessageKeyBase}}`>("${invalidPasswordConfirmMessage}"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
"annotations": {},
|
"annotations": {},
|
||||||
@ -382,6 +384,7 @@ export function useFormValidationSlice(params: {
|
|||||||
"attributes": attributesWithPassword,
|
"attributes": attributesWithPassword,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
i18n,
|
||||||
});
|
});
|
||||||
|
|
||||||
const initialInternalState = useMemo(
|
const initialInternalState = useMemo(
|
||||||
|
Reference in New Issue
Block a user