New i18n with dynamic imports

This commit is contained in:
garronej
2022-07-31 18:57:30 +02:00
parent 2be40816b2
commit 69c15bd473
34 changed files with 135 additions and 32032 deletions

View File

@ -2,10 +2,10 @@ import React, { memo } from "react";
import Template from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { getMsg } from "../i18n";
import { useI18n } from "../i18n";
const Error = memo(({ kcContext, ...props }: { kcContext: KcContextBase.Error } & KcProps) => {
const { msg } = getMsg(kcContext);
const { msg } = useI18n();
const { message, client } = kcContext;

View File

@ -3,10 +3,10 @@ import Template from "./Template";
import type { KcProps } from "./KcProps";
import { assert } from "../tools/assert";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { getMsg } from "../i18n";
import { useI18n } from "../i18n";
const Info = memo(({ kcContext, ...props }: { kcContext: KcContextBase.Info } & KcProps) => {
const { msg, msgStr } = getMsg(kcContext);
const { msg, msgStr } = useI18n();
assert(kcContext.message !== undefined);

View File

@ -1,6 +1,7 @@
import React, { lazy, memo, Suspense } from "react";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import type { KcProps } from "./KcProps";
import { I18nProvider } from "../i18n";
const Login = lazy(() => import("./Login"));
const Register = lazy(() => import("./Register"));
@ -21,44 +22,46 @@ const LogoutConfirm = lazy(() => import("./LogoutConfirm"));
const KcApp = memo(({ kcContext, ...props }: { kcContext: KcContextBase } & KcProps) => {
return (
<Suspense fallback={null}>
{(() => {
switch (kcContext.pageId) {
case "login.ftl":
return <Login {...{ kcContext, ...props }} />;
case "register.ftl":
return <Register {...{ kcContext, ...props }} />;
case "register-user-profile.ftl":
return <RegisterUserProfile {...{ kcContext, ...props }} />;
case "info.ftl":
return <Info {...{ kcContext, ...props }} />;
case "error.ftl":
return <Error {...{ kcContext, ...props }} />;
case "login-reset-password.ftl":
return <LoginResetPassword {...{ kcContext, ...props }} />;
case "login-verify-email.ftl":
return <LoginVerifyEmail {...{ kcContext, ...props }} />;
case "terms.ftl":
return <Terms {...{ kcContext, ...props }} />;
case "login-otp.ftl":
return <LoginOtp {...{ kcContext, ...props }} />;
case "login-update-password.ftl":
return <LoginUpdatePassword {...{ kcContext, ...props }} />;
case "login-update-profile.ftl":
return <LoginUpdateProfile {...{ kcContext, ...props }} />;
case "login-idp-link-confirm.ftl":
return <LoginIdpLinkConfirm {...{ kcContext, ...props }} />;
case "login-idp-link-email.ftl":
return <LoginIdpLinkEmail {...{ kcContext, ...props }} />;
case "login-page-expired.ftl":
return <LoginPageExpired {...{ kcContext, ...props }} />;
case "login-config-totp.ftl":
return <LoginConfigTotp {...{ kcContext, ...props }} />;
case "logout-confirm.ftl":
return <LogoutConfirm {...{ kcContext, ...props }} />;
}
})()}
</Suspense>
<I18nProvider kcContext={kcContext}>
<Suspense>
{(() => {
switch (kcContext.pageId) {
case "login.ftl":
return <Login {...{ kcContext, ...props }} />;
case "register.ftl":
return <Register {...{ kcContext, ...props }} />;
case "register-user-profile.ftl":
return <RegisterUserProfile {...{ kcContext, ...props }} />;
case "info.ftl":
return <Info {...{ kcContext, ...props }} />;
case "error.ftl":
return <Error {...{ kcContext, ...props }} />;
case "login-reset-password.ftl":
return <LoginResetPassword {...{ kcContext, ...props }} />;
case "login-verify-email.ftl":
return <LoginVerifyEmail {...{ kcContext, ...props }} />;
case "terms.ftl":
return <Terms {...{ kcContext, ...props }} />;
case "login-otp.ftl":
return <LoginOtp {...{ kcContext, ...props }} />;
case "login-update-password.ftl":
return <LoginUpdatePassword {...{ kcContext, ...props }} />;
case "login-update-profile.ftl":
return <LoginUpdateProfile {...{ kcContext, ...props }} />;
case "login-idp-link-confirm.ftl":
return <LoginIdpLinkConfirm {...{ kcContext, ...props }} />;
case "login-idp-link-email.ftl":
return <LoginIdpLinkEmail {...{ kcContext, ...props }} />;
case "login-page-expired.ftl":
return <LoginPageExpired {...{ kcContext, ...props }} />;
case "login-config-totp.ftl":
return <LoginConfigTotp {...{ kcContext, ...props }} />;
case "logout-confirm.ftl":
return <LogoutConfirm {...{ kcContext, ...props }} />;
}
})()}
</Suspense>
</I18nProvider>
);
});

View File

@ -2,15 +2,15 @@ import React, { useState, memo } from "react";
import Template from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { getMsg } from "../i18n";
import { useCssAndCx } from "tss-react";
import { useConstCallback } from "powerhooks/useConstCallback";
import type { FormEventHandler } from "react";
import { useI18n } from "../i18n";
const Login = memo(({ kcContext, ...props }: { kcContext: KcContextBase.Login } & KcProps) => {
const { social, realm, url, usernameEditDisabled, login, auth, registrationDisabled } = kcContext;
const { msg, msgStr } = getMsg(kcContext);
const { msg, msgStr } = useI18n();
const { cx } = useCssAndCx();

View File

@ -2,7 +2,7 @@ import React, { memo } from "react";
import Template from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { getMsg } from "../i18n";
import { useI18n } from "../i18n";
import { useCssAndCx } from "tss-react";
const LoginConfigTotp = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginConfigTotp } & KcProps) => {
@ -10,7 +10,7 @@ const LoginConfigTotp = memo(({ kcContext, ...props }: { kcContext: KcContextBas
const { cx } = useCssAndCx();
const { msg, msgStr } = getMsg(kcContext);
const { msg, msgStr } = useI18n();
const algToKeyUriAlg: Record<KcContextBase.LoginConfigTotp["totp"]["policy"]["algorithm"], string> = {
HmacSHA1: "SHA1",
HmacSHA256: "SHA256",

View File

@ -2,13 +2,13 @@ import React, { memo } from "react";
import Template from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { getMsg } from "../i18n";
import { useI18n } from "../i18n";
import { useCssAndCx } from "tss-react";
const LoginIdpLinkConfirm = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginIdpLinkConfirm } & KcProps) => {
const { url, idpAlias } = kcContext;
const { msg } = getMsg(kcContext);
const { msg } = useI18n();
const { cx } = useCssAndCx();

View File

@ -2,12 +2,12 @@ import React, { memo } from "react";
import Template from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { getMsg } from "../i18n";
import { useI18n } from "../i18n";
const LoginIdpLinkEmail = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginIdpLinkEmail } & KcProps) => {
const { url, realm, brokerContext, idpAlias } = kcContext;
const { msg } = getMsg(kcContext);
const { msg } = useI18n();
return (
<Template

View File

@ -2,7 +2,7 @@ import React, { useEffect, memo } from "react";
import Template from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { getMsg } from "../i18n";
import { useI18n } from "../i18n";
import { headInsert } from "../tools/headInsert";
import { pathJoin } from "../../bin/tools/pathJoin";
import { useCssAndCx } from "tss-react";
@ -12,7 +12,7 @@ const LoginOtp = memo(({ kcContext, ...props }: { kcContext: KcContextBase.Login
const { cx } = useCssAndCx();
const { msg, msgStr } = getMsg(kcContext);
const { msg, msgStr } = useI18n();
useEffect(() => {
let isCleanedUp = false;

View File

@ -2,12 +2,12 @@ import React, { memo } from "react";
import Template from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { getMsg } from "../i18n";
import { useI18n } from "../i18n";
const LoginPageExpired = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginPageExpired } & KcProps) => {
const { url } = kcContext;
const { msg } = getMsg(kcContext);
const { msg } = useI18n();
return (
<Template

View File

@ -2,13 +2,13 @@ import React, { memo } from "react";
import Template from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { getMsg } from "../i18n";
import { useI18n } from "../i18n";
import { useCssAndCx } from "tss-react";
const LoginResetPassword = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginResetPassword } & KcProps) => {
const { url, realm, auth } = kcContext;
const { msg, msgStr } = getMsg(kcContext);
const { msg, msgStr } = useI18n();
const { cx } = useCssAndCx();

View File

@ -2,13 +2,13 @@ import React, { memo } from "react";
import Template from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { getMsg } from "../i18n";
import { useI18n } from "../i18n";
import { useCssAndCx } from "tss-react";
const LoginUpdatePassword = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginUpdatePassword } & KcProps) => {
const { cx } = useCssAndCx();
const { msg, msgStr } = getMsg(kcContext);
const { msg, msgStr } = useI18n();
const { url, messagesPerField, isAppInitiatedAction, username } = kcContext;

View File

@ -2,13 +2,13 @@ import React, { memo } from "react";
import Template from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { getMsg } from "../i18n";
import { useI18n } from "../i18n";
import { useCssAndCx } from "tss-react";
const LoginUpdateProfile = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginUpdateProfile } & KcProps) => {
const { cx } = useCssAndCx();
const { msg, msgStr } = getMsg(kcContext);
const { msg, msgStr } = useI18n();
const { url, user, messagesPerField, isAppInitiatedAction } = kcContext;

View File

@ -2,10 +2,10 @@ import React, { memo } from "react";
import Template from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { getMsg } from "../i18n";
import { useI18n } from "../i18n";
const LoginVerifyEmail = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LoginVerifyEmail } & KcProps) => {
const { msg } = getMsg(kcContext);
const { msg } = useI18n();
const { url, user } = kcContext;

View File

@ -4,14 +4,14 @@ import { useCssAndCx } from "tss-react";
import Template from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { getMsg } from "../i18n";
import { useI18n } from "../i18n";
const LogoutConfirm = memo(({ kcContext, ...props }: { kcContext: KcContextBase.LogoutConfirm } & KcProps) => {
const { url, client, logoutConfirm } = kcContext;
const { cx } = useCssAndCx();
const { msg, msgStr } = getMsg(kcContext);
const { msg, msgStr } = useI18n();
return (
<Template

View File

@ -2,13 +2,13 @@ import React, { memo } from "react";
import Template from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { getMsg } from "../i18n";
import { useI18n } from "../i18n";
import { useCssAndCx } from "tss-react";
const Register = memo(({ kcContext, ...props }: { kcContext: KcContextBase.Register } & KcProps) => {
const { url, messagesPerField, register, realm, passwordRequired, recaptchaRequired, recaptchaSiteKey } = kcContext;
const { msg, msgStr } = getMsg(kcContext);
const { msg, msgStr } = useI18n();
const { cx } = useCssAndCx();

View File

@ -2,7 +2,7 @@ import React, { useMemo, memo, useEffect, useState, Fragment } from "react";
import Template from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase, Attribute } from "../getKcContext/KcContextBase";
import { getMsg } from "../i18n";
import { useI18n } from "../i18n";
import { useCssAndCx } from "tss-react";
import type { ReactComponent } from "../tools/ReactComponent";
import { useCallbackFactory } from "powerhooks/useCallbackFactory";
@ -11,7 +11,7 @@ import { useFormValidationSlice } from "../useFormValidationSlice";
const RegisterUserProfile = memo(({ kcContext, ...props_ }: { kcContext: KcContextBase.RegisterUserProfile } & KcProps) => {
const { url, messagesPerField, recaptchaRequired, recaptchaSiteKey } = kcContext;
const { msg, msgStr } = getMsg(kcContext);
const { msg, msgStr } = useI18n();
const { cx, css } = useCssAndCx();
@ -74,7 +74,7 @@ type UserProfileFormFieldsProps = { kcContext: KcContextBase.RegisterUserProfile
const UserProfileFormFields = memo(({ kcContext, onIsFormSubmittableValueChange, ...props }: UserProfileFormFieldsProps) => {
const { cx, css } = useCssAndCx();
const { advancedMsg } = getMsg(kcContext);
const { advancedMsg } = useI18n();
const {
formValidationState: { fieldStateByAttributeName, isFormSubmittable },

View File

@ -1,7 +1,5 @@
import React, { useReducer, useEffect, memo } from "react";
import type { ReactNode } from "react";
import { getMsg, getCurrentKcLanguageTag, changeLocale, getTagLabel } from "../i18n";
import type { KcLanguageTag } from "../i18n";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { assert } from "../tools/assert";
import { useCallbackFactory } from "powerhooks/useCallbackFactory";
@ -10,6 +8,7 @@ import { pathJoin } from "../../bin/tools/pathJoin";
import { useConstCallback } from "powerhooks/useConstCallback";
import type { KcTemplateProps } from "./KcProps";
import { useCssAndCx } from "tss-react";
import { useI18n } from "../i18n";
export type TemplateProps = {
displayInfo?: boolean;
@ -48,14 +47,9 @@ const Template = memo((props: TemplateProps) => {
console.log("Rendering this page with react using keycloakify");
}, []);
const { msg } = getMsg(kcContext);
const { msg, changeLocale, labelBySupportedLanguageTag, currentLanguageTag } = useI18n();
const onChangeLanguageClickFactory = useCallbackFactory(([kcLanguageTag]: [KcLanguageTag]) =>
changeLocale({
kcContext,
kcLanguageTag,
}),
);
const onChangeLanguageClickFactory = useCallbackFactory(([kcLanguageTag]: [string]) => changeLocale(kcLanguageTag));
const onTryAnotherWayClick = useConstCallback(() => (document.forms["kc-select-try-another-way-form" as never].submit(), false));
@ -138,13 +132,13 @@ const Template = memo((props: TemplateProps) => {
<div id="kc-locale-wrapper" className={cx(props.kcLocaleWrapperClass)}>
<div className="kc-dropdown" id="kc-locale-dropdown">
<a href="#" id="kc-current-locale-link">
{getTagLabel({ "kcLanguageTag": getCurrentKcLanguageTag(kcContext), kcContext })}
{labelBySupportedLanguageTag[currentLanguageTag]}
</a>
<ul>
{locale.supported.map(({ languageTag }) => (
<li key={languageTag} className="kc-dropdown-item">
<a href="#" onClick={onChangeLanguageClickFactory(languageTag)}>
{getTagLabel({ "kcLanguageTag": languageTag, kcContext })}
{labelBySupportedLanguageTag[languageTag]}
</a>
</li>
))}

View File

@ -1,38 +1,50 @@
import React, { useReducer, useEffect, memo } from "react";
import React, { useEffect, memo } from "react";
import Template from "./Template";
import type { KcProps } from "./KcProps";
import type { KcContextBase } from "../getKcContext/KcContextBase";
import { getMsg } from "../i18n";
import { useI18n } from "../i18n";
import { useCssAndCx } from "tss-react";
import { kcMessages, getCurrentKcLanguageTag } from "../i18n";
import type { KcLanguageTag } from "../i18n";
import { Evt } from "evt";
import { useRerenderOnStateChange } from "evt/hooks";
export const evtTermMarkdown = Evt.create<string | undefined>(undefined);
/** Allow to avoid bundling the terms and download it on demand*/
export function useDownloadTerms(params: {
kcContext: KcContextBase;
downloadTermMarkdown: (params: { currentKcLanguageTag: KcLanguageTag }) => Promise<string>;
}) {
const { kcContext, downloadTermMarkdown } = params;
export function useDownloadTerms(params: { downloadTermMarkdown: (params: { currentLanguageTag: string }) => Promise<string> }) {
const { downloadTermMarkdown } = params;
const [, forceUpdate] = useReducer(x => x + 1, 0);
const { currentLanguageTag } = useI18n();
useEffect(() => {
const currentKcLanguageTag = getCurrentKcLanguageTag(kcContext);
let isMounted = true;
downloadTermMarkdown({ currentKcLanguageTag }).then(thermMarkdown => {
kcMessages[currentKcLanguageTag].termsText = thermMarkdown;
forceUpdate();
downloadTermMarkdown({ currentLanguageTag }).then(thermMarkdown => {
if (!isMounted) {
return;
}
evtTermMarkdown.state = thermMarkdown;
});
return () => {
isMounted = false;
};
}, []);
}
const Terms = memo(({ kcContext, ...props }: { kcContext: KcContextBase.Terms } & KcProps) => {
const { msg, msgStr } = getMsg(kcContext);
const { msg, msgStr } = useI18n();
useRerenderOnStateChange(evtTermMarkdown);
const { cx } = useCssAndCx();
const { url } = kcContext;
if (evtTermMarkdown.state === undefined) {
return null;
}
return (
<Template
{...{ kcContext, ...props }}
@ -41,7 +53,7 @@ const Terms = memo(({ kcContext, ...props }: { kcContext: KcContextBase.Terms }
headerNode={msg("termsTitle")}
formNode={
<>
<div id="kc-terms-text">{msg("termsText")}</div>
<div id="kc-terms-text">{evtTermMarkdown.state}</div>
<form className="form-actions" action={url.loginAction} method="POST">
<input
className={cx(