Skip to content

Commit

Permalink
added i18n
Browse files Browse the repository at this point in the history
  • Loading branch information
assafnoahkoren committed Nov 22, 2023
1 parent 9c221cd commit dbde1a4
Show file tree
Hide file tree
Showing 18 changed files with 159 additions and 30 deletions.
22 changes: 11 additions & 11 deletions services/client/index.html
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<!doctype html>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/png" href="/letter-s.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Sintra</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
2 changes: 2 additions & 0 deletions services/client/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@
"@fontsource/roboto": "^5.0.8",
"@mui/material": "^5.14.18",
"firebase": "^10.6.0",
"i18next": "^23.7.6",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-firebase-hooks": "^5.1.1",
"react-hook-form": "^7.48.2",
"react-i18next": "^13.5.0",
"react-router-dom": "^6.19.0"
},
"devDependencies": {
Expand Down
Binary file added services/client/public/flags/en.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added services/client/public/flags/es.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added services/client/public/letter-s.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
5 changes: 3 additions & 2 deletions services/client/src/_templates/Comp.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import React from 'react';
import { FC } from 'react';

export const Comp: FC = () => {
export const Comp: FC = React.memo(() => {
return (
<>
<h1>Comp Works.</h1>
</>
);
};
});
4 changes: 2 additions & 2 deletions services/client/src/_templates/CompWithChildren.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@ interface CompProps extends React.PropsWithChildren {
children: ReactElement | ReactElement[];
}

export const Comp: FC<CompProps> = props => {
export const Comp: FC<CompProps> = React.memo(props => {
return (
<>
<h1>Comp Works.</h1>
{props.children}
</>
);
};
});
4 changes: 2 additions & 2 deletions services/client/src/_templates/CompWithProps.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@ import React, { FC } from 'react';

interface CompProps extends React.PropsWithChildren {}

export const Comp: FC<CompProps> = () => {
export const Comp: FC<CompProps> = React.memo(props => {
return (
<>
<h1>Comp Works.</h1>
</>
);
};
});
28 changes: 28 additions & 0 deletions services/client/src/core/translations/LanguagePicker.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import { ToggleButtonGroup, ToggleButton } from '@mui/material';
import React, { ReactElement, FC } from 'react';
import { useTranslation } from 'react-i18next';

interface TProps extends React.PropsWithChildren {}

const languages = ['es', 'en'];

export const LanguagePicker: FC<TProps> = React.memo(props => {
const { i18n } = useTranslation();

const [language, setLanguage] = React.useState<string>(i18n.language);
const handleChange = (event: React.MouseEvent<HTMLElement>, newLanguage: string | null) => {
if (!newLanguage) return;
setLanguage(newLanguage);
i18n.changeLanguage(newLanguage);
};

return (
<ToggleButtonGroup color="primary" value={language} exclusive onChange={handleChange} aria-label="Platform">
{languages.map((lang: string) => (
<ToggleButton className="p-2 py-1" key={lang} value={lang}>
<img className="w-4 h-4" src={`/flags/${lang}.png`} alt="flag" />
</ToggleButton>
))}
</ToggleButtonGroup>
);
});
31 changes: 31 additions & 0 deletions services/client/src/core/translations/i18n.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import es from './languages/es';
import en from './languages/en';

// the translations
// (tip move them in a JSON file and import them,
// or even better, manage them separated from your code: https://react.i18next.com/guides/multiple-translation-files)
const resources = {
en: {
translation: en
},
es: {
translation: es
}
};

i18n
.use(initReactI18next) // passes i18n down to react-i18next
.init({
resources,
lng: 'es', // language to use, more information here: https://www.i18next.com/overview/configuration-options#languages-namespaces-resources
// you can use the i18n.changeLanguage function to change the language manually: https://www.i18next.com/overview/api#changelanguage
// if you're using a language detector, do not define the lng option

interpolation: {
escapeValue: false // react already safes from xss
}
});

export default i18n;
1 change: 1 addition & 0 deletions services/client/src/core/translations/languages/en.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export default {};
9 changes: 9 additions & 0 deletions services/client/src/core/translations/languages/es.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Spanish translations
export default {
Login: 'Iniciar sesión',
Email: 'Correo electrónico',
Password: 'Contraseña',
'Login with Google': 'Iniciar sesión con Google',
'Register with Email': 'Registrarse con correo electrónico',
'© 2023 Sintra. All rights reserved.': '© 2023 Sintra. Todos los derechos reservados.'
};
6 changes: 6 additions & 0 deletions services/client/src/core/translations/useTranslate.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { useTranslation } from 'react-i18next';

export const useTranslate = () => {
const { t } = useTranslation();
return t;
};
2 changes: 2 additions & 0 deletions services/client/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import '@fontsource/roboto/400.css';
import '@fontsource/roboto/500.css';
import '@fontsource/roboto/700.css';

import './core/translations/i18n';

ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<App />
Expand Down
4 changes: 3 additions & 1 deletion services/client/src/view/layout/Shell.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Button, Popover } from '@mui/material';
import React, { ReactElement, FC } from 'react';
import { useLogout } from '../../core/firebase/firebase';
import { ProfileButton } from './ProfileButton';
import { LanguagePicker } from '../../core/translations/LanguagePicker';

