diff --git a/webapp/package-lock.json b/webapp/package-lock.json index ccd4979..c6593dd 100644 --- a/webapp/package-lock.json +++ b/webapp/package-lock.json @@ -15,8 +15,10 @@ "@testing-library/react": "^14.1.2", "@testing-library/user-event": "^14.5.2", "axios": "^1.6.5", + "i18next": "^23.10.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-i18next": "^14.0.5", "react-scripts": "5.0.1", "web-vitals": "^3.5.1" }, @@ -1990,9 +1992,9 @@ "integrity": "sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==" }, "node_modules/@babel/runtime": { - "version": "7.23.7", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.7.tgz", - "integrity": "sha512-w06OXVOFso7LcbzMiDGt+3X7Rh7Ho8MmgPoWU3rarH+8upf+wSU/grlGbWzQyr3DkdN6ZeuMFjpdwW0Q+HxobA==", + "version": "7.23.9", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.23.9.tgz", + "integrity": "sha512-0CX6F+BI2s9dkUqr08KFrAIZgNFj75rdBU/DjCyYLIaV/quFjkk6T+EJ2LkZHyZTbEV4L5p97mNkUsHl2wLFAw==", "dependencies": { "regenerator-runtime": "^0.14.0" }, @@ -11941,6 +11943,14 @@ "node": ">=12" } }, + "node_modules/html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "dependencies": { + "void-elements": "3.1.0" + } + }, "node_modules/html-webpack-plugin": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/html-webpack-plugin/-/html-webpack-plugin-5.6.0.tgz", @@ -12084,6 +12094,28 @@ "node": ">=10.17.0" } }, + "node_modules/i18next": { + "version": "23.10.0", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.10.0.tgz", + "integrity": "sha512-/TgHOqsa7/9abUKJjdPeydoyDc0oTi/7u9F8lMSj6ufg4cbC1Oj3f/Jja7zj7WRIhEQKB7Q4eN6y68I9RDxxGQ==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, "node_modules/iconv-lite": { "version": "0.6.3", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", @@ -21608,6 +21640,27 @@ "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.11.tgz", "integrity": "sha512-/6UZ2qgEyH2aqzYZgQPxEnz33NJ2gNsnHA2o5+o4wW9bLM/JYQitNP9xPhsXwC08hMMovfGe/8retsdDsczPRg==" }, + "node_modules/react-i18next": { + "version": "14.0.5", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-14.0.5.tgz", + "integrity": "sha512-5+bQSeEtgJrMBABBL5lO7jPdSNAbeAZ+MlFWDw//7FnVacuVu3l9EeWFzBQvZsKy+cihkbThWOAThEdH8YjGEw==", + "dependencies": { + "@babel/runtime": "^7.23.9", + "html-parse-stringify": "^3.0.1" + }, + "peerDependencies": { + "i18next": ">= 23.2.3", + "react": ">= 16.8.0" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + } + } + }, "node_modules/react-is": { "version": "17.0.2", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", @@ -26146,6 +26199,14 @@ "node": ">= 0.8" } }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/w3c-hr-time": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.2.tgz", diff --git a/webapp/package.json b/webapp/package.json index e2a20a4..b66d518 100644 --- a/webapp/package.json +++ b/webapp/package.json @@ -10,8 +10,10 @@ "@testing-library/react": "^14.1.2", "@testing-library/user-event": "^14.5.2", "axios": "^1.6.5", + "i18next": "^23.10.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "react-i18next": "^14.0.5", "react-scripts": "5.0.1", "web-vitals": "^3.5.1" }, diff --git a/webapp/src/App.tsx b/webapp/src/App.tsx index 59fbd7c..de38782 100644 --- a/webapp/src/App.tsx +++ b/webapp/src/App.tsx @@ -1,12 +1,15 @@ import { useState } from 'react'; +import { useTranslation } from 'react-i18next'; import AddUser from './components/AddUser'; import Login from './components/Login'; import CssBaseline from '@mui/material/CssBaseline'; import Container from '@mui/material/Container'; import Typography from '@mui/material/Typography'; import Init from './components/Init'; +import './i18n' function App() { + const { t } = useTranslation() const [showLogin, setShowLogin] = useState(true); const [showInit, setShowInit] = useState(true); @@ -23,7 +26,7 @@ function App() { - Conocer y Vencer + {t('app_name')} {showInit ? < Init changeView={handleLogginRegisterToggleView}/> diff --git a/webapp/src/components/AddUser.tsx b/webapp/src/components/AddUser.tsx index a871a55..f20a8dd 100644 --- a/webapp/src/components/AddUser.tsx +++ b/webapp/src/components/AddUser.tsx @@ -2,6 +2,7 @@ import { useState } from 'react'; import axios from 'axios'; import { Container, Typography, TextField, Snackbar } from '@mui/material'; +import { useTranslation } from 'react-i18next'; const apiEndpoint = process.env.REACT_APP_API_ENDPOINT || 'http://localhost:8000'; @@ -10,6 +11,7 @@ type ActionProps = { } const AddUser = (props:ActionProps) => { + const { t } = useTranslation() const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [error, setError] = useState(''); @@ -31,7 +33,7 @@ const AddUser = (props:ActionProps) => { return ( - Add User + {t('add_user')} { onChange={(e) => setPassword(e.target.value)} /> {error && ( diff --git a/webapp/src/components/Init.tsx b/webapp/src/components/Init.tsx index 6cf6b85..bf1a694 100644 --- a/webapp/src/components/Init.tsx +++ b/webapp/src/components/Init.tsx @@ -1,19 +1,20 @@ - +import { useTranslation } from 'react-i18next'; type ActionProps = { changeView:(arg:boolean)=> void; } const Init = (props:ActionProps) =>{ + const { t } = useTranslation() return (
); diff --git a/webapp/src/components/Login.tsx b/webapp/src/components/Login.tsx index 4a4236f..4c43cd5 100644 --- a/webapp/src/components/Login.tsx +++ b/webapp/src/components/Login.tsx @@ -2,12 +2,14 @@ import { useState } from 'react'; import axios from 'axios'; import { Container, Typography, TextField, Snackbar } from '@mui/material'; +import { useTranslation } from 'react-i18next'; type ActionProps = { goBack:()=> void; } const Login = (props: ActionProps) => { + const { t } = useTranslation() const [username, setUsername] = useState(''); const [password, setPassword] = useState(''); const [error, setError] = useState(''); @@ -51,7 +53,7 @@ const Login = (props: ActionProps) => { ) : (
- Login + {t('login')} { onChange={(e) => setPassword(e.target.value)} /> {error && ( diff --git a/webapp/src/constants/index.ts b/webapp/src/constants/index.ts new file mode 100644 index 0000000..963e29a --- /dev/null +++ b/webapp/src/constants/index.ts @@ -0,0 +1,4 @@ +export const LANGUAGES = [ + { label: 'Spanish', code: 'es'}, + { label: 'English', code: 'en'}, +] \ No newline at end of file diff --git a/webapp/src/i18n.ts b/webapp/src/i18n.ts new file mode 100644 index 0000000..a330cc1 --- /dev/null +++ b/webapp/src/i18n.ts @@ -0,0 +1,35 @@ +import i18n from 'i18next'; +import { initReactI18next } from 'react-i18next'; + +i18n + .use(initReactI18next) + .init({ + lng: 'en', + fallbackLng: 'en', + interpolation:{ + escapeValue: false + }, + resources: { + en: { + translation: { + app_name: 'Conocer y Vencer', + login: 'Login', + go_back: 'Go back', + register: 'Register', + add_user: 'Add user' + } + }, + es: { + translation: { + app_name: 'Conocer y Vencer', + login: 'Iniciar sesión', + go_back: 'Ir atrás', + register: 'Registrarse', + add_user: 'Añadir usuario' + } + }, + + } + }); + +export default i18n; \ No newline at end of file