diff --git a/.vercel/README.txt b/.vercel/README.txt deleted file mode 100644 index 525d8ce..0000000 --- a/.vercel/README.txt +++ /dev/null @@ -1,11 +0,0 @@ -> Why do I have a folder named ".vercel" in my project? -The ".vercel" folder is created when you link a directory to a Vercel project. - -> What does the "project.json" file contain? -The "project.json" file contains: -- The ID of the Vercel project that you linked ("projectId") -- The ID of the user or team your Vercel project is owned by ("orgId") - -> Should I commit the ".vercel" folder? -No, you should not share the ".vercel" folder with anyone. -Upon creation, it will be automatically added to your ".gitignore" file. diff --git a/.vercel/project.json b/.vercel/project.json deleted file mode 100644 index e57c056..0000000 --- a/.vercel/project.json +++ /dev/null @@ -1 +0,0 @@ -{"orgId":"geMaCxBkqXsN4lGh58Jus5t0","projectId":"prj_WyreEDN2T9Xtct9wUqz7Jrm1QWr9"} \ No newline at end of file diff --git a/next-i18next.config.js b/next-i18next.config.js new file mode 100644 index 0000000..e0ea3d5 --- /dev/null +++ b/next-i18next.config.js @@ -0,0 +1,6 @@ +module.exports = { + i18n: { + defaultLocale: "en", + locales: ["en", "es"], + }, +}; diff --git a/next.config.js b/next.config.js index dd48eff..17bb867 100644 --- a/next.config.js +++ b/next.config.js @@ -1,3 +1,5 @@ +const { i18n } = require("./next-i18next.config"); + module.exports = { reactStrictMode: true, async rewrites() { @@ -8,4 +10,5 @@ module.exports = { }, ]; }, + i18n, }; diff --git a/package.json b/package.json index 5988b44..dbda040 100644 --- a/package.json +++ b/package.json @@ -42,9 +42,12 @@ "date-fns": "^2.24.0", "file-loader": "^6.2.0", "filesize.js": "^2.0.0", + "i18next": "^21.5.4", + "i18next-browser-languagedetector": "^6.1.2", "isomorphic-fetch": "^3.0.0", "js-cookie": "^3.0.1", "next": "11.1.2", + "next-i18next": "^10.0.1", "protobufjs": "^6.11.2", "qrcode": "^1.4.4", "react": "17.0.2", diff --git a/packages/ui/components/LoginBox/LoginBox.tsx b/packages/ui/components/LoginBox/LoginBox.tsx index bf312de..e2c66e5 100644 --- a/packages/ui/components/LoginBox/LoginBox.tsx +++ b/packages/ui/components/LoginBox/LoginBox.tsx @@ -1,7 +1,8 @@ import { FunctionComponent, useState, useEffect } from "react"; -import logo from '../../assets/tella-logo.png' +import { useTranslation } from "next-i18next"; +import logo from "../../assets/tella-logo.png"; import Img from "next/image"; -import { AiFillEyeInvisible, AiFillEye } from 'react-icons/ai' +import { AiFillEyeInvisible, AiFillEye } from "react-icons/ai"; type Credential = { username: string; @@ -21,25 +22,27 @@ export const LoginBox: FunctionComponent = ({ username: "", password: "", }); - const [message, handleMessage] = useState('') - const [canSubmit, handleCanSubmit] = useState(false) - const [showPass, handleShowPass] = useState(false) + const [message, handleMessage] = useState(""); + const [canSubmit, handleCanSubmit] = useState(false); + const [showPass, handleShowPass] = useState(false); + const { t } = useTranslation("login"); useEffect(() => { if (message.length > 0) { - handleMessage('') + handleMessage(""); } - - const auxCanSubmit = credentail.username.length > 0 && credentail.password.length > 0 + + const auxCanSubmit = + credentail.username.length > 0 && credentail.password.length > 0; if (auxCanSubmit !== canSubmit) { - handleCanSubmit(auxCanSubmit) + handleCanSubmit(auxCanSubmit); } - }, [credentail]) + }, [credentail]); const testMail = () => { // const expression = "" - return /\S+@\S+\.\S+/.test(credentail.username) - } + return /\S+@\S+\.\S+/.test(credentail.username); + }; return (
= ({ onSubmit(credentail); }} > -
- Tella logo +
+ Tella logo
-

Sign in

+

{t("title")}

{ setCredentail({ ...credentail, username: e.target.value }); }} /> -
+
{ setCredentail({ ...credentail, password: e.target.value }); }} /> -
{ - handleShowPass(!showPass) + handleShowPass(!showPass); }} > - { showPass ? ( - + {showPass ? ( + ) : ( - + )}
@@ -96,7 +99,7 @@ export const LoginBox: FunctionComponent = ({ disabled={!canSubmit} type={"submit"} > - Sign in + {t("login.btn")} {errorMessage && (
= ({ {errorMessage}
)} - { !testMail() && + {!testMail() && (
- Please enter a valid email address + {t("error.email")}
- } + )} ); }; diff --git a/pages/_app.tsx b/pages/_app.tsx index 4be4f91..68e995a 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,6 +1,7 @@ import "../styles/globals.css"; import "../styles/tailwind.css"; import type { AppProps } from "next/app"; +import { appWithTranslation } from "next-i18next"; import { dependenciesLocator as plocs } from "packages/bloc"; import { createContext } from "../common/Context"; import { ToastWrapper } from '../components/ToastWrapper' @@ -23,4 +24,4 @@ function MyApp({ Component, pageProps }: AppProps) { ); } -export default MyApp; +export default appWithTranslation(MyApp); diff --git a/pages/login/index.tsx b/pages/login/index.tsx index a681210..96d563e 100644 --- a/pages/login/index.tsx +++ b/pages/login/index.tsx @@ -1,6 +1,8 @@ +import { useEffect } from "react"; +import { serverSideTranslations } from "next-i18next/serverSideTranslations"; +import { useTranslation } from "next-i18next"; import { LoginPage } from "packages/ui"; import { useRouter } from "next/dist/client/router"; -import { useEffect } from "react"; import { Credential } from "packages/bloc"; import { usePlocState } from "../../common/usePlocState"; import { usePloc } from "../_app"; @@ -9,6 +11,7 @@ const Login = () => { const { auth: authPloc } = usePloc(); const state = usePlocState(authPloc); const router = useRouter(); + const { t } = useTranslation("login"); useEffect(() => { if (state?.loggedIn) router.replace("/report"); @@ -19,9 +22,17 @@ const Login = () => { onSubmit={(userAndPassword: Credential) => authPloc.login(userAndPassword) } - errorMessage={state?.kind === "ErrorAuthState" ? state.error : undefined} + errorMessage={ + state?.kind === "ErrorAuthState" ? t("error.credentials") : undefined + } /> ); }; +export const getStaticProps = async ({ locale }) => ({ + props: { + ...(await serverSideTranslations(locale, ["login"])), + }, +}); + export default Login; diff --git a/public/locales/en/common.json b/public/locales/en/common.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/public/locales/en/common.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/public/locales/en/login.json b/public/locales/en/login.json new file mode 100644 index 0000000..84110cb --- /dev/null +++ b/public/locales/en/login.json @@ -0,0 +1,8 @@ +{ + "title": "Sign in", + "username": "Username", + "password": "Password", + "login.btn": "Sign in", + "error.email": "Please enter a valid email address", + "error.credentials" : "Sorry, invalid username or password" +} \ No newline at end of file diff --git a/public/locales/es/common.json b/public/locales/es/common.json new file mode 100644 index 0000000..9e26dfe --- /dev/null +++ b/public/locales/es/common.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/public/locales/es/login.json b/public/locales/es/login.json new file mode 100644 index 0000000..fc612ff --- /dev/null +++ b/public/locales/es/login.json @@ -0,0 +1,8 @@ +{ + "title": "Inicio de sesión", + "username": "Usuario", + "password": "Contraseña", + "login.btn": "Ingresar", + "error.email": "Introduzca una dirección de correo electrónico válida", + "error.credentials": "Disculpa, tu usuario o contraseña son incorrectos." +} \ No newline at end of file diff --git a/yarn.lock b/yarn.lock index b89a6b3..f2eca1f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1065,6 +1065,13 @@ dependencies: regenerator-runtime "^0.13.4" +"@babel/runtime@^7.12.0", "@babel/runtime@^7.13.17", "@babel/runtime@^7.14.5", "@babel/runtime@^7.14.6": + version "7.16.3" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.3.tgz#b86f0db02a04187a3c17caa77de69840165d42d5" + integrity sha512-WBwekcqacdY2e9AF/Q7WLFUWmdJGJTkbjqTjoMDgXkVZ3ZRUvOPsLb5KdwISoQVsbP+DQzVZW4Zhci0DvpbNTQ== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/template@^7.12.7", "@babel/template@^7.15.4": version "7.15.4" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.15.4.tgz#51898d35dcf3faa670c4ee6afcfd517ee139f194" @@ -2476,6 +2483,14 @@ dependencies: "@types/unist" "*" +"@types/hoist-non-react-statics@^3.3.1": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" + integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + "@types/html-minifier-terser@^5.0.0": version "5.1.2" resolved "https://registry.yarnpkg.com/@types/html-minifier-terser/-/html-minifier-terser-5.1.2.tgz#693b316ad323ea97eed6b38ed1a3cc02b1672b57" @@ -4505,6 +4520,11 @@ core-js-pure@^3.8.2: resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.18.2.tgz#d8cc11d4885ea919f3de776d45e720e4c769d406" integrity sha512-4hMMLUlZhKJKOWbbGD1/VDUxGPEhEoN/T01k7bx271WiBKCvCfkgPzy0IeRS4PB50p6/N1q/SZL4B/TRsTE5bA== +core-js@^3: + version "3.19.3" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.19.3.tgz#6df8142a996337503019ff3235a7022d7cdf4559" + integrity sha512-LeLBMgEGSsG7giquSzvgBrTS7V5UL6ks3eQlUSbN8dJStlLFiRzUm5iqsRyzUB8carhfKjkJ2vzKqE6z1Vga9g== + core-js@^3.0.4, core-js@^3.6.5, core-js@^3.8.2: version "3.18.2" resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.18.2.tgz#63a551e8a29f305cd4123754846e65896619ba5b" @@ -6626,7 +6646,7 @@ hoist-non-react-statics@^1.0.3: resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-1.2.0.tgz#aa448cf0986d55cc40773b17174b7dd066cb7cfb" integrity sha1-qkSM8JhtVcxAdzsXF0t90GbLfPs= -hoist-non-react-statics@^3.3.0: +hoist-non-react-statics@^3.2.0, hoist-non-react-statics@^3.3.0: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== @@ -6676,6 +6696,13 @@ html-minifier-terser@^5.0.1: relateurl "^0.2.7" terser "^4.6.3" +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" + html-tags@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/html-tags/-/html-tags-3.1.0.tgz#7b5e6f7e665e9fb41f30007ed9e0d41e97fb2140" @@ -6751,6 +6778,25 @@ https-proxy-agent@^4.0.0: agent-base "5" debug "4" +i18next-browser-languagedetector@^6.1.2: + version "6.1.2" + resolved "https://registry.yarnpkg.com/i18next-browser-languagedetector/-/i18next-browser-languagedetector-6.1.2.tgz#68565a28b929cbc98ab6a56826ef2faf0e927ff8" + integrity sha512-YDzIGHhMRvr7M+c8B3EQUKyiMBhfqox4o1qkFvt4QXuu5V2cxf74+NCr+VEkUuU0y+RwcupA238eeolW1Yn80g== + dependencies: + "@babel/runtime" "^7.14.6" + +i18next-fs-backend@^1.0.7: + version "1.1.4" + resolved "https://registry.yarnpkg.com/i18next-fs-backend/-/i18next-fs-backend-1.1.4.tgz#d0e9b9ed2fa7a0f11002d82b9fa69c3c3d6482da" + integrity sha512-/MfAGMP0jHonV966uFf9PkWWuDjPYLIcsipnSO3NxpNtAgRUKLTwvm85fEmsF6hGeu0zbZiCQ3W74jwO6K9uXA== + +i18next@^21.5.3, i18next@^21.5.4: + version "21.5.4" + resolved "https://registry.yarnpkg.com/i18next/-/i18next-21.5.4.tgz#55ab39c31cc0b4f9f58c3a599db50c4515698284" + integrity sha512-ukwRJpLhYg4EUfCOtbaKjlwF71qyel1XMXQN78OkQMcaQG68UzlYgLC6g2fhoTNBvoH2tJkaaqzDumhC9skAhA== + dependencies: + "@babel/runtime" "^7.12.0" + iconv-lite@0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" @@ -8444,6 +8490,19 @@ nested-error-stacks@^2.0.0, nested-error-stacks@^2.1.0: resolved "https://registry.yarnpkg.com/nested-error-stacks/-/nested-error-stacks-2.1.0.tgz#0fbdcf3e13fe4994781280524f8b96b0cdff9c61" integrity sha512-AO81vsIO1k1sM4Zrd6Hu7regmJN1NSiAja10gc4bX3F0wd+9rQmcuHQaHVQCYIEC8iFXnE+mavh23GOt7wBgug== +next-i18next@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/next-i18next/-/next-i18next-10.0.1.tgz#f42239f276b3c7a3e62b9fd83ac5c21607c2f938" + integrity sha512-wcpZDip4Vt89dCHgN2EKeNyA5Ckz6NenPUVpzQMphmuXyTPMqmFkWyWbpMDdg4QuMiYiX98eTeY4RLGhUXzHmQ== + dependencies: + "@babel/runtime" "^7.13.17" + "@types/hoist-non-react-statics" "^3.3.1" + core-js "^3" + hoist-non-react-statics "^3.2.0" + i18next "^21.5.3" + i18next-fs-backend "^1.0.7" + react-i18next "^11.8.13" + next@*, next@11.1.2: version "11.1.2" resolved "https://registry.yarnpkg.com/next/-/next-11.1.2.tgz#527475787a9a362f1bc916962b0c0655cc05bc91" @@ -9869,6 +9928,14 @@ react-helmet-async@^1.0.7: react-fast-compare "^3.2.0" shallowequal "^1.1.0" +react-i18next@^11.8.13: + version "11.14.3" + resolved "https://registry.yarnpkg.com/react-i18next/-/react-i18next-11.14.3.tgz#b44b5c4d1aadac5211be011827a2830be60f2522" + integrity sha512-Hf2aanbKgYxPjG8ZdKr+PBz9sY6sxXuZWizxCYyJD2YzvJ0W9JTQcddVEjDaKyBoCyd3+5HTerdhc9ehFugc6g== + dependencies: + "@babel/runtime" "^7.14.5" + html-parse-stringify "^3.0.1" + react-icons@^4.3.1: version "4.3.1" resolved "https://registry.yarnpkg.com/react-icons/-/react-icons-4.3.1.tgz#2fa92aebbbc71f43d2db2ed1aed07361124e91ca" @@ -11994,6 +12061,11 @@ vm-browserify@1.1.2, vm-browserify@^1.0.1: resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" integrity sha512-2ham8XPWTONajOR0ohOKOHXkm3+gaBmGut3SRuu75xLd/RRaY6vqgh8NBYYk7+RW3u5AtzPQZG8F10LHkl0lAQ== +void-elements@3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-3.1.0.tgz#614f7fbf8d801f0bb5f0661f5b2f5785750e4f09" + integrity sha1-YU9/v42AHwu18GYfWy9XhXUOTwk= + walker@^1.0.7, walker@~1.0.5: version "1.0.7" resolved "https://registry.yarnpkg.com/walker/-/walker-1.0.7.tgz#2f7f9b8fd10d677262b18a884e28d19618e028fb"