interface CompProps extends React.PropsWithChildren {
children: ReactElement | ReactElement[];
Expand All @@ -12,13 +13,14 @@ export const Shell: FC<CompProps> = props => {
<div className="flex h-screen">
<div className="flex flex-col flex-1">
<div className="h-12 bg-slate-200 flex justify-between items-center py-0 px-4">
<span className=" flex h-full gap-2">
<span className=" flex items-center h-full gap-2">
<Button variant="text" className="text-slate-500 rounded-none min-w-0 h-full">
<i className="fa-solid fa-bars"></i>
</Button>
<Button variant="text" className="text-slate-500 rounded-none min-w-0 h-full">
<i className="fa-solid fa-bell"></i>
</Button>
<LanguagePicker />
</span>
<span className="text-slate-500 font-bold">Sintra</span>
<ProfileButton />
Expand Down
23 changes: 15 additions & 8 deletions services/client/src/view/pages/LoginPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@ import { FC, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { useSignInGoogle, useSignInPassword } from '../../core/firebase/firebase';
import { useForm, SubmitHandler } from 'react-hook-form';
import { useTranslate } from '../../core/translations/useTranslate';
import { LanguagePicker } from '../../core/translations/LanguagePicker';

type LoginFormFields = {
email: string;
password: string;
password2: string;
};

export const LoginPage: FC = () => {
export const LoginPage: FC = props => {
const t = useTranslate();

const navigate = useNavigate();
const [
signInWithEmailAndPassword,
Expand All @@ -36,13 +40,16 @@ export const LoginPage: FC = () => {

return (
<div className="flex flex-col justify-center items-center bg-slate-200 h-screen">
<div className="flex flex-col items-center gap-4 bg-white p-10 h-min shadow-lg w-1/3 min-w-[400px] rounded">
<div className="flex flex-col items-center gap-4 bg-white p-10 h-min shadow-lg w-1/3 min-w-[400px] rounded relative">
<div className="absolute top-0 left-0 p-4">
<LanguagePicker />
</div>
<h1 className="text-5xl font-bold mb-4">Sintra</h1>
<form onSubmit={handleSubmit(onSubmit)} className="flex flex-col items-center gap-4 w-4/5">
<TextField {...register('email')} placeholder="Email" fullWidth />
<TextField {...register('password')} type="password" placeholder="Password" fullWidth />
<TextField {...register('email')} placeholder={t('Email')} fullWidth />
<TextField {...register('password')} type="password" placeholder={t('Password')} fullWidth />
<Button type="submit" className="px-10">
Login
{t('Login')}
</Button>
{loading_signInWithPassword && <CircularProgress color="inherit" size={24} />}
</form>
Expand All @@ -52,7 +59,7 @@ export const LoginPage: FC = () => {
<div className="h-px bg-gray-300 w-14"></div>
</div>
<Button onClick={() => signInWithGoogle()} variant="outlined" className="w-4/5">
Login with Google
{t('Login with Google')}
<i className="fa-brands fa-google ms-2"></i>
</Button>
<Button
Expand All @@ -61,11 +68,11 @@ export const LoginPage: FC = () => {
color="inherit"
className="text-slate-400 w-4/5"
>
Register with Email
{t('Register with Email')}
</Button>
</div>
<div className="mt-5 mb-60">
<p className="text-sm opacity-50">© 2023 Sintra. All rights reserved.</p>
<p className="text-sm opacity-50">{t('© 2023 Sintra. All rights reserved.')}</p>
</div>
</div>
);
Expand Down
14 changes: 10 additions & 4 deletions services/client/src/view/pages/RegisterPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ import { FC, useEffect } from 'react';
import { useNavigate } from 'react-router-dom';
import { SubmitHandler, useForm } from 'react-hook-form';
import { useCreateUser } from '../../core/firebase/firebase';
import { LanguagePicker } from '../../core/translations/LanguagePicker';
import { useTranslate } from '../../core/translations/useTranslate';

type RegisterFormFields = {
email: string;
Expand All @@ -11,6 +13,7 @@ type RegisterFormFields = {
};

export const RegisterPage: FC = () => {
const t = useTranslate();
const navigate = useNavigate();
const [
createUserWithEmailAndPassword,
Expand All @@ -34,12 +37,15 @@ export const RegisterPage: FC = () => {

return (
<div className="flex flex-col justify-center items-center bg-slate-200 h-screen">
<div className="flex flex-col items-center gap-4 bg-white p-10 h-min shadow-lg w-1/3 min-w-[400px] rounded">
<div className="flex flex-col items-center gap-4 bg-white p-10 h-min shadow-lg w-1/3 min-w-[400px] rounded relative">
<div className="absolute top-0 left-0 p-4">
<LanguagePicker />
</div>
<h1 className="text-5xl font-bold mb-4">Sintra</h1>
<form className="flex flex-col items-center gap-4 w-4/5" onSubmit={handleSubmit(onSubmit)}>
<TextField {...register('email')} placeholder="Email" fullWidth />
<TextField {...register('password')} type="password" placeholder="Password" fullWidth />
<TextField {...register('password2')} type="password" placeholder="Verify password" fullWidth />
<TextField {...register('email')} placeholder={t('Email')} fullWidth />
<TextField {...register('password')} type="password" placeholder={t('Password')} fullWidth />
<TextField {...register('password2')} type="password" placeholder={t('Verify password')} fullWidth />
<Button type="submit" className="px-10" disabled={loading}>
Register
</Button>
Expand Down
34 changes: 34 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,13 @@
dependencies:
regenerator-runtime "^0.14.0"

"@babel/runtime@^7.22.5":
version "7.23.4"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.23.4.tgz#36fa1d2b36db873d25ec631dcc4923fdc1cf2e2e"
integrity sha512-2Yv65nlWnWlSpe3fXEyX5i7fx5kIKo4Qbcj+hMO0odwaneFjfXw5fdum+4yL20O0QiaHpia0cYQ9xpNMqrBwHg==
dependencies:
regenerator-runtime "^0.14.0"

"@babel/template@^7.22.15", "@babel/template@^7.3.3":
version "7.22.15"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.22.15.tgz#09576efc3830f0430f4548ef971dde1350ef2f38"
Expand Down Expand Up @@ -3883,6 +3890,13 @@ html-escaper@^2.0.0:
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453"
integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==

html-parse-stringify@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz#dfc1017347ce9f77c8141a507f233040c59c55d2"
integrity sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==
dependencies:
void-elements "3.1.0"

[email protected]:
version "2.0.0"
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3"
Expand Down Expand Up @@ -3924,6 +3938,13 @@ husky@^8.0.3:
resolved "https://registry.yarnpkg.com/husky/-/husky-8.0.3.tgz#4936d7212e46d1dea28fef29bb3a108872cd9184"
integrity sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==

i18next@^23.7.6:
version "23.7.6"
resolved "https://registry.yarnpkg.com/i18next/-/i18next-23.7.6.tgz#7328e76c899052d5d33d930164612dd21e575f74"
integrity sha512-O66BhXBw0fH4bEJMA0/klQKPEbcwAp5wjXEL803pdAynNbg2f4qhLIYlNHJyE7icrL6XmSZKPYaaXwy11kJ6YQ==
dependencies:
"@babel/runtime" "^7.23.2"

[email protected], iconv-lite@^0.4.24:
version "0.4.24"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b"
Expand Down Expand Up @@ -5520,6 +5541,14 @@ react-hook-form@^7.48.2:
resolved "https://registry.yarnpkg.com/react-hook-form/-/react-hook-form-7.48.2.tgz#01150354d2be61412ff56a030b62a119283b9935"
integrity sha512-H0T2InFQb1hX7qKtDIZmvpU1Xfn/bdahWBN1fH19gSe4bBEqTfmlr7H3XWTaVtiK4/tpPaI1F3355GPMZYge+A==

react-i18next@^13.5.0:
version "13.5.0"
resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-13.5.0.tgz#44198f747628267a115c565f0c736a50a76b1ab0"
integrity sha512-CFJ5NDGJ2MUyBohEHxljOq/39NQ972rh1ajnadG9BjTk+UXbHLq4z5DKEbEQBDoIhUmmbuS/fIMJKo6VOax1HA==
dependencies:
"@babel/runtime" "^7.22.5"
html-parse-stringify "^3.0.1"

react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
Expand Down Expand Up @@ -6466,6 +6495,11 @@ vite@^4.4.5:
optionalDependencies:
fsevents "~2.3.2"

[email protected]:
version "3.1.0"
resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09"
integrity sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==

walker@^1.0.8:
version "1.0.8"
resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.8.tgz#bd498db477afe573dc04185f011d3ab8a8d7653f"
Expand Down

0 comments on commit dbde1a4

Please sign in to comment.