From b221934d723f5b1373ba1ded5d937cbfa416194f Mon Sep 17 00:00:00 2001 From: FabioL <1647128+loviuz@users.noreply.github.com> Date: Thu, 7 Mar 2024 08:44:07 +0100 Subject: [PATCH] Added i18n to almost all strings (#273) * Added italian translation * Added i18n to almost all strings * Fix english translation --------- Co-authored-by: loviuz Co-authored-by: bjarneo --- public/locales/en/translation.json | 82 ++++++++++++++++++- public/locales/it/translation.json | 82 ++++++++++++++++++- src/client/app-shell.jsx | 6 +- src/client/components/header/nav.jsx | 6 +- src/client/components/settings/main-links.jsx | 7 +- .../components/settings/secondary-links.jsx | 4 +- src/client/components/spinner/index.jsx | 2 +- src/client/routes/account/account.jsx | 39 +++++---- src/client/routes/account/index.jsx | 21 ++--- src/client/routes/account/secrets.jsx | 23 +++--- src/client/routes/account/settings.jsx | 24 +++--- src/client/routes/account/users.jsx | 10 +-- src/client/routes/privacy/index.jsx | 5 +- src/client/routes/signin/index.jsx | 10 +-- src/client/routes/signout/index.jsx | 6 +- src/client/routes/signup/index.jsx | 12 +-- src/client/routes/terms/index.jsx | 5 +- 17 files changed, 263 insertions(+), 81 deletions(-) diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 47873dba..9832fd19 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -2,7 +2,62 @@ "sign_in": "Sign in", "sign_out": "Sign out", "sign_up": "Sign up", - "account": "Account", + "not_logged_in": "Not logged in", + "something_went_wrong": "Something went wrong!", + "loading": "Loading", + "account": { + "home": { + "title": "Account", + "update_password": "Update your password", + "first_time_message": "If this is the first time you sign in on this user account, you should go to Account settings and update your password", + "hi": "Hi", + "intro": "We are glad you logged in. Here is the list of features signed in users get", + "upload_files": "Upload files", + "expiration": "Expiration time of 14 and 28 days for secrets", + "secrets": "List and delete your secrets", + "more": "More features are coming! Thanks for joining Hemmelig.app!" + }, + "account": { + "passwords_does_not_match": "Passwords did not match", + "can_not_update_profile": "Could not update your user profile", + "can_not_delete": "Could not delete the user", + "delete_account": "Delete your profile", + "do_you_want_delete": "Are you sure you want to delete your profile?", + "dont_delete_account": "No, don't delete it", + + "email": "Email", + "your_password": "Current password", + "current_password": "Your current password", + "update_your_password": "Update your password", + "new_password": "New password", + "confirm_password": "Confirm Password", + "confirm_new_password": "Confirm your new password", + "update_details": "Update details" + }, + "secrets": { + "id": "Id", + "expires": "Expires", + "public": "Public", + "delete": "Delete", + "yes": "Yes", + "no": "No", + "delete_secret": "Delete secret", + "dont_delete_secret": "No don't delete it", + "do_you_want_delete": "Are you sure you want to delete this secret?" + }, + "settings": { + "read_only_mode": "Read only mode", + "readonly_only_for_non_admin": "Should the Hemmelig instance be read only for non admin users?", + "disable_users": "Disable users", + "disable_signin": "Should user sign in be disabled?", + "disable_user_account_creation": "Disable user account creation", + "disable_user_account_creation_description": "Do not allow users to create a new account. As an admin, you will still be able to create new user accounts", + "disable_file_upload": "Disable file upload", + "disable_file_upload_description": "Disable file upload for your instance", + "restrict_organization_email": "Restrict to email domain", + "restrict_organization_email_description": "This will limit user registration for a certain email domain" + } + }, "public_list": "Public pastes", "account_settings": "Account settings", "instance_settings": "Instance settings", @@ -98,5 +153,30 @@ "expires": "Expires", "username": "Username", "of": "of" + }, + "signout": { + "title": "Signing out", + "waiting": "Please wait" + }, + "signin": { + "title": "Sign in", + "heading": "Everything you need to access, and manage the Hemmelig secrets.", + "username": "Username", + "password": "Your password", + "signup": "Sign in" + }, + "signup": { + "title": "Sign up", + "heading": "Everything you need to access, and manage the Hemmelig secrets.", + "email": "Email", + "username": "Username", + "password": "Your password", + "signup": "Sign up" + }, + "privacy": { + "title": "Privacy" + }, + "terms": { + "title": "Terms & conditions" } } diff --git a/public/locales/it/translation.json b/public/locales/it/translation.json index f54bf09e..16d0425e 100644 --- a/public/locales/it/translation.json +++ b/public/locales/it/translation.json @@ -2,7 +2,62 @@ "sign_in": "Accedi", "sign_out": "Esci", "sign_up": "Registrati", - "account": "Profilo", + "not_logged_in": "Non autenticatǝ", + "something_went_wrong": "Qualcosa è andato storto!", + "loading": "Caricamento", + "account": { + "home": { + "title": "Profilo", + "update_password": "Aggiorna la tua password", + "first_time_message": "Se questa è la prima volta che accedi a questo profilo, dovresti entrare su Impostazioni profilo e aggiornare la tua password", + "hi": "Ciao", + "intro": "Siamo lietɜ che tu sia entratǝ. Ecco una lista di funzionalità che hanno lɜ utentɜ autenticatɜ", + "upload_files": "Caricare file", + "expiration": "Estensione giorni di scadenza per i segreti fino a 14 e 28 giorni", + "secrets": "Visualizzare ed eliminare i tuoi segreti", + "more": "Altre funzionalità sono in arrivo! Grazie per aver scelto Hemmelig.app!" + }, + "account": { + "passwords_does_not_match": "Le password non coincidono", + "can_not_update_profile": "Impossibile aggiornare il tuo profilo", + "can_not_delete": "Impossibile eliminare l'utente", + "delete_account": "Elimina profilo", + "do_you_want_delete": "Sei sicurǝ di eliminare il tuo profilo?", + "dont_delete_account": "No, non eliminarlo", + + "email": "Email", + "your_password": "Password attuale", + "current_password": "La tua password attuale", + "update_your_password": "Aggiorna la tua password", + "new_password": "Nuova password", + "confirm_password": "Conferma Password", + "confirm_new_password": "Conferma la tua nuova password", + "update_details": "Aggiorna dati" + }, + "secrets": { + "id": "Id", + "expires": "Scadenza", + "public": "Pubblico", + "delete": "Elimina", + "yes": "Sì", + "no": "No", + "delete_secret": "Elimina segreto", + "dont_delete_secret": "No, non eliminarlo", + "do_you_want_delete": "Sei sicurǝ di eliminare questo segreto?" + }, + "settings": { + "read_only_mode": "Modalità sola lettura", + "readonly_only_for_non_admin": "L'stanza Hemmelig può essere utilizzabile solo da utenti non amministratori?", + "disable_users": "Disabilita utenti", + "disable_signin": "L'accesso utente dovrebbe essere disabilitato?", + "disable_user_account_creation": "Disabilita la creazione di profili utente", + "disable_user_account_creation_description": "Non permettere allǝ utentǝ di creare un nuovo profilo. Come amministratore, sarai sempre in grando di creare nuovi profili utente", + "disable_file_upload": "Disabilita caricamento file", + "disable_file_upload_description": "Disabilita il caricamento di file per la tua istanza", + "restrict_organization_email": "Limita al dominio dell'email", + "restrict_organization_email_description": "Questo limiterà la registrazione utente per un certo dominio" + } + }, "public_list": "Note pubbliche", "account_settings": "Impostazioni profilo", "instance_settings": "Impostazioni istanza", @@ -98,5 +153,30 @@ "expires": "Scadenza", "username": "Nome utente", "of": "di" + }, + "signout": { + "title": "Uscita in corso", + "waiting": "Attendere" + }, + "signin": { + "title": "Accedi", + "heading": "Tutto il necessario per accedere e gestire i segreti di Hemmelig.", + "username": "Nome utente", + "password": "La tua password", + "signin": "Accedi" + }, + "signup": { + "title": "Registrati", + "heading": "Tutto il necessario per accedere e gestire i segreti di Hemmelig.", + "email": "Email", + "username": "Nome utente", + "password": "Scegli una password", + "signup": "Registrati" + }, + "privacy": { + "title": "Privacy" + }, + "terms": { + "title": "Termini e condizioni" } } diff --git a/src/client/app-shell.jsx b/src/client/app-shell.jsx index f32a8b78..53fe2630 100644 --- a/src/client/app-shell.jsx +++ b/src/client/app-shell.jsx @@ -37,7 +37,7 @@ const ApplicationShell = () => { size="xs" transform="uppercase" > - {t('account')} + {t('account.home.title')} | { size="xs" transform="uppercase" > - Privacy + {t('privacy.title')} | { size="xs" transform="uppercase" > - Terms & Condition + {t('terms.title')} | diff --git a/src/client/components/header/nav.jsx b/src/client/components/header/nav.jsx index 0a74f537..eb3b27a3 100644 --- a/src/client/components/header/nav.jsx +++ b/src/client/components/header/nav.jsx @@ -42,7 +42,7 @@ const Nav = ({ opened, toggle, isLoggedIn }) => { navItems.push( { - label: t('account'), + label: t('account.home.title'), icon: , component: Link, onClick: toggle, @@ -59,14 +59,14 @@ const Nav = ({ opened, toggle, isLoggedIn }) => { if (isMobile) { navItems.push({ - label: 'Privacy', + label: t('privacy.title'), icon: , component: Link, onClick: toggle, to: '/privacy', }); navItems.push({ - label: 'Terms & Condition', + label: t('terms.title'), icon: , component: Link, onClick: toggle, diff --git a/src/client/components/settings/main-links.jsx b/src/client/components/settings/main-links.jsx index 2d284493..f7b55935 100644 --- a/src/client/components/settings/main-links.jsx +++ b/src/client/components/settings/main-links.jsx @@ -36,7 +36,12 @@ export default function MainLinks() { const { t } = useTranslation(); const data = [ - { icon: , color: 'blue', label: t('account'), route: 'account' }, + { + icon: , + color: 'blue', + label: t('account.home.title'), + route: 'account', + }, { icon: , color: 'teal', diff --git a/src/client/components/settings/secondary-links.jsx b/src/client/components/settings/secondary-links.jsx index 47082f4c..5e5b5444 100644 --- a/src/client/components/settings/secondary-links.jsx +++ b/src/client/components/settings/secondary-links.jsx @@ -43,12 +43,12 @@ export default function SecondaryLinks() { route: '/public', }, { - label: 'Privacy', + label: t('privacy.title'), icon: , route: '/account/privacy', }, { - label: 'Terms & Condition', + label: t('terms.title'), icon: , route: '/account/terms', }, diff --git a/src/client/components/spinner/index.jsx b/src/client/components/spinner/index.jsx index 27942f71..8d8de11e 100644 --- a/src/client/components/spinner/index.jsx +++ b/src/client/components/spinner/index.jsx @@ -1,5 +1,5 @@ import style from './style.module.css'; -const Spinner = () =>
Loading...
; +const Spinner = () =>
{t('loading')}...
; export default Spinner; diff --git a/src/client/routes/account/account.jsx b/src/client/routes/account/account.jsx index 4a044db9..8f8153c8 100644 --- a/src/client/routes/account/account.jsx +++ b/src/client/routes/account/account.jsx @@ -40,7 +40,7 @@ const Account = () => { }, validate: { confirmNewPassword: (value, values) => - value !== values.newPassword ? 'Passwords did not match' : null, + value !== values.newPassword ? t('account.account.passwords_does_not_match') : null, }, }); @@ -50,7 +50,7 @@ const Account = () => { const userInfo = await getUser(); if (userInfo.error || [401, 500].includes(userInfo.statusCode)) { - setUserError(userInfo.error ? userInfo.error : 'Not logged in'); + setUserError(userInfo.error ? userInfo.error : t('not_logged_in')); return; } @@ -77,7 +77,7 @@ const Account = () => { setError( updatedUserInfo.error ? updatedUserInfo.error - : 'Could not update your user profile' + : t('account.account.can_not_update_profile') ); return; @@ -114,7 +114,9 @@ const Account = () => { if (deletedUserInfo.error || [401, 500].includes(deletedUserInfo.statusCode)) { setError( - deletedUserInfo.error ? deletedUserInfo.error : 'Could not delete the user' + deletedUserInfo.error + ? deletedUserInfo.error + : t('account.account.can_not_delete') ); return; @@ -134,10 +136,13 @@ const Account = () => { const openDeleteModal = () => openConfirmModal({ - title: 'Delete your profile', + title: t('account.accounts.delete_account'), centered: true, - children: Are you sure you want to delete your profile?, - labels: { confirm: 'Delete account', cancel: "No don't delete it" }, + children: {t('account.account.do_you_want_delete')}, + labels: { + confirm: t('account.account.delete_account'), + cancel: t('account.account.dont_delete_account'), + }, confirmProps: { color: 'red' }, onConfirm: () => onDeleteUser(), }); @@ -170,30 +175,30 @@ const Account = () => { } - placeholder="Email" + placeholder={t('account.account.email')} {...form.getInputProps('email')} /> } - placeholder="Your current password" + placeholder={t('account.account.current_password')} {...form.getInputProps('currentPassword')} /> } - placeholder="Update your password" + placeholder={t('account.account.update_your_password')} {...form.getInputProps('newPassword')} /> } - placeholder="Confirm your new password" + placeholder={t('account.account.confirm_new_password')} {...form.getInputProps('confirmNewPassword')} /> @@ -206,7 +211,7 @@ const Account = () => { onClick={openDeleteModal} leftIcon={} > - Delete profile + {t('account.account.delete_account')} diff --git a/src/client/routes/account/index.jsx b/src/client/routes/account/index.jsx index 5a718fd5..e41909db 100644 --- a/src/client/routes/account/index.jsx +++ b/src/client/routes/account/index.jsx @@ -19,7 +19,7 @@ const HomeAccount = () => { const userInfo = await getUser(); if (userInfo.error || [401, 500].includes(userInfo.statusCode)) { - setError(userInfo.error ? userInfo.error : 'Not logged in'); + setError(userInfo.error ? userInfo.error : t('not_logged_in')); return; } @@ -54,26 +54,27 @@ const HomeAccount = () => { ); } - const firstTimeMessage = - 'If this is the first time you sign in on this user account, you should go to Account settings and update your password.'; return ( {user?.generated && ( - + )} - Hi, {user.username} + {t('account.home.hi')}, {user.username} - We are glad you logged in. Here is the list of features signed in users get: + {t('account.home.intro')}:
    -
  • Upload files
  • -
  • Expiration time of 14 and 28 days for secrets
  • -
  • List and delete your secrets
  • +
  • {t('account.home.upload_files')}
  • +
  • {t('account.home.expiration')}
  • +
  • {t('account.home.secrets')}
- More features are coming! Thanks for joining Hemmelig.app! + {t('account.home.more')} 🎉 🎉 🎉 🎉 diff --git a/src/client/routes/account/secrets.jsx b/src/client/routes/account/secrets.jsx index 2f7e4b21..c3d1097f 100644 --- a/src/client/routes/account/secrets.jsx +++ b/src/client/routes/account/secrets.jsx @@ -51,7 +51,7 @@ const Secrets = () => { const secrets = await getSecrets(); if (secrets.error || [401, 500].includes(secrets.statusCode)) { - setUserError(secrets.error ? secrets.error : 'Not logged in'); + setUserError(secrets.error ? secrets.error : t('not_logged_in')); return; } @@ -71,7 +71,7 @@ const Secrets = () => { const burnedSecret = await burnSecret(secret.id); if (burnedSecret.error) { - setError(burnedSecret.error ? burnedSecret.error : 'Something went wrong!'); + setError(burnedSecret.error ? burnedSecret.error : t('something_went_wrong')); return; } @@ -92,10 +92,13 @@ const Secrets = () => { const openDeleteModal = (secret) => { setSuccess(false); openConfirmModal({ - title: 'Delete ' + secret.id, + title: t('account.secrets.delete') + ' ' + secret.id, centered: true, - children: Are you sure you want to delete this secret?, - labels: { confirm: 'Delete secret', cancel: "No don't delete it" }, + children: {t('account.secrets.do_you_want_delete')}, + labels: { + confirm: t('account.secrets.delete_secret'), + cancel: t('account.secrets.dont_delete_secret'), + }, confirmProps: { color: 'red' }, onConfirm: () => onDeleteSecret(secret), }); @@ -109,7 +112,7 @@ const Secrets = () => { {secret.isPublic ? secret.title : secret.id} {getTime(secret.expiresAt)} - {secret.isPublic ? 'Yes' : 'No'} + {secret.isPublic ? t('account.secrets.yes') : t('account.secrets.no')} openDeleteModal(secret)}> @@ -142,10 +145,10 @@ const Secrets = () => { - - - - + + + + {rows} diff --git a/src/client/routes/account/settings.jsx b/src/client/routes/account/settings.jsx index 2b24e345..f9a2f552 100644 --- a/src/client/routes/account/settings.jsx +++ b/src/client/routes/account/settings.jsx @@ -32,7 +32,7 @@ const Settings = () => { const adminSettings = await getSettings(); if (adminSettings.error || [401, 403, 500].includes(adminSettings.statusCode)) { - setUserError(adminSettings.error ? adminSettings.error : 'Not logged in'); + setUserError(adminSettings.error ? adminSettings.error : t('not_logged_in')); return; } @@ -60,7 +60,7 @@ const Settings = () => { setError( updatedAdminSettings.error ? updatedAdminSettings.error - : 'Something went wrong!' + : t('something_went_wrong') ); return; @@ -103,16 +103,16 @@ const Settings = () => { {t('settings.description')} form.setValues({ read_only: event.currentTarget.checked })} /> form.setValues({ disable_users: event.currentTarget.checked }) @@ -121,8 +121,8 @@ const Settings = () => { form.setValues({ @@ -133,8 +133,8 @@ const Settings = () => { form.setValues({ disable_file_upload: event.currentTarget.checked }) @@ -143,8 +143,8 @@ const Settings = () => { } diff --git a/src/client/routes/account/users.jsx b/src/client/routes/account/users.jsx index 0d485feb..6b2d50c9 100644 --- a/src/client/routes/account/users.jsx +++ b/src/client/routes/account/users.jsx @@ -80,7 +80,7 @@ const Users = () => { const users = await getUsers(); if (users.error || [401, 403, 500].includes(users.statusCode)) { - setUserError(users.error ? users.error : 'Not logged in'); + setUserError(users.error ? users.error : t('not_logged_in')); return; } @@ -111,7 +111,7 @@ const Users = () => { updatedUserInfo.error || [401, 403, 409, 500].includes(updatedUserInfo.statusCode) ) { - setError(updatedUserInfo.error ? updatedUserInfo.error : 'Sonething went wrong!'); + setError(updatedUserInfo.error ? updatedUserInfo.error : t('something_went_wrong')); return; } @@ -136,7 +136,7 @@ const Users = () => { const addedUserInfo = await addUser(form.values); if (addedUserInfo.error || [401, 403, 409, 500].includes(addedUserInfo.statusCode)) { - setError(addedUserInfo.error ? addedUserInfo.error : 'Something went wrong!'); + setError(addedUserInfo.error ? addedUserInfo.error : t('something_went_wrong')); return; } @@ -162,7 +162,7 @@ const Users = () => { deletedUserInfo.error || [401, 403, 409, 500].includes(deletedUserInfo.statusCode) ) { - setError(deletedUserInfo.error ? deletedUserInfo.error : 'Something went wrong!'); + setError(deletedUserInfo.error ? deletedUserInfo.error : t('something_went_wrong')); return; } @@ -187,7 +187,7 @@ const Users = () => { const moreUsers = await getUsers(skip); if (moreUsers.error || [401, 403, 409, 500].includes(moreUsers.statusCode)) { - setError(moreUsers.error ? moreUsers.error : 'Something went wrong!'); + setError(moreUsers.error ? moreUsers.error : t('something_went_wrong')); return; } diff --git a/src/client/routes/privacy/index.jsx b/src/client/routes/privacy/index.jsx index 8a6a976a..a5cbb756 100644 --- a/src/client/routes/privacy/index.jsx +++ b/src/client/routes/privacy/index.jsx @@ -1,10 +1,13 @@ import { Container, Stack, Text, Title } from '@mantine/core'; +import { useTranslation } from 'react-i18next'; const Privacy = () => { + const { t } = useTranslation(); + return ( - Privacy + {t('privacy.title')} Is my data secure? diff --git a/src/client/routes/signin/index.jsx b/src/client/routes/signin/index.jsx index 3e875a23..78d15423 100644 --- a/src/client/routes/signin/index.jsx +++ b/src/client/routes/signin/index.jsx @@ -58,18 +58,18 @@ const SignIn = () => {
onSignIn(values))}> - Sign in + {t('signin.title')} - Everything you need to access, and manage the Hemmelig secrets. + {t('signin.heading')} {error && } } - placeholder="Username" + placeholder={t('signin.username')} required autoFocus {...form.getInputProps('username')} @@ -77,13 +77,13 @@ const SignIn = () => { } - placeholder="Your password" + placeholder={t('signin.password')} required {...form.getInputProps('password')} /> diff --git a/src/client/routes/signout/index.jsx b/src/client/routes/signout/index.jsx index d5654750..1a44c93b 100644 --- a/src/client/routes/signout/index.jsx +++ b/src/client/routes/signout/index.jsx @@ -1,5 +1,6 @@ import { Center, Container, Image, Stack, Text, Title } from '@mantine/core'; import { useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; import { useDispatch } from 'react-redux'; import { Navigate } from 'react-router-dom'; import { userLogin, userLoginChanged } from '../../actions'; @@ -10,6 +11,7 @@ import styles from './style.module.css'; const SignOut = () => { const dispatch = useDispatch(); const [redirect, setRedirect] = useState(false); + const { t } = useTranslation(); useEffect(async () => { removeCookie(); @@ -29,10 +31,10 @@ const SignOut = () => {
- Signing out... + {t('signout.title')}...
- Please wait... + {t('signout.waiting')}...
{
onSignUp(values))}> - Sign up + {t('signup.title')} - Everything you need to access, and manage the Hemmelig secrets. + {t('signup.heading')} {error && } } - placeholder="Email" + placeholder={t('signup.email')} required {...form.getInputProps('email')} /> } - placeholder="Username" + placeholder={t('signup.username')} required {...form.getInputProps('username')} /> } - placeholder="Your password" + placeholder={t('signup.password')} required {...form.getInputProps('password')} /> diff --git a/src/client/routes/terms/index.jsx b/src/client/routes/terms/index.jsx index 6acf3ee6..cc6838cf 100644 --- a/src/client/routes/terms/index.jsx +++ b/src/client/routes/terms/index.jsx @@ -1,10 +1,13 @@ import { Container, Stack, Text, Title } from '@mantine/core'; +import { useTranslation } from 'react-i18next'; const TermsAndCondition = () => { + const { t } = useTranslation(); + return ( - Terms & Condition + {t('terms.title')} AGREEMENT TO TERMS
IdExpiresPublicDelete{t('account.secrets.id')}{t('account.secrets.expires')}{t('account.secrets.public')}{t('account.secrets.delete')}