From 62c92d5497334431f2f7a233d7c2170a3c651c22 Mon Sep 17 00:00:00 2001 From: Dylan <36567925+Airyzz@users.noreply.github.com> Date: Sat, 6 Aug 2022 17:09:14 +0930 Subject: [PATCH] Added translation to templates Added to Auth.jsx and Client.jsx --- public/locales/en/translation.json | 72 +++++++++++++++ src/app/templates/auth/Auth.jsx | 131 ++++++++++++++++------------ src/app/templates/client/Client.jsx | 17 ++-- 3 files changed, 157 insertions(+), 63 deletions(-) diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index e51163f5..1b8a5d71 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -759,6 +759,78 @@ "SSOButtons": { "login_with": "Login with {{idp_name}}" } + }, + "Templates": { + "Auth": { + "loading_homeserver_list": "Loading homeserver list...", + "looking_for_homeserver": "Looking for homeserver...", + "connecting_to_homeserver": "Connecting to {{homeserver}}...", + "unable_to_connect": "Unable to connect. Please check your input", + "username_taken": "Username is already taken", + "registration_in_progress": "Registration in progress...", + "link_about": "About", + "link_twitter": "Twitter", + "link_matrix": "Powered by Matrix", + "dont_have_an_account": "Dont have an account?", + "already_have_an_account": "Already have an account?", + "register_link": "Register", + "login_link": "Login", + "redirecting": "Redirecting...", + "login_in_progress": "Login in progress...", + "bad_localpart_error": "Username can only contain characters a-z, 0-9, or '=_-./'", + "user_id_too_long_error": "Your user ID, including the hostname, can't be more than 255 characters long.", + "bad_password_error": "Password must contain at least 1 lowercase, 1 uppercase, 1 number, 1 non-alphanumeric character, 8-127 characters with no space.", + "confirm_password_error": "Passwords don't match.", + "bad_email_error": "Invalid email address", + "invalid_password": "Invalid password", + "check_credentials": "Please check your credentials", + "login_types": { + "username": "Username", + "email": "Email" + }, + "homeserver_form": { + "title": "Homeserver", + "header": "Homeserver list" + }, + "login_form": { + "title": "Login", + "prompt_email": "Email", + "prompt_username": "Username", + "prompt_password": "Password", + "login_button": "Login" + }, + "register_form": { + "title": "Register", + "prompt_email_required": "Email (Required)", + "prompt_email_optional": "Email", + "prompt_username": "Username", + "prompt_password": "Password", + "prompt_confirm_password": "Confirm password", + "register_button": "Register" + }, + "terms_and_conditions": { + "title": "Agree with terms", + "description": "In order to complete registration, you need to agree to the terms and conditions.", + "accept": "I accept Terms and Conditions", + "submit_button": "Submit" + }, + "validate_email": { + "title": "Verify email", + "continue_button": "Continue", + "message": "Please check your email {{email_address}} and validate before continuing further" + }, + "captcha": { + "message": "Pleace check the box below to proceed" + } + }, + "Client": { + "loading_messages": { + "default": "Heating up", + "message_one": "Almost there...", + "message_two": "Looks like you have a lot of stuff to heat up!" + }, + "logout_prompt": "Logout" + } } } } \ No newline at end of file diff --git a/src/app/templates/auth/Auth.jsx b/src/app/templates/auth/Auth.jsx index 7c211736..264e0869 100644 --- a/src/app/templates/auth/Auth.jsx +++ b/src/app/templates/auth/Auth.jsx @@ -5,6 +5,7 @@ import './Auth.scss'; import ReCAPTCHA from 'react-google-recaptcha'; import { Formik } from 'formik'; +import { useTranslation, Trans } from 'react-i18next'; import * as auth from '../../../client/action/auth'; import cons from '../../../client/state/cons'; import { Debounce, getUrlPrams } from '../../../util/common'; @@ -26,16 +27,13 @@ import EyeBlindIC from '../../../../public/res/ic/outlined/eye-blind.svg'; import CinnySvg from '../../../../public/res/svg/cinny.svg'; import SSOButtons from '../../molecules/sso-buttons/SSOButtons'; +import '../../i18n'; + const LOCALPART_SIGNUP_REGEX = /^[a-z0-9_\-.=/]+$/; -const BAD_LOCALPART_ERROR = 'Username can only contain characters a-z, 0-9, or \'=_-./\''; -const USER_ID_TOO_LONG_ERROR = 'Your user ID, including the hostname, can\'t be more than 255 characters long.'; const PASSWORD_STRENGHT_REGEX = /^(?=.*\d)(?=.*[A-Z])(?=.*[a-z])(?=.*[^\w\d\s:])([^\s]){8,127}$/; -const BAD_PASSWORD_ERROR = 'Password must contain at least 1 lowercase, 1 uppercase, 1 number, 1 non-alphanumeric character, 8-127 characters with no space.'; -const CONFIRM_PASSWORD_ERROR = 'Passwords don\'t match.'; const EMAIL_REGEX = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}$/i; -const BAD_EMAIL_ERROR = 'Invalid email address'; function isValidInput(value, regex) { if (typeof regex === 'string') return regex === value; @@ -50,16 +48,19 @@ let searchingHs = null; function Homeserver({ onChange }) { const [hs, setHs] = useState(null); const [debounce] = useState(new Debounce()); - const [process, setProcess] = useState({ isLoading: true, message: 'Loading homeserver list...' }); + + const { t } = useTranslation(); + + const [process, setProcess] = useState({ isLoading: true, message: t('Templates.Auth.loading_homeserver_list') }); const hsRef = useRef(); const setupHsConfig = async (servername) => { - setProcess({ isLoading: true, message: 'Looking for homeserver...' }); + setProcess({ isLoading: true, message: t('Templates.Auth.looking_for_homeserver') }); let baseUrl = null; baseUrl = await getBaseUrl(servername); if (searchingHs !== servername) return; - setProcess({ isLoading: true, message: `Connecting to ${baseUrl}...` }); + setProcess({ isLoading: true, message: t('Templates.Auth.connecting_to_homeserver', { homeserver: baseUrl }) }); const tempClient = auth.createTemporaryClient(baseUrl); Promise.allSettled([tempClient.loginFlows(), tempClient.register()]) @@ -74,7 +75,7 @@ function Homeserver({ onChange }) { }).catch(() => { if (searchingHs !== servername) return; onChange(null); - setProcess({ isLoading: false, error: 'Unable to connect. Please check your input.' }); + setProcess({ isLoading: false, error: t('Templates.Auth.unable_to_connect') }); }); }; @@ -118,14 +119,14 @@ function Homeserver({ onChange }) { onChange={handleHsInput} value={hs?.selected} forwardRef={hsRef} - label="Homeserver" + label={t('Templates.Auth.homeserver_form.title')} disabled={hs === null || !hs.allowCustom} /> ( <> - Homeserver list + {t('Templates.Auth.homeserver_form.header')} { hs?.list.map((hsName) => ( flow.type === 'm.login.password')[0]; const ssoProviders = loginFlow?.filter((flow) => flow.type === 'm.login.sso')[0]; @@ -173,7 +175,7 @@ function Login({ loginFlow, baseUrl }) { const validator = (values) => { const errors = {}; if (typeIndex === 1 && values.email.length > 0 && !isValidInput(values.email, EMAIL_REGEX)) { - errors.email = BAD_EMAIL_ERROR; + errors.email = t('Templates.Auth.bad_email_error'); } return errors; }; @@ -196,7 +198,7 @@ function Login({ loginFlow, baseUrl }) { window.location.reload(); }).catch((error) => { let msg = error.message; - if (msg === 'Unknown message') msg = 'Please check your credentials'; + if (msg === 'Unknown message') msg = t('Templates.Auth.check_credentials'); actions.setErrors({ password: msg === 'Invalid password' ? msg : undefined, other: msg !== 'Invalid password' ? msg : undefined, @@ -208,7 +210,7 @@ function Login({ loginFlow, baseUrl }) { return ( <>
- Login + {t('Templates.Auth.login_form.title')} {isPassword && ( ( <> - {isSubmitting && } + {isSubmitting && }
- {typeIndex === 0 && } + {typeIndex === 0 && } {errors.username && {errors.username}} - {typeIndex === 1 && } + {typeIndex === 1 && } {errors.email && {errors.email}}
- + setPassVisible(!passVisible)} src={passVisible ? EyeIC : EyeBlindIC} size="extra-small" />
{errors.password && {errors.password}} {errors.other && {errors.other}}
- +
)} )} - {ssoProviders && isPassword && OR} + {ssoProviders && isPassword && {t('common.or')}} {ssoProviders && ( { const errors = {}; - if (values.username.list > 255) errors.username = USER_ID_TOO_LONG_ERROR; + if (values.username.list > 255) errors.username = t('Templates.Auth.user_id_too_long_error'); if (values.username.length > 0 && !isValidInput(values.username, LOCALPART_SIGNUP_REGEX)) { - errors.username = BAD_LOCALPART_ERROR; + errors.username = t('Templates.Auth.bad_localpart_error'); } if (values.password.length > 0 && !isValidInput(values.password, PASSWORD_STRENGHT_REGEX)) { - errors.password = BAD_PASSWORD_ERROR; + errors.password = t('Templates.Auth.bad_password_error'); } if (values.confirmPassword.length > 0 && !isValidInput(values.confirmPassword, values.password)) { - errors.confirmPassword = CONFIRM_PASSWORD_ERROR; + errors.confirmPassword = t('Templates.Auth.confirm_password_error'); } if (values.email.length > 0 && !isValidInput(values.email, EMAIL_REGEX)) { - errors.email = BAD_EMAIL_ERROR; + errors.email = t('Templates.Auth.bad_email_error'); } return errors; }; @@ -335,7 +339,7 @@ function Register({ registerInfo, loginFlow, baseUrl }) { return tempClient.isUsernameAvailable(values.username) .then(async (isAvail) => { if (!isAvail) { - actions.setErrors({ username: 'Username is already taken' }); + actions.setErrors({ username: t('Templates.Auth.username_taken') }); actions.setSubmitting(false); return; } @@ -349,12 +353,12 @@ function Register({ registerInfo, loginFlow, baseUrl }) { } sid = result.sid; } - setProcess({ type: 'processing', message: 'Registration in progress....' }); + setProcess({ type: 'processing', message: t('Templates.Auth.registration_in_progress') }); actions.setSubmitting(false); }).catch((err) => { const msg = err.message || err.error; if (['M_USER_IN_USE', 'M_INVALID_USERNAME', 'M_EXCLUSIVE'].indexOf(err.errcode) > -1) { - actions.setErrors({ username: err.errcode === 'M_USER_IN_USE' ? 'Username is already taken' : msg }); + actions.setErrors({ username: err.errcode === 'M_USER_IN_USE' ? t('Templates.Auth.username_taken') : msg }); } else if (msg) actions.setErrors({ other: msg }); actions.setSubmitting(false); @@ -409,7 +413,7 @@ function Register({ registerInfo, loginFlow, baseUrl }) { session, }); if (d.done) refreshWindow(); - else setProcess({ type: 'processing', message: 'Registration in progress...' }); + else setProcess({ type: 'processing', message: t('Templates.Auth.registration_in_progress') }); }; const handleTerms = async () => { const [username, password] = getInputs(); @@ -418,7 +422,7 @@ function Register({ registerInfo, loginFlow, baseUrl }) { session, }); if (d.done) refreshWindow(); - else setProcess({ type: 'processing', message: 'Registration in progress...' }); + else setProcess({ type: 'processing', message: t('Templates.Auth.registration_in_progress') }); }; const handleEmailVerify = async () => { const [username, password] = getInputs(); @@ -429,17 +433,17 @@ function Register({ registerInfo, loginFlow, baseUrl }) { session, }); if (d.done) refreshWindow(); - else setProcess({ type: 'processing', message: 'Registration in progress...' }); + else setProcess({ type: 'processing', message: t('Templates.Auth.registration_in_progress') }); }; return ( <> {process.type === 'processing' && } - {process.type === 'm.login.recaptcha' && } + {process.type === 'm.login.recaptcha' && } {process.type === 'm.login.terms' && } {process.type === 'm.login.email.identity' && }
- {!isDisabled && Register} + {!isDisabled && {t('Templates.Auth.register_form.title')}} {isDisabled && {registerInfo.error}}
{!isDisabled && ( @@ -452,25 +456,25 @@ function Register({ registerInfo, loginFlow, baseUrl }) { values, errors, handleChange, handleSubmit, isSubmitting, }) => ( <> - {process.type === undefined && isSubmitting && } + {process.type === undefined && isSubmitting && }
- + {errors.username && {errors.username}}
- + setPassVisible(!passVisible)} src={passVisible ? EyeIC : EyeBlindIC} size="extra-small" />
{errors.password && {errors.password}}
- + setCPassVisible(!cPassVisible)} src={cPassVisible ? EyeIC : EyeBlindIC} size="extra-small" />
{errors.confirmPassword && {errors.confirmPassword}} - {isEmail && } + {isEmail && } {errors.email && {errors.email}} {errors.other && {errors.other}}
- +
@@ -499,6 +503,8 @@ function AuthCard() { const [hsConfig, setHsConfig] = useState(null); const [type, setType] = useState('login'); + const { t } = useTranslation(); + const handleHsChange = (info) => { console.log(info); setHsConfig(info); @@ -520,13 +526,13 @@ function AuthCard() { )} { hsConfig !== null && ( - {`${(type === 'login' ? 'Don\'t have' : 'Already have')} an account?`} + {(type === 'login' ? t('Templates.Auth.dont_have_an_account') : t('Templates.Auth.already_have_an_account'))} )} @@ -537,6 +543,8 @@ function AuthCard() { function Auth() { const [loginToken, setLoginToken] = useState(getUrlPrams('loginToken')); + const { t } = useTranslation(); + useEffect(async () => { if (!loginToken) return; if (localStorage.getItem(cons.secretKey.BASE_URL) === undefined) { @@ -558,13 +566,13 @@ function Auth() {
- {loginToken && } + {loginToken && } {!loginToken && (
- Cinny + {t('common.cinny')}
@@ -624,21 +632,26 @@ Recaptcha.propTypes = { }; function Terms({ url, onSubmit }) { + const { t } = useTranslation(); + return (
{ e.preventDefault(); onSubmit(); }}>
- Agree with terms + {t('Templates.Auth.terms_and_conditions.title')}
- In order to complete registration, you need to agree to the terms and conditions. + {t('Templates.Auth.terms_and_conditions.description')}
- {'I accept '} - Terms and Conditions + }} + />
- +
@@ -650,18 +663,22 @@ Terms.propTypes = { }; function EmailVerify({ email, onContinue }) { + const { t } = useTranslation(); return (
- Verify email + {t('Templates.Auth.validate_email.title')}
- {'Please check your email '} - {`(${email})`} - {' and validate before continuing further.'} + }} + />
- +
); diff --git a/src/app/templates/client/Client.jsx b/src/app/templates/client/Client.jsx index 8610b62c..499af8cf 100644 --- a/src/app/templates/client/Client.jsx +++ b/src/app/templates/client/Client.jsx @@ -1,6 +1,7 @@ import React, { useState, useEffect, useRef } from 'react'; import './Client.scss'; +import { useTranslation } from 'react-i18next'; import { initHotkeys } from '../../../client/event/hotkeys'; import { initRoomListListener } from '../../../client/event/roomList'; @@ -19,9 +20,11 @@ import navigation from '../../../client/state/navigation'; import cons from '../../../client/state/cons'; import DragDrop from '../../organisms/drag-drop/DragDrop'; +import '../../i18n'; + function Client() { const [isLoading, changeLoading] = useState(true); - const [loadingMsg, setLoadingMsg] = useState('Heating up'); + const [loadingMsg, setLoadingMsg] = useState('Templates.Client.loading_messages.default'); const [dragCounter, setDragCounter] = useState(0); const classNameHidden = 'client__item-hidden'; @@ -37,6 +40,8 @@ function Client() { roomWrapperRef.current?.classList.add(classNameHidden); } + const { t } = useTranslation(); + useEffect(() => { navigation.on(cons.events.navigation.ROOM_SELECTED, onRoomSelected); navigation.on(cons.events.navigation.NAVIGATION_OPENED, onNavigationSelected); @@ -51,8 +56,8 @@ function Client() { let counter = 0; const iId = setInterval(() => { const msgList = [ - 'Almost there...', - 'Looks like you have a lot of stuff to heat up!', + 'Templates.Client.loading_messages.message_one', + 'Templates.Client.loading_messages.message_two', ]; if (counter === msgList.length - 1) { setLoadingMsg(msgList[msgList.length - 1]); @@ -75,13 +80,13 @@ function Client() { return (
- {loadingMsg} + {t(loadingMsg)}
- Cinny + {t('common.cinny')}
);