Set up translation with i18next
This commit is contained in:
@@ -23,6 +23,7 @@ import React, {
|
||||
useMemo,
|
||||
} from "react";
|
||||
import { useHistory, useLocation, Link } from "react-router-dom";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
|
||||
import { ReactComponent as Logo } from "../icons/LogoLarge.svg";
|
||||
import { useClient } from "../ClientContext";
|
||||
@@ -34,7 +35,8 @@ import { useInteractiveLogin } from "./useInteractiveLogin";
|
||||
import { usePageTitle } from "../usePageTitle";
|
||||
|
||||
export const LoginPage: FC = () => {
|
||||
usePageTitle("Login");
|
||||
const { t } = useTranslation();
|
||||
usePageTitle(t("Login"));
|
||||
|
||||
const { setClient } = useClient();
|
||||
const login = useInteractiveLogin();
|
||||
@@ -93,8 +95,8 @@ export const LoginPage: FC = () => {
|
||||
<InputField
|
||||
type="text"
|
||||
ref={usernameRef}
|
||||
placeholder="Username"
|
||||
label="Username"
|
||||
placeholder={t("Username")}
|
||||
label={t("Username")}
|
||||
autoCorrect="off"
|
||||
autoCapitalize="none"
|
||||
prefix="@"
|
||||
@@ -105,18 +107,18 @@ export const LoginPage: FC = () => {
|
||||
<InputField
|
||||
type="password"
|
||||
ref={passwordRef}
|
||||
placeholder="Password"
|
||||
label="Password"
|
||||
placeholder={t("Password")}
|
||||
label={t("Password")}
|
||||
/>
|
||||
</FieldRow>
|
||||
{error && (
|
||||
<FieldRow>
|
||||
<ErrorMessage>{error.message}</ErrorMessage>
|
||||
<ErrorMessage error={error} />
|
||||
</FieldRow>
|
||||
)}
|
||||
<FieldRow>
|
||||
<Button type="submit" disabled={loading}>
|
||||
{loading ? "Logging in..." : "Login"}
|
||||
{loading ? t("Logging in…") : t("Login")}
|
||||
</Button>
|
||||
</FieldRow>
|
||||
</form>
|
||||
@@ -124,9 +126,11 @@ export const LoginPage: FC = () => {
|
||||
<div className={styles.authLinks}>
|
||||
<p>Not registered yet?</p>
|
||||
<p>
|
||||
<Link to="/register">Create an account</Link>
|
||||
{" Or "}
|
||||
<Link to="/">Access as a guest</Link>
|
||||
<Trans>
|
||||
<Link to="/register">Create an account</Link>
|
||||
{" Or "}
|
||||
<Link to="/">Access as a guest</Link>
|
||||
</Trans>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -26,6 +26,7 @@ import React, {
|
||||
import { useHistory, useLocation } from "react-router-dom";
|
||||
import { captureException } from "@sentry/react";
|
||||
import { sleep } from "matrix-js-sdk/src/utils";
|
||||
import { Trans, useTranslation } from "react-i18next";
|
||||
|
||||
import { FieldRow, InputField, ErrorMessage } from "../input/Input";
|
||||
import { Button } from "../button";
|
||||
@@ -40,7 +41,8 @@ import { Caption, Link } from "../typography/Typography";
|
||||
import { usePageTitle } from "../usePageTitle";
|
||||
|
||||
export const RegisterPage: FC = () => {
|
||||
usePageTitle("Register");
|
||||
const { t } = useTranslation();
|
||||
usePageTitle(t("Register"));
|
||||
|
||||
const { loading, isAuthenticated, isPasswordlessUser, client, setClient } =
|
||||
useClient();
|
||||
@@ -126,11 +128,11 @@ export const RegisterPage: FC = () => {
|
||||
|
||||
useEffect(() => {
|
||||
if (password && passwordConfirmation && password !== passwordConfirmation) {
|
||||
confirmPasswordRef.current?.setCustomValidity("Passwords must match");
|
||||
confirmPasswordRef.current?.setCustomValidity(t("Passwords must match"));
|
||||
} else {
|
||||
confirmPasswordRef.current?.setCustomValidity("");
|
||||
}
|
||||
}, [password, passwordConfirmation]);
|
||||
}, [password, passwordConfirmation, t]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!loading && isAuthenticated && !isPasswordlessUser && !registering) {
|
||||
@@ -154,8 +156,8 @@ export const RegisterPage: FC = () => {
|
||||
<InputField
|
||||
type="text"
|
||||
name="userName"
|
||||
placeholder="Username"
|
||||
label="Username"
|
||||
placeholder={t("Username")}
|
||||
label={t("Username")}
|
||||
autoCorrect="off"
|
||||
autoCapitalize="none"
|
||||
prefix="@"
|
||||
@@ -171,8 +173,8 @@ export const RegisterPage: FC = () => {
|
||||
setPassword(e.target.value)
|
||||
}
|
||||
value={password}
|
||||
placeholder="Password"
|
||||
label="Password"
|
||||
placeholder={t("Password")}
|
||||
label={t("Password")}
|
||||
/>
|
||||
</FieldRow>
|
||||
<FieldRow>
|
||||
@@ -184,45 +186,49 @@ export const RegisterPage: FC = () => {
|
||||
setPasswordConfirmation(e.target.value)
|
||||
}
|
||||
value={passwordConfirmation}
|
||||
placeholder="Confirm Password"
|
||||
label="Confirm Password"
|
||||
placeholder={t("Confirm password")}
|
||||
label={t("Confirm password")}
|
||||
ref={confirmPasswordRef}
|
||||
/>
|
||||
</FieldRow>
|
||||
<Caption>
|
||||
This site is protected by ReCAPTCHA and the Google{" "}
|
||||
<Link href="https://www.google.com/policies/privacy/">
|
||||
Privacy Policy
|
||||
</Link>{" "}
|
||||
and{" "}
|
||||
<Link href="https://policies.google.com/terms">
|
||||
Terms of Service
|
||||
</Link>{" "}
|
||||
apply.
|
||||
<br />
|
||||
By clicking "Register", you agree to our{" "}
|
||||
<Link href={privacyPolicyUrl}>Terms and conditions</Link>
|
||||
<Trans>
|
||||
This site is protected by ReCAPTCHA and the Google{" "}
|
||||
<Link href="https://www.google.com/policies/privacy/">
|
||||
Privacy Policy
|
||||
</Link>{" "}
|
||||
and{" "}
|
||||
<Link href="https://policies.google.com/terms">
|
||||
Terms of Service
|
||||
</Link>{" "}
|
||||
apply.
|
||||
<br />
|
||||
By clicking "Register", you agree to our{" "}
|
||||
<Link href={privacyPolicyUrl}>Terms and conditions</Link>
|
||||
</Trans>
|
||||
</Caption>
|
||||
{error && (
|
||||
<FieldRow>
|
||||
<ErrorMessage>{error.message}</ErrorMessage>
|
||||
<ErrorMessage error={error} />
|
||||
</FieldRow>
|
||||
)}
|
||||
<FieldRow>
|
||||
<Button type="submit" disabled={registering}>
|
||||
{registering ? "Registering..." : "Register"}
|
||||
{registering ? t("Registering…") : t("Register")}
|
||||
</Button>
|
||||
</FieldRow>
|
||||
<div id={recaptchaId} />
|
||||
</form>
|
||||
</div>
|
||||
<div className={styles.authLinks}>
|
||||
<p>Already have an account?</p>
|
||||
<p>
|
||||
<Link to="/login">Log in</Link>
|
||||
{" Or "}
|
||||
<Link to="/">Access as a guest</Link>
|
||||
</p>
|
||||
<Trans>
|
||||
<p>Already have an account?</p>
|
||||
<p>
|
||||
<Link to="/login">Log in</Link>
|
||||
{" Or "}
|
||||
<Link to="/">Access as a guest</Link>
|
||||
</p>
|
||||
</Trans>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -16,6 +16,9 @@ limitations under the License.
|
||||
|
||||
import { useEffect, useCallback, useRef, useState } from "react";
|
||||
import { randomString } from "matrix-js-sdk/src/randomstring";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { translatedError } from "../TranslatedError";
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
@@ -32,6 +35,7 @@ interface RecaptchaPromiseRef {
|
||||
}
|
||||
|
||||
export const useRecaptcha = (sitekey: string) => {
|
||||
const { t } = useTranslation();
|
||||
const [recaptchaId] = useState(() => randomString(16));
|
||||
const promiseRef = useRef<RecaptchaPromiseRef>();
|
||||
|
||||
@@ -71,14 +75,14 @@ export const useRecaptcha = (sitekey: string) => {
|
||||
|
||||
if (!window.grecaptcha) {
|
||||
console.log("Recaptcha not loaded");
|
||||
return Promise.reject(new Error("Recaptcha not loaded"));
|
||||
return Promise.reject(translatedError("Recaptcha not loaded", t));
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
const observer = new MutationObserver((mutationsList) => {
|
||||
for (const item of mutationsList) {
|
||||
if ((item.target as HTMLElement)?.style?.visibility !== "visible") {
|
||||
reject(new Error("Recaptcha dismissed"));
|
||||
reject(translatedError("Recaptcha dismissed", t));
|
||||
observer.disconnect();
|
||||
return;
|
||||
}
|
||||
@@ -108,7 +112,7 @@ export const useRecaptcha = (sitekey: string) => {
|
||||
});
|
||||
}
|
||||
});
|
||||
}, [sitekey]);
|
||||
}, [sitekey, t]);
|
||||
|
||||
const reset = useCallback(() => {
|
||||
window.grecaptcha?.reset();
|
||||
|
||||
Reference in New Issue
Block a